From 0a39df3e1cfa0f378ae7e4bd70ee7873235dfc87 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Wed, 13 Mar 2024 04:31:18 +0000 Subject: [PATCH 001/102] Fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-es-rMX/strings.xml | 82 +++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index ee27c7c0d3..b07e68d259 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -644,50 +644,96 @@ Actualizar contenido Volver a cargar (remoto) + ¡No se pudo encontrar el archivo! + Puede eliminar el cifrado punto a punto local en este cliente + Puede eliminar el cifrado punto a punto local en este cliente. Los archivos cifrados permanecerán en el servidor, pero no se sincronizarán con esta computadora. Falla al eliminar + Eliminar cuenta local + Eliminar la cuenta del dispositivo y borrar todos los archivos locales + No se pudo eliminar la notificación. Eliminar Eliminado Ingresa un nombre nuevo No se pudo renombrar la copia local, intenta con un nombre diferente No fue posible renombrar, el nombre ya está ocupado Solicitar borrado de cuenta + Solicitar eliminación + Solicitar la eliminación permanente de la cuenta al proveedor del servicio No está permitido recompartir No está permitido recompartir No hay una imagen a escala disponible. ¿Descargar la imagen completa? + Restaurar archivo + Restaurar respaldo Restaurar archivo eliminado + Restaurar seleccionados + Recuperando archivo... + ¡No se pudo cargar el documento! + Iniciar sesión usando un código QR + Escanear página + Protegiendo sus datos + productividad auto alojada + Navegar y compartir + todas las acciones en la punta de sus dedos Actividad, compartidos, ... + todo accesible rápidamente + Todas sus cuentas + en un solo lugar + Carga automática + para sus fotos y videos + Calendario y contactos + Sincronizar con DAVx5 + Error al obtener los resultados de búsqueda + Compartir de forma segura ... Seleccionar todo + Establecer la carpeta multimedia + Por favor, seleccione una plantilla Seleccionar plantilla Enviar + Enviar recurso compartido Ícono de botón de envío Establecer Como + Establecer nota Usar imagen como Establecer estado Establecer mensaje de estado Compartir + Compartir y copiar enlace Compartiendo + %1$s Expira el %1$s Compartir %1$s %1$s (grupo) Compartir enlace interno + en %1$s Compartir liga Debes ingresar una contraseña para compartir este archivo Ingresa una contraseña + Compartir enlace (%1$s) Establece la fecha de expiración Establecer contraseña Protegido con contraseña Puede editar Sólo lectura + Permisos del recurso compartido %1$s (remoto) + %1$s (conversación) + Enviar nuevo correo electrónico + Nota al destinatario Ajustes Ocultar descarga Compartir liga + Enviar enlace Desestablecer Compartir con… compartir compartido + compartido mediante enlace + Compartido con Ud. por %1$s Se presentó una falla al agregar una persona para compartir + Mostrar fotos + Mostrar videos + Registrarse con el proveedor Ordenar por Ocultar Detalles @@ -715,8 +761,17 @@ - Las fechas del certificado del servidor están en el futuro - La URL no corresponde con el nombre del servidor en el certificado Mensaje de estado + Cámara + Elegir la ubicación del almacenamiento Predeterminado + Documentos Descargas + Almacenamiento interno + Películas + Música + Acceso completo + Imágenes + Transmitir con... \"%1$s\" ha sido compartido contigo %1$s ha compartido \"%2$s\" contigo Se encontraron conflictos @@ -842,9 +897,34 @@ Nuevo en %1$s Descargando archivos… Enviar correo electrónico + + %d archivo exportado + %d archivos exportados + %d archivos exportados + + + No se pudo exportar %d archivo + No se pudieron exportar %d archivos + No se pudieron exportar %d archivos + + + %1$d carpeta + %1$d carpetas + %1$d carpetas + %1$d archivo %1$d archivos %1$d archivos - + + Mostrar %1$d carpeta oculta + Mostrar %1$d carpetas ocultas + Mostrar %1$d carpetas ocultas + + + %d seleccionado + %d seleccionados + %d seleccionados + + From c577a0550f5472b9cdf5bb794321661afefb9528 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 26 Feb 2024 16:18:30 +0100 Subject: [PATCH 002/102] Add assistant to the navbar Signed-off-by: alperozturk --- .../android/ui/activity/DrawerActivity.java | 3 ++ .../ui/activity/FileDisplayActivity.java | 6 +++- app/src/main/res/drawable/ic_assistant.xml | 32 +++++++++++++++++++ .../main/res/menu/partial_drawer_entries.xml | 7 ++++ app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_assistant.xml diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 61ad9629e0..f3b7e0ba03 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -542,6 +542,9 @@ public abstract class DrawerActivity extends ToolbarActivity intent.setAction(FileDisplayActivity.LIST_GROUPFOLDERS); intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); + } else if (itemId == R.id.nav_assistant) { + // TODO ADD JETPACK Compose PAGE + Log_OC.w(TAG, "ADD JETPACK Compose PAGE"); } else { if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 4f72a9387b..108a1010b3 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -74,9 +74,14 @@ import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.utils.IntentUtil; +import com.nextcloud.common.NextcloudClient; import com.nextcloud.java.util.Optional; import com.nextcloud.model.WorkerState; import com.nextcloud.model.WorkerStateLiveData; +import com.nextcloud.operations.assistant.AssistantRepository; +import com.nextcloud.operations.assistant.model.CreatedTask; +import com.nextcloud.operations.assistant.model.Ocs; +import com.nextcloud.operations.assistant.model.TaskTypes; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.nextcloud.utils.view.FastScrollUtils; @@ -150,7 +155,6 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Stack; import javax.inject.Inject; diff --git a/app/src/main/res/drawable/ic_assistant.xml b/app/src/main/res/drawable/ic_assistant.xml new file mode 100644 index 0000000000..237c16cc01 --- /dev/null +++ b/app/src/main/res/drawable/ic_assistant.xml @@ -0,0 +1,32 @@ + + + + + diff --git a/app/src/main/res/menu/partial_drawer_entries.xml b/app/src/main/res/menu/partial_drawer_entries.xml index b2e2b89fb0..c9d8d2b600 100644 --- a/app/src/main/res/menu/partial_drawer_entries.xml +++ b/app/src/main/res/menu/partial_drawer_entries.xml @@ -25,11 +25,18 @@ + + + Biggest first Smallest first + + Assistant All files Personal files Home From 6afa518e2ae70891aefc65688739d6dacd5c526f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 26 Feb 2024 16:57:41 +0100 Subject: [PATCH 003/102] Add Jetpack Compose feature Signed-off-by: alperozturk --- app/build.gradle | 11 +++ .../client/assistant/AsssistantScreen.kt | 43 +++++++++++ .../ui/composeFragment/ComposeDestinations.kt | 26 +++++++ .../ui/composeFragment/ComposeFragment.kt | 74 +++++++++++++++++++ .../android/ui/activity/DrawerActivity.java | 25 +++++-- .../main/res/layout/fragment_compose_view.xml | 31 ++++++++ appscan/build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 8 files changed, 208 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt create mode 100644 app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt create mode 100644 app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt create mode 100644 app/src/main/res/layout/fragment_compose_view.xml diff --git a/app/build.gradle b/app/build.gradle index 4108fbdbfc..fe00f041b2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -223,6 +223,7 @@ android { dataBinding true viewBinding true aidl true + compose = true } compileOptions { @@ -246,6 +247,10 @@ android { // Adds exported schema location as test app assets. androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } + + composeOptions { + kotlinCompilerExtensionVersion = "1.5.9" + } } dependencies { @@ -255,6 +260,12 @@ dependencies { exclude group: 'org.ogce', module: 'xpp3' // unused in Android and brings wrong Junit version } + // Jetpack Compose + implementation(platform("androidx.compose:compose-bom:2024.02.00")) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-graphics") + implementation("androidx.compose.material3:material3") + compileOnly 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2' // remove after entire switch to lib v2 implementation "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2 diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt new file mode 100644 index 0000000000..e74f5d9128 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -0,0 +1,43 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun AssistantScreen() { + Scaffold { + LazyColumn(modifier = Modifier.padding(it)) { + stickyHeader { + Text(text = "AssistantScreen") + } + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt new file mode 100644 index 0000000000..8809b1b0c3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.ui.composeFragment + +enum class ComposeDestinations { + AssistantScreen +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt new file mode 100644 index 0000000000..e56f66eaef --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -0,0 +1,74 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.ui.composeFragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.Fragment +import com.nextcloud.client.assistant.AssistantScreen +import com.nextcloud.utils.extensions.getSerializableArgument +import com.owncloud.android.databinding.FragmentComposeViewBinding + +class ComposeFragment : Fragment() { + + private var _binding: FragmentComposeViewBinding? = null + + private val binding get() = _binding!! + private var destination: ComposeDestinations? = null + + companion object { + const val destinationKey = "destinationKey" + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentComposeViewBinding.inflate(inflater, container, false) + destination = arguments.getSerializableArgument(destinationKey, ComposeDestinations::class.java) + + binding.composeView.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + when(destination) { + ComposeDestinations.AssistantScreen -> { + AssistantScreen() + } + else -> { + + } + } + } + } + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index f3b7e0ba03..f4f25e3266 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -74,6 +74,8 @@ import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.NextcloudClient; import com.nextcloud.java.util.Optional; import com.nextcloud.ui.ChooseAccountDialogFragment; +import com.nextcloud.ui.composeFragment.ComposeDestinations; +import com.nextcloud.ui.composeFragment.ComposeFragment; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.PassCodeManager; @@ -404,8 +406,8 @@ public abstract class DrawerActivity extends ToolbarActivity } /** - * Open app store page of specified app or search for specified string. - * Will attempt to open browser when no app store is available. + * Open app store page of specified app or search for specified string. Will attempt to open browser when no app + * store is available. * * @param string packageName or url-encoded search string * @param search false -> show app corresponding to packageName; true -> open search for string @@ -543,7 +545,18 @@ public abstract class DrawerActivity extends ToolbarActivity intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); } else if (itemId == R.id.nav_assistant) { - // TODO ADD JETPACK Compose PAGE + // FIXME Back navigation is broken, create general function to switch to Jetpack Compose + ComposeFragment composeFragment = new ComposeFragment(); + Bundle bundle = new Bundle(); + bundle.putSerializable(ComposeFragment.destinationKey, ComposeDestinations.AssistantScreen); + composeFragment.setArguments( bundle); + + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.left_fragment_container, composeFragment) + .commit(); + + Log_OC.w(TAG, "ADD JETPACK Compose PAGE"); } else { if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && @@ -695,8 +708,8 @@ public abstract class DrawerActivity extends ToolbarActivity /** * Enable or disable interaction with all drawers. * - * @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED}, {@link - * DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}. + * @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED}, + * {@link DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}. */ public void setDrawerLockMode(int lockMode) { if (mDrawerLayout != null) { @@ -1158,7 +1171,7 @@ public abstract class DrawerActivity extends ToolbarActivity return true; } - public AppPreferences getAppPreferences(){ + public AppPreferences getAppPreferences() { return preferences; } diff --git a/app/src/main/res/layout/fragment_compose_view.xml b/app/src/main/res/layout/fragment_compose_view.xml new file mode 100644 index 0000000000..7732bd7b78 --- /dev/null +++ b/app/src/main/res/layout/fragment_compose_view.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/appscan/build.gradle b/appscan/build.gradle index 3e40abb952..8343eadac3 100644 --- a/appscan/build.gradle +++ b/appscan/build.gradle @@ -10,11 +10,11 @@ apply plugin: 'kotlin-android' android { namespace 'com.nextcloud.appscan' - compileSdk 33 + compileSdk 34 defaultConfig { minSdk 21 - targetSdk 33 + targetSdk 34 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5c..52c0aef1db 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +zipStorePath=wrapper/dists \ No newline at end of file From b7b5907f9804b0d6720b17d9fc9c554c040a3f67 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 27 Feb 2024 13:37:31 +0100 Subject: [PATCH 004/102] Add ViewModel Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 79 +++++++++++++++++++ .../client/assistant/AsssistantScreen.kt | 19 +++-- .../ui/composeFragment/ComposeFragment.kt | 15 +++- .../android/ui/activity/DrawerActivity.java | 2 +- 4 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt new file mode 100644 index 0000000000..54528682e7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -0,0 +1,79 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.nextcloud.client.account.User +import com.nextcloud.operations.assistant.AssistantRepository +import com.nextcloud.operations.assistant.model.CreatedTask +import com.nextcloud.operations.assistant.model.TaskTypes +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class AssistantViewModel(context: Context, user: User) : ViewModel() { + + private val repository = AssistantRepository(user, context) + + private val _taskTypes = MutableStateFlow(null) + val taskTypes: StateFlow = _taskTypes + + private val _task = MutableStateFlow(null) + val task: StateFlow = _task + + init { + viewModelScope.launch(Dispatchers.IO) { + _taskTypes.update { + repository.getTaskTypes() + } + } + } + + fun deleteTask(id: String) { + viewModelScope.launch(Dispatchers.IO) { + repository.deleteTask(id) + } + } + + fun getTask(id: String) { + viewModelScope.launch(Dispatchers.IO) { + _task.update { + repository.getTask(id) + } + } + } + + fun createTask( + input: String, + type: String, + appId: String, + identifier: String, + ) { + viewModelScope.launch(Dispatchers.IO) { + repository.createTask(input, type, appId, identifier) + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index e74f5d9128..6cc8f5f8f1 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -22,22 +22,29 @@ package com.nextcloud.client.assistant import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp @OptIn(ExperimentalFoundationApi::class) @Composable -fun AssistantScreen() { - Scaffold { - LazyColumn(modifier = Modifier.padding(it)) { - stickyHeader { - Text(text = "AssistantScreen") - } +fun AssistantScreen(viewModel: AssistantViewModel) { + val taskTypes by viewModel.taskTypes.collectAsState() + LazyColumn(modifier = Modifier.fillMaxSize().padding(16.dp)) { + items(taskTypes?.ocs?.data?.types ?: listOf()) { + Text(text = it.toString()) } } + } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index e56f66eaef..16bbc35549 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -28,10 +28,12 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import com.nextcloud.client.assistant.AssistantScreen +import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.databinding.FragmentComposeViewBinding +import com.owncloud.android.ui.fragment.FileFragment -class ComposeFragment : Fragment() { +class ComposeFragment : FileFragment() { private var _binding: FragmentComposeViewBinding? = null @@ -53,12 +55,17 @@ class ComposeFragment : Fragment() { binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { - when(destination) { + when (destination) { ComposeDestinations.AssistantScreen -> { - AssistantScreen() + AssistantScreen( + viewModel = AssistantViewModel( + context = requireContext(), + user = containerActivity.storageManager.user + ) + ) } - else -> { + else -> { } } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index f4f25e3266..7ce479603a 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -549,7 +549,7 @@ public abstract class DrawerActivity extends ToolbarActivity ComposeFragment composeFragment = new ComposeFragment(); Bundle bundle = new Bundle(); bundle.putSerializable(ComposeFragment.destinationKey, ComposeDestinations.AssistantScreen); - composeFragment.setArguments( bundle); + composeFragment.setArguments(bundle); getSupportFragmentManager() .beginTransaction() From 6767ab8636b81b0252f5ab3b09fbc66defc309bc Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 27 Feb 2024 16:44:17 +0100 Subject: [PATCH 005/102] Add UI Components Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 14 +-- .../client/assistant/AsssistantScreen.kt | 119 +++++++++++++++++- .../ui/composeComponents/SimpleAlertDialog.kt | 97 ++++++++++++++ .../ui/composeFragment/ComposeFragment.kt | 39 ++++-- .../android/ui/activity/DrawerActivity.java | 3 - settings.gradle | 6 + 6 files changed, 249 insertions(+), 29 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 54528682e7..92ef9749a2 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -36,7 +36,7 @@ import kotlinx.coroutines.launch class AssistantViewModel(context: Context, user: User) : ViewModel() { - private val repository = AssistantRepository(user, context) + private var repository: AssistantRepository? = null private val _taskTypes = MutableStateFlow(null) val taskTypes: StateFlow = _taskTypes @@ -46,22 +46,24 @@ class AssistantViewModel(context: Context, user: User) : ViewModel() { init { viewModelScope.launch(Dispatchers.IO) { + repository = AssistantRepository(user, context) + _taskTypes.update { - repository.getTaskTypes() + repository?.getTaskTypes() } } } fun deleteTask(id: String) { viewModelScope.launch(Dispatchers.IO) { - repository.deleteTask(id) + repository?.deleteTask(id) } } fun getTask(id: String) { viewModelScope.launch(Dispatchers.IO) { _task.update { - repository.getTask(id) + repository?.getTask(id) } } } @@ -69,11 +71,9 @@ class AssistantViewModel(context: Context, user: User) : ViewModel() { fun createTask( input: String, type: String, - appId: String, - identifier: String, ) { viewModelScope.launch(Dispatchers.IO) { - repository.createTask(input, type, appId, identifier) + repository?.createTask(input, type, identifier = " ") } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 6cc8f5f8f1..f8ee5bb12a 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -22,29 +22,136 @@ package com.nextcloud.client.assistant import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material3.Scaffold +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.nextcloud.operations.assistant.model.OcsType +import com.nextcloud.ui.composeComponents.SimpleAlertDialog +import com.owncloud.android.R @OptIn(ExperimentalFoundationApi::class) @Composable -fun AssistantScreen(viewModel: AssistantViewModel) { +fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { + // TODO hide sort group, floating action and search bar val taskTypes by viewModel.taskTypes.collectAsState() + var selectedTaskType: String? by remember { + mutableStateOf(null) + } + var showAddTaskAlertDialog by remember { + mutableStateOf(false) + } + + floatingActionButton.setOnClickListener { + showAddTaskAlertDialog = true + } + + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + stickyHeader { + taskTypes?.let { it -> + TaskTypesRow(selectedTaskType, data = it.ocs.data.types) { taskId -> + selectedTaskType = taskId + } + } + } - LazyColumn(modifier = Modifier.fillMaxSize().padding(16.dp)) { items(taskTypes?.ocs?.data?.types ?: listOf()) { Text(text = it.toString()) } } -} \ No newline at end of file + if (showAddTaskAlertDialog) { + selectedTaskType?.let { + AddTaskAlertDialog(viewModel, it) { + showAddTaskAlertDialog = false + } + } + } +} + +@Composable +private fun AddTaskAlertDialog(viewModel: AssistantViewModel, type: String, dismiss: () -> Unit) { + var input by remember { + mutableStateOf("") + } + + // TODO add to UI LIB + SimpleAlertDialog( + backgroundColor = Color.White, + textColor = Color.Black, + titleId = R.string.about_title, + description = stringResource(id = R.string.about_title), + dismiss = { dismiss() }, + onComplete = { viewModel.createTask(input = input, type = type) }, + content = { + TextField( + placeholder = { + Text( + text = stringResource(id = R.string.samples), + ) + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), + value = input, + onValueChange = { + input = it + }, + singleLine = true + ) + } + ) +} + +@Composable +private fun TaskTypesRow(selectedTaskType: String?, data: List, selectTaskType: (String) -> Unit) { + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()) + ) { + data.forEach { + FilledTonalButton( + onClick = { selectTaskType(it.id) }, + colors = ButtonDefaults.buttonColors( + containerColor = if (selectedTaskType == it.id) { + Color.Unspecified + } else { + Color.Gray + } + ) + ) { + Text(text = it.name) + } + + Spacer(modifier = Modifier.padding(end = 8.dp)) + } + } +} diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt new file mode 100644 index 0000000000..d689c599c1 --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt @@ -0,0 +1,97 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.ui.composeComponents + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.owncloud.android.R + +@Composable +fun SimpleAlertDialog( + backgroundColor: Color, + textColor: Color, + titleId: Int, + description: String?, + heightFraction: Float? = null, + content: @Composable (() -> Unit)? = null, + onComplete: () -> Unit, + dismiss: () -> Unit +) { + val modifier = if (heightFraction != null) { + Modifier + .fillMaxWidth() + .fillMaxHeight(heightFraction) + } else { + Modifier.fillMaxWidth() + } + + AlertDialog( + containerColor = backgroundColor, + onDismissRequest = { dismiss() }, + title = { + Text(text = stringResource(id = titleId), color = textColor) + }, + text = { + Column(modifier = modifier) { + if (description != null) { + Text(text = description, color = textColor) + } + + content?.let { + Spacer(modifier = Modifier.height(16.dp)) + + content() + } + } + }, + confirmButton = { + TextButton(onClick = { + onComplete() + dismiss() + }) { + Text( + stringResource(id = R.string.common_ok), + color = textColor + ) + } + }, + dismissButton = { + TextButton(onClick = { dismiss() }) { + Text( + stringResource(id = R.string.common_cancel), + color = textColor + ) + } + } + ) +} diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index 16bbc35549..db08e16a1e 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -25,13 +25,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.runtime.Composable import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment +import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.utils.extensions.getSerializableArgument +import com.owncloud.android.R import com.owncloud.android.databinding.FragmentComposeViewBinding import com.owncloud.android.ui.fragment.FileFragment +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext class ComposeFragment : FileFragment() { @@ -54,26 +59,34 @@ class ComposeFragment : FileFragment() { binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - when (destination) { - ComposeDestinations.AssistantScreen -> { - AssistantScreen( - viewModel = AssistantViewModel( - context = requireContext(), - user = containerActivity.storageManager.user - ) - ) - } - else -> { - } - } + setContent { + Content(destination) } } return binding.root } + @Composable + private fun Content(destination: ComposeDestinations?) { + val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) + + return when (destination) { + ComposeDestinations.AssistantScreen -> { + AssistantScreen( + viewModel = AssistantViewModel( + context = requireContext(), + user = containerActivity.storageManager.user + ), + floatingActionButton + ) + } + else -> { + } + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 7ce479603a..307e7acc31 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -555,9 +555,6 @@ public abstract class DrawerActivity extends ToolbarActivity .beginTransaction() .replace(R.id.left_fragment_container, composeFragment) .commit(); - - - Log_OC.w(TAG, "ADD JETPACK Compose PAGE"); } else { if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) { diff --git a/settings.gradle b/settings.gradle index 5c2438e2ad..34030893f3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,3 +14,9 @@ include ':appscan' // substitute module('com.github.nextcloud:android-library') using project(':library') // } //} + +includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_library') { + dependencySubstitution { + substitute module('com.github.nextcloud:android-library') using project(':library') + } +} From 352b482066d058af35e979c4beb0d51a45fa2005 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 27 Feb 2024 17:12:04 +0100 Subject: [PATCH 006/102] Add AssistantRepositoryTests Signed-off-by: alperozturk --- .../assistant/AssistantRepositoryTests.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt new file mode 100644 index 0000000000..7d653ee06e --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -0,0 +1,52 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant + +import com.nextcloud.client.account.UserAccountManagerImpl +import com.nextcloud.operations.assistant.AssistantRepository +import com.owncloud.android.AbstractOnServerIT +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class AssistantRepositoryTests: AbstractOnServerIT() { + + private var sut: AssistantRepository? = null + + @Before + fun setup() { + val userAccountManager = UserAccountManagerImpl.fromContext(targetContext) + sut = AssistantRepository(userAccountManager.user, targetContext) + } + + @Test + fun testGetTaskTypes() { + assertTrue(sut?.getTaskTypes()?.ocs?.data?.types?.isNotEmpty() == true) + } + + @Test + fun testGetTaskList() { + assertTrue(sut?.getTaskList("assistant")?.ocs?.data?.types?.isNotEmpty() == true) + } + + +} From b2c0202d42b685541189416c9641693cdd6da478 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 28 Feb 2024 12:00:47 +0100 Subject: [PATCH 007/102] Add AssistantRepository Signed-off-by: alperozturk --- .../assistant/AssistantRepositoryTests.kt | 24 ++++++- .../owncloud/android/AbstractOnServerIT.java | 2 +- .../client/assistant/AssistantViewModel.kt | 37 ++++++---- .../client/assistant/AsssistantScreen.kt | 19 +++-- .../repository/AssistantRepository.kt | 72 +++++++++++++++++++ .../repository/AssistantRepositoryType.kt | 46 ++++++++++++ .../ui/composeFragment/ComposeFragment.kt | 45 +++++++++--- .../ui/activity/FileDisplayActivity.java | 5 -- 8 files changed, 207 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt create mode 100644 app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index 7d653ee06e..a935ca1ad1 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -22,7 +22,7 @@ package com.nextcloud.client.assistant import com.nextcloud.client.account.UserAccountManagerImpl -import com.nextcloud.operations.assistant.AssistantRepository +import com.nextcloud.client.assistant.repository.AssistantRepository import com.owncloud.android.AbstractOnServerIT import org.junit.Assert.assertTrue import org.junit.Before @@ -40,13 +40,31 @@ class AssistantRepositoryTests: AbstractOnServerIT() { @Test fun testGetTaskTypes() { - assertTrue(sut?.getTaskTypes()?.ocs?.data?.types?.isNotEmpty() == true) + assertTrue(sut?.getTaskTypes()?.resultData?.isNotEmpty() == true) } - @Test + /* + + @Test fun testGetTaskList() { assertTrue(sut?.getTaskList("assistant")?.ocs?.data?.types?.isNotEmpty() == true) } + @Test + fun testCreateTask() { + val input = "How many files I have?" + val type = "OCP\\TextProcessing\\HeadlineTaskType" + val result = sut?.createTask(input, type) + assertTrue(result != null) + } + + @Test + fun testDeleteTask() { + val taskList = sut?.getTaskList("assistant")?.ocs?.data + assertTrue(sut?.getTaskList("assistant")?.ocs?.data?.types?.isNotEmpty() == true) + } + + */ + } diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index 343938e121..bcd43ccafb 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -112,7 +112,7 @@ public abstract class AbstractOnServerIT extends AbstractIT { @After public void after() { - deleteAllFilesOnServer(); + // deleteAllFilesOnServer(); super.after(); } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 92ef9749a2..dc516804ac 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -21,39 +21,41 @@ package com.nextcloud.client.assistant -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.nextcloud.client.account.User -import com.nextcloud.operations.assistant.AssistantRepository -import com.nextcloud.operations.assistant.model.CreatedTask -import com.nextcloud.operations.assistant.model.TaskTypes +import com.nextcloud.client.assistant.repository.AssistantRepository +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.assistant.model.TaskTypes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -class AssistantViewModel(context: Context, user: User) : ViewModel() { +class AssistantViewModel(client: NextcloudClient) : ViewModel() { - private var repository: AssistantRepository? = null + private val repository: AssistantRepository = AssistantRepository(client) - private val _taskTypes = MutableStateFlow(null) - val taskTypes: StateFlow = _taskTypes + private val _taskTypes = MutableStateFlow?>(null) + val taskTypes: StateFlow?> = _taskTypes - private val _task = MutableStateFlow(null) + /* + private val _task = MutableStateFlow(null) val task: StateFlow = _task + */ + init { viewModelScope.launch(Dispatchers.IO) { - repository = AssistantRepository(user, context) - + val result = repository.getTaskTypes() _taskTypes.update { - repository?.getTaskTypes() + result } } } + /* fun deleteTask(id: String) { viewModelScope.launch(Dispatchers.IO) { repository?.deleteTask(id) @@ -76,4 +78,13 @@ class AssistantViewModel(context: Context, user: User) : ViewModel() { repository?.createTask(input, type, identifier = " ") } } + */ + + + fun createTask( + input: String, + type: String, + ) { + } + } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index f8ee5bb12a..7521a6dc55 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -32,12 +32,10 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Text import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -50,10 +48,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import com.google.android.material.floatingactionbutton.FloatingActionButton -import com.nextcloud.android.common.ui.theme.utils.ColorRole -import com.nextcloud.operations.assistant.model.OcsType import com.nextcloud.ui.composeComponents.SimpleAlertDialog import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.model.TaskType @OptIn(ExperimentalFoundationApi::class) @Composable @@ -77,14 +74,16 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin .padding(16.dp) ) { stickyHeader { - taskTypes?.let { it -> - TaskTypesRow(selectedTaskType, data = it.ocs.data.types) { taskId -> - selectedTaskType = taskId + taskTypes?.let { taskTypes -> + taskTypes.resultData?.types.let { + TaskTypesRow(selectedTaskType, data = it) { taskId -> + selectedTaskType = taskId + } } } } - items(taskTypes?.ocs?.data?.types ?: listOf()) { + items(taskTypes?.resultData?.types ?: listOf()) { Text(text = it.toString()) } } @@ -131,13 +130,13 @@ private fun AddTaskAlertDialog(viewModel: AssistantViewModel, type: String, dism } @Composable -private fun TaskTypesRow(selectedTaskType: String?, data: List, selectTaskType: (String) -> Unit) { +private fun TaskTypesRow(selectedTaskType: String?, data: List?, selectTaskType: (String) -> Unit) { Row( modifier = Modifier .fillMaxWidth() .horizontalScroll(rememberScrollState()) ) { - data.forEach { + data?.forEach { FilledTonalButton( onClick = { selectTaskType(it.id) }, colors = ButtonDefaults.buttonColors( diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt new file mode 100644 index 0000000000..c3a6d8f00f --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt @@ -0,0 +1,72 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant.repository + +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation +import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.lib.resources.assistant.model.TaskTypes + +class AssistantRepository(private val client: NextcloudClient): AssistantRepositoryType { + + override fun getTaskTypes(): RemoteOperationResult { + return GetTaskTypesRemoteOperation().execute(client) + } + + /* + // TODO Check return type + override fun getTaskList(appId: String): TaskTypes? { + return operation.get("/ocs/v2.php/textprocessing/tasks/app/$appId", TaskTypes::class.java) + } + + // TODO Check return type + override fun deleteTask(id: String): CreatedTask? { + return operation.delete("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java) + } + + // TODO Check return type + override fun getTask(id: String): CreatedTask? { + return operation.get("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java) + } + + override fun createTask( + input: String, + type: String, + appId: String, + identifier: String, + ): CreatedTask? { + val json = JSONObject().apply { + put("input", input) + put("type", type) + put("appId", appId) + put("identifier", identifier) + } + + return operation.post( + "/ocs/v2.php/textprocessing/schedule", + CreatedTask::class.java, + json + ) + } + */ +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt new file mode 100644 index 0000000000..379052c9db --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant.repository + +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.lib.resources.assistant.model.TaskTypes + +interface AssistantRepositoryType { + fun getTaskTypes(): RemoteOperationResult + + /* + fun getTask(id: String): CreatedTask? + + fun deleteTask(id: String): CreatedTask? + + fun getTaskList(appId: String): TaskTypes? + + fun createTask( + input: String, + type: String, + appId: String = "assistant", + identifier: String = "" + ): CreatedTask? + */ + +} diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index db08e16a1e..225fa34239 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -26,14 +26,24 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel +import com.nextcloud.common.NextcloudClient +import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.R import com.owncloud.android.databinding.FragmentComposeViewBinding +import com.owncloud.android.lib.common.OwnCloudClientFactory +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.fragment.FileFragment import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -71,18 +81,31 @@ class ComposeFragment : FileFragment() { @Composable private fun Content(destination: ComposeDestinations?) { val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) + var nextcloudClient by remember { mutableStateOf(null) } - return when (destination) { - ComposeDestinations.AssistantScreen -> { - AssistantScreen( - viewModel = AssistantViewModel( - context = requireContext(), - user = containerActivity.storageManager.user - ), - floatingActionButton - ) - } - else -> { + LaunchedEffect(Unit) { + nextcloudClient = getNextcloudClient() + } + + return if (destination == ComposeDestinations.AssistantScreen && nextcloudClient != null) { + AssistantScreen( + viewModel = AssistantViewModel( + client = nextcloudClient!! + ), + floatingActionButton + ) + } else { + + } + } + + private suspend fun getNextcloudClient(): NextcloudClient? { + return withContext(Dispatchers.IO) { + try { + OwnCloudClientFactory.createNextcloudClient(containerActivity.storageManager.user, requireContext()) + } catch (e: AccountUtils.AccountNotFoundException) { + Log_OC.e(this, "Error caught at init of AssistantRepository", e) + null } } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 108a1010b3..b9b2856a2b 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -74,14 +74,9 @@ import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.utils.IntentUtil; -import com.nextcloud.common.NextcloudClient; import com.nextcloud.java.util.Optional; import com.nextcloud.model.WorkerState; import com.nextcloud.model.WorkerStateLiveData; -import com.nextcloud.operations.assistant.AssistantRepository; -import com.nextcloud.operations.assistant.model.CreatedTask; -import com.nextcloud.operations.assistant.model.Ocs; -import com.nextcloud.operations.assistant.model.TaskTypes; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.nextcloud.utils.view.FastScrollUtils; From ab78bd8c202ba7e6591c571ef303b912d4927763 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 28 Feb 2024 13:00:22 +0100 Subject: [PATCH 008/102] Add CreateTaskRemoteOperation Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 36 +++++++++---------- .../client/assistant/AsssistantScreen.kt | 12 +++++++ .../repository/AssistantRepository.kt | 27 +++++--------- .../repository/AssistantRepositoryType.kt | 12 +++---- 4 files changed, 42 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index dc516804ac..c0c40ba63c 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -40,11 +40,8 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _taskTypes = MutableStateFlow?>(null) val taskTypes: StateFlow?> = _taskTypes - /* - private val _task = MutableStateFlow(null) - val task: StateFlow = _task - */ - + private val _isTaskCreated = MutableStateFlow(false) + val isTaskCreated: StateFlow = _isTaskCreated init { viewModelScope.launch(Dispatchers.IO) { @@ -55,6 +52,19 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } + fun createTask( + input: String, + type: String, + ) { + viewModelScope.launch(Dispatchers.IO) { + val result = repository.createTask(input, type) + + _isTaskCreated.update { + result.isSuccess + } + } + } + /* fun deleteTask(id: String) { viewModelScope.launch(Dispatchers.IO) { @@ -70,21 +80,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } - fun createTask( - input: String, - type: String, - ) { - viewModelScope.launch(Dispatchers.IO) { - repository?.createTask(input, type, identifier = " ") - } - } + */ - - fun createTask( - input: String, - type: String, - ) { - } - } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 7521a6dc55..3bc18af21c 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -21,6 +21,7 @@ package com.nextcloud.client.assistant +import android.app.Activity import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Row @@ -44,6 +45,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp @@ -51,11 +53,13 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.ui.composeComponents.SimpleAlertDialog import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.utils.DisplayUtils @OptIn(ExperimentalFoundationApi::class) @Composable fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { // TODO hide sort group, floating action and search bar + val isTaskCreated by viewModel.isTaskCreated.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() var selectedTaskType: String? by remember { mutableStateOf(null) @@ -88,6 +92,13 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } } + if (isTaskCreated) { + DisplayUtils.showSnackMessage( + LocalContext.current as Activity, + stringResource(id = R.string.assistant_screen_task_create_success_message) + ) + } + if (showAddTaskAlertDialog) { selectedTaskType?.let { AddTaskAlertDialog(viewModel, it) { @@ -99,6 +110,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin @Composable private fun AddTaskAlertDialog(viewModel: AssistantViewModel, type: String, dismiss: () -> Unit) { + var input by remember { mutableStateOf("") } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt index c3a6d8f00f..f232287385 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt @@ -23,8 +23,8 @@ package com.nextcloud.client.assistant.repository import com.nextcloud.common.NextcloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.assistant.CreateTaskRemoteOperation import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation -import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.lib.resources.assistant.model.TaskTypes class AssistantRepository(private val client: NextcloudClient): AssistantRepositoryType { @@ -33,6 +33,13 @@ class AssistantRepository(private val client: NextcloudClient): AssistantReposit return GetTaskTypesRemoteOperation().execute(client) } + override fun createTask( + input: String, + type: String, + ): RemoteOperationResult { + return CreateTaskRemoteOperation(input, type).execute(client) + } + /* // TODO Check return type override fun getTaskList(appId: String): TaskTypes? { @@ -49,24 +56,6 @@ class AssistantRepository(private val client: NextcloudClient): AssistantReposit return operation.get("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java) } - override fun createTask( - input: String, - type: String, - appId: String, - identifier: String, - ): CreatedTask? { - val json = JSONObject().apply { - put("input", input) - put("type", type) - put("appId", appId) - put("identifier", identifier) - } - return operation.post( - "/ocs/v2.php/textprocessing/schedule", - CreatedTask::class.java, - json - ) - } */ } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt index 379052c9db..e3f6c882c6 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt @@ -28,6 +28,11 @@ import com.owncloud.android.lib.resources.assistant.model.TaskTypes interface AssistantRepositoryType { fun getTaskTypes(): RemoteOperationResult + fun createTask( + input: String, + type: String, + ): RemoteOperationResult + /* fun getTask(id: String): CreatedTask? @@ -35,12 +40,7 @@ interface AssistantRepositoryType { fun getTaskList(appId: String): TaskTypes? - fun createTask( - input: String, - type: String, - appId: String = "assistant", - identifier: String = "" - ): CreatedTask? + */ } From ca52db74376338eccb5fcdb6745205d7f5540db8 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 28 Feb 2024 15:01:58 +0100 Subject: [PATCH 009/102] Add GetTaskListRemoteOperation Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 42 ++++++++++++------- .../client/assistant/AsssistantScreen.kt | 3 +- .../repository/AssistantRepository.kt | 13 +++--- .../repository/AssistantRepositoryType.kt | 6 ++- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index c0c40ba63c..dca8d11e39 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -26,6 +26,7 @@ import androidx.lifecycle.viewModelScope import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.assistant.model.TaskList import com.owncloud.android.lib.resources.assistant.model.TaskTypes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -40,16 +41,15 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _taskTypes = MutableStateFlow?>(null) val taskTypes: StateFlow?> = _taskTypes + private val _taskList = MutableStateFlow?>(null) + val taskList: StateFlow?> = _taskList + private val _isTaskCreated = MutableStateFlow(false) val isTaskCreated: StateFlow = _isTaskCreated init { - viewModelScope.launch(Dispatchers.IO) { - val result = repository.getTaskTypes() - _taskTypes.update { - result - } - } + getTaskTypes() + getTaskList() } fun createTask( @@ -65,22 +65,32 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } + private fun getTaskTypes() { + viewModelScope.launch(Dispatchers.IO) { + val result = repository.getTaskTypes() + _taskTypes.update { + result + } + } + } + + private fun getTaskList(appId: String = "assistant") { + viewModelScope.launch(Dispatchers.IO) { + val result = repository.getTaskList(appId) + + _taskList.update { + result + } + } + } + + /* fun deleteTask(id: String) { viewModelScope.launch(Dispatchers.IO) { repository?.deleteTask(id) } } - - fun getTask(id: String) { - viewModelScope.launch(Dispatchers.IO) { - _task.update { - repository?.getTask(id) - } - } - } - - */ } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 3bc18af21c..60a233f21f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -59,6 +59,7 @@ import com.owncloud.android.utils.DisplayUtils @Composable fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { // TODO hide sort group, floating action and search bar + val taskList by viewModel.taskList.collectAsState() val isTaskCreated by viewModel.isTaskCreated.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() var selectedTaskType: String? by remember { @@ -87,7 +88,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } } - items(taskTypes?.resultData?.types ?: listOf()) { + items(taskList?.resultData?.tasks ?: listOf()) { Text(text = it.toString()) } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt index f232287385..df29df35d6 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt @@ -24,10 +24,12 @@ package com.nextcloud.client.assistant.repository import com.nextcloud.common.NextcloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.assistant.CreateTaskRemoteOperation +import com.owncloud.android.lib.resources.assistant.GetTaskListRemoteOperation import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation +import com.owncloud.android.lib.resources.assistant.model.TaskList import com.owncloud.android.lib.resources.assistant.model.TaskTypes -class AssistantRepository(private val client: NextcloudClient): AssistantRepositoryType { +class AssistantRepository(private val client: NextcloudClient) : AssistantRepositoryType { override fun getTaskTypes(): RemoteOperationResult { return GetTaskTypesRemoteOperation().execute(client) @@ -40,12 +42,11 @@ class AssistantRepository(private val client: NextcloudClient): AssistantReposit return CreateTaskRemoteOperation(input, type).execute(client) } - /* - // TODO Check return type - override fun getTaskList(appId: String): TaskTypes? { - return operation.get("/ocs/v2.php/textprocessing/tasks/app/$appId", TaskTypes::class.java) + override fun getTaskList(appId: String): RemoteOperationResult { + return GetTaskListRemoteOperation(appId).execute(client) } + /* // TODO Check return type override fun deleteTask(id: String): CreatedTask? { return operation.delete("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java) @@ -55,7 +56,5 @@ class AssistantRepository(private val client: NextcloudClient): AssistantReposit override fun getTask(id: String): CreatedTask? { return operation.get("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java) } - - */ } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt index e3f6c882c6..2f5c1081ca 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt @@ -22,7 +22,7 @@ package com.nextcloud.client.assistant.repository import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.lib.resources.assistant.model.TaskList import com.owncloud.android.lib.resources.assistant.model.TaskTypes interface AssistantRepositoryType { @@ -33,12 +33,14 @@ interface AssistantRepositoryType { type: String, ): RemoteOperationResult + fun getTaskList(appId: String): RemoteOperationResult + /* fun getTask(id: String): CreatedTask? fun deleteTask(id: String): CreatedTask? - fun getTaskList(appId: String): TaskTypes? + */ From f55017f077d091e81c65298bc4035d511aeb24e8 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 28 Feb 2024 16:05:45 +0100 Subject: [PATCH 010/102] UI Fixes Signed-off-by: alperozturk --- .idea/inspectionProfiles/ktlint.xml | 31 ++++++- .../client/assistant/AssistantViewModel.kt | 16 ++++ .../client/assistant/AsssistantScreen.kt | 90 +++---------------- .../assistant/component/AddTaskAlertDialog.kt | 69 ++++++++++++++ .../assistant/component/TaskTypesRow.kt | 63 +++++++++++++ .../client/assistant/component/TaskView.kt | 82 +++++++++++++++++ .../ui/composeComponents/SimpleAlertDialog.kt | 4 +- app/src/main/res/values/strings.xml | 5 ++ 8 files changed, 278 insertions(+), 82 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt create mode 100644 app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt create mode 100644 app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt diff --git a/.idea/inspectionProfiles/ktlint.xml b/.idea/inspectionProfiles/ktlint.xml index 63c66a65e3..caad859bb6 100644 --- a/.idea/inspectionProfiles/ktlint.xml +++ b/.idea/inspectionProfiles/ktlint.xml @@ -2,9 +2,36 @@ - + \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index dca8d11e39..ac96ad264a 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -26,7 +26,9 @@ import androidx.lifecycle.viewModelScope import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskList +import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.lib.resources.assistant.model.TaskTypes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -38,6 +40,9 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val repository: AssistantRepository = AssistantRepository(client) + private val _selectedTask = MutableStateFlow(null) + val selectedTask: StateFlow = _selectedTask + private val _taskTypes = MutableStateFlow?>(null) val taskTypes: StateFlow?> = _taskTypes @@ -65,12 +70,23 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } + fun selectTask(task: TaskType) { + _selectedTask.update { + task + } + } + private fun getTaskTypes() { viewModelScope.launch(Dispatchers.IO) { val result = repository.getTaskTypes() + _taskTypes.update { result } + + _selectedTask.update { + result.resultData.types.first() + } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 60a233f21f..7c29ca7f43 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -23,20 +23,12 @@ package com.nextcloud.client.assistant import android.app.Activity import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.FilledTonalButton -import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -44,13 +36,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import com.google.android.material.floatingactionbutton.FloatingActionButton -import com.nextcloud.ui.composeComponents.SimpleAlertDialog +import com.nextcloud.client.assistant.component.AddTaskAlertDialog +import com.nextcloud.client.assistant.component.TaskTypesRow +import com.nextcloud.client.assistant.component.TaskView import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.utils.DisplayUtils @@ -59,12 +51,10 @@ import com.owncloud.android.utils.DisplayUtils @Composable fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { // TODO hide sort group, floating action and search bar + val selectedTask by viewModel.selectedTask.collectAsState() val taskList by viewModel.taskList.collectAsState() val isTaskCreated by viewModel.isTaskCreated.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() - var selectedTaskType: String? by remember { - mutableStateOf(null) - } var showAddTaskAlertDialog by remember { mutableStateOf(false) } @@ -81,15 +71,18 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin stickyHeader { taskTypes?.let { taskTypes -> taskTypes.resultData?.types.let { - TaskTypesRow(selectedTaskType, data = it) { taskId -> - selectedTaskType = taskId + TaskTypesRow(selectedTask, data = it) { task-> + viewModel.selectTask(task) } } } + + Spacer(modifier = Modifier.height(8.dp)) } items(taskList?.resultData?.tasks ?: listOf()) { - Text(text = it.toString()) + TaskView(task = it) + Spacer(modifier = Modifier.height(8.dp)) } } @@ -101,69 +94,10 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } if (showAddTaskAlertDialog) { - selectedTaskType?.let { + selectedTask?.let { AddTaskAlertDialog(viewModel, it) { showAddTaskAlertDialog = false } } } } - -@Composable -private fun AddTaskAlertDialog(viewModel: AssistantViewModel, type: String, dismiss: () -> Unit) { - - var input by remember { - mutableStateOf("") - } - - // TODO add to UI LIB - SimpleAlertDialog( - backgroundColor = Color.White, - textColor = Color.Black, - titleId = R.string.about_title, - description = stringResource(id = R.string.about_title), - dismiss = { dismiss() }, - onComplete = { viewModel.createTask(input = input, type = type) }, - content = { - TextField( - placeholder = { - Text( - text = stringResource(id = R.string.samples), - ) - }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), - value = input, - onValueChange = { - input = it - }, - singleLine = true - ) - } - ) -} - -@Composable -private fun TaskTypesRow(selectedTaskType: String?, data: List?, selectTaskType: (String) -> Unit) { - Row( - modifier = Modifier - .fillMaxWidth() - .horizontalScroll(rememberScrollState()) - ) { - data?.forEach { - FilledTonalButton( - onClick = { selectTaskType(it.id) }, - colors = ButtonDefaults.buttonColors( - containerColor = if (selectedTaskType == it.id) { - Color.Unspecified - } else { - Color.Gray - } - ) - ) { - Text(text = it.name) - } - - Spacer(modifier = Modifier.padding(end = 8.dp)) - } - } -} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt new file mode 100644 index 0000000000..14caeba4ff --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -0,0 +1,69 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant.component + +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import com.nextcloud.client.assistant.AssistantViewModel +import com.nextcloud.ui.composeComponents.SimpleAlertDialog +import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.model.TaskType + +@Composable +fun AddTaskAlertDialog(viewModel: AssistantViewModel, taskType: TaskType, dismiss: () -> Unit) { + var input by remember { + mutableStateOf("") + } + + SimpleAlertDialog( + backgroundColor = Color.White, + textColor = Color.Black, + title = taskType.name, + description = taskType.description, + dismiss = { dismiss() }, + onComplete = { viewModel.createTask(input = input, type = taskType.id) }, + content = { + TextField( + placeholder = { + Text( + text = stringResource(id = R.string.assistant_screen_create_task_alert_dialog_input_field_placeholder), + ) + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), + value = input, + onValueChange = { + input = it + }, + singleLine = true + ) + } + ) +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt new file mode 100644 index 0000000000..7a849169fb --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt @@ -0,0 +1,63 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant.component + +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.owncloud.android.lib.resources.assistant.model.TaskType + +@Composable +fun TaskTypesRow(selectedTaskType: TaskType?, data: List?, selectTaskType: (TaskType) -> Unit) { + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()) + ) { + data?.forEach { + FilledTonalButton( + onClick = { selectTaskType(it) }, + colors = ButtonDefaults.buttonColors( + containerColor = if (selectedTaskType?.id == it.id) { + Color.Unspecified + } else { + Color.Gray + } + ) + ) { + Text(text = it.name) + } + + Spacer(modifier = Modifier.padding(end = 8.dp)) + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt new file mode 100644 index 0000000000..dbc9b43450 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -0,0 +1,82 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant.component + +import android.annotation.SuppressLint +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.model.Task + +@SuppressLint("ResourceAsColor") +@Composable +fun TaskView( + task: Task, +) { + var expanded by remember { mutableStateOf(false) } + + // TODO Check color + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .background(Color(R.color.primary)) + ) { + Text( + text = if (expanded) task.output else task.output.take(100) + "...", + modifier = Modifier + .padding(16.dp) + .clickable { expanded = !expanded } + ) + + if (task.output.length >= 100) { + Text( + text = if (!expanded) { + stringResource(id = R.string.assistant_screen_task_view_show_more) + } else { + stringResource(id = R.string.assistant_screen_task_view_show_less) + }, + textAlign = TextAlign.End, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .clickable { expanded = true } + ) + } + } +} diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt index d689c599c1..cf727b87d9 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt @@ -40,7 +40,7 @@ import com.owncloud.android.R fun SimpleAlertDialog( backgroundColor: Color, textColor: Color, - titleId: Int, + title: String, description: String?, heightFraction: Float? = null, content: @Composable (() -> Unit)? = null, @@ -59,7 +59,7 @@ fun SimpleAlertDialog( containerColor = backgroundColor, onDismissRequest = { dismiss() }, title = { - Text(text = stringResource(id = titleId), color = textColor) + Text(text = title, color = textColor) }, text = { Column(modifier = modifier) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df90f84ca5..2f624a72d1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,6 +18,11 @@ Biggest first Smallest first + Task successfully created + Type some text + + Show more + Show less Assistant All files From b48ddf5d448f65c59f79eb9c7af9e9fcba431059 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 28 Feb 2024 16:34:22 +0100 Subject: [PATCH 011/102] Add loading and empty states Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 10 ++- .../client/assistant/AsssistantScreen.kt | 72 +++++++++++++------ .../client/assistant/component/CenterText.kt | 44 ++++++++++++ .../client/assistant/component/TaskView.kt | 31 +++++++- .../android/ui/activity/DrawerActivity.java | 2 + app/src/main/res/values/strings.xml | 5 ++ 6 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index ac96ad264a..2c2909dc95 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -21,12 +21,13 @@ package com.nextcloud.client.assistant +import androidx.compose.ui.res.stringResource import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient +import com.owncloud.android.R import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskList import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.lib.resources.assistant.model.TaskTypes @@ -49,6 +50,9 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _taskList = MutableStateFlow?>(null) val taskList: StateFlow?> = _taskList + private val _loading = MutableStateFlow(true) + val loading: StateFlow = _loading + private val _isTaskCreated = MutableStateFlow(false) val isTaskCreated: StateFlow = _isTaskCreated @@ -97,6 +101,10 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { _taskList.update { result } + + _loading.update { + false + } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 7c29ca7f43..edb251bea6 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -23,34 +23,43 @@ package com.nextcloud.client.assistant import android.app.Activity import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.component.AddTaskAlertDialog +import com.nextcloud.client.assistant.component.CenterText import com.nextcloud.client.assistant.component.TaskTypesRow import com.nextcloud.client.assistant.component.TaskView import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.utils.DisplayUtils -@OptIn(ExperimentalFoundationApi::class) @Composable fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { - // TODO hide sort group, floating action and search bar + // TODO hide sort group, search bar + // TODO top bar, back button causes crash + val loading by viewModel.loading.collectAsState() val selectedTask by viewModel.selectedTask.collectAsState() val taskList by viewModel.taskList.collectAsState() val isTaskCreated by viewModel.isTaskCreated.collectAsState() @@ -63,27 +72,12 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin showAddTaskAlertDialog = true } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - stickyHeader { - taskTypes?.let { taskTypes -> - taskTypes.resultData?.types.let { - TaskTypesRow(selectedTask, data = it) { task-> - viewModel.selectTask(task) - } - } - } - - Spacer(modifier = Modifier.height(8.dp)) - } - - items(taskList?.resultData?.tasks ?: listOf()) { - TaskView(task = it) - Spacer(modifier = Modifier.height(8.dp)) - } + if (loading) { + CenterText(text = stringResource(id = R.string.assistant_screen_loading)) + } else { + val tasks = taskList?.resultData?.tasks ?: return + val types = taskTypes?.resultData?.types ?: return + AssistantContent(tasks, types, selectedTask, viewModel) } if (isTaskCreated) { @@ -101,3 +95,35 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } } } + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun AssistantContent( + taskList: List, + taskTypes: List, + selectedTask: TaskType?, + viewModel: AssistantViewModel +) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + stickyHeader { + TaskTypesRow(selectedTask, data = taskTypes) { task -> + viewModel.selectTask(task) + } + + Spacer(modifier = Modifier.height(8.dp)) + } + + items(taskList) { + if (taskList.isEmpty()) { + CenterText(text = stringResource(id = R.string.assistant_screen_no_task_available_text)) + } else { + TaskView(task = it) + Spacer(modifier = Modifier.height(8.dp)) + } + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt new file mode 100644 index 0000000000..20c56d17e4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant.component + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.sp + +@Composable +fun CenterText(text: String) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text( + text = text, + fontSize = 18.sp, + color = Color.Black, + textAlign = TextAlign.Center + ) + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index dbc9b43450..dd300be986 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -22,10 +22,13 @@ package com.nextcloud.client.assistant.component import android.annotation.SuppressLint +import android.widget.Space import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text @@ -57,10 +60,35 @@ fun TaskView( .clip(RoundedCornerShape(16.dp)) .background(Color(R.color.primary)) ) { + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = stringResource(id = R.string.assistant_screen_task_view_input), + modifier = Modifier.padding(4.dp), + color = Color.White + ) + + Text( + text = task.input, + modifier = Modifier.padding(4.dp), + color = Color.White + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = stringResource(id = R.string.assistant_screen_task_view_output), + color = Color.White, + modifier = Modifier + .padding(4.dp) + .clickable { expanded = !expanded } + ) + Text( text = if (expanded) task.output else task.output.take(100) + "...", + color = Color.White, modifier = Modifier - .padding(16.dp) + .padding(4.dp) .clickable { expanded = !expanded } ) @@ -72,6 +100,7 @@ fun TaskView( stringResource(id = R.string.assistant_screen_task_view_show_less) }, textAlign = TextAlign.End, + color = Color.White, modifier = Modifier .fillMaxWidth() .padding(16.dp) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 307e7acc31..dee3775740 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -546,6 +546,8 @@ public abstract class DrawerActivity extends ToolbarActivity startActivity(intent); } else if (itemId == R.id.nav_assistant) { // FIXME Back navigation is broken, create general function to switch to Jetpack Compose + + showSortListGroup(false); ComposeFragment composeFragment = new ComposeFragment(); Bundle bundle = new Bundle(); bundle.putSerializable(ComposeFragment.destinationKey, ComposeDestinations.AssistantScreen); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2f624a72d1..8121888306 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,9 +18,14 @@ Biggest first Smallest first + Task List are loading, please wait + No task available, you can create a new task from bottom right. + Task successfully created Type some text + Input: + Output: Show more Show less From 90b4be3215ba5cf2304cf7ca49b39fe9fa228c48 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 28 Feb 2024 16:34:47 +0100 Subject: [PATCH 012/102] Add loading and empty states Signed-off-by: alperozturk --- .../com/nextcloud/client/assistant/AssistantViewModel.kt | 2 -- .../java/com/nextcloud/client/assistant/AsssistantScreen.kt | 6 ------ 2 files changed, 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 2c2909dc95..04adb37022 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -21,12 +21,10 @@ package com.nextcloud.client.assistant -import androidx.compose.ui.res.stringResource import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient -import com.owncloud.android.R import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.assistant.model.TaskList import com.owncloud.android.lib.resources.assistant.model.TaskType diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index edb251bea6..174de5c419 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -23,28 +23,22 @@ package com.nextcloud.client.assistant import android.app.Activity import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.component.AddTaskAlertDialog import com.nextcloud.client.assistant.component.CenterText From 5ff7ffa9792e6df04b1e02fd2318fa81f92a77c7 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 28 Feb 2024 16:40:24 +0100 Subject: [PATCH 013/102] Add TODO commands Signed-off-by: alperozturk --- .../main/java/com/nextcloud/client/assistant/AsssistantScreen.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 174de5c419..b75b568bce 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -53,6 +53,7 @@ import com.owncloud.android.utils.DisplayUtils fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { // TODO hide sort group, search bar // TODO top bar, back button causes crash + // TODO generate list according to selection (selectedTask). val loading by viewModel.loading.collectAsState() val selectedTask by viewModel.selectedTask.collectAsState() val taskList by viewModel.taskList.collectAsState() From 9afdbd89c9643ef7f8d57e28321703d993c097c0 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 09:37:24 +0100 Subject: [PATCH 014/102] Add Task delete feature Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 34 ++++-- .../client/assistant/AsssistantScreen.kt | 22 ++-- .../assistant/component/AddTaskAlertDialog.kt | 2 +- .../client/assistant/component/TaskView.kt | 43 +++++++- .../repository/AssistantRepository.kt | 13 +-- .../repository/AssistantRepositoryType.kt | 11 +- .../{ => alertDialog}/SimpleAlertDialog.kt | 2 +- .../bottomSheet/MoreActionsBottomSheet.kt | 101 ++++++++++++++++++ app/src/main/res/values/strings.xml | 3 + 9 files changed, 189 insertions(+), 42 deletions(-) rename app/src/main/java/com/nextcloud/ui/composeComponents/{ => alertDialog}/SimpleAlertDialog.kt (98%) create mode 100644 app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 04adb37022..31288edd72 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -39,8 +39,8 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val repository: AssistantRepository = AssistantRepository(client) - private val _selectedTask = MutableStateFlow(null) - val selectedTask: StateFlow = _selectedTask + private val _selectedTaskType = MutableStateFlow(null) + val selectedTaskType: StateFlow = _selectedTaskType private val _taskTypes = MutableStateFlow?>(null) val taskTypes: StateFlow?> = _taskTypes @@ -54,6 +54,9 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _isTaskCreated = MutableStateFlow(false) val isTaskCreated: StateFlow = _isTaskCreated + private val _isTaskDeleted = MutableStateFlow(false) + val isTaskDeleted: StateFlow = _isTaskDeleted + init { getTaskTypes() getTaskList() @@ -73,7 +76,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } fun selectTask(task: TaskType) { - _selectedTask.update { + _selectedTaskType.update { task } } @@ -86,7 +89,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { result } - _selectedTask.update { + _selectedTaskType.update { result.resultData.types.first() } } @@ -106,13 +109,26 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } - - /* - fun deleteTask(id: String) { + fun deleteTask(id: Long) { viewModelScope.launch(Dispatchers.IO) { - repository?.deleteTask(id) + val result = repository.deleteTask(id) + + _isTaskDeleted.update { + if (result.isSuccess) { + removeTaskFromList(id) + } + + result.isSuccess + } } } - */ + private fun removeTaskFromList(id: Long) { + _taskList.update { currentList -> + currentList?.resultData?.tasks?.let { tasks -> + currentList.resultData.tasks = tasks.filter { it.id != id } + } + currentList + } + } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index b75b568bce..a3599c6962 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -54,10 +54,13 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin // TODO hide sort group, search bar // TODO top bar, back button causes crash // TODO generate list according to selection (selectedTask). + // TODO add swipe to refresh + val activity = LocalContext.current as Activity val loading by viewModel.loading.collectAsState() - val selectedTask by viewModel.selectedTask.collectAsState() + val selectedTaskType by viewModel.selectedTaskType.collectAsState() val taskList by viewModel.taskList.collectAsState() val isTaskCreated by viewModel.isTaskCreated.collectAsState() + val isTaskDeleted by viewModel.isTaskDeleted.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() var showAddTaskAlertDialog by remember { mutableStateOf(false) @@ -72,18 +75,25 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } else { val tasks = taskList?.resultData?.tasks ?: return val types = taskTypes?.resultData?.types ?: return - AssistantContent(tasks, types, selectedTask, viewModel) + AssistantContent(tasks, types, selectedTaskType, viewModel) } if (isTaskCreated) { DisplayUtils.showSnackMessage( - LocalContext.current as Activity, + activity, stringResource(id = R.string.assistant_screen_task_create_success_message) ) } + if (isTaskDeleted) { + DisplayUtils.showSnackMessage( + activity, + stringResource(id = R.string.assistant_screen_task_delete_success_message) + ) + } + if (showAddTaskAlertDialog) { - selectedTask?.let { + selectedTaskType?.let { AddTaskAlertDialog(viewModel, it) { showAddTaskAlertDialog = false } @@ -97,7 +107,7 @@ private fun AssistantContent( taskList: List, taskTypes: List, selectedTask: TaskType?, - viewModel: AssistantViewModel + viewModel: AssistantViewModel, ) { LazyColumn( modifier = Modifier @@ -116,7 +126,7 @@ private fun AssistantContent( if (taskList.isEmpty()) { CenterText(text = stringResource(id = R.string.assistant_screen_no_task_available_text)) } else { - TaskView(task = it) + TaskView(task = it, deleteTask = { viewModel.deleteTask(it.id) } ) Spacer(modifier = Modifier.height(8.dp)) } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index 14caeba4ff..238ae83bfd 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -33,7 +33,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import com.nextcloud.client.assistant.AssistantViewModel -import com.nextcloud.ui.composeComponents.SimpleAlertDialog +import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.TaskType diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index dd300be986..13be4ee264 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -22,9 +22,12 @@ package com.nextcloud.client.assistant.component import android.annotation.SuppressLint -import android.widget.Space +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -43,15 +46,20 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task +@OptIn(ExperimentalFoundationApi::class) @SuppressLint("ResourceAsColor") @Composable fun TaskView( task: Task, + deleteTask: () -> Unit, ) { var expanded by remember { mutableStateOf(false) } + var showMoreActionsBottomSheet by remember { mutableStateOf(false) } + // TODO Check color Column( @@ -59,6 +67,11 @@ fun TaskView( .fillMaxWidth() .clip(RoundedCornerShape(16.dp)) .background(Color(R.color.primary)) + .combinedClickable(onClick = { + expanded = !expanded + }, onLongClick = { + showMoreActionsBottomSheet = true + }) ) { Spacer(modifier = Modifier.height(8.dp)) @@ -81,15 +94,19 @@ fun TaskView( color = Color.White, modifier = Modifier .padding(4.dp) - .clickable { expanded = !expanded } ) Text( text = if (expanded) task.output else task.output.take(100) + "...", color = Color.White, modifier = Modifier + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ) .padding(4.dp) - .clickable { expanded = !expanded } ) if (task.output.length >= 100) { @@ -104,7 +121,23 @@ fun TaskView( modifier = Modifier .fillMaxWidth() .padding(16.dp) - .clickable { expanded = true } + ) + } + + if (showMoreActionsBottomSheet) { + val bottomSheetAction = listOf( + Triple( + R.drawable.ic_delete, + R.string.assistant_screen_task_more_actions_bottom_sheet_delete_action + ) { + deleteTask() + }, + ) + + MoreActionsBottomSheet( + title = task.input, + actions = bottomSheetAction, + dismiss = { showMoreActionsBottomSheet = false } ) } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt index df29df35d6..a1198d846f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt @@ -24,6 +24,7 @@ package com.nextcloud.client.assistant.repository import com.nextcloud.common.NextcloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.assistant.CreateTaskRemoteOperation +import com.owncloud.android.lib.resources.assistant.DeleteTaskRemoteOperation import com.owncloud.android.lib.resources.assistant.GetTaskListRemoteOperation import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation import com.owncloud.android.lib.resources.assistant.model.TaskList @@ -46,15 +47,7 @@ class AssistantRepository(private val client: NextcloudClient) : AssistantReposi return GetTaskListRemoteOperation(appId).execute(client) } - /* - // TODO Check return type - override fun deleteTask(id: String): CreatedTask? { - return operation.delete("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java) + override fun deleteTask(id: Long): RemoteOperationResult { + return DeleteTaskRemoteOperation(id).execute(client) } - - // TODO Check return type - override fun getTask(id: String): CreatedTask? { - return operation.get("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java) - } - */ } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt index 2f5c1081ca..8a21a8638a 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt @@ -35,14 +35,5 @@ interface AssistantRepositoryType { fun getTaskList(appId: String): RemoteOperationResult - /* - fun getTask(id: String): CreatedTask? - - fun deleteTask(id: String): CreatedTask? - - - - - */ - + fun deleteTask(id: Long): RemoteOperationResult } diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt similarity index 98% rename from app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt rename to app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt index cf727b87d9..fc47fe5307 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.nextcloud.ui.composeComponents +package com.nextcloud.ui.composeComponents.alertDialog import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt new file mode 100644 index 0000000000..00c004e6df --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt @@ -0,0 +1,101 @@ +package com.nextcloud.ui.composeComponents.bottomSheet + +import android.annotation.SuppressLint +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.owncloud.android.R +import kotlinx.coroutines.launch + +@SuppressLint("ResourceAsColor") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MoreActionsBottomSheet( + title: String? = null, + actions: List Unit>>, + dismiss: () -> Unit +) { + val sheetState = rememberModalBottomSheetState() + val scope = rememberCoroutineScope() + + ModalBottomSheet( + modifier = Modifier.padding(top = 32.dp), + onDismissRequest = { + dismiss() + }, + sheetState = sheetState + ) { + Column( + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.Top, + modifier = Modifier + .fillMaxWidth() + .padding(all = 8.dp) + ) { + title?.let { + Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) { + Text(text = title, fontSize = 18.sp) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + actions.forEach { action -> + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + scope + .launch { sheetState.hide() } + .invokeOnCompletion { + if (!sheetState.isVisible) { + action.third() + dismiss() + } + } + } + .padding(all = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(id = action.first), + contentDescription = "action icon", + tint = Color(R.color.secondary_button_background_color), + modifier = Modifier.size(20.dp) + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Text( + text = stringResource(action.second), + fontSize = 16.sp, + ) + } + } + + Spacer(modifier = Modifier.height(32.dp)) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8121888306..3559014476 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,7 +21,10 @@ Task List are loading, please wait No task available, you can create a new task from bottom right. + Delete Task + Task successfully created + Task deleted Type some text Input: From 07edd95bf604ce1a55014e3ae9a1c2f981e7cdae Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 09:48:29 +0100 Subject: [PATCH 015/102] Add Pull To Refresh for fetching task list Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 4 +- .../client/assistant/AsssistantScreen.kt | 41 +++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 31288edd72..efe400310c 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -95,7 +95,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } - private fun getTaskList(appId: String = "assistant") { + fun getTaskList(appId: String = "assistant", onCompleted: () -> Unit = {}) { viewModelScope.launch(Dispatchers.IO) { val result = repository.getTaskList(appId) @@ -106,6 +106,8 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { _loading.update { false } + + onCompleted() } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index a3599c6962..6d9db09f24 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -23,19 +23,26 @@ package com.nextcloud.client.assistant import android.app.Activity import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -48,13 +55,14 @@ import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.utils.DisplayUtils +import kotlinx.coroutines.delay +@OptIn(ExperimentalMaterial3Api::class) @Composable fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { // TODO hide sort group, search bar // TODO top bar, back button causes crash // TODO generate list according to selection (selectedTask). - // TODO add swipe to refresh val activity = LocalContext.current as Activity val loading by viewModel.loading.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() @@ -66,16 +74,35 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin mutableStateOf(false) } + val pullRefreshState = rememberPullToRefreshState() + + if (pullRefreshState.isRefreshing) { + LaunchedEffect(true) { + delay(1500) + viewModel.getTaskList(onCompleted = { + pullRefreshState.endRefresh() + }) + } + } + floatingActionButton.setOnClickListener { showAddTaskAlertDialog = true } - if (loading) { - CenterText(text = stringResource(id = R.string.assistant_screen_loading)) - } else { - val tasks = taskList?.resultData?.tasks ?: return - val types = taskTypes?.resultData?.types ?: return - AssistantContent(tasks, types, selectedTaskType, viewModel) + Box(Modifier.nestedScroll(pullRefreshState.nestedScrollConnection)) { + if (loading || pullRefreshState.isRefreshing) { + CenterText(text = stringResource(id = R.string.assistant_screen_loading)) + } else { + val tasks = taskList?.resultData?.tasks ?: return + val types = taskTypes?.resultData?.types ?: return + AssistantContent(tasks, types, selectedTaskType, viewModel) + } + + if (pullRefreshState.isRefreshing) { + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + } else { + LinearProgressIndicator(progress = { pullRefreshState.progress }, modifier = Modifier.fillMaxWidth()) + } } if (isTaskCreated) { From cbe3b8e58edd779ba02bfae83f764aba88a24f21 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 10:01:38 +0100 Subject: [PATCH 016/102] Better feedback for task deletion Signed-off-by: alperozturk --- .../client/assistant/AsssistantScreen.kt | 32 +++++++++++++++---- .../client/assistant/component/TaskView.kt | 4 +-- app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 6d9db09f24..117a32389c 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -42,6 +42,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -51,6 +52,7 @@ import com.nextcloud.client.assistant.component.AddTaskAlertDialog import com.nextcloud.client.assistant.component.CenterText import com.nextcloud.client.assistant.component.TaskTypesRow import com.nextcloud.client.assistant.component.TaskView +import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskType @@ -70,10 +72,11 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin val isTaskCreated by viewModel.isTaskCreated.collectAsState() val isTaskDeleted by viewModel.isTaskDeleted.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() - var showAddTaskAlertDialog by remember { - mutableStateOf(false) + var showAddTaskAlertDialog by remember { mutableStateOf(false) } + var showDeleteTaskAlertDialog by remember { mutableStateOf(false) } + var taskIdToDeleted: Long? by remember { + mutableStateOf(null) } - val pullRefreshState = rememberPullToRefreshState() if (pullRefreshState.isRefreshing) { @@ -95,7 +98,10 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } else { val tasks = taskList?.resultData?.tasks ?: return val types = taskTypes?.resultData?.types ?: return - AssistantContent(tasks, types, selectedTaskType, viewModel) + AssistantContent(tasks, types, selectedTaskType, viewModel, showDeleteTaskAlertDialog = { taskId -> + taskIdToDeleted = taskId + showDeleteTaskAlertDialog = true + }) } if (pullRefreshState.isRefreshing) { @@ -119,6 +125,19 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin ) } + if (showDeleteTaskAlertDialog) { + taskIdToDeleted?.let { id -> + SimpleAlertDialog( + backgroundColor = Color.White, + textColor = Color.Black, + title =stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title), + description = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_description), + dismiss = {showDeleteTaskAlertDialog = false }, + onComplete = { viewModel.deleteTask(id) }, + ) + } + } + if (showAddTaskAlertDialog) { selectedTaskType?.let { AddTaskAlertDialog(viewModel, it) { @@ -135,6 +154,7 @@ private fun AssistantContent( taskTypes: List, selectedTask: TaskType?, viewModel: AssistantViewModel, + showDeleteTaskAlertDialog: (Long) -> Unit, ) { LazyColumn( modifier = Modifier @@ -149,11 +169,11 @@ private fun AssistantContent( Spacer(modifier = Modifier.height(8.dp)) } - items(taskList) { + items(taskList) { task -> if (taskList.isEmpty()) { CenterText(text = stringResource(id = R.string.assistant_screen_no_task_available_text)) } else { - TaskView(task = it, deleteTask = { viewModel.deleteTask(it.id) } ) + TaskView(task, showDeleteTaskAlertDialog = { showDeleteTaskAlertDialog(task.id) }) Spacer(modifier = Modifier.height(8.dp)) } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 13be4ee264..aaf7fd110d 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -55,7 +55,7 @@ import com.owncloud.android.lib.resources.assistant.model.Task @Composable fun TaskView( task: Task, - deleteTask: () -> Unit, + showDeleteTaskAlertDialog: (Long) -> Unit, ) { var expanded by remember { mutableStateOf(false) } var showMoreActionsBottomSheet by remember { mutableStateOf(false) } @@ -130,7 +130,7 @@ fun TaskView( R.drawable.ic_delete, R.string.assistant_screen_task_more_actions_bottom_sheet_delete_action ) { - deleteTask() + showDeleteTaskAlertDialog(task.id) }, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3559014476..f9c6cc9da1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,8 @@ Task List are loading, please wait No task available, you can create a new task from bottom right. + Delete Task + Are you sure you want to delete this task? Delete Task From 642f858bb2b4b650ca3efca3bf75b609c5a78b1b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 10:12:26 +0100 Subject: [PATCH 017/102] Handle Nullable responses Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 6 +-- .../client/assistant/AsssistantScreen.kt | 11 +++++- .../assistant/component/AddTaskAlertDialog.kt | 8 +++- .../assistant/component/TaskTypesRow.kt | 30 ++++++++------- .../client/assistant/component/TaskView.kt | 38 ++++++++++--------- app/src/main/res/values/strings.xml | 3 +- 6 files changed, 57 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index efe400310c..7365aa5f11 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -54,8 +54,8 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _isTaskCreated = MutableStateFlow(false) val isTaskCreated: StateFlow = _isTaskCreated - private val _isTaskDeleted = MutableStateFlow(false) - val isTaskDeleted: StateFlow = _isTaskDeleted + private val _isTaskDeleted = MutableStateFlow(null) + val isTaskDeleted: StateFlow = _isTaskDeleted init { getTaskTypes() @@ -90,7 +90,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } _selectedTaskType.update { - result.resultData.types.first() + result.resultData.types?.first() } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 117a32389c..ceb593601b 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -98,6 +98,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } else { val tasks = taskList?.resultData?.tasks ?: return val types = taskTypes?.resultData?.types ?: return + AssistantContent(tasks, types, selectedTaskType, viewModel, showDeleteTaskAlertDialog = { taskId -> taskIdToDeleted = taskId showDeleteTaskAlertDialog = true @@ -118,10 +119,16 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin ) } - if (isTaskDeleted) { + isTaskDeleted?.let { + val messageId = if (it) { + R.string.assistant_screen_task_delete_success_message + } else { + R.string.assistant_screen_task_delete_success_message + } + DisplayUtils.showSnackMessage( activity, - stringResource(id = R.string.assistant_screen_task_delete_success_message) + stringResource(id = messageId) ) } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index 238ae83bfd..aa10b9dbae 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -46,10 +46,14 @@ fun AddTaskAlertDialog(viewModel: AssistantViewModel, taskType: TaskType, dismis SimpleAlertDialog( backgroundColor = Color.White, textColor = Color.Black, - title = taskType.name, + title = taskType.name ?: "", description = taskType.description, dismiss = { dismiss() }, - onComplete = { viewModel.createTask(input = input, type = taskType.id) }, + onComplete = { + taskType.id?.let { + viewModel.createTask(input = input, type = it) + } + }, content = { TextField( placeholder = { diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt index 7a849169fb..2a86f9dd64 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt @@ -43,21 +43,23 @@ fun TaskTypesRow(selectedTaskType: TaskType?, data: List?, selectTaskT .fillMaxWidth() .horizontalScroll(rememberScrollState()) ) { - data?.forEach { - FilledTonalButton( - onClick = { selectTaskType(it) }, - colors = ButtonDefaults.buttonColors( - containerColor = if (selectedTaskType?.id == it.id) { - Color.Unspecified - } else { - Color.Gray - } - ) - ) { - Text(text = it.name) - } + data?.forEach { taskType -> + taskType.name?.let { taskTypeName -> + FilledTonalButton( + onClick = { selectTaskType(taskType) }, + colors = ButtonDefaults.buttonColors( + containerColor = if (selectedTaskType?.id == taskType.id) { + Color.Unspecified + } else { + Color.Gray + } + ) + ) { + Text(text = taskTypeName) + } - Spacer(modifier = Modifier.padding(end = 8.dp)) + Spacer(modifier = Modifier.padding(end = 8.dp)) + } } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index aaf7fd110d..187de9b2a0 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -81,11 +81,13 @@ fun TaskView( color = Color.White ) - Text( - text = task.input, - modifier = Modifier.padding(4.dp), - color = Color.White - ) + task.input?.let { + Text( + text = it, + modifier = Modifier.padding(4.dp), + color = Color.White + ) + } Spacer(modifier = Modifier.height(16.dp)) @@ -96,20 +98,22 @@ fun TaskView( .padding(4.dp) ) - Text( - text = if (expanded) task.output else task.output.take(100) + "...", - color = Color.White, - modifier = Modifier - .animateContentSize( - animationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow + task.output?.let { + Text( + text = if (expanded) it else it.take(100) + "...", + color = Color.White, + modifier = Modifier + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) ) - ) - .padding(4.dp) - ) + .padding(4.dp) + ) + } - if (task.output.length >= 100) { + if ((task.output?.length ?: 0) >= 100) { Text( text = if (!expanded) { stringResource(id = R.string.assistant_screen_task_view_show_more) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f9c6cc9da1..7d29fbd1c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,7 +26,8 @@ Delete Task Task successfully created - Task deleted + An error occurred while deleting the task + TS Type some text Input: From d39d5257ac5a3b09d4b4687508facf7346de67ff Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 10:34:40 +0100 Subject: [PATCH 018/102] Add filter task type Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 36 ++++++++++++++----- .../client/assistant/AsssistantScreen.kt | 6 ++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 7365aa5f11..b7f6faaae7 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -45,8 +45,10 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _taskTypes = MutableStateFlow?>(null) val taskTypes: StateFlow?> = _taskTypes - private val _taskList = MutableStateFlow?>(null) - val taskList: StateFlow?> = _taskList + private var _taskList: RemoteOperationResult? = null + + private val _filteredTaskList = MutableStateFlow?>(null) + val filteredTaskList: StateFlow?> = _filteredTaskList private val _loading = MutableStateFlow(true) val loading: StateFlow = _loading @@ -75,8 +77,9 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } - fun selectTask(task: TaskType) { + fun selectTaskType(task: TaskType) { _selectedTaskType.update { + filterTaskList(it?.id) task } } @@ -84,6 +87,11 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private fun getTaskTypes() { viewModelScope.launch(Dispatchers.IO) { val result = repository.getTaskTypes() + val new = ArrayList(result.resultData.types ?: listOf()).apply { + add(TaskType(null, "All", null)) + } + + result.resultData.types = new _taskTypes.update { result @@ -97,11 +105,9 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { fun getTaskList(appId: String = "assistant", onCompleted: () -> Unit = {}) { viewModelScope.launch(Dispatchers.IO) { - val result = repository.getTaskList(appId) + _taskList = repository.getTaskList(appId) - _taskList.update { - result - } + filterTaskList(_selectedTaskType.value?.id) _loading.update { false @@ -125,8 +131,22 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } + fun filterTaskList(taskTypeId: String?) { + if (taskTypeId == null) { + _filteredTaskList.update { + _taskList + } + } else { + val result = _taskList?.resultData?.tasks?.filter { it.type == taskTypeId } + _filteredTaskList.update { + it?.resultData?.tasks = result + it + } + } + } + private fun removeTaskFromList(id: Long) { - _taskList.update { currentList -> + _filteredTaskList.update { currentList -> currentList?.resultData?.tasks?.let { tasks -> currentList.resultData.tasks = tasks.filter { it.id != id } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index ceb593601b..6136098f1c 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -68,7 +68,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin val activity = LocalContext.current as Activity val loading by viewModel.loading.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() - val taskList by viewModel.taskList.collectAsState() + val filteredTaskList by viewModel.filteredTaskList.collectAsState() val isTaskCreated by viewModel.isTaskCreated.collectAsState() val isTaskDeleted by viewModel.isTaskDeleted.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() @@ -96,7 +96,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin if (loading || pullRefreshState.isRefreshing) { CenterText(text = stringResource(id = R.string.assistant_screen_loading)) } else { - val tasks = taskList?.resultData?.tasks ?: return + val tasks = filteredTaskList?.resultData?.tasks ?: return val types = taskTypes?.resultData?.types ?: return AssistantContent(tasks, types, selectedTaskType, viewModel, showDeleteTaskAlertDialog = { taskId -> @@ -170,7 +170,7 @@ private fun AssistantContent( ) { stickyHeader { TaskTypesRow(selectedTask, data = taskTypes) { task -> - viewModel.selectTask(task) + viewModel.selectTaskType(task) } Spacer(modifier = Modifier.height(8.dp)) From a52ec924ab1ef099b92be029250b8159de3b8e30 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 10:39:39 +0100 Subject: [PATCH 019/102] Simplify viewModel Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 36 +++++++------------ .../client/assistant/AsssistantScreen.kt | 23 ++++++------ 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index b7f6faaae7..2967c17395 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -25,10 +25,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.assistant.model.TaskList +import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskType -import com.owncloud.android.lib.resources.assistant.model.TaskTypes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -42,13 +40,13 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _selectedTaskType = MutableStateFlow(null) val selectedTaskType: StateFlow = _selectedTaskType - private val _taskTypes = MutableStateFlow?>(null) - val taskTypes: StateFlow?> = _taskTypes + private val _taskTypes = MutableStateFlow?>(null) + val taskTypes: StateFlow?> = _taskTypes - private var _taskList: RemoteOperationResult? = null + private var _taskList: List? = null - private val _filteredTaskList = MutableStateFlow?>(null) - val filteredTaskList: StateFlow?> = _filteredTaskList + private val _filteredTaskList = MutableStateFlow?>(null) + val filteredTaskList: StateFlow?> = _filteredTaskList private val _loading = MutableStateFlow(true) val loading: StateFlow = _loading @@ -86,26 +84,21 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private fun getTaskTypes() { viewModelScope.launch(Dispatchers.IO) { - val result = repository.getTaskTypes() - val new = ArrayList(result.resultData.types ?: listOf()).apply { - add(TaskType(null, "All", null)) - } - - result.resultData.types = new + val result = repository.getTaskTypes().resultData.types _taskTypes.update { result } _selectedTaskType.update { - result.resultData.types?.first() + result?.first() } } } fun getTaskList(appId: String = "assistant", onCompleted: () -> Unit = {}) { viewModelScope.launch(Dispatchers.IO) { - _taskList = repository.getTaskList(appId) + _taskList = repository.getTaskList(appId).resultData.tasks filterTaskList(_selectedTaskType.value?.id) @@ -131,26 +124,21 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } - fun filterTaskList(taskTypeId: String?) { + private fun filterTaskList(taskTypeId: String?) { if (taskTypeId == null) { _filteredTaskList.update { _taskList } } else { - val result = _taskList?.resultData?.tasks?.filter { it.type == taskTypeId } _filteredTaskList.update { - it?.resultData?.tasks = result - it + _taskList?.filter { it.type == taskTypeId } } } } private fun removeTaskFromList(id: Long) { _filteredTaskList.update { currentList -> - currentList?.resultData?.tasks?.let { tasks -> - currentList.resultData.tasks = tasks.filter { it.id != id } - } - currentList + currentList?.filter { it.id != id } } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 6136098f1c..5a8171ad87 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -96,13 +96,16 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin if (loading || pullRefreshState.isRefreshing) { CenterText(text = stringResource(id = R.string.assistant_screen_loading)) } else { - val tasks = filteredTaskList?.resultData?.tasks ?: return - val types = taskTypes?.resultData?.types ?: return - - AssistantContent(tasks, types, selectedTaskType, viewModel, showDeleteTaskAlertDialog = { taskId -> - taskIdToDeleted = taskId - showDeleteTaskAlertDialog = true - }) + AssistantContent( + filteredTaskList ?: listOf(), + taskTypes, + selectedTaskType, + viewModel, + showDeleteTaskAlertDialog = { taskId -> + taskIdToDeleted = taskId + showDeleteTaskAlertDialog = true + } + ) } if (pullRefreshState.isRefreshing) { @@ -137,9 +140,9 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin SimpleAlertDialog( backgroundColor = Color.White, textColor = Color.Black, - title =stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title), + title = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title), description = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_description), - dismiss = {showDeleteTaskAlertDialog = false }, + dismiss = { showDeleteTaskAlertDialog = false }, onComplete = { viewModel.deleteTask(id) }, ) } @@ -158,7 +161,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin @Composable private fun AssistantContent( taskList: List, - taskTypes: List, + taskTypes: List?, selectedTask: TaskType?, viewModel: AssistantViewModel, showDeleteTaskAlertDialog: (Long) -> Unit, From cbc9466d2922bd3749468392dfb3f19e550c4d60 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 10:49:18 +0100 Subject: [PATCH 020/102] Add all section to task types Signed-off-by: alperozturk --- .../com/nextcloud/client/assistant/AssistantViewModel.kt | 8 +++++--- .../com/nextcloud/client/assistant/AsssistantScreen.kt | 9 ++++++++- app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 2967c17395..3d18c6cd78 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -84,14 +84,16 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private fun getTaskTypes() { viewModelScope.launch(Dispatchers.IO) { - val result = repository.getTaskTypes().resultData.types + val result = arrayListOf(TaskType(null, "All", null)) + val taskTypes = repository.getTaskTypes().resultData.types ?: listOf() + result.addAll(taskTypes) _taskTypes.update { - result + result.toList() } _selectedTaskType.update { - result?.first() + result.first() } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 5a8171ad87..a646d08178 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -89,7 +89,14 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } floatingActionButton.setOnClickListener { - showAddTaskAlertDialog = true + if (selectedTaskType?.id != null) { + showAddTaskAlertDialog = true + } else { + DisplayUtils.showSnackMessage( + activity, + activity.getString(R.string.assistant_screen_select_different_task_type_to_add) + ) + } } Box(Modifier.nestedScroll(pullRefreshState.nestedScrollConnection)) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d29fbd1c3..ad16d86ae0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,8 @@ Delete Task Are you sure you want to delete this task? + Please select different task type to create a new task + Delete Task Task successfully created From 9ddf9d902f3ed7adbc6a60cc4c68d7b3bb0b534b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 11:09:02 +0100 Subject: [PATCH 021/102] Add task list selection according to task type Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 2 +- .../client/assistant/AsssistantScreen.kt | 58 +++++++++++++------ app/src/main/res/values/strings.xml | 2 +- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 3d18c6cd78..00516f02be 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -77,7 +77,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { fun selectTaskType(task: TaskType) { _selectedTaskType.update { - filterTaskList(it?.id) + filterTaskList(task.id) task } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index a646d08178..9200a3fc7f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -24,6 +24,7 @@ package com.nextcloud.client.assistant import android.app.Activity import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -64,7 +65,6 @@ import kotlinx.coroutines.delay fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { // TODO hide sort group, search bar // TODO top bar, back button causes crash - // TODO generate list according to selection (selectedTask). val activity = LocalContext.current as Activity val loading by viewModel.loading.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() @@ -103,16 +103,20 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin if (loading || pullRefreshState.isRefreshing) { CenterText(text = stringResource(id = R.string.assistant_screen_loading)) } else { - AssistantContent( - filteredTaskList ?: listOf(), - taskTypes, - selectedTaskType, - viewModel, - showDeleteTaskAlertDialog = { taskId -> - taskIdToDeleted = taskId - showDeleteTaskAlertDialog = true - } - ) + if (filteredTaskList.isNullOrEmpty()) { + EmptyTaskList(selectedTaskType, taskTypes, viewModel) + } else { + AssistantContent( + filteredTaskList!!, + taskTypes, + selectedTaskType, + viewModel, + showDeleteTaskAlertDialog = { taskId -> + taskIdToDeleted = taskId + showDeleteTaskAlertDialog = true + } + ) + } } if (pullRefreshState.isRefreshing) { @@ -169,7 +173,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin private fun AssistantContent( taskList: List, taskTypes: List?, - selectedTask: TaskType?, + selectedTaskType: TaskType?, viewModel: AssistantViewModel, showDeleteTaskAlertDialog: (Long) -> Unit, ) { @@ -179,7 +183,7 @@ private fun AssistantContent( .padding(16.dp) ) { stickyHeader { - TaskTypesRow(selectedTask, data = taskTypes) { task -> + TaskTypesRow(selectedTaskType, data = taskTypes) { task -> viewModel.selectTaskType(task) } @@ -187,12 +191,28 @@ private fun AssistantContent( } items(taskList) { task -> - if (taskList.isEmpty()) { - CenterText(text = stringResource(id = R.string.assistant_screen_no_task_available_text)) - } else { - TaskView(task, showDeleteTaskAlertDialog = { showDeleteTaskAlertDialog(task.id) }) - Spacer(modifier = Modifier.height(8.dp)) - } + TaskView(task, showDeleteTaskAlertDialog = { showDeleteTaskAlertDialog(task.id) }) + Spacer(modifier = Modifier.height(8.dp)) } } } + +@Composable +private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List?, viewModel: AssistantViewModel) { + Column(modifier = Modifier + .fillMaxSize() + .padding(16.dp)) { + TaskTypesRow(selectedTaskType, data = taskTypes) { task -> + viewModel.selectTaskType(task) + } + + Spacer(modifier = Modifier.height(8.dp)) + + CenterText( + text = stringResource( + id = R.string.assistant_screen_no_task_available_text, + selectedTaskType?.name ?: "" + ) + ) + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ad16d86ae0..016fe4bf86 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,7 +19,7 @@ Smallest first Task List are loading, please wait - No task available, you can create a new task from bottom right. + No task available for %s task type, you can create a new task from bottom right. Delete Task Are you sure you want to delete this task? From 5fa6b17fe1fd0194a7e5c5e2c75e08a34e08459d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 11:32:55 +0100 Subject: [PATCH 022/102] Extract navigation to function Signed-off-by: alperozturk --- ...eDestinations.kt => ComposeDestination.kt} | 2 +- .../ui/composeFragment/ComposeFragment.kt | 12 ++++---- .../android/ui/activity/DrawerActivity.java | 30 +++++++++++-------- .../android/ui/activity/ToolbarActivity.java | 8 +++++ 4 files changed, 32 insertions(+), 20 deletions(-) rename app/src/main/java/com/nextcloud/ui/composeFragment/{ComposeDestinations.kt => ComposeDestination.kt} (96%) diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt similarity index 96% rename from app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt rename to app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt index 8809b1b0c3..c503a541a1 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt @@ -21,6 +21,6 @@ package com.nextcloud.ui.composeFragment -enum class ComposeDestinations { +enum class ComposeDestination { AssistantScreen } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index 225fa34239..67ce389090 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -21,6 +21,7 @@ package com.nextcloud.ui.composeFragment +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -32,12 +33,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.fragment.app.Fragment import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.common.NextcloudClient -import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.R import com.owncloud.android.databinding.FragmentComposeViewBinding @@ -53,7 +52,7 @@ class ComposeFragment : FileFragment() { private var _binding: FragmentComposeViewBinding? = null private val binding get() = _binding!! - private var destination: ComposeDestinations? = null + private var destination: ComposeDestination? = null companion object { const val destinationKey = "destinationKey" @@ -65,7 +64,7 @@ class ComposeFragment : FileFragment() { savedInstanceState: Bundle? ): View { _binding = FragmentComposeViewBinding.inflate(inflater, container, false) - destination = arguments.getSerializableArgument(destinationKey, ComposeDestinations::class.java) + destination = arguments.getSerializableArgument(destinationKey, ComposeDestination::class.java) binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) @@ -78,8 +77,9 @@ class ComposeFragment : FileFragment() { return binding.root } + @SuppressLint("UnusedContentLambdaTargetStateParameter") @Composable - private fun Content(destination: ComposeDestinations?) { + private fun Content(destination: ComposeDestination?) { val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) var nextcloudClient by remember { mutableStateOf(null) } @@ -87,7 +87,7 @@ class ComposeFragment : FileFragment() { nextcloudClient = getNextcloudClient() } - return if (destination == ComposeDestinations.AssistantScreen && nextcloudClient != null) { + return if (destination == ComposeDestination.AssistantScreen && nextcloudClient != null) { AssistantScreen( viewModel = AssistantViewModel( client = nextcloudClient!! diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index dee3775740..1da2c5b464 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -74,7 +74,7 @@ import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.NextcloudClient; import com.nextcloud.java.util.Optional; import com.nextcloud.ui.ChooseAccountDialogFragment; -import com.nextcloud.ui.composeFragment.ComposeDestinations; +import com.nextcloud.ui.composeFragment.ComposeDestination; import com.nextcloud.ui.composeFragment.ComposeFragment; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -545,18 +545,7 @@ public abstract class DrawerActivity extends ToolbarActivity intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); } else if (itemId == R.id.nav_assistant) { - // FIXME Back navigation is broken, create general function to switch to Jetpack Compose - - showSortListGroup(false); - ComposeFragment composeFragment = new ComposeFragment(); - Bundle bundle = new Bundle(); - bundle.putSerializable(ComposeFragment.destinationKey, ComposeDestinations.AssistantScreen); - composeFragment.setArguments(bundle); - - getSupportFragmentManager() - .beginTransaction() - .replace(R.id.left_fragment_container, composeFragment) - .commit(); + navigateComposeView(ComposeDestination.AssistantScreen, false, true); } else { if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) { @@ -568,6 +557,21 @@ public abstract class DrawerActivity extends ToolbarActivity } } + private void navigateComposeView(ComposeDestination destination, boolean showSortListGroup, boolean hideSearchText) { + showSortListGroup(showSortListGroup); + hideSearchText(hideSearchText); + + ComposeFragment composeFragment = new ComposeFragment(); + Bundle bundle = new Bundle(); + bundle.putSerializable(ComposeFragment.destinationKey, destination); + composeFragment.setArguments(bundle); + + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.left_fragment_container, composeFragment) + .commit(); + } + private void startActivity(Class activity) { startActivity(new Intent(getApplicationContext(), activity)); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index b9823f0fa1..74d32aefd0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -155,6 +155,14 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable mSearchText.setText(getString(R.string.appbar_search_in, title)); } + public void hideSearchText(boolean hide) { + if (hide) { + mSearchText.setVisibility(View.GONE); + } else { + mSearchText.setVisibility(View.VISIBLE); + } + } + @SuppressLint("PrivateResource") private void showHomeSearchToolbar(boolean isShow) { viewThemeUtils.material.themeToolbar(mToolbar); From b9cc8a11c6496a0ef470efbca6c1835539d298b9 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 5 Mar 2024 14:38:36 +0100 Subject: [PATCH 023/102] Solve git conflicts Signed-off-by: alperozturk --- settings.gradle | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/settings.gradle b/settings.gradle index 34030893f3..0e7a78bade 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,10 +13,4 @@ include ':appscan' // dependencySubstitution { // substitute module('com.github.nextcloud:android-library') using project(':library') // } -//} - -includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_library') { - dependencySubstitution { - substitute module('com.github.nextcloud:android-library') using project(':library') - } -} +//} \ No newline at end of file From 292c6604ead3b5af69fb06f340a171adbabbe9f9 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 12:38:40 +0100 Subject: [PATCH 024/102] Reset Signed-off-by: alperozturk --- .../java/com/owncloud/android/AbstractOnServerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index bcd43ccafb..343938e121 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -112,7 +112,7 @@ public abstract class AbstractOnServerIT extends AbstractIT { @After public void after() { - // deleteAllFilesOnServer(); + deleteAllFilesOnServer(); super.after(); } From f21fc1f4dc175668ea50a2f91329a25b47cd9107 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 12:54:56 +0100 Subject: [PATCH 025/102] Add local libs warnings Signed-off-by: alperozturk --- gradle.properties | 3 +++ settings.gradle | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 58e60e8e55..43fcf18f1b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,3 +16,6 @@ kotlin.daemon.jvmargs=-Xmx4096m org.gradle.caching=true org.gradle.parallel=true org.gradle.configureondemand=true + +# Needed for local libs +org.gradle.dependency.verification=lenient \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 0e7a78bade..34030893f3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,4 +13,10 @@ include ':appscan' // dependencySubstitution { // substitute module('com.github.nextcloud:android-library') using project(':library') // } -//} \ No newline at end of file +//} + +includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_library') { + dependencySubstitution { + substitute module('com.github.nextcloud:android-library') using project(':library') + } +} From b77bbe742f8c232fc985f92c3980fdef4695ae4d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 14:37:46 +0100 Subject: [PATCH 026/102] Fixes Signed-off-by: alperozturk --- .../java/com/nextcloud/client/assistant/AsssistantScreen.kt | 2 -- .../java/com/nextcloud/ui/composeFragment/ComposeFragment.kt | 2 -- .../java/com/owncloud/android/ui/activity/DrawerActivity.java | 3 +++ 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 9200a3fc7f..2fde66d53e 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -63,8 +63,6 @@ import kotlinx.coroutines.delay @OptIn(ExperimentalMaterial3Api::class) @Composable fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { - // TODO hide sort group, search bar - // TODO top bar, back button causes crash val activity = LocalContext.current as Activity val loading by viewModel.loading.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index 67ce389090..7ec2a3e85f 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -21,7 +21,6 @@ package com.nextcloud.ui.composeFragment -import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -77,7 +76,6 @@ class ComposeFragment : FileFragment() { return binding.root } - @SuppressLint("UnusedContentLambdaTargetStateParameter") @Composable private fun Content(destination: ComposeDestination?) { val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 1da2c5b464..afa4a7f85a 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -248,6 +248,9 @@ public abstract class DrawerActivity extends ToolbarActivity if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } + + showSortListGroup(true); + hideSearchText(false); } /** From 3fc5f426facd3dc6afad2407e2b0c34bf8e36055 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 15:24:18 +0100 Subject: [PATCH 027/102] Navigation fixes Signed-off-by: alperozturk --- .../assistant/AssistantRepositoryTests.kt | 4 +- app/src/main/AndroidManifest.xml | 179 ++++++++---------- .../client/assistant/AssistantViewModel.kt | 8 +- .../client/assistant/AsssistantScreen.kt | 53 ++++-- .../assistant/component/AddTaskAlertDialog.kt | 4 +- .../client/assistant/component/TaskView.kt | 5 +- .../repository/AssistantRepository.kt | 2 +- .../repository/AssistantRepositoryType.kt | 2 +- .../nextcloud/client/di/ComponentsModule.java | 4 + .../ComposeActivity.kt} | 86 +++++---- .../ComposeDestination.kt | 8 +- .../bottomSheet/MoreActionsBottomSheet.kt | 6 +- .../android/ui/activity/CommunityActivity.kt | 1 - .../android/ui/activity/DrawerActivity.java | 28 +-- .../android/ui/activity/ToolbarActivity.java | 8 - app/src/main/res/layout/activity_compose.xml | 54 ++++++ app/src/main/res/values/strings.xml | 3 +- 17 files changed, 254 insertions(+), 201 deletions(-) rename app/src/main/java/com/nextcloud/ui/{composeFragment/ComposeFragment.kt => composeActivity/ComposeActivity.kt} (55%) rename app/src/main/java/com/nextcloud/ui/{composeFragment => composeActivity}/ComposeDestination.kt (87%) create mode 100644 app/src/main/res/layout/activity_compose.xml diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index a935ca1ad1..dfdddc308d 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -28,7 +28,7 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test -class AssistantRepositoryTests: AbstractOnServerIT() { +class AssistantRepositoryTests : AbstractOnServerIT() { private var sut: AssistantRepository? = null @@ -65,6 +65,4 @@ class AssistantRepositoryTests: AbstractOnServerIT() { } */ - - } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c0494a90d4..bc362900fa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,22 +1,4 @@ - + @@ -24,16 +6,15 @@ - - - + - - + - - - + @@ -62,35 +41,72 @@ - - - - - + + - - - + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tools:ignore="UnusedAttribute" + tools:replace="android:allowBackup"> + + android:exported="false" + android:theme="@style/Theme.NoBackground" /> + + + + @@ -236,9 +259,10 @@ android:theme="@style/Theme.ownCloud.Overlay" /> + @@ -264,8 +288,8 @@ + android:exported="true" + android:permission="android.permission.BIND_REMOTEVIEWS" /> - - - - - + - + android:permission="android.permission.MANAGE_DOCUMENTS" /> + android:exported="false" + android:foregroundServiceType="dataSync" /> + android:exported="false" + android:foregroundServiceType="mediaPlayback" /> + @@ -514,7 +534,6 @@ android:name="com.nextcloud.client.editimage.EditImageActivity" android:exported="false" android:theme="@style/Theme.ownCloud.Toolbar.NullBackground" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 00516f02be..e465676983 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -64,7 +64,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { fun createTask( input: String, - type: String, + type: String ) { viewModelScope.launch(Dispatchers.IO) { val result = repository.createTask(input, type) @@ -126,6 +126,12 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } + fun resetTaskDeletionState() { + _isTaskDeleted.update { + null + } + } + private fun filterTaskList(taskTypeId: String?) { if (taskTypeId == null) { _filteredTaskList.update { diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 2fde66d53e..d6d9c975d4 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -32,7 +32,11 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable @@ -42,13 +46,13 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.component.AddTaskAlertDialog import com.nextcloud.client.assistant.component.CenterText import com.nextcloud.client.assistant.component.TaskTypesRow @@ -62,7 +66,7 @@ import kotlinx.coroutines.delay @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: FloatingActionButton) { +fun AssistantScreen(viewModel: AssistantViewModel) { val activity = LocalContext.current as Activity val loading by viewModel.loading.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() @@ -86,17 +90,6 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } } - floatingActionButton.setOnClickListener { - if (selectedTaskType?.id != null) { - showAddTaskAlertDialog = true - } else { - DisplayUtils.showSnackMessage( - activity, - activity.getString(R.string.assistant_screen_select_different_task_type_to_add) - ) - } - } - Box(Modifier.nestedScroll(pullRefreshState.nestedScrollConnection)) { if (loading || pullRefreshState.isRefreshing) { CenterText(text = stringResource(id = R.string.assistant_screen_loading)) @@ -122,6 +115,24 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin } else { LinearProgressIndicator(progress = { pullRefreshState.progress }, modifier = Modifier.fillMaxWidth()) } + + FloatingActionButton( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(16.dp), + onClick = { + if (selectedTaskType?.id != null) { + showAddTaskAlertDialog = true + } else { + DisplayUtils.showSnackMessage( + activity, + activity.getString(R.string.assistant_screen_select_different_task_type_to_add) + ) + } + } + ) { + Icon(Icons.Filled.Add, "Add Task Icon") + } } if (isTaskCreated) { @@ -133,7 +144,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin isTaskDeleted?.let { val messageId = if (it) { - R.string.assistant_screen_task_delete_success_message + R.string.assistant_screen_task_create_success_message } else { R.string.assistant_screen_task_delete_success_message } @@ -142,6 +153,8 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin activity, stringResource(id = messageId) ) + + viewModel.resetTaskDeletionState() } if (showDeleteTaskAlertDialog) { @@ -152,7 +165,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin title = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title), description = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_description), dismiss = { showDeleteTaskAlertDialog = false }, - onComplete = { viewModel.deleteTask(id) }, + onComplete = { viewModel.deleteTask(id) } ) } } @@ -173,7 +186,7 @@ private fun AssistantContent( taskTypes: List?, selectedTaskType: TaskType?, viewModel: AssistantViewModel, - showDeleteTaskAlertDialog: (Long) -> Unit, + showDeleteTaskAlertDialog: (Long) -> Unit ) { LazyColumn( modifier = Modifier @@ -197,9 +210,11 @@ private fun AssistantContent( @Composable private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List?, viewModel: AssistantViewModel) { - Column(modifier = Modifier - .fillMaxSize() - .padding(16.dp)) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { TaskTypesRow(selectedTaskType, data = taskTypes) { task -> viewModel.selectTaskType(task) } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index aa10b9dbae..ac019e259a 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -58,7 +58,9 @@ fun AddTaskAlertDialog(viewModel: AssistantViewModel, taskType: TaskType, dismis TextField( placeholder = { Text( - text = stringResource(id = R.string.assistant_screen_create_task_alert_dialog_input_field_placeholder), + text = stringResource( + id = R.string.assistant_screen_create_task_alert_dialog_input_field_placeholder + ) ) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 187de9b2a0..e92aa0630e 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -55,12 +55,11 @@ import com.owncloud.android.lib.resources.assistant.model.Task @Composable fun TaskView( task: Task, - showDeleteTaskAlertDialog: (Long) -> Unit, + showDeleteTaskAlertDialog: (Long) -> Unit ) { var expanded by remember { mutableStateOf(false) } var showMoreActionsBottomSheet by remember { mutableStateOf(false) } - // TODO Check color Column( modifier = Modifier @@ -135,7 +134,7 @@ fun TaskView( R.string.assistant_screen_task_more_actions_bottom_sheet_delete_action ) { showDeleteTaskAlertDialog(task.id) - }, + } ) MoreActionsBottomSheet( diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt index a1198d846f..ff7bc851b0 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt @@ -38,7 +38,7 @@ class AssistantRepository(private val client: NextcloudClient) : AssistantReposi override fun createTask( input: String, - type: String, + type: String ): RemoteOperationResult { return CreateTaskRemoteOperation(input, type).execute(client) } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt index 8a21a8638a..a89a92f6e8 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt @@ -30,7 +30,7 @@ interface AssistantRepositoryType { fun createTask( input: String, - type: String, + type: String ): RemoteOperationResult fun getTaskList(appId: String): RemoteOperationResult diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java index 3918fc4006..84e1f447fb 100644 --- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -41,6 +41,7 @@ import com.nextcloud.client.widget.DashboardWidgetService; import com.nextcloud.ui.ChooseAccountDialogFragment; import com.nextcloud.ui.ImageDetailFragment; import com.nextcloud.ui.SetStatusDialogFragment; +import com.nextcloud.ui.composeActivity.ComposeActivity; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; import com.nmc.android.ui.LauncherActivity; import com.owncloud.android.MainApp; @@ -199,6 +200,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract CommunityActivity participateActivity(); + @ContributesAndroidInjector + abstract ComposeActivity composeActivity(); + @ContributesAndroidInjector abstract PassCodeActivity passCodeActivity(); diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt similarity index 55% rename from app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt rename to app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 7ec2a3e85f..8c67f73196 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -19,97 +19,105 @@ * along with this program. If not, see . */ -package com.nextcloud.ui.composeFragment +package com.nextcloud.ui.composeActivity +import android.content.Context import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.MenuItem import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.platform.ViewCompositionStrategy -import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.common.NextcloudClient +import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.R -import com.owncloud.android.databinding.FragmentComposeViewBinding +import com.owncloud.android.databinding.ActivityComposeBinding import com.owncloud.android.lib.common.OwnCloudClientFactory import com.owncloud.android.lib.common.accounts.AccountUtils import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.ui.fragment.FileFragment +import com.owncloud.android.ui.activity.DrawerActivity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -class ComposeFragment : FileFragment() { +class ComposeActivity : DrawerActivity() { - private var _binding: FragmentComposeViewBinding? = null - - private val binding get() = _binding!! - private var destination: ComposeDestination? = null + lateinit var binding: ActivityComposeBinding companion object { const val destinationKey = "destinationKey" + const val titleKey = "titleKey" + const val menuItemKey = "menuItemKey" } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentComposeViewBinding.inflate(inflater, container, false) - destination = arguments.getSerializableArgument(destinationKey, ComposeDestination::class.java) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityComposeBinding.inflate(layoutInflater) + setContentView(binding.root) - binding.composeView.apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + val destination = intent.getSerializableArgument(destinationKey, ComposeDestination::class.java) + val titleId = intent.getIntExtra(titleKey, R.string.empty) + val menuItemId = intent.getIntExtra(menuItemKey, R.id.nav_assistant) - setContent { - Content(destination) - } + setupToolbar() + updateActionBarTitleAndHomeButtonByString(getString(titleId)) + + setupDrawer(menuItemId) + + binding.composeView.setContent { + Content(destination, storageManager.user, this) } + } - return binding.root + override fun onResume() { + super.onResume() + setDrawerMenuItemChecked(R.id.nav_assistant) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + var retval = true + if (item.itemId == android.R.id.home) { + if (isDrawerOpen) { + closeDrawer() + } else { + openDrawer() + } + } else { + retval = super.onOptionsItemSelected(item) + } + return retval } @Composable - private fun Content(destination: ComposeDestination?) { - val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) + private fun Content(destination: ComposeDestination?, user: User, context: Context) { var nextcloudClient by remember { mutableStateOf(null) } LaunchedEffect(Unit) { - nextcloudClient = getNextcloudClient() + nextcloudClient = getNextcloudClient(user, context) } return if (destination == ComposeDestination.AssistantScreen && nextcloudClient != null) { AssistantScreen( viewModel = AssistantViewModel( client = nextcloudClient!! - ), - floatingActionButton + ) ) } else { - } } - private suspend fun getNextcloudClient(): NextcloudClient? { + private suspend fun getNextcloudClient(user: User, context: Context): NextcloudClient? { return withContext(Dispatchers.IO) { try { - OwnCloudClientFactory.createNextcloudClient(containerActivity.storageManager.user, requireContext()) + OwnCloudClientFactory.createNextcloudClient(user, context) } catch (e: AccountUtils.AccountNotFoundException) { Log_OC.e(this, "Error caught at init of AssistantRepository", e) null } } } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } } diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt similarity index 87% rename from app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt rename to app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt index c503a541a1..9247f724db 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt @@ -19,8 +19,10 @@ * along with this program. If not, see . */ -package com.nextcloud.ui.composeFragment +package com.nextcloud.ui.composeActivity -enum class ComposeDestination { +import java.io.Serializable + +enum class ComposeDestination : Serializable { AssistantScreen -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt index 00c004e6df..ef9038603c 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt @@ -77,7 +77,7 @@ fun MoreActionsBottomSheet( } } .padding(all = 16.dp), - verticalAlignment = Alignment.CenterVertically, + verticalAlignment = Alignment.CenterVertically ) { Icon( painter = painterResource(id = action.first), @@ -90,7 +90,7 @@ fun MoreActionsBottomSheet( Text( text = stringResource(action.second), - fontSize = 16.sp, + fontSize = 16.sp ) } } @@ -98,4 +98,4 @@ fun MoreActionsBottomSheet( Spacer(modifier = Modifier.height(32.dp)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt index 28dc94c67f..bc065e0fa0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt @@ -42,7 +42,6 @@ open class CommunityActivity : DrawerActivity() { setupToolbar() updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_community)) - setupDrawer(R.id.nav_community) binding.communityReleaseCandidateText.movementMethod = LinkMovementMethod.getInstance() setupContributeForumView() diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index afa4a7f85a..fdd1f442b5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -74,8 +74,8 @@ import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.NextcloudClient; import com.nextcloud.java.util.Optional; import com.nextcloud.ui.ChooseAccountDialogFragment; -import com.nextcloud.ui.composeFragment.ComposeDestination; -import com.nextcloud.ui.composeFragment.ComposeFragment; +import com.nextcloud.ui.composeActivity.ComposeActivity; +import com.nextcloud.ui.composeActivity.ComposeDestination; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.PassCodeManager; @@ -248,9 +248,6 @@ public abstract class DrawerActivity extends ToolbarActivity if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } - - showSortListGroup(true); - hideSearchText(false); } /** @@ -548,7 +545,7 @@ public abstract class DrawerActivity extends ToolbarActivity intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); } else if (itemId == R.id.nav_assistant) { - navigateComposeView(ComposeDestination.AssistantScreen, false, true); + startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, itemId); } else { if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) { @@ -560,19 +557,12 @@ public abstract class DrawerActivity extends ToolbarActivity } } - private void navigateComposeView(ComposeDestination destination, boolean showSortListGroup, boolean hideSearchText) { - showSortListGroup(showSortListGroup); - hideSearchText(hideSearchText); - - ComposeFragment composeFragment = new ComposeFragment(); - Bundle bundle = new Bundle(); - bundle.putSerializable(ComposeFragment.destinationKey, destination); - composeFragment.setArguments(bundle); - - getSupportFragmentManager() - .beginTransaction() - .replace(R.id.left_fragment_container, composeFragment) - .commit(); + private void startComposeActivity(ComposeDestination destination, int titleId, int menuItemId) { + Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class); + composeActivity.putExtra(ComposeActivity.destinationKey, destination); + composeActivity.putExtra(ComposeActivity.titleKey, titleId); + composeActivity.putExtra(ComposeActivity.menuItemKey, menuItemId); + startActivity(composeActivity); } private void startActivity(Class activity) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 74d32aefd0..b9823f0fa1 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -155,14 +155,6 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable mSearchText.setText(getString(R.string.appbar_search_in, title)); } - public void hideSearchText(boolean hide) { - if (hide) { - mSearchText.setVisibility(View.GONE); - } else { - mSearchText.setVisibility(View.VISIBLE); - } - } - @SuppressLint("PrivateResource") private void showHomeSearchToolbar(boolean isShow) { viewThemeUtils.material.themeToolbar(mToolbar); diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml new file mode 100644 index 0000000000..828596d59a --- /dev/null +++ b/app/src/main/res/layout/activity_compose.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 016fe4bf86..22c4b360a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,6 +18,7 @@ Biggest first Smallest first + Assistant Task List are loading, please wait No task available for %s task type, you can create a new task from bottom right. Delete Task @@ -1155,7 +1156,7 @@ %s MP File details Image taking conditions - %1$s %2$s + %1$s %2$s Year Year/Month Year/Month/Day From 22c3c3442dc0399d1b7b32fbd97ead5aebe16881 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 15:36:58 +0100 Subject: [PATCH 028/102] UI Fixes Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 10 +++- .../client/assistant/AsssistantScreen.kt | 59 ++++++++++++------- app/src/main/res/values/strings.xml | 5 +- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index e465676983..d946576323 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -51,8 +51,8 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private val _loading = MutableStateFlow(true) val loading: StateFlow = _loading - private val _isTaskCreated = MutableStateFlow(false) - val isTaskCreated: StateFlow = _isTaskCreated + private val _isTaskCreated = MutableStateFlow(null) + val isTaskCreated: StateFlow = _isTaskCreated private val _isTaskDeleted = MutableStateFlow(null) val isTaskDeleted: StateFlow = _isTaskDeleted @@ -132,6 +132,12 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { } } + fun resetTaskAddState() { + _isTaskCreated.update { + null + } + } + private fun filterTaskList(taskTypeId: String?) { if (taskTypeId == null) { _filteredTaskList.update { diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index d6d9c975d4..69e44ef6f6 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -135,27 +135,8 @@ fun AssistantScreen(viewModel: AssistantViewModel) { } } - if (isTaskCreated) { - DisplayUtils.showSnackMessage( - activity, - stringResource(id = R.string.assistant_screen_task_create_success_message) - ) - } - - isTaskDeleted?.let { - val messageId = if (it) { - R.string.assistant_screen_task_create_success_message - } else { - R.string.assistant_screen_task_delete_success_message - } - - DisplayUtils.showSnackMessage( - activity, - stringResource(id = messageId) - ) - - viewModel.resetTaskDeletionState() - } + checkTaskAdd(isTaskCreated, activity, viewModel) + checkTaskDeletion(isTaskDeleted, activity, viewModel) if (showDeleteTaskAlertDialog) { taskIdToDeleted?.let { id -> @@ -179,6 +160,42 @@ fun AssistantScreen(viewModel: AssistantViewModel) { } } +@Composable +private fun checkTaskAdd(isTaskCreated: Boolean?, activity: Activity, viewModel: AssistantViewModel) { + isTaskCreated?.let { + val messageId = if (it) { + R.string.assistant_screen_task_create_success_message + } else { + R.string.assistant_screen_task_create_fail_message + } + + DisplayUtils.showSnackMessage( + activity, + stringResource(id = messageId) + ) + + viewModel.resetTaskAddState() + } +} + +@Composable +private fun checkTaskDeletion(isTaskDeleted: Boolean?, activity: Activity, viewModel: AssistantViewModel) { + isTaskDeleted?.let { + val messageId = if (it) { + R.string.assistant_screen_task_delete_success_message + } else { + R.string.assistant_screen_task_delete_fail_message + } + + DisplayUtils.showSnackMessage( + activity, + stringResource(id = messageId) + ) + + viewModel.resetTaskDeletionState() + } +} + @OptIn(ExperimentalFoundationApi::class) @Composable private fun AssistantContent( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 22c4b360a2..fb5261c2a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -29,8 +29,11 @@ Delete Task Task successfully created + An error occurred while creating the task + An error occurred while deleting the task - TS + Task successfully deleted + Type some text Input: From 6b7fc48230823fbf104cc8e8f68ff524751b9210 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 15:46:19 +0100 Subject: [PATCH 029/102] Add Composable function naming convention Signed-off-by: alperozturk --- app/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/detekt.yml b/app/detekt.yml index 5e9aad61c9..91280a3a92 100644 --- a/app/detekt.yml +++ b/app/detekt.yml @@ -199,7 +199,7 @@ naming: minimumFunctionNameLength: 3 FunctionNaming: active: true - functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' + functionPattern: '^([a-z$A-Z][a-zA-Z$0-9]*)|(`.*`)$' excludeClassPattern: '$^' ignoreOverridden: true excludes: From 202d177128bd3d339e18f55f76eac4a8d3182535 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 15:46:32 +0100 Subject: [PATCH 030/102] Fix code analytics Signed-off-by: alperozturk --- .../nextcloud/client/assistant/AsssistantScreen.kt | 2 ++ .../client/assistant/component/TaskView.kt | 1 + .../nextcloud/ui/composeActivity/ComposeActivity.kt | 13 +++++++------ .../alertDialog/SimpleAlertDialog.kt | 1 + .../android/ui/activity/SyncedFoldersActivity.kt | 2 +- .../java/com/owncloud/android/utils/WebViewUtil.kt | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 69e44ef6f6..5d4c9520f3 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -64,6 +64,7 @@ import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.utils.DisplayUtils import kotlinx.coroutines.delay +@Suppress("LongMethod") @OptIn(ExperimentalMaterial3Api::class) @Composable fun AssistantScreen(viewModel: AssistantViewModel) { @@ -81,6 +82,7 @@ fun AssistantScreen(viewModel: AssistantViewModel) { } val pullRefreshState = rememberPullToRefreshState() + @Suppress("MagicNumber") if (pullRefreshState.isRefreshing) { LaunchedEffect(true) { delay(1500) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index e92aa0630e..4069c57d05 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -52,6 +52,7 @@ import com.owncloud.android.lib.resources.assistant.model.Task @OptIn(ExperimentalFoundationApi::class) @SuppressLint("ResourceAsColor") +@Suppress("LongMethod", "MagicNumber") @Composable fun TaskView( task: Task, diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 8c67f73196..6112fbe296 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -100,13 +100,14 @@ class ComposeActivity : DrawerActivity() { nextcloudClient = getNextcloudClient(user, context) } - return if (destination == ComposeDestination.AssistantScreen && nextcloudClient != null) { - AssistantScreen( - viewModel = AssistantViewModel( - client = nextcloudClient!! + if (destination == ComposeDestination.AssistantScreen) { + nextcloudClient?.let { + AssistantScreen( + viewModel = AssistantViewModel( + client = it + ) ) - ) - } else { + } } } diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt index fc47fe5307..d7f5ba3508 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.owncloud.android.R +@Suppress("LongParameterList") @Composable fun SimpleAlertDialog( backgroundColor: Color, diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index 55cf725a00..c4aa216d4d 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -80,7 +80,7 @@ import javax.inject.Inject /** * Activity displaying all auto-synced folders and/or instant upload media folders. */ -@Suppress("TooManyFunctions") +@Suppress("TooManyFunctions", "LargeClass") class SyncedFoldersActivity : FileActivity(), SyncedFolderAdapter.ClickListener, diff --git a/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt b/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt index 550297e3df..26f66c8c49 100644 --- a/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt +++ b/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt @@ -123,7 +123,7 @@ class WebViewUtil(private val context: Context) { * @return */ @SuppressLint("PrivateApi", "DiscouragedPrivateApi") - @Suppress("TooGenericExceptionCaught") + @Suppress("TooGenericExceptionCaught", "NestedBlockDepth") fun setProxyKKPlus(webView: WebView) { val proxyHost = OwnCloudClientManagerFactory.getProxyHost() val proxyPort = OwnCloudClientManagerFactory.getProxyPort() From 8aa37e8f79374cfb60b799caaff32f756c572be5 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 15:52:32 +0100 Subject: [PATCH 031/102] Add AssistantRepositoryTests Signed-off-by: alperozturk --- .../assistant/AssistantRepositoryTests.kt | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index dfdddc308d..40c0ad2f0e 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -21,10 +21,10 @@ package com.nextcloud.client.assistant -import com.nextcloud.client.account.UserAccountManagerImpl import com.nextcloud.client.assistant.repository.AssistantRepository import com.owncloud.android.AbstractOnServerIT import org.junit.Assert.assertTrue +import org.junit.Assert.fail import org.junit.Before import org.junit.Test @@ -34,20 +34,17 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Before fun setup() { - val userAccountManager = UserAccountManagerImpl.fromContext(targetContext) - sut = AssistantRepository(userAccountManager.user, targetContext) + sut = AssistantRepository(nextcloudClient) } @Test fun testGetTaskTypes() { - assertTrue(sut?.getTaskTypes()?.resultData?.isNotEmpty() == true) + assertTrue(sut?.getTaskTypes()?.resultData?.types?.isNotEmpty() == true) } - /* - - @Test + @Test fun testGetTaskList() { - assertTrue(sut?.getTaskList("assistant")?.ocs?.data?.types?.isNotEmpty() == true) + assertTrue(sut?.getTaskList("assistant")?.resultData?.tasks?.isNotEmpty() == true) } @Test @@ -60,9 +57,18 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Test fun testDeleteTask() { - val taskList = sut?.getTaskList("assistant")?.ocs?.data - assertTrue(sut?.getTaskList("assistant")?.ocs?.data?.types?.isNotEmpty() == true) - } + testCreateTask() - */ + longSleep() + + val taskList = sut?.getTaskList("assistant")?.resultData?.tasks + val taskListCountBeforeDelete = taskList?.size + + if (taskList.isNullOrEmpty()) { + fail("Expected to get task list but found null or empty list") + } + + sut?.deleteTask(taskList!!.first().id) + assertTrue(taskListCountBeforeDelete == taskListCountBeforeDelete?.minus(1)) + } } From 5a67b58b5efbc24850d271c038d321015ae517d3 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 11:05:35 +0100 Subject: [PATCH 032/102] Add AssistantRepositoryTests Signed-off-by: alperozturk --- .../client/assistant/AssistantRepositoryTests.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index 40c0ad2f0e..857abf1b91 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -44,7 +44,13 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Test fun testGetTaskList() { - assertTrue(sut?.getTaskList("assistant")?.resultData?.tasks?.isNotEmpty() == true) + val result = sut?.getTaskList("assistant")?.resultData?.tasks + + if (result == null) { + fail("Expected to get task list but found null") + } + + assertTrue(result?.isEmpty() == true || (result?.size ?: 0) > 0) } @Test @@ -59,7 +65,7 @@ class AssistantRepositoryTests : AbstractOnServerIT() { fun testDeleteTask() { testCreateTask() - longSleep() + shortSleep() val taskList = sut?.getTaskList("assistant")?.resultData?.tasks val taskListCountBeforeDelete = taskList?.size From 0639983b6b5340dc97f0f2ab85ae3b067696bd3e Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 13:59:25 +0100 Subject: [PATCH 033/102] Fix tests Signed-off-by: alperozturk --- .../assistant/AssistantRepositoryTests.kt | 41 ++++++++++--------- .../java/com/owncloud/android/AbstractIT.java | 8 ++++ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index 857abf1b91..cea7fd5bcb 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -24,7 +24,6 @@ package com.nextcloud.client.assistant import com.nextcloud.client.assistant.repository.AssistantRepository import com.owncloud.android.AbstractOnServerIT import org.junit.Assert.assertTrue -import org.junit.Assert.fail import org.junit.Before import org.junit.Test @@ -39,42 +38,46 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Test fun testGetTaskTypes() { - assertTrue(sut?.getTaskTypes()?.resultData?.types?.isNotEmpty() == true) + val result = sut?.getTaskTypes() + assertTrue(result?.isSuccess == true) + + val taskTypes = result?.resultData?.types + assertTrue(taskTypes?.isNotEmpty() == true) } @Test fun testGetTaskList() { - val result = sut?.getTaskList("assistant")?.resultData?.tasks + val result = sut?.getTaskList("assistant") + assertTrue(result?.isSuccess == true) - if (result == null) { - fail("Expected to get task list but found null") - } - - assertTrue(result?.isEmpty() == true || (result?.size ?: 0) > 0) + val taskList = result?.resultData?.tasks + assertTrue(taskList?.isEmpty() == true || (taskList?.size ?: 0) > 0) } @Test fun testCreateTask() { - val input = "How many files I have?" - val type = "OCP\\TextProcessing\\HeadlineTaskType" + val input = "Give me some random output for test purpose" + val type = "OCP\\TextProcessing\\FreePromptTaskType" val result = sut?.createTask(input, type) - assertTrue(result != null) + assertTrue(result?.isSuccess == true) } @Test fun testDeleteTask() { testCreateTask() - shortSleep() + sleep(120) - val taskList = sut?.getTaskList("assistant")?.resultData?.tasks - val taskListCountBeforeDelete = taskList?.size + val resultOfTaskList = sut?.getTaskList("assistant") + assertTrue(resultOfTaskList?.isSuccess == true) - if (taskList.isNullOrEmpty()) { - fail("Expected to get task list but found null or empty list") - } + sleep(120) - sut?.deleteTask(taskList!!.first().id) - assertTrue(taskListCountBeforeDelete == taskListCountBeforeDelete?.minus(1)) + val taskList = resultOfTaskList?.resultData?.tasks + + assert((taskList?.size ?: 0) > 0) + + val result = sut?.deleteTask(taskList!!.first().id) + assertTrue(result?.isSuccess == true) } } diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index ee5515eb2d..9621972d2e 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -334,6 +334,14 @@ public abstract class AbstractIT { } } + protected void sleep(int second) { + try { + Thread.sleep(1000L * second); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + public OCFile createFolder(String remotePath) { RemoteOperationResult check = new ExistenceCheckRemoteOperation(remotePath, false).execute(client); From 103b84d7ab660b4dda7b2fba65be08bd8fd4d7a5 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:37:27 +0100 Subject: [PATCH 034/102] Rebase master Signed-off-by: alperozturk --- .../owncloud/android/ui/activity/FileDisplayActivity.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index b9b2856a2b..108a1010b3 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -74,9 +74,14 @@ import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.utils.IntentUtil; +import com.nextcloud.common.NextcloudClient; import com.nextcloud.java.util.Optional; import com.nextcloud.model.WorkerState; import com.nextcloud.model.WorkerStateLiveData; +import com.nextcloud.operations.assistant.AssistantRepository; +import com.nextcloud.operations.assistant.model.CreatedTask; +import com.nextcloud.operations.assistant.model.Ocs; +import com.nextcloud.operations.assistant.model.TaskTypes; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.nextcloud.utils.view.FastScrollUtils; From cca6f60ed222d6c7548d5a8d0b41e6cc6be3a239 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:38:10 +0100 Subject: [PATCH 035/102] Rebase master Signed-off-by: alperozturk --- .../ui/composeFragment/ComposeDestinations.kt | 26 +++++++ .../ui/composeFragment/ComposeFragment.kt | 74 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt create mode 100644 app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt new file mode 100644 index 0000000000..8809b1b0c3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.ui.composeFragment + +enum class ComposeDestinations { + AssistantScreen +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt new file mode 100644 index 0000000000..e56f66eaef --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -0,0 +1,74 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.ui.composeFragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.Fragment +import com.nextcloud.client.assistant.AssistantScreen +import com.nextcloud.utils.extensions.getSerializableArgument +import com.owncloud.android.databinding.FragmentComposeViewBinding + +class ComposeFragment : Fragment() { + + private var _binding: FragmentComposeViewBinding? = null + + private val binding get() = _binding!! + private var destination: ComposeDestinations? = null + + companion object { + const val destinationKey = "destinationKey" + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentComposeViewBinding.inflate(inflater, container, false) + destination = arguments.getSerializableArgument(destinationKey, ComposeDestinations::class.java) + + binding.composeView.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + when(destination) { + ComposeDestinations.AssistantScreen -> { + AssistantScreen() + } + else -> { + + } + } + } + } + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} From 1257ce067546e894316977b12200ba71a0897fcc Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:38:34 +0100 Subject: [PATCH 036/102] Rebase master Signed-off-by: alperozturk --- .../ui/composeFragment/ComposeFragment.kt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index e56f66eaef..16bbc35549 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -28,10 +28,12 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import com.nextcloud.client.assistant.AssistantScreen +import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.databinding.FragmentComposeViewBinding +import com.owncloud.android.ui.fragment.FileFragment -class ComposeFragment : Fragment() { +class ComposeFragment : FileFragment() { private var _binding: FragmentComposeViewBinding? = null @@ -53,12 +55,17 @@ class ComposeFragment : Fragment() { binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { - when(destination) { + when (destination) { ComposeDestinations.AssistantScreen -> { - AssistantScreen() + AssistantScreen( + viewModel = AssistantViewModel( + context = requireContext(), + user = containerActivity.storageManager.user + ) + ) } - else -> { + else -> { } } } From 7b1c6e46487a39f87bac0fb5cc6ebe2c304367d8 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:38:43 +0100 Subject: [PATCH 037/102] Rebase master Signed-off-by: alperozturk --- .../ui/composeComponents/SimpleAlertDialog.kt | 97 +++++++++++++++++++ .../ui/composeFragment/ComposeFragment.kt | 39 +++++--- 2 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt new file mode 100644 index 0000000000..d689c599c1 --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt @@ -0,0 +1,97 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.ui.composeComponents + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.owncloud.android.R + +@Composable +fun SimpleAlertDialog( + backgroundColor: Color, + textColor: Color, + titleId: Int, + description: String?, + heightFraction: Float? = null, + content: @Composable (() -> Unit)? = null, + onComplete: () -> Unit, + dismiss: () -> Unit +) { + val modifier = if (heightFraction != null) { + Modifier + .fillMaxWidth() + .fillMaxHeight(heightFraction) + } else { + Modifier.fillMaxWidth() + } + + AlertDialog( + containerColor = backgroundColor, + onDismissRequest = { dismiss() }, + title = { + Text(text = stringResource(id = titleId), color = textColor) + }, + text = { + Column(modifier = modifier) { + if (description != null) { + Text(text = description, color = textColor) + } + + content?.let { + Spacer(modifier = Modifier.height(16.dp)) + + content() + } + } + }, + confirmButton = { + TextButton(onClick = { + onComplete() + dismiss() + }) { + Text( + stringResource(id = R.string.common_ok), + color = textColor + ) + } + }, + dismissButton = { + TextButton(onClick = { dismiss() }) { + Text( + stringResource(id = R.string.common_cancel), + color = textColor + ) + } + } + ) +} diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index 16bbc35549..db08e16a1e 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -25,13 +25,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.runtime.Composable import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment +import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.utils.extensions.getSerializableArgument +import com.owncloud.android.R import com.owncloud.android.databinding.FragmentComposeViewBinding import com.owncloud.android.ui.fragment.FileFragment +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext class ComposeFragment : FileFragment() { @@ -54,26 +59,34 @@ class ComposeFragment : FileFragment() { binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - when (destination) { - ComposeDestinations.AssistantScreen -> { - AssistantScreen( - viewModel = AssistantViewModel( - context = requireContext(), - user = containerActivity.storageManager.user - ) - ) - } - else -> { - } - } + setContent { + Content(destination) } } return binding.root } + @Composable + private fun Content(destination: ComposeDestinations?) { + val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) + + return when (destination) { + ComposeDestinations.AssistantScreen -> { + AssistantScreen( + viewModel = AssistantViewModel( + context = requireContext(), + user = containerActivity.storageManager.user + ), + floatingActionButton + ) + } + else -> { + } + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null From 751ef701fd18f34174a6c145653c0719a43eff1c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:39:04 +0100 Subject: [PATCH 038/102] Rebase master Signed-off-by: alperozturk --- .../owncloud/android/AbstractOnServerIT.java | 2 +- .../ui/composeFragment/ComposeFragment.kt | 45 ++++++++++++++----- .../ui/activity/FileDisplayActivity.java | 5 --- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index 343938e121..bcd43ccafb 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -112,7 +112,7 @@ public abstract class AbstractOnServerIT extends AbstractIT { @After public void after() { - deleteAllFilesOnServer(); + // deleteAllFilesOnServer(); super.after(); } diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index db08e16a1e..225fa34239 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -26,14 +26,24 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel +import com.nextcloud.common.NextcloudClient +import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.R import com.owncloud.android.databinding.FragmentComposeViewBinding +import com.owncloud.android.lib.common.OwnCloudClientFactory +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.fragment.FileFragment import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -71,18 +81,31 @@ class ComposeFragment : FileFragment() { @Composable private fun Content(destination: ComposeDestinations?) { val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) + var nextcloudClient by remember { mutableStateOf(null) } - return when (destination) { - ComposeDestinations.AssistantScreen -> { - AssistantScreen( - viewModel = AssistantViewModel( - context = requireContext(), - user = containerActivity.storageManager.user - ), - floatingActionButton - ) - } - else -> { + LaunchedEffect(Unit) { + nextcloudClient = getNextcloudClient() + } + + return if (destination == ComposeDestinations.AssistantScreen && nextcloudClient != null) { + AssistantScreen( + viewModel = AssistantViewModel( + client = nextcloudClient!! + ), + floatingActionButton + ) + } else { + + } + } + + private suspend fun getNextcloudClient(): NextcloudClient? { + return withContext(Dispatchers.IO) { + try { + OwnCloudClientFactory.createNextcloudClient(containerActivity.storageManager.user, requireContext()) + } catch (e: AccountUtils.AccountNotFoundException) { + Log_OC.e(this, "Error caught at init of AssistantRepository", e) + null } } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 108a1010b3..b9b2856a2b 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -74,14 +74,9 @@ import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.utils.IntentUtil; -import com.nextcloud.common.NextcloudClient; import com.nextcloud.java.util.Optional; import com.nextcloud.model.WorkerState; import com.nextcloud.model.WorkerStateLiveData; -import com.nextcloud.operations.assistant.AssistantRepository; -import com.nextcloud.operations.assistant.model.CreatedTask; -import com.nextcloud.operations.assistant.model.Ocs; -import com.nextcloud.operations.assistant.model.TaskTypes; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.nextcloud.utils.view.FastScrollUtils; From 6ac017de69e92f123392358a60da224c2b7d2e9f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:39:32 +0100 Subject: [PATCH 039/102] Rebase master Signed-off-by: alperozturk --- .../com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt index d689c599c1..cf727b87d9 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt @@ -40,7 +40,7 @@ import com.owncloud.android.R fun SimpleAlertDialog( backgroundColor: Color, textColor: Color, - titleId: Int, + title: String, description: String?, heightFraction: Float? = null, content: @Composable (() -> Unit)? = null, @@ -59,7 +59,7 @@ fun SimpleAlertDialog( containerColor = backgroundColor, onDismissRequest = { dismiss() }, title = { - Text(text = stringResource(id = titleId), color = textColor) + Text(text = title, color = textColor) }, text = { Column(modifier = modifier) { From ba3a0950ebd14f2ff05e9183e72177554a13f9e4 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:40:02 +0100 Subject: [PATCH 040/102] Rebase master Signed-off-by: alperozturk --- .../ui/composeComponents/SimpleAlertDialog.kt | 97 ------------------- 1 file changed, 97 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt deleted file mode 100644 index cf727b87d9..0000000000 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Alper Ozturk - * Copyright (C) 2024 Alper Ozturk - * Copyright (C) 2024 Nextcloud GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.ui.composeComponents - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.owncloud.android.R - -@Composable -fun SimpleAlertDialog( - backgroundColor: Color, - textColor: Color, - title: String, - description: String?, - heightFraction: Float? = null, - content: @Composable (() -> Unit)? = null, - onComplete: () -> Unit, - dismiss: () -> Unit -) { - val modifier = if (heightFraction != null) { - Modifier - .fillMaxWidth() - .fillMaxHeight(heightFraction) - } else { - Modifier.fillMaxWidth() - } - - AlertDialog( - containerColor = backgroundColor, - onDismissRequest = { dismiss() }, - title = { - Text(text = title, color = textColor) - }, - text = { - Column(modifier = modifier) { - if (description != null) { - Text(text = description, color = textColor) - } - - content?.let { - Spacer(modifier = Modifier.height(16.dp)) - - content() - } - } - }, - confirmButton = { - TextButton(onClick = { - onComplete() - dismiss() - }) { - Text( - stringResource(id = R.string.common_ok), - color = textColor - ) - } - }, - dismissButton = { - TextButton(onClick = { dismiss() }) { - Text( - stringResource(id = R.string.common_cancel), - color = textColor - ) - } - } - ) -} From f3fc4ed89d78a10ce5178884b5e7e35e642af1bd Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:40:49 +0100 Subject: [PATCH 041/102] Rebase master Signed-off-by: alperozturk --- ...{ComposeDestinations.kt => ComposeDestination.kt} | 2 +- .../nextcloud/ui/composeFragment/ComposeFragment.kt | 12 ++++++------ .../android/ui/activity/ToolbarActivity.java | 8 ++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) rename app/src/main/java/com/nextcloud/ui/composeFragment/{ComposeDestinations.kt => ComposeDestination.kt} (96%) diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt similarity index 96% rename from app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt rename to app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt index 8809b1b0c3..c503a541a1 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestinations.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt @@ -21,6 +21,6 @@ package com.nextcloud.ui.composeFragment -enum class ComposeDestinations { +enum class ComposeDestination { AssistantScreen } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index 225fa34239..67ce389090 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -21,6 +21,7 @@ package com.nextcloud.ui.composeFragment +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -32,12 +33,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.fragment.app.Fragment import com.google.android.material.floatingactionbutton.FloatingActionButton import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.common.NextcloudClient -import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.R import com.owncloud.android.databinding.FragmentComposeViewBinding @@ -53,7 +52,7 @@ class ComposeFragment : FileFragment() { private var _binding: FragmentComposeViewBinding? = null private val binding get() = _binding!! - private var destination: ComposeDestinations? = null + private var destination: ComposeDestination? = null companion object { const val destinationKey = "destinationKey" @@ -65,7 +64,7 @@ class ComposeFragment : FileFragment() { savedInstanceState: Bundle? ): View { _binding = FragmentComposeViewBinding.inflate(inflater, container, false) - destination = arguments.getSerializableArgument(destinationKey, ComposeDestinations::class.java) + destination = arguments.getSerializableArgument(destinationKey, ComposeDestination::class.java) binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) @@ -78,8 +77,9 @@ class ComposeFragment : FileFragment() { return binding.root } + @SuppressLint("UnusedContentLambdaTargetStateParameter") @Composable - private fun Content(destination: ComposeDestinations?) { + private fun Content(destination: ComposeDestination?) { val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) var nextcloudClient by remember { mutableStateOf(null) } @@ -87,7 +87,7 @@ class ComposeFragment : FileFragment() { nextcloudClient = getNextcloudClient() } - return if (destination == ComposeDestinations.AssistantScreen && nextcloudClient != null) { + return if (destination == ComposeDestination.AssistantScreen && nextcloudClient != null) { AssistantScreen( viewModel = AssistantViewModel( client = nextcloudClient!! diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index b9823f0fa1..74d32aefd0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -155,6 +155,14 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable mSearchText.setText(getString(R.string.appbar_search_in, title)); } + public void hideSearchText(boolean hide) { + if (hide) { + mSearchText.setVisibility(View.GONE); + } else { + mSearchText.setVisibility(View.VISIBLE); + } + } + @SuppressLint("PrivateResource") private void showHomeSearchToolbar(boolean isShow) { viewThemeUtils.material.themeToolbar(mToolbar); From 3fdee00a1f47ce4703d9ef730e1c963ade77048c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:40:59 +0100 Subject: [PATCH 042/102] Rebase master Signed-off-by: alperozturk --- settings.gradle | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/settings.gradle b/settings.gradle index 34030893f3..0e7a78bade 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,10 +13,4 @@ include ':appscan' // dependencySubstitution { // substitute module('com.github.nextcloud:android-library') using project(':library') // } -//} - -includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_library') { - dependencySubstitution { - substitute module('com.github.nextcloud:android-library') using project(':library') - } -} +//} \ No newline at end of file From 658360bb3e26623bb03b9b24fc908306424403d9 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 12:38:40 +0100 Subject: [PATCH 043/102] Reset Signed-off-by: alperozturk --- .../java/com/owncloud/android/AbstractOnServerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index bcd43ccafb..343938e121 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -112,7 +112,7 @@ public abstract class AbstractOnServerIT extends AbstractIT { @After public void after() { - // deleteAllFilesOnServer(); + deleteAllFilesOnServer(); super.after(); } From 2649b51fbae2702af0bdbd24daf653ec1b3bf2f7 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 29 Feb 2024 12:54:56 +0100 Subject: [PATCH 044/102] Add local libs warnings Signed-off-by: alperozturk --- settings.gradle | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 0e7a78bade..34030893f3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,4 +13,10 @@ include ':appscan' // dependencySubstitution { // substitute module('com.github.nextcloud:android-library') using project(':library') // } -//} \ No newline at end of file +//} + +includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_library') { + dependencySubstitution { + substitute module('com.github.nextcloud:android-library') using project(':library') + } +} From 9076d160093eecb2367a7c4c93774a04fc9b9bec Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:41:08 +0100 Subject: [PATCH 045/102] Rebase master Signed-off-by: alperozturk --- .../java/com/nextcloud/ui/composeFragment/ComposeFragment.kt | 2 -- .../java/com/owncloud/android/ui/activity/DrawerActivity.java | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt index 67ce389090..7ec2a3e85f 100644 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt @@ -21,7 +21,6 @@ package com.nextcloud.ui.composeFragment -import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -77,7 +76,6 @@ class ComposeFragment : FileFragment() { return binding.root } - @SuppressLint("UnusedContentLambdaTargetStateParameter") @Composable private fun Content(destination: ComposeDestination?) { val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index fdd1f442b5..2b8e3953a1 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -248,6 +248,9 @@ public abstract class DrawerActivity extends ToolbarActivity if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } + + showSortListGroup(true); + hideSearchText(false); } /** From 1f31127df31a41b92a37174c229ea496fe4a2844 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:41:17 +0100 Subject: [PATCH 046/102] Rebase master Signed-off-by: alperozturk --- .../ui/composeFragment/ComposeDestination.kt | 26 ---- .../ui/composeFragment/ComposeFragment.kt | 115 ------------------ .../android/ui/activity/DrawerActivity.java | 3 - .../android/ui/activity/ToolbarActivity.java | 8 -- 4 files changed, 152 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt delete mode 100644 app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt deleted file mode 100644 index c503a541a1..0000000000 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeDestination.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Alper Ozturk - * Copyright (C) 2024 Alper Ozturk - * Copyright (C) 2024 Nextcloud GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.ui.composeFragment - -enum class ComposeDestination { - AssistantScreen -} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt b/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt deleted file mode 100644 index 7ec2a3e85f..0000000000 --- a/app/src/main/java/com/nextcloud/ui/composeFragment/ComposeFragment.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Alper Ozturk - * Copyright (C) 2024 Alper Ozturk - * Copyright (C) 2024 Nextcloud GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.ui.composeFragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.platform.ViewCompositionStrategy -import com.google.android.material.floatingactionbutton.FloatingActionButton -import com.nextcloud.client.assistant.AssistantScreen -import com.nextcloud.client.assistant.AssistantViewModel -import com.nextcloud.common.NextcloudClient -import com.nextcloud.utils.extensions.getSerializableArgument -import com.owncloud.android.R -import com.owncloud.android.databinding.FragmentComposeViewBinding -import com.owncloud.android.lib.common.OwnCloudClientFactory -import com.owncloud.android.lib.common.accounts.AccountUtils -import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.ui.fragment.FileFragment -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -class ComposeFragment : FileFragment() { - - private var _binding: FragmentComposeViewBinding? = null - - private val binding get() = _binding!! - private var destination: ComposeDestination? = null - - companion object { - const val destinationKey = "destinationKey" - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentComposeViewBinding.inflate(inflater, container, false) - destination = arguments.getSerializableArgument(destinationKey, ComposeDestination::class.java) - - binding.composeView.apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - - setContent { - Content(destination) - } - } - - return binding.root - } - - @Composable - private fun Content(destination: ComposeDestination?) { - val floatingActionButton: FloatingActionButton = requireActivity().findViewById(R.id.fab_main) - var nextcloudClient by remember { mutableStateOf(null) } - - LaunchedEffect(Unit) { - nextcloudClient = getNextcloudClient() - } - - return if (destination == ComposeDestination.AssistantScreen && nextcloudClient != null) { - AssistantScreen( - viewModel = AssistantViewModel( - client = nextcloudClient!! - ), - floatingActionButton - ) - } else { - - } - } - - private suspend fun getNextcloudClient(): NextcloudClient? { - return withContext(Dispatchers.IO) { - try { - OwnCloudClientFactory.createNextcloudClient(containerActivity.storageManager.user, requireContext()) - } catch (e: AccountUtils.AccountNotFoundException) { - Log_OC.e(this, "Error caught at init of AssistantRepository", e) - null - } - } - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 2b8e3953a1..fdd1f442b5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -248,9 +248,6 @@ public abstract class DrawerActivity extends ToolbarActivity if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } - - showSortListGroup(true); - hideSearchText(false); } /** diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 74d32aefd0..b9823f0fa1 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -155,14 +155,6 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable mSearchText.setText(getString(R.string.appbar_search_in, title)); } - public void hideSearchText(boolean hide) { - if (hide) { - mSearchText.setVisibility(View.GONE); - } else { - mSearchText.setVisibility(View.VISIBLE); - } - } - @SuppressLint("PrivateResource") private void showHomeSearchToolbar(boolean isShow) { viewThemeUtils.material.themeToolbar(mToolbar); From b4c5de4e7ecd30a96729f6ddac2d6fbb43153c01 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:00:25 +0100 Subject: [PATCH 047/102] Add Jetpack Compose ColorScheme Signed-off-by: alperozturk --- .../client/assistant/AsssistantScreen.kt | 3 - .../assistant/component/AddTaskAlertDialog.kt | 3 - .../client/assistant/component/CenterText.kt | 2 - .../client/assistant/component/TaskView.kt | 5 +- .../ui/composeActivity/ComposeActivity.kt | 21 +++++-- .../alertDialog/SimpleAlertDialog.kt | 16 ++--- .../bottomSheet/MoreActionsBottomSheet.kt | 3 - .../utils/extensions/SchemeExtensions.kt | 61 +++++++++++++++++++ 8 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 5d4c9520f3..7b22b5a26f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -48,7 +48,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -143,8 +142,6 @@ fun AssistantScreen(viewModel: AssistantViewModel) { if (showDeleteTaskAlertDialog) { taskIdToDeleted?.let { id -> SimpleAlertDialog( - backgroundColor = Color.White, - textColor = Color.Black, title = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title), description = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_description), dismiss = { showDeleteTaskAlertDialog = false }, diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index ac019e259a..14e71f39de 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -29,7 +29,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import com.nextcloud.client.assistant.AssistantViewModel @@ -44,8 +43,6 @@ fun AddTaskAlertDialog(viewModel: AssistantViewModel, taskType: TaskType, dismis } SimpleAlertDialog( - backgroundColor = Color.White, - textColor = Color.Black, title = taskType.name ?: "", description = taskType.description, dismiss = { dismiss() }, diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt index 20c56d17e4..66eabe37d0 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt @@ -27,7 +27,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.sp @@ -37,7 +36,6 @@ fun CenterText(text: String) { Text( text = text, fontSize = 18.sp, - color = Color.Black, textAlign = TextAlign.Center ) } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 4069c57d05..4c7da22843 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -36,6 +36,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -46,6 +47,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.nextcloud.ui.composeActivity.ComposeActivity.Companion.schemeFlow import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @@ -58,6 +60,7 @@ fun TaskView( task: Task, showDeleteTaskAlertDialog: (Long) -> Unit ) { + val scheme = schemeFlow.collectAsState().value var expanded by remember { mutableStateOf(false) } var showMoreActionsBottomSheet by remember { mutableStateOf(false) } @@ -66,7 +69,7 @@ fun TaskView( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(16.dp)) - .background(Color(R.color.primary)) + .background(scheme.primary) .combinedClickable(onClick = { expanded = !expanded }, onLongClick = { diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 6112fbe296..50ddeedd61 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -24,6 +24,8 @@ package com.nextcloud.ui.composeActivity import android.content.Context import android.os.Bundle import android.view.MenuItem +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -35,6 +37,7 @@ import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.common.NextcloudClient import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument +import com.nextcloud.utils.extensions.toColorScheme import com.owncloud.android.R import com.owncloud.android.databinding.ActivityComposeBinding import com.owncloud.android.lib.common.OwnCloudClientFactory @@ -42,6 +45,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.activity.DrawerActivity import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.withContext class ComposeActivity : DrawerActivity() { @@ -52,6 +56,8 @@ class ComposeActivity : DrawerActivity() { const val destinationKey = "destinationKey" const val titleKey = "titleKey" const val menuItemKey = "menuItemKey" + + lateinit var schemeFlow: MutableStateFlow } override fun onCreate(savedInstanceState: Bundle?) { @@ -68,8 +74,15 @@ class ComposeActivity : DrawerActivity() { setupDrawer(menuItemId) + schemeFlow = MutableStateFlow(viewThemeUtils.material.getScheme(this).toColorScheme()) + binding.composeView.setContent { - Content(destination, storageManager.user, this) + MaterialTheme( + colorScheme = schemeFlow.value, + content = { + Content(destination, storageManager.user, this) + } + ) } } @@ -79,7 +92,7 @@ class ComposeActivity : DrawerActivity() { } override fun onOptionsItemSelected(item: MenuItem): Boolean { - var retval = true + var result = true if (item.itemId == android.R.id.home) { if (isDrawerOpen) { closeDrawer() @@ -87,9 +100,9 @@ class ComposeActivity : DrawerActivity() { openDrawer() } } else { - retval = super.onOptionsItemSelected(item) + result = super.onOptionsItemSelected(item) } - return retval + return result } @Composable diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt index d7f5ba3508..90bf056652 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt @@ -31,7 +31,6 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.owncloud.android.R @@ -39,8 +38,6 @@ import com.owncloud.android.R @Suppress("LongParameterList") @Composable fun SimpleAlertDialog( - backgroundColor: Color, - textColor: Color, title: String, description: String?, heightFraction: Float? = null, @@ -57,15 +54,14 @@ fun SimpleAlertDialog( } AlertDialog( - containerColor = backgroundColor, onDismissRequest = { dismiss() }, title = { - Text(text = title, color = textColor) + Text(text = title) }, text = { Column(modifier = modifier) { - if (description != null) { - Text(text = description, color = textColor) + description?.let { + Text(text = description) } content?.let { @@ -81,16 +77,14 @@ fun SimpleAlertDialog( dismiss() }) { Text( - stringResource(id = R.string.common_ok), - color = textColor + stringResource(id = R.string.common_ok) ) } }, dismissButton = { TextButton(onClick = { dismiss() }) { Text( - stringResource(id = R.string.common_cancel), - color = textColor + stringResource(id = R.string.common_cancel) ) } } diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt index ef9038603c..94bd1bf95c 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt @@ -21,12 +21,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.owncloud.android.R import kotlinx.coroutines.launch @SuppressLint("ResourceAsColor") @@ -82,7 +80,6 @@ fun MoreActionsBottomSheet( Icon( painter = painterResource(id = action.first), contentDescription = "action icon", - tint = Color(R.color.secondary_button_background_color), modifier = Modifier.size(20.dp) ) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt new file mode 100644 index 0000000000..e069b2f9f9 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt @@ -0,0 +1,61 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.utils.extensions + +import androidx.compose.material3.ColorScheme +import androidx.compose.ui.graphics.Color +import com.vanniktech.ui.color +import scheme.Scheme + +fun Scheme.toColorScheme(): ColorScheme { + return ColorScheme( + primary = Color(primary.color.argb), + onPrimary = Color(onPrimary.color.argb), + primaryContainer = Color(primaryContainer.color.argb), + onPrimaryContainer = Color(onPrimaryContainer.color.argb), + inversePrimary = Color(inversePrimary.color.argb), + secondary = Color(secondary.color.argb), + onSecondary = Color(onSecondary.color.argb), + secondaryContainer = Color(secondaryContainer.color.argb), + onSecondaryContainer = Color(onSecondaryContainer.color.argb), + tertiary = Color(tertiary.color.argb), + onTertiary = Color(onTertiary.color.argb), + tertiaryContainer = Color(tertiaryContainer.color.argb), + onTertiaryContainer = Color(onTertiaryContainer.color.argb), + background = Color(background.color.argb), + onBackground = Color(onBackground.color.argb), + surface = Color(surface.color.argb), + onSurface = Color(onSurface.color.argb), + surfaceVariant = Color(surfaceVariant.color.argb), + onSurfaceVariant = Color(onSurfaceVariant.color.argb), + surfaceTint = Color(surfaceVariant.color.argb), + inverseSurface = Color(inverseSurface.color.argb), + inverseOnSurface = Color(inverseOnSurface.color.argb), + error = Color(error.color.argb), + onError = Color(onError.color.argb), + errorContainer = Color(errorContainer.color.argb), + onErrorContainer = Color(onErrorContainer.color.argb), + outline = Color(outline.color.argb), + outlineVariant = Color(outlineVariant.color.argb), + scrim = Color(scrim.color.argb) + ) +} From 62e3f00c45e804c36b733311b5f96bdd6b14d0f7 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:01:51 +0100 Subject: [PATCH 048/102] Simplify Signed-off-by: alperozturk --- .../ui/composeActivity/ComposeActivity.kt | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 50ddeedd61..7d91380a92 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -92,17 +92,13 @@ class ComposeActivity : DrawerActivity() { } override fun onOptionsItemSelected(item: MenuItem): Boolean { - var result = true - if (item.itemId == android.R.id.home) { - if (isDrawerOpen) { - closeDrawer() - } else { - openDrawer() + return when (item.itemId) { + android.R.id.home -> { + if (isDrawerOpen) closeDrawer() else openDrawer() + true } - } else { - result = super.onOptionsItemSelected(item) + else -> super.onOptionsItemSelected(item) } - return result } @Composable From 79cffa8d043574a7d77372ff6dc992bdc3c0db38 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:16:24 +0100 Subject: [PATCH 049/102] Revert dependency versions Signed-off-by: alperozturk --- .../java/com/nextcloud/ui/composeActivity/ComposeActivity.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 7d91380a92..64c96d64e9 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -74,11 +74,12 @@ class ComposeActivity : DrawerActivity() { setupDrawer(menuItemId) - schemeFlow = MutableStateFlow(viewThemeUtils.material.getScheme(this).toColorScheme()) + val colorScheme = viewThemeUtils.material.getScheme(this).toColorScheme() + schemeFlow = MutableStateFlow(colorScheme) binding.composeView.setContent { MaterialTheme( - colorScheme = schemeFlow.value, + colorScheme = colorScheme, content = { Content(destination, storageManager.user, this) } From 6f79f63bcbeadbfc37068807d94c7ddaa45f382b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:18:35 +0100 Subject: [PATCH 050/102] Rename const values Signed-off-by: alperozturk --- .../nextcloud/ui/composeActivity/ComposeActivity.kt | 12 ++++++------ .../owncloud/android/ui/activity/DrawerActivity.java | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 64c96d64e9..e86588fa31 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -53,9 +53,9 @@ class ComposeActivity : DrawerActivity() { lateinit var binding: ActivityComposeBinding companion object { - const val destinationKey = "destinationKey" - const val titleKey = "titleKey" - const val menuItemKey = "menuItemKey" + const val DESTINATION_KEY = "DESTINATION_KEY" + const val TITLE_KEY = "TITLE_KEY" + const val MENU_ITEM_KEY = "MENU_ITEM_KEY" lateinit var schemeFlow: MutableStateFlow } @@ -65,9 +65,9 @@ class ComposeActivity : DrawerActivity() { binding = ActivityComposeBinding.inflate(layoutInflater) setContentView(binding.root) - val destination = intent.getSerializableArgument(destinationKey, ComposeDestination::class.java) - val titleId = intent.getIntExtra(titleKey, R.string.empty) - val menuItemId = intent.getIntExtra(menuItemKey, R.id.nav_assistant) + val destination = intent.getSerializableArgument(DESTINATION_KEY, ComposeDestination::class.java) + val titleId = intent.getIntExtra(TITLE_KEY, R.string.empty) + val menuItemId = intent.getIntExtra(MENU_ITEM_KEY, R.id.nav_assistant) setupToolbar() updateActionBarTitleAndHomeButtonByString(getString(titleId)) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index fdd1f442b5..521e729416 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -559,9 +559,9 @@ public abstract class DrawerActivity extends ToolbarActivity private void startComposeActivity(ComposeDestination destination, int titleId, int menuItemId) { Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class); - composeActivity.putExtra(ComposeActivity.destinationKey, destination); - composeActivity.putExtra(ComposeActivity.titleKey, titleId); - composeActivity.putExtra(ComposeActivity.menuItemKey, menuItemId); + composeActivity.putExtra(ComposeActivity.DESTINATION_KEY, destination); + composeActivity.putExtra(ComposeActivity.TITLE_KEY, titleId); + composeActivity.putExtra(ComposeActivity.MENU_ITEM_KEY, menuItemId); startActivity(composeActivity); } From d492bce313ebe808b87c33b9028774596e20bfdf Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:40:29 +0100 Subject: [PATCH 051/102] Fix code analytics Signed-off-by: alperozturk --- .../java/com/nextcloud/ui/composeActivity/ComposeActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index e86588fa31..8596d979f4 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -60,6 +60,7 @@ class ComposeActivity : DrawerActivity() { lateinit var schemeFlow: MutableStateFlow } + @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityComposeBinding.inflate(layoutInflater) @@ -74,7 +75,7 @@ class ComposeActivity : DrawerActivity() { setupDrawer(menuItemId) - val colorScheme = viewThemeUtils.material.getScheme(this).toColorScheme() + val colorScheme = viewThemeUtils.getScheme(this).toColorScheme() schemeFlow = MutableStateFlow(colorScheme) binding.composeView.setContent { From 12454ed6a1ac3c3b220d26be51b12bfd8b0785f3 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:40:50 +0100 Subject: [PATCH 052/102] Fix code analytics Signed-off-by: alperozturk --- .../java/com/nextcloud/ui/composeActivity/ComposeActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 8596d979f4..70294e560f 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -127,7 +127,7 @@ class ComposeActivity : DrawerActivity() { try { OwnCloudClientFactory.createNextcloudClient(user, context) } catch (e: AccountUtils.AccountNotFoundException) { - Log_OC.e(this, "Error caught at init of AssistantRepository", e) + Log_OC.e(this, "Error caught at init of createNextcloudClient", e) null } } From 578ee5137fe3e573f430322032e0575c214da941 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:43:17 +0100 Subject: [PATCH 053/102] Update Jetpack Compose library version Signed-off-by: alperozturk --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index fe00f041b2..3f308f78ff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -261,7 +261,7 @@ dependencies { } // Jetpack Compose - implementation(platform("androidx.compose:compose-bom:2024.02.00")) + implementation(platform("androidx.compose:compose-bom:2024.02.01")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.material3:material3") From ae4cbb9e128815e4020a74c7cfc2d8ef53395b13 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 15:53:59 +0100 Subject: [PATCH 054/102] Rename enum keys Signed-off-by: alperozturk --- .../nextcloud/ui/composeActivity/ComposeActivity.kt | 12 ++++++------ .../owncloud/android/ui/activity/DrawerActivity.java | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 70294e560f..bfaf774764 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -53,9 +53,9 @@ class ComposeActivity : DrawerActivity() { lateinit var binding: ActivityComposeBinding companion object { - const val DESTINATION_KEY = "DESTINATION_KEY" - const val TITLE_KEY = "TITLE_KEY" - const val MENU_ITEM_KEY = "MENU_ITEM_KEY" + const val DESTINATION = "DESTINATION" + const val TITLE = "TITLE" + const val MENU_ITEM = "MENU_ITEM" lateinit var schemeFlow: MutableStateFlow } @@ -66,9 +66,9 @@ class ComposeActivity : DrawerActivity() { binding = ActivityComposeBinding.inflate(layoutInflater) setContentView(binding.root) - val destination = intent.getSerializableArgument(DESTINATION_KEY, ComposeDestination::class.java) - val titleId = intent.getIntExtra(TITLE_KEY, R.string.empty) - val menuItemId = intent.getIntExtra(MENU_ITEM_KEY, R.id.nav_assistant) + val destination = intent.getSerializableArgument(DESTINATION, ComposeDestination::class.java) + val titleId = intent.getIntExtra(TITLE, R.string.empty) + val menuItemId = intent.getIntExtra(MENU_ITEM, R.id.nav_assistant) setupToolbar() updateActionBarTitleAndHomeButtonByString(getString(titleId)) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 521e729416..e144f861cf 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -559,9 +559,9 @@ public abstract class DrawerActivity extends ToolbarActivity private void startComposeActivity(ComposeDestination destination, int titleId, int menuItemId) { Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class); - composeActivity.putExtra(ComposeActivity.DESTINATION_KEY, destination); - composeActivity.putExtra(ComposeActivity.TITLE_KEY, titleId); - composeActivity.putExtra(ComposeActivity.MENU_ITEM_KEY, menuItemId); + composeActivity.putExtra(ComposeActivity.DESTINATION, destination); + composeActivity.putExtra(ComposeActivity.TITLE, titleId); + composeActivity.putExtra(ComposeActivity.MENU_ITEM, menuItemId); startActivity(composeActivity); } From bc1663616c182b53c7ea9f685500dee899efa759 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 16:24:15 +0100 Subject: [PATCH 055/102] UI fixes Signed-off-by: alperozturk --- .../client/assistant/component/TaskView.kt | 20 +++++++------------ .../ui/composeActivity/ComposeActivity.kt | 7 ++----- .../alertDialog/SimpleAlertDialog.kt | 3 ++- .../bottomSheet/MoreActionsBottomSheet.kt | 3 +++ 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 4c7da22843..03af5de8f7 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -21,7 +21,6 @@ package com.nextcloud.client.assistant.component -import android.annotation.SuppressLint import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring @@ -34,9 +33,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -47,58 +46,54 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.nextcloud.ui.composeActivity.ComposeActivity.Companion.schemeFlow +import com.nextcloud.ui.composeActivity.ComposeActivity.Companion.colorScheme import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @OptIn(ExperimentalFoundationApi::class) -@SuppressLint("ResourceAsColor") @Suppress("LongMethod", "MagicNumber") @Composable fun TaskView( task: Task, showDeleteTaskAlertDialog: (Long) -> Unit ) { - val scheme = schemeFlow.collectAsState().value var expanded by remember { mutableStateOf(false) } var showMoreActionsBottomSheet by remember { mutableStateOf(false) } - // TODO Check color Column( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(16.dp)) - .background(scheme.primary) + .background(colorScheme.primary) .combinedClickable(onClick = { expanded = !expanded }, onLongClick = { showMoreActionsBottomSheet = true }) + .padding(start = 8.dp) ) { Spacer(modifier = Modifier.height(8.dp)) Text( text = stringResource(id = R.string.assistant_screen_task_view_input), - modifier = Modifier.padding(4.dp), color = Color.White ) task.input?.let { Text( text = it, - modifier = Modifier.padding(4.dp), color = Color.White ) } - Spacer(modifier = Modifier.height(16.dp)) + Spacer(modifier = Modifier.height(4.dp)) + HorizontalDivider() + Spacer(modifier = Modifier.height(4.dp)) Text( text = stringResource(id = R.string.assistant_screen_task_view_output), color = Color.White, - modifier = Modifier - .padding(4.dp) ) task.output?.let { @@ -112,7 +107,6 @@ fun TaskView( stiffness = Spring.StiffnessLow ) ) - .padding(4.dp) ) } diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index bfaf774764..78e478d655 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -45,7 +45,6 @@ import com.owncloud.android.lib.common.accounts.AccountUtils import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.activity.DrawerActivity import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.withContext class ComposeActivity : DrawerActivity() { @@ -57,7 +56,7 @@ class ComposeActivity : DrawerActivity() { const val TITLE = "TITLE" const val MENU_ITEM = "MENU_ITEM" - lateinit var schemeFlow: MutableStateFlow + lateinit var colorScheme: ColorScheme } @Suppress("DEPRECATION") @@ -74,9 +73,7 @@ class ComposeActivity : DrawerActivity() { updateActionBarTitleAndHomeButtonByString(getString(titleId)) setupDrawer(menuItemId) - - val colorScheme = viewThemeUtils.getScheme(this).toColorScheme() - schemeFlow = MutableStateFlow(colorScheme) + colorScheme = viewThemeUtils.getScheme(this).toColorScheme() binding.composeView.setContent { MaterialTheme( diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt index 90bf056652..f55c40e062 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt @@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.AlertDialog +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -72,7 +73,7 @@ fun SimpleAlertDialog( } }, confirmButton = { - TextButton(onClick = { + FilledTonalButton(onClick = { onComplete() dismiss() }) { diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt index 94bd1bf95c..35414aac8a 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt @@ -14,10 +14,12 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -80,6 +82,7 @@ fun MoreActionsBottomSheet( Icon( painter = painterResource(id = action.first), contentDescription = "action icon", + tint = colorScheme.primary, modifier = Modifier.size(20.dp) ) From bab532149de57727517aaa764abbe52c97426cdf Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 1 Mar 2024 16:29:08 +0100 Subject: [PATCH 056/102] UI fixes Signed-off-by: alperozturk --- .../client/assistant/component/TaskView.kt | 21 ++++++------------- .../bottomSheet/MoreActionsBottomSheet.kt | 1 - app/src/main/res/values/strings.xml | 4 ++-- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 03af5de8f7..875d7d5dbe 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -75,30 +75,21 @@ fun TaskView( ) { Spacer(modifier = Modifier.height(8.dp)) - Text( - text = stringResource(id = R.string.assistant_screen_task_view_input), - color = Color.White - ) - task.input?.let { Text( - text = it, + text = stringResource(id = R.string.assistant_screen_task_view_input, it), color = Color.White ) } - Spacer(modifier = Modifier.height(4.dp)) - HorizontalDivider() - Spacer(modifier = Modifier.height(4.dp)) - - Text( - text = stringResource(id = R.string.assistant_screen_task_view_output), - color = Color.White, - ) + HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) task.output?.let { Text( - text = if (expanded) it else it.take(100) + "...", + text = stringResource( + id = R.string.assistant_screen_task_view_output, + if (expanded) it else it.take(100) + "..." + ), color = Color.White, modifier = Modifier .animateContentSize( diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt index 35414aac8a..89d0f87eab 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt @@ -19,7 +19,6 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fb5261c2a2..edef6eb769 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,8 +36,8 @@ Type some text - Input: - Output: + Input\n%s + Output\n%s Show more Show less From bed4fb1ee5c06bc1211013d0a07ca4cdef783d52 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:50:24 +0100 Subject: [PATCH 057/102] Rebase master Signed-off-by: alperozturk --- gradle.properties | 2 +- settings.gradle | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/gradle.properties b/gradle.properties index 43fcf18f1b..b0163b9470 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,4 @@ org.gradle.parallel=true org.gradle.configureondemand=true # Needed for local libs -org.gradle.dependency.verification=lenient \ No newline at end of file +# org.gradle.dependency.verification=lenient \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 34030893f3..5c2438e2ad 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,9 +14,3 @@ include ':appscan' // substitute module('com.github.nextcloud:android-library') using project(':library') // } //} - -includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_library') { - dependencySubstitution { - substitute module('com.github.nextcloud:android-library') using project(':library') - } -} From a9e102aeb3aa8c867b1b8508251744497a8ac940 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:36:37 +0100 Subject: [PATCH 058/102] Fix Code Analytics Signed-off-by: alperozturk --- .../com/nextcloud/client/assistant/AssistantRepositoryTests.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index cea7fd5bcb..7bb5bf95ed 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -27,6 +27,7 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test +@Suppress("MagicNumber") class AssistantRepositoryTests : AbstractOnServerIT() { private var sut: AssistantRepository? = null From c5171d33d174d163517176d94c8df7f9dee7c88d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 11:51:06 +0100 Subject: [PATCH 059/102] Rebase master Signed-off-by: alperozturk --- gradle/verification-metadata.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index cec7df13cb..6e60f96aa4 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -747,6 +747,11 @@ + + + + + From 96deb984e42c7cebe6e4d3b00fb086c78ddd8148 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 11 Mar 2024 11:32:02 +0100 Subject: [PATCH 060/102] Fix git conflicts Signed-off-by: alperozturk --- gradle/verification-metadata.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 6e60f96aa4..5af996e74e 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -3978,6 +3978,14 @@ + + + + + + + + From 6a8b50e116ac5b8a0a990d035083d122a657025d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 13:26:32 +0100 Subject: [PATCH 061/102] Use Common-UI Lib Signed-off-by: alperozturk --- .../client/assistant/component/TaskView.kt | 4 +- .../ui/composeActivity/ComposeActivity.kt | 8 +-- .../utils/extensions/SchemeExtensions.kt | 61 ------------------- 3 files changed, 3 insertions(+), 70 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 875d7d5dbe..7f8d6b0cc5 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -46,7 +47,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.nextcloud.ui.composeActivity.ComposeActivity.Companion.colorScheme import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @@ -65,7 +65,7 @@ fun TaskView( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(16.dp)) - .background(colorScheme.primary) + .background(MaterialTheme.colorScheme.primary) .combinedClickable(onClick = { expanded = !expanded }, onLongClick = { diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 78e478d655..573f11ce62 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -24,7 +24,6 @@ package com.nextcloud.ui.composeActivity import android.content.Context import android.os.Bundle import android.view.MenuItem -import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -37,7 +36,6 @@ import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.common.NextcloudClient import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument -import com.nextcloud.utils.extensions.toColorScheme import com.owncloud.android.R import com.owncloud.android.databinding.ActivityComposeBinding import com.owncloud.android.lib.common.OwnCloudClientFactory @@ -55,11 +53,8 @@ class ComposeActivity : DrawerActivity() { const val DESTINATION = "DESTINATION" const val TITLE = "TITLE" const val MENU_ITEM = "MENU_ITEM" - - lateinit var colorScheme: ColorScheme } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityComposeBinding.inflate(layoutInflater) @@ -73,11 +68,10 @@ class ComposeActivity : DrawerActivity() { updateActionBarTitleAndHomeButtonByString(getString(titleId)) setupDrawer(menuItemId) - colorScheme = viewThemeUtils.getScheme(this).toColorScheme() binding.composeView.setContent { MaterialTheme( - colorScheme = colorScheme, + colorScheme = viewThemeUtils.getColorScheme(this), content = { Content(destination, storageManager.user, this) } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt deleted file mode 100644 index e069b2f9f9..0000000000 --- a/app/src/main/java/com/nextcloud/utils/extensions/SchemeExtensions.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Alper Ozturk - * Copyright (C) 2024 Alper Ozturk - * Copyright (C) 2024 Nextcloud GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.utils.extensions - -import androidx.compose.material3.ColorScheme -import androidx.compose.ui.graphics.Color -import com.vanniktech.ui.color -import scheme.Scheme - -fun Scheme.toColorScheme(): ColorScheme { - return ColorScheme( - primary = Color(primary.color.argb), - onPrimary = Color(onPrimary.color.argb), - primaryContainer = Color(primaryContainer.color.argb), - onPrimaryContainer = Color(onPrimaryContainer.color.argb), - inversePrimary = Color(inversePrimary.color.argb), - secondary = Color(secondary.color.argb), - onSecondary = Color(onSecondary.color.argb), - secondaryContainer = Color(secondaryContainer.color.argb), - onSecondaryContainer = Color(onSecondaryContainer.color.argb), - tertiary = Color(tertiary.color.argb), - onTertiary = Color(onTertiary.color.argb), - tertiaryContainer = Color(tertiaryContainer.color.argb), - onTertiaryContainer = Color(onTertiaryContainer.color.argb), - background = Color(background.color.argb), - onBackground = Color(onBackground.color.argb), - surface = Color(surface.color.argb), - onSurface = Color(onSurface.color.argb), - surfaceVariant = Color(surfaceVariant.color.argb), - onSurfaceVariant = Color(onSurfaceVariant.color.argb), - surfaceTint = Color(surfaceVariant.color.argb), - inverseSurface = Color(inverseSurface.color.argb), - inverseOnSurface = Color(inverseOnSurface.color.argb), - error = Color(error.color.argb), - onError = Color(onError.color.argb), - errorContainer = Color(errorContainer.color.argb), - onErrorContainer = Color(onErrorContainer.color.argb), - outline = Color(outline.color.argb), - outlineVariant = Color(outlineVariant.color.argb), - scrim = Color(scrim.color.argb) - ) -} From 948f249d9dbfdae7f8d3bc5ade99d89682f8816f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 13:55:57 +0100 Subject: [PATCH 062/102] lint decreased Signed-off-by: alperozturk --- .../com/nextcloud/client/assistant/AsssistantScreen.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 7b22b5a26f..0eb57b8afc 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -136,8 +136,8 @@ fun AssistantScreen(viewModel: AssistantViewModel) { } } - checkTaskAdd(isTaskCreated, activity, viewModel) - checkTaskDeletion(isTaskDeleted, activity, viewModel) + CheckTaskAdd(isTaskCreated, activity, viewModel) + CheckTaskDeletion(isTaskDeleted, activity, viewModel) if (showDeleteTaskAlertDialog) { taskIdToDeleted?.let { id -> @@ -160,7 +160,7 @@ fun AssistantScreen(viewModel: AssistantViewModel) { } @Composable -private fun checkTaskAdd(isTaskCreated: Boolean?, activity: Activity, viewModel: AssistantViewModel) { +private fun CheckTaskAdd(isTaskCreated: Boolean?, activity: Activity, viewModel: AssistantViewModel) { isTaskCreated?.let { val messageId = if (it) { R.string.assistant_screen_task_create_success_message @@ -178,7 +178,7 @@ private fun checkTaskAdd(isTaskCreated: Boolean?, activity: Activity, viewModel: } @Composable -private fun checkTaskDeletion(isTaskDeleted: Boolean?, activity: Activity, viewModel: AssistantViewModel) { +private fun CheckTaskDeletion(isTaskDeleted: Boolean?, activity: Activity, viewModel: AssistantViewModel) { isTaskDeleted?.let { val messageId = if (it) { R.string.assistant_screen_task_delete_success_message From 37af72aff2321e82c137dc3e63cbba74e4945781 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 14:26:53 +0100 Subject: [PATCH 063/102] use local common ui lib Signed-off-by: alperozturk --- gradle/verification-metadata.xml | 61 ++++++++++++++++++++++++++++++++ settings.gradle | 7 ++++ 2 files changed, 68 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 5af996e74e..b88e14c8f7 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -187,6 +187,7 @@ + @@ -439,6 +440,9 @@ + + + @@ -534,6 +538,9 @@ + + + @@ -1234,6 +1241,14 @@ + + + + + + + + @@ -1401,6 +1416,14 @@ + + + + + + + + @@ -1518,6 +1541,11 @@ + + + + + @@ -1534,6 +1562,14 @@ + + + + + + + + @@ -2720,6 +2756,11 @@ + + + + + @@ -2736,6 +2777,11 @@ + + + + + @@ -6063,6 +6109,11 @@ + + + + + @@ -8009,6 +8060,16 @@ + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 5c2438e2ad..92f7c0215a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,3 +14,10 @@ include ':appscan' // substitute module('com.github.nextcloud:android-library') using project(':library') // } //} + + +includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_common') { + dependencySubstitution { + substitute module('com.github.nextcloud.android-common:ui') using project(':ui') + } +} \ No newline at end of file From 728767d3b48468852313bc3cfaff8951e6b07bed Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 16:22:30 +0100 Subject: [PATCH 064/102] UI Fixes Signed-off-by: alperozturk --- app/build.gradle | 2 +- .../client/assistant/AssistantViewModel.kt | 5 ++++- .../client/assistant/AsssistantScreen.kt | 21 +++++++------------ .../client/assistant/component/TaskView.kt | 9 ++++++-- .../ui/composeActivity/ComposeActivity.kt | 2 +- app/src/main/res/values/strings.xml | 5 ++--- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3f308f78ff..f9bc33210a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -249,7 +249,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion = "1.5.9" + kotlinCompilerExtensionVersion = "1.5.10" } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index d946576323..2a97e7589f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -25,6 +25,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient +import com.owncloud.android.MainApp +import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task import com.owncloud.android.lib.resources.assistant.model.TaskType import kotlinx.coroutines.Dispatchers @@ -84,7 +86,8 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { private fun getTaskTypes() { viewModelScope.launch(Dispatchers.IO) { - val result = arrayListOf(TaskType(null, "All", null)) + val allTaskType = MainApp.getAppContext().getString(R.string.assistant_screen_all_task_type) + val result = arrayListOf(TaskType(null, allTaskType, null)) val taskTypes = repository.getTaskTypes().resultData.types ?: listOf() result.addAll(taskTypes) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 0eb57b8afc..df9786edb6 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -117,22 +117,17 @@ fun AssistantScreen(viewModel: AssistantViewModel) { LinearProgressIndicator(progress = { pullRefreshState.progress }, modifier = Modifier.fillMaxWidth()) } - FloatingActionButton( - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(16.dp), - onClick = { - if (selectedTaskType?.id != null) { + if (selectedTaskType?.name != stringResource(id = R.string.assistant_screen_all_task_type)) { + FloatingActionButton( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(16.dp), + onClick = { showAddTaskAlertDialog = true - } else { - DisplayUtils.showSnackMessage( - activity, - activity.getString(R.string.assistant_screen_select_different_task_type_to_add) - ) } + ) { + Icon(Icons.Filled.Add, "Add Task Icon") } - ) { - Icon(Icons.Filled.Add, "Add Task Icon") } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index 7f8d6b0cc5..a94b5a86d7 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @@ -78,18 +79,22 @@ fun TaskView( task.input?.let { Text( text = stringResource(id = R.string.assistant_screen_task_view_input, it), - color = Color.White + color = Color.White, + fontSize = 18.sp, ) } - HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) + Spacer(modifier = Modifier.height(16.dp)) task.output?.let { + HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) + Text( text = stringResource( id = R.string.assistant_screen_task_view_output, if (expanded) it else it.take(100) + "..." ), + fontSize = 12.sp, color = Color.White, modifier = Modifier .animateContentSize( diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 573f11ce62..23ff8b5d31 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -71,7 +71,7 @@ class ComposeActivity : DrawerActivity() { binding.composeView.setContent { MaterialTheme( - colorScheme = viewThemeUtils.getColorScheme(this), + // colorScheme = viewThemeUtils.getColorScheme(this), content = { Content(destination, storageManager.user, this) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index edef6eb769..24f983e212 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,8 +24,6 @@ Delete Task Are you sure you want to delete this task? - Please select different task type to create a new task - Delete Task Task successfully created @@ -36,7 +34,8 @@ Type some text - Input\n%s + All + Input %s Output\n%s Show more Show less From cabde4e77925328586bdcedb13c1bebbaf2e601d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 16:39:34 +0100 Subject: [PATCH 065/102] Update ui library, add jetpack compose preview capability Signed-off-by: alperozturk --- app/build.gradle | 3 ++ .../client/assistant/component/TaskView.kt | 45 ++++++++++++++++--- .../ui/composeActivity/ComposeActivity.kt | 2 +- app/src/main/res/values/strings.xml | 2 - settings.gradle | 7 --- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f9bc33210a..44e3873660 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -265,6 +265,8 @@ dependencies { implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.material3:material3") + implementation("androidx.compose.ui:ui-tooling-preview:1.6.2") + compileOnly 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2' // remove after entire switch to lib v2 @@ -304,6 +306,7 @@ dependencies { implementation "com.github.nextcloud-deps.hwsecurity:hwsecurity-fido:$fidoVersion" implementation "com.github.nextcloud-deps.hwsecurity:hwsecurity-fido2:$fidoVersion" + debugImplementation 'androidx.compose.ui:ui-tooling:1.6.2' // document scanner not available on FDroid (generic) due to OpenCV binaries gplayImplementation project(':appscan') diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt index a94b5a86d7..1fd0116b0f 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet @@ -78,9 +79,9 @@ fun TaskView( task.input?.let { Text( - text = stringResource(id = R.string.assistant_screen_task_view_input, it), + text = it, color = Color.White, - fontSize = 18.sp, + fontSize = 18.sp ) } @@ -90,10 +91,7 @@ fun TaskView( HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) Text( - text = stringResource( - id = R.string.assistant_screen_task_view_output, - if (expanded) it else it.take(100) + "..." - ), + text = if (expanded) it else it.take(100) + "...", fontSize = 12.sp, color = Color.White, modifier = Modifier @@ -139,3 +137,38 @@ fun TaskView( } } } + +@Preview +@Composable +private fun TaskViewPreview() { + val output = + "Lorem Ipsum is simply dummy text of the printing and " + + "typesetting industry. Lorem Ipsum has been the " + + "industry's standard dummy text ever since the 1500s, " + + "when an unknown printer took a galley of type and " + + "scrambled it to make a type specimen book. " + + "It has survived not only five centuries, but also " + + "the leap into electronic typesetting, remaining" + + " essentially unchanged. It wLorem Ipsum is simply dummy" + + " text of the printing and typesetting industry. " + + "Lorem Ipsum has been the industry's standard dummy " + + "text ever since the 1500s, when an unknown printer took a" + + " galley of type and scrambled it to make a type specimen book. " + + "It has survived not only five centuries, but also the leap " + + "into electronic typesetting, remaining essentially unchanged." + + TaskView( + task = Task( + 1, + "Free Prompt", + 0, + "1", + "1", + "Give me text", + output, + "", + "" + ) + ) { + } +} diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 23ff8b5d31..573f11ce62 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -71,7 +71,7 @@ class ComposeActivity : DrawerActivity() { binding.composeView.setContent { MaterialTheme( - // colorScheme = viewThemeUtils.getColorScheme(this), + colorScheme = viewThemeUtils.getColorScheme(this), content = { Content(destination, storageManager.user, this) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 24f983e212..b875deb946 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -35,8 +35,6 @@ Type some text All - Input %s - Output\n%s Show more Show less diff --git a/settings.gradle b/settings.gradle index 92f7c0215a..5c2438e2ad 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,10 +14,3 @@ include ':appscan' // substitute module('com.github.nextcloud:android-library') using project(':library') // } //} - - -includeBuild('/Users/alperozturk/Desktop/nextcloud/nextcloud_android_common') { - dependencySubstitution { - substitute module('com.github.nextcloud.android-common:ui') using project(':ui') - } -} \ No newline at end of file From 15c560e9b050053b97ea1c207bfe66d89b90b408 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 4 Mar 2024 16:52:58 +0100 Subject: [PATCH 066/102] Update ui library, add jetpack compose preview capability Signed-off-by: alperozturk --- app/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 44e3873660..abfa60bbdb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -266,7 +266,7 @@ dependencies { implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.material3:material3") implementation("androidx.compose.ui:ui-tooling-preview:1.6.2") - + debugImplementation 'androidx.compose.ui:ui-tooling:1.6.2' compileOnly 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2' // remove after entire switch to lib v2 @@ -306,7 +306,6 @@ dependencies { implementation "com.github.nextcloud-deps.hwsecurity:hwsecurity-fido:$fidoVersion" implementation "com.github.nextcloud-deps.hwsecurity:hwsecurity-fido2:$fidoVersion" - debugImplementation 'androidx.compose.ui:ui-tooling:1.6.2' // document scanner not available on FDroid (generic) due to OpenCV binaries gplayImplementation project(':appscan') From 2cc60c0036735e2ad5978f53695f820ff014a2c2 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 5 Mar 2024 08:48:13 +0100 Subject: [PATCH 067/102] add jetpack compose preview capability Signed-off-by: alperozturk --- .idea/inspectionProfiles/ktlint.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.idea/inspectionProfiles/ktlint.xml b/.idea/inspectionProfiles/ktlint.xml index caad859bb6..60b453cca1 100644 --- a/.idea/inspectionProfiles/ktlint.xml +++ b/.idea/inspectionProfiles/ktlint.xml @@ -7,30 +7,39 @@ From 4be53bbd8a48e74005262d605ec0deff0c409a40 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 6 Mar 2024 08:38:22 +0100 Subject: [PATCH 068/102] Fix tests Signed-off-by: alperozturk --- app/src/main/res/values-sk-rSK/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 01bdc92a34..32a141ac41 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -789,7 +789,7 @@ Plný prístup Médiá iba načítanie Obrázky - Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou.\n\nFunkcie:\n * Jednoducho použiteľné moderné rozhranie, hodiace sa k vzhľadu vášho servera* \n Nahrávanie súborov na Nextcloud server\n * Ich sprístupnenie s inými ľudmi\n * Synchronizácia vašich obľúbených súborov a adresárov\n * Vyhľadávanie naprieč všetkými adresármi na serveri\n * Automatické nahrávanie fotiek a videí nasnímaných vašim zariadením\n * Doručovanie notifikácií\n * Podpora viac účtov naraz\n * Zabezpečený prístup k vašim dátam pomocou odtlačku prstu alebo kódom PIN\n * Začlenenie DAVx5 (predtým známy ako DAVdroid) pre jednoduchý prístup ku kalendáru & synchronizácii kontaktov\n\n Akékoľvek problémy prosím hláste na https://github.com/nextcloud/android/issues a o aplikácii môžete diskutovať na https://help.nextcloud.com/c/clients/android\n\n Nepoznáte ešte Nextcloud? Nextcloud je server pre súkromnú synchronizáciu súborov &, zdieľanie a komunikáciu. Je to slobodný softvér a môžete si ho prevádzkovať buď sami alebo si ho prenajímať od nejakej spoločnosti. Týmto spôsobom získate plnú vládu nad svojimi fotkami, údajom v kalendári a kontaktoch, dokumentom a všetkým ostatným.\n\n Viac zistíte na https://nextcloud.com + Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou.\n\nFunkcie:\n * Jednoducho použiteľné moderné rozhranie, hodiace sa k vzhľadu vášho servera* \n Nahrávanie súborov na Nextcloud server\n * Ich sprístupnenie s inými ľudmi\n * Synchronizácia vašich obľúbených súborov a adresárov\n * Vyhľadávanie naprieč všetkými adresármi na serveri\n * Automatické nahrávanie fotiek a videí nasnímaných vašim zariadením\n * Doručovanie notifikácií\n * Podpora viac účtov naraz\n * Zabezpečený prístup k vašim dátam pomocou odtlačku prstu alebo kódom PIN\n * Začlenenie DAVx5 (predtým známy ako DAVdroid) pre jednoduchý prístup ku kalendáru synchronizácii kontaktov\n\n Akékoľvek problémy prosím hláste na https://github.com/nextcloud/android/issues a o aplikácii môžete diskutovať na https://help.nextcloud.com/c/clients/android\n\n Nepoznáte ešte Nextcloud? Nextcloud je server pre súkromnú synchronizáciu súborov , zdieľanie a komunikáciu. Je to slobodný softvér a môžete si ho prevádzkovať buď sami alebo si ho prenajímať od nejakej spoločnosti. Týmto spôsobom získate plnú vládu nad svojimi fotkami, údajom v kalendári a kontaktoch, dokumentom a všetkým ostatným.\n\n Viac zistíte na https://nextcloud.com Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou.\nToto je oficiálna vývojová verzia, obsahujúca dennú vzorku všetkých nových a nevyskúšaných funkcií, ktoré môžu spôsobovať nestabilitu a viesť ku strate dát. Aplikácia v tomto štádiu vývoja je určená tým používateľom, ktorí sú ochotní skúšať a hlásiť chyby, ktoré sa vyskytnú. Nepoužívajte ju pre svoju produkčnú prácu.\n\nObe oficiálne verzie, tak vývojová ako aj produkčná sú k dispozícii na F-droid a je možné ich mať nainštalované súbežne. Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou. Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou (vývojová verzia) From dbb4aa4fb24435b1cb26eb1944bc7feec4f5a67b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 5 Mar 2024 11:50:33 +0100 Subject: [PATCH 069/102] Add Assistant Capability Signed-off-by: alperozturk --- .../78.json | 12 +- .../79.json | 1203 +++++++++++++++++ .../database/entity/CapabilityEntity.kt | 2 + .../datamodel/FileDataStorageManager.java | 2 + .../com/owncloud/android/db/ProviderMeta.java | 3 +- .../android/ui/activity/DrawerActivity.java | 1 + .../android/utils/DrawerMenuUtil.java | 6 + 7 files changed, 1225 insertions(+), 4 deletions(-) create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json index 21af9f58d8..d1da7a4406 100644 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 78, - "identityHash": "f26afed3b9b87a3acb578947a26223ac", + "identityHash": "ec997f271f9045e8483b260f036a168f", "entities": [ { "tableName": "arbitrary_data", @@ -44,7 +44,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", "fields": [ { "fieldPath": "id", @@ -52,6 +52,12 @@ "affinity": "INTEGER", "notNull": false }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, { "fieldPath": "accountName", "columnName": "account", @@ -1191,7 +1197,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f26afed3b9b87a3acb578947a26223ac')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec997f271f9045e8483b260f036a168f')" ] } } \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json new file mode 100644 index 0000000000..63509f229f --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json @@ -0,0 +1,1203 @@ +{ + "formatVersion": 1, + "database": { + "version": 79, + "identityHash": "ec997f271f9045e8483b260f036a168f", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` INTEGER, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec997f271f9045e8483b260f036a168f')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt index 5a9a208b94..63709be80f 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt @@ -32,6 +32,8 @@ data class CapabilityEntity( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = ProviderTableMeta._ID) val id: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_ASSISTANT) + val assistant: Int?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME) val accountName: String?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_VERSION_MAYOR) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 3901ccbbbf..a63a84355e 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -2005,6 +2005,7 @@ public class FileDataStorageManager { capability.getUserStatusSupportsEmoji().getValue()); contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION, capability.getFilesLockingVersion()); + contentValues.put(ProviderTableMeta.CAPABILITIES_ASSISTANT, capability.getAssistant().getValue()); contentValues.put(ProviderTableMeta.CAPABILITIES_GROUPFOLDERS, capability.getGroupfolders().getValue()); contentValues.put(ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT, capability.getDropAccount().getValue()); contentValues.put(ProviderTableMeta.CAPABILITIES_SECURITY_GUARD, capability.getSecurityGuard().getValue()); @@ -2173,6 +2174,7 @@ public class FileDataStorageManager { getBoolean(cursor, ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI)); capability.setFilesLockingVersion( getString(cursor, ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION)); + capability.setAssistant(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_ASSISTANT)); capability.setGroupfolders(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_GROUPFOLDERS)); capability.setDropAccount(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT)); capability.setSecurityGuard(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_SECURITY_GUARD)); diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index 6d2f72438f..8a7f47a47d 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -35,7 +35,7 @@ import java.util.List; */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 78; + public static final int DB_VERSION = 79; private ProviderMeta() { // No instance @@ -265,6 +265,7 @@ public class ProviderMeta { public static final String CAPABILITIES_ETAG = "etag"; public static final String CAPABILITIES_USER_STATUS = "user_status"; public static final String CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI = "user_status_supports_emoji"; + public static final String CAPABILITIES_ASSISTANT = "assistant"; public static final String CAPABILITIES_GROUPFOLDERS = "groupfolders"; public static final String CAPABILITIES_DROP_ACCOUNT = "drop_account"; public static final String CAPABILITIES_SECURITY_GUARD = "security_guard"; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index e144f861cf..3ba730fcea 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -468,6 +468,7 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterSearchMenuItems(menu, user, getResources()); DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); + DrawerMenuUtil.filterAssistantMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index 8ac2280a87..a09099800e 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -64,6 +64,12 @@ public final class DrawerMenuUtil { } } + public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability) { + if (capability != null && !capability.getAssistant().isTrue()) { + filterMenuItems(menu, R.id.nav_assistant); + } + } + public static void filterGroupfoldersMenuItem(Menu menu, @Nullable OCCapability capability) { if (capability != null && !capability.getGroupfolders().isTrue()) { filterMenuItems(menu, R.id.nav_groupfolders); From 170863bde760c88aaa86b17e3844648e03b3aa07 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 11 Mar 2024 11:32:14 +0100 Subject: [PATCH 070/102] Fix git conflicts Signed-off-by: alperozturk --- gradle/verification-metadata.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b88e14c8f7..a0c05c591e 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -754,11 +754,6 @@ - - - - - @@ -4048,6 +4043,14 @@ + + + + + + + + From d201f9b4e3535d782c3d1ad0729a49b090a99038 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 5 Mar 2024 16:48:12 +0100 Subject: [PATCH 071/102] Fix tests Signed-off-by: alperozturk --- .../java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt index d8a742995d..1b26035deb 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt @@ -831,7 +831,7 @@ class EncryptionUtilsV2IT : AbstractIT() { val signature = encryptionUtilsV2.getMessageSignature(enc1Cert, enc1PrivateKey, encryptedFolderMetadata1) // serialize - val encryptedJson = EncryptionUtils.serializeJSON(encryptedFolderMetadata1) + val encryptedJson = EncryptionUtils.serializeJSON(encryptedFolderMetadata1, true) // de-serialize val encryptedFolderMetadata2 = EncryptionUtils.deserializeJSON( From f7f63741203b3c23dd706ccd992a53306d3ed577 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 5 Mar 2024 16:58:40 +0100 Subject: [PATCH 072/102] Check Capability and server version for tests Signed-off-by: alperozturk --- .../assistant/AssistantRepositoryTests.kt | 25 +++++++++++++++++++ .../java/com/owncloud/android/AbstractIT.java | 7 +++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index 7bb5bf95ed..42be5605cb 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -23,6 +23,7 @@ package com.nextcloud.client.assistant import com.nextcloud.client.assistant.repository.AssistantRepository import com.owncloud.android.AbstractOnServerIT +import com.owncloud.android.lib.resources.status.NextcloudVersion import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -39,6 +40,12 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Test fun testGetTaskTypes() { + testOnlyOnServer(NextcloudVersion.nextcloud_28) + + if (capability.assistant.isFalse) { + return + } + val result = sut?.getTaskTypes() assertTrue(result?.isSuccess == true) @@ -48,6 +55,12 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Test fun testGetTaskList() { + testOnlyOnServer(NextcloudVersion.nextcloud_28) + + if (capability.assistant.isFalse) { + return + } + val result = sut?.getTaskList("assistant") assertTrue(result?.isSuccess == true) @@ -57,6 +70,12 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Test fun testCreateTask() { + testOnlyOnServer(NextcloudVersion.nextcloud_28) + + if (capability.assistant.isFalse) { + return + } + val input = "Give me some random output for test purpose" val type = "OCP\\TextProcessing\\FreePromptTaskType" val result = sut?.createTask(input, type) @@ -65,6 +84,12 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Test fun testDeleteTask() { + testOnlyOnServer(NextcloudVersion.nextcloud_28) + + if (capability.assistant.isFalse) { + return + } + testCreateTask() sleep(120) diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index 9621972d2e..52669efd18 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -195,13 +195,18 @@ public abstract class AbstractIT { } protected void testOnlyOnServer(OwnCloudVersion version) throws AccountUtils.AccountNotFoundException { + OCCapability ocCapability = getCapability(); + assumeTrue(ocCapability.getVersion().isNewerOrEqual(version)); + } + + protected OCCapability getCapability() throws AccountUtils.AccountNotFoundException { NextcloudClient client = OwnCloudClientFactory.createNextcloudClient(user, targetContext); OCCapability ocCapability = (OCCapability) new GetCapabilitiesRemoteOperation() .execute(client) .getSingleData(); - assumeTrue(ocCapability.getVersion().isNewerOrEqual(version)); + return ocCapability; } @Before From 7c75aee94c137d603c3498e912af4591f12469c1 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 6 Mar 2024 08:37:44 +0100 Subject: [PATCH 073/102] Fix tests Signed-off-by: alperozturk --- .../java/com/owncloud/android/util/EncryptionTestIT.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java index 77a72864ec..17c3fc8a2f 100644 --- a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java +++ b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java @@ -610,7 +610,7 @@ public class EncryptionTestIT extends AbstractIT { EncryptionUtils.encryptFileDropFiles(decryptedFolderMetadata1, encryptedFolderMetadata1, publicKey); // serialize - String encryptedJson = serializeJSON(encryptedFolderMetadata1); + String encryptedJson = serializeJSON(encryptedFolderMetadata1, true); // de-serialize EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = deserializeJSON(encryptedJson, @@ -626,8 +626,8 @@ public class EncryptionTestIT extends AbstractIT { folderID); // compare - assertFalse(compareJsonStrings(serializeJSON(decryptedFolderMetadata1), - serializeJSON(decryptedFolderMetadata2))); + assertFalse(compareJsonStrings(serializeJSON(decryptedFolderMetadata1, true), + serializeJSON(decryptedFolderMetadata2, true))); assertEquals(decryptedFolderMetadata1.getFiles().size() + decryptedFolderMetadata1.getFiledrop().size(), decryptedFolderMetadata2.getFiles().size()); From 299224bee0dfe1cd48a7aadb69ba1ba8a9e51ab8 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 11 Mar 2024 11:32:30 +0100 Subject: [PATCH 074/102] Fix git conflicts Signed-off-by: alperozturk --- ...ud.client.SyncedFoldersActivityIT_open.png | Bin 11273 -> 7482 bytes .../client/SyncedFoldersActivityIT.java | 2 +- .../ui/activity/NotificationsActivityIT.kt | 14 ++++++------ .../FileDetailFragmentStaticServerIT.kt | 21 ++++++++---------- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png b/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png index 6cc302bdb7fc1a3985f0e613cc7c847dafb8c114..8c6ef2ead35605729391bd314006201670cab4d5 100644 GIT binary patch literal 7482 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 literal 11273 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 diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java index 6ebeaaa054..ad72200985 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java @@ -52,7 +52,7 @@ public class SyncedFoldersActivityIT extends AbstractIT { @ScreenshotTest public void open() { Activity sut = activityRule.launchActivity(null); - + longSleep(); screenshot(sut); } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt index 60e536526f..ca745eff11 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt @@ -53,8 +53,6 @@ class NotificationsActivityIT : AbstractIT() { @ScreenshotTest @SuppressWarnings("MagicNumber") fun showNotifications() { - val sut: NotificationsActivity = activityRule.launchActivity(null) - val date = GregorianCalendar() date.set(2005, 4, 17, 10, 35, 30) // random date @@ -133,11 +131,13 @@ class NotificationsActivityIT : AbstractIT() { ) ) - sut.runOnUiThread { sut.populateList(notifications) } - - shortSleep() - - screenshot(sut) + activityRule.launchActivity(null).apply { + runOnUiThread { + populateList(notifications) + } + longSleep() + screenshot(this) + } } @Test diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index ef8bfaaa98..3840a18fbf 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -94,12 +94,6 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { @ScreenshotTest @Suppress("MagicNumber") fun showDetailsActivities() { - val activity = testActivityRule.launchActivity(null) - val sut = FileDetailFragment.newInstance(oCFile, user, 0) - activity.addFragment(sut) - - waitForIdleSync() - val date = GregorianCalendar() date.set(2005, 4, 17, 10, 35, 30) // random date @@ -152,13 +146,16 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { ) ) - activity.runOnUiThread { - sut.fileDetailActivitiesFragment.populateList(activities as List?, true) + val sut = FileDetailFragment.newInstance(oCFile, user, 0) + testActivityRule.launchActivity(null).apply { + addFragment(sut) + waitForIdleSync() + runOnUiThread { + sut.fileDetailActivitiesFragment.populateList(activities as List?, true) + } + longSleep() + screenshot(this) } - - shortSleep() - shortSleep() - screenshot(activity) } // @Test From 9122fac78d4394cb63fb510cc8f9a3aef3f49f5c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 8 Mar 2024 10:53:17 +0100 Subject: [PATCH 075/102] Fix screenshot tests Signed-off-by: alperozturk --- ...ud.client.SyncedFoldersActivityIT_open.png | Bin 7482 -> 9020 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png b/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png index 8c6ef2ead35605729391bd314006201670cab4d5..4b8e70d7faf75b90af6566f36065866c1c7e6a9a 100644 GIT binary patch literal 9020 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 From d21a6297c9c4987b2c93e618ef9f2ce675148eb9 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 8 Mar 2024 11:25:50 +0100 Subject: [PATCH 076/102] Take ss of view rather than whole activity Signed-off-by: alperozturk --- .../owncloud/android/ui/activity/NotificationsActivityIT.kt | 4 ++-- .../com/owncloud/android/ui/activity/NotificationsActivity.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt index ca745eff11..c2acf20484 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt @@ -135,8 +135,8 @@ class NotificationsActivityIT : AbstractIT() { runOnUiThread { populateList(notifications) } - longSleep() - screenshot(this) + shortSleep() + screenshot(binding.list) } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt index 82ae5bea7a..f3e35a1ba7 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt @@ -55,7 +55,7 @@ import com.owncloud.android.utils.PushUtils */ class NotificationsActivity : DrawerActivity(), NotificationsContract.View { - private lateinit var binding: NotificationsLayoutBinding + lateinit var binding: NotificationsLayoutBinding private var adapter: NotificationListAdapter? = null private var snackbar: Snackbar? = null From e47437255f3e3514be24cdca9f762f2e34edfc02 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 11 Mar 2024 12:09:55 +0100 Subject: [PATCH 077/102] Fix build errors Signed-off-by: alperozturk --- build.gradle | 4 ++-- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b98ce56d24..fb54564eb1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - androidLibraryVersion ="5f92d92c490d2d9039bfd392cf16c61a37738646" + androidLibraryVersion ="0c886d61f6" androidPluginVersion = '8.3.0' androidxMediaVersion = '1.3.0' androidxTestVersion = "1.5.0" @@ -11,7 +11,7 @@ buildscript { espressoVersion = "3.5.1" fidoVersion = "4.1.0-patch2" jacoco_version = '0.8.11' - kotlin_version = '1.9.23' + kotlin_version = '1.9.22' markwonVersion = "4.6.2" mockitoVersion = "4.11.0" mockitoKotlinVersion = "4.1.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index a0c05c591e..6b78d34a02 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -4003,6 +4003,14 @@ + + + + + + + + From 108d5223e58f9f364b72b9ec1ac5d1907c2eca91 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 11 Mar 2024 12:22:20 +0100 Subject: [PATCH 078/102] Use view instead activity for ss tests Signed-off-by: alperozturk --- .../java/com/nextcloud/client/SyncedFoldersActivityIT.java | 7 ++++--- .../ui/fragment/FileDetailFragmentStaticServerIT.kt | 2 +- .../owncloud/android/ui/activity/SyncedFoldersActivity.kt | 2 +- .../android/ui/fragment/FileDetailActivitiesFragment.java | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java index ad72200985..fe8ceb3c12 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java @@ -27,6 +27,7 @@ import android.content.Intent; import com.nextcloud.client.preferences.SubFolderRule; import com.owncloud.android.AbstractIT; +import com.owncloud.android.databinding.SyncedFoldersLayoutBinding; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.ui.activity.SyncedFoldersActivity; @@ -51,9 +52,9 @@ public class SyncedFoldersActivityIT extends AbstractIT { @Test @ScreenshotTest public void open() { - Activity sut = activityRule.launchActivity(null); - longSleep(); - screenshot(sut); + SyncedFoldersLayoutBinding sut = activityRule.launchActivity(null).binding; + shortSleep(); + screenshot(sut.list); } @Test diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index 3840a18fbf..b03b94a858 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -173,7 +173,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { shortSleep() shortSleep() - screenshot(activity) + screenshot(sut.fileDetailActivitiesFragment.binding.list) } @Test diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index c4aa216d4d..26b5ed8cdb 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -165,7 +165,7 @@ class SyncedFoldersActivity : @Inject lateinit var syncedFolderProvider: SyncedFolderProvider - private lateinit var binding: SyncedFoldersLayoutBinding + lateinit var binding: SyncedFoldersLayoutBinding private lateinit var adapter: SyncedFolderAdapter private var syncedFolderPreferencesDialogFragment: SyncedFolderPreferencesDialogFragment? = null diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index f2732478ea..f33ba3422f 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -106,7 +106,7 @@ public class FileDetailActivitiesFragment extends Fragment implements private FileOperationsHelper operationsHelper; private VersionListInterface.CommentCallback callback; - private FileDetailsActivitiesFragmentBinding binding; + FileDetailsActivitiesFragmentBinding binding; @Inject UserAccountManager accountManager; @Inject ClientFactory clientFactory; From 547a90a594b8be637e0ef3707d04a57712e30f76 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 11 Mar 2024 15:31:38 +0100 Subject: [PATCH 079/102] Make AssistantViewModel testable Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 9 +- .../client/assistant/AsssistantScreen.kt | 22 ++++- .../repository/AssistantMockRepository.kt | 87 +++++++++++++++++++ .../ui/composeActivity/ComposeActivity.kt | 8 +- .../android/ui/activity/DrawerActivity.java | 2 +- 5 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 2a97e7589f..3383df42bb 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -23,8 +23,7 @@ package com.nextcloud.client.assistant import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.nextcloud.client.assistant.repository.AssistantRepository -import com.nextcloud.common.NextcloudClient +import com.nextcloud.client.assistant.repository.AssistantRepositoryType import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @@ -35,9 +34,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -class AssistantViewModel(client: NextcloudClient) : ViewModel() { - - private val repository: AssistantRepository = AssistantRepository(client) +class AssistantViewModel(private val repository: AssistantRepositoryType) : ViewModel() { private val _selectedTaskType = MutableStateFlow(null) val selectedTaskType: StateFlow = _selectedTaskType @@ -88,7 +85,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() { viewModelScope.launch(Dispatchers.IO) { val allTaskType = MainApp.getAppContext().getString(R.string.assistant_screen_all_task_type) val result = arrayListOf(TaskType(null, allTaskType, null)) - val taskTypes = repository.getTaskTypes().resultData.types ?: listOf() + val taskTypes = repository.getTaskTypes().resultData.types result.addAll(taskTypes) _taskTypes.update { diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index df9786edb6..b49f0db0c9 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -38,6 +38,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -49,13 +50,15 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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 import androidx.compose.ui.unit.dp import com.nextcloud.client.assistant.component.AddTaskAlertDialog import com.nextcloud.client.assistant.component.CenterText import com.nextcloud.client.assistant.component.TaskTypesRow import com.nextcloud.client.assistant.component.TaskView +import com.nextcloud.client.assistant.repository.AssistantMockRepository +import com.nextcloud.ui.composeActivity.ComposeActivity import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog import com.owncloud.android.R import com.owncloud.android.lib.resources.assistant.model.Task @@ -66,8 +69,7 @@ import kotlinx.coroutines.delay @Suppress("LongMethod") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AssistantScreen(viewModel: AssistantViewModel) { - val activity = LocalContext.current as Activity +fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { val loading by viewModel.loading.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() val filteredTaskList by viewModel.filteredTaskList.collectAsState() @@ -240,3 +242,17 @@ private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List ) } } + +@Composable +@Preview +private fun AssistantScreenPreview() { + val mockRepository = AssistantMockRepository() + MaterialTheme( + content = { + AssistantScreen( + viewModel = AssistantViewModel(repository = mockRepository), + activity = ComposeActivity() + ) + } + ) +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt new file mode 100644 index 0000000000..c2e059ce42 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -0,0 +1,87 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2024 Alper Ozturk + * Copyright (C) 2024 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.client.assistant.repository + +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.assistant.model.Task +import com.owncloud.android.lib.resources.assistant.model.TaskList +import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.lib.resources.assistant.model.TaskTypes + +class AssistantMockRepository : AssistantRepositoryType { + override fun getTaskTypes(): RemoteOperationResult { + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { + resultData = TaskTypes( + listOf( + TaskType("1", "FreePrompt", "You can create free prompt text"), + TaskType("2", "Generate Headline", "You can create generate headline text") + ) + ) + } + } + + override fun createTask(input: String, type: String): RemoteOperationResult { + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + } + + override fun getTaskList(appId: String): RemoteOperationResult { + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { + resultData = TaskList( + listOf( + Task( + 1, + "FreePrompt", + null, + "12", + "", + "Give me some text", + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. " + + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s," + + " when an unknown printer took a galley of type and scrambled it to make a type" + + " specimen book. It has survived not only five centuries, " + + "but also the leap into electronic typesetting, remaining essentially unchanged." + + " It was popularised in the 1960s with the release of Letraset sheets containing " + + "Lorem Ipsum passages, and more recently with desktop publishing software like Aldus" + + " PageMaker including versions of Lorem Ipsum", + "", + "" + ), + Task( + 2, + "GenerateHeadline", + null, + "12", + "", + "Give me some text 2", + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.", + "", + "" + ) + ) + ) + } + } + + override fun deleteTask(id: Long): RemoteOperationResult { + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + } +} diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 573f11ce62..8c812791ad 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -33,6 +33,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel +import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument @@ -103,11 +104,12 @@ class ComposeActivity : DrawerActivity() { } if (destination == ComposeDestination.AssistantScreen) { - nextcloudClient?.let { + nextcloudClient?.let { client -> AssistantScreen( viewModel = AssistantViewModel( - client = it - ) + repository = AssistantRepository(client) + ), + activity = this ) } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 3ba730fcea..b4eba3b10c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -468,7 +468,7 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterSearchMenuItems(menu, user, getResources()); DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); - DrawerMenuUtil.filterAssistantMenuItem(menu, capability); + // DrawerMenuUtil.filterAssistantMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); From 3a842f2fd4edbf94589decc268ea8a51574fc96f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 12 Mar 2024 09:15:34 +0100 Subject: [PATCH 080/102] Fix ss tests Signed-off-by: alperozturk --- .../79.json | 1203 ----------------- ...ificationsActivityIT_showNotifications.png | Bin 47559 -> 30305 bytes .../client/SyncedFoldersActivityIT.java | 2 +- .../com/owncloud/android/db/ProviderMeta.java | 2 +- .../android/ui/activity/DrawerActivity.java | 2 +- 5 files changed, 3 insertions(+), 1206 deletions(-) delete mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json deleted file mode 100644 index 63509f229f..0000000000 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json +++ /dev/null @@ -1,1203 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 79, - "identityHash": "ec997f271f9045e8483b260f036a168f", - "entities": [ - { - "tableName": "arbitrary_data", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "cloudId", - "columnName": "cloud_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "key", - "columnName": "key", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "assistant", - "columnName": "assistant", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "accountName", - "columnName": "account", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "versionMajor", - "columnName": "version_mayor", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMinor", - "columnName": "version_minor", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMicro", - "columnName": "version_micro", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionString", - "columnName": "version_string", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "versionEditor", - "columnName": "version_edition", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "extendedSupport", - "columnName": "extended_support", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "corePollinterval", - "columnName": "core_pollinterval", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingApiEnabled", - "columnName": "sharing_api_enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicEnabled", - "columnName": "sharing_public_enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicPasswordEnforced", - "columnName": "sharing_public_password_enforced", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicExpireDateEnabled", - "columnName": "sharing_public_expire_date_enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicExpireDateDays", - "columnName": "sharing_public_expire_date_days", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicExpireDateEnforced", - "columnName": "sharing_public_expire_date_enforced", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicSendMail", - "columnName": "sharing_public_send_mail", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingPublicUpload", - "columnName": "sharing_public_upload", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingUserSendMail", - "columnName": "sharing_user_send_mail", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingResharing", - "columnName": "sharing_resharing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingFederationOutgoing", - "columnName": "sharing_federation_outgoing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharingFederationIncoming", - "columnName": "sharing_federation_incoming", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "filesBigfilechunking", - "columnName": "files_bigfilechunking", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "filesUndelete", - "columnName": "files_undelete", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "filesVersioning", - "columnName": "files_versioning", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "externalLinks", - "columnName": "external_links", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "serverName", - "columnName": "server_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverColor", - "columnName": "server_color", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverTextColor", - "columnName": "server_text_color", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverElementColor", - "columnName": "server_element_color", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverSlogan", - "columnName": "server_slogan", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverLogo", - "columnName": "server_logo", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "serverBackgroundUrl", - "columnName": "background_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "endToEndEncryption", - "columnName": "end_to_end_encryption", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "endToEndEncryptionKeysExist", - "columnName": "end_to_end_encryption_keys_exist", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "endToEndEncryptionApiVersion", - "columnName": "end_to_end_encryption_api_version", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "activity", - "columnName": "activity", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "serverBackgroundDefault", - "columnName": "background_default", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "serverBackgroundPlain", - "columnName": "background_plain", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocument", - "columnName": "richdocument", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentMimetypeList", - "columnName": "richdocument_mimetype_list", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "richdocumentDirectEditing", - "columnName": "richdocument_direct_editing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentTemplates", - "columnName": "richdocument_direct_templates", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentOptionalMimetypeList", - "columnName": "richdocument_optional_mimetype_list", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharingPublicAskForOptionalPassword", - "columnName": "sharing_public_ask_for_optional_password", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "richdocumentProductName", - "columnName": "richdocument_product_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "directEditingEtag", - "columnName": "direct_editing_etag", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "userStatus", - "columnName": "user_status", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "userStatusSupportsEmoji", - "columnName": "user_status_supports_emoji", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "etag", - "columnName": "etag", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "filesLockingVersion", - "columnName": "files_locking_version", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "groupfolders", - "columnName": "groupfolders", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "dropAccount", - "columnName": "drop_account", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "securityGuard", - "columnName": "security_guard", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "external_links", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "iconUrl", - "columnName": "icon_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "language", - "columnName": "language", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "redirect", - "columnName": "redirect", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "filelist", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "name", - "columnName": "filename", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "encryptedName", - "columnName": "encrypted_filename", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "path", - "columnName": "path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "pathDecrypted", - "columnName": "path_decrypted", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "parent", - "columnName": "parent", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "creation", - "columnName": "created", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "modified", - "columnName": "modified", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "contentType", - "columnName": "content_type", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "contentLength", - "columnName": "content_length", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "storagePath", - "columnName": "media_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "accountOwner", - "columnName": "file_owner", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lastSyncDate", - "columnName": "last_sync_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lastSyncDateForData", - "columnName": "last_sync_date_for_data", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "modifiedAtLastSyncForData", - "columnName": "modified_at_last_sync_for_data", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "etag", - "columnName": "etag", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "etagOnServer", - "columnName": "etag_on_server", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharedViaLink", - "columnName": "share_by_link", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "permissions", - "columnName": "permissions", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "remoteId", - "columnName": "remote_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "localId", - "columnName": "local_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "-1" - }, - { - "fieldPath": "updateThumbnail", - "columnName": "update_thumbnail", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isDownloading", - "columnName": "is_downloading", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "favorite", - "columnName": "favorite", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hidden", - "columnName": "hidden", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isEncrypted", - "columnName": "is_encrypted", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "etagInConflict", - "columnName": "etag_in_conflict", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharedWithSharee", - "columnName": "shared_via_users", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "mountType", - "columnName": "mount_type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasPreview", - "columnName": "has_preview", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadCommentsCount", - "columnName": "unread_comments_count", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "ownerId", - "columnName": "owner_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "ownerDisplayName", - "columnName": "owner_display_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "note", - "columnName": "note", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "sharees", - "columnName": "sharees", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "richWorkspace", - "columnName": "rich_workspace", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "metadataSize", - "columnName": "metadata_size", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "metadataLivePhoto", - "columnName": "metadata_live_photo", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "locked", - "columnName": "locked", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockType", - "columnName": "lock_type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockOwner", - "columnName": "lock_owner", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lockOwnerDisplayName", - "columnName": "lock_owner_display_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lockOwnerEditor", - "columnName": "lock_owner_editor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lockTimestamp", - "columnName": "lock_timestamp", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockTimeout", - "columnName": "lock_timeout", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lockToken", - "columnName": "lock_token", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "metadataGPS", - "columnName": "metadata_gps", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "e2eCounter", - "columnName": "e2e_counter", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "filesystem", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localPath", - "columnName": "local_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "fileIsFolder", - "columnName": "is_folder", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "fileFoundRecently", - "columnName": "found_at", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "fileSentForUpload", - "columnName": "upload_triggered", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "syncedFolderId", - "columnName": "syncedfolder_id", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "crc32", - "columnName": "crc32", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "fileModified", - "columnName": "modified_at", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ocshares", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` INTEGER, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "fileSource", - "columnName": "file_source", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "itemSource", - "columnName": "item_source", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "shareType", - "columnName": "share_type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "shareWith", - "columnName": "shate_with", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "path", - "columnName": "path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "permissions", - "columnName": "permissions", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "sharedDate", - "columnName": "shared_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "expirationDate", - "columnName": "expiration_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "token", - "columnName": "token", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "shareWithDisplayName", - "columnName": "shared_with_display_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isDirectory", - "columnName": "is_directory", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "userId", - "columnName": "user_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "idRemoteShared", - "columnName": "id_remote_shared", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "accountOwner", - "columnName": "owner_share", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isPasswordProtected", - "columnName": "is_password_protected", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "note", - "columnName": "note", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "hideDownload", - "columnName": "hide_download", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "shareLink", - "columnName": "share_link", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "shareLabel", - "columnName": "share_label", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "synced_folders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localPath", - "columnName": "local_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "remotePath", - "columnName": "remote_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "wifiOnly", - "columnName": "wifi_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "chargingOnly", - "columnName": "charging_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "existing", - "columnName": "existing", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "enabled", - "columnName": "enabled", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "enabledTimestampMs", - "columnName": "enabled_timestamp_ms", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "subfolderByDate", - "columnName": "subfolder_by_date", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "account", - "columnName": "account", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "uploadAction", - "columnName": "upload_option", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "nameCollisionPolicy", - "columnName": "name_collision_policy", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hidden", - "columnName": "hidden", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "subFolderRule", - "columnName": "sub_folder_rule", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "excludeHidden", - "columnName": "exclude_hidden", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "list_of_uploads", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localPath", - "columnName": "local_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "remotePath", - "columnName": "remote_path", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "accountName", - "columnName": "account_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "fileSize", - "columnName": "file_size", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "localBehaviour", - "columnName": "local_behaviour", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "uploadTime", - "columnName": "upload_time", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "nameCollisionPolicy", - "columnName": "name_collision_policy", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isCreateRemoteFolder", - "columnName": "is_create_remote_folder", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "uploadEndTimestamp", - "columnName": "upload_end_timestamp", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "lastResult", - "columnName": "last_result", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isWhileChargingOnly", - "columnName": "is_while_charging_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isWifiOnly", - "columnName": "is_wifi_only", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "createdBy", - "columnName": "created_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "folderUnlockToken", - "columnName": "folder_unlock_token", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "virtual", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "_id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "ocFileId", - "columnName": "ocfile_id", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "_id" - ] - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec997f271f9045e8483b260f036a168f')" - ] - } -} \ No newline at end of file diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications.png index 95a1ab660e1e01440d12c870469de5889fcdba1e..ff2419da15a75e5bbb9a549c5335b6fc807e8bf8 100644 GIT binary patch literal 30305 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 z
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 literal 47559 zcmce;by!v5)-MXujUq@Rpdc+ET~g95-BJ>ZMfajp>6VrdX{0+u0qL$qcY}0uU-sVL zIs4r2JI_7OJ@=k}c#yf~TyxHMykm@C48xQarEoAwF%b|DaAc$f zUx*at%@7cL5o9Dp-@DE1rlZt7U3=Dv$1+DTLgDXJ8=vJL8YuhSS32Z*daMItUSsa# z=>7fui_x#o=PonP7nvJ9dCZ~hLFea!*R#z6r>EEH$LU`%j&J^aekO)a9T=^w;6{W< z9f&U`N)LXZqoji$)K8rrjwugefCHHP4@X~ZJ{k{Vz`Y$I<`dw12F!|^PxvhN#0-q%7|ZgD>#!<(LVRhtS>)3i_`k?VcJ{7WA{6=;SaG^IM%Y^=cMn+ zD_0Z(H_U%xUyi<^;B}Mcu;_WZ95Fn30@mF9ceCfY&4OW*+vC*@pAG$e@nD=d)jV05 z(low+Tb7&X`~`=bV{kH#gLQskA3cM zHSg(UlQ$z+MO?Q)oE~8oe^DY8;h!3~SJd5{t}sssC*kZVo~|@InfftOB}0%I8L~KF zSpN2GcbeAqU_Rk#@S0wQsSJZg)%ViD)No=h+5P!?TB#_C$$NVFv{&}azY;$YGRl6F zCs`XSP~(8!d98Swuhbq@m~N@^Ic-L#@w$thQ}KAGNkmcVV?KSAwSV5PThqPXpE_O{ zeH?&CKH_wBLZW>!NyF!`S}y2zs63LZNK441VYlNq)q3x1-~7^H_rgBxDMM_oLfkvy z0#(ToVjrZGJKy-nQ}xzK#rlmJlglf=!e}ek``AZn4MC(Z@FZD4Hxz zP%qN>6ZHAn3#+kLu`IeSmt)=H%fG_-{PD%+N#@;?JR7+oW^-50M>~t{sugY+sn&2Z zG(5^!kA;b(-lQ_=^}a7JyqhjKT<-))prQIDY_0M8a}6@TLLWODh90ajC9NH_-cQ`~ z*v^}VT1XInJUkb<4_z4gdIY&eWelZP%!oH*O6(mknsmCJIo*kHl#I@mi6il6Wo(%e zQAnx6^>1;R(fbG_qRGr>2IsNGG^$vEt9+!$1v>U2_Tp$mznbB(=YTcc~R9A;9I zWlTDSH(sQq9hzOW(`P&3?YSlA}}jQk?jW zetjU5&0-)eL4!rNFgY14xH!M-?h7W3()h0(!7>rs<1bxyr>J8?+` z0Y>j^HC}T|CF1Q>pkA4{J5{e1Nhx4X;H0&0ekMSr-Wd zgL>8ZA8Tfwu6uD5L4*{M0CX{R$fJv@le z6dv`x?tFV#eB_%#Z?FQoJH6XR!16A}GuE`y1Ka5ab&u11TJhVPn?0m+uaq|hYg0c~ z8q6u!3oaN=zoo5FsjS)d7LEc%xUGHq56+JUq=_NG%Q2ajkrMzj%KB0 zj#>jZRICn7Kyz^DVJJ)xC^2cObqhIC3i*WBs&9M0J346ES7#qA9T`m%^m&S=7?EZTgj!C_)PSF}TQ+Ik z{i1hte!at5j|bLdw@S_%^nrQCbYYX=a7ux9-dl5BXJnMvNN0Ns8_|lnu%j^s)}Xnh z6SS&uFT}W5k!Q@&RhA>NK}Tygt>UZpaPrW{wER+!#k$GBtGFZewR7GXQD@Y+)B4^1 z#b&xYUA*meg^--} z{5*t8Ho)Wby##nzmQ1gsuqKO8{(X4H{YIv0lNNiy)pS0t|L5y!IO*lvhBy*SVVB)x zVJYaQn*szt*`uXts87{=}(D$A@hX$ z=%hQ_|I|3Gqf4ZZRuV(B|v%F`Ji%r!eEVx>Yp{=Jde zd$tE3C&b?Ixq!B!e91yW{}`JqV|J1}JhHTrdCDWmL1f+Qrck_=Z4^AHyz_#v3CXgquMG zniMsoc+c0-AUfrGkrsTeOT6{`+Smk-n`GB|NJCM2@l}>H9*rFOKApvNy*x4lVc)bj zZ&mExC_9KO5L1B`W#{Z2&g6@~!d(h&xss(Cy;}XFM#$qyU5ln6kWmmB>MD3nu`{@y zqqSFZ>HBSAlH)4d!}+%E5nh|Ub41@4&fYq9lhyTpU5AxUXIL!^Y)SWCQtH+803wg) zeji2Oz@|m&=GwG4e5)Z`LipQ@@~vfNPw@}SNrfLr{?8lm|3gjhDun3db^JdXf1>|W zEq^JqVT;bsz{em{zmUrS00Se!AD5kq*DloC=dW0EfL&jYk=m!r6FYdf^y&rD+EA+?F`NBP~*3R`AMLfv9a_#QhD-U9Ijw}$uX*>vr;w^fymKn`HDrI&RZh} zpl{o@Uubkakg2tWy}t%Eg|Gp6ZoYpX8<~>Cdc1JUx_V+TQ-saXOGT|!3N({75P0rS z#d&Y@!3?JRyX&%FQB;P)*SiF_d&|GEIAB-Uw=FJv4fupkdafh6e<;r{H*+dDTy|e+ zP*OwDY2Ss%++O3!z-uV-3|lj7B`H2c!VDAV>B0r6P-CUmN>R zs**5ime#;p9XBG24O>+h)eGA+u5e#N=e@LRZJXm6)ko+yEk|-=j5}^tNAh}An?0)W z6_h|%9RA15`PO@_hfsm+l}=UY3&k|8)_leEPlQt4WD-fN6uAnind;x?!Wx>B!Jk#5 zFmU}9hBo?jH{)I!<|{_vmxVrKGwAO7Qh9RidC*LcMb~&qleVnXY+FeB|5xQA|NS=nPygRl%)3sRhk!cV`5d^M|KA zwt5U2#i1``Vt@S^ca-IZ&Syuwl>GEmHCuu`?8(bd0q4dY!3rKFZhx0$V`ze2f@ixc zHLSA}<#XAsWA7Erx>o*-1V>{L>Tko=$0=`EblaP`4`-;5a8dfW{2!i``vnQze z9(~e|Ppairq|BQ|cNh8Jwak?u9>8l2=cXRu1pqYy@q-m+y^#GqQ1`fJb}~E9mOHs< zEQjcjPcnT4ry{-AAF>EA+(miNB!?QD<+i3PYnk;Mo)uAb_WE#lBs9R zIwwhQY%T&i_&80a*;YCuV$K(ozV7r)MNT0Qp!a@P=};%v(_b*1t{~k0G+*zSv{yUd z?AfhpIF4~>-=^E5yuUx7-{8bRBH|tX>D(7F;MXAZJ0!J|S4^>(+@&I~+TxUwS(>XnWR2s~C3!?thmhFRyky9JWSYrUxpda!>E@ z6=NPlTXB+F?{tbOL%yrzki=zky+=422<1m-V8_!C?Me}|+a-%{lSKk)L7kd{-zgci zu5jWuO_{j{!)v&R?JRF++ng2=n*k%(U9rX;-btifE#*~s#bmB;5sdPF&NR6mWK`Kq zzmvpUR&Ku9@SsF@OXc1(9WPuvT5Lz!n)>mXQn&u7OSS5wvH0`g!s;71v{kOr1(NH2 zoXGgrZqoZm#;p1+kImF8h18np(;ZC#J*tebyW8s?fHpwLmguk^s-En@rQU@T=Mlt% z@R|FwDLMv>t`h2%(lim}{onV_Q~csk_salXPwr z{5QEY$nE^@N4fHucOfd=3VCYznKvxEF!9pvvprr2Q!uQ&+%I-Ed~p$PXNl*NaFTA&7_fZ2*an&W`C^xbsr|NXY%0@ zzw7?@E{pyY^`1nQG9sFqj%?2M#qc*$VNbNt95@xUl!A*$;-1v$8&k}z^xET-Krbsc z8V^XnA9h7iDFrTmmckoX)S7;>)|W*ZxgSIAYw@&iObm`Km8-{a{GDiubXrtC#~OF zv?%3$i!Q%)UbHQu%OUTCo+UrZe5FEzHd)`C)d=q*uuDkoq9*Rmb%ER)`0q?YiL+h) zw9_IKYTUjTF&g_mT(hUu1-s-a*yE`a*+ZFYa=AEse522%S7*n))Mm6_C|FEx@zM}b zJ0}ZoJ6f%>wYs==u-me$ltP*l1byxvooX0W7!88B@Uo(R#RN6mB8M+B&YANg(XMM8 zC$bp|pT;xmRB6rB-fN50Q4kp9GpaKRp!5GCmk7ld&AC-*O7FltOv9!U7Slheb6ouu z7DxW~kAUB8^3&KscQ*?ZSi8Tbs3&o8n&0)5_K?Evq^j{WniG|#MCf6m>}$6fyozt% z^%~D)$3XB}mi)vFw#)vzXt1UUH^e=%8%=G4P>g8(c{Y1AU;a#DRMi!A|F8z)GjgDX zHkia#R*IJT_esm)w}iZergs0}ohHTflI_3R6{ZE9T#HC}k+-G;Hkn$OTci1z>?*`Z z3%@#!`4;;t7}rCY-1#oUdt)-1#(B}&5S4}xo}N5M*tgQ+LuiL$Jo)8Xu2oKnD}zAIZ>5k-$5Xy)(BUYBpYSE^I3GZ@fL9Mew+dY_@{Y0$?MnMGcI z^E}(Ng}2zR(CJ%rEoe@5lP{auzBEK9Iz~-=CSLoeqSZFXhpF)pAPLS}00`#SM8V=!M3S8A|+1 zWDHTG(Bv+TFzpGtGa7&T?)D=d+L?p7MkqYkvq7`uNi{zf5i_H=cR;vfzCCYJLWM+D z3Xjnonq+)qLeop740-Px`vz~RS+S~*!;sqCwZ1Q-TV4#+w@RsJA)S0q2lHx}PrElp z`F7h)Lp~_JS}&XE6Pg<#Vnn7mPq@aUd>xSA8H8+p!n&W|pDs|_!96QwTVd4x$a3mX z{S6y322mb^ascY-Xtk7Q@$O1Qsea>kvp3#1$Z!w({SDj6((3--pEr*Tnmu|!5?AkA zGJ6~7Sfax0UOuT$Vv(!+gdrn4Zg5GeP+xFsn&lAY= zSJ|^At2owkpxP%HdT*79K|I2|Z~mfn^spYVAw0IUUT8`8I-A~LoUeOXZaq0bIk<$2 zEHIG9H)p1YNPUo!j08O|XdU+7-?A^$phKjl-mWv8q$s~&)#Ej}UhqEb*b}z1SV1Jw z!gJzE)}2N(?TH^=;y*LGeS$=v-EE%%%8yK6a%5D_@7qrCVC+QD8*D$1w4QDZx8G68 z6mEXQL8dapE9|h^O<5M@H*?7&QL;iXwNku_CAQ&}k#wFfa2M>Em&AH22W1d1Cq%9> zXoixXwXdSavByV{*znjHiG;uwTQYf|HQ!LoOcJAxPn#0*#+|O8tQiz*sU+w|3qDs^CcEOisW^c}t4V zUi<7cx?%)LRPjeLkO8s@*PGn_3s8YQqB#P}xRE6;PA`2jzSz*^nn=gJyXu}`Kd zus^+WH9SJ~Uv5VBszs#Ux11{X6HcXway&J4PR+(;?_zJ~s6jo&NMcDHB&7qqwxVzC zH|^>g1rd1$e1elweF0)l z0+HMKZwi&lYH!`5Lu${hb*DoBnsR4C7VYa|5ASbrxMPI zYcm39msz8zL~tw4-?y^v&o|S{B{C<0W=uAb`O72cQUHZ|J0nt0uO|&BF;AVnb|xS& zh;g0Wl7Tfppc!8RA~LQ}y=e1#b|F|0tUYg1Fr=(Z3xm^BSs9Nt3WCZ<1 zcYouk{s8yDnOGrG0a z^1j%w{$_T(4ykba>u|XmC+lZr0#(f@&h4*HH_4LJy}i5kfZn{-F42L6k@HpDoWY-m zJ*I8gJ3rfN>`7qq>Am>dA?`%(2Eb|JTo+Xm9=9l7p$qBAWQE9N9KBLgjX3laKV@c1 zfNfL1@hrx1V?d@O`1JI47hba$z0}bqo&1_cz3@F|%o|YbG;qmzUsYNtxtsKuU7yWu zS8vWW7UjyGdVeq5kdC1tA&5VCsoziqRe;0dj#n8W&<_H&!YVCnuKTkIKTPOlJ`;&d zdHY-(ew}Ugxi6tWo9`4}E=gMm_Hd&|p_sV*VbbMHr}<-9>PmNimTzt7YfgpB-aDE2 zS6_{*MPg_qG4Cv#1rtHHN!m&u4H|sg(0f*s&O}Aq**eR#jldTQDOyMC{SbN7pHrpw z7l$ghi~3v1GN4}>qz1U$hTA;we$!c_%zzENRlf(qx^{2DM|p1kBY=GyDhGZTzlcin zjyKpyHJq5|aBE{Z5BZHcN!dRjGibeT&0E2x6twZe-8x4?!`)(jRyoe1TSG=cTW!CK zvLeWqYq#{M0dJ%Zx`5d4TB^qvULo_fRRl;bQGcSTOY|CD^c6D%^WwgV1s(MQhL4Lv zYx$LGt}jq-=BRN1G9MQmWA@DToNI{C@&4A;rhl0{S*>IZxPoww&|LQ)UHUUWoPNNr zS%pIQ)VfYz>((m8CGfX-^G+B6+ntW<0>P*O?@f*Iu@l$TDC9Ypu?t%eKFx_1?f$0N0I7 z+`mG|(>i|u(yo3VkTe9J+T5vbs@8Sg_{k038)DvQt5I{c#O~vj7ClF-8=#T$R;sa` zqb6kZ%B6|GBo)4yyf?QpF}=HypQsQUYP;p2Z z0Yc|SjkIxC`Y9~cuhTEDrR3L!D~zw zX}ta}2f5fe0WgHOb$}l@UAaNlg~QEnAR(Z_PY1SLl1$t?nV*3ePOop)l0-*}wP^{R zl-5jq^f^n2(f^X^#hYJx$_k)r?Jm^WBf$}Pq4PB9^(F{aI*Fk9{o61fwm>~#+`<2e z5EWMs>rz5Hikwdo6?-#JY^{KNBu{RIO(|n0Eq_O=f3Q73%5?cx!8_gb!VeTi=u{{R|qHIRj}jK834DK;iJLe($ri zkJ0vsx~Kii%~#J(>_u7^B7PW`Q=2}%-CHRLXa9&tyhv}>n;5^|?-Ok~YHrm?%x#ke zw{O#jH5{d&e8VoTQu7o%Z3X$!gMtiJpP0{a>cYBvP(m?-8vn`zRML0C!Q$JiRyGv0ILJHpnJw$Zp0 zI&z@pa1wFtYTtfcpjj5FX!AO`Z7uT4`GU`V4*YdY`zk^N{uU{hRT?gO9coXOT#`X2 z0+*~s8QsURJ$UU%Fd!}PI(K3DxS^XjAAf}!^TMve*#lr)!wfoAS(wuU>7G^CAXk=H zGAcjMoIGOxp;W|l5S~!g*r7%-mGrU)2lCzYPj=_xjF`n;iLR@1J2Ln*dnw;YH-l%ey|!-E;kLa@3X+_QUA|SN?u}6v-44!8ZT40-B^0o2I85Rq z!4;eH*k7+aN12maBqiaott-~4o=CPY9r;2ZSZpz<$e+^@ZoSf30xG7Y^IuT=x;8I{ zZTur;IRbc9bEWA3i86jrZm05tcojhqk#_@Zt)_1b0E^EUpYIDDE)*a786Z)hy*2tV zn8t@SCX~!+;Q}Oq%o3AFeHlysV!l+U;?OSbJgpyM+&uFjuZ*8!90Y5lCNL&w@+^bu z1Fnzk6Eu+P zr;vEE@~b=@tgloPiM3v58E1!b_^OBawUgT!*>^~i2)NkjuOoKT&VD226-)?l%n+>R zgHHZP$pj&}?GARW`KnyFV9tacndTy!EozKj$q`PhaB7zu5|Wj z-OA=5XmWPuB|@vC{M9k~Aw_LitE>CkBlP+g290c#F3#F=s8=SW-W_Xehpc_su-( zYnl#dqD9{VC}YNQQMgJ~STx23ij*~8At8mPlJm~low9Bof%yOc3%GZeub(edacTq8 zLrn9@Yc=b4$$Br2_oNFXHDE#)T8gaKet#lVVN0^a0E9J{^6kj3_?Z#D^W-xm-J}-P z);rBkWb?*``8{}ALPOx9(I50%f;FrdmyrA`r;I+78tkTOHN#GN=;`*q;uX`X@5W8e z#HdRYvq5r^hV;$HWLzCB9;A8)SeErxUDV&ao!W9-n$~+^SriT>N^4fZVt2&Z5&vpM z-)W9V#zw<#k0u8FkU~HAoD3g((8(xGaJr+*_oxO zdfwhZVmt18jq$Logp(frTXb>YAQHQVm3_s4=-O2C3H9F7`aZGP*;o#^fe zi3;z0JovG=i_3walnKFJDUL4TP9s}FrMj=U-h#tAes zj+MBSF#@P+JOu_cpFP!k2So63ZGO_%6E_^D()p#@b%scA6{G&=&oPlgNbL7Yy!>hQ z?3{n5=v10F-$kKo6fdL@B_!~_h9k~SqrV6s^vg>DS#-L_q9(Sj_^~{UdfRjbs_Dyb z2a~MbnbZsi)=DY(r4p}a6iZHwF+I?Iznqq}-qFxXMJ9RQdaJvlJ%T)6KPz?CY6eP1 zb%~@dBIk!bqwBL<^_l9On*~yRW6PmMYoBZH6BPyuYysDOhTH4&_eo|Eq~oTY;e4Go z-U&J%?W)rST-$3hLKn%Ao7;t~GLVSsx9-^65k6}Rq540jy_uzahX49nA{r<+-tP=N zr$~AH)%>?bpAt9O6@|j4Y40<7=wFRQBGy=rc5Z7x=B_LVW97e$zy*0Sw%hCvKfe;W z9aHr0xbtVoY0#O!^J&lh;AZ`Vu0AB#r(k=_^(DY$M{$+^-|O17ib z>Ko@Za>mB6A#t|h8zeW*4e%nqvgv=_|*Yo^n;6q>*UZ1IP zL=!)2!Eo}1z;4KZ1)knplTK{4m>@3W0h{!8S`>RY-iT4D)RGL8%i6n?(^_*!+-@(P zNmGY~sSmSXm*Vwxt9hZSJZEx}5lgZ;R`_f8XC)?w{|H#F{ z*W4BG(anWv+xTGnJze9@DrW<&?y>@HSI<$E$69Yk+r3!iW@k7dGrZO1dcPUp5~Ja4 z=fI8Eo>L5{ESL7QtN%yopn7e;ES--brGhpXSM<=k&0j@xmOjMWh(5f=;!kWn>kee; z=ah@p?j~h_TIdDSeejzr>%<8{>85^++IDR4ur)-^H5+mV3pT!_}o!3ql-;F!}u=@ zng46f$`I1S5B~1QM6x~c2RpP3hz8-lle){(KLPoGjw1Bbw|0BcIgZLl&j5u2x>2BB zl+ohwM!UeURaL&isgaY`WzIY0%0`?5KI91D|zwX?M@$8SWi+qk8TX4k518J z=)QPxvo^2+w)s84AJl)Bf2)g9vVSP9g244;6Bt=1%NF7vP|(q;%-Y$Wzykn+v2hwK zXcbL7yBk2E?z*#Ux9CfrANdP_$t3IvoqSf;zu>^jFPs*pMK8dUh}Ql*-Qh7E`)&`q z8PTnwuhmU%f5jS{wqAAwM{pEx;z6z-xJQ0xt9&>amld`3gpD1;=DwZl4M-5-))Dd9 zl2|g=XC-gCOSLOq06crTHCaZl`|=<6PPq8U=VbH8`|pP_fjDFuK1J^fns%ptz^7bP zOUx0hr)M*0Bsr$5t;>`q?Okue2=d2Rs6b<+-RP2YM%}_Gyhgu%143f&#Tw z`aT2bv;h9(EC5O+--m!+@#*d)4?XsHA&iEQF~0g%aVPCz5$Di=y8fa-wV4f6iv(i! z?4oGJjGGYC?r6o4d?h9z3gb@fJQM)ce8oKLb_bxqoPH z>(S-2Er2%7qxY2Zid(DJEJmb z9PU6NTl>gZrJIbwVU_#Cqiq}-KnrxHbY82uZ!*T_NQpqju#x4kn`F}8y;-i>YnXt zv#dR^o+!5^E5P2l9qA3Ifjj8I5vp4WWAVJpp3dgI+c-n=8_R~-78rcb9UwZ|hlm5* zWrN^ko}6Z`cICU8+G5Gbn~ZNOOC3_3HN8g{e@zHo9CZQDG^QR^`?DU7Dtews^{^98 zYGWV#)DP?(5Rd5qnvXkL>y^H}GTd@4x1D>i9|jJVv8VGnMtYrTn}saw4kmy&zD>S4 z@X24>9`{j?Un-0O3}OJWc=y30k&Ip}e>Jp_IUgZy{}ueQFIEqu=)WPAJ{ zFEtSmh}L%SjZ7cL2b+j`(S&T+6G`>gfP-K>-5x*N@^ai@jh9X60UJYwyx2~2L#&El zZ+F4_I-=vCu2#r$)R3;y6qM+jbo6J+>62EZh2k`Z_fb$SRc#mE7u--!}oPdtPc zPIo3>dYW4EtZ3$p7-|t-{{49)`rLO+%I@*g zQ=0@}_9xwd{x?h6&*4weJpU`cFjOnkV)deL)V3uH^^Q_4SB0hC4|a=yEMT`DtAUK< z{-vgS4hm)T)~LnidA0RqvzouHAr%nZW7UfiwPrn zZ&a>MceXUfsMn`9){u?DPYxECPSE+0;pxfn02mP4tjO9H)PY%zwj=oR4|vKvk5j7y z8@|~l_tB5LwBvG*)A~|*41rF}mM5D4Ja}S{UfoRu~0~C>qEG#2QEXIk;9^Q1G!NkWse0>kNPc%-Ql0 zvu=&v{(?^yP7Q!hS8!mmUOn03ylR&JqS{&WQ8*!q6L^pk21UvWSjUh*goDWzX!uED6^(?CnJOx~6n-U~S6q&^IzbVgF-`ET|J{9TFa zKk+PlUx58X00N)Ll`*;8%CEVLtoONZ*&{9u0rLqA;LM`%3d}&{ry9q0App-JUI}N2EIIstSfXX;U*TJ05m+%&3>La>w40Aw8pf0 zxJ27Pv2#4-I@h4ixcQmrHt(_)?UIzoHrzhq9iH zfcbaudmt>jOnD#73t3KAM0`U0mF;mlXtvg)&VN{IIjT76DfF1fmfYmfmihBDw(*r< zoNIlnu?sQPZ(a)yq{hwX2Me0(1L^sR6ve9m97r#;z~1Xo&!~NqR}-yufLPcEnqd{; z#{~krZy_%si;l!^k_o1se9KXy3h!GhLIynoOMZvdP@Q4x$rf@9d0nnOBaM4)+6@iH6CwGp%Ns8L@nU=sHH87mkak3CO7 z#o7J?(wbAOUbU5RWL03(yZ>&k3L$!(yu5;|mAifhR;+Lh`-q1bz#zgAw z7wQi(m1PUUF9FFNX0DDp44qm{LpcC7f>FbLRm) z#qFt+Bg>nIr(f8GE>-%wM}ly^fD-}Z!!4F*TSG&FH5`k7_RqSv-DnEXM^{^0WgxA_ z0uwn!?=Q8$E@cBar4k|sOmn7xVzq@R=X-j|&{b0f-9PDk(Ne1Il7;z#F_XdsVfUl= zw<1qq!r3PLQZUz61Q!IJ&pT!w&qY*J-P)EXbcVBge(jUifZ`rF zgROKbG(?8Cmh~)~jk#wC*5j~${*s4eyG7q`U!|hvIjqJ$!dTnl83;i3y2gmF zr;9}Q@?4zZnQnoFc%7(d!`hUMex>0bdHkMj8k~i*K%So9BTooCs>XNXHtl}>h0D4| z(DO`wE_bTNZb>kzi}u3mXP`{O1Wx(Dr>x@Wwz+E18Lm0_6boq+9l{^*6Y z3VhHFB~DwaM@u1WnX$pTb&Q&&#kTgoFVT)gc{O@^KdoW(A#-{qH#O+ZEAphL{o@ND;&{5=SbR(!t*d;8kp!AX^sReuMD z>c$Zgo-w<)`9-&Ys?)pJE^f2&Ml{`s#sQEV5uM=n>PrTi{4kJzr$zH9^(ic!ImzjM z$9J05+-GuY!_FW*ryHrZex^@GA>!>ne~)dn1safBgsy`dOBR93oWbt#`l>&5`!?x8z(dw{`o4^Dlau4-w|KKj`Ey; z)P~K08nzeBFA_L27dLiTCE(>>*i=4nz*3TI*IeoXMlgs$yEJ4`xnf_MQEaOQdPZJ4 zt%m;8i&4~TbvI=^fNXODE5oPVoqlU4Y(Dj@zOkzc6iGXAgb$_vPRHi|NsDm5`8+mm%QZn%q;F4Vv_X zFLPr9vcQaqQkj0O?Vj?>O^uoFu#8MW_alv3OUx@W?D>Ac13DC_=K1TjdW(bV%hsP# zov=UWlZYqMcR9cl4%|jPen@C$XS)oDGXoq)^10G6fI0f7r3iB00A|R;DFkG6sxI2k zt^s6P1NMrt<{O!z+Tq$}1+}3CJ3xLuf3TA*fW`mBUSz|7go+&#Mj#~%rb|!HA3R_H zcVWv3hp+Y|h|l|o1hpOm|WyL^DP{`B-!wC24C?5fziTu3!Hv_Yiwb7Z234<(p_ zsOkSORRc*frpS*24MWer@xlrV!8oZnI~!E}qub*<5f}q#PdLr7b@>G$wW!VlLDTD7)0U08Y$g@B87>(%+fj&1M#;Zn}k!^le_+Q>m<+lgE}-kIoSzEbAE zNOGm)1|=UL^X$`C-^~bD33_W;Ziwn{)YZVqP7^IQxc4Awh`@F8y2rG(1~HdGB|A{( zAP8%tHlqD(<9v*5Zf$t7Wo_p|T+{~%d_%?iQaw>HXut%p3nyxpO~uDyRT6%uz+a!A zX_67tRR5Dq-?!TZ1kk@bj9hLWxb+8zk(wJn9xB}~{K0V3#tsZO)q-g#w~Otxou(#* zfQ(ZoJ6iqY14evwK{1ipo=D@e&M;_{2&3WnIHdeQAFi+b4EpS$Akyj`%Q$}RaC@cD zY!2#~vygyTAo@G;d(8)$r9gcLOS{f4Oz|lQIvYLf77*YgqC;~K&@s^Y++8NQfTI1g zFllwC%v!cTW!#wz)iU>S94j_HQ2sdSbeqXhdE?-<(4Cg#^2LTuVq@$kU}YKS7&x!* zXqoRJ>vBD~`;6=KN@UVR_cM$JZYRdn`=wR|qkqTg6gNTbXp z@gu$xsbT^yARKFtaz&2xzfOwS6-cNTGSd|=^W2ZAOlPXTG~f!`OuemLyYHf^G`qQA zV8s5Y+ilqDEmSopKrrRaXEo-gfY(Eu4V*&6Q6_!6(>`K9t6IM{PZp?&TTPZOIp37e z33RiEGK;p0zj0=r;q@u`Ac(iM?2gx2O#HcrMTeSC<&@k{s-a5_ zEj7*{*+|E}2s>e$SRYz?7eShzUMn6lF@p}|4l``JoUz!d%s+q^(Gpk%dJ13;WcgP* zidg{+HW*yNqVkIF$9G~kZ|I6ZH7oA&T%)eG7`&AGuGs ztlsyUVaK%DP$KbdAN>rH>*PWsa~H}@G*?dYD-wJ@NG!2EdEZ`c=Tb;47X5lLoKi>> zu=hDcpYjD$7gL%&oF_zkb+5p8eGd>Q21162YA?owqTi#-NAKzJSIzrL$&jq2Oh)*+ z@XOgKfR;`ln59v&9XusJd4@|}pi%OvhD>t}??>Brbp?#7(s_R-^L{F5{bh=}h60lg(k3-YT_x%6DJ-#<|xmZw~X)(VWxK zBr}8}IFp_FQn(hVAs}S{)sXG>C;+cQw@|JK#sp^P5J`PrZ9>V*X5ZT3wV$FSHIXc; z7O1*AUxH7o>c6H-j`%=VYrmq=(Np$)zXFWp6H_pyaEmEz{9VO$;(9It_;L^Y*8);+ zsrV-+N(8EYWCwsAx1;?1hi!x!*8{)%;zcwMh?DJxU%R{-_-_SFCrg_VS@|B83K0m7 znP6gRsuYh2alc{ehe_{DHQ8w`_@zJqXZZkE>*@L>;7;So`JE&Lyo0f~m^Y9TAN4>7 z(onW5K(c5AEFRlbPumxO-nM-)nGl^HVU1O>D#-l>cfu{|nq*P;-B6JmYg#V~jD?=0 zw1Cf9rv6@xY`wrSyC31&X;It_Xd^ERc*5n9Sa%lO{u(qC8w|MQO!s@`OfzG7Dz}^U#78rznSZ1Y$}`gWX~*TxAI0WR zDeUEzQ@N(fGYnb==e;JtvK{rZAwf8Y!DN?}_}klI(*HCDHfWR)v>}=&yXtsnPh;bSI1H^hA~kzr}AI}%5{9O0gc+OrQze$ zUH>ru?BOfjxB~3h_ZaS`aOiqNekCtTjp2tjv=%p?UG~w75y+nRe}t@aM*4NVd&}l< z4r-wxs#z=N++Re<4!>R^GYODz&<@G_16Y59Q zrexdyi?a6)XZ!8@hp85=8vVA_X>=H^qNS)!wbj$#lg{H_WzCtGISrtqwyiD$We6nOyC0pOVRE$fg_CS ze_4;H^RF7^cO{6UADN{)MHaR7DCj)}JU^rY#rf_=FziYRwCsx*zIgo_y8AphU z>j1}xDvMju%NaS4suKlH{|oWHui0_#EJv(BU{C=y6>v?(5ugVmw;aMar009>Q;i8) zX;(8CP&yZE=p$3h%GWy@Z?3L?xyNOwad~newCN=Gor(0slgYY27>iGzKI&LvTJtFT zeKAgQeU2z&I(XO3LfoPMYs1M4>k^F|Uy0Z~$--}H=XD`m>p&T1zZ_DL$x;RRZS2N$ z?eSS|k=@y3DUw2b(?ks$%B4^FRO1O5@m6-7*)~#<=K*nDv3JsR?eE)Z|HqdEmoySe zylRm70gIv$d1$HXHa@V(%xU>PWX7?3{h2A!&h~zPnQSdt6&9zadJ~URO7-sst4?$e z0NUIk)w77XW~6RbF^z>uZ!j-xm}j(ht9MLR(&i~r2jZbPt6dbrZ9AFQe&$vpw6o=!;&UMK7d5-F#Qx8H|?S?oT zr$hObbu5Zc_S4VX-+l&m?vEMid(*}By56jF%^@n&W0D)6LT!w0WM|%FDm404FNGDsUc2g!fC!E z4(D(2e$`*u=~--$C$EKoEb+K+8B6+kPre72{W>ZNH(qM-1!wo5Ue%)G&qde!5oX>D?{rqS^pa@;Trep5Cl%g@d>D24@CWF&3uX%yLL*k16Dz>2} zphqTAe_YmfqV$ZLvVh#XLYi|!8Vl4L@D7{J4#>naX~1eu-?M!`&*GqE;YFMK(^Z&HngntZi==3R4FgTF$WlQ+0;EiM)D7OM4vzgxqu zU3;{1r+2QHS9!8kPW|r%-2CzXWGesPB;dbgNdI4{arBj=;@2BHPyYQfKYbKw^>aKA z;Pv&fDK>wgPX;)>1{2)xug2>u?;A6ghMcy!1&Cx1nw&LIfc#*dybJn`iiogjPG$dd zMfm2=r-5_3#I>oMQt+cQI;o1p{A{ebQ`mXOI}w+j)<5`|!@>ST`9|JjI8 z{$p?Qvrx5Pne9w_r|RcteIKEJrKRfalR^8|w4>_XvW@Db05>$$iI}l z7QSKYfBOTDw4ZF(o1OsdO5Z*!`K+c^O{F!}0A9t$bUQi+V9R`oA0++*Dgf6+0Iug- zIF(D;tyja>=JzPe6E*5U!w-!O^!OD+R10-pU1AmUn-evw9g@~Ki*H^(w)9hPHz;53 zhlGkn2|#~p&P*c#01;>Nnm?XtKDIZ^KGsa#I459GWMnK&AxC-}ww0{ELlorzKiB3M zcMs4V`8JI=1!udybnr3g=zl8f>j-g60L8vZ=RZ^}pqfd&=ic${^InyYd!2|~{bwFm z?GVE&pt0^jqc~r<=l1Q-$6#&Y1=X%hRW%#HHb-Amm#%S}`TFJj9fQ5S-9^%IBm{c{ zthEI5$DBv%_1D1O>+|8x4#9ojddG|X57^7Xei=0NJa6cKug1@UJP0As8Hhf9`q5+H zc=vkN1PlZ+!!kE#bP zzp+nefu{pk0m&jn^)gKeC<=ra0WytYWP*=AXiwM8Ztbl1W!p*ys*wahni`1#S~8&> zs8~+UyuJle4-)P=JYQ1zvv?k|)NV~#R=%_8cmcpdwWXngE1-${*I4~v-_NWw%YBwv z`c9&ZYm{*b7n9GT%-V}k;m$_WOCi`%<`VM;Tcg+1%SU;Lq)Ga@YwUg~C(tgr0UUL= zOJr0c)?R?`MBty+=Wu@S>Z<;^ll}DqkU%pHN8Fx5J1rjVZTGwQazk7Unj#gjXN^#o zU8e)@yIN&S2f%kbW;Pyh{`#SG?(66`T{e&@7{Vbp5(u<*a`r01WevauA^}1YS_XFdNNYf+ zZg6J=gUAQ0$`esmFUlTu~EvB71HsQB)#d-BD|ITLIY$7Z!P(a<(FkyVI9O^f4AT#*EJE z#EkKUqh)4wJ?jAShmj1QvEBW)3l^KmmfmqZ{kYEJj|YypA}R+AfGD4|c1FnVz)*qY zi#-$2&dZkh#B~DB@Zo}Fp4ZBlIb~Im|F9PVL})4@&V2=GEVm_Dg&uyg2FgiuI)}^^ z(<0F2vmQ4GaM0@6l(Un3fuy%iTT67S66mwV0YTaKLTLy_p_PHRTW|r;5ig>G1>RV_ z(oKpFaR>uB8EwwIWr0X@x&Ua{fz&ZiEtZLQZoYsHGXWuM-)KL>c{B*krMAmV;kVyd za?yYbDVZZx$a!q2Drng80&~s;VpQf|Xez)6>xOO7Vn-L|%Xx4=fq=)9D=M?vyUg;P zlMCFB|AcUctS9$3c_q7^WuQmL9w@N*iQ>zUIT{I%BHCD>EkNd>8`Twg#Vm45)ODrj zoI>2`zp>hzXJZX(JZzj6HDd%(T7AV70Zm>(tzvq_Y{$Xt3Q(PLn#@E$u0Dj6hghSpF}F_Kl%we!(frxJeKGs>!Nv zaSEH?ee(^8*TU>?Usg*M0`SOJ4&_b4x?e{ES}cw&7b6?vv&7qu&h3GKp?&d{?FO8# z#W8{&5A!yvOo>Fq12)<$llOS_rz?@*qS-D8XHo;@o|6qvETYq-j_tcgv$W3Q-N93e zwInYiQ9I2Sx9=1fZ=L?m4z?7_VXw}%=ie@g=_KHiFji7Jv^{JCSBN93-peF?5No7^ zy5Ilxo&L$vNp_J5*+h}sMJ8xn?psE;fb1#?^w38FWP~mPg{P%agpD({u6%;)0}Iy= z;F~bew&>_fuqs;pT$y4D%ohiigx!YitG-3qtf$PS>7WaFtU8r2oQ0;G8i8KR_z?Ha z1RZ|YWv^IUc$&k!(*7ebO?h|1oc@X2aG;8`%7DrO+1FH{49_Ic+KXox{0qSq}@C-Jtv9cAH1jL<<= ziF(oE)r*8nAKJBns-pC!^#%5EToL9BlT8zHJwQ%budE8Sp>FFj4-OH5-LsYD>mUf z`w8G4pjeJ@HfIolEvJfCo)uYC&4d6^9E#&l7gcW~yqt3kGhag(ofRddclt4{WDlU7 zctSBN$I>BCnb|IdxgA~GDSz}bPxFb|$yUcV`p<%-6sq_Bs`AGlU;i9h%FpG{$e`RH z#I3?`>iit!*|m(Ha<4>=D z{&jNImK88DxgN{_bc2{w+|ork7a0Mt5+_wAra3}TbM@w-LD121DhIYnidd~90jeUb zF=OL?yr4)M)6w6J$?b6@CKki=jWW=|bKg;hU?w zha$K*s}dz_JdzZAD_bA%PqU*NRb#tpBgLBn6XAw+lyh0DQ7G_N-ZUfUkwi0(&RscG zD){Qk3@;w^9pR!RD234^{f)qHhi6*`@7q&6_x*49h|ny#e!jUBDc3CL^p>?B@f0f# zJD&(OSv%v~fzJQ;+BZoRPr%QPV1RLf8ttMpKX)U|tacOkgkjbE&3w3epJn&N%hZM~ zflsnP-1`$4onPYrxb@z){reWS^N91GnHH#~%9rzG-07a=|H1{j>xrfS7c?f;$63t^ zo$&$zzAt++4p`WNVdrl)4zj6#`RhkqI~#1Oy5ogoyZ@TK`%X*bIx2A1ecy z@Aib^m3{~DzMgrj_#XmlmdWu66x4ORu22=^pYcpO*MnLGaau2wqj-znTWC-$xeqCD zwRIer^CJ*Jd$1h71rPNEmyrR;1G^<^vcIry3aaxc6jWw`O_75~?kcFOaWXP9Nxi@b z8wG0nrH$;hygbmyd|GIrrTr3H(9FsTj;9=?2;18dUaXDDhxt^&!Jj2NL=_)JtgXG|WqYpl1zE~XajzV^vQMhIdg>+Rd+Tj%O+gZih*t95N^Iy!>X11EXa zmMJNd>4Tk-%;gFU3`Z1y9S2CZyB|){I!mt#h1WeS-omUu^`SvrW4^V1p_<=UQ)|%n z87+_V4$T7+>ehS3pz3~%FX18h*bkVvF8KpW7U#0#4x$R!yn&di(4Z<0-T+NN6YuuX zML8MXsOUt8^Rh?fY8=Ev0+Jlij`gIH)&UHCmV!=J4$Qji;;&xhintfB_rP~L=#MV% zMyf&9{bS2*HbA`X0pbhTY@EO2{p9@KmofVq8700A%~;G{Jg1*<&eLYU(*4I!>2&TUwNT7AV$!X^6uP#ib^mh)t#s zXKp-~i-4?C|NH%}JC}F^lYjLD5RU^H{t325PzxK!R1+C0R5Vj}Via~PJ`1BPT?ger zbjRsh@;#|-W^WzmT=M4+A3m(tvYqSE0MV=nC8Db@1H(~xbndkt60TSP$lfXsLV|;f zxQo2*`fjQ^?=Md_r~~jMDvsH?AKPY?93Rlr7{q5BR;ynaIuT*l89%%`F10+Erv;7R zk@a{H9P*j!-!rw&z`(kB9mFC0l&)R@ymW%3T~B+eEVh%!X9(R0Hq1wbts$$j$_UT6S&k|1W*I5=?X50);igV|f36^;;pxxRgN za6r`!K9(#U*PDTDr&mkI1n$h#4HV)v)>rHJIsjR9D{;$z8{F5UP=7_D^S_rSUdEMI z{;a5if@yH@KrQX-*4%zSKD(!jHO3@VvzTJ2>O627fsFU$8zou**XN1mv_ zEh=H^iS*Yu&!#?fe9wQ}w zpYw9IdHwN*myv>fc|iDL{w;g7Rkomy}86R{!W&7OI5HH*c3-22@VY(n8RQE(xZj!Vwp zI0>F-kKFF6L0fVb|39*3p#q<5;{7h2%exKM_?K4uT7`uFrr?RIA9t2XTF>*1d9@vw zz#GX}1x(x|UcDzJTF}=|o(&iiOG_8lcy+jWKhs%sI+31~=Uh*^#IY+;2BN_QxlD)u zQFgkj0|a^qf4FX>!8coE!raaX{`n(&iq13ECqqlHHsPa(pQhB$wR(B-T!5AmI3r`D z6`_%r zVLH_0QAa|LAb>}aF*8J))n>Ycf0y9L$8UmOOMRbLHwkm-@Um8P>W+*`B`*;Rfek4-OLYhjIWbzE$Iq z6L&+>d$b2_JfAwS*VF8=7~QT8akdb+UMkw}(ge?DRfefHYo{lOW9WwF%g>tV)$cub z7B?7mh%x<_<}80pcE>5&2H&3qu29_1e)g=y+F;b@Z>8ogEZ?-6OEyv` zWu1&I{ID^0vfSbGHdE|Ssto!Mc^2{-jm)F2&B72a4K@qA8MW=mMl ztr%bDRd(+odI;u39yM=smqM(XJG>Cbz9@-oA*%jbf?bKe`tI>?ipSHk=A`8{<@n37 zA)z`N4}J(FRk&ea6vlSG%y3ZPxY*MfeKs_=sDf}=KA`6%L?|b-B5kNBc|334b+-`D z=gA>=HOD1PfpQQ|5OSs!nu((6SOoq_-TQ!+TIy&L!_nQXvmSer>0dIg&y6PeFg?vLd@#;@H^tN=(&A1o>UB~# z60UpZ4Lz^77rd~uX6$!xi6Ao0VF2|DBb{?|+`khsBL0FuTZ@!`p^gv~;1hBmH&qf; z!onOfRWze1U$N}vJwe^bOW@X4!S45*sMFrfoE)CB74q_ydzB{XC&xzKSKD{@G1)^x z1qvV{$ajwcZ?W7J1P-Fk;9M5bK7^=UGjC;D-77RUMdVN9HTB;1&}+C(Vu<57qrR7( z-Aa5G#HEx;PIOR5-8I@!e(HMI5-}kojksCaSwR$QkI?gZeB6%U%UsLbRWIojcE%fr zSoq}`EeLxm`;P|>_s)fFKM#wOjl3-7@>%o3u!@9@rZXPewnF`L@p;^b~+nZoHjz8PS5cBJ!^9DqfSKaTedNw+j)2G#F_}D$)fi}1~ z%y?`H8H6-r9FywHMfes3c#4re_s+fD;8b=_5XX++%Kc;)HABWl3!=}Ksvw^Urs#d{ z6d@fb&fIF7^1d{9#dSDx`f8L^gGz#A&2Zmfu9g5HLsVQGQ={3cHM)A#`J)KOJ(P_) zXDl36Xic~a-{3b>miqoDdOIzoNQsOr7xY>Ay#2^TYq#ELeDYwTGq9unAx*T8u7&;N z*?6bJ_n=i&XfDrV)CNeE@p${r4j$P0%S61FIm;Ur>}qhTvBQHSkSp3Pe)D5oBpIZ> z6#AV}%=n3pIzFVkWA}w3gFF<@VEZiApNnhes70PI3RNtxMqrw#|e0JWf8I+65M z&*^3P(NWK{Y1GGy_39z_dk=7Yim%;|)#4r?MDsC9H3NB+x{GIG$%3vG<=Z)`-)#nq zbg#%K>W!7Jp(|XZosYvfsyvuCFU+SgiQ#Ed6J};S`+3NqbS1N!b+4kBeNPajj4vH{ z`p&6N%w*dYedJ{b3Lwn4$K#Seoc<(jMJiT6k=;iUE?Fl!MRIGgY2eS85*_BrZkGpTyh@+S1EYy#1E#M&(C)ML{ z-rdup8b)DzpIv24JW&IJEAiXR8tJBs6RMQ)V8l9gBry9jt9@q>hxW#NU)12LvEgQE zL=UWZ_eFtKz&qSmDK}J&eu#m|ne@P~MUu#$PS4T|mnHC{7_#fg6q&!k9j2~WF6eCsC$YS1_s&mt>r0kIE? zVN#xTS(o>&{{7L~Ynqe0Zm4%9;eT<)g{ya^x=_CQDKr*DtwF7VSxB9sFf9p)=S}=}I_uQ>Pnm3TbIt#y8Wna&I z;kmo8-U^eQtqniTM3DC4`aP(4u7+>DLE}PAWneHToegr&z0RY-&}mZI7(4o~zsWQa2Buiysg^&`D`f|062LPfbl* zHBdgM+b_6re9+q%iwe;88oOMAys^~P8D(WhAV!ENKGZ_GQ11FNu)tQIv;l4KNk&I2a=|7dV2mLaiU_PtqnEoA#RN=ne9@ zdapF>`u1J?i(^23$U_mI@iBeB`?kT%Y~@=7l{~&-EA&FFV`=Nk^S?9y;GF9IBGn8x zquOj_tr`N-U@0$3W>*W5#+>Dra-oWtJWg;qo!K19fWkuJbDZXUQs$dFug&SCe~Qeo zTaE8mcB)wwy$xz|q0^ibW<$xvy$?-uZvR$+&hC0<_d!o83>68BFf#>Eo?RAQJlK>a7dcB6Z&Nh2^zPr3WJH!L( zxB%I}^v}H$T$fzGpBeZTzzDABJ9YIU;JyB8FlD!Sp3PZN6R@6$U6BAqTggqIv;|Rn zvcrq^5{3lYy4i04veb+0-_-YEQt1nicc_89v0*0Kk4Jv7*4gd+`pX)ul)hkNB59*} z4X_(kG(SV(m~NLv<0@-|6D(jK@91H?q-l)*4^%sZY5xnV9m2GKL$&`vb^n9x{^zCt zjqLvCrT;e|@W0;qe__V|^+BT5GJBJy44>z4IZRYD3UD&0Sf2D^-{4d_ZyN}yy*`k0 zJd1-t$<|k{EGF~ zOAUujl7al*OFf$|Po*zq5qH~xE67b#l z^Q3R<&a39@U>waiFqzGS_w}?AjR8 z5KtKT`iL3RxJjbl03N8B`x}^aN`O)G3SXeY3lIw{E^49_>(n{3vo_n)x?h8Oemw`U z=k0scz{f4`*#V|EEY``u&*vuW+o`UI4k@;Pgbzzf`rAm0AYk4aG8&!(Rxg+uvJ!B- z?Vu&GqH-8|>M`!Tm7@}Li=S3_H^n*&Bz?Halupmw5CT)qnfR*_?; zLJ^`@M=1srVW1ppE~ZD$-YQ~cvcUwfIM&F)Uf@_QfoXjM55q42Mx8^PC#s_qtALf^ z(D6hkfRqMejb_Asy3hZh_;XksqQdX%;u`zLah4OInJI6JfkgM$q2 zv7cK2b`7>2)WsK9Wn3~z95o12_J(4pgfWEtmho0*M$$&QWU3N-6rI!Q+{$#5l zCFe2ms=aitnnU3_QBuO}j`)5D6o>^@eJ?2Q`>~Nh4QaMELL+m=XQ?TB=*&9SaVt@+ zt1H9;U>p}YfVm|pl_|`3**%DKW@*Xzoo_I6E#=1bAJExxPUXFY4xG;oB6eFvpsMiDXJEKu zZ?kL_NsUvR7qNNjQp|0q5g~1RBpOe817ZosfKXp5H6)MGyXqarKurnw*9@{(h6^JQ z+8ckic}oPfmya;c+QHnn4b|1l+7cm zKm?7m(P1-fdlpeNVV-hZr%y%amY3akZJwe(Mcw#|{DA*27aRay=GTY= z&tAoAK7`>HKyqZ+lxR_9V?IFG1A8VyO60f~e>km1*0 zDWDtr)vw>pjf}KXVj>KmO9p}d+g9oMe~&dwg#jb7>6$6!ayGCEdSD z@D7s2U^ve)u>-M%0L={i{sOn?Y8>Fbe}PCLLho5|bJLqIgk42>_d7cIcygq@mt_n( zhxWa6f{HlLUN-G=T(oKnfF6J>rvBY|#3-=FwnXUQf&pys1B_2-z zl8=Wkeaf;>@Rj}Z^4^s|aQF}miD~BLuI5m{V%>Q-)iT@e0~2I+>a5>fd%tjR{*w*J zsYTy5crpl{4xixbRWe{tfZpiq?Ex9BH1dZJtTY4=a%@|vs;W{3 zq-;%JRq7A{HdX^Q7eZ~ZXWw7Eele%BC7$ZpZ*Y{y(+ykz2wt-T^j?&dt@BnNQ*I(A zHYh$pIo*t_BpGhnY}%&%fH!u|y7z>sT0cwU4M_IX@l9wEuwk!aVbgKa8;)+>Ns{>F zZV?w^Mw+QmKx3aRZ{#kz?V*!$9Z8*}j6p!gsQ)g0T`ZIV=M8?=c9aHsbzkxgin4SWHr0Xj_npp^o zz2cbOeP2UT@C?kaJhgz2#7)97<2mFmNiSPb*V>MQP~c-w!C_*acG~g=U2ey5bKwpS zUtF1?&thZ7HmT$X9D8>#=CpKh$h7Td%fsMJqdSucV8f1ihFz43HF5b^|1>$hdAQ!! z6UuEkHa}gP_!2V+NCI1{I0yfdFBUO?%|4 zRfpOIxqhZ7;07D9-vifr9X?4J&Y+seBx9af7d(!?;r#Jx-&Xzl$N*c{g8r<_0|bpy zhUo_hL*vxmr%*Vueqhk`p2Lp)O8?nMAuL!sqAG-~jPAtV?=!h54jA;9wOJvfdk*Y| z>d~ZfILAro(6=D@KA2Y>(SEZ!HbIEzGA^s7LV7`5nq)Qf4e9(W85f@M`fFO+kv}IU zn?gdzs2e&;)VD>ZH62F?XVYu<;L=TR7+pI7Q*-OJ)EC%oGhifLQMpnzpSuCl1}uo5 z_mWZP7e*Pu2lWn^^w3eC+UQqTOIwfU{XK^lB*Bk;j(^(tZ7rp4b+qDIE3q;{YS&YaYZ(wc=>9)&N4;myjG-Rh{ilAV zll!A^Vq<3r*aH)a(#qf$he|Ka!DsykSYmoz$!ksZ0+NnqJ;EDkaT>&c;cql8@}fm{ z73zsuf?lswgf_HcGH(W1Z&{&KDXzG?Xt|$vu->V-(l%(9;SfeYy?rp&1!Aj@`eDT~ zpcl42y#v=w6u8q}XA0;4EGb7g@Em(?4u3lS_2EV_^bw7)m8qs9zL9f|CHc!F z-cw=MZbs_G7!2YwDc%B74P$BhKMZ{xl(AUfVWtuiDvhC9jt4jm@wWKRn%(cPy8C7= zrc*Kev>!X^(_Kk=MlB3)tX0X$-1g4nXP1r}{Hifvqc<`Y3n=Wx(u?WQ;FOE9HZ0}% zXVAp_JCtx4uBd#8!HsTOp+X+th7sGXztQOf8fGzGSxdryNw9+@5_J^3BiBOQO?}M) zlm`Pt^uvEKPyfH5JqFMNyUTs(T$ofi{=ILYz9LDN{Knj5Z=exTS z6TBwfD?V%`sKoMC$4;+>3O-ed)0}yEVa;iU|4xx?Uq#tpPbVE-6@WZ zC#L%?eLq46yN$9B28WSqnc=1#DpUBpZaojYnbsVYn4uLtg#;PDokAHW<8Qr`sN)M< zL18?m8&xkzIqpb_9ze^wfot|f038+&siv3k7$ zd_0-k4KjWpB_{Xb=Q@GwbBSFk+o4`7yXEy;xP!TPlkeAQ>{dH}qkuKjIO+CGN+CR5 zVqFv*_yaW?p3lF(_;sB|Kfxw{1E@5kRtXtC$^jr`6^MF=VB,y2QZ&<;oF%n5*$ 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 diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java index fe8ceb3c12..1c78c4106e 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java @@ -54,7 +54,7 @@ public class SyncedFoldersActivityIT extends AbstractIT { public void open() { SyncedFoldersLayoutBinding sut = activityRule.launchActivity(null).binding; shortSleep(); - screenshot(sut.list); + screenshot(sut.loadingContent); } @Test diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index 8a7f47a47d..971b8f1682 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -35,7 +35,7 @@ import java.util.List; */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 79; + public static final int DB_VERSION = 78; private ProviderMeta() { // No instance diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index b4eba3b10c..3ba730fcea 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -468,7 +468,7 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterSearchMenuItems(menu, user, getResources()); DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); - // DrawerMenuUtil.filterAssistantMenuItem(menu, capability); + DrawerMenuUtil.filterAssistantMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); From 9ce57d998282ab08a6b2de5eb124600294b15978 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 12 Mar 2024 11:19:14 +0100 Subject: [PATCH 081/102] Fix ss tests Signed-off-by: alperozturk --- .../com/nextcloud/client/ActivitiesActivityIT.kt | 13 +++++++++---- .../nextcloud/client/SyncedFoldersActivityIT.java | 6 ++++-- .../ui/fragment/FileDetailFragmentStaticServerIT.kt | 2 +- .../android/ui/trashbin/TrashbinActivityIT.kt | 2 +- .../android/ui/activities/ActivitiesActivity.java | 2 +- .../android/ui/activity/SyncedFoldersActivity.kt | 2 +- .../android/ui/adapter/SyncedFolderAdapter.java | 7 +++++++ .../android/ui/trashbin/TrashbinActivity.kt | 2 +- app/src/main/res/values-sk-rSK/strings.xml | 2 +- 9 files changed, 26 insertions(+), 12 deletions(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt index 616fc1bc9b..d821fcc182 100644 --- a/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt @@ -21,6 +21,7 @@ */ package com.nextcloud.client +import android.view.View import androidx.test.espresso.Espresso import androidx.test.espresso.contrib.DrawerActions import androidx.test.espresso.intent.rule.IntentsTestRule @@ -60,15 +61,19 @@ class ActivitiesActivityIT : AbstractIT() { @Test @ScreenshotTest fun loading() { - val sut: ActivitiesActivity = activityRule.launchActivity(null) - sut.runOnUiThread { - sut.dismissSnackbar() + val sut: ActivitiesActivity = activityRule.launchActivity(null).apply { + runOnUiThread { + dismissSnackbar() + binding.emptyList.root.visibility = View.GONE + binding.swipeContainingList.visibility = View.GONE + binding.loadingContent.visibility = View.VISIBLE + } } shortSleep() waitForIdleSync() - Screenshot.snapActivity(sut).record() + Screenshot.snap(sut.binding.loadingContent).record() } @Test diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java index 1c78c4106e..7afaf3bc17 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java @@ -52,9 +52,11 @@ public class SyncedFoldersActivityIT extends AbstractIT { @Test @ScreenshotTest public void open() { - SyncedFoldersLayoutBinding sut = activityRule.launchActivity(null).binding; + SyncedFoldersActivity activity = activityRule.launchActivity(null); + activity.adapter.clear(); + SyncedFoldersLayoutBinding sut = activity.binding; shortSleep(); - screenshot(sut.loadingContent); + screenshot(sut.emptyList.emptyListView); } @Test diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index b03b94a858..b664600ee5 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -194,7 +194,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { shortSleep() shortSleep() - screenshot(activity) + screenshot(sut.fileDetailActivitiesFragment.binding.emptyList.emptyListView) } @Test diff --git a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt index 510a8b3ebf..e5543c665f 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt @@ -105,7 +105,7 @@ class TrashbinActivityIT : AbstractIT() { shortSleep() - screenshot(sut) + screenshot(sut.binding.listFragmentLayout) } @Test diff --git a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java index c449c50855..9efcf95a38 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java @@ -61,7 +61,7 @@ import static com.owncloud.android.ui.activity.FileActivity.EXTRA_USER; public class ActivitiesActivity extends DrawerActivity implements ActivityListInterface, ActivitiesContract.View { private static final String TAG = ActivitiesActivity.class.getSimpleName(); - private ActivityListLayoutBinding binding; + ActivityListLayoutBinding binding; private ActivityListAdapter adapter; private int lastGiven; private boolean isLoadingActivities; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index 26b5ed8cdb..5200e01b7c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -166,7 +166,7 @@ class SyncedFoldersActivity : lateinit var syncedFolderProvider: SyncedFolderProvider lateinit var binding: SyncedFoldersLayoutBinding - private lateinit var adapter: SyncedFolderAdapter + lateinit var adapter: SyncedFolderAdapter private var syncedFolderPreferencesDialogFragment: SyncedFolderPreferencesDialogFragment? = null private var path: String? = null diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index 2f31692980..2b2ef95eb7 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -50,6 +50,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; /** * Adapter to display all auto-synced folders and/or instant upload media folders. @@ -179,6 +180,12 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter 0) { return syncFolderItems.size() + 1; diff --git a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt index ff99ef9024..f8921d60f9 100644 --- a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt @@ -84,7 +84,7 @@ class TrashbinActivity : var trashbinPresenter: TrashbinPresenter? = null private var active = false - private lateinit var binding: TrashbinActivityBinding + lateinit var binding: TrashbinActivityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 32a141ac41..01bdc92a34 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -789,7 +789,7 @@ Plný prístup Médiá iba načítanie Obrázky - Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou.\n\nFunkcie:\n * Jednoducho použiteľné moderné rozhranie, hodiace sa k vzhľadu vášho servera* \n Nahrávanie súborov na Nextcloud server\n * Ich sprístupnenie s inými ľudmi\n * Synchronizácia vašich obľúbených súborov a adresárov\n * Vyhľadávanie naprieč všetkými adresármi na serveri\n * Automatické nahrávanie fotiek a videí nasnímaných vašim zariadením\n * Doručovanie notifikácií\n * Podpora viac účtov naraz\n * Zabezpečený prístup k vašim dátam pomocou odtlačku prstu alebo kódom PIN\n * Začlenenie DAVx5 (predtým známy ako DAVdroid) pre jednoduchý prístup ku kalendáru synchronizácii kontaktov\n\n Akékoľvek problémy prosím hláste na https://github.com/nextcloud/android/issues a o aplikácii môžete diskutovať na https://help.nextcloud.com/c/clients/android\n\n Nepoznáte ešte Nextcloud? Nextcloud je server pre súkromnú synchronizáciu súborov , zdieľanie a komunikáciu. Je to slobodný softvér a môžete si ho prevádzkovať buď sami alebo si ho prenajímať od nejakej spoločnosti. Týmto spôsobom získate plnú vládu nad svojimi fotkami, údajom v kalendári a kontaktoch, dokumentom a všetkým ostatným.\n\n Viac zistíte na https://nextcloud.com + Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou.\n\nFunkcie:\n * Jednoducho použiteľné moderné rozhranie, hodiace sa k vzhľadu vášho servera* \n Nahrávanie súborov na Nextcloud server\n * Ich sprístupnenie s inými ľudmi\n * Synchronizácia vašich obľúbených súborov a adresárov\n * Vyhľadávanie naprieč všetkými adresármi na serveri\n * Automatické nahrávanie fotiek a videí nasnímaných vašim zariadením\n * Doručovanie notifikácií\n * Podpora viac účtov naraz\n * Zabezpečený prístup k vašim dátam pomocou odtlačku prstu alebo kódom PIN\n * Začlenenie DAVx5 (predtým známy ako DAVdroid) pre jednoduchý prístup ku kalendáru & synchronizácii kontaktov\n\n Akékoľvek problémy prosím hláste na https://github.com/nextcloud/android/issues a o aplikácii môžete diskutovať na https://help.nextcloud.com/c/clients/android\n\n Nepoznáte ešte Nextcloud? Nextcloud je server pre súkromnú synchronizáciu súborov &, zdieľanie a komunikáciu. Je to slobodný softvér a môžete si ho prevádzkovať buď sami alebo si ho prenajímať od nejakej spoločnosti. Týmto spôsobom získate plnú vládu nad svojimi fotkami, údajom v kalendári a kontaktoch, dokumentom a všetkým ostatným.\n\n Viac zistíte na https://nextcloud.com Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou.\nToto je oficiálna vývojová verzia, obsahujúca dennú vzorku všetkých nových a nevyskúšaných funkcií, ktoré môžu spôsobovať nestabilitu a viesť ku strate dát. Aplikácia v tomto štádiu vývoja je určená tým používateľom, ktorí sú ochotní skúšať a hlásiť chyby, ktoré sa vyskytnú. Nepoužívajte ju pre svoju produkčnú prácu.\n\nObe oficiálne verzie, tak vývojová ako aj produkčná sú k dispozícii na F-droid a je možné ich mať nainštalované súbežne. Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou. Platforma, ktorú môžete bežať na vlastnom serveri, ktorú môžete mať kompletne pod kontrolou (vývojová verzia) From ee066a2e7a99e8c3b03589f951ff5d93e2bdee29 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 12 Mar 2024 12:23:08 +0100 Subject: [PATCH 082/102] Add new ss tests Signed-off-by: alperozturk --- ...ud.client.ActivitiesActivityIT_loading.png | Bin 5686 -> 2567 bytes ...ud.client.SyncedFoldersActivityIT_open.png | Bin 9020 -> 5756 bytes ...ui.trashbin.TrashbinActivityIT_loading.png | Bin 6474 -> 2459 bytes .../FileDetailFragmentStaticServerIT.kt | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_loading.png b/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_loading.png index 6e705fabd661c0b50e3bdde2c6ea6a23e368ea49..a9acbd463afd087066baaa5bfa1b1f75150a69f3 100644 GIT binary patch literal 2567 zcmeAS@N?(olHy`uVBq!ia0y~yVEn?sz!c2E1{C?H{=|`if%BZFi(^Q|t+#g#yQB>n z+AiwG-aPy0T*8UJ*0KlOIrQ8lJUQkxpIO!MTfps5@?Q4%`GQ>bhQiNYa|4aQ0~-Dd zCC4nSKYL$-p;@n)ufcJ;Bm02`rx!3wl$-<+k|hj0yLhY^*kaja4=nHo5kSd=3#k_v z4PF?%0E&YNHd%yrgBOz=*};-v?RYGwiPVGj0*ACbgWl_`bDXXsS;DhR<>UhC63g&P%gY$d!I5J)SN!hznHo9G=Ah*l3X%pSsi0;>V9A05%$UMIrvfwxa<_gRV z0?#lIDbkff-E`nfQqO7?&%Lv&aE{QCB|DmVF`OeKTfOknEH*H~Io^Y;wMWu!X?-II(^_nAF(<@KC2{Sh#J zNiQ|9Ck=2?LCuQ5>Ig?19avIx;K=bv21K0$sj^U_9hw}VwHu-WBUlXKu#|Y=3Tefb z&`C?~MaF=m4qWzv3QTZW3@tm+%2MQFngpe=5`UR^a*V3ytGic{qqA+n_TAqYbZVvS zfwQ0@{g>azy!PK}wujY7($zL{_unr3-(v$Re@xCU*lvH~y}vDp z^CbSozuU7Yi)2_N%Ae_$nxvwC6qJOK>tFOp2lLS~88qplh9r(!4HhQo@zvn@v!Szp rGXru;05^6>Pa#+;DsbP0l+XkKi`GC` literal 5686 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 Zaf+39eBir9{682tXaO_7v(N3~e*w>iUd8|b diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png b/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png index 4b8e70d7faf75b90af6566f36065866c1c7e6a9a..562481fb55f3a8d86a5a8ac925925cd4c5b7fb91 100644 GIT binary patch literal 5756 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 Wyd|g|6o>j7m(6**a}{TO?*BK}?D}^A literal 9020 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 au)nj{sA?CE=Bn8QGRM=^&t;ucLK6TTmS`ma literal 6474 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, true) } longSleep() - screenshot(this) + screenshot(sut.fileDetailActivitiesFragment.binding.swipeContainingList) } } @@ -194,7 +194,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { shortSleep() shortSleep() - screenshot(sut.fileDetailActivitiesFragment.binding.emptyList.emptyListView) + screenshot(activity) } @Test From 3fe80303e1316b28cd532a486ba2dbda09f4415b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 12 Mar 2024 13:10:24 +0100 Subject: [PATCH 083/102] Reduce kotlin ksp version because compatiblity issue with compose compiler Signed-off-by: alperozturk --- app/build.gradle | 2 +- ...ntStaticServerIT_showDetailsActivities.png | Bin 20960 -> 9779 bytes .../FileDetailFragmentStaticServerIT.kt | 2 +- .../android/ui/trashbin/TrashbinActivityIT.kt | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index abfa60bbdb..63d815dfdb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,7 @@ buildscript { plugins { id "com.diffplug.spotless" version "6.20.0" - id 'com.google.devtools.ksp' version '1.9.23-1.0.19' apply false + id 'com.google.devtools.ksp' version '1.9.22-1.0.18' apply false } apply plugin: 'com.android.application' diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities.png index a7e1742bf335c053e6f3ca80a8b2db0e1d4c1b7b..73192a79800d267055a6a4634ab3e3d261e8ba3d 100644 GIT binary patch literal 9779 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 KW7Q8Q(*FaBwT|Ne literal 20960 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 diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index e33eed8645..f273523c66 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -194,7 +194,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { shortSleep() shortSleep() - screenshot(activity) + screenshot(sut.fileDetailActivitiesFragment.binding.emptyList.emptyListView) } @Test diff --git a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt index e5543c665f..579fc3067e 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt @@ -89,7 +89,7 @@ class TrashbinActivityIT : AbstractIT() { shortSleep() waitForIdleSync() - screenshot(sut) + screenshot(sut.binding.emptyList.emptyListView) } @Test From b2b31021d1a409254709ee4e18c848e9458548f1 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 12 Mar 2024 13:13:58 +0100 Subject: [PATCH 084/102] Reduce kotlin ksp version because compatiblity issue with compose compiler Signed-off-by: alperozturk --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 63d815dfdb..512659c6a8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,7 @@ buildscript { plugins { id "com.diffplug.spotless" version "6.20.0" - id 'com.google.devtools.ksp' version '1.9.22-1.0.18' apply false + id 'com.google.devtools.ksp' version '1.9.22-1.0.17' apply false } apply plugin: 'com.android.application' From dd641237fbff2ef0a968d644362b556d901f4e5a Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 12 Mar 2024 13:48:24 +0100 Subject: [PATCH 085/102] Add new ss Signed-off-by: alperozturk --- ...ticServerIT_showDetailsActivitiesError.png | Bin 14455 -> 5080 bytes ...d.ui.trashbin.TrashbinActivityIT_empty.png | Bin 10394 -> 6167 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png index 8b6c4ed1b861567c5a1071cf0076f10a1758d3df..c9b4cee5038290d8a5777e018f615b094d9701db 100644 GIT binary patch literal 5080 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 D!H-_n literal 14455 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty.png index 784f51ec88b511b391661f78000b7bf289b6f73d..eaa2e34914b7363a03fff88306326fda243af743 100644 GIT binary patch literal 6167 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 E1)p4D8vp$V_LlF3h*tB4ATU3veUv%vW{ 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 Date: Tue, 12 Mar 2024 15:12:42 +0100 Subject: [PATCH 086/102] Add new ss Signed-off-by: alperozturk --- .../com/owncloud/android/ui/activity/UploadFilesActivityIT.kt | 2 +- .../owncloud/android/ui/fragment/ExtendedListFragment.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt index af620133b1..e9f713979e 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt @@ -72,7 +72,7 @@ class UploadFilesActivityIT : AbstractIT() { waitForIdleSync() shortSleep() - screenshot(sut) + screenshot(sut.fileListFragment.binding.emptyList.emptyListView) } @Test diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java index 8da505303b..334ebeaabc 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java @@ -142,6 +142,10 @@ public class ExtendedListFragment extends Fragment implements private ListFragmentBinding binding; + public ListFragmentBinding getBinding() { + return binding; + } + protected void setRecyclerViewAdapter(RecyclerView.Adapter recyclerViewAdapter) { mRecyclerView.setAdapter(recyclerViewAdapter); } From 3ab9241b4bd5db7698462cd19ed7c9b416733539 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 12 Mar 2024 15:48:17 +0100 Subject: [PATCH 087/102] Add new ss Signed-off-by: alperozturk --- ...ticServerIT_showDetailsActivitiesError.png | Bin 5080 -> 4120 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png index c9b4cee5038290d8a5777e018f615b094d9701db..b27159ae2b503c73744d194c1345b657f8fe9959 100644 GIT binary patch delta 3048 zcmZWqc~sKb9{;&zSz}t7H6_g0!kCj}zI0py%PE^FaS}5%h17C4vfKfGZfR-i^cu5q zgGL8;aa>bSvQj6P+@S#x(=k!hL=Y4NAD#2gJMW!$|GS@iKA-#fe(&er`yTe}tDg^p zyLmr+f7UIQy~N%5=t!K8_DZ|P%wA1s?D5{OGuDAeX47%$EIXg^oWJHP|4LCaEYUmq z&7G+ov6uR}re6#7^#_)@cXcetCWSkU9s5?ZpilU!l=K!V{Mru&at(|GsW!m3SIS8UqD&YZ=ke{lH1xg-@h$%( zDiXgs1U$7F`#g^;-+2%4SpCUJCH41n^M17(ZykM}X}tVrdfQ2rlig9DRUr$X3HJXX zJpRKG)J?)5V!hyIIFF#WnJ1if0lMS__YMXrZ*HHbYdPbszDA8twKj^S0%pBgP(#G$IOTF*L$$1`oVp z-!bXMFsYHh7K&&~zqO1afS1p8#BN{na@9o?o4&=90~zS7=Scqo)Z81;(KQHEts0@i zVQ>t(eSSTtYP@X=#nw)IZOC^foh6k|wcMX)e?0u92C>`9^Z^O2wHnxyyHUDUO%T5* zLec(=89@MtDji(Lg$4MfRZq*jusZ|Qb<0gw)G!^fC#O`wXpVnSJ9?Qo>XT z)b%3gAeg@^I_ytFck_<+xjL}|{g-hM5!e6Qr$I_?4!79NFi4Wn#E;cXF&%D1V=#G zA>NM^BW>thd*Z9QD^oz`6b46%y;;@}*C<0s`VJWa|1%ylVBig8qgRa_nrfvx;!)7d z$q`C8v%8}kR*x#^^av_@u_?1hESl~L-cX1|E;tU>>P1IP^?JgV7La}c4(Ck{W(=at z+X*X{+O0f6&6=5Q(JJlBo;*Y7r+}2Z*Ls3>vtJm@U>iwmOVOk6ep{ulXJg!povBqE z$wHK&Z!yT;bZz`=u$RZ5?J_%tVUZ9)pk<=s3vuFkgWkw+E|bYrw0Ukc=&w|m$nImTUIef~|2da>i<*3+K|xdBT(oV9(?DohFmldp zSn|;6LIIafh<^G-2aP@WWMqdttHPFMRvaVd4^#8UFNE18O!iAwJDUV?LjS!9-r<=bJkKHQj-S2nx+-l-Tz z;N8Z;6@-!}*`JUUBI9&(rZrS}thB*h`FFJg$#Prb_-=6mV{5!(k8K``2}QvM87{D? zvdj4D%2&mO5V^PQW91c{2j~jWM9vtKL^ZI3*)HQPf{}-E%XuE}(vmXROUmH73)|?{}|SJ8j7+FM1!?WCYs~PsHTNM?=C9-%##}2qLG2@JG_ePok@=(TTaKSao%t~jY0vsr>#}+orgIath+&&F%ob1oUf{?YC6^?#JMa|ZM zZGZnFWXW6kvH9#@WBMzjTFMaZG2gv@Vgw%SQLdlvfqFyz=@kcR`+8lv5UQLBX$kE& zyQrnJyb-HU2iNc`C$8F23-|Fw-9VjJQZY(7e?+<<+`~E|fHB>)&Ja%#1UcvL|AKn9 zWt_|1IQe5P+BObMI7L}5I}_+dX0di>>vou<=vO?MvUt4aEyFYpB_nXjpWsJgVcfds zTd{BJXL#tV*)9*s`C&7g2-hLztJzz@92PTK6n5EycRk4np1+TO<$iZTa{R6QVmYO; z@DZuuTX{Dhr|DP2VEihHnrr;85^LK+Ktbr}pfr+Yl|ZmB7Oxvy*hO^mtj$0KCJ?_C zUm^SMz!X}eeLhOF&d)G@!zg0J7+&GG7+&=YJEh0xCubHU({wm8S$VE-tS6^S93CR? zv-sV9$p&pEmL;g7slQ(lEgUQVm}bbWGci|2VaP7aGlc!tjd@CiKrH`a92}%({)l%c zV1>4J3hNJ`mSLnrkDcH|i*{*%QCh6j?OkBUg~ zMC-wAiz(6hIi4HL{rE=_^`}Tid>XNR@yJkt>J0pnCQr^z8t~(;lVvmhnX&(m_6r@NXAi z4Ta{^1kRR}=!CSm5JVea2{6*7KYT8mVlR_pa>7A-D@av~4SXnHy}hD6Bt#OXLozCD z-iiz9VjJ*#wjm7!Gx@jCWGTxEYGI3jL0m%x<8!cr6D!t^ArsrEdpZ!|W$%NeJCxv6>57~8#v z2z6(B#s#uj(8~w6ZvF&6rE1+rW3T}eD(Zb^l4JL-M~wv8x_CL600r^i2t15z&*)?HVmW5USk!%lU*-~&NTL^2A=@F&FF^~OUI z)KICtdu2RT2c4I)D#tsUhNW)>op#&Bha+Hd6Rilk{0tmvQ`y!>1Ix1T>k&;K{y4sT z0M!!MC0HQUWamV6@nDpOlEGj)HKP~LUN6&%AC27?95`u_S+G<|V3WC+?xu ziPxxkMVc}bFb1GJ_%WPXW?f=i#0*HiPB=!OvN=1i=y$skDVwXlrwa^m1x>_MR^uQE zhop41WTg<`-HxEeQol_=-uW$xj~#=GflIr1#)(%8gn#z2%6jFjhQ;;WyBLv2%@D;9 zJms(kaOp_LjuRd9o3UqXYPs33Ht#xZYj~~))p-0;fB%iMTf4iE4>0>V_<+wJ?GNWs K-`AeIk^C=`+EPRS literal 5080 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 D!H-_n From a2b74062a753eaf9955b74a0d0136f4a3a868f9b Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 13 Mar 2024 07:39:32 +0100 Subject: [PATCH 088/102] fix database upgrade Signed-off-by: tobiasKaminsky --- .../78.json | 14 +- .../79.json | 1203 +++++++++++++++++ .../client/database/NextcloudDatabase.kt | 3 +- .../com/owncloud/android/db/ProviderMeta.java | 2 +- 4 files changed, 1210 insertions(+), 12 deletions(-) create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json index d1da7a4406..61b9b1a823 100644 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 78, - "identityHash": "ec997f271f9045e8483b260f036a168f", + "identityHash": "f26afed3b9b87a3acb578947a26223ac", "entities": [ { "tableName": "arbitrary_data", @@ -44,7 +44,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", "fields": [ { "fieldPath": "id", @@ -52,12 +52,6 @@ "affinity": "INTEGER", "notNull": false }, - { - "fieldPath": "assistant", - "columnName": "assistant", - "affinity": "INTEGER", - "notNull": false - }, { "fieldPath": "accountName", "columnName": "account", @@ -1197,7 +1191,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec997f271f9045e8483b260f036a168f')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f26afed3b9b87a3acb578947a26223ac')" ] } -} \ No newline at end of file +} diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json new file mode 100644 index 0000000000..708ca00a73 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json @@ -0,0 +1,1203 @@ +{ + "formatVersion": 1, + "database": { + "version": 79, + "identityHash": "ec997f271f9045e8483b260f036a168f", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` INTEGER, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec997f271f9045e8483b260f036a168f')" + ] + } +} diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index 57560286dd..f18aa7fc55 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -71,7 +71,8 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 74, to = 75), AutoMigration(from = 75, to = 76), AutoMigration(from = 76, to = 77), - AutoMigration(from = 77, to = 78) + AutoMigration(from = 77, to = 78), + AutoMigration(from = 78, to = 79), ], exportSchema = true ) diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index 971b8f1682..8a7f47a47d 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -35,7 +35,7 @@ import java.util.List; */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 78; + public static final int DB_VERSION = 79; private ProviderMeta() { // No instance From e2b309184abc4e8a5ce912c29ee51be33fa7678f Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 13 Mar 2024 07:46:17 +0100 Subject: [PATCH 089/102] fix database upgrade Signed-off-by: tobiasKaminsky --- .../java/com/nextcloud/client/database/NextcloudDatabase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index f18aa7fc55..32f54a775d 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -72,7 +72,7 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 75, to = 76), AutoMigration(from = 76, to = 77), AutoMigration(from = 77, to = 78), - AutoMigration(from = 78, to = 79), + AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), ], exportSchema = true ) From 631ddf5250f4059895a6b982a4c6f10b1f463f2c Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 13 Mar 2024 07:48:27 +0100 Subject: [PATCH 090/102] fix database upgrade Signed-off-by: tobiasKaminsky --- .../com.nextcloud.client.database.NextcloudDatabase/78.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json index 61b9b1a823..9ac5f2e87f 100644 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/78.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 78, - "identityHash": "f26afed3b9b87a3acb578947a26223ac", + "identityHash": "f26afed3b9b87a3acb578947a26223ac", "entities": [ { "tableName": "arbitrary_data", @@ -44,7 +44,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", "fields": [ { "fieldPath": "id", @@ -1191,7 +1191,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f26afed3b9b87a3acb578947a26223ac')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f26afed3b9b87a3acb578947a26223ac')" ] } } From 6f777580e2845a0ac4819ccb2dab5825aa716fbb Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 09:25:56 +0100 Subject: [PATCH 091/102] Add multine line for add task alert dialog Signed-off-by: alperozturk --- .../client/assistant/AsssistantScreen.kt | 16 +++++++++++---- .../assistant/component/AddTaskAlertDialog.kt | 20 ++++++++++--------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index b49f0db0c9..30a59a7b5a 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -148,10 +148,18 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { } if (showAddTaskAlertDialog) { - selectedTaskType?.let { - AddTaskAlertDialog(viewModel, it) { - showAddTaskAlertDialog = false - } + selectedTaskType?.let { taskType -> + AddTaskAlertDialog( + title = taskType.name, + description = taskType.description, + addTask = { input -> + taskType.id?.let { + viewModel.createTask(input = input, type = it) + } + }, dismiss = { + showAddTaskAlertDialog = false + } + ) } } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index 14e71f39de..a8dd78b66e 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -31,25 +31,22 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType -import com.nextcloud.client.assistant.AssistantViewModel +import androidx.compose.ui.tooling.preview.Preview import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog import com.owncloud.android.R -import com.owncloud.android.lib.resources.assistant.model.TaskType @Composable -fun AddTaskAlertDialog(viewModel: AssistantViewModel, taskType: TaskType, dismiss: () -> Unit) { +fun AddTaskAlertDialog(title: String?, description: String?, addTask: (String) -> Unit, dismiss: () -> Unit) { var input by remember { mutableStateOf("") } SimpleAlertDialog( - title = taskType.name ?: "", - description = taskType.description, + title = title ?: "", + description = description ?: "", dismiss = { dismiss() }, onComplete = { - taskType.id?.let { - viewModel.createTask(input = input, type = it) - } + addTask(input) }, content = { TextField( @@ -65,8 +62,13 @@ fun AddTaskAlertDialog(viewModel: AssistantViewModel, taskType: TaskType, dismis onValueChange = { input = it }, - singleLine = true ) } ) } + +@Composable +@Preview +private fun AddTaskAlertDialogPreview() { + AddTaskAlertDialog(title = "Title", description = "Description", addTask = { }, dismiss = {}) +} From f12657d420abe130efcdb95b37376ca8cc68eb4e Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 09:33:55 +0100 Subject: [PATCH 092/102] Fix text for empty task list Signed-off-by: alperozturk --- .../client/assistant/AsssistantScreen.kt | 30 +++++++++++++++---- .../repository/AssistantMockRepository.kt | 12 ++++++-- app/src/main/res/values/strings.xml | 1 + 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 30a59a7b5a..c4388d3a48 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -231,6 +231,15 @@ private fun AssistantContent( @Composable private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List?, viewModel: AssistantViewModel) { + val text = if (selectedTaskType?.name == stringResource(id = R.string.assistant_screen_all_task_type)) { + stringResource(id = R.string.assistant_screen_no_task_available_for_all_task_filter_text) + } else { + stringResource( + id = R.string.assistant_screen_no_task_available_text, + selectedTaskType?.name ?: "" + ) + } + Column( modifier = Modifier .fillMaxSize() @@ -242,12 +251,7 @@ private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List Spacer(modifier = Modifier.height(8.dp)) - CenterText( - text = stringResource( - id = R.string.assistant_screen_no_task_available_text, - selectedTaskType?.name ?: "" - ) - ) + CenterText(text = text) } } @@ -264,3 +268,17 @@ private fun AssistantScreenPreview() { } ) } + +@Composable +@Preview +private fun AssistantEmptyScreenPreview() { + val mockRepository = AssistantMockRepository(giveEmptyTasks = true) + MaterialTheme( + content = { + AssistantScreen( + viewModel = AssistantViewModel(repository = mockRepository), + activity = ComposeActivity() + ) + } + ) +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index c2e059ce42..a69ec393d2 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -27,7 +27,7 @@ import com.owncloud.android.lib.resources.assistant.model.TaskList import com.owncloud.android.lib.resources.assistant.model.TaskType import com.owncloud.android.lib.resources.assistant.model.TaskTypes -class AssistantMockRepository : AssistantRepositoryType { +class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : AssistantRepositoryType { override fun getTaskTypes(): RemoteOperationResult { return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { resultData = TaskTypes( @@ -44,8 +44,10 @@ class AssistantMockRepository : AssistantRepositoryType { } override fun getTaskList(appId: String): RemoteOperationResult { - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { - resultData = TaskList( + val taskList = if (giveEmptyTasks) { + TaskList(listOf()) + } else { + TaskList( listOf( Task( 1, @@ -79,6 +81,10 @@ class AssistantMockRepository : AssistantRepositoryType { ) ) } + + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { + resultData = taskList + } } override fun deleteTask(id: Long): RemoteOperationResult { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b875deb946..b82d8eb412 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,7 @@ Assistant Task List are loading, please wait + No task available. Select a task type to create a new task. No task available for %s task type, you can create a new task from bottom right. Delete Task Are you sure you want to delete this task? From ffe19232dc0c07b9e4a35c8fbf41408cb700e726 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 09:53:38 +0100 Subject: [PATCH 093/102] Add assistant to header Signed-off-by: alperozturk --- .../ui/composeActivity/ComposeActivity.kt | 9 ------ .../android/ui/activity/DrawerActivity.java | 10 +++--- .../android/utils/DrawerMenuUtil.java | 6 ---- app/src/main/res/layout/drawer_header.xml | 31 +++++++++++++++++++ .../main/res/menu/partial_drawer_entries.xml | 6 ---- app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 37 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 8c812791ad..008312b7c6 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -53,7 +53,6 @@ class ComposeActivity : DrawerActivity() { companion object { const val DESTINATION = "DESTINATION" const val TITLE = "TITLE" - const val MENU_ITEM = "MENU_ITEM" } override fun onCreate(savedInstanceState: Bundle?) { @@ -63,13 +62,10 @@ class ComposeActivity : DrawerActivity() { val destination = intent.getSerializableArgument(DESTINATION, ComposeDestination::class.java) val titleId = intent.getIntExtra(TITLE, R.string.empty) - val menuItemId = intent.getIntExtra(MENU_ITEM, R.id.nav_assistant) setupToolbar() updateActionBarTitleAndHomeButtonByString(getString(titleId)) - setupDrawer(menuItemId) - binding.composeView.setContent { MaterialTheme( colorScheme = viewThemeUtils.getColorScheme(this), @@ -80,11 +76,6 @@ class ComposeActivity : DrawerActivity() { } } - override fun onResume() { - super.onResume() - setDrawerMenuItemChecked(R.id.nav_assistant) - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 3ba730fcea..5257411942 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -362,12 +362,14 @@ public abstract class DrawerActivity extends ToolbarActivity LinearLayout[] views = { ecosystemApps.findViewById(R.id.drawer_ecosystem_notes), ecosystemApps.findViewById(R.id.drawer_ecosystem_talk), - ecosystemApps.findViewById(R.id.drawer_ecosystem_more) + ecosystemApps.findViewById(R.id.drawer_ecosystem_more), + ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant), }; views[0].setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); views[1].setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); views[2].setOnClickListener(v -> openAppStore("Nextcloud", true)); + views[3].setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title)); int iconColor; if (Hct.fromInt(primaryColor).getTone() < 80.0) { @@ -468,7 +470,6 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterSearchMenuItems(menu, user, getResources()); DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); - DrawerMenuUtil.filterAssistantMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); @@ -545,8 +546,6 @@ public abstract class DrawerActivity extends ToolbarActivity intent.setAction(FileDisplayActivity.LIST_GROUPFOLDERS); intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); - } else if (itemId == R.id.nav_assistant) { - startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, itemId); } else { if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) { @@ -558,11 +557,10 @@ public abstract class DrawerActivity extends ToolbarActivity } } - private void startComposeActivity(ComposeDestination destination, int titleId, int menuItemId) { + private void startComposeActivity(ComposeDestination destination, int titleId) { Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class); composeActivity.putExtra(ComposeActivity.DESTINATION, destination); composeActivity.putExtra(ComposeActivity.TITLE, titleId); - composeActivity.putExtra(ComposeActivity.MENU_ITEM, menuItemId); startActivity(composeActivity); } diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index a09099800e..8ac2280a87 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -64,12 +64,6 @@ public final class DrawerMenuUtil { } } - public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability) { - if (capability != null && !capability.getAssistant().isTrue()) { - filterMenuItems(menu, R.id.nav_assistant); - } - } - public static void filterGroupfoldersMenuItem(Menu menu, @Nullable OCCapability capability) { if (capability != null && !capability.getGroupfolders().isTrue()) { filterMenuItems(menu, R.id.nav_groupfolders); diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml index 8ec80c5da5..f4f07bf5c6 100644 --- a/app/src/main/res/layout/drawer_header.xml +++ b/app/src/main/res/layout/drawer_header.xml @@ -71,6 +71,37 @@ android:layout_marginBottom="@dimen/standard_half_margin" android:orientation="horizontal"> + + + + + + + + - - Biggest first Smallest first + Assistant + Assistant Task List are loading, please wait No task available. Select a task type to create a new task. From a959c15354db69ff053a0068eb2c0c6cb894a689 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 10:12:30 +0100 Subject: [PATCH 094/102] Fix crash when no internet connection Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 48 ++++++++++++++----- .../client/assistant/AsssistantScreen.kt | 14 ++++++ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 3383df42bb..63852523b0 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -36,6 +36,9 @@ import kotlinx.coroutines.launch class AssistantViewModel(private val repository: AssistantRepositoryType) : ViewModel() { + private val _errorMessage = MutableStateFlow(null) + val errorMessage: StateFlow = _errorMessage + private val _selectedTaskType = MutableStateFlow(null) val selectedTaskType: StateFlow = _selectedTaskType @@ -85,30 +88,43 @@ class AssistantViewModel(private val repository: AssistantRepositoryType) : View viewModelScope.launch(Dispatchers.IO) { val allTaskType = MainApp.getAppContext().getString(R.string.assistant_screen_all_task_type) val result = arrayListOf(TaskType(null, allTaskType, null)) - val taskTypes = repository.getTaskTypes().resultData.types - result.addAll(taskTypes) + val taskTypesResult = repository.getTaskTypes() - _taskTypes.update { - result.toList() - } + if (taskTypesResult.isSuccess) { + result.addAll(taskTypesResult.resultData.types) + _taskTypes.update { + result.toList() + } - _selectedTaskType.update { - result.first() + _selectedTaskType.update { + result.first() + } + } else { + _errorMessage.update { + taskTypesResult.message + } } } } fun getTaskList(appId: String = "assistant", onCompleted: () -> Unit = {}) { viewModelScope.launch(Dispatchers.IO) { - _taskList = repository.getTaskList(appId).resultData.tasks + val result = repository.getTaskList(appId) + if (result.isSuccess) { + _taskList = result.resultData.tasks - filterTaskList(_selectedTaskType.value?.id) + filterTaskList(_selectedTaskType.value?.id) - _loading.update { - false + _loading.update { + false + } + + onCompleted() + } else { + _errorMessage.update { + result.message + } } - - onCompleted() } } @@ -126,6 +142,12 @@ class AssistantViewModel(private val repository: AssistantRepositoryType) : View } } + fun resetErrorState() { + _errorMessage.update { + null + } + } + fun resetTaskDeletionState() { _isTaskDeleted.update { null diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index c4388d3a48..37714bfffe 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -71,6 +71,7 @@ import kotlinx.coroutines.delay @Composable fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { val loading by viewModel.loading.collectAsState() + val errorMessage by viewModel.errorMessage.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() val filteredTaskList by viewModel.filteredTaskList.collectAsState() val isTaskCreated by viewModel.isTaskCreated.collectAsState() @@ -133,6 +134,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { } } + CheckErrorMessage(errorMessage, activity, viewModel) CheckTaskAdd(isTaskCreated, activity, viewModel) CheckTaskDeletion(isTaskDeleted, activity, viewModel) @@ -182,6 +184,18 @@ private fun CheckTaskAdd(isTaskCreated: Boolean?, activity: Activity, viewModel: } } +@Composable +private fun CheckErrorMessage(errorMessage: String?, activity: Activity, viewModel: AssistantViewModel) { + errorMessage?.let { + DisplayUtils.showSnackMessage( + activity, + errorMessage + ) + + viewModel.resetErrorState() + } +} + @Composable private fun CheckTaskDeletion(isTaskDeleted: Boolean?, activity: Activity, viewModel: AssistantViewModel) { isTaskDeleted?.let { From b50a114e7d9bf0ce8cd963043c68333161038681 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 10:35:19 +0100 Subject: [PATCH 095/102] Add Screen State Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 77 +++++++++---------- .../client/assistant/AsssistantScreen.kt | 72 +++++++---------- app/src/main/res/values/strings.xml | 3 + 3 files changed, 67 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 63852523b0..2a0d7c8428 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -36,8 +36,16 @@ import kotlinx.coroutines.launch class AssistantViewModel(private val repository: AssistantRepositoryType) : ViewModel() { - private val _errorMessage = MutableStateFlow(null) - val errorMessage: StateFlow = _errorMessage + sealed class State { + data object Idle: State() + data object Loading: State() + data class Error(val messageId: Int): State() + data class TaskCreated(val messageId: Int): State() + data class TaskDeleted(val messageId: Int): State() + } + + private val _state = MutableStateFlow(State.Loading) + val state: StateFlow = _state private val _selectedTaskType = MutableStateFlow(null) val selectedTaskType: StateFlow = _selectedTaskType @@ -50,15 +58,6 @@ class AssistantViewModel(private val repository: AssistantRepositoryType) : View private val _filteredTaskList = MutableStateFlow?>(null) val filteredTaskList: StateFlow?> = _filteredTaskList - private val _loading = MutableStateFlow(true) - val loading: StateFlow = _loading - - private val _isTaskCreated = MutableStateFlow(null) - val isTaskCreated: StateFlow = _isTaskCreated - - private val _isTaskDeleted = MutableStateFlow(null) - val isTaskDeleted: StateFlow = _isTaskDeleted - init { getTaskTypes() getTaskList() @@ -71,8 +70,14 @@ class AssistantViewModel(private val repository: AssistantRepositoryType) : View viewModelScope.launch(Dispatchers.IO) { val result = repository.createTask(input, type) - _isTaskCreated.update { - result.isSuccess + val messageId = if (result.isSuccess) { + R.string.assistant_screen_task_create_success_message + } else { + R.string.assistant_screen_task_create_fail_message + } + + _state.update { + State.TaskCreated(messageId) } } } @@ -100,8 +105,8 @@ class AssistantViewModel(private val repository: AssistantRepositoryType) : View result.first() } } else { - _errorMessage.update { - taskTypesResult.message + _state.update { + State.Error(R.string.assistant_screen_task_types_error_state_message) } } } @@ -115,14 +120,14 @@ class AssistantViewModel(private val repository: AssistantRepositoryType) : View filterTaskList(_selectedTaskType.value?.id) - _loading.update { - false + _state.update { + State.Idle } onCompleted() } else { - _errorMessage.update { - result.message + _state.update { + State.Error(R.string.assistant_screen_task_list_error_state_message) } } } @@ -132,31 +137,25 @@ class AssistantViewModel(private val repository: AssistantRepositoryType) : View viewModelScope.launch(Dispatchers.IO) { val result = repository.deleteTask(id) - _isTaskDeleted.update { - if (result.isSuccess) { - removeTaskFromList(id) - } + val messageId = if (result.isSuccess) { + R.string.assistant_screen_task_delete_success_message + } else { + R.string.assistant_screen_task_delete_fail_message + } - result.isSuccess + _state.update { + State.TaskDeleted(messageId) + } + + if (result.isSuccess) { + removeTaskFromList(id) } } } - fun resetErrorState() { - _errorMessage.update { - null - } - } - - fun resetTaskDeletionState() { - _isTaskDeleted.update { - null - } - } - - fun resetTaskAddState() { - _isTaskCreated.update { - null + fun resetState() { + _state.update { + State.Idle } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 37714bfffe..d605dedb79 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -70,12 +70,9 @@ import kotlinx.coroutines.delay @OptIn(ExperimentalMaterial3Api::class) @Composable fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { - val loading by viewModel.loading.collectAsState() - val errorMessage by viewModel.errorMessage.collectAsState() + val state by viewModel.state.collectAsState() val selectedTaskType by viewModel.selectedTaskType.collectAsState() val filteredTaskList by viewModel.filteredTaskList.collectAsState() - val isTaskCreated by viewModel.isTaskCreated.collectAsState() - val isTaskDeleted by viewModel.isTaskDeleted.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() var showAddTaskAlertDialog by remember { mutableStateOf(false) } var showDeleteTaskAlertDialog by remember { mutableStateOf(false) } @@ -95,7 +92,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { } Box(Modifier.nestedScroll(pullRefreshState.nestedScrollConnection)) { - if (loading || pullRefreshState.isRefreshing) { + if (state == AssistantViewModel.State.Loading || pullRefreshState.isRefreshing) { CenterText(text = stringResource(id = R.string.assistant_screen_loading)) } else { if (filteredTaskList.isNullOrEmpty()) { @@ -134,9 +131,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { } } - CheckErrorMessage(errorMessage, activity, viewModel) - CheckTaskAdd(isTaskCreated, activity, viewModel) - CheckTaskDeletion(isTaskDeleted, activity, viewModel) + ScreenState(state, activity, viewModel) if (showDeleteTaskAlertDialog) { taskIdToDeleted?.let { id -> @@ -167,50 +162,35 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { } @Composable -private fun CheckTaskAdd(isTaskCreated: Boolean?, activity: Activity, viewModel: AssistantViewModel) { - isTaskCreated?.let { - val messageId = if (it) { - R.string.assistant_screen_task_create_success_message - } else { - R.string.assistant_screen_task_create_fail_message +private fun ScreenState( + state: AssistantViewModel.State, + activity: Activity, + viewModel: AssistantViewModel +) { + val messageId: Int? = when (state) { + is AssistantViewModel.State.Error -> { + state.messageId } + is AssistantViewModel.State.TaskCreated -> { + state.messageId + } + + is AssistantViewModel.State.TaskDeleted -> { + state.messageId + } + else -> { + null + } + } + + messageId?.let { DisplayUtils.showSnackMessage( activity, stringResource(id = messageId) ) - viewModel.resetTaskAddState() - } -} - -@Composable -private fun CheckErrorMessage(errorMessage: String?, activity: Activity, viewModel: AssistantViewModel) { - errorMessage?.let { - DisplayUtils.showSnackMessage( - activity, - errorMessage - ) - - viewModel.resetErrorState() - } -} - -@Composable -private fun CheckTaskDeletion(isTaskDeleted: Boolean?, activity: Activity, viewModel: AssistantViewModel) { - isTaskDeleted?.let { - val messageId = if (it) { - R.string.assistant_screen_task_delete_success_message - } else { - R.string.assistant_screen_task_delete_fail_message - } - - DisplayUtils.showSnackMessage( - activity, - stringResource(id = messageId) - ) - - viewModel.resetTaskDeletionState() + viewModel.resetState() } } @@ -245,7 +225,7 @@ private fun AssistantContent( @Composable private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List?, viewModel: AssistantViewModel) { - val text = if (selectedTaskType?.name == stringResource(id = R.string.assistant_screen_all_task_type)) { + val text = if (selectedTaskType?.name == stringResource(id = R.string.assistant_screen_all_task_type)) { stringResource(id = R.string.assistant_screen_no_task_available_for_all_task_filter_text) } else { stringResource( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 35265bf9d7..a20f4feb62 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,9 @@ Assistant + Cannot able to fetch task types, please check your internet connection. + Cannot able to fetch task list, please check your internet connection. + Assistant Task List are loading, please wait No task available. Select a task type to create a new task. From 69a450092480424f4a745e52bb7e6e952cf95ad6 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 10:54:18 +0100 Subject: [PATCH 096/102] Add Assistant under the notification in drawer for branded clients Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 10 +++++----- .../nextcloud/client/assistant/AsssistantScreen.kt | 3 ++- .../assistant/component/AddTaskAlertDialog.kt | 2 +- .../nextcloud/client/database/NextcloudDatabase.kt | 2 +- .../ui/composeActivity/ComposeActivity.kt | 14 ++++++++++++++ .../android/ui/activity/DrawerActivity.java | 9 ++++++--- .../com/owncloud/android/utils/DrawerMenuUtil.java | 6 ++++++ app/src/main/res/layout/drawer_header.xml | 2 +- app/src/main/res/menu/partial_drawer_entries.xml | 8 ++++++++ 9 files changed, 44 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index 2a0d7c8428..c9b75829ad 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -37,11 +37,11 @@ import kotlinx.coroutines.launch class AssistantViewModel(private val repository: AssistantRepositoryType) : ViewModel() { sealed class State { - data object Idle: State() - data object Loading: State() - data class Error(val messageId: Int): State() - data class TaskCreated(val messageId: Int): State() - data class TaskDeleted(val messageId: Int): State() + data object Idle : State() + data object Loading : State() + data class Error(val messageId: Int) : State() + data class TaskCreated(val messageId: Int) : State() + data class TaskDeleted(val messageId: Int) : State() } private val _state = MutableStateFlow(State.Loading) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index d605dedb79..4204d3594a 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -153,7 +153,8 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { taskType.id?.let { viewModel.createTask(input = input, type = it) } - }, dismiss = { + }, + dismiss = { showAddTaskAlertDialog = false } ) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index a8dd78b66e..4933ee8deb 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -61,7 +61,7 @@ fun AddTaskAlertDialog(title: String?, description: String?, addTask: (String) - value = input, onValueChange = { input = it - }, + } ) } ) diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index 32f54a775d..fc051c483e 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -72,7 +72,7 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 75, to = 76), AutoMigration(from = 76, to = 77), AutoMigration(from = 77, to = 78), - AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), + AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class) ], exportSchema = true ) diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index 008312b7c6..97ea319a68 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -49,10 +49,12 @@ import kotlinx.coroutines.withContext class ComposeActivity : DrawerActivity() { lateinit var binding: ActivityComposeBinding + private var menuItemId: Int? = null companion object { const val DESTINATION = "DESTINATION" const val TITLE = "TITLE" + const val MENU_ITEM = "MENU_ITEM" } override fun onCreate(savedInstanceState: Bundle?) { @@ -62,10 +64,15 @@ class ComposeActivity : DrawerActivity() { val destination = intent.getSerializableArgument(DESTINATION, ComposeDestination::class.java) val titleId = intent.getIntExtra(TITLE, R.string.empty) + menuItemId = intent.getIntExtra(MENU_ITEM, -1) setupToolbar() updateActionBarTitleAndHomeButtonByString(getString(titleId)) + if (menuItemId != -1) { + setupDrawer(menuItemId!!) + } + binding.composeView.setContent { MaterialTheme( colorScheme = viewThemeUtils.getColorScheme(this), @@ -76,6 +83,13 @@ class ComposeActivity : DrawerActivity() { } } + override fun onResume() { + super.onResume() + if (menuItemId != -1) { + setDrawerMenuItemChecked(R.id.nav_assistant) + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 5257411942..d28c2011bc 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -369,7 +369,7 @@ public abstract class DrawerActivity extends ToolbarActivity views[0].setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); views[1].setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); views[2].setOnClickListener(v -> openAppStore("Nextcloud", true)); - views[3].setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title)); + views[3].setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1)); int iconColor; if (Hct.fromInt(primaryColor).getTone() < 80.0) { @@ -471,7 +471,7 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); - + DrawerMenuUtil.filterAssistantMenuItem(menu, capability); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, @@ -539,6 +539,8 @@ public abstract class DrawerActivity extends ToolbarActivity startSharedSearch(menuItem); } else if (itemId == R.id.nav_recently_modified) { startRecentlyModifiedSearch(menuItem); + } else if (itemId == R.id.nav_assistant) { + startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, itemId); } else if (itemId == R.id.nav_groupfolders) { MainApp.showOnlyFilesOnDevice(false); Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); @@ -557,10 +559,11 @@ public abstract class DrawerActivity extends ToolbarActivity } } - private void startComposeActivity(ComposeDestination destination, int titleId) { + private void startComposeActivity(ComposeDestination destination, int titleId, int menuItemId) { Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class); composeActivity.putExtra(ComposeActivity.DESTINATION, destination); composeActivity.putExtra(ComposeActivity.TITLE, titleId); + composeActivity.putExtra(ComposeActivity.MENU_ITEM, menuItemId); startActivity(composeActivity); } diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index 8ac2280a87..a09099800e 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -64,6 +64,12 @@ public final class DrawerMenuUtil { } } + public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability) { + if (capability != null && !capability.getAssistant().isTrue()) { + filterMenuItems(menu, R.id.nav_assistant); + } + } + public static void filterGroupfoldersMenuItem(Menu menu, @Nullable OCCapability capability) { if (capability != null && !capability.getGroupfolders().isTrue()) { filterMenuItems(menu, R.id.nav_groupfolders); diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml index f4f07bf5c6..0113835777 100644 --- a/app/src/main/res/layout/drawer_header.xml +++ b/app/src/main/res/layout/drawer_header.xml @@ -74,7 +74,7 @@ + + + + Date: Wed, 13 Mar 2024 11:31:03 +0100 Subject: [PATCH 097/102] Check capability Signed-off-by: alperozturk --- .../com/owncloud/android/ui/activity/DrawerActivity.java | 8 +++++++- .../java/com/owncloud/android/utils/DrawerMenuUtil.java | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index d28c2011bc..4c3611e748 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -366,6 +366,12 @@ public abstract class DrawerActivity extends ToolbarActivity ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant), }; + if (getCapabilities() != null && getCapabilities().getAssistant().isTrue() && !getResources().getBoolean(R.bool.is_branded_client)) { + views[3].setVisibility(View.VISIBLE); + } else { + views[3].setVisibility(View.GONE); + } + views[0].setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); views[1].setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); views[2].setOnClickListener(v -> openAppStore("Nextcloud", true)); @@ -471,7 +477,7 @@ public abstract class DrawerActivity extends ToolbarActivity DrawerMenuUtil.filterTrashbinMenuItem(menu, capability); DrawerMenuUtil.filterActivityMenuItem(menu, capability); DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability); - DrawerMenuUtil.filterAssistantMenuItem(menu, capability); + DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources()); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index a09099800e..8c2414b14a 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -64,8 +64,8 @@ public final class DrawerMenuUtil { } } - public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability) { - if (capability != null && !capability.getAssistant().isTrue()) { + public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources) { + if (capability != null && !capability.getAssistant().isTrue() && resources.getBoolean(R.bool.is_branded_client)) { filterMenuItems(menu, R.id.nav_assistant); } } From 955639215da0054da1722bf5704dd810d38ab1fd Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 12:43:52 +0100 Subject: [PATCH 098/102] Fix condition Signed-off-by: alperozturk --- .../java/com/owncloud/android/ui/activity/DrawerActivity.java | 2 +- .../main/java/com/owncloud/android/utils/DrawerMenuUtil.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 4c3611e748..158e05953c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -366,7 +366,7 @@ public abstract class DrawerActivity extends ToolbarActivity ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant), }; - if (getCapabilities() != null && getCapabilities().getAssistant().isTrue() && !getResources().getBoolean(R.bool.is_branded_client)) { + if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) { views[3].setVisibility(View.VISIBLE); } else { views[3].setVisibility(View.GONE); diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index 8c2414b14a..96bd6cd7f4 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -65,7 +65,8 @@ public final class DrawerMenuUtil { } public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources) { - if (capability != null && !capability.getAssistant().isTrue() && resources.getBoolean(R.bool.is_branded_client)) { + boolean showCondition = capability != null && capability.getAssistant().isTrue() && !resources.getBoolean(R.bool.is_branded_client); + if (!showCondition) { filterMenuItems(menu, R.id.nav_assistant); } } From d5fded1cf0e287536d121d5b36b9bf7e186d4e2e Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 13 Mar 2024 13:14:15 +0100 Subject: [PATCH 099/102] Reduce spotbugs Signed-off-by: alperozturk --- .../android/ui/activity/DrawerActivity.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 158e05953c..7205a25403 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -125,6 +125,7 @@ import org.greenrobot.eventbus.ThreadMode; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.inject.Inject; @@ -359,23 +360,22 @@ public abstract class DrawerActivity extends ToolbarActivity if (getResources().getBoolean(R.bool.is_branded_client) || !preferences.isShowEcosystemApps()) { ecosystemApps.setVisibility(View.GONE); } else { - LinearLayout[] views = { - ecosystemApps.findViewById(R.id.drawer_ecosystem_notes), - ecosystemApps.findViewById(R.id.drawer_ecosystem_talk), - ecosystemApps.findViewById(R.id.drawer_ecosystem_more), - ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant), - }; + LinearLayout notesView = ecosystemApps.findViewById(R.id.drawer_ecosystem_notes); + LinearLayout talkView = ecosystemApps.findViewById(R.id.drawer_ecosystem_talk); + LinearLayout moreView = ecosystemApps.findViewById(R.id.drawer_ecosystem_more); + LinearLayout assistantView = ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant); + notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); + talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); + moreView.setOnClickListener(v -> openAppStore("Nextcloud", true)); + assistantView.setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1)); if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) { - views[3].setVisibility(View.VISIBLE); + assistantView.setVisibility(View.VISIBLE); } else { - views[3].setVisibility(View.GONE); + assistantView.setVisibility(View.GONE); } - views[0].setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); - views[1].setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); - views[2].setOnClickListener(v -> openAppStore("Nextcloud", true)); - views[3].setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1)); + List views = Arrays.asList(notesView, talkView, moreView, assistantView); int iconColor; if (Hct.fromInt(primaryColor).getTone() < 80.0) { @@ -383,6 +383,7 @@ public abstract class DrawerActivity extends ToolbarActivity } else { iconColor = getColor(R.color.grey_800_transparent); } + for (LinearLayout view : views) { ImageView imageView = (ImageView) view.getChildAt(0); imageView.setImageTintList(ColorStateList.valueOf(iconColor)); From 0cd84acbf551e62f95c0b0a5ed2bf4aacab30a6b Mon Sep 17 00:00:00 2001 From: nextcloud-android-bot Date: Wed, 13 Mar 2024 17:44:04 +0000 Subject: [PATCH 100/102] =?UTF-8?q?=F0=9F=94=84=20synced=20local=20'.githu?= =?UTF-8?q?b/workflows/'=20with=20remote=20'config/workflows/'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nextcloud-android-bot --- .github/workflows/analysis.yml | 2 +- .github/workflows/codeql.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 403d94a6f7..7c97ceb1cb 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -49,7 +49,7 @@ jobs: repository: ${{ steps.get-vars.outputs.repo }} ref: ${{ steps.get-vars.outputs.branch }} - name: Set up JDK 17 - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 231a62a54a..6f2a3b6cbc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -43,7 +43,7 @@ jobs: with: languages: ${{ matrix.language }} - name: Set up JDK 17 - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 with: distribution: "temurin" java-version: 17 From ac809af68ede3ff025bd50b6e3646165c697639b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:21:06 +0000 Subject: [PATCH 101/102] Update actions/setup-java action to v4.2.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/assembleFlavors.yml | 2 +- .github/workflows/check.yml | 2 +- .github/workflows/detectWrongSettings.yml | 2 +- .github/workflows/qa.yml | 2 +- .github/workflows/screenShotTest.yml | 2 +- .github/workflows/unit-tests.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/assembleFlavors.yml b/.github/workflows/assembleFlavors.yml index d2d6f02f4c..d170e32f9d 100644 --- a/.github/workflows/assembleFlavors.yml +++ b/.github/workflows/assembleFlavors.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: set up JDK 17 - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 5ff5ea5d49..39f4deecc4 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up JDK 17 - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/detectWrongSettings.yml b/.github/workflows/detectWrongSettings.yml index 532c77e81e..9330450111 100644 --- a/.github/workflows/detectWrongSettings.yml +++ b/.github/workflows/detectWrongSettings.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up JDK 17 - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index ca436ccb3c..df6824fdaf 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 if: ${{ steps.check-secrets.outputs.ok == 'true' }} - name: set up JDK 17 - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 if: ${{ steps.check-secrets.outputs.ok == 'true' }} with: distribution: "temurin" diff --git a/.github/workflows/screenShotTest.yml b/.github/workflows/screenShotTest.yml index 5601da71dc..191a71dfb5 100644 --- a/.github/workflows/screenShotTest.yml +++ b/.github/workflows/screenShotTest.yml @@ -40,7 +40,7 @@ jobs: ~/.android/adb* key: avd-${{ matrix.api-level }} - - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + - uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a10b7cdd80..5adf1dcb8d 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up JDK 17 - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 # v4.2.0 with: distribution: "temurin" java-version: 17 From f67639b646c1e5d34bf28c26f6ad5eef5bea4954 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 14 Mar 2024 02:41:14 +0000 Subject: [PATCH 102/102] Fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-b+en+001/strings.xml | 2 + app/src/main/res/values-de/strings.xml | 2 + app/src/main/res/values-el/strings.xml | 2 + app/src/main/res/values-eo/strings.xml | 1 + app/src/main/res/values-es-rEC/strings.xml | 2 + app/src/main/res/values-es-rMX/strings.xml | 120 +++++++++++++++++++ app/src/main/res/values-es-rSV/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 2 + app/src/main/res/values-et-rEE/strings.xml | 1 + app/src/main/res/values-eu/strings.xml | 5 + app/src/main/res/values-fa/strings.xml | 5 + app/src/main/res/values-fi-rFI/strings.xml | 4 + app/src/main/res/values-fr/strings.xml | 5 + app/src/main/res/values-gl/strings.xml | 5 + app/src/main/res/values-hr/strings.xml | 3 + app/src/main/res/values-hu-rHU/strings.xml | 5 + app/src/main/res/values-in/strings.xml | 1 + app/src/main/res/values-is/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 3 + app/src/main/res/values-iw/strings.xml | 3 + app/src/main/res/values-ja-rJP/strings.xml | 3 + app/src/main/res/values-ka-rGE/strings.xml | 1 + app/src/main/res/values-ka/strings.xml | 3 + app/src/main/res/values-ko/strings.xml | 3 + app/src/main/res/values-lt-rLT/strings.xml | 3 + app/src/main/res/values-lv/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 3 + app/src/main/res/values-nb-rNO/strings.xml | 5 + app/src/main/res/values-nl/strings.xml | 3 + app/src/main/res/values-pl/strings.xml | 3 + app/src/main/res/values-pt-rBR/strings.xml | 5 + app/src/main/res/values-pt-rPT/strings.xml | 1 + app/src/main/res/values-ro/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 5 + app/src/main/res/values-sc/strings.xml | 3 + app/src/main/res/values-sk-rSK/strings.xml | 3 + app/src/main/res/values-sl/strings.xml | 3 + app/src/main/res/values-sq/strings.xml | 1 + app/src/main/res/values-sr/strings.xml | 5 + app/src/main/res/values-sv/strings.xml | 4 + app/src/main/res/values-th-rTH/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 5 + app/src/main/res/values-uk/strings.xml | 4 + app/src/main/res/values-vi/strings.xml | 1 + app/src/main/res/values-zh-rCN/strings.xml | 4 + app/src/main/res/values-zh-rHK/strings.xml | 5 + app/src/main/res/values-zh-rTW/strings.xml | 4 + 47 files changed, 256 insertions(+) diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index e0a4940f05..9484d01bdf 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -37,6 +37,8 @@ Allow resharing Shows one widget from dashboard Search in %s + Type some text + Show less Associated account not found! Access failed: %1$s The account is not added on this device yet diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1adbe69c4b..3e2f57f825 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -37,6 +37,8 @@ Weiterteilen erlauben Zeigt ein Widget aus dem Dashboard an Suche in %s + Bitte einen Text eingeben + Weniger anzeigen Verknüpftes Konto nicht gefunden! Zugriffsfehler: %1$s Das Konto ist bislang auf dem Gerät nicht vorhanden diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index ad3dc29248..d853fcf737 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -34,6 +34,8 @@ Επιτρέπεται ο επαναδιαμοιρασμός Εμφάνιση ενός γραφικού στοιχείου από τον πίνακα ελέγχου Αναζήτηση στο %s + Πληκτρολογήστε κάποιο κείμενο + Εμφάνιση λιγότερων Δεν βρέθηκε ο συνδεδεμένος λογαριασμός! Αποτυχία πρόσβασης: %1$s Ο λογαριασμός δεν υπάρχει ακόμα στη συσκευή diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 841c587f62..36263b939b 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -32,6 +32,7 @@ Detalaj agordoj Permesi rekunhavigon Serĉi en 1%s + Montri malpli Aliro malsukcesis: %1$s La konto ankoraŭ ne aldoniĝis al tiu ĉi aparato Konto pri samaj uzanto kaj servilo jam ekzistas tiuaparate diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml index 0d5c5cb1c3..79c2dcae22 100644 --- a/app/src/main/res/values-es-rEC/strings.xml +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -35,6 +35,8 @@ Permitir volver a compartir Muestra un widget del panel de control Compartir en %s + Tarea eliminada con éxito + Mostrar menos ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta aún no ha sido agregada a este dispositivo diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index b07e68d259..e67bd43f53 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -569,6 +569,7 @@ 2012/05/18 12:23 PM detener alternar + ¡Desactivar la comprobación de ahorro de energía podría resultar en la carga de archivos cuando la batería esté baja! Borrado mantenido en la carpeta original movido a la carpeta de la aplicación @@ -581,6 +582,7 @@ Agregar cuenta Sincronizar calendario y contactos No ha sido instalado ni F-Droid o Google Play + Configurar DAVx5 (antes conocido como DAVdroid) (v1.3.0+) para la cuenta actual Sincronización de calendario y contactos configurada Acerca de Detalles @@ -589,6 +591,7 @@ Más Respaldar diariamente el calendario y contactos Respaldo diario de tus contactos + Error inesperado al configurar DAVx5 (anteriormente conocido como DAVdroid) ¡El cifrado punto a punto está configurado! Mnemotécnico de E2E Para mostrar el mnemotécnico, por favor, habilite las credenciales del dispositivo. @@ -618,6 +621,7 @@ Eliminar el cifrado local Configurar el cifrado punto a punto Mostrar el conmutador de aplicaciones + Sugerencias de aplicaciones de Nextcloud en el encabezado de navegación Mostrar archivos ocultos Obtener el código fuente Carpeta de almacenamiento de datos @@ -683,6 +687,7 @@ Calendario y contactos Sincronizar con DAVx5 Error al obtener los resultados de búsqueda + Compartir en modo seguro no está configurado para este usuario Compartir de forma segura ... Seleccionar todo Establecer la carpeta multimedia @@ -696,6 +701,7 @@ Usar imagen como Establecer estado Establecer mensaje de estado + Durante la configuración del cifrado punto a punto, recibirá un mnemotécnico aleatorio de 12 palabras, que necesitará para abrir sus archivos en otros dispositivos. Esto sólo se almacenará en este dispositivo y se puede volver a mostrar en esta pantalla. ¡Por favor, guárdelo en un lugar seguro! Compartir Compartir y copiar enlace Compartiendo @@ -704,20 +710,29 @@ Compartir %1$s %1$s (grupo) Compartir enlace interno + El enlace compartido interno sólo funciona para usuarios con acceso a este archivo + El enlace compartido interno sólo funciona para usuarios con acceso a esta carpeta en %1$s Compartir liga Debes ingresar una contraseña + Ocurrió un error al intentar compartir este archivo o carpeta. + No se puede compartir. Por favor, verifique que el archivo exista. para compartir este archivo + Ingrese una contraseña opcional Ingresa una contraseña Compartir enlace (%1$s) Establece la fecha de expiración Establecer contraseña + No se permite volver a compartir durante la entrega segura de archivos Protegido con contraseña Puede editar + Entrega de archivos + Entrega de archivos segura Sólo lectura Permisos del recurso compartido %1$s (remoto) %1$s (conversación) + Nombre, identificador de nube federada o dirección de correo electrónico ... Enviar nuevo correo electrónico Nota al destinatario Ajustes @@ -726,6 +741,7 @@ Enviar enlace Desestablecer Compartir con… + Avatar del usuario compartido compartir compartido compartido mediante enlace @@ -734,6 +750,7 @@ Mostrar fotos Mostrar videos Registrarse con el proveedor + ¿Permitir que %1$s acceda a su cuenta de Nextcloud %2$s? Ordenar por Ocultar Detalles @@ -770,10 +787,23 @@ Películas Música Acceso completo + Sólo lectura de medios Imágenes + La plataforma de productividad auto alojada que le mantiene en control + La plataforma de productividad auto alojada que le mantiene en control (versión preliminar para desarrolladores) Transmitir con... + La transmisión interna no es posible + Por favor, descargue el medio o utilice una aplicación externa. + ¡Modo estricto: no se permiten conexiones HTTP! + Año/Mes/Día + Año/Mes + Año \"%1$s\" ha sido compartido contigo %1$s ha compartido \"%2$s\" contigo + Sólo fotos + Fotos y videos + Sólo videos + Sugerir Se encontraron conflictos La carpeta %1$s ya no existe No fue posible sincronizar %1$s @@ -782,14 +812,20 @@ Falla en la sincronización La sincronización falló, inicia sesión de nuevo Los contenidos del archivo ya han sido sincronizados + La sincronización de la carpeta %1$sno pudo ser completada A partir de la versión 1.3.16, los archivos cargados desde este dispositivo serán copiados a la carpeta local %1$s para prevenir pérdidas de datos cuando un archivo se sincroniza entre múltiples cuentas. \n\nDerivado de este cambio, todos los archivos cargados con versiones anteriores de la aplicación fueron copiados a la carpeta %2$s. Sin embargo, un error evitó que se completara esta operación durante la sincronización de la cuenta. Puedes dejar el(los) archivo(s) como está(n) y eliminar el enlace a %3$s, o bien, mover el(los) archivo(s) a la carpeta %1$s y mantener el enlace a %4$s.\n\nSe enlistan a continuación los archivos locales así como los archivos remotos en %5$s a donde estaban ligados Algunos archivos locales se han perdido Obteniendo la versión más reciente del archivo. + Elija qué sincronizar + Liberar espacio + %1$s tiene %2$s, pero sólo hay %3$s disponibles en el dispositivo. + No hay espacio suficiente Botón de status de sincronización Archivos Botón de configuración Configurar carpetas Las cargas instantáneas se han mejorado por completo. Re-configura tu carga automática desde el menú principal.\n\nDisfruta la nueva y mejorada carga automática. + No se encontraron carpetas de medios Para %1$s Tipo Sincronizado @@ -798,26 +834,42 @@ 30 minutos Esta semana Miniatura + Miniatura para el archivo existente + Miniatura para el nuevo archivo + La carga está tardando más de lo esperado Hoy Papelera No hay archivos eliminados Podrás recuperar los archivos eliminados desde aquí ¡El archivo %1$s no pudo ser eliminado! + ¡No se pudo restaurar el archivo %1$s! Borrar permanentemente + ¡No se pudo cargar la papelera de reciclaje! ¡Los archivos no pudieron ser eliminados permanentemente! Archivo desbloqueado + Existen comentarios sin leer Desestablecer encripción Eliminar de favoritos + Ocurrió un error al intentar dejar de compartir este archivo o carpeta. + No se pudo dejar de compartir. Por favor, verifique que el archivo exista. para dejar de compartir este archivo Se presentó una falla al dejar de compartir Accede a través de un dominio no de confianza. Por favor consulta la documentación para más información. + Ocurrió un error al tratar de actualizar el recurso compartido + No se pudo actualizar. Por favor, verifique que el archivo exista. para actualizar este recurso compartido Se presentó una falla al actualizar el elemento compartido + Limpiar cargas canceladas + Reanudar cargas canceladas Borrar cargas fallidas Reintentar cargas fallidas + Algunos archivos no existen, estos archivos no se pueden resumir + Pausar todas las cargas + Reanudar todas las cargas No se puede crear el archivo local Cargar forma… Cargar contenido de otras aplicaciones + Cargar desde la cámara Nombre de archivo Tipo de archivo Archivo de acceso directo a Google Maps(%s) @@ -825,11 +877,21 @@ Archivo snippet de texto(.txt) Ingresa el nombre y el tipo del archivo a cargar Cargar archivos + Todas las cargas están pausadas Botón de cargar elemento Eliminar No hay cargas disponibles Carga algún contenido o activa la carga automática + Resolver conflicto + Almacenamiento local lleno + No se pudo copiar el archivo al almacenamiento local + No se pudo bloquear la carpeta + La carga fue cancelada por el usuario + El cifrado sólo es posible con >= Android 5.0 El espacio insuficiente evita que se copien los archivos seleccionados dentro de la carpeta %1$s. ¿Te gustaría moverlos ahí en su lugar? + Se excedió la cuota de almacenamiento + Escanear documento desde la cámara + Conflicto de sincronización, por favor, resuélvalo manualmente Se presentó un error desconocido Seleccionar Cargar @@ -837,14 +899,21 @@ %1$s no tiene permitido leer un archivo recibido No fue posible copiar el archivo a una carpeta temporal. Por favor intenta enviarlo de nuevo. El archivo seleccionado para cargar no fue encontrado. Por favor verifica si el archivo existe. + No se puede cargar este archivo No hay un archivo a cargar + Archivo no encontrado. ¿Está seguro que este archivo existe o tiene un conflicto previo que no ha sido resuelto? + No se pudo ubicar el archivo en el servidor. Otro usuario pudo haber eliminado el archivo Nombre de la carpeta + Reintentar la carga de archivos locales fallidos Selecciona la carpeta de cargas No fue posible cargar %1$s La carga falló, inicia sesión de nuevo + Conflicto de carga del archivo + Elegir qué versión mantener de %1$s Falla en la carga Opción de carga: Mover el archivo a la carpeta %1$s + la carpeta de origen es de sólo lectura; el archivo solo se cargará Mantener el archivo en la carpeta de origen Borrar el archivo de la carpeta de origen para cargar a esta carpeta @@ -871,12 +940,15 @@ No se encontró el archivo local Error de permisos El certificado del servidor no es de confianza + Obteniendo la versión del servidor... La aplicación ha sido terminada Completado + Se encontró el mismo archivo en el remoto, omitiendo carga Error desconocido Virus detectado. ¡La carga no puede ser completada! Esperando a salir de modo de conservación de energía Aguardando la regarga del dispositivo + Esperando un Wi-Fi de uso no medido Usuario Dirección Correo electrónico @@ -888,15 +960,53 @@ Agrega tu nombre, una imagen y detalles de contacto en tu página de perfil. Usuario Descargar + Ícono de superposición de video Aguarda un momento… Verificando credenciales almacenadas Copiando el archivo desde almacenamiento privado + Por favor, actualice la aplicación WebView del sistema de Android para iniciar sesión Actualizar + Actualice la aplicación WebView del sistema de Android Imagen de qué es nuevo Omitir Nuevo en %1$s + ¿Cuál es su estado? + Los widgets sólo están disponibles para %1$s 25 o superior + No disponible Descargando archivos… Enviar correo electrónico + ¡La carpeta de almacenamiento de datos no existe! + Esto puede deberse a una restauración de un respaldo en otro dispositivo. Se volverá a la configuración predeterminada. Por favor, compruebe la configuración para ajustar la carpeta de almacenamiento de datos. + + No se pudo sincronizar %1$d archivo (conflictos: %2$d) + No se pudieron sincronizar %1$d archivos (conflictos: %2$d) + No se pudieron sincronizar %1$d archivos (conflictos: %2$d) + + + No se pudo copiar %1$d archivo de la carpeta %2$s en + No se pudieron copiar %1$d archivos de la carpeta %2$s en + No se pudieron copiar %1$d archivos de la carpeta %2$s en + + + Se escribió %1$d evento en %2$s + Se escribieron %1$d eventos en %2$s + Se escribieron %1$d eventos en %2$s + + + Se creó %1$d nuevos UID + Se crearon %1$d nuevos UIDs + Se crearon %1$d nuevos UIDs + + + Se procesó %d entrada. + Se procesaron %d entradas. + Se procesaron %d entradas. + + + Se encontró %d entrada duplicada. + Se encontraron %d entradas duplicadas. + Se encontraron %d entradas duplicadas. + %d archivo exportado %d archivos exportados @@ -907,6 +1017,16 @@ No se pudieron exportar %d archivos No se pudieron exportar %d archivos + + Se exportó %d archivo, se omitió el resto debido a un error + Se exportaron %d archivos, se omitió el resto debido a un error + Se exportaron %d archivos, se omitió el resto debido a un error + + + %d archivo se exportará. Vea la notificación para más detalles. + %d archivos se exportarán. Vea la notificación para más detalles. + %d archivos se exportarán. Vea la notificación para más detalles. + %1$d carpeta %1$d carpetas diff --git a/app/src/main/res/values-es-rSV/strings.xml b/app/src/main/res/values-es-rSV/strings.xml index e02ad5de95..2f28e9387d 100644 --- a/app/src/main/res/values-es-rSV/strings.xml +++ b/app/src/main/res/values-es-rSV/strings.xml @@ -24,6 +24,7 @@ Actividad Agregar a %1$s Permitir volver a compartir + Todos La cuenta aún no ha sido agregada a este dispositivo Ya existe una cuenta en el dispositivo para el mismo usuario y servidor El usuario ingresado no corresponde con el usuario de esta cuenta diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3ed5e6d986..a93726025f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -36,6 +36,8 @@ Permitir que se vuelva a compartir Muestra un widget del tablero Buscar en %s + Escriba algo de texto + Ver menos ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta no se ha añadido aún en este dispositivo diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 696d219e6a..13d2c8aa1f 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -30,6 +30,7 @@ Lisa veel üks link Täpsemad seaded Luba edasijagamine + Kõik Seotud kontot ei leitud! Ligipääs ebaõnnestus: %1$s Seda kontot pole veel sellesse seadmesse lisatud diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 136305e2d1..e82e3291bc 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -37,6 +37,11 @@ Baimendu birpartekatzea Paneleko trepeta bat erakusten du Bilatu %s(e)n + Denak + Idatzi testu bat + Zeregina ongi ezabatu da + Erakutsi gutxiago + Erakutsi gehiago Ez da aurkitu lotutako konturik Huts egin du atzitzean: %1$s Kontua ez da gailu honetan gehitu oraindik diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 0e55bc0eac..c92e7569da 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -37,6 +37,11 @@ مجاز به اشتراک گذاری مجدد نمایش یک ابزارک از پیشخوان جستجو در %s + همه + مقداری متن را تایپ کنید + Task successfully deleted + Show less + Show more حساب مرتبط یافت نشد! دسترسی خطای %1$s حساب به این وسیله اضافه نشده است diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index fb8d5dea70..4e5550ca97 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -34,6 +34,10 @@ Salli uudelleenjakaminen Näyttää yhden pienoissovelluksen konsolista Etsi kohteesta %s + Kaikki + Tehtävä poistettu + Näytä vähemmän + Näytä enemmän Liitettyä tiliä ei löydy! Pääsy epäonnistui: %1$s Tälle laitteelle ei ole vielä asennettu tiliä. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2b1791c928..6da1af3662 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -37,6 +37,11 @@ Autoriser le repartage Affiche un widget du tableau de bord Recherche dans %s + Tout + Tapez du texte + Tâche supprimée avec succès + Afficher moins + Afficher plus Compte associé introuvable ! L\'accès a échoué: %1$s Le compte n\'est pas encore ajouté sur cet appareil diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 6589bce67c..f0befa3d27 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -36,6 +36,11 @@ Permitir compartir Amosa un trebello do taboleiro Buscar en %s + Todo + Escriba algún texto + A tarefa foi eliminada satisfactoriamente + Amosar menos + Amosar máis Non se atopou unha conta asociada! Acceso fallado: %1$s A conta aínda non existe no dispositivo diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 1f0b13c78e..f0c252790f 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -33,6 +33,9 @@ Napredne postavke Dopusti ponovno dijeljenje Traži u %s + Sve + Prikaži manje + Prikaži više Pripadajući račun nije pronađen! Neuspjeli pristup: %1$s Račun još nije dodan na ovaj uređaj diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index f730a61d45..0f1c874c53 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -36,6 +36,11 @@ Újra megosztás engedélyezése Egy modult jelenít meg a irányítópultról Keresés itt: %s + Összes + Gépeljen be szöveget + Feladat sikeresen törölve + Kevesebb megjelenítése + Több megjelenítése A kapcsolódó fiók nem található! Hozzáférési hiba: %1$s Az eszközön még nem létezik a fiók diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 4e41cf4469..ab55a9234b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -34,6 +34,7 @@ Pengaturan Tambahan Izinkan pembagian ulang Cari dalam %s + Semua Akun terkait tidak ditemukan! Akses gagal: %1$s Akun ini belum ditambahkan ke perangkat ini diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 3ce1e170ff..cf3b0ca354 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -35,6 +35,7 @@ Leyfa endurdeilingu Sýnir einn viðmótshluta af stjórnborði Leita í %s + Allt Tengdur notandaaðgangur fannst ekki! Aðgangur mistókst: %1$s Aðgangur er ekki ennþá til á tækinu diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 206b1e7cd2..ee31dcc257 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -36,6 +36,9 @@ Consenti la ri-condivisione Mostra un widget dal cruscotto Cerca in %s + Tutti + Mostra meno + Mostra più Account associato non trovato. Accesso non riuscito: %1$s L\'account non è ancora aggiunto su questo dispositivo diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index e71591ced1..213145c792 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -34,6 +34,9 @@ לאפשר שיתוף מחדש מצג וידג׳ט אחד מלוח הבקרה חפש ב %s + הכול + להציג פחות + להציג יותר לא נמצא חשבון משויך! גישה נכשלה: %1$s החשבון לא נוסף למכשיר הזה עדיין diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index aadbacb8d3..0a97210bf7 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -37,6 +37,9 @@ 再共有を許可する ダッシュボードから一つのウィジェットを表示 %s の中を検索 + すべて + 表示を減らす + 表示を増やす 関連付けられたアカウントが見つかりません! アクセスに失敗しました: %1$s このアカウントはまだこのデバイスに追加されていません diff --git a/app/src/main/res/values-ka-rGE/strings.xml b/app/src/main/res/values-ka-rGE/strings.xml index 57c88f6152..01b6281dcc 100644 --- a/app/src/main/res/values-ka-rGE/strings.xml +++ b/app/src/main/res/values-ka-rGE/strings.xml @@ -23,6 +23,7 @@ გააგზავნეთ ბმული… აქტივობა ხელახალი გაზიარების დაშვება + ყველა ანგარიში ამ მოწყობილობაზე ჯერ არაა დამატებული მოწყობილობაზე ანგარიში ამ მომხმარებლით და სერვერით უკვე არსებობს შეყვანილი მომხმარებელი არ ემთხვევა ამ ანგარიშის მომხმარებელს diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index f234893ab8..fb05656348 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -36,6 +36,9 @@ Allow resharing Shows one widget from dashboard Search in %s + All + Show less + Show more Associated account not found! Access failed: %1$s The account is not added on this device yet diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 7ba0732b00..87aabbef6d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -36,6 +36,9 @@ 다시 공유 허용 대시보드에 위젯 하나만 표시 %s에서 검색 + 모두 + 적게 보기 + 더 보기 관련 계정을 찾을 수 없습니다! 접근 실패: %1$s 이 장치에 아직 계정이 추가되지 않았음 diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index af6abf3198..ef5816ac92 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -33,6 +33,9 @@ Išplėstiniai nustatymai Leisti bendrinti iš naujo Ieškoti %s + Visos + Rodyti mažiau + Rodyti daugiau Susieta paskyra nerasta! Prieiga nepavyko: %1$s Paskyra kol kas šiame įrenginyje nėra pridėta diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 789e97e7a6..617ab172ca 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -30,6 +30,7 @@ Pievienot %1$s Paplašināti iestatījumi Atļaut atkārtotu koplietošanu + Visi Saistītais konts nav atrasts! Piekļuve neizdevās: %1$s Konts vēl nav pievienots šai iekārtai diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 00fa6997b9..66612c9d9c 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -34,6 +34,9 @@ Дозволи повторно споделување Прикажува еден виџет од контролната табла Барај во %s + Сите + Помалку + Прикажи повеќе Не е пронајдена поврзана сметка! Неуспешен пристап: %1$s Сметката сеуште не е додадена на овој уред diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index af169cec12..d00b07103b 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -37,6 +37,11 @@ TIllat videre deling Viser en widget fra dashbordet Søk i %s + Alle + Skriv inn litt tekst + Oppgaven er slettet + Vis mindre + Vis mer Tilknyttet bruker ikke funnet! Tilgang mislyktes: %1$s Kontoen er ikke lagt til på denne enheten enda diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 555a492d35..d9eba6efcc 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -35,6 +35,9 @@ Opnieuw delen toestaan Toont één widget van dashboard Zoeken in %s + Alle + Toon minder + Toon meer Bijbehorend account niet gevonden! Toegang mislukt: %1$s Dit account is nog niet toegevoegd op dit apparaat diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index b6d558f562..78f5723b3d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -36,6 +36,9 @@ Zezwalaj na udostępnianie dalej Pokazuje jeden widżet z pulpitu nawigacyjnego Szukaj w %s + Wszystkie + Pokaż mniej + Pokaż więcej Nie znaleziono powiązanego konta! Dostęp nieudany: %1$s Konto nie jest jeszcze dodane na tym urządzeniu diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 8a7318628f..2dbd67058d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -37,6 +37,11 @@ Permitir recompartilhamento Mostra um widget do painel Pesquisar em %s + Tudos + Digite algum texto + Tarefa excluída com sucesso + Mostrar menos + Mostrar mais Conta associada não encontrada! O acesso falhou: %1$s A conta ainda não está adicionada neste dispositivo diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 93a9475b0b..6718f48701 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -35,6 +35,7 @@ Permitir repartilha Mostra um \'\'widget\'\' do painel Procurar em %s + Todos Conta associada não encontrada! Acesso falhou: %1$s A conta ainda não foi adicionada a este dispositivo diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 30901dbe7a..b429ca2557 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -35,6 +35,7 @@ Permite repartajarea Arată un singur widget din panoul principal Caută in %s + Toate Contul asociat nu a fost găsit! Accesul a eșuat: %1$s Contul nu există încă pe dispozitiv diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index b532a6bf8c..7582dee47c 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -37,6 +37,11 @@ Разрешить повторную публикацию Показывает один виджет с главного экрана. Искать в %s + Все + Наберите какой-то текст + Задача удалена + Показывать меньше + Показывать больше Связанный аккаунт не найден! Доступ запрещен: %1$s Учётная запись ещё не создана на этом устройстве diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 248a23964c..363579d543 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -33,6 +33,9 @@ Cunfiguratziones avantzadas Permite sa re-cumpartzidura Chirca in %s + Totu + Mustra prus pagu + Mustra de prus Contu assotziadu no agatadu! Atzessu faddidu: %1$s Su contu non s\'agatat ancora in custu dispositivu diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 01bdc92a34..aac2a9d50f 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -37,6 +37,9 @@ Povoliť sprístupňovanie ďalej Zobrazí jeden widget z hlavného panela Hľadať v %s + Všetko + Zobraziť menej + Zobraziť viac Priradený účet sa nenašiel Prístup zamietnutý: %1$s Účet zatiaľ v zariadení neexistuje diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 2542f1d502..f7bbe06666 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -36,6 +36,9 @@ Dovoli nadaljnje omogočanje souporabe Pokaži en gradnik iz nadzorne plošče Poišči v %s + Vse + Pokaži manj + Pokaži več Povezanega računa ni mogoče najti! Dostop je spodletel: %1$s Račun še ni dodan na to napravo. diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 651b5fccb9..5850c46df2 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -30,6 +30,7 @@ Cilësime të Avancuara Lejo rindarje Kërkoni në %s + Të gjithë Llogaria e lidhur nuk u gjet! Hyrja dështoi: %1$s Kjo llogari nuk është shtuar në këtë paisje akoma diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 48d02127b5..c747549030 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -37,6 +37,11 @@ Дозволи дељење даље Приказује један виџет са контролне табле Тражи у %s + Све + Откуцајте неки текст + Задатак је успешно завршен + Прикажи мање + Прикажи више Придружени налог није нађен! Неуспешан приступ: %1$s Налог још није додат на овај уређај diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index dc9804ea77..c12664fcdd 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -37,6 +37,10 @@ Tillåt dela vidare Visa en widget från dashboard Sök i %s + Alla + Skriv någon text + Visa mindre + Visa mer Associerat konto kunde inte hittas! Åtkomst misslyckades: %1$s Kontot har ännu inte lagts till på den här enheten diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index 8762ad3cc9..f805615509 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -35,6 +35,7 @@ อนุญาตให้แชร์ซ้ำ แสดงหนึ่งวิดเจ็ตจากแดชบอร์ด ค้นหาใน %s + ทั้งหมด ไม่พบบัญชีที่เกี่ยวข้อง! การเข้าถึงล้มเหลว: %1$s บัญชียังไม่ถูกเพิ่มบนอุปกรณ์นี้ diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f9821bc6dd..c1e4212417 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -37,6 +37,11 @@ Yeniden paylaşılabilsin Panodan bir pano bileşeni görüntüler %s içinde arama + Tümü + Bir şeyler yazın + Görev silindi + Daha az ayrıntı + Daha çok ayrıntı İlişkili hesap bulunamadı! Erişilemedi: %1$s Aygıt üzerinde henüz bu hesap açılmamış diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 49603da41b..e8b18ffd1d 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -37,6 +37,10 @@ Дозволяти передавати у спільний доступ іншим Показувати один віджет з панелі віджетів Пошук у %s + Всі + Завдання успішно видалено + Показувати менше + Показати більше Пов\'язаний обліковий запис не знайдено! Доступ невдалий: %1$s Обліковий запис все ще не створено на цьому пристрої diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 731b7d45c4..5576a2af84 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -34,6 +34,7 @@ Cho phép chia sẻ lại Hiện thị lối tắt từ dashboard Tìm kiếm trong %s + Tất cả Không tìm thấy tài khoản được liên kết! Lỗi truy cập: %1$s Tài khoản chưa được thêm vào thiết bị này. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c1d21a5190..35e4c22184 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -37,6 +37,10 @@ 允许二次共享 显示仪表盘中的一个小工具 在 %s 中搜索 + 所有 + 输入一些文字 + 显示更少 + 显示更多 相关账号未找到! 访问已失败: %1$s 该账号尚未添加到此设备上 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index d2caed62c0..4d7072fecf 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -37,6 +37,11 @@ 允許轉貼分享 顯示儀表板中的一個小部件 %s內搜尋 + 全部 + 輸入一些文字 + 任務已刪除 + 顯示較少 + 顯示更多 無法找到連結賬戶 存取失敗:%1$s 此賬戶尚未加入到這個裝置 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index f43787f603..3c0fca5490 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -37,6 +37,10 @@ 允許轉貼分享 顯示儀表板中的一個小工具 %s內搜尋 + 全部 + 輸入一些文字 + 顯示較少 + 顯示更多 找不到相關的帳號! 存取失敗:%1$s 此帳號尚未加入到這個裝置