diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2ffa6a3a..ea9c077f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,7 +26,7 @@ android { minSdk = sdk.versions.minSdk.get().toInt() targetSdk = sdk.versions.targetSdk.get().toInt() versionCode = versionCodeDate() - versionName = "1.4.2" + versionName = "1.4.3" multiDexEnabled = true signingConfig = signingConfigs.getByName("debug") } diff --git a/build.gradle.kts b/build.gradle.kts index ee2a1325..43f8e21d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.1.0") + classpath("com.android.tools.build:gradle:7.1.1") classpath(libs.kotlin.gradle) classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") // NOTE: Do not place your application dependencies here; they belong diff --git a/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt b/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt index 5088bb12..5a61f8f7 100644 --- a/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt +++ b/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt @@ -35,9 +35,9 @@ abstract class AppDatabase : RoomDatabase() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db) db.execSQL("INSERT INTO Websearch (urlTemplate, label, color, icon) VALUES " + - "('${context.getString(R.string.websearch_google_url)}', '${context.getString(R.string.websearch_google)}', 0xFF4285F4, NULL )," + - "('${context.getString(R.string.websearch_youtube_url)}', '${context.getString(R.string.websearch_youtube)}', 0xFFFF0000, NULL )," + - "('${context.getString(R.string.websearch_playstore_url)}', '${context.getString(R.string.websearch_playstore)}', 0xFF00D3FF, NULL );") + "('${context.getString(R.string.websearch_google_url)}', '${context.getString(R.string.websearch_google)}', 0, NULL )," + + "('${context.getString(R.string.websearch_youtube_url)}', '${context.getString(R.string.websearch_youtube)}', 0, NULL )," + + "('${context.getString(R.string.websearch_playstore_url)}', '${context.getString(R.string.websearch_playstore)}', 0, NULL );") db.execSQL("INSERT INTO Widget (type, data, height, position, label) VALUES " + "('internal', 'weather', -1, 0, '${context.getString(R.string.widget_name_weather)}')," + diff --git a/database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt b/database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt index ef317713..d1584204 100644 --- a/database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt +++ b/database/src/main/java/de/mm20/launcher2/database/WidgetDao.kt @@ -3,11 +3,12 @@ package de.mm20.launcher2.database import androidx.lifecycle.LiveData import androidx.room.* import de.mm20.launcher2.database.entities.WidgetEntity +import kotlinx.coroutines.flow.Flow @Dao interface WidgetDao { @Query("SELECT * FROM Widget ORDER BY position ASC") - fun getWidgets(): List + fun getWidgets(): Flow> @Transaction fun updateWidgets(widgets: List) { diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt index 8dd6444f..2bff334f 100644 --- a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesItem.kt @@ -18,9 +18,10 @@ data class FavoritesItem( var pinPosition: Int, var hidden: Boolean ) : KoinComponent { - private val serializer: SearchableSerializer by inject { parametersOf(searchable) } + private val serializer: SearchableSerializer? by inject { parametersOf(searchable) } - fun toDatabaseEntity(): FavoritesItemEntity { + fun toDatabaseEntity(): FavoritesItemEntity? { + val serializer = serializer ?: return null return FavoritesItemEntity( key = key, diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt index 48aba1f3..585ee9d6 100644 --- a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt @@ -103,7 +103,7 @@ internal class FavoritesRepositoryImpl( pinPosition = 1, hidden = false ) - dao.insertReplaceExisting(favoritesItem.toDatabaseEntity()) + favoritesItem.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) } } } } @@ -132,7 +132,7 @@ internal class FavoritesRepositoryImpl( pinPosition = 0, hidden = true ) - dao.insertReplaceExisting(favoritesItem.toDatabaseEntity()) + favoritesItem.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) } } } } @@ -149,8 +149,10 @@ internal class FavoritesRepositoryImpl( scope.launch { withContext(Dispatchers.IO) { val item = FavoritesItem(searchable.key, searchable, 0, 0, false) - AppDatabase.getInstance(context).searchDao() - .incrementLaunchCount(item.toDatabaseEntity()) + item.toDatabaseEntity()?.let { + AppDatabase.getInstance(context).searchDao() + .incrementLaunchCount(it) + } } } } @@ -167,7 +169,7 @@ internal class FavoritesRepositoryImpl( scope.launch { withContext(Dispatchers.IO) { AppDatabase.getInstance(context).searchDao() - .saveFavorites(favorites.map { it.toDatabaseEntity() }) + .saveFavorites(favorites.mapNotNull { it.toDatabaseEntity() }) } } } diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt index 3759ef60..ea8939dc 100644 --- a/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/Module.kt @@ -50,7 +50,7 @@ val favoritesModule = module { if (searchable is Website) { return@factory WebsiteSerializer() } - throw IllegalArgumentException("No known serializer exists for type ${searchable.javaClass.canonicalName}") + return@factory null } factory { (serialized: String) -> diff --git a/files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt index 792e286b..5bff0dae 100644 --- a/files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt +++ b/files/src/main/java/de/mm20/launcher2/search/data/NextcloudFile.kt @@ -2,20 +2,18 @@ package de.mm20.launcher2.search.data import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.net.Uri -import de.mm20.launcher2.files.R -import de.mm20.launcher2.helper.NetworkUtils -import de.mm20.launcher2.nextcloud.NextcloudApiHelper class NextcloudFile( - fileId: Long, - override val label: String, - path: String, - mimeType: String, - size: Long, - isDirectory: Boolean, - val server: String, - metaData: List> + fileId: Long, + override val label: String, + path: String, + mimeType: String, + size: Long, + isDirectory: Boolean, + val server: String, + metaData: List> ) : File(fileId, path, mimeType, size, isDirectory, metaData) { override val badgeKey: String = "nextcloud://" @@ -28,6 +26,22 @@ class NextcloudFile( return Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("$server/f/$id") flags = Intent.FLAG_ACTIVITY_NEW_TASK + `package` = getNextcloudAppPackage(context) + } + } + + companion object { + private fun getNextcloudAppPackage(context: Context): String? { + val candidates = listOf("com.nextcloud.client", "com.nextcloud.android.beta") + + for (c in candidates) { + try { + context.packageManager.getPackageInfo(c, 0) + return c + } catch (e: PackageManager.NameNotFoundException) { + } + } + return null } } } \ No newline at end of file diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index c4f9cf6f..586f243d 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -210,7 +210,7 @@ Configure icon badges Notification badges Show a badge for applications with unread notifications - Show a badge for suspended applicatoins + Show a badge for suspended applications Suspended apps Cloud badges Show a badge for files that are stored in a cloud diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt index 70f9ad2e..1f9db8df 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt @@ -2,34 +2,29 @@ package de.mm20.launcher2.ui.launcher.widgets import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData import androidx.lifecycle.liveData -import androidx.lifecycle.viewModelScope import de.mm20.launcher2.widgets.Widget import de.mm20.launcher2.widgets.WidgetRepository -import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject -class WidgetsVM: ViewModel(), KoinComponent { +class WidgetsVM : ViewModel(), KoinComponent { private val widgetRepository: WidgetRepository by inject() val isEditMode = MutableLiveData(false) - val widgets = liveData?> { - emit(widgetRepository.getWidgets()) - } + val widgets = widgetRepository.getWidgets().asLiveData() fun setEditMode(editMode: Boolean) { isEditMode.value = editMode } fun saveWidgets(widgets: List) { - viewModelScope.launch { - widgetRepository.saveWidgets(widgets) - } + widgetRepository.saveWidgets(widgets) } - fun getInternalWidgets() : List { + fun getInternalWidgets(): List { return widgetRepository.getInternalWidgets() } } \ No newline at end of file diff --git a/widgets/src/main/java/de/mm20/launcher2/widgets/Module.kt b/widgets/src/main/java/de/mm20/launcher2/widgets/Module.kt index 8a6e4d1b..d1759bc0 100644 --- a/widgets/src/main/java/de/mm20/launcher2/widgets/Module.kt +++ b/widgets/src/main/java/de/mm20/launcher2/widgets/Module.kt @@ -6,5 +6,4 @@ import org.koin.dsl.module val widgetsModule = module { single { WidgetRepository(androidContext()) } - viewModel { WidgetViewModel(get()) } } \ No newline at end of file diff --git a/widgets/src/main/java/de/mm20/launcher2/widgets/WidgetRepository.kt b/widgets/src/main/java/de/mm20/launcher2/widgets/WidgetRepository.kt index 65e3de6e..3d003361 100644 --- a/widgets/src/main/java/de/mm20/launcher2/widgets/WidgetRepository.kt +++ b/widgets/src/main/java/de/mm20/launcher2/widgets/WidgetRepository.kt @@ -3,19 +3,22 @@ package de.mm20.launcher2.widgets import android.content.Context import de.mm20.launcher2.widgets.R import de.mm20.launcher2.database.AppDatabase -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import java.util.concurrent.Executors class WidgetRepository( val context: Context ) { - suspend fun getWidgets(): List { - return withContext(Dispatchers.IO) { - AppDatabase.getInstance(context).widgetDao().getWidgets().map { Widget(it) } - } + private val scope = CoroutineScope(Job() + Dispatchers.Default) + + fun getWidgets(): Flow> { + return AppDatabase.getInstance(context).widgetDao() + .getWidgets() + .map { it.map { Widget(it) } } } fun getInternalWidgets(): List { @@ -27,9 +30,11 @@ class WidgetRepository( } - suspend fun saveWidgets(widgets: List) { - withContext(Dispatchers.IO) { - AppDatabase.getInstance(context).widgetDao().updateWidgets(widgets.mapIndexed { i, widget -> widget.toDatabaseEntity(i) }) + fun saveWidgets(widgets: List) { + scope.launch { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).widgetDao().updateWidgets(widgets.mapIndexed { i, widget -> widget.toDatabaseEntity(i) }) + } } } } \ No newline at end of file diff --git a/widgets/src/main/java/de/mm20/launcher2/widgets/WidgetViewModel.kt b/widgets/src/main/java/de/mm20/launcher2/widgets/WidgetViewModel.kt deleted file mode 100644 index 6a690334..00000000 --- a/widgets/src/main/java/de/mm20/launcher2/widgets/WidgetViewModel.kt +++ /dev/null @@ -1,30 +0,0 @@ -package de.mm20.launcher2.widgets - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class WidgetViewModel( - private val widgetRepository: WidgetRepository -) : ViewModel() { - - - suspend fun getWidgets(): List { - return withContext(viewModelScope.coroutineContext + Dispatchers.IO) { - widgetRepository.getWidgets() - } - } - - fun saveWidgets(widgets: List) { - viewModelScope.launch(Dispatchers.IO) { - widgetRepository.saveWidgets(widgets) - } - } - - fun getInternalWidgets(): List { - return widgetRepository.getInternalWidgets() - } - -} \ No newline at end of file