diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1ad12f795..4fadd4f7d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -178,6 +178,7 @@ dependencies { ksp(libs.androidx.hilt.compiler) implementation(libs.androidx.hilt.work) + implementation(libs.androidx.datastore) implementation(libs.androidx.fragment.ktx) implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.runtime.compose) diff --git a/app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt b/app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt index a1e5d9af3..f114e6844 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt +++ b/app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt @@ -3,6 +3,7 @@ package com.todoroo.astrid.activity import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -32,8 +33,8 @@ import org.tasks.filters.Filter import org.tasks.filters.FilterProvider import org.tasks.filters.NavigationDrawerSubheader import org.tasks.filters.getIcon +import org.tasks.preferences.TasksPreferences import org.tasks.preferences.DefaultFilterProvider -import org.tasks.preferences.Preferences import org.tasks.themes.ColorProvider import timber.log.Timber import javax.inject.Inject @@ -48,7 +49,7 @@ class MainActivityViewModel @Inject constructor( private val inventory: Inventory, private val colorProvider: ColorProvider, private val caldavDao: CaldavDao, - private val preferences: Preferences, + private val tasksPreferences: TasksPreferences, ) : ViewModel() { data class State( @@ -193,7 +194,7 @@ class MainActivityViewModel @Inject constructor( val collapsed = !subheader.isCollapsed when (subheader.subheaderType) { NavigationDrawerSubheader.SubheaderType.PREFERENCE -> { - preferences.setBoolean(subheader.id.toInt(), collapsed) + tasksPreferences.set(booleanPreferencesKey(subheader.id), collapsed) localBroadcastManager.broadcastRefreshList() } NavigationDrawerSubheader.SubheaderType.GOOGLE_TASKS, diff --git a/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt index 1f72cca10..b5df4c9b3 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt @@ -3,6 +3,7 @@ package com.todoroo.astrid.adapter import android.app.Activity import android.content.Intent import androidx.appcompat.app.AppCompatActivity +import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import org.tasks.LocalBroadcastManager @@ -21,15 +22,15 @@ import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.CALDAV import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.GOOGLE_TASKS import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.PREFERENCE import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.TASKS +import org.tasks.preferences.TasksPreferences import org.tasks.location.LocationPickerActivity import org.tasks.preferences.MainPreferences -import org.tasks.preferences.Preferences import timber.log.Timber import javax.inject.Inject class SubheaderClickHandler @Inject constructor( private val activity: Activity, - private val preferences: Preferences, + private val tasksPreferences: TasksPreferences, private val caldavDao: CaldavDao, private val localBroadcastManager: LocalBroadcastManager, ): SubheaderViewHolder.ClickHandler { @@ -37,7 +38,7 @@ class SubheaderClickHandler @Inject constructor( (activity as AppCompatActivity).lifecycleScope.launch { val collapsed = !subheader.isCollapsed when (subheader.subheaderType) { - PREFERENCE -> preferences.setBoolean(subheader.id.toInt(), collapsed) + PREFERENCE -> tasksPreferences.set(booleanPreferencesKey(subheader.id), collapsed) GOOGLE_TASKS, CALDAV, TASKS -> caldavDao.setCollapsed(subheader.id, collapsed) diff --git a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt b/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt deleted file mode 100644 index 4a37b78f5..000000000 --- a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2012 Todoroo Inc - * - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.core - -import org.tasks.R -import org.tasks.data.dao.TaskDao -import org.tasks.filters.Filter -import org.tasks.filters.NotificationsFilter -import org.tasks.filters.RecentlyModifiedFilter -import org.tasks.filters.SnoozedFilter -import org.tasks.filters.TimerFilter -import org.tasks.filters.TodayFilter -import org.tasks.preferences.Preferences -import javax.inject.Inject - -class BuiltInFilterExposer @Inject constructor( - private val preferences: Preferences, - private val taskDao: TaskDao -) { - suspend fun filters(): List { - val filters: MutableList = ArrayList() - if (preferences.getBoolean(R.string.p_show_today_filter, true)) { - filters.add(TodayFilter.create()) - } - if (preferences.getBoolean(R.string.p_show_recently_modified_filter, true)) { - filters.add(RecentlyModifiedFilter.create()) - } - if (taskDao.snoozedReminders() > 0) { - filters.add(SnoozedFilter.create()) - } - if (taskDao.activeTimers() > 0) { - filters.add(TimerFilter.create()) - } - if (taskDao.hasNotifications() > 0) { - filters.add(NotificationsFilter.create()) - } - return filters - } -} diff --git a/app/src/main/java/org/tasks/filters/PreferenceDrawerConfiguration.kt b/app/src/main/java/org/tasks/filters/PreferenceDrawerConfiguration.kt new file mode 100644 index 000000000..766ea00ba --- /dev/null +++ b/app/src/main/java/org/tasks/filters/PreferenceDrawerConfiguration.kt @@ -0,0 +1,33 @@ +package org.tasks.filters + +import org.tasks.R +import org.tasks.compose.drawer.DrawerConfiguration +import org.tasks.preferences.Preferences + +class PreferenceDrawerConfiguration( + private val preferences: Preferences +) : DrawerConfiguration { + override val filtersEnabled: Boolean + get() = preferences.getBoolean(R.string.p_filters_enabled, super.filtersEnabled) + + override val placesEnabled: Boolean + get() = preferences.getBoolean(R.string.p_places_enabled, super.placesEnabled) + + override val hideUnusedPlaces: Boolean + get() = preferences.getBoolean(R.string.p_places_hide_unused, super.hideUnusedPlaces) + + override val tagsEnabled: Boolean + get() = preferences.getBoolean(R.string.p_tags_enabled, super.tagsEnabled) + + override val hideUnusedTags: Boolean + get() = preferences.getBoolean(R.string.p_tags_hide_unused, super.hideUnusedTags) + + override val todayFilter: Boolean + get() = preferences.getBoolean(R.string.p_show_today_filter, super.todayFilter) + + override val recentlyModifiedFilter: Boolean + get() = preferences.getBoolean(R.string.p_show_recently_modified_filter, super.recentlyModifiedFilter) + + override val localListsEnabled: Boolean + get() = preferences.getBoolean(R.string.p_lists_enabled, super.localListsEnabled) +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/injection/ApplicationModule.kt b/app/src/main/java/org/tasks/injection/ApplicationModule.kt index 3c432a7a4..823c054ac 100644 --- a/app/src/main/java/org/tasks/injection/ApplicationModule.kt +++ b/app/src/main/java/org/tasks/injection/ApplicationModule.kt @@ -3,7 +3,6 @@ package org.tasks.injection import android.app.NotificationManager import android.content.Context import androidx.appcompat.app.AppCompatDelegate -import org.tasks.data.db.Database import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,21 +16,28 @@ import org.tasks.billing.BillingClient import org.tasks.billing.BillingClientImpl import org.tasks.billing.Inventory import org.tasks.data.dao.AlarmDao -import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.Astrid2ContentProviderDao +import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.DeletionDao import org.tasks.data.dao.FilterDao import org.tasks.data.dao.GoogleTaskDao import org.tasks.data.dao.GoogleTaskListDao import org.tasks.data.dao.LocationDao +import org.tasks.data.dao.NotificationDao import org.tasks.data.dao.TagDao import org.tasks.data.dao.TagDataDao import org.tasks.data.dao.TaskAttachmentDao import org.tasks.data.dao.TaskDao import org.tasks.data.dao.TaskListMetadataDao import org.tasks.data.dao.UserActivityDao +import org.tasks.data.db.Database +import org.tasks.filters.PreferenceDrawerConfiguration import org.tasks.jobs.WorkManager -import org.tasks.data.dao.NotificationDao +import org.tasks.kmp.createDataStore +import org.tasks.compose.drawer.DrawerConfiguration +import org.tasks.filters.FilterProvider +import org.tasks.preferences.TasksPreferences +import org.tasks.preferences.Preferences import java.util.Locale import javax.inject.Singleton @@ -134,4 +140,34 @@ class ApplicationModule { @Provides fun providesNotificationManager(@ApplicationContext context: Context) = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + @Singleton + @Provides + fun providesTasksPreferences(@ApplicationContext context: Context) = + TasksPreferences(createDataStore(context)) + + @Provides + fun providesDrawerConfiguration(preferences: Preferences): DrawerConfiguration = + PreferenceDrawerConfiguration(preferences) + + @Provides + fun providesFilterProvider( + filterDao: FilterDao, + tagDataDao: TagDataDao, + googleTaskListDao: GoogleTaskListDao, + caldavDao: CaldavDao, + drawerConfiguration: DrawerConfiguration, + locationDao: LocationDao, + taskDao: TaskDao, + tasksPreferences: TasksPreferences, + ) = FilterProvider( + filterDao = filterDao, + tagDataDao = tagDataDao, + googleTaskListDao = googleTaskListDao, + caldavDao = caldavDao, + configuration = drawerConfiguration, + locationDao = locationDao, + taskDao = taskDao, + tasksPreferences = tasksPreferences, + ) } \ No newline at end of file diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index b6b5dfd9e..87e6777d9 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -390,10 +390,6 @@ chips_list chips_tag desaturate_colors - collapse_filters - collapse_debug - collapse_tags - collapse_locations auto_dismiss_datetime_edit_screen auto_dismiss_datetime_list_screen auto_dismiss_datetime_widget diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt index 0feec8916..633a2fb24 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt @@ -55,7 +55,7 @@ abstract class CaldavDao(private val database: Database) { abstract suspend fun getAccount(type: Int, username: String): CaldavAccount? @Query("SELECT * FROM caldav_accounts WHERE cda_id = :id") - abstract suspend fun getAccount(id: Long): CaldavAccount? + abstract suspend fun getAccount(id: String): CaldavAccount? @Query("SELECT * FROM caldav_accounts WHERE cda_id = :id") abstract fun watchAccount(id: Long): Flow @@ -83,7 +83,7 @@ ORDER BY CASE cda_account_type abstract suspend fun getAccounts(): List @Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id") - abstract suspend fun setCollapsed(id: Long, collapsed: Boolean) + abstract suspend fun setCollapsed(id: String, collapsed: Boolean) @Insert abstract suspend fun insert(caldavAccount: CaldavAccount): Long diff --git a/deps_fdroid.txt b/deps_fdroid.txt index 5ab296157..54a9b5432 100644 --- a/deps_fdroid.txt +++ b/deps_fdroid.txt @@ -611,6 +611,56 @@ +| | +--- androidx.compose.material:material-icons-core:1.6.8 (c) +| | \--- androidx.compose.material:material-ripple:1.6.8 (c) +| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) ++| +--- androidx.datastore:datastore-preferences:1.1.1 ++| | \--- androidx.datastore:datastore-preferences-android:1.1.1 ++| | +--- androidx.datastore:datastore:1.1.1 ++| | | \--- androidx.datastore:datastore-android:1.1.1 ++| | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.0 (*) ++| | | +--- androidx.datastore:datastore-core:1.1.1 ++| | | | \--- androidx.datastore:datastore-core-android:1.1.1 ++| | | | +--- androidx.annotation:annotation:1.7.0 -> 1.8.0 (*) ++| | | | +--- org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.22 -> 2.0.0 (*) ++| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | | | +--- androidx.datastore:datastore:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-core-okio:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | | | \--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-core-okio:1.1.1 ++| | | | \--- androidx.datastore:datastore-core-okio-jvm:1.1.1 ++| | | | +--- androidx.datastore:datastore-core:1.1.1 (*) ++| | | | +--- com.squareup.okio:okio:3.4.0 -> 3.8.0 ++| | | | | \--- com.squareup.okio:okio-jvm:3.8.0 ++| | | | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.0 (*) ++| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | | | +--- androidx.datastore:datastore:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | | | \--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | | +--- com.squareup.okio:okio:3.4.0 -> 3.8.0 (*) ++| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-core-okio:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | | \--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | +--- androidx.datastore:datastore-preferences-core:1.1.1 ++| | | \--- androidx.datastore:datastore-preferences-core-jvm:1.1.1 ++| | | +--- androidx.datastore:datastore-core:1.1.1 (*) ++| | | +--- androidx.datastore:datastore-core-okio:1.1.1 (*) ++| | | +--- com.squareup.okio:okio:3.4.0 -> 3.8.0 (*) ++| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | +--- androidx.datastore:datastore:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-core-okio:1.1.1 (c) ++| | | \--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | +--- androidx.datastore:datastore:1.1.1 (c) ++| | +--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | \--- androidx.datastore:datastore-core-okio:1.1.1 (c) +| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.2 +| | \--- androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.2 +| | +--- androidx.annotation:annotation:1.8.0 (*) @@ -720,9 +770,7 @@ +| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*) +| +--- org.apache.commons:commons-lang3:3.8.1 -> 3.12.0 +| \--- com.squareup.okhttp3:okhttp:4.10.0 -> 4.12.0 -+| +--- com.squareup.okio:okio:3.6.0 -> 3.8.0 -+| | \--- com.squareup.okio:okio-jvm:3.8.0 -+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.0 (*) ++| +--- com.squareup.okio:okio:3.6.0 -> 3.8.0 (*) +| \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 -> 2.0.0 (*) ++--- com.github.tasks:ical4android:12fe73a +| +--- org.mnode.ical4j:ical4j:3.2.7 @@ -886,6 +934,7 @@ +| | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava +| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.21 -> 2.0.0 (*) +| \--- com.google.dagger:hilt-android:2.49 -> 2.50 (*) +++--- androidx.datastore:datastore-preferences:1.1.1 (*) ++--- androidx.fragment:fragment-ktx:1.8.0 +| +--- androidx.activity:activity-ktx:1.8.1 -> 1.9.0 (*) +| +--- androidx.collection:collection-ktx:1.1.0 -> 1.4.0 (*) diff --git a/deps_googleplay.txt b/deps_googleplay.txt index a2bd6ab80..9a7894cde 100644 --- a/deps_googleplay.txt +++ b/deps_googleplay.txt @@ -375,19 +375,59 @@ +| | | | +--- com.google.firebase:firebase-encoders-json:18.0.0 -> 18.0.1 (*) +| | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.0 (*) +| | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.0 (*) -+| | +--- androidx.datastore:datastore-preferences:1.0.0 -+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.0 (*) -+| | | +--- androidx.datastore:datastore:1.0.0 -+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.0 (*) -+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0 -> 1.8.0 (*) -+| | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.0 (*) -+| | | | \--- androidx.datastore:datastore-core:1.0.0 -+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.0 (*) -+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0 -> 1.8.0 (*) -+| | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.0 (*) -+| | | \--- androidx.datastore:datastore-preferences-core:1.0.0 -+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.0 (*) -+| | | \--- androidx.datastore:datastore-core:1.0.0 (*) ++| | +--- androidx.datastore:datastore-preferences:1.0.0 -> 1.1.1 ++| | | \--- androidx.datastore:datastore-preferences-android:1.1.1 ++| | | +--- androidx.datastore:datastore:1.1.1 ++| | | | \--- androidx.datastore:datastore-android:1.1.1 ++| | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.0 (*) ++| | | | +--- androidx.datastore:datastore-core:1.1.1 ++| | | | | \--- androidx.datastore:datastore-core-android:1.1.1 ++| | | | | +--- androidx.annotation:annotation:1.7.0 -> 1.8.0 (*) ++| | | | | +--- org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.22 -> 2.0.0 ++| | | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) ++| | | | | | \--- org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.0.0 ++| | | | | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) ++| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | | | | +--- androidx.datastore:datastore:1.1.1 (c) ++| | | | | +--- androidx.datastore:datastore-core-okio:1.1.1 (c) ++| | | | | +--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | | | | \--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-core-okio:1.1.1 ++| | | | | \--- androidx.datastore:datastore-core-okio-jvm:1.1.1 ++| | | | | +--- androidx.datastore:datastore-core:1.1.1 (*) ++| | | | | +--- com.squareup.okio:okio:3.4.0 -> 3.8.0 ++| | | | | | \--- com.squareup.okio:okio-jvm:3.8.0 ++| | | | | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.0 (*) ++| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | | | | +--- androidx.datastore:datastore:1.1.1 (c) ++| | | | | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | | | | +--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | | | | \--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | | | +--- com.squareup.okio:okio:3.4.0 -> 3.8.0 (*) ++| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | | | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-core-okio:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | | | \--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-preferences-core:1.1.1 ++| | | | \--- androidx.datastore:datastore-preferences-core-jvm:1.1.1 ++| | | | +--- androidx.datastore:datastore-core:1.1.1 (*) ++| | | | +--- androidx.datastore:datastore-core-okio:1.1.1 (*) ++| | | | +--- com.squareup.okio:okio:3.4.0 -> 3.8.0 (*) ++| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | | +--- androidx.datastore:datastore:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | | | +--- androidx.datastore:datastore-core-okio:1.1.1 (c) ++| | | | \--- androidx.datastore:datastore-preferences:1.1.1 (c) ++| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) ++| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) ++| | | +--- androidx.datastore:datastore:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-preferences-core:1.1.1 (c) ++| | | +--- androidx.datastore:datastore-core:1.1.1 (c) ++| | | \--- androidx.datastore:datastore-core-okio:1.1.1 (c) +| | +--- com.google.android.datatransport:transport-api:3.0.0 (*) +| | \--- androidx.annotation:annotation:1.5.0 -> 1.8.0 (*) +| +--- com.google.android.gms:play-services-tasks:18.0.1 -> 18.2.0 (*) @@ -590,10 +630,7 @@ +| +--- com.google.android.gms:play-services-base:18.5.0 (*) +| +--- com.google.android.gms:play-services-basement:18.4.0 (*) +| \--- com.google.android.gms:play-services-tasks:18.2.0 (*) -++--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 -+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) -+| \--- org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.0.0 -+| \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) +++--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 (*) ++--- project :data +| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) +| +--- androidx.room:room-runtime:2.7.0-alpha04 @@ -920,6 +957,7 @@ +| | +--- androidx.compose.material:material-icons-core:1.6.8 (c) +| | \--- androidx.compose.material:material-ripple:1.6.8 (c) +| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) ++| +--- androidx.datastore:datastore-preferences:1.1.1 (*) +| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.2 +| | \--- androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.2 +| | +--- androidx.annotation:annotation:1.8.0 (*) @@ -965,9 +1003,7 @@ +| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*) +| +--- org.apache.commons:commons-lang3:3.8.1 -> 3.12.0 +| \--- com.squareup.okhttp3:okhttp:4.10.0 -> 4.12.0 -+| +--- com.squareup.okio:okio:3.6.0 -> 3.8.0 -+| | \--- com.squareup.okio:okio-jvm:3.8.0 -+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.0 (*) ++| +--- com.squareup.okio:okio:3.6.0 -> 3.8.0 (*) +| \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 -> 2.0.0 (*) ++--- com.github.tasks:ical4android:12fe73a +| +--- org.mnode.ical4j:ical4j:3.2.7 @@ -1122,6 +1158,7 @@ +| | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava +| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.21 -> 2.0.0 (*) +| \--- com.google.dagger:hilt-android:2.49 -> 2.50 (*) +++--- androidx.datastore:datastore-preferences:1.1.1 (*) ++--- androidx.fragment:fragment-ktx:1.8.0 +| +--- androidx.activity:activity-ktx:1.8.1 -> 1.9.0 (*) +| +--- androidx.collection:collection-ktx:1.1.0 -> 1.4.0 (*) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4b2741ab2..e54a31f50 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -72,6 +72,7 @@ androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "a androidx-compose = { module = "androidx.compose:compose-bom", version.ref = "compose" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } +androidx-datastore = { module = "androidx.datastore:datastore-preferences", version = "1.1.1" } androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragment-ktx" } androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hilt" } androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "hilt" } diff --git a/kmp/build.gradle.kts b/kmp/build.gradle.kts index e14284483..1d30fb645 100644 --- a/kmp/build.gradle.kts +++ b/kmp/build.gradle.kts @@ -32,6 +32,7 @@ kotlin { implementation(compose.material3) implementation(compose.materialIconsExtended) implementation(compose.runtime) + implementation(libs.androidx.datastore) implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.kermit) implementation(libs.kotlinx.datetime) diff --git a/kmp/src/androidMain/kotlin/Platform.android.kt b/kmp/src/androidMain/kotlin/Platform.android.kt index 7ca90e675..940e33d42 100644 --- a/kmp/src/androidMain/kotlin/Platform.android.kt +++ b/kmp/src/androidMain/kotlin/Platform.android.kt @@ -1,6 +1,16 @@ package org.tasks.kmp +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import org.tasks.data.BuildConfig import org.tasks.extensions.formatNumber import java.util.Locale -actual fun formatNumber(number: Int) = Locale.getDefault().formatNumber(number) \ No newline at end of file +actual fun formatNumber(number: Int) = Locale.getDefault().formatNumber(number) + +fun createDataStore(context: Context): DataStore = createDataStore( + producePath = { context.filesDir.resolve(dataStoreFileName).absolutePath } +) + +actual val IS_DEBUG = BuildConfig.DEBUG \ No newline at end of file diff --git a/kmp/src/androidMain/kotlin/org/tasks/previews/TaskListDrawerPreview.kt b/kmp/src/androidMain/kotlin/org/tasks/previews/TaskListDrawerPreview.kt index 661d68d2e..d3901011e 100644 --- a/kmp/src/androidMain/kotlin/org/tasks/previews/TaskListDrawerPreview.kt +++ b/kmp/src/androidMain/kotlin/org/tasks/previews/TaskListDrawerPreview.kt @@ -34,7 +34,7 @@ fun MenuPreview() { false, false, NavigationDrawerSubheader.SubheaderType.PREFERENCE, - 0L, + "", ), ) ), diff --git a/kmp/src/androidMain/kotlin/org/tasks/previews/pickers/SearchableFilterPickerPreviews.kt b/kmp/src/androidMain/kotlin/org/tasks/previews/pickers/SearchableFilterPickerPreviews.kt index b6ae4f606..4475e3c08 100644 --- a/kmp/src/androidMain/kotlin/org/tasks/previews/pickers/SearchableFilterPickerPreviews.kt +++ b/kmp/src/androidMain/kotlin/org/tasks/previews/pickers/SearchableFilterPickerPreviews.kt @@ -26,7 +26,7 @@ fun FilterPickerPreview() { false, false, NavigationDrawerSubheader.SubheaderType.PREFERENCE, - 0L, + "", ), ), query = "", diff --git a/kmp/src/commonMain/composeResources/values/strings.xml b/kmp/src/commonMain/composeResources/values/strings.xml index 98b0c216f..196926440 100644 --- a/kmp/src/commonMain/composeResources/values/strings.xml +++ b/kmp/src/commonMain/composeResources/values/strings.xml @@ -14,4 +14,8 @@ Default list Help & feedback Settings + Filters + Tags + Places + Local lists \ No newline at end of file diff --git a/kmp/src/commonMain/kotlin/Platform.kt b/kmp/src/commonMain/kotlin/Platform.kt index 3ed21dcd0..3cf564323 100644 --- a/kmp/src/commonMain/kotlin/Platform.kt +++ b/kmp/src/commonMain/kotlin/Platform.kt @@ -1,3 +1,17 @@ package org.tasks.kmp -expect fun formatNumber(number: Int): String \ No newline at end of file +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.core.Preferences +import okio.Path.Companion.toPath + +expect fun formatNumber(number: Int): String + +expect val IS_DEBUG: Boolean + +fun createDataStore(producePath: () -> String): DataStore = + PreferenceDataStoreFactory.createWithPath( + produceFile = { producePath().toPath() } + ) + +const val dataStoreFileName = "tasks.preferences_pb" \ No newline at end of file diff --git a/kmp/src/commonMain/kotlin/org/tasks/compose/drawer/DrawerConfiguration.kt b/kmp/src/commonMain/kotlin/org/tasks/compose/drawer/DrawerConfiguration.kt new file mode 100644 index 000000000..b48aabcc5 --- /dev/null +++ b/kmp/src/commonMain/kotlin/org/tasks/compose/drawer/DrawerConfiguration.kt @@ -0,0 +1,27 @@ +package org.tasks.compose.drawer + +interface DrawerConfiguration { + val filtersEnabled: Boolean + get() = true + + val placesEnabled: Boolean + get() = true + + val hideUnusedPlaces: Boolean + get() = false + + val tagsEnabled: Boolean + get() = true + + val hideUnusedTags: Boolean + get() = false + + val todayFilter: Boolean + get() = true + + val recentlyModifiedFilter: Boolean + get() = true + + val localListsEnabled: Boolean + get() = true +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/LocationFiltersExtensions.kt b/kmp/src/commonMain/kotlin/org/tasks/data/LocationFiltersExtensions.kt similarity index 100% rename from app/src/main/java/org/tasks/data/LocationFiltersExtensions.kt rename to kmp/src/commonMain/kotlin/org/tasks/data/LocationFiltersExtensions.kt diff --git a/app/src/main/java/org/tasks/data/TagFiltersExtensions.kt b/kmp/src/commonMain/kotlin/org/tasks/data/TagFiltersExtensions.kt similarity index 100% rename from app/src/main/java/org/tasks/data/TagFiltersExtensions.kt rename to kmp/src/commonMain/kotlin/org/tasks/data/TagFiltersExtensions.kt diff --git a/app/src/main/java/org/tasks/filters/FilterProvider.kt b/kmp/src/commonMain/kotlin/org/tasks/filters/FilterProvider.kt similarity index 72% rename from app/src/main/java/org/tasks/filters/FilterProvider.kt rename to kmp/src/commonMain/kotlin/org/tasks/filters/FilterProvider.kt index ee975f1a4..22dd6d1f0 100644 --- a/app/src/main/java/org/tasks/filters/FilterProvider.kt +++ b/kmp/src/commonMain/kotlin/org/tasks/filters/FilterProvider.kt @@ -1,10 +1,6 @@ package org.tasks.filters -import android.content.Context -import com.todoroo.astrid.core.BuiltInFilterExposer -import dagger.hilt.android.qualifiers.ApplicationContext -import org.tasks.BuildConfig -import org.tasks.R +import org.jetbrains.compose.resources.getString import org.tasks.data.GoogleTaskFilters import org.tasks.data.LocationFilters import org.tasks.data.NO_ORDER @@ -14,26 +10,37 @@ import org.tasks.data.dao.FilterDao import org.tasks.data.dao.GoogleTaskListDao import org.tasks.data.dao.LocationDao import org.tasks.data.dao.TagDataDao +import org.tasks.data.dao.TaskDao import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount.Companion.TYPE_LOCAL import org.tasks.data.entity.CaldavAccount.Companion.TYPE_OPENTASKS import org.tasks.data.setupLocalAccount import org.tasks.data.toGtasksFilter +import org.tasks.filters.NavigationDrawerSubheader.SubheaderType +import org.tasks.kmp.IS_DEBUG +import org.tasks.compose.drawer.DrawerConfiguration import org.tasks.data.toLocationFilter import org.tasks.data.toTagFilter -import org.tasks.filters.NavigationDrawerSubheader.SubheaderType -import org.tasks.preferences.Preferences -import javax.inject.Inject +import org.tasks.preferences.TasksPreferences +import org.tasks.preferences.TasksPreferences.Companion.collapseDebug +import org.tasks.preferences.TasksPreferences.Companion.collapseFilters +import org.tasks.preferences.TasksPreferences.Companion.collapsePlaces +import org.tasks.preferences.TasksPreferences.Companion.collapseTags +import tasks.kmp.generated.resources.Res +import tasks.kmp.generated.resources.drawer_filters +import tasks.kmp.generated.resources.drawer_local_lists +import tasks.kmp.generated.resources.drawer_places +import tasks.kmp.generated.resources.drawer_tags -class FilterProvider @Inject constructor( - @param:ApplicationContext private val context: Context, - private val builtInFilterExposer: BuiltInFilterExposer, +class FilterProvider( private val filterDao: FilterDao, private val tagDataDao: TagDataDao, private val googleTaskListDao: GoogleTaskListDao, private val caldavDao: CaldavDao, - private val preferences: Preferences, - private val locationDao: LocationDao + private val configuration: DrawerConfiguration, + private val locationDao: LocationDao, + private val taskDao: TaskDao, + private val tasksPreferences: TasksPreferences, ) { suspend fun listPickerItems(): List = caldavFilters(showCreate = false, forceExpand = false) @@ -41,7 +48,6 @@ class FilterProvider @Inject constructor( suspend fun drawerItems(): List = getAllFilters(showCreate = true, hideUnused = true) - suspend fun allLists(): List = caldavFilters(showCreate = false, forceExpand = true) .filterIsInstance() @@ -56,27 +62,27 @@ class FilterProvider @Inject constructor( suspend fun drawerCustomizationItems(): List = getAllFilters(showBuiltIn = false, showCreate = true) - private fun getDebugFilters(): List = - if (BuildConfig.DEBUG) { - val collapsed = preferences.getBoolean(R.string.p_collapse_debug, false) + private suspend fun getDebugFilters(): List = + if (IS_DEBUG) { + val collapsed = tasksPreferences.get(collapseDebug, false) listOf( NavigationDrawerSubheader( - context.getString(R.string.debug), + "Debug", false, collapsed, SubheaderType.PREFERENCE, - R.string.p_collapse_debug.toLong(), + collapseDebug.name, ) ) .apply { if (collapsed) return this } .plus(listOf( - DebugFilters.getNoListFilter(), - DebugFilters.getNoTitleFilter(), - DebugFilters.getMissingListFilter(), - DebugFilters.getMissingAccountFilter(), - DebugFilters.getNoCreateDateFilter(), - DebugFilters.getNoModificationDateFilter(), - DebugFilters.getDeleted() + DebugFilters.getNoListFilter(), + DebugFilters.getNoTitleFilter(), + DebugFilters.getMissingListFilter(), + DebugFilters.getMissingAccountFilter(), + DebugFilters.getNoCreateDateFilter(), + DebugFilters.getNoModificationDateFilter(), + DebugFilters.getDeleted() )) } else { @@ -88,23 +94,23 @@ class FilterProvider @Inject constructor( showBuiltIn: Boolean, forceExpand: Boolean, ): List = - if (!preferences.getBoolean(R.string.p_filters_enabled, true)) { + if (!configuration.filtersEnabled) { emptyList() } else { - val collapsed = !forceExpand && preferences.getBoolean(R.string.p_collapse_filters, false) + val collapsed = !forceExpand && tasksPreferences.get(collapseFilters, false) listOf( NavigationDrawerSubheader( - context.getString(R.string.filters), + getString(Res.string.drawer_filters), false, collapsed, SubheaderType.PREFERENCE, - R.string.p_collapse_filters.toLong(), + collapseFilters.name, if (showCreate) REQUEST_NEW_FILTER else 0, ) ) .apply { if (collapsed) return this } .plusAllIf(showBuiltIn) { - builtInFilterExposer.filters() + builtInFilters() } .plus(filterDao.getFilters().map(::CustomFilter).sort()) } @@ -114,23 +120,23 @@ class FilterProvider @Inject constructor( hideUnused: Boolean, forceExpand: Boolean, ): List = - if (!preferences.getBoolean(R.string.p_tags_enabled, true)) { + if (!configuration.tagsEnabled) { emptyList() } else { - val collapsed = !forceExpand && preferences.getBoolean(R.string.p_collapse_tags, false) + val collapsed = !forceExpand && tasksPreferences.get(collapseTags, false) listOf( NavigationDrawerSubheader( - context.getString(R.string.tags), + getString(Res.string.drawer_tags), false, collapsed, SubheaderType.PREFERENCE, - R.string.p_collapse_tags.toLong(), + collapseTags.name, if (showCreate) REQUEST_NEW_TAGS else 0, ) ) .apply { if (collapsed) return this } .plus(tagDataDao.getTagFilters() - .filterIf(hideUnused && preferences.getBoolean(R.string.p_tags_hide_unused, false)) { + .filterIf(hideUnused && configuration.hideUnusedTags) { it.count > 0 } .map(TagFilters::toTagFilter) @@ -142,23 +148,23 @@ class FilterProvider @Inject constructor( hideUnused: Boolean, forceExpand: Boolean, ): List = - if (!preferences.getBoolean(R.string.p_places_enabled, true)) { + if (!configuration.placesEnabled) { emptyList() } else { - val collapsed = !forceExpand && preferences.getBoolean(R.string.p_collapse_locations, false) + val collapsed = !forceExpand && tasksPreferences.get(collapsePlaces, false) listOf( NavigationDrawerSubheader( - context.getString(R.string.places), + getString(Res.string.drawer_places), false, collapsed, SubheaderType.PREFERENCE, - R.string.p_collapse_locations.toLong(), + collapsePlaces.name, if (showCreate) REQUEST_NEW_PLACE else 0, ) ) .apply { if (collapsed) return this } .plus(locationDao.getPlaceFilters() - .filterIf(hideUnused && preferences.getBoolean(R.string.p_places_hide_unused, false)) { + .filterIf(hideUnused && configuration.hideUnusedPlaces) { it.count > 0 } .map(LocationFilters::toLocationFilter) @@ -182,7 +188,7 @@ class FilterProvider @Inject constructor( .plus(addPlaces(showCreate, hideUnused, forceExpand)) .plus(caldavFilters(showCreate, forceExpand)) .toList() - .plusAllIf(BuildConfig.DEBUG) { getDebugFilters() } + .plusAllIf(IS_DEBUG) { getDebugFilters() } private suspend fun googleTaskFilter( account: CaldavAccount, @@ -196,7 +202,7 @@ class FilterProvider @Inject constructor( account.error?.isNotBlank() ?: false, collapsed, SubheaderType.GOOGLE_TASKS, - account.id, + account.id.toString(), if (showCreate) REQUEST_NEW_LIST else 0, ) ) @@ -215,7 +221,7 @@ class FilterProvider @Inject constructor( ): List = caldavDao.getAccounts() .ifEmpty { listOf(caldavDao.setupLocalAccount()) } - .filter { it.accountType != TYPE_LOCAL || preferences.getBoolean(R.string.p_lists_enabled, true) } + .filter { it.accountType != TYPE_LOCAL || configuration.localListsEnabled } .flatMap { if (it.isGoogleTasks) { googleTaskFilter(it, showCreate, forceExpand) @@ -237,7 +243,7 @@ class FilterProvider @Inject constructor( return listOf( NavigationDrawerSubheader( if (account.accountType == TYPE_LOCAL) { - context.getString(R.string.local_lists) + getString(Res.string.drawer_local_lists) } else { account.name }, @@ -247,7 +253,7 @@ class FilterProvider @Inject constructor( account.isTasksOrg -> SubheaderType.TASKS else -> SubheaderType.CALDAV }, - account.id, + account.id.toString(), if (showCreate) REQUEST_NEW_LIST else 0, ) ) @@ -264,6 +270,26 @@ class FilterProvider @Inject constructor( .sort()) } + private suspend fun builtInFilters(): List { + val filters: MutableList = ArrayList() + if (configuration.todayFilter) { + filters.add(TodayFilter.create()) + } + if (configuration.recentlyModifiedFilter) { + filters.add(RecentlyModifiedFilter.create()) + } + if (taskDao.snoozedReminders() > 0) { + filters.add(SnoozedFilter.create()) + } + if (taskDao.activeTimers() > 0) { + filters.add(TimerFilter.create()) + } + if (taskDao.hasNotifications() > 0) { + filters.add(NotificationsFilter.create()) + } + return filters + } + companion object { const val REQUEST_NEW_LIST = 10100 const val REQUEST_NEW_TAGS = 10101 diff --git a/kmp/src/commonMain/kotlin/org/tasks/filters/NavigationDrawerSubheader.kt b/kmp/src/commonMain/kotlin/org/tasks/filters/NavigationDrawerSubheader.kt index d70715583..99bc9c271 100644 --- a/kmp/src/commonMain/kotlin/org/tasks/filters/NavigationDrawerSubheader.kt +++ b/kmp/src/commonMain/kotlin/org/tasks/filters/NavigationDrawerSubheader.kt @@ -5,7 +5,7 @@ data class NavigationDrawerSubheader( val error: Boolean, val isCollapsed: Boolean, val subheaderType: SubheaderType, - val id: Long, + val id: String, val addIntentRc: Int = 0, ) : FilterListItem { override fun areItemsTheSame(other: FilterListItem): Boolean { diff --git a/kmp/src/commonMain/kotlin/org/tasks/preferences/TasksPreferences.kt b/kmp/src/commonMain/kotlin/org/tasks/preferences/TasksPreferences.kt new file mode 100644 index 000000000..c0383dab0 --- /dev/null +++ b/kmp/src/commonMain/kotlin/org/tasks/preferences/TasksPreferences.kt @@ -0,0 +1,29 @@ +package org.tasks.preferences + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map + +class TasksPreferences(private val dataStore: DataStore) { + + suspend fun get(key: Preferences.Key, defaultValue: T): T = + dataStore.data.map { it[key] }.firstOrNull() ?: defaultValue + + fun flow(key: Preferences.Key, defaultValue: T): Flow = + dataStore.data.map { it[key] ?: defaultValue } + + suspend fun set(key: Preferences.Key, value: T) { + dataStore.edit { it[key] = value } + } + + companion object { + val collapseFilters = booleanPreferencesKey("drawer_collapse_filters") + val collapseTags = booleanPreferencesKey("drawer_collapse_tags") + val collapseDebug = booleanPreferencesKey("drawer_collapse_debug") + val collapsePlaces = booleanPreferencesKey("drawer_collapse_places") + } +} \ No newline at end of file diff --git a/kmp/src/jvmMain/kotlin/Platform.jvm.kt b/kmp/src/jvmMain/kotlin/Platform.jvm.kt index 06e262f3d..e1e28d4dd 100644 --- a/kmp/src/jvmMain/kotlin/Platform.jvm.kt +++ b/kmp/src/jvmMain/kotlin/Platform.jvm.kt @@ -1,3 +1,5 @@ package org.tasks.kmp -actual fun formatNumber(number: Int) = number.toString() \ No newline at end of file +actual fun formatNumber(number: Int) = number.toString() + +actual val IS_DEBUG = true \ No newline at end of file