diff --git a/.github/workflows/android-tests.yml b/.github/workflows/android-tests.yml index 58b7532b0..065a63a36 100644 --- a/.github/workflows/android-tests.yml +++ b/.github/workflows/android-tests.yml @@ -19,7 +19,7 @@ jobs: with: distribution: temurin java-version: 17 - - uses: gradle/actions/setup-gradle@v3 + - uses: gradle/actions/setup-gradle@v4 - name: Enable KVM group perms run: | diff --git a/.github/workflows/create-alpha.yml b/.github/workflows/create-alpha.yml index fe45a0e27..9f0a6bc5f 100644 --- a/.github/workflows/create-alpha.yml +++ b/.github/workflows/create-alpha.yml @@ -17,7 +17,7 @@ jobs: with: distribution: temurin java-version: 17 - - uses: gradle/actions/setup-gradle@v3 + - uses: gradle/actions/setup-gradle@v4 - name: Prepare keystore run: echo ${{ secrets.android_keystore_base64 }} | base64 -d >$GITHUB_WORKSPACE/keystore.jks diff --git a/.github/workflows/create-baseline-profiles.yml b/.github/workflows/create-baseline-profiles.yml index 7d653da5b..e2526053f 100644 --- a/.github/workflows/create-baseline-profiles.yml +++ b/.github/workflows/create-baseline-profiles.yml @@ -50,7 +50,7 @@ jobs: # Sets gradle up - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 # Grants execute permission to gradle (safety step) - name: Grant Permissions to gradlew diff --git a/.github/workflows/create-beta.yml b/.github/workflows/create-beta.yml index 37c68fb5a..e11ecea7b 100644 --- a/.github/workflows/create-beta.yml +++ b/.github/workflows/create-beta.yml @@ -17,7 +17,7 @@ jobs: with: distribution: temurin java-version: 17 - - uses: gradle/actions/setup-gradle@v3 + - uses: gradle/actions/setup-gradle@v4 - name: Prepare keystore run: echo ${{ secrets.android_keystore_base64 }} | base64 -d >$GITHUB_WORKSPACE/keystore.jks @@ -53,7 +53,7 @@ jobs: run: cp app/build/outputs/apk/ose/release/app-ose-release.apk jtxBoard-${{ github.ref_name }}.apk - name: Create Github release - uses: softprops/action-gh-release@v2.0.6 + uses: softprops/action-gh-release@v2.0.9 with: prerelease: ${{ contains(github.ref_name, '-alpha') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-rc') }} files: | diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 7a99bcee7..9e4c4f3b0 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -19,7 +19,7 @@ jobs: with: distribution: temurin java-version: 17 - - uses: gradle/actions/setup-gradle@v3 + - uses: gradle/actions/setup-gradle@v4 - name: Prepare keystore run: echo ${{ secrets.android_keystore_base64 }} | base64 -d >$GITHUB_WORKSPACE/keystore.jks @@ -66,7 +66,7 @@ jobs: run: cp app/build/outputs/apk/ose/release/app-ose-release.apk jtxBoard-${{ github.ref_name }}.apk - name: Create Github release - uses: softprops/action-gh-release@v2.0.6 + uses: softprops/action-gh-release@v2.0.9 with: prerelease: ${{ contains(github.ref_name, '-alpha') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-rc') }} files: | diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 19ab194b2..e72a55965 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,8 +29,8 @@ android { buildConfigField("long", "buildTime", "${System.currentTimeMillis()}L") minSdk = 23 targetSdk = 34 - versionCode = 209000010 - versionName = "2.09.00-beta01" // keep -release as a suffix also for release, build flavor adds the suffix e.g. .gplay (e.g. 1.00.00-rc0.gplay) + versionCode = 209030005 + versionName = "2.09.03-beta03" // keep -release as a suffix also for release, build flavor adds the suffix e.g. .gplay (e.g. 1.00.00-rc0.gplay) buildConfigField("String", "versionCodename", "\"Pride is a protest \uD83C\uDF08\"") multiDexEnabled = true vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/at/techbee/jtx/database/properties/Alarm.kt b/app/src/main/java/at/techbee/jtx/database/properties/Alarm.kt index 6cd004020..fbcf3ba19 100644 --- a/app/src/main/java/at/techbee/jtx/database/properties/Alarm.kt +++ b/app/src/main/java/at/techbee/jtx/database/properties/Alarm.kt @@ -418,14 +418,6 @@ data class Alarm( if (isReadOnly && SettingsStateHolder(context).settingDisableAlarmsReadonly.value) // don't schedule alarm for read only if option was deactivated! return - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - Log.i( - "scheduleNotification", - "Due to necessity of PendingIntent.FLAG_IMMUTABLE, the notification functionality can only be used from Build Versions > M (Api-Level 23)" - ) - return - } - val notification = createNotification( icalObjectId, alarmId, @@ -452,10 +444,20 @@ data class Alarm( // the alarmManager finally takes care, that the pendingIntent is queued to start the notification Intent that on click would start the contentIntent val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && alarmManager.canScheduleExactAlarms()) || Build.VERSION.SDK_INT < Build.VERSION_CODES.S) - alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime!!, pendingIntent) - else - alarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime!!, pendingIntent) + try { + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && alarmManager.canScheduleExactAlarms()) || Build.VERSION.SDK_INT < Build.VERSION_CODES.S) + alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime!!, pendingIntent) + else + alarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime!!, pendingIntent) + } catch (e: IllegalStateException) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + alarmManager.cancelAll() + } else { + while(alarmManager.nextAlarmClock!=null) { + alarmManager.cancel(alarmManager.nextAlarmClock.showIntent) + } + } + } } /** diff --git a/app/src/main/java/at/techbee/jtx/database/relations/ICal4ListRel.kt b/app/src/main/java/at/techbee/jtx/database/relations/ICal4ListRel.kt index cf391b715..b749b11e5 100644 --- a/app/src/main/java/at/techbee/jtx/database/relations/ICal4ListRel.kt +++ b/app/src/main/java/at/techbee/jtx/database/relations/ICal4ListRel.kt @@ -97,7 +97,12 @@ data class ICal4ListRel( this[context.getString(R.string.filter_no_category)] = mutableListOf(sortedEntry) } } - } + }.toSortedMap( + if(sortOrder == SortOrder.DESC) + compareByDescending { it.uppercase() } + else + compareBy { it.uppercase() } + ) GroupBy.RESOURCE -> mutableMapOf>().apply { sortedList.forEach { sortedEntry -> if (sortedEntry.resources.isNotEmpty()) { @@ -114,7 +119,12 @@ data class ICal4ListRel( this[context.getString(R.string.filter_no_resource)] = mutableListOf(sortedEntry) } } - } + }.toSortedMap( + if(sortOrder == SortOrder.DESC) + compareByDescending { it.uppercase() } + else + compareBy { it.uppercase() } + ) //GroupBy.CATEGORY -> sortedList.groupBy { if(it.categories.isEmpty()) context.getString(R.string.filter_no_category) else it.categories.joinToString(separator = ", ") { category -> category.text } }.toSortedMap() //GroupBy.RESOURCE -> sortedList.groupBy { if(it.resources.isEmpty()) context.getString(R.string.filter_no_resource) else it.resources.joinToString(separator = ", ") { resource -> resource.text?:"" } }.toSortedMap() GroupBy.STATUS -> sortedList.groupBy { diff --git a/app/src/main/java/at/techbee/jtx/ui/collections/CollectionsScreen.kt b/app/src/main/java/at/techbee/jtx/ui/collections/CollectionsScreen.kt index 62a257bb0..db908073d 100644 --- a/app/src/main/java/at/techbee/jtx/ui/collections/CollectionsScreen.kt +++ b/app/src/main/java/at/techbee/jtx/ui/collections/CollectionsScreen.kt @@ -42,18 +42,22 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController +import androidx.preference.PreferenceManager import at.techbee.jtx.R import at.techbee.jtx.database.ICalCollection import at.techbee.jtx.database.Module +import at.techbee.jtx.database.locals.StoredListSettingData import at.techbee.jtx.database.views.CollectionsView import at.techbee.jtx.ui.GlobalStateHolder import at.techbee.jtx.ui.reusable.appbars.JtxNavigationDrawer import at.techbee.jtx.ui.reusable.appbars.JtxTopAppBar import at.techbee.jtx.ui.reusable.appbars.OverflowMenu +import at.techbee.jtx.ui.reusable.destinations.FilteredListDestination import at.techbee.jtx.ui.reusable.dialogs.CollectionsAddOrEditDialog import at.techbee.jtx.ui.reusable.dialogs.SelectModuleForTxtImportDialog import at.techbee.jtx.ui.settings.DropdownSettingOption import at.techbee.jtx.ui.settings.SettingsStateHolder +import at.techbee.jtx.ui.settings.SettingsStateHolder.Companion.PREFS_LAST_MODULE import at.techbee.jtx.util.DateTimeUtils import at.techbee.jtx.util.SyncUtil @@ -305,8 +309,25 @@ fun CollectionsScreen( } }, onCollectionClicked = { collection -> - if (globalStateHolder.icalString2Import.value?.isNotEmpty() == true && !collection.readonly) + if (globalStateHolder.icalString2Import.value?.isNotEmpty() == true && !collection.readonly) { importCollection = collection + } else { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val lastUsedModule = try { Module.valueOf(prefs.getString(PREFS_LAST_MODULE, null)?: Module.JOURNAL.name) } catch (e: java.lang.IllegalArgumentException) { Module.JOURNAL } + + navController.navigate( + FilteredListDestination.FilteredList.getRoute( + module = when { + lastUsedModule == Module.JOURNAL && collection.supportsVJOURNAL -> lastUsedModule + lastUsedModule == Module.NOTE && collection.supportsVJOURNAL -> lastUsedModule + lastUsedModule == Module.TODO && collection.supportsVTODO -> lastUsedModule + collection.supportsVTODO -> Module.TODO + else -> Module.JOURNAL + }, + storedListSettingData = StoredListSettingData(searchCollection = listOf(collection.displayName?:"")) + ) + ) + } }, onDeleteAccount = { account -> collectionsViewModel.removeAccount(account) } ) diff --git a/app/src/main/java/at/techbee/jtx/ui/detail/DetailViewModel.kt b/app/src/main/java/at/techbee/jtx/ui/detail/DetailViewModel.kt index ffdafcfc1..5371e0da2 100644 --- a/app/src/main/java/at/techbee/jtx/ui/detail/DetailViewModel.kt +++ b/app/src/main/java/at/techbee/jtx/ui/detail/DetailViewModel.kt @@ -106,6 +106,7 @@ class DetailViewModel(application: Application) : AndroidViewModel(application) val mediaPlayer = MediaPlayer() private var _isAuthenticated = false + private var immediateAlarmTriggeredOnce = false companion object { const val PREFS_DETAIL_JOURNALS = "prefsDetailJournals" @@ -116,6 +117,7 @@ class DetailViewModel(application: Application) : AndroidViewModel(application) fun load(icalObjectId: Long, isAuthenticated: Boolean) { mainICalObjectId = icalObjectId _isAuthenticated = isAuthenticated + immediateAlarmTriggeredOnce = false viewModelScope.launch { withContext(Dispatchers.Main) { changeState.value = DetailChangeState.LOADING } @@ -210,6 +212,7 @@ class DetailViewModel(application: Application) : AndroidViewModel(application) } ?: emptyList() else emptyList(), searchAccount = if (sameAccount) collection.value?.accountName?.let { listOf(it) } ?: emptyList() else emptyList(), + flatView = true, orderBy = OrderBy.LAST_MODIFIED, sortOrder = SortOrder.DESC, hideBiometricProtected = if (_isAuthenticated) emptyList() else ListSettings.getProtectedClassificationsFromSettings( @@ -443,9 +446,16 @@ class DetailViewModel(application: Application) : AndroidViewModel(application) databaseDao.setAlarmNotification(it.id, false) } - if(triggerImmediateAlarm) - NotificationPublisher.triggerImmediateAlarm(it, _application) + val triggerInPastButNotDone = + mutableAlarms.any { alarm -> (alarm.triggerTime?:0L) <= System.currentTimeMillis() } + && mutableAlarms.none { alarm -> (alarm.triggerTime?:0L) > System.currentTimeMillis() } + && it.percent != 100 + && it.status != Status.COMPLETED.status + if(!immediateAlarmTriggeredOnce && (triggerImmediateAlarm || triggerInPastButNotDone)) { + NotificationPublisher.triggerImmediateAlarm(it, _application) + immediateAlarmTriggeredOnce = true + } } } diff --git a/app/src/main/java/at/techbee/jtx/ui/list/ListScreen.kt b/app/src/main/java/at/techbee/jtx/ui/list/ListScreen.kt index 82b6e3198..089cbfaf8 100644 --- a/app/src/main/java/at/techbee/jtx/ui/list/ListScreen.kt +++ b/app/src/main/java/at/techbee/jtx/ui/list/ListScreen.kt @@ -10,12 +10,9 @@ package at.techbee.jtx.ui.list import android.widget.Toast -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.navigation.NavController import at.techbee.jtx.database.ICalDatabase @@ -23,7 +20,6 @@ import at.techbee.jtx.database.relations.ICal4ListRel import at.techbee.jtx.database.views.ICal4List import at.techbee.jtx.ui.reusable.destinations.DetailDestination import at.techbee.jtx.ui.settings.SettingsStateHolder -import at.techbee.jtx.util.SyncUtil @Composable @@ -34,7 +30,6 @@ fun ListScreen( val context = LocalContext.current val database = ICalDatabase.getInstance(context).iCalDatabaseDao() val settingsStateHolder = SettingsStateHolder(context) - val isPullRefreshEnabled = SyncUtil.availableSyncApps(context).any { SyncUtil.isSyncAppCompatible(it, context) } && settingsStateHolder.settingSyncOnPullRefresh.value listViewModel.toastMessage.value?.let { Toast.makeText(context, it, Toast.LENGTH_SHORT).show() @@ -73,131 +68,122 @@ fun ListScreen( } - Column(modifier = Modifier.fillMaxSize()) { - when (listViewModel.listSettings.viewMode.value) { - ViewMode.LIST -> { - ListScreenList( - groupedList = groupedList, - subtasksLive = listViewModel.allSubtasks, - subnotesLive = listViewModel.allSubnotes, - parentsLive = listViewModel.allParents, - selectedEntries = listViewModel.selectedEntries, - attachmentsLive = listViewModel.allAttachmentsMap, - scrollOnceId = listViewModel.scrollOnceId, - listSettings = listViewModel.listSettings, - storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, - storedResources = database.getStoredResources().observeAsState(emptyList()).value, - storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, - isSubtasksExpandedDefault = settingsStateHolder.settingAutoExpandSubtasks.value, - isSubnotesExpandedDefault = settingsStateHolder.settingAutoExpandSubnotes.value, - isAttachmentsExpandedDefault = settingsStateHolder.settingAutoExpandAttachments.value, - settingShowProgressMaintasks = settingsStateHolder.settingShowProgressForMainTasks.value, - settingShowProgressSubtasks = settingsStateHolder.settingShowProgressForSubTasks.value, - settingProgressIncrement = settingsStateHolder.settingStepForProgress.value, - settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, - settingDisplayTimezone = settingsStateHolder.settingDisplayTimezone.value, - settingIsAccessibilityMode = settingsStateHolder.settingAccessibilityMode.value, - isPullRefreshEnabled = isPullRefreshEnabled, - markdownEnabled = listViewModel.listSettings.markdownEnabled.value, - player = listViewModel.mediaPlayer, - isListDragAndDropEnabled = listViewModel.listSettings.orderBy.value == OrderBy.DRAG_AND_DROP || listViewModel.listSettings.orderBy2.value == OrderBy.DRAG_AND_DROP, - isSubtaskDragAndDropEnabled = listViewModel.listSettings.subtasksOrderBy.value == OrderBy.DRAG_AND_DROP, - isSubnoteDragAndDropEnabled = listViewModel.listSettings.subnotesOrderBy.value == OrderBy.DRAG_AND_DROP, - onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, - onLongClick = { itemId, isReadOnly -> processOnLongClick(itemId, isReadOnly) }, - onProgressChanged = { itemId, newPercent -> - processOnProgressChanged(itemId, newPercent) - }, - onExpandedChanged = { itemId: Long, isSubtasksExpanded: Boolean, isSubnotesExpanded: Boolean, isParentsExpanded: Boolean, isAttachmentsExpanded: Boolean -> - listViewModel.updateExpanded( - itemId, - isSubtasksExpanded, - isSubnotesExpanded, - isParentsExpanded, - isAttachmentsExpanded - ) - }, - onSyncRequested = { listViewModel.syncAccounts() }, - onSaveListSettings = { listViewModel.saveListSettings() }, - onUpdateSortOrder = { listViewModel.updateSortOrder(it) } - ) - } - ViewMode.GRID -> { - ListScreenGrid( - list = list, - subtasksLive = listViewModel.allSubtasks, - storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, - storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, - selectedEntries = listViewModel.selectedEntries, - scrollOnceId = listViewModel.scrollOnceId, - settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, - isPullRefreshEnabled = isPullRefreshEnabled, - markdownEnabled = listViewModel.listSettings.markdownEnabled.value, - player = listViewModel.mediaPlayer, - onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, - onLongClick = { itemId, isReadOnly -> processOnLongClick(itemId, isReadOnly) }, - onProgressChanged = { itemId, newPercent -> - processOnProgressChanged(itemId, newPercent) - }, - onSyncRequested = { listViewModel.syncAccounts() }, - isListDragAndDropEnabled = listViewModel.listSettings.orderBy.value == OrderBy.DRAG_AND_DROP || listViewModel.listSettings.orderBy2.value == OrderBy.DRAG_AND_DROP + when (listViewModel.listSettings.viewMode.value) { + ViewMode.LIST -> { + ListScreenList( + groupedList = groupedList, + subtasksLive = listViewModel.allSubtasks, + subnotesLive = listViewModel.allSubnotes, + parentsLive = listViewModel.allParents, + selectedEntries = listViewModel.selectedEntries, + attachmentsLive = listViewModel.allAttachmentsMap, + scrollOnceId = listViewModel.scrollOnceId, + listSettings = listViewModel.listSettings, + storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, + storedResources = database.getStoredResources().observeAsState(emptyList()).value, + storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, + isSubtasksExpandedDefault = settingsStateHolder.settingAutoExpandSubtasks.value, + isSubnotesExpandedDefault = settingsStateHolder.settingAutoExpandSubnotes.value, + isAttachmentsExpandedDefault = settingsStateHolder.settingAutoExpandAttachments.value, + isParentsExpandedDefault = settingsStateHolder.settingAutoExpandParents.value, + settingShowProgressMaintasks = settingsStateHolder.settingShowProgressForMainTasks.value, + settingShowProgressSubtasks = settingsStateHolder.settingShowProgressForSubTasks.value, + settingProgressIncrement = settingsStateHolder.settingStepForProgress.value, + settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, + settingDisplayTimezone = settingsStateHolder.settingDisplayTimezone.value, + settingIsAccessibilityMode = settingsStateHolder.settingAccessibilityMode.value, + markdownEnabled = listViewModel.listSettings.markdownEnabled.value, + player = listViewModel.mediaPlayer, + isListDragAndDropEnabled = listViewModel.listSettings.orderBy.value == OrderBy.DRAG_AND_DROP || listViewModel.listSettings.orderBy2.value == OrderBy.DRAG_AND_DROP, + isSubtaskDragAndDropEnabled = listViewModel.listSettings.subtasksOrderBy.value == OrderBy.DRAG_AND_DROP, + isSubnoteDragAndDropEnabled = listViewModel.listSettings.subnotesOrderBy.value == OrderBy.DRAG_AND_DROP, + onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, + onLongClick = { itemId, ical4list -> processOnLongClick(itemId, ical4list) }, + onProgressChanged = { itemId, newPercent -> + processOnProgressChanged(itemId, newPercent) + }, + onExpandedChanged = { itemId: Long, isSubtasksExpanded: Boolean, isSubnotesExpanded: Boolean, isParentsExpanded: Boolean, isAttachmentsExpanded: Boolean -> + listViewModel.updateExpanded( + itemId, + isSubtasksExpanded, + isSubnotesExpanded, + isParentsExpanded, + isAttachmentsExpanded ) - } - ViewMode.COMPACT -> { - ListScreenCompact( - groupedList = groupedList, - subtasksLive = listViewModel.allSubtasks, - storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, - storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, - selectedEntries = listViewModel.selectedEntries, - scrollOnceId = listViewModel.scrollOnceId, - listSettings = listViewModel.listSettings, - settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, - isPullRefreshEnabled = isPullRefreshEnabled, - player = listViewModel.mediaPlayer, - isListDragAndDropEnabled = listViewModel.listSettings.orderBy.value == OrderBy.DRAG_AND_DROP || listViewModel.listSettings.orderBy2.value == OrderBy.DRAG_AND_DROP, - isSubtaskDragAndDropEnabled = listViewModel.listSettings.subtasksOrderBy.value == OrderBy.DRAG_AND_DROP, - onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, - onLongClick = { itemId, isReadOnly -> processOnLongClick(itemId, isReadOnly) }, - onProgressChanged = { itemId, newPercent -> processOnProgressChanged(itemId, newPercent) }, - onSyncRequested = { listViewModel.syncAccounts() }, - onSaveListSettings = { listViewModel.saveListSettings() }, - onUpdateSortOrder = { listViewModel.updateSortOrder(it) } - ) - } - ViewMode.KANBAN -> { - ListScreenKanban( - module = listViewModel.module, - list = list, - subtasksLive = listViewModel.allSubtasks, - storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, - storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, - selectedEntries = listViewModel.selectedEntries, - kanbanColumnsStatus = listViewModel.listSettings.kanbanColumnsStatus, - kanbanColumnsXStatus = listViewModel.listSettings.kanbanColumnsXStatus, - kanbanColumnsCategory = listViewModel.listSettings.kanbanColumnsCategory, - scrollOnceId = listViewModel.scrollOnceId, - settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, - isPullRefreshEnabled = isPullRefreshEnabled, - markdownEnabled = listViewModel.listSettings.markdownEnabled.value, - player = listViewModel.mediaPlayer, - onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, - onLongClick = { itemId, isReadOnly -> processOnLongClick(itemId, isReadOnly) }, - onStatusChanged = { itemId, newStatus, scrollOnce -> listViewModel.updateStatus(itemId, newStatus, scrollOnce) }, - onXStatusChanged = { itemId, newXStatus, scrollOnce -> listViewModel.updateXStatus(itemId, newXStatus, scrollOnce) }, - onSwapCategories = { itemId, oldCategory, newCategory -> listViewModel.swapCategories(itemId, oldCategory, newCategory) }, - onSyncRequested = { listViewModel.syncAccounts() } - ) - } - ViewMode.WEEK -> { - ListScreenWeek( - list = list, - selectedEntries = listViewModel.selectedEntries, - scrollOnceId = listViewModel.scrollOnceId, - onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, - onLongClick = { itemId, isReadOnly -> processOnLongClick(itemId, isReadOnly) }, + }, + onSaveListSettings = { listViewModel.saveListSettings() }, + onUpdateSortOrder = { listViewModel.updateSortOrder(it) } + ) + } + ViewMode.GRID -> { + ListScreenGrid( + list = list, + subtasksLive = listViewModel.allSubtasks, + storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, + storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, + selectedEntries = listViewModel.selectedEntries, + scrollOnceId = listViewModel.scrollOnceId, + settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, + markdownEnabled = listViewModel.listSettings.markdownEnabled.value, + player = listViewModel.mediaPlayer, + onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, + onLongClick = { itemId, ical4list -> processOnLongClick(itemId, ical4list) }, + onProgressChanged = { itemId, newPercent -> + processOnProgressChanged(itemId, newPercent) + }, + isListDragAndDropEnabled = listViewModel.listSettings.orderBy.value == OrderBy.DRAG_AND_DROP || listViewModel.listSettings.orderBy2.value == OrderBy.DRAG_AND_DROP ) - } + } + ViewMode.COMPACT -> { + ListScreenCompact( + groupedList = groupedList, + subtasksLive = listViewModel.allSubtasks, + storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, + storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, + selectedEntries = listViewModel.selectedEntries, + scrollOnceId = listViewModel.scrollOnceId, + listSettings = listViewModel.listSettings, + settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, + player = listViewModel.mediaPlayer, + isListDragAndDropEnabled = listViewModel.listSettings.orderBy.value == OrderBy.DRAG_AND_DROP || listViewModel.listSettings.orderBy2.value == OrderBy.DRAG_AND_DROP, + isSubtaskDragAndDropEnabled = listViewModel.listSettings.subtasksOrderBy.value == OrderBy.DRAG_AND_DROP, + onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, + onLongClick = { itemId, ical4list -> processOnLongClick(itemId, ical4list) }, + onProgressChanged = { itemId, newPercent -> processOnProgressChanged(itemId, newPercent) }, + onSaveListSettings = { listViewModel.saveListSettings() }, + onUpdateSortOrder = { listViewModel.updateSortOrder(it) } + ) + } + ViewMode.KANBAN -> { + ListScreenKanban( + module = listViewModel.module, + list = list, + subtasksLive = listViewModel.allSubtasks, + storedCategories = database.getStoredCategories().observeAsState(emptyList()).value, + storedStatuses = database.getStoredStatuses().observeAsState(emptyList()).value, + selectedEntries = listViewModel.selectedEntries, + kanbanColumnsStatus = listViewModel.listSettings.kanbanColumnsStatus, + kanbanColumnsXStatus = listViewModel.listSettings.kanbanColumnsXStatus, + kanbanColumnsCategory = listViewModel.listSettings.kanbanColumnsCategory, + scrollOnceId = listViewModel.scrollOnceId, + settingLinkProgressToSubtasks = settingsStateHolder.settingLinkProgressToSubtasks.value, + markdownEnabled = listViewModel.listSettings.markdownEnabled.value, + player = listViewModel.mediaPlayer, + onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, + onLongClick = { itemId, ical4list -> processOnLongClick(itemId, ical4list) }, + onStatusChanged = { itemId, newStatus, scrollOnce -> listViewModel.updateStatus(itemId, newStatus, scrollOnce) }, + onXStatusChanged = { itemId, newXStatus, scrollOnce -> listViewModel.updateXStatus(itemId, newXStatus, scrollOnce) }, + onSwapCategories = { itemId, oldCategory, newCategory -> listViewModel.swapCategories(itemId, oldCategory, newCategory) } + ) + } + ViewMode.WEEK -> { + ListScreenWeek( + list = list, + selectedEntries = listViewModel.selectedEntries, + scrollOnceId = listViewModel.scrollOnceId, + onClick = { itemId, ical4list, isReadOnly -> processOnClick(itemId, ical4list, isReadOnly) }, + onLongClick = { itemId, isReadOnly -> processOnLongClick(itemId, isReadOnly) }, + ) } } } diff --git a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenCompact.kt b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenCompact.kt index 667c067ab..8d421d719 100644 --- a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenCompact.kt +++ b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenCompact.kt @@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -30,14 +29,11 @@ import androidx.compose.material.icons.outlined.ArrowDropUp import androidx.compose.material.icons.outlined.VerticalAlignTop import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.pulltorefresh.PullToRefreshContainer -import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -52,7 +48,6 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -78,7 +73,7 @@ import sh.calvin.reorderable.rememberReorderableLazyListState import java.util.UUID -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalFoundationApi::class) @Composable fun ListScreenCompact( groupedList: Map>, @@ -89,14 +84,12 @@ fun ListScreenCompact( scrollOnceId: MutableLiveData, listSettings: ListSettings, settingLinkProgressToSubtasks: Boolean, - isPullRefreshEnabled: Boolean, player: MediaPlayer?, isListDragAndDropEnabled: Boolean, isSubtaskDragAndDropEnabled: Boolean, onProgressChanged: (itemId: Long, newPercent: Int) -> Unit, onClick: (itemId: Long, list: List, isReadOnly: Boolean) -> Unit, onLongClick: (itemId: Long, isReadOnly: Boolean) -> Unit, - onSyncRequested: () -> Unit, onSaveListSettings: () -> Unit, onUpdateSortOrder: (List) -> Unit ) { @@ -115,88 +108,76 @@ fun ListScreenCompact( } val scope = rememberCoroutineScope() - val pullToRefreshState = rememberPullToRefreshState( - enabled = { isPullRefreshEnabled } - ) - LaunchedEffect(pullToRefreshState.isRefreshing) { - if(pullToRefreshState.isRefreshing) { - onSyncRequested() - pullToRefreshState.endRefresh() - } - } - - Box( - contentAlignment = Alignment.TopCenter, - modifier = Modifier.fillMaxSize().nestedScroll(pullToRefreshState.nestedScrollConnection) + LazyColumn( + modifier = Modifier + .padding(start = 2.dp, end = 2.dp) + .fillMaxSize(), + state = listState, ) { - LazyColumn( - modifier = Modifier.padding(start = 2.dp, end = 2.dp).fillMaxSize(), - state = listState, - ) { - groupedList.forEach { (groupName, group) -> + groupedList.forEach { (groupName, group) -> - if (groupedList.keys.size > 1) { - stickyHeader { + if (groupedList.keys.size > 1) { + stickyHeader { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.background) - ) { - TextButton(onClick = { - if (listSettings.collapsedGroups.contains(groupName)) - listSettings.collapsedGroups.remove(groupName) - else - listSettings.collapsedGroups.add(groupName) - onSaveListSettings() - }) { - Text( - text = groupName, - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(horizontal = 4.dp) - ) + ) { + TextButton(onClick = { + if (listSettings.collapsedGroups.contains(groupName)) + listSettings.collapsedGroups.remove(groupName) + else + listSettings.collapsedGroups.add(groupName) + onSaveListSettings() + }) { + Text( + text = groupName, + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(horizontal = 4.dp) + ) - if (listSettings.collapsedGroups.contains(groupName)) - Icon(Icons.Outlined.ArrowDropUp, stringResource(R.string.list_collapse)) - else - Icon(Icons.Outlined.ArrowDropDown, stringResource(R.string.list_expand)) - } + if (listSettings.collapsedGroups.contains(groupName)) + Icon(Icons.Outlined.ArrowDropUp, stringResource(R.string.list_collapse)) + else + Icon(Icons.Outlined.ArrowDropDown, stringResource(R.string.list_expand)) } } } + } - if (groupedList.keys.size <= 1 || (groupedList.keys.size > 1 && !listSettings.collapsedGroups.contains(groupName))) { - items( - items = group.toList(), - key = { item -> - if(listSettings.groupBy.value == GroupBy.CATEGORY || listSettings.groupBy.value == GroupBy.RESOURCE) - item.iCal4List.id.toString() + UUID.randomUUID() - else - item.iCal4List.id - } - ) - { iCal4ListRelObject -> + if (groupedList.keys.size <= 1 || (groupedList.keys.size > 1 && !listSettings.collapsedGroups.contains(groupName))) { + items( + items = group.toList(), + key = { item -> + if(listSettings.groupBy.value == GroupBy.CATEGORY || listSettings.groupBy.value == GroupBy.RESOURCE) + item.iCal4List.id.toString() + UUID.randomUUID() + else + item.iCal4List.id + } + ) + { iCal4ListRelObject -> - var currentSubtasks = - subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } - .map { it.iCal4List } - if (listSettings.isExcludeDone.value) // exclude done if applicable - currentSubtasks = currentSubtasks.filter { subtask -> subtask.percent != 100 && subtask.status != Status.COMPLETED.status } + var currentSubtasks = + subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } + .map { it.iCal4List } + if (listSettings.isExcludeDone.value) // exclude done if applicable + currentSubtasks = currentSubtasks.filter { subtask -> subtask.percent != 100 && subtask.status != Status.COMPLETED.status } - if (scrollId != null) { - LaunchedEffect(group) { - val index = group.indexOfFirst { iCalObject -> iCalObject.iCal4List.id == scrollId } - if (index > -1) { - listState.scrollToItem(index) - scrollOnceId.postValue(null) - } + if (scrollId != null) { + LaunchedEffect(group) { + val index = group.indexOfFirst { iCalObject -> iCalObject.iCal4List.id == scrollId } + if (index > -1) { + listState.scrollToItem(index) + scrollOnceId.postValue(null) } } + } ReorderableItem( reorderableLazyListState, @@ -230,7 +211,7 @@ fun ListScreenCompact( ) }, onLongClick = { - onLongClick(iCal4ListRelObject.iCal4List.id, iCal4ListRelObject.iCal4List.isReadOnly) + onLongClick(iCal4ListRelObject.iCal4List.id, iCal4ListRelObject.iCal4List.isReadOnly) } ), onProgressChanged = onProgressChanged, @@ -240,38 +221,32 @@ fun ListScreenCompact( ) } - if (iCal4ListRelObject != group.last()) - HorizontalDivider( - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.alpha(0.25f) - ) - } + if (iCal4ListRelObject != group.last()) + HorizontalDivider( + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.alpha(0.25f) + ) } } } + } - PullToRefreshContainer( - modifier = Modifier.align(Alignment.TopCenter).offset(y = (-30).dp), - state = pullToRefreshState, - ) - - Crossfade(listState.canScrollBackward, label = "showScrollUp") { - if(it) { - Box( - contentAlignment = Alignment.BottomCenter, - modifier = Modifier.fillMaxSize() + Crossfade(listState.canScrollBackward, label = "showScrollUp") { + if(it) { + Box( + contentAlignment = Alignment.BottomCenter, + modifier = Modifier.fillMaxSize() + ) { + Button( + onClick = { + scope.launch { listState.scrollToItem(0) } + }, + colors = ButtonDefaults.filledTonalButtonColors(), + modifier = Modifier + .padding(8.dp) + .alpha(0.33f) ) { - Button( - onClick = { - scope.launch { listState.scrollToItem(0) } - }, - colors = ButtonDefaults.filledTonalButtonColors(), - modifier = Modifier - .padding(8.dp) - .alpha(0.33f) - ) { - Icon(Icons.Outlined.VerticalAlignTop, stringResource(R.string.list_scroll_to_top)) - } + Icon(Icons.Outlined.VerticalAlignTop, stringResource(R.string.list_scroll_to_top)) } } } @@ -330,14 +305,12 @@ fun ListScreenCompact_TODO() { selectedEntries = remember { mutableStateListOf() }, listSettings = listSettings, settingLinkProgressToSubtasks = false, - isPullRefreshEnabled = true, player = null, isListDragAndDropEnabled = true, isSubtaskDragAndDropEnabled = true, onProgressChanged = { _, _ -> }, onClick = { _, _, _ -> }, onLongClick = { _, _ -> }, - onSyncRequested = { }, onSaveListSettings = { }, onUpdateSortOrder = { } ) @@ -397,14 +370,12 @@ fun ListScreenCompact_JOURNAL() { scrollOnceId = MutableLiveData(null), listSettings = listSettings, settingLinkProgressToSubtasks = false, - isPullRefreshEnabled = true, player = null, isListDragAndDropEnabled = true, isSubtaskDragAndDropEnabled = true, onProgressChanged = { _, _ -> }, onClick = { _, _, _ -> }, onLongClick = { _, _ -> }, - onSyncRequested = { }, onSaveListSettings = { }, onUpdateSortOrder = { } ) diff --git a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenGrid.kt b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenGrid.kt index 6bc70bfe3..728079f9f 100644 --- a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenGrid.kt +++ b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenGrid.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells @@ -27,11 +26,8 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.VerticalAlignTop import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.pulltorefresh.PullToRefreshContainer -import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -46,7 +42,6 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -71,7 +66,7 @@ import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.rememberReorderableLazyStaggeredGridState -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalFoundationApi::class) @Composable fun ListScreenGrid( list: List, @@ -81,14 +76,12 @@ fun ListScreenGrid( selectedEntries: SnapshotStateList, scrollOnceId: MutableLiveData, settingLinkProgressToSubtasks: Boolean, - isPullRefreshEnabled: Boolean, markdownEnabled: Boolean, player: MediaPlayer?, isListDragAndDropEnabled: Boolean, onProgressChanged: (itemId: Long, newPercent: Int) -> Unit, onClick: (itemId: Long, list: List, isReadOnly: Boolean) -> Unit, - onLongClick: (itemId: Long, isReadOnly: Boolean) -> Unit, - onSyncRequested: () -> Unit + onLongClick: (itemId: Long, isReadOnly: Boolean) -> Unit ) { val context = LocalContext.current @@ -115,45 +108,28 @@ fun ListScreenGrid( } } - val pullToRefreshState = rememberPullToRefreshState( - enabled = { isPullRefreshEnabled } - ) - LaunchedEffect(pullToRefreshState.isRefreshing) { - if(pullToRefreshState.isRefreshing) { - onSyncRequested() - pullToRefreshState.endRefresh() - } - } - - Box( - modifier = Modifier - .fillMaxSize() - .nestedScroll(pullToRefreshState.nestedScrollConnection), - contentAlignment = Alignment.TopCenter + LazyVerticalStaggeredGrid( + state = gridState, + columns = StaggeredGridCells.Adaptive(150.dp), + contentPadding = PaddingValues(8.dp), + verticalItemSpacing = 8.dp, + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxSize() ) { + items( + items = list, + key = { item -> item.iCal4List.id } + ) + { iCal4ListRelObject -> - LazyVerticalStaggeredGrid( - state = gridState, - columns = StaggeredGridCells.Adaptive(150.dp), - contentPadding = PaddingValues(8.dp), - verticalItemSpacing = 8.dp, - horizontalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier.fillMaxSize() - ) { - items( - items = list, - key = { item -> item.iCal4List.id } - ) - { iCal4ListRelObject -> - - val currentSubtasks = - subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } - .map { it.iCal4List } + val currentSubtasks = + subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } + .map { it.iCal4List } - ReorderableItem( - state = reorderableLazyListState, - key = iCal4ListRelObject.iCal4List.id - ) { + ReorderableItem( + state = reorderableLazyListState, + key = iCal4ListRelObject.iCal4List.id + ) { ListCardGrid( iCal4ListRelObject.iCal4List, @@ -188,30 +164,22 @@ fun ListScreenGrid( } } - PullToRefreshContainer( - modifier = Modifier - .align(Alignment.TopCenter) - .offset(y = (-30).dp), - state = pullToRefreshState, - ) - - Crossfade(gridState.canScrollBackward, label = "showScrollUp") { - if (it) { - Box( - contentAlignment = Alignment.BottomCenter, - modifier = Modifier.fillMaxSize() + Crossfade(gridState.canScrollBackward, label = "showScrollUp") { + if (it) { + Box( + contentAlignment = Alignment.BottomCenter, + modifier = Modifier.fillMaxSize() + ) { + Button( + onClick = { + scope.launch { gridState.scrollToItem(0) } + }, + colors = ButtonDefaults.filledTonalButtonColors(), + modifier = Modifier + .padding(8.dp) + .alpha(0.33f) ) { - Button( - onClick = { - scope.launch { gridState.scrollToItem(0) } - }, - colors = ButtonDefaults.filledTonalButtonColors(), - modifier = Modifier - .padding(8.dp) - .alpha(0.33f) - ) { - Icon(Icons.Outlined.VerticalAlignTop, stringResource(R.string.list_scroll_to_top)) - } + Icon(Icons.Outlined.VerticalAlignTop, stringResource(R.string.list_scroll_to_top)) } } } @@ -263,13 +231,11 @@ fun ListScreenGrid_TODO() { selectedEntries = remember { mutableStateListOf() }, scrollOnceId = MutableLiveData(null), settingLinkProgressToSubtasks = false, - isPullRefreshEnabled = true, markdownEnabled = false, player = null, onProgressChanged = { _, _ -> }, onClick = { _, _, _ -> }, onLongClick = { _, _ -> }, - onSyncRequested = { }, isListDragAndDropEnabled = true ) } @@ -320,13 +286,11 @@ fun ListScreenGrid_JOURNAL() { selectedEntries = remember { mutableStateListOf() }, scrollOnceId = MutableLiveData(null), settingLinkProgressToSubtasks = false, - isPullRefreshEnabled = true, markdownEnabled = false, player = null, onProgressChanged = { _, _ -> }, onClick = { _, _, _ -> }, onLongClick = { _, _ -> }, - onSyncRequested = { }, isListDragAndDropEnabled = false ) } diff --git a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenKanban.kt b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenKanban.kt index 6b2cd0a7b..714fa43fb 100644 --- a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenKanban.kt +++ b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenKanban.kt @@ -22,20 +22,15 @@ import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.rememberDraggableState import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.offset import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.pulltorefresh.PullToRefreshContainer -import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -50,7 +45,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -73,8 +67,7 @@ import kotlin.math.abs import kotlin.math.roundToInt -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class -) +@OptIn(ExperimentalFoundationApi::class) @Composable fun ListScreenKanban( module: Module, @@ -88,15 +81,13 @@ fun ListScreenKanban( kanbanColumnsCategory: SnapshotStateList, scrollOnceId: MutableLiveData, settingLinkProgressToSubtasks: Boolean, - isPullRefreshEnabled: Boolean, markdownEnabled: Boolean, player: MediaPlayer?, onStatusChanged: (itemid: Long, status: Status, scrollOnce: Boolean) -> Unit, onXStatusChanged: (itemid: Long, status: ExtendedStatus, scrollOnce: Boolean) -> Unit, onSwapCategories: (itemid: Long, old: String, new: String) -> Unit, onClick: (itemId: Long, list: List, isReadOnly: Boolean) -> Unit, - onLongClick: (itemId: Long, isReadOnly: Boolean) -> Unit, - onSyncRequested: () -> Unit + onLongClick: (itemId: Long, isReadOnly: Boolean) -> Unit ) { val context = LocalContext.current @@ -134,69 +125,55 @@ fun ListScreenKanban( } } - val pullToRefreshState = rememberPullToRefreshState( - enabled = { isPullRefreshEnabled } - ) - LaunchedEffect(pullToRefreshState.isRefreshing) { - if(pullToRefreshState.isRefreshing) { - onSyncRequested() - pullToRefreshState.endRefresh() - } - } - - Box( - modifier = Modifier.fillMaxSize().nestedScroll(pullToRefreshState.nestedScrollConnection), - contentAlignment = Alignment.TopCenter - ) { - Row(modifier = Modifier.fillMaxWidth()) { + Row(modifier = Modifier.fillMaxWidth()) { - columns.forEachIndexed { index, column -> + columns.forEachIndexed { index, column -> - val listState = rememberLazyListState() + val listState = rememberLazyListState() - if (scrollId != null) { - LaunchedEffect(list) { - val itemIndex = groupedList[column]?.indexOfFirst { iCal4ListRelObject -> iCal4ListRelObject.iCal4List.id == scrollId } ?: -1 - if (itemIndex > -1) { - listState.scrollToItem(itemIndex) - scrollOnceId.postValue(null) - } + if (scrollId != null) { + LaunchedEffect(list) { + val itemIndex = groupedList[column]?.indexOfFirst { iCal4ListRelObject -> iCal4ListRelObject.iCal4List.id == scrollId } ?: -1 + if (itemIndex > -1) { + listState.scrollToItem(itemIndex) + scrollOnceId.postValue(null) } } + } - LazyColumn( - state = listState, - contentPadding = PaddingValues(4.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth().weight(1F) - ) { - - stickyHeader { - Text( - text = column, - modifier = Modifier - .align(Alignment.CenterVertically) - .background(MaterialTheme.colorScheme.background) - .fillMaxWidth(), - style = MaterialTheme.typography.titleSmall, - textAlign = TextAlign.Center - ) - } + LazyColumn( + state = listState, + contentPadding = PaddingValues(4.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth().weight(1F) + ) { - items( - items = groupedList[column]?: emptyList(), - key = { item -> item.iCal4List.id } + stickyHeader { + Text( + text = column, + modifier = Modifier + .align(Alignment.CenterVertically) + .background(MaterialTheme.colorScheme.background) + .fillMaxWidth(), + style = MaterialTheme.typography.titleSmall, + textAlign = TextAlign.Center ) - { iCal4ListRelObject -> + } - val currentSubtasks = - subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRel.iCal4List.uid } } - .map { it.iCal4List } + items( + items = groupedList[column]?: emptyList(), + key = { item -> item.iCal4List.id } + ) + { iCal4ListRelObject -> - var offsetX by remember { mutableFloatStateOf(0f) } // see https://developer.android.com/jetpack/compose/gestures - val maxOffset = 50f + val currentSubtasks = + subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRel.iCal4List.uid } } + .map { it.iCal4List } + + var offsetX by remember { mutableFloatStateOf(0f) } // see https://developer.android.com/jetpack/compose/gestures + val maxOffset = 50f ListCardKanban( iCal4ListRelObject.iCal4List, @@ -228,57 +205,51 @@ fun ListScreenKanban( onDragStopped = { if (abs(offsetX) > maxOffset / 2 && !iCal4ListRelObject.iCal4List.isReadOnly) { - val draggedToColumn = when { - offsetX < 0f && index > 0 -> index - 1 - offsetX > 0F && index < columns.lastIndex -> index + 1 - else -> { - offsetX = 0f - return@draggable - } + val draggedToColumn = when { + offsetX < 0f && index > 0 -> index - 1 + offsetX > 0F && index < columns.lastIndex -> index + 1 + else -> { + offsetX = 0f + return@draggable } + } - when { - kanbanColumnsXStatus.isNotEmpty() -> storedStatuses - .find { xstatus -> xstatus.module == module && xstatus.xstatus == columns[draggedToColumn] } - ?.let { xstatus -> - onXStatusChanged(iCal4ListRelObject.iCal4List.id, xstatus, true) - } + when { + kanbanColumnsXStatus.isNotEmpty() -> storedStatuses + .find { xstatus -> xstatus.module == module && xstatus.xstatus == columns[draggedToColumn] } + ?.let { xstatus -> + onXStatusChanged(iCal4ListRelObject.iCal4List.id, xstatus, true) + } - kanbanColumnsCategory.isNotEmpty() -> onSwapCategories(iCal4ListRelObject.iCal4List.id, column, columns[draggedToColumn]) + kanbanColumnsCategory.isNotEmpty() -> onSwapCategories(iCal4ListRelObject.iCal4List.id, column, columns[draggedToColumn]) - else -> Status.entries - .find { status -> context.getString(status.stringResource) == columns[draggedToColumn] } - ?.let { status -> - onStatusChanged(iCal4ListRelObject.iCal4List.id, status, true) - } - } + else -> Status.entries + .find { status -> context.getString(status.stringResource) == columns[draggedToColumn] } + ?.let { status -> + onStatusChanged(iCal4ListRelObject.iCal4List.id, status, true) + } + } - // make a short vibration - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val vibratorManager = context.getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager - val vibrator = vibratorManager.defaultVibrator - val vibrationEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK) - vibrator.vibrate(vibrationEffect) - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - @Suppress("DEPRECATION") - val vibrator = context.getSystemService(VIBRATOR_SERVICE) as Vibrator - val vibrationEffect = VibrationEffect.createOneShot(150, 10) - vibrator.vibrate(vibrationEffect) - } + // make a short vibration + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val vibratorManager = context.getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager + val vibrator = vibratorManager.defaultVibrator + val vibrationEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK) + vibrator.vibrate(vibrationEffect) + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + @Suppress("DEPRECATION") + val vibrator = context.getSystemService(VIBRATOR_SERVICE) as Vibrator + val vibrationEffect = VibrationEffect.createOneShot(150, 10) + vibrator.vibrate(vibrationEffect) } - offsetX = 0f } - ), - ) - } + offsetX = 0f + } + ), + ) } } } - - PullToRefreshContainer( - modifier = Modifier.align(Alignment.TopCenter).offset(y = (-30).dp), - state = pullToRefreshState, - ) } } @@ -331,15 +302,13 @@ fun ListScreenKanban_TODO() { kanbanColumnsCategory = remember { mutableStateListOf() }, scrollOnceId = MutableLiveData(null), settingLinkProgressToSubtasks = false, - isPullRefreshEnabled = true, markdownEnabled = false, player = null, onStatusChanged = { _, _, _ -> }, onXStatusChanged = { _, _, _ -> }, onSwapCategories = { _, _, _ -> }, onClick = { _, _, _ -> }, - onLongClick = { _, _ -> }, - onSyncRequested = { } + onLongClick = { _, _ -> } ) } } @@ -393,15 +362,13 @@ fun ListScreenKanban_JOURNAL() { kanbanColumnsCategory = remember { mutableStateListOf() }, scrollOnceId = MutableLiveData(null), settingLinkProgressToSubtasks = false, - isPullRefreshEnabled = true, markdownEnabled = false, player = null, onStatusChanged = { _, _, _ -> }, onXStatusChanged = { _, _, _ -> }, onSwapCategories = { _, _, _ -> }, onClick = { _, _, _ -> }, - onLongClick = { _, _ -> }, - onSyncRequested = { } + onLongClick = { _, _ -> } ) } } diff --git a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenList.kt b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenList.kt index ec30674cf..b87d530cb 100644 --- a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenList.kt +++ b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenList.kt @@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -30,13 +29,10 @@ import androidx.compose.material.icons.outlined.ArrowDropUp import androidx.compose.material.icons.outlined.VerticalAlignTop import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.pulltorefresh.PullToRefreshContainer -import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -51,7 +47,6 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource @@ -81,8 +76,7 @@ import sh.calvin.reorderable.rememberReorderableLazyListState import java.util.UUID -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class -) +@OptIn(ExperimentalFoundationApi::class) @Composable fun ListScreenList( groupedList: Map>, @@ -99,13 +93,13 @@ fun ListScreenList( isSubtasksExpandedDefault: Boolean, isSubnotesExpandedDefault: Boolean, isAttachmentsExpandedDefault: Boolean, + isParentsExpandedDefault: Boolean, settingShowProgressMaintasks: Boolean, settingShowProgressSubtasks: Boolean, settingProgressIncrement: DropdownSettingOption, settingDisplayTimezone: DropdownSettingOption, settingLinkProgressToSubtasks: Boolean, settingIsAccessibilityMode: Boolean, - isPullRefreshEnabled: Boolean, markdownEnabled: Boolean, player: MediaPlayer?, isListDragAndDropEnabled: Boolean, @@ -115,7 +109,6 @@ fun ListScreenList( onLongClick: (itemId: Long, isReadOnly: Boolean) -> Unit, onProgressChanged: (itemId: Long, newPercent: Int) -> Unit, onExpandedChanged: (itemId: Long, isSubtasksExpanded: Boolean, isSubnotesExpanded: Boolean, isParentsExpanded: Boolean, isAttachmentsExpanded: Boolean) -> Unit, - onSyncRequested: () -> Unit, onSaveListSettings: () -> Unit, onUpdateSortOrder: (List) -> Unit ) { @@ -137,95 +130,81 @@ fun ListScreenList( } ICalDatabase.getInstance(context).iCalDatabaseDao().updateSortOrder(reordered.map { it.id }) } - val pullToRefreshState = rememberPullToRefreshState( - enabled = { isPullRefreshEnabled } - ) - LaunchedEffect(pullToRefreshState.isRefreshing) { - if(pullToRefreshState.isRefreshing) { - onSyncRequested() - pullToRefreshState.endRefresh() - } - } - Box( - contentAlignment = Alignment.TopCenter, - modifier = Modifier.fillMaxSize().nestedScroll(pullToRefreshState.nestedScrollConnection) + LazyColumn( + modifier = Modifier.padding(start = 8.dp, end = 8.dp, top = 4.dp), + state = listState, ) { + groupedList.forEach { (groupName, group) -> - LazyColumn( - modifier = Modifier.padding(start = 8.dp, end = 8.dp, top = 4.dp), - state = listState, - ) { - groupedList.forEach { (groupName, group) -> - - if (groupedList.keys.size > 1) { - stickyHeader { + if (groupedList.keys.size > 1) { + stickyHeader { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.background) - ) { - TextButton(onClick = { - if (listSettings.collapsedGroups.contains(groupName)) - listSettings.collapsedGroups.remove(groupName) - else - listSettings.collapsedGroups.add(groupName) - onSaveListSettings() - }) { - Text( - text = groupName, - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(horizontal = 4.dp) - ) + ) { + TextButton(onClick = { + if (listSettings.collapsedGroups.contains(groupName)) + listSettings.collapsedGroups.remove(groupName) + else + listSettings.collapsedGroups.add(groupName) + onSaveListSettings() + }) { + Text( + text = groupName, + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(horizontal = 4.dp) + ) - if (listSettings.collapsedGroups.contains(groupName)) - Icon(Icons.Outlined.ArrowDropUp, stringResource(R.string.list_collapse)) - else - Icon(Icons.Outlined.ArrowDropDown, stringResource(R.string.list_expand)) - } + if (listSettings.collapsedGroups.contains(groupName)) + Icon(Icons.Outlined.ArrowDropUp, stringResource(R.string.list_collapse)) + else + Icon(Icons.Outlined.ArrowDropDown, stringResource(R.string.list_expand)) } } } + } - if (groupedList.keys.size <= 1 || (groupedList.keys.size > 1 && !listSettings.collapsedGroups.contains(groupName))) { - items( - items = group, - key = { item -> - if(listSettings.groupBy.value == GroupBy.CATEGORY || listSettings.groupBy.value == GroupBy.RESOURCE) - item.iCal4List.id.toString() + UUID.randomUUID() - else - item.iCal4List.id - } - ) { iCal4ListRelObject -> - - var currentSubtasks = - subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } - .map { it.iCal4List } - if (listSettings.isExcludeDone.value) // exclude done if applicable - currentSubtasks = - currentSubtasks.filter { subtask -> subtask.percent != 100 && subtask.status != Status.COMPLETED.status } - - val currentSubnotes = - subnotes.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } - .map { it.iCal4List } - val currentParents = parents.filter { iCal4ListRel -> iCal4ListRelObject.relatedto.any { related -> related.text == iCal4ListRel.iCal4List.uid } }.map { it.iCal4List } - val currentAttachments = attachments[iCal4ListRelObject.iCal4List.id] - - if (scrollId != null) { - LaunchedEffect(group) { - val index = - group.indexOfFirst { iCalObject -> iCalObject.iCal4List.id == scrollId } - if (index > -1) { - listState.scrollToItem(index) - scrollOnceId.postValue(null) - } + if (groupedList.keys.size <= 1 || (groupedList.keys.size > 1 && !listSettings.collapsedGroups.contains(groupName))) { + items( + items = group, + key = { item -> + if(listSettings.groupBy.value == GroupBy.CATEGORY || listSettings.groupBy.value == GroupBy.RESOURCE) + item.iCal4List.id.toString() + UUID.randomUUID() + else + item.iCal4List.id + } + ) { iCal4ListRelObject -> + + var currentSubtasks = + subtasks.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } + .map { it.iCal4List } + if (listSettings.isExcludeDone.value) // exclude done if applicable + currentSubtasks = + currentSubtasks.filter { subtask -> subtask.percent != 100 && subtask.status != Status.COMPLETED.status } + + val currentSubnotes = + subnotes.filter { iCal4ListRel -> iCal4ListRel.relatedto.any { relatedto -> relatedto.reltype == Reltype.PARENT.name && relatedto.text == iCal4ListRelObject.iCal4List.uid } } + .map { it.iCal4List } + val currentParents = parents.filter { iCal4ListRel -> iCal4ListRelObject.relatedto.any { related -> related.text == iCal4ListRel.iCal4List.uid } }.map { it.iCal4List } + val currentAttachments = attachments[iCal4ListRelObject.iCal4List.id] + + if (scrollId != null) { + LaunchedEffect(group) { + val index = + group.indexOfFirst { iCalObject -> iCalObject.iCal4List.id == scrollId } + if (index > -1) { + listState.scrollToItem(index) + scrollOnceId.postValue(null) } } + } ReorderableItem(reorderableLazyListState, key = iCal4ListRelObject.iCal4List.id) { ListCard( @@ -243,6 +222,7 @@ fun ListScreenList( isSubtasksExpandedDefault = isSubtasksExpandedDefault, isSubnotesExpandedDefault = isSubnotesExpandedDefault, isAttachmentsExpandedDefault = isAttachmentsExpandedDefault, + isParentsExpandedDefault = isParentsExpandedDefault, settingShowProgressMaintasks = settingShowProgressMaintasks, settingShowProgressSubtasks = settingShowProgressSubtasks, settingDisplayTimezone = settingDisplayTimezone, @@ -288,26 +268,20 @@ fun ListScreenList( } } - PullToRefreshContainer( - modifier = Modifier.align(Alignment.TopCenter).offset(y = (-30).dp), - state = pullToRefreshState, - ) - - Crossfade(listState.canScrollBackward, label = "showScrollUp") { - if (it) { - Box( - contentAlignment = Alignment.BottomCenter, - modifier = Modifier.fillMaxSize() + Crossfade(listState.canScrollBackward, label = "showScrollUp") { + if (it) { + Box( + contentAlignment = Alignment.BottomCenter, + modifier = Modifier.fillMaxSize() + ) { + Button( + onClick = { + scope.launch { listState.scrollToItem(0) } + }, + colors = ButtonDefaults.filledTonalButtonColors(), + modifier = Modifier.padding(8.dp).alpha(0.33f) ) { - Button( - onClick = { - scope.launch { listState.scrollToItem(0) } - }, - colors = ButtonDefaults.filledTonalButtonColors(), - modifier = Modifier.padding(8.dp).alpha(0.33f) - ) { - Icon(Icons.Outlined.VerticalAlignTop, stringResource(R.string.list_scroll_to_top)) - } + Icon(Icons.Outlined.VerticalAlignTop, stringResource(R.string.list_scroll_to_top)) } } } @@ -371,13 +345,13 @@ fun ListScreenList_TODO() { isSubtasksExpandedDefault = true, isSubnotesExpandedDefault = true, isAttachmentsExpandedDefault = true, + isParentsExpandedDefault = true, settingShowProgressMaintasks = true, settingShowProgressSubtasks = true, settingProgressIncrement = DropdownSettingOption.PROGRESS_STEP_1, settingLinkProgressToSubtasks = false, settingDisplayTimezone = DropdownSettingOption.DISPLAY_TIMEZONE_LOCAL, settingIsAccessibilityMode = false, - isPullRefreshEnabled = true, markdownEnabled = false, player = null, isListDragAndDropEnabled = true, @@ -388,7 +362,6 @@ fun ListScreenList_TODO() { onLongClick = { _, _ -> }, listSettings = listSettings, onExpandedChanged = { _, _, _, _, _ -> }, - onSyncRequested = { }, onSaveListSettings = { }, onUpdateSortOrder = { } ) @@ -455,13 +428,13 @@ fun ListScreenList_JOURNAL() { isSubtasksExpandedDefault = false, isSubnotesExpandedDefault = false, isAttachmentsExpandedDefault = false, + isParentsExpandedDefault = false, settingShowProgressMaintasks = false, settingShowProgressSubtasks = false, settingProgressIncrement = DropdownSettingOption.PROGRESS_STEP_1, settingDisplayTimezone = DropdownSettingOption.DISPLAY_TIMEZONE_LOCAL, settingLinkProgressToSubtasks = false, settingIsAccessibilityMode = false, - isPullRefreshEnabled = true, markdownEnabled = false, player = null, isListDragAndDropEnabled = true, @@ -472,7 +445,6 @@ fun ListScreenList_JOURNAL() { onLongClick = { _, _ -> }, listSettings = listSettings, onExpandedChanged = { _, _, _, _, _ -> }, - onSyncRequested = { }, onSaveListSettings = { }, onUpdateSortOrder = { } ) diff --git a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenTabContainer.kt b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenTabContainer.kt index 705ab1382..6cf44d325 100644 --- a/app/src/main/java/at/techbee/jtx/ui/list/ListScreenTabContainer.kt +++ b/app/src/main/java/at/techbee/jtx/ui/list/ListScreenTabContainer.kt @@ -17,9 +17,8 @@ import android.location.LocationManager import android.widget.Toast import androidx.biometric.BiometricPrompt import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager @@ -45,6 +44,8 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SheetValue import androidx.compose.material3.Tab import androidx.compose.material3.Text +import androidx.compose.material3.pulltorefresh.PullToRefreshBox +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.material3.rememberDrawerState import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -102,8 +103,7 @@ import kotlin.time.Duration.Companion.seconds @OptIn( - ExperimentalMaterial3Api::class, - ExperimentalFoundationApi::class, ExperimentalPermissionsApi::class + ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class ) @Composable fun ListScreenTabContainer( @@ -186,6 +186,10 @@ fun ListScreenTabContainer( var showUpdateEntriesDialog by rememberSaveable { mutableStateOf(false) } var showCollectionSelectorDialog by rememberSaveable { mutableStateOf(false) } + var isRefreshing by remember { mutableStateOf(false) } + val isPullRefreshEnabled = remember { + SyncUtil.availableSyncApps(context).any { SyncUtil.isSyncAppCompatible(it, context) } && settingsStateHolder.settingSyncOnPullRefresh.value + } val goToEdit = listViewModel.goToEdit.observeAsState() goToEdit.value?.let { icalObjectId -> listViewModel.goToEdit.value = null @@ -661,11 +665,11 @@ fun ListScreenTabContainer( JtxNavigationDrawer( drawerState, mainContent = { - Column { + Column(modifier = Modifier.fillMaxSize()) { if(enabledTabs.size > 1) { PrimaryTabRow( - selectedTabIndex = pagerState.currentPage // adding the indicator might make a smooth movement of the tabIndicator, but Accompanist does not support all components (TODO: Check again in future) https://www.geeksforgeeks.org/tab-layout-in-android-using-jetpack-compose/ + selectedTabIndex = pagerState.currentPage ) { enabledTabs.forEach { enabledTab -> Tab( @@ -761,12 +765,36 @@ fun ListScreenTabContainer( ) } - Box { - HorizontalPager( - state = pagerState, - userScrollEnabled = !filterSheetState.isVisible, - verticalAlignment = Alignment.Top - ) { + HorizontalPager( + state = pagerState, + userScrollEnabled = !filterSheetState.isVisible, + verticalAlignment = Alignment.Top, + modifier = Modifier.fillMaxSize() + ) { page -> + + // TODO: Remove this stupid condition once PullToRefreshBox allows a disabled state! + if (isPullRefreshEnabled) { + PullToRefreshBox( + state = rememberPullToRefreshState(), + onRefresh = { + //TODO: Get rid of this stupid workaround with isRefreshing and delay once the new version properly makes the animation disappear!!! + isRefreshing = true + listViewModel.syncAccounts() + scope.launch { + delay(100) + isRefreshing = false + } + }, + isRefreshing = isRefreshing, + contentAlignment = Alignment.TopCenter, + modifier = Modifier.fillMaxSize() + ) { + ListScreen( + listViewModel = listViewModel, + navController = navController + ) + } + } else { ListScreen( listViewModel = listViewModel, navController = navController diff --git a/app/src/main/java/at/techbee/jtx/ui/list/ListViewModel.kt b/app/src/main/java/at/techbee/jtx/ui/list/ListViewModel.kt index bcb108fde..aeefe9166 100644 --- a/app/src/main/java/at/techbee/jtx/ui/list/ListViewModel.kt +++ b/app/src/main/java/at/techbee/jtx/ui/list/ListViewModel.kt @@ -212,6 +212,9 @@ open class ListViewModel(application: Application, val module: Module) : Android selectFromAllListQuery.postValue(ICal4List.constructQuery( modules = listOf(Module.JOURNAL, Module.NOTE, Module.TODO), searchText = searchText, + flatView = true, + orderBy = OrderBy.LAST_MODIFIED, + sortOrder = SortOrder.DESC, hideBiometricProtected = if(isAuthenticated) emptyList() else ListSettings.getProtectedClassificationsFromSettings(_application) )) } diff --git a/app/src/main/java/at/techbee/jtx/ui/reusable/destinations/NavigationDrawerDestination.kt b/app/src/main/java/at/techbee/jtx/ui/reusable/destinations/NavigationDrawerDestination.kt index ed326e4b7..9015d9b31 100644 --- a/app/src/main/java/at/techbee/jtx/ui/reusable/destinations/NavigationDrawerDestination.kt +++ b/app/src/main/java/at/techbee/jtx/ui/reusable/destinations/NavigationDrawerDestination.kt @@ -24,7 +24,7 @@ enum class NavigationDrawerDestination ( BOARD( titleResource = R.string.navigation_drawer_board, iconRes = R.drawable.ic_widget_jtx, - navigationAction = { navHost, _ -> navHost.popBackStack(BOARD.name, false)} + navigationAction = { navHost, _ -> navHost.navigate(BOARD.name)} ), PRESETS( titleResource = R.string.navigation_drawer_presets, diff --git a/app/src/main/java/at/techbee/jtx/ui/settings/SettingsScreen.kt b/app/src/main/java/at/techbee/jtx/ui/settings/SettingsScreen.kt index f77e6a598..370fbd6d7 100644 --- a/app/src/main/java/at/techbee/jtx/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/at/techbee/jtx/ui/settings/SettingsScreen.kt @@ -71,6 +71,7 @@ import at.techbee.jtx.ui.settings.DropdownSetting.SETTING_PROTECT_BIOMETRIC import at.techbee.jtx.ui.settings.DropdownSetting.SETTING_THEME import at.techbee.jtx.ui.settings.SwitchSetting.SETTING_ACCESSIBILITY_MODE import at.techbee.jtx.ui.settings.SwitchSetting.SETTING_AUTO_EXPAND_ATTACHMENTS +import at.techbee.jtx.ui.settings.SwitchSetting.SETTING_AUTO_EXPAND_PARENTS import at.techbee.jtx.ui.settings.SwitchSetting.SETTING_AUTO_EXPAND_SUBNOTES import at.techbee.jtx.ui.settings.SwitchSetting.SETTING_AUTO_EXPAND_SUBTASKS import at.techbee.jtx.ui.settings.SwitchSetting.SETTING_DISABLE_ALARMS_FOR_READONLY @@ -393,6 +394,13 @@ fun SettingsScreen( settingsStateHolder.settingAutoExpandAttachments.value = it SETTING_AUTO_EXPAND_ATTACHMENTS.saveSetting(it, settingsStateHolder.prefs) }) + SwitchSettingElement( + setting = SETTING_AUTO_EXPAND_PARENTS, + checked = settingsStateHolder.settingAutoExpandParents, + onCheckedChanged = { + settingsStateHolder.settingAutoExpandParents.value = it + SETTING_AUTO_EXPAND_PARENTS.saveSetting(it, settingsStateHolder.prefs) + }) } ExpandableSettingsSection( diff --git a/app/src/main/java/at/techbee/jtx/ui/settings/SettingsStateHolder.kt b/app/src/main/java/at/techbee/jtx/ui/settings/SettingsStateHolder.kt index dd7f0a9cb..46b1423c6 100644 --- a/app/src/main/java/at/techbee/jtx/ui/settings/SettingsStateHolder.kt +++ b/app/src/main/java/at/techbee/jtx/ui/settings/SettingsStateHolder.kt @@ -19,7 +19,7 @@ class SettingsStateHolder(val context: Context) { companion object { private const val SETTINGS_PRO_INFO_SHOWN = "settingsProInfoShown" - private const val PREFS_LAST_MODULE = "lastUsedModule" + const val PREFS_LAST_MODULE = "lastUsedModule" private const val PREFS_DETAIL_TOP_APP_BAR_MODE = "detailTopAppBarMode" } @@ -36,6 +36,7 @@ class SettingsStateHolder(val context: Context) { var settingAutoExpandSubtasks = mutableStateOf(SwitchSetting.SETTING_AUTO_EXPAND_SUBTASKS.getSetting(prefs)) var settingAutoExpandSubnotes = mutableStateOf(SwitchSetting.SETTING_AUTO_EXPAND_SUBNOTES.getSetting(prefs)) var settingAutoExpandAttachments = mutableStateOf(SwitchSetting.SETTING_AUTO_EXPAND_ATTACHMENTS.getSetting(prefs)) + var settingAutoExpandParents = mutableStateOf(SwitchSetting.SETTING_AUTO_EXPAND_PARENTS.getSetting(prefs)) var settingShowProgressForMainTasks = mutableStateOf(SwitchSetting.SETTING_SHOW_PROGRESS_FOR_MAINTASKS.getSetting(prefs)) var settingShowProgressForSubTasks = mutableStateOf(SwitchSetting.SETTING_SHOW_PROGRESS_FOR_SUBTASKS.getSetting(prefs)) diff --git a/app/src/main/java/at/techbee/jtx/ui/settings/SwitchSetting.kt b/app/src/main/java/at/techbee/jtx/ui/settings/SwitchSetting.kt index e82bb1c45..b9a0e2ee6 100644 --- a/app/src/main/java/at/techbee/jtx/ui/settings/SwitchSetting.kt +++ b/app/src/main/java/at/techbee/jtx/ui/settings/SwitchSetting.kt @@ -25,6 +25,7 @@ import androidx.compose.material.icons.outlined.Fullscreen import androidx.compose.material.icons.outlined.MyLocation import androidx.compose.material.icons.outlined.PublishedWithChanges import androidx.compose.material.icons.outlined.RestartAlt +import androidx.compose.material.icons.outlined.SubdirectoryArrowRight import androidx.compose.material.icons.outlined.SwipeDown import androidx.compose.material.icons.outlined.TaskAlt import androidx.compose.material3.Icon @@ -80,6 +81,15 @@ enum class SwitchSetting( title = R.string.settings_default_expand_attachments, default = false ), + SETTING_AUTO_EXPAND_PARENTS( + key = "settings_auto_expand_parents", + icon = { Icon( + Icons.Outlined.SubdirectoryArrowRight, null, modifier = Modifier.padding( + 16.dp + )) }, + title = R.string.settings_default_expand_parents, + default = false + ), SETTING_SHOW_PROGRESS_FOR_MAINTASKS( key = "settings_show_progress_for_maintasks_in_list", icon = { Icon( diff --git a/app/src/main/java/at/techbee/jtx/widgets/ListWidget.kt b/app/src/main/java/at/techbee/jtx/widgets/ListWidget.kt index 3b7507616..2fd749650 100644 --- a/app/src/main/java/at/techbee/jtx/widgets/ListWidget.kt +++ b/app/src/main/java/at/techbee/jtx/widgets/ListWidget.kt @@ -184,6 +184,19 @@ class ListWidget : GlanceAppWidget() { ) } + val defaultOnSurfaceVariantColor = GlanceTheme.colors.onSurfaceVariant + val entryHeaderTextColor = remember(listWidgetConfig) { + if (listWidgetConfig.widgetColorEntries == null) + defaultOnSurfaceVariantColor + else + FixedColorProvider( + if(UiUtil.isDarkColor(entryColor.getColor(context))) + Color.White.copy(alpha = if(listWidgetConfig.widgetAlphaEntries < MIN_ALPHA_FOR_TEXT) MIN_ALPHA_FOR_TEXT else listWidgetConfig.widgetAlphaEntries) + else + Color.Black.copy(alpha = if(listWidgetConfig.widgetAlphaEntries < MIN_ALPHA_FOR_TEXT) MIN_ALPHA_FOR_TEXT else listWidgetConfig.widgetAlphaEntries) + ) + } + GlanceTheme { ListWidgetContent( listWidgetConfig, @@ -194,6 +207,7 @@ class ListWidget : GlanceAppWidget() { textColor = textColor, entryColor = entryColor, entryTextColor = entryTextColor, + entryHeaderTextColor = entryHeaderTextColor, onCheckedChange = { iCalObjectId, checked -> scope.launch(Dispatchers.IO) { val settingsStateHolder = SettingsStateHolder(context) diff --git a/app/src/main/java/at/techbee/jtx/widgets/ListWidgetContent.kt b/app/src/main/java/at/techbee/jtx/widgets/ListWidgetContent.kt index 45761015e..3f66a75e9 100644 --- a/app/src/main/java/at/techbee/jtx/widgets/ListWidgetContent.kt +++ b/app/src/main/java/at/techbee/jtx/widgets/ListWidgetContent.kt @@ -50,6 +50,7 @@ fun ListWidgetContent( textColor: ColorProvider, entryColor: ColorProvider, entryTextColor: ColorProvider, + entryHeaderTextColor: ColorProvider, onCheckedChange: (iCalObjectId: Long, checked: Boolean) -> Unit, onOpenWidgetConfig: () -> Unit, onAddNew: () -> Unit, @@ -149,6 +150,7 @@ fun ListWidgetContent( obj = entry.iCal4List, entryColor = entryColor, textColor = entryTextColor, + headerTextColor = entryHeaderTextColor, checkboxPosition = listWidgetConfig.checkboxPosition, showDescription = listWidgetConfig.showDescription, onCheckedChange = onCheckedChange, @@ -177,6 +179,7 @@ fun ListWidgetContent( obj = subtask.iCal4List, entryColor = entryColor, textColor = entryTextColor, + headerTextColor = entryHeaderTextColor, checkboxPosition = listWidgetConfig.checkboxPosition, showDescription = listWidgetConfig.showDescription, onCheckedChange = onCheckedChange, @@ -204,6 +207,7 @@ fun ListWidgetContent( obj = subnote.iCal4List, entryColor = entryColor, textColor = entryTextColor, + headerTextColor = entryHeaderTextColor, checkboxPosition = listWidgetConfig.checkboxPosition, showDescription = listWidgetConfig.showDescription, onCheckedChange = onCheckedChange, diff --git a/app/src/main/java/at/techbee/jtx/widgets/ListWidgetEntry.kt b/app/src/main/java/at/techbee/jtx/widgets/ListWidgetEntry.kt index 1d45c04cc..53af4b4af 100644 --- a/app/src/main/java/at/techbee/jtx/widgets/ListWidgetEntry.kt +++ b/app/src/main/java/at/techbee/jtx/widgets/ListWidgetEntry.kt @@ -50,6 +50,7 @@ fun ListEntry( obj: ICal4List, entryColor: ColorProvider, textColor: ColorProvider, + headerTextColor: ColorProvider, checkboxPosition: CheckboxPosition, showDescription: Boolean, onCheckedChange: (iCalObjectId: Long, checked: Boolean) -> Unit, @@ -57,8 +58,7 @@ fun ListEntry( ) { val context = LocalContext.current - val metaBarColor = ColorProvider(textColor.getColor(context).copy(alpha = 0.7f)) - val textStyleMetaInfo = TextStyle(fontStyle = FontStyle.Italic, fontSize = 12.sp, color = metaBarColor) + val textStyleMetaInfo = TextStyle(fontStyle = FontStyle.Italic, fontSize = 12.sp, color = headerTextColor) val textStyleDateOverdue = textStyleMetaInfo.copy(color = ColorProvider(Color.Red), fontWeight = FontWeight.Bold) val textStyleSummary = TextStyle(fontWeight = FontWeight.Bold, fontSize = 14.sp, color = textColor) val textStyleDescription = TextStyle(color = textColor, fontSize = 12.sp) @@ -108,7 +108,7 @@ fun ListEntry( provider = ImageProvider(if (obj.module == Module.TODO.name) R.drawable.ic_widget_start else R.drawable.ic_start2), contentDescription = context.getString(R.string.started), modifier = GlanceModifier.size(imageSize).padding(end = 4.dp), - colorFilter = ColorFilter.tint(metaBarColor) + colorFilter = ColorFilter.tint(textStyleMetaInfo.color) ) Text( text = ICalObject.getDtstartTextInfo( @@ -128,7 +128,7 @@ fun ListEntry( provider = ImageProvider(R.drawable.ic_widget_due), contentDescription = context.getString(R.string.due), modifier = GlanceModifier.size(imageSize).padding(end = 4.dp), - colorFilter = ColorFilter.tint(metaBarColor) + colorFilter = ColorFilter.tint(textStyleMetaInfo.color) ) Text( text = ICalObject.getDueTextInfo( @@ -149,7 +149,7 @@ fun ListEntry( provider = ImageProvider(R.drawable.ic_priority), contentDescription = context.getString(R.string.priority), modifier = GlanceModifier.size(imageSize).padding(end = 4.dp), - colorFilter = ColorFilter.tint(metaBarColor) + colorFilter = ColorFilter.tint(textStyleMetaInfo.color) ) Text( text = obj.priority.toString(), @@ -164,7 +164,7 @@ fun ListEntry( provider = ImageProvider(R.drawable.ic_status), contentDescription = context.getString(R.string.status), modifier = GlanceModifier.size(imageSize).padding(end = 4.dp), - colorFilter = ColorFilter.tint(metaBarColor) + colorFilter = ColorFilter.tint(textStyleMetaInfo.color) ) Text( text = obj.xstatus @@ -181,7 +181,7 @@ fun ListEntry( provider = ImageProvider(R.drawable.ic_classification), contentDescription = context.getString(R.string.classification), modifier = GlanceModifier.size(imageSize).padding(end = 4.dp), - colorFilter = ColorFilter.tint(metaBarColor) + colorFilter = ColorFilter.tint(textStyleMetaInfo.color) ) Text( text = Classification.getClassificationFromString(obj.classification)?.let { context.getString(it.stringResource) } ?: "", diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 9e1eac891..6fc18439c 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -384,6 +384,7 @@ Gràcies!" "Expandeix les tasques en llista per defecte" "Expandeix les sub-notes en llista per defecte" "Expandeix els adjunts en llista per defecte" + "Expandeix els elements superiors en llistes per defecte" "Disculpa" "Enrere" "Detalls" diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 64c468526..cc54618f9 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -383,6 +383,7 @@ Děkujeme vám!" "Rozbalovat dílčí úkoly v seznamu ve výchozím nastavení" "Rozbalovat dílčí poznámky v seznamu ve výchozím nastavení" "Rozbalovat přílohy v seznamu ve výchozím nastavení" + "Ve výchozím nastavení rozbalovat nadřezené položky" "Omlouváme se" "Zpět" "Podrobnosti" diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d524774f8..fad6691d7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -384,6 +384,7 @@ Danke!" "Unteraufgaben standardmäßig erweitern" "Unternotizen standardmäßig erweitern" "Anhänge standardmäßig erweitern" + "Übergeordnete Einträge in der Liste standardmäßig erweitern" "Sorry" "Zurück" "Details" diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 142f5aeda..79f879cbd 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -382,6 +382,7 @@ "Ανάπτυξη των δευτερευουσών εργασιών στη λίστα από προεπιλογή" "Ανάπτυξη των δευτερευουσών σημειώσεων στη λίστα από προεπιλογή" "Ανάπτυξη των επισυναπτωμένων στην λίστα από προεπιλογή" + "Ανάπτυξη των κύριων επισυναπτωμένων στην λίστα από προεπιλογή" "Συγνώμη" "Πίσω" "Λεπτομέριες" diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e638cbad5..de922d5c8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -358,6 +358,7 @@ "Afficher l'heure dans le fuseau horaire local" "Afficher l'heure dans le fuseau horaire d'origine" "Afficher l'heure en local et dans le fuseau horaire d'origine (vue détaillée seulement)" + "Police" "Vous aimez jtx Board?" "Le développement d'une application est complexe et la maintenance et le support sont des tâches difficiles. Si vous aimez cette application et si vous souhaitez en assurer le développement continu, pensez à faire un don :-) Merci !" @@ -382,6 +383,7 @@ Merci !" "Développer les sous-tâches dans la liste par défaut" "Développer les sous-notes dans la liste par défaut" "Développer les pièces jointes dans la liste par défaut" + "Développer les parents en liste par défaut" "Désolé" "Retour" "Détails" @@ -521,6 +523,7 @@ Merci !" "Alarmes en plein écran" "Affiche les alarmes en plein écran lorsque l'appareil est verrouillé" "Mode d'accessibilité" + "Assurer une taille de police minimale pour les petits élements" "Veuillez autoriser les notifications en plein écran dans les paramètres du système." "Synchroniser au démarrage de l'appli" "Synchroniser lors du tirage au sort" @@ -609,6 +612,7 @@ Merci !" "Mettre à jour le widget" "Note : Ceci est une fonctionnalité expérimentale! Des changements d'opacité réduits dans le thème de couleur peuvent ne pas être reflétés immédiatement!" "Note : Ceci est une fonctionnalité expérimentale ! Les changements dans le thème de couleur ne seront pas reflétés ! Ne choisissez aucune couleur pour revenir à la valeur par défaut du système." + "Définir des catégories par défaut pour les nouvelles entrées depuis le widget" "Opacité" "Général" "Fermer" diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 5e9c86f0f..3677cf570 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,7 +1,7 @@ - "jtx Board sync Diari, Note e Attività" + "jtx Board sincronizza Diari, Note & Attività" "sincronizza il contenuto della jtx Board con altre applicazioni come DAVx⁵" @@ -49,7 +49,7 @@ "Tavola" "Sincronizzazione" "Informazioni / Licenza" - "Diari, Note ed Attività" + "diari, note & attività" "Impostazioni" "Notizie e aggiornamenti" "Link esterni" @@ -102,7 +102,7 @@ "Aggiungi link allegato" "Aggiungi una sottoattività" "Aggiunta rapida sottoattività" - "Aggiunta rapida subnota" + "Aggiunta rapida sottonota" "Aggiungi link allegato" "Ripeti ogni" "in poi" @@ -157,7 +157,7 @@ Mostra l\'avanzamento delle sottoattività nell\'elenco Mostra i progressi per le attività principali "Impostazioni dell'app" - "Seleziona Tipo MI per le note audio" + "Seleziona formato per le note audio" "Sincronizza con %1$s" "jtx Board non fornisce la sincronizzazione del server, ma supporta la sincronizzazione tramite DAVx5. Questo metodo ti permetterà di utilizzare un server CalDAV-compatibile di tua scelta per memorizzare, eseguire il backup e sincronizzare i tuoi dati." @@ -184,7 +184,7 @@ "Grazie a" "jtx Board è un'app open source che apprezza la partecipazione e il contributo della comunità open source. In questa pagina vorremmo attribuire gli sforzi dei membri della comunità per le traduzioni." "Se desideri modificare/aggiungere traduzioni o aggiungere nuove lingue a questa app, sentiti libero di unirti al progetto jtx Board sulla piattaforma di traduzione Crowdin:" - "Partecipa a Crowdin.com" + "Partecipa su Crowdin.com" "Congratulazioni, questa è la tua prima annotazione nel diario :-)" "I Diari possono essere utilizzati per conservare i tuoi appunti per una data specifica. Utilizzare questa funzionalità ad esempio per i protocolli, i verbali delle riunioni, le voci del diario e così via. Se modifichi questa voce, vedrai le opzioni per aggiungere categorie, partecipanti, allegati, … Sentiti libero di eliminare questa voce quando vuoi!" "Congratulazioni, questa è la tua prima nota :-)" @@ -192,7 +192,7 @@ "Congratulazioni, questa è la tua prima voce di Attività :-)" "Oltre ai Diari e alle Note, le Attività possono avere un inizio pianificato, una data di scadenza e una data di completamento. Possono essere verificati al termine o è possibile impostare un avanzamento. Modifica questa voce per controllare tutte le opzioni :-)" "#PrimiPassi" - "Salva e modifica" + "Salva & modifica" "Salva & nuovo" "Salva & chiudi" "Registra allegato audio" @@ -200,7 +200,7 @@ "Aggiungere contenuto a un Diario, una Nota o un'Attività?" "Aggiungi" "Modifica" - "Resettare" + "Reimposta" "Applica" "n/d" "Collezione" @@ -217,7 +217,7 @@ "All'inizio" "Alla scadenza" "prima dell'inizio" - "Dopo l'inizio" + "dopo l'inizio" "prima della scadenza" "dopo la scadenza" "minuti" @@ -235,7 +235,7 @@ "Eliminare \"%1$s\"?" "La cancellazione di una collezione cancellerà anche tutti i diari, le note e le attività all'interno di questa collezione. Questo non può essere annullato. Sei sicuro di voler continuare?" "Aggiungi collezione locale" - "Notizie e aggiornamenti" + "Notizie & Note di Rilascio" "Colore" "Nessuna priorità" "1 - Più alto" @@ -273,7 +273,7 @@ "Relativo a" "Commenti" "Allegati" - "Notificazioni" + "Notifiche" "Ricorrenza" "Eccezioni" "Aggiunte" @@ -319,6 +319,7 @@ Senza risorse Ordina sottoattività per Ordina note collegate per + "Trascina & Rilascia" "Nessun filtro" "Altro (%1$d)" "Meno" @@ -382,6 +383,7 @@ Grazie!" "Espansione predefinita delle sottoattività nell'elenco" "Espansione predefinita delle note secondarie nell'elenco" "Espansione predefinita degli allegati nell'elenco" + "Espansione predefinita dei genitori nell'elenco" "Spiacente" "Indietro" "Dettagli" @@ -392,9 +394,12 @@ Grazie!" "Inizio (giorno)" "Inizio (settimana)" "Inizio (mese)" + "Scadenza (giorno)" "Scadenza (settimana)" "Scadenza (mese)" "Settimana %1$d/%2$d" + "Riassunto/Descrizione" + "Stato/Classificazione/Priorità" "Fuso orario" "Non impostato" "Inverti selezione" @@ -442,8 +447,8 @@ Grazie!" "Scollega dal genitore" "Rimuovere la relazione come sotto-voce? La voce rimarrà come voce autonoma." "Modifica commento" - "Aggiungere nota secondaria" - "Modifica sottoattività" + "Aggiungere sotto-nota" + "Modifica sotto-attività" "Modifica nota secondaria" "jtx Board visualizza le notifiche quando gli allarmi sono in scadenza. Per utilizzare questa funzione, concedere l'autorizzazione alle notifiche." "jtx Board può usare la vostra posizione per zoomare automaticamente verso la vostra posizione quando si usano le mappe. Se si desidera utilizzare questa funzione, concedere l'autorizzazione alla localizzazione grossolana." @@ -470,16 +475,19 @@ Grazie!" "Staccare questo elemento dalla serie ti permetterà di avere sotto-attività indipendenti o note collegate, ma non ci sarà più link alla serie originale. Vorresti continuare?" "Attenzione: Questa operazione non può essere annullata!" "Scollega dalla serie" + "Applicazione di sincronizzazione obsoleta" "La tua versione attuale di %1$s non è compatibile con questa versione di jtx Board. Si prega di aggiornare a %1$s %2$s o successivamente! La sincronizzazione attraverso %1$s è attualmente sospesa." "Salva la configurazione del filtro corrente" - "Mio filtro" + "Il mio filtro" "Aggiungi/modifica preimpostazione categoria" "Aggiungi/modifica preimpostazione risorsa" "Aggiungi/modifica preimpostato di stato" "Aggiungi voce audio" "Vista compatta" "Vista Kanban" + "Vista per settimana" "Impostazioni delle attività" + "Impostazioni Allarmi" "Impostazioni Attività (stato/progresso)" "Impostazioni Di Sincronizzazione" "Impostare una data di inizio predefinita per le attività" @@ -509,6 +517,8 @@ Grazie!" "Mantieni lo stato, avanzamento & completato in sincronizzazione" "Attenzione: Questa è una funzione sperimentale" "Crea nuove voci con la posizione corrente" + "Concedi l'accesso alla tua posizione durante l'utilizzo di questa app per utilizzare questa funzionalità." + "Allarmi persistenti" "Mantieni le notifiche fino al termine o al rinvio" "Allarmi a schermo intero" "Mostra gli allarmi a schermo intero quando il dispositivo è bloccato" @@ -531,11 +541,15 @@ Grazie!" "Seleziona lingua" "Sistema predefinito" "Filtri speciali" - "Filtro preimpostati" + "Filtri preimpostati" "Si prega di aggiungere/aggiornare/eliminare le preimpostazioni del filtro dal menu filtro nella vista elenco." "Qualsiasi" "Tutti/e" "Nessuno/a" + "Data: %1$s - %2$s" + "Inizi(at)o: %1$s - %2$s" + "Scadenza: %1$s - %2$s" + "Completato: %1$s - %2$s" "Data nel passato" "Oggi" @@ -555,6 +569,7 @@ Grazie!" Voci biometriche protette sbloccate "Allarme automatico" "Sempre in scadenza" + "Sempre al salvataggio (sperimentale)" "Attivare per creare automaticamente gli allarmi. Scegliere ´\"Sempre attivo\" per non creare un allarme, ma per essere avvisati in ogni caso quando un'attività è in scadenza." "Proteggi le voci con la biometria" "Questa funzione nasconde le voci selezionate fino a quando non vengono sbloccate con dati biometrici, ma non crittografa/decifrano i dati in background." @@ -568,7 +583,7 @@ Grazie!" "Senza data di scadenza" "Aggiungere una raccolta locale o sincronizzare una raccolta remota che supporti questo modulo." "Quindi ordinare per" - "Gruppo per" + "Raggruppa per" "Il raggruppamento è disponibile per l'elenco e la vista compatta" "Salva come preimpostato" "Vista piana" @@ -597,6 +612,7 @@ Grazie!" "Aggiornamento del widget" "Nota: Questa è una funzione sperimentale! Con cambiamenti di opacità ridotti nel tema di colore potrebbe non essere riflessa immediatamente!" "Nota: Questa è una funzione sperimentale! Le modifiche al tema dei colori non saranno riflesse! Scegli nessun colore per tornare al valore predefinito di sistema." + "Imposta categorie predefinite per le nuove voci dal widget" "Opacità" "Generale" "Ripiega" @@ -607,8 +623,8 @@ Grazie!" "Sfondo del widget" "Sfondo delle voci" "Mostra descrizione" - "Vero Scuro (AMOLED)" - "Contrasto (e-Inchiostro)" + "Nero Puro (AMOLED)" + "Contrasto (e-Ink)" "Impostazioni diari" "Impostazioni Delle Note" "Impostare una data/ora predefinita per i diari" @@ -640,6 +656,7 @@ Grazie!" \"Stato esteso Filtri attivi: "Kanban" + "Colonne Kanban" basato su stati standard basato su stati estesi basato sulla prima categoria @@ -652,4 +669,12 @@ Grazie!" "jtx Board può avvisarti quando sei vicino alle coordinate date. Si prega di impostare l'autorizzazione di posizione per 'Consenti tutto il tempo' e concedere l'autorizzazione di notifica per utilizzare questa funzione." "Geofence attivato" "jtx Board ha bisogno del permesso di pianificare gli avvisi esatti, altrimenti le notifiche di allarme potrebbero essere ritardate. Si prega di concedere questa autorizzazione per utilizzare questa funzione." + "Chiama %1$s" + "E-mail a %1$s" + Crea sottoattività in massa + Crea note collegate in massa + Il testo sembra contenere più sottoattività. Vuoi creare %d sottoattività? + Il testo sembra contenere più note collegate. Vuoi creare %d note collegate? + Crea multipli + Crea singolo diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 1c461f6fa..b06f2e7b7 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -382,6 +382,7 @@ "デフォルトでサブ ToDo を一覧に展開" "デフォルトでサブメモを一覧に展開" "デフォルトで添付ファイルを一覧に展開" + "デフォルトでリストに親を展開する" "申し訳ありません" "戻る" "詳細" diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index d88a105a4..e2fd248e5 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -383,6 +383,7 @@ Dank u!" "Subtaken standaard uitvouwen in lijst" "Subtaken standaard uitvouwen in lijst" "Subtaken standaard uitvouwen in lijst" + "Bovenliggende elementen automatisch uitklappen in lijst" "Sorry" "Terug" "Details" diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 41114f99a..ee7fa5f22 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -19,7 +19,8 @@ "ਲੰਬਕਾਰ" "ਅਨਲਿੰਕ ਕਰੋ" "ਬੰਦ" - "ਮਨਜ਼ੂਰੀਆਂ" + "ਟਿਕਾਣੇ ਦੀ ਮਨਜ਼ੂਰੀ ਰੋਕੀ ਗਈ" + "ਮਨਜੂਰੀਆਂ" "ਮੌਜੂਦਾ ਟਿਕਾਣਾ" "ਜਰਨਲ ਸ਼ਾਮਲ ਕਰੋ" "ਨੋਟ ਸ਼ਾਮਲ ਕਰੋ" @@ -105,6 +106,8 @@ "ਅਟੈਚਮੈਂਟ ਲਿੰਕ ਜੋੜੋ" "ਰੋਜ਼ਾਨਾ ਦੁਹਰਾਓ" "ਚਾਲੂ" + "ਤੇ" + "ਮਹੀਨੇ ਦਾ ਦਿਨ" "ਅਖੀਰਲਾ" "ਵਾਰੀ" "ਦੁਹਰਾਓ ਨੂੰ ਸੈੱਟ ਕਰਨ ਲਈ ਇੱਕ ਸ਼ੁਰੂਆਤੀ ਮਿਤੀ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ!" @@ -113,6 +116,15 @@ "ਸਮਾਂ ਜੋੜੋ" "ਸਮੇਂ ਵਿੱਚ ਤਰਮੀਮ ਕਰੋ" "ਸਮਾਂ ਖੇਤਰ ਸ਼ਾਮਲ ਕਰੋ" + "ਸਮਾਂ ਖੇਤਰ ਸ਼ਾਮਲ ਕਰੋ" + "\"%1$s\" ਮਿਟਾਓ?" + "ਇਸ ਐਂਟਰੀ ਨੂੰ ਖ਼ਾਰਜ ਕਰਨਾ ਹੈ?" + "ਮੁਕੰਮਲ ਹੋਏ ਸਾਰੇ ਕਾਰਜ ਮਿਟਾਉਣੇ ਹਨ?" + "ਧਿਆਨ ਦਿਓ: ਉਪ-ਕਾਰਜ ਆਪਣੀ ਸਥਿਤੀ ਤੋਂ ਸੁਤੰਤਰ ਤੌਰ 'ਤੇ ਮਿਟਾ ਦਿੱਤੇ ਜਾਂਦੇ ਹਨ ਜੇਕਰ ਉਹਨਾਂ ਦੇ ਪਿੱਤਰ ਕਾਰਜ ਦੀ ਕੀਤੇ ਵਜੋਂ ਨਿਸ਼ਾਨਦੇਹੀ ਕਰ ਦਿੱਤੀ ਗਈ ਹੋਵੇ। ਇਸ ਕਾਰਵਾਈ ਨੂੰ ਅਣਕੀਤਾ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ!" + "ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ 'ਤੇ \"%1$s\" ਨੂੰ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + "ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ 'ਤੇ ਇਸ ਐਂਟਰੀ ਨੂੰ ਰੱਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਹ ਐਂਟਰੀ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤੀ ਜਾਵੇਗੀ।" + "ਨਿਯਤ ਮਿਤੀ ਸ਼ੁਰੂਆਤੀ ਮਿਤੀ ਤੋਂ ਪਹਿਲਾਂ ਨਹੀਂ ਹੋ ਸਕਦੀ।" + "ਐਪ ਦੀ ਇਜਾਜ਼ਤ" diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 903e1b7df..1f6ad98d2 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -207,8 +207,20 @@ "Właściciel: %1$s" "Połącz z zadaniem głównym" "Zadanie główne" + "Podzadanie/-notatka" + "Załącz wybrane jako …" "Kolekcje" + "Szablony" + "Data/Czas" + "Czas trwania" + "Na początku" + "W terminie" + "przed rozpoczęciem" + "po rozpoczęciu" + "przed terminem" + "po terminie" + "Minut" "Godzina(-ny)" "Dzień/Dni" @@ -216,10 +228,14 @@ "Notatki: %1$s" "Zadania: %1$s" + "Kolekcje są zbiorem wpisów podobnie foldery z plikami. Możesz tworzyć nowe lokalne kolekcje za pomocą menu lub zsynchronizować nowe kolekcje zdalne, jeśli masz zainstalowaną aplikację synchronizacji (np. DAVx5)." + "Menu kolekcji" "Dodaj lokalną Kolekcję" "Edytuj lokalną Kolekcję" "Usunąć \"%1$s\"?" + "Usunięcie kolekcji spowoduje również usunięcie wszystkich dzienników, notatek i zadań w tej kolekcji. Tej operacji nie można cofnąć. Czy na pewno chcesz kontynuować?" "Dodaj lokalną Kolekcję" + "Aktualności & Informacje o wersji" "Kolor" "Bez priorytetu" "Najwyższy" @@ -232,16 +248,27 @@ "tydzień/tygodnie" "miesiąc/miesiące" "rok/lata" + "Zapisano pomyślnie" + "Wystąpił błąd podczas zapisywania wybranego pliku!" + "Dodaj zdalną kolekcję (%1$s)" + "Pokaż w %1$s" "Wyeksportuj jako .ics" "Dziennik" "Notatka" "Zadanie" "Konto" + "Stan" "Klasyfikacja" "Kategoria" + "Zasób" "Priorytet" + "Tylko do odczytu" + "Przesyłanie w toku" + "Adres URL" "Lokalizacja" "Uczestnicy" + "Zasoby" + "Organizer" "Kontakt" "Związane z" "Komentarze" @@ -249,17 +276,40 @@ "Alarmy" "Powtarzanie" "Wyjątki" + "Dodatki" "Tytuł" "Opis" "Szukaj" + Szukaj dzienników + Szukaj notatek + Szukaj zadań "Filtr" + "Widoczność" + "Kolejność" "Synchornizuj teraz" + "Do" + "Ukończone" + "Rozpoczęto" + "Dodaj czas" + "Postęp" "Kategorie" "Podzadania" + "Połączenie z zadaniem głównym" "Niski" "Niższy" "Najniższy" + "Termin w przyszłości" + "Ten wpis jest częścią serii." + "Ten wpis powtarza się co" + "Przenieś wszystkie wpisy z \"%1$s\" do" + "Uwaga: Można wybrać tylko kolekcje z możliwością zapisu i kolekcje z obsługą tych samych komponentów (dzienniki, notatki, zadania)." "Przenieść wpisy" + "Brak czasu" + "Nie wybrano daty" + "Bez daty rozpoczęcia" + "Bez terminu zakończenia" + "Bez daty wykonania" + "Pokaż tylko pasujące wpisy podrzędne" "Utworzono" "Ostatnio zmodyfikowano" "Importuj z .ics" @@ -277,6 +327,7 @@ "Dziękujemy za zakup jtx Board Pro!" "Dziękujemy!" "Widok siatki" + "Dzienniki" "Cofnij" "Szczegóły" "Odtwórz" diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 7e4658b32..d6334f112 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -161,10 +161,20 @@ "Sincronizar com %1$s" "jtx Board não fornece sincronização com servidor por si mesmo, mas suporta a sincronização pelo DAVx⁵. Esse método permite que você use qualquer servidor CalDAV de sua escolha para armazenar, fazer backup e sincronizar seus dados." "kSync é um aplicativo baseado no DAVx⁵ desenvolvido por Infomaniak especialmente para seus serviços. Enquanto o DAVx⁵ está aberto a qualquer servidor CalDAV compatível, kSync só permite sincronização com contas Infomaniak." + "Parabéns! :-)" + "Nós detectamos %1$s no seu telefone." + "Instruções de instalação" + "Confira %1$s!" "Não detectamos %1$s no seu telefone." "Saiba mais sobre %1$s" "Baixar %1$s" + "Adicione uma conta em %1$s" + "Falhou em abrir %1$s, por favor, abra o app manualmente." + "Olá, eu sou Patrick, o desenvolvedor do jtx Board. Eu estou dedicando muito tempo e esforço neste app que eu estou oferecendo para você de graça. Se você gosta do meu app, por favor mande algum carinho e faça uma pequena doação!" + "Obrigado!" + "Doar com" + "Se você gostaria de considerar outro método de doação, por favor, visite nosso site:" "Nenhum aplicativo foi encontrado para abrir este arquivo/URL." "Item recorrente agora é uma exceção." "Tarefas concluídas excluídas (%d)." @@ -176,6 +186,8 @@ "Parabéns, esta é a sua primeira entrada no diário :-)" "Os diários podem ser usados para guardar as suas notas para uma data específica. Utilizar esta funcionalidade por exemplo para protocolos, atas de reunião, entradas do diário e assim por diante. Se você editar esta postagem, você verá as opções para adicionar categorias, participantes, anexos, … Sinta-se à vontade para apagar esta entrada quando quiser!" "Parabéns, esta é a sua primeira nota :-)" + "Notas são notas tradicionais e não estão ligadas a uma data específica. Como entradas de diário, você pode adicionar anexos, participantes e mais quando edita ou adiciona uma entrada. Aliás, você pode também adicionar subtarefas a qualquer entrada, não importa se é uma entrada de diário, nota ou tarefa!" + "Parabéns, esta é a sua primeira tarefa :-)" "Vincular a um nível superior" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f111b7d4a..8e3246340 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -397,6 +397,7 @@ Thank you!" "Expand subtasks in list by default" "Expand subnotes in list by default" "Expand attachments in list by default" + "Expand parents in list by default" "https://www.paypal.com/donate?hosted_button_id=8BVX7PUVVTCWY" "https://jtx.techbee.at/contribute" "Sorry" diff --git a/fastlane/metadata/android/en-US/changelogs/209000011.txt b/fastlane/metadata/android/en-US/changelogs/209000011.txt new file mode 100644 index 000000000..61e12aa34 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/209000011.txt @@ -0,0 +1,4 @@ +* Improved alarm handling +* Version updates and several technical upgrades + +Attention: From this version the minimum required Android version is Android 6. Due to dependencies Android 5 is not supported anymore. \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/209010003.txt b/fastlane/metadata/android/en-US/changelogs/209010003.txt new file mode 100644 index 000000000..aaaa697b4 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/209010003.txt @@ -0,0 +1,2 @@ +- Show a notification immediately when an alarm is set in the past +- Allow linking entries to parents that can be sub-entries \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/209020004.txt b/fastlane/metadata/android/en-US/changelogs/209020004.txt new file mode 100644 index 000000000..e5a09da28 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/209020004.txt @@ -0,0 +1,2 @@ +- Open filtered list from collections overview +- Added setting to automatically collapse/expand parents in list \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 329b2e0f5..515fe4330 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,57 +2,57 @@ [versions] amazonAppstoreSdk = "3.0.5" -android-agp = "8.5.0" -android-desugaring = "2.0.4" -androidx-activityCompose = "1.9.0" +android-agp = "8.5.2" +android-desugaring = "2.1.2" +androidx-activityCompose = "1.9.3" androidx-appcompat = "1.7.0" androidx-arch = "2.2.0" androidx-core = "1.13.1" -androidx-lifecycle = "2.8.3" +androidx-lifecycle = "2.8.7" androidx-preference = "1.2.1" androidx-test-core = "1.6.1" -androidx-test-runner = "1.6.1" +androidx-test-runner = "1.6.2" androidx-test-rules = "1.6.1" androidx-test-junit = "1.2.1" -androidx-work = "2.9.0" -annotation = "1.8.0" +androidx-work = "2.9.1" +annotation = "1.9.1" biometricKtx = "1.2.0-alpha05" bitfire-ical4android = "83cda23ceb" -calendarCompose = "2.5.2" -coilCompose = "2.6.0" -compose-accompanist = "0.34.0" -compose-bom = "2024.06.00" -compose-navigation = "2.7.7" -glance = "1.1.0" +calendarCompose = "2.6.0" +coilCompose = "2.7.0" +compose-accompanist = "0.36.0" +compose-bom = "2024.10.01" +compose-navigation = "2.8.3" +glance = "1.1.1" godaddy-colorpicker = "0.7.0" #huawei = "1.8.1.300" #huawei-iap = "6.10.0.300" -kotlin = "2.0.0" -kotlinx-coroutines = "1.8.1" -kotlinxSerializationJson = "1.7.1" +kotlin = "2.0.21" +kotlinx-coroutines = "1.9.0" +kotlinxSerializationJson = "1.7.3" # see https://github.com/google/ksp/releases for version numbers -ksp = "2.0.0-1.0.22" -libphonenumber = "8.13.40" -mapsCompose = "6.0.0" +ksp = "2.0.21-1.0.26" +libphonenumber = "8.13.49" +mapsCompose = "6.1.1" markdowntext = "1.3.2" -mikepenz-aboutLibraries = "11.2.2" +mikepenz-aboutLibraries = "11.2.3" mockitoCore = "5.12.0" -osmdroidAndroid = "6.1.18" +osmdroidAndroid = "6.1.20" playServicesLocation = "21.3.0" playServicesMaps = "19.0.0" -profileinstaller = "1.3.1" -reorderable = "2.1.1" -uiTextGoogleFonts = "1.6.8" +profileinstaller = "1.4.1" +reorderable = "2.4.0" +uiTextGoogleFonts = "1.7.5" room = "2.6.1" volley = "1.2.1" # gplay and managed build variants -android-billing = "7.0.0" -android-review = "2.0.1" +android-billing = "7.1.1" +android-review = "2.0.2" espressoCore = "3.6.1" uiautomator = "2.3.0" -benchmarkMacroJunit4 = "1.2.4" -baselineprofile = "1.2.4" +benchmarkMacroJunit4 = "1.3.3" +baselineprofile = "1.3.3" [libraries] amazon-appstore-sdk = { module = "com.amazon.device:amazon-appstore-sdk", version.ref = "amazonAppstoreSdk" }