From c577a0550f5472b9cdf5bb794321661afefb9528 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 26 Feb 2024 16:18:30 +0100 Subject: [PATCH 01/98] 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 61ad9629e005..f3b7e0ba03d2 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 4f72a9387bbc..108a1010b3fe 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.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.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 000000000000..237c16cc017b --- /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 b2e2b89fb03c..c9d8d2b600fc 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 02/98] 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 4108fbdbfcaf..fe00f041b2a1 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 000000000000..e74f5d9128a8 --- /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 000000000000..8809b1b0c39b --- /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 000000000000..e56f66eaefb9 --- /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 f3b7e0ba03d2..f4f25e326627 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.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 @@ private void openAppOrStore(String packageName) { } /** - * 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 void openDrawer() { /** * 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 boolean isDrawerIndicatorAvailable() { 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 000000000000..7732bd7b7895 --- /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 3e40abb95234..8343eadac383 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 a80b22ce5cff..52c0aef1dbdb 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 03/98] 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 000000000000..54528682e745 --- /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 e74f5d9128a8..6cc8f5f8f1c4 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 e56f66eaefb9..16bbc35549d7 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 f4f25e326627..7ce479603a9b 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 04/98] 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 54528682e745..92ef9749a2c2 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 6cc8f5f8f1c4..f8ee5bb12acb 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 000000000000..d689c599c1ae --- /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 16bbc35549d7..db08e16a1eea 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 7ce479603a9b..307e7acc3148 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { .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 5c2438e2ad80..34030893f3ea 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 05/98] 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 000000000000..7d653ee06e76 --- /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 06/98] 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 7d653ee06e76..a935ca1ad1cb 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 343938e121c0..bcd43ccafba4 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 static void beforeAll() { @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 92ef9749a2c2..dc516804ac07 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 f8ee5bb12acb..7521a6dc5563 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 000000000000..c3a6d8f00f08 --- /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 000000000000..379052c9db88 --- /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 db08e16a1eea..225fa34239d4 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 108a1010b3fe..b9b2856a2ba5 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.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 07/98] 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 dc516804ac07..c0c40ba63c05 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 7521a6dc5563..3bc18af21c4f 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 c3a6d8f00f08..f23228738530 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 379052c9db88..e3f6c882c6ed 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 08/98] Add GetTaskListRemoteOperation Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 34 ++++++++++++------- .../client/assistant/AsssistantScreen.kt | 3 +- .../repository/AssistantRepository.kt | 13 ++++--- .../repository/AssistantRepositoryType.kt | 6 ++-- 4 files changed, 34 insertions(+), 22 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 c0c40ba63c05..dca8d11e39e2 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() { } } - /* - fun deleteTask(id: String) { + private fun getTaskTypes() { viewModelScope.launch(Dispatchers.IO) { - repository?.deleteTask(id) + val result = repository.getTaskTypes() + _taskTypes.update { + result + } } } - fun getTask(id: String) { + private fun getTaskList(appId: String = "assistant") { viewModelScope.launch(Dispatchers.IO) { - _task.update { - repository?.getTask(id) + val result = repository.getTaskList(appId) + + _taskList.update { + result } } } + /* + fun deleteTask(id: String) { + viewModelScope.launch(Dispatchers.IO) { + repository?.deleteTask(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 3bc18af21c4f..60a233f21f8d 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 f23228738530..df29df35d622 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 e3f6c882c6ed..2f5c1081ca99 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 09/98] 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 63c66a65e3f3..caad859bb62c 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 dca8d11e39e2..ac96ad264aa8 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 60a233f21f8d..7c29ca7f439a 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 000000000000..14caeba4ffdd --- /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 000000000000..7a849169fb15 --- /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 000000000000..dbc9b4345060 --- /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 d689c599c1ae..cf727b87d927 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 df90f84ca513..2f624a72d1b6 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 10/98] 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 ac96ad264aa8..2c2909dc9547 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 7c29ca7f439a..edb251bea666 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 000000000000..20c56d17e457 --- /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 dbc9b4345060..dd300be98616 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 307e7acc3148..dee377574007 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 2f624a72d1b6..812188830662 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 11/98] 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 2c2909dc9547..04adb3702281 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 edb251bea666..174de5c419c8 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 12/98] 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 174de5c419c8..b75b568bce75 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 13/98] 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 04adb3702281..31288edd72ce 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 b75b568bce75..a3599c6962ea 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 14caeba4ffdd..238ae83bfd90 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 dd300be98616..13be4ee26488 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 df29df35d622..a1198d846fca 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 2f5c1081ca99..8a21a8638a4f 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 cf727b87d927..fc47fe5307d2 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 000000000000..00c004e6df46 --- /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 812188830662..355901447651 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 14/98] 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 31288edd72ce..efe400310c87 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 a3599c6962ea..6d9db09f2407 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 15/98] 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 6d9db09f2407..117a32389c44 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 13be4ee26488..aaf7fd110da0 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 355901447651..f9c6cc9da153 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 16/98] 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 efe400310c87..7365aa5f11a3 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 117a32389c44..ceb593601b61 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 238ae83bfd90..aa10b9dbae17 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 7a849169fb15..2a86f9dd64c1 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 aaf7fd110da0..187de9b2a0cd 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 f9c6cc9da153..7d29fbd1c365 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 17/98] 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 7365aa5f11a3..b7f6faaae7d3 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 ceb593601b61..6136098f1c50 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 18/98] 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 b7f6faaae7d3..2967c17395f0 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 6136098f1c50..5a8171ad87f4 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 19/98] 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 2967c17395f0..3d18c6cd784b 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 5a8171ad87f4..a646d0817866 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 7d29fbd1c365..ad16d86ae014 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 20/98] 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 3d18c6cd784b..00516f02be8f 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 a646d0817866..9200a3fc7f9e 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 ad16d86ae014..016fe4bf8698 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 21/98] 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 8809b1b0c39b..c503a541a18f 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 225fa34239d4..67ce3890903b 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 dee377574007..1da2c5b464e7 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.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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { } } + 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 b9823f0fa1f5..74d32aefd0da 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 @@ private void showHomeSearchToolbar(String title, boolean isRoot) { 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 22/98] 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 34030893f3ea..0e7a78bade3c 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 23/98] 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 bcd43ccafba4..343938e121c0 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 static void beforeAll() { @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 24/98] 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 58e60e8e55b4..43fcf18f1bb0 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 0e7a78bade3c..34030893f3ea 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 25/98] 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 9200a3fc7f9e..2fde66d53e3e 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 67ce3890903b..7ec2a3e85f0d 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 1da2c5b464e7..afa4a7f85aa6 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 @@ protected void setupDrawer() { 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 26/98] 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 a935ca1ad1cb..dfdddc308d47 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 c0494a90d4c0..bc362900fa3e 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 00516f02be8f..e465676983c6 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 2fde66d53e3e..d6d9c975d449 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 aa10b9dbae17..ac019e259a7c 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 187de9b2a0cd..e92aa0630e47 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 a1198d846fca..ff7bc851b069 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 8a21a8638a4f..a89a92f6e87e 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 3918fc40060d..84e1f447fbd1 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.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 7ec2a3e85f0d..8c67f73196f8 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 c503a541a18f..9247f724db14 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 00c004e6df46..ef9038603c4e 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 28dc94c67f10..bc065e0fa032 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 afa4a7f85aa6..fdd1f442b54b 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.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 @@ protected void setupDrawer() { if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } - - showSortListGroup(true); - hideSearchText(false); } /** @@ -548,7 +545,7 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { } } - 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 74d32aefd0da..b9823f0fa1f5 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 @@ private void showHomeSearchToolbar(String title, boolean isRoot) { 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 000000000000..828596d59ad3 --- /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 016fe4bf8698..22c4b360a222 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 27/98] 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 e465676983c6..d9465763236e 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 d6d9c975d449..69e44ef6f6fb 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 22c4b360a222..fb5261c2a2a6 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 28/98] 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 5e9aad61c9ba..91280a3a9242 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 29/98] 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 69e44ef6f6fb..5d4c9520f338 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 e92aa0630e47..4069c57d059f 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 8c67f73196f8..6112fbe29624 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 fc47fe5307d2..d7f5ba350832 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 55cf725a006e..c4aa216d4df2 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 550297e3dfd2..26f66c8c4942 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 30/98] 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 dfdddc308d47..40c0ad2f0e6c 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 31/98] 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 40c0ad2f0e6c..857abf1b91d4 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 32/98] 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 857abf1b91d4..cea7fd5bcbc0 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 - - if (result == null) { - fail("Expected to get task list but found null") - } + val result = sut?.getTaskList("assistant") + assertTrue(result?.isSuccess == true) - 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 resultOfTaskList = sut?.getTaskList("assistant") + assertTrue(resultOfTaskList?.isSuccess == true) + + sleep(120) - val taskList = sut?.getTaskList("assistant")?.resultData?.tasks - val taskListCountBeforeDelete = taskList?.size + val taskList = resultOfTaskList?.resultData?.tasks - if (taskList.isNullOrEmpty()) { - fail("Expected to get task list but found null or empty list") - } + assert((taskList?.size ?: 0) > 0) - sut?.deleteTask(taskList!!.first().id) - assertTrue(taskListCountBeforeDelete == taskListCountBeforeDelete?.minus(1)) + 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 ee5515eb2d1b..9621972d2e72 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 @@ protected void longSleep() { } } + 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 33/98] 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 b9b2856a2ba5..108a1010b3fe 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.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 34/98] 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 000000000000..8809b1b0c39b --- /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 000000000000..e56f66eaefb9 --- /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 35/98] 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 e56f66eaefb9..16bbc35549d7 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 36/98] 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 000000000000..d689c599c1ae --- /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 16bbc35549d7..db08e16a1eea 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 37/98] 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 343938e121c0..bcd43ccafba4 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 static void beforeAll() { @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 db08e16a1eea..225fa34239d4 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 108a1010b3fe..b9b2856a2ba5 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.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 38/98] 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 d689c599c1ae..cf727b87d927 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 39/98] 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 cf727b87d927..000000000000 --- 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 40/98] 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 8809b1b0c39b..c503a541a18f 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 225fa34239d4..67ce3890903b 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 b9823f0fa1f5..74d32aefd0da 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 @@ private void showHomeSearchToolbar(String title, boolean isRoot) { 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 41/98] 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 34030893f3ea..0e7a78bade3c 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 42/98] 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 bcd43ccafba4..343938e121c0 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 static void beforeAll() { @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 43/98] 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 0e7a78bade3c..34030893f3ea 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 44/98] 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 67ce3890903b..7ec2a3e85f0d 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 fdd1f442b54b..2b8e3953a19c 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 @@ protected void setupDrawer() { 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 45/98] 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 c503a541a18f..000000000000 --- 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 7ec2a3e85f0d..000000000000 --- 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 2b8e3953a19c..fdd1f442b54b 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 @@ protected void setupDrawer() { 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 74d32aefd0da..b9823f0fa1f5 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 @@ private void showHomeSearchToolbar(String title, boolean isRoot) { 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 46/98] 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 5d4c9520f338..7b22b5a26f49 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 ac019e259a7c..14e71f39dee2 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 20c56d17e457..66eabe37d0dd 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 4069c57d059f..4c7da2284375 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 6112fbe29624..50ddeedd6138 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 d7f5ba350832..90bf05665224 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 ef9038603c4e..94bd1bf95cf7 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 000000000000..e069b2f9f9eb --- /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 47/98] 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 50ddeedd6138..7d91380a921e 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 48/98] 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 7d91380a921e..64c96d64e940 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 49/98] 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 64c96d64e940..e86588fa31e9 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 fdd1f442b54b..521e729416ad 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 50/98] 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 e86588fa31e9..8596d979f48a 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 51/98] 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 8596d979f48a..70294e560ffb 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 52/98] 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 fe00f041b2a1..3f308f78ffdd 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 53/98] 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 70294e560ffb..bfaf77476499 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 521e729416ad..e144f861cf9f 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 54/98] 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 4c7da2284375..03af5de8f7a0 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 bfaf77476499..78e478d655cd 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 90bf05665224..f55c40e06233 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 94bd1bf95cf7..35414aac8a6d 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 55/98] 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 03af5de8f7a0..875d7d5dbe41 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 35414aac8a6d..89d0f87eabe1 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 fb5261c2a2a6..edef6eb7696a 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 56/98] 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 43fcf18f1bb0..b0163b9470f8 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 34030893f3ea..5c2438e2ad80 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 57/98] 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 cea7fd5bcbc0..7bb5bf95ed0f 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 58/98] 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 cec7df13cb39..6e60f96aa4e0 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 59/98] 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 6e60f96aa4e0..5af996e74e93 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 60/98] 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 875d7d5dbe41..7f8d6b0cc502 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 78e478d655cd..573f11ce62b0 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 e069b2f9f9eb..000000000000 --- 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 61/98] 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 7b22b5a26f49..0eb57b8afc85 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 62/98] 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 5af996e74e93..b88e14c8f703 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 5c2438e2ad80..92f7c0215a29 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 63/98] 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 3f308f78ffdd..f9bc33210abb 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 d9465763236e..2a97e7589fcf 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 0eb57b8afc85..df9786edb683 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 7f8d6b0cc502..a94b5a86d7ec 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 573f11ce62b0..23ff8b5d314a 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 edef6eb7696a..24f983e212c5 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 64/98] 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 f9bc33210abb..44e38736600d 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 a94b5a86d7ec..1fd0116b0fec 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 23ff8b5d314a..573f11ce62b0 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 24f983e212c5..b875deb94689 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 92f7c0215a29..5c2438e2ad80 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 65/98] 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 44e38736600d..abfa60bbdb30 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 66/98] 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 caad859bb62c..60b453cca171 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 67/98] 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 01bdc92a34fb..32a141ac4103 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 68/98] 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 21af9f58d885..d1da7a4406fd 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 000000000000..63509f229fee --- /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 5a9a208b9420..63709be80fc8 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 3901ccbbbfa3..a63a84355e7e 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 @@ private ContentValues createContentValues(String accountName, OCCapability capab 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 @@ private OCCapability createCapabilityInstance(Cursor cursor) { 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 6d2f72438f32..8a7f47a47d21 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 @@ */ 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 @@ static public class ProviderTableMeta implements BaseColumns { 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 e144f861cf9f..3ba730fceaf1 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 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { 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 8ac2280a87d7..a09099800ec0 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 static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capa } } + 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 69/98] 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 b88e14c8f703..a0c05c591e4a 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 70/98] 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 d8a742995d8d..1b26035deb66 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 71/98] 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 7bb5bf95ed0f..42be5605cbd9 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 9621972d2e72..52669efd1833 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 static void beforeAll() { } 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 72/98] 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 77a72864ec39..17c3fc8a2f7f 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 void filedrop() throws Exception { 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 void filedrop() throws Exception { 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 73/98] 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 6ebeaaa05481..ad7220098503 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 60e536526f66..ca745eff11ed 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 ef8bfaaa985b..3840a18fbf2b 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 74/98] 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 75/98] 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 ca745eff11ed..c2acf20484a6 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 82ae5bea7a98..f3e35a1ba70f 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 76/98] 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 b98ce56d2452..fb54564eb134 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 a0c05c591e4a..6b78d34a024c 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 77/98] 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 ad7220098503..fe8ceb3c1287 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 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 3840a18fbf2b..b03b94a858fc 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 c4aa216d4df2..26b5ed8cdb58 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 f2732478ea70..f33ba3422f24 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 78/98] 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 2a97e7589fcf..3383df42bbc5 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 df9786edb683..b49f0db0c9c0 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 000000000000..c2e059ce424f --- /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 573f11ce62b0..8c812791ad5c 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 3ba730fceaf1..b4eba3b10c54 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 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { 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 79/98] 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 63509f229fee..000000000000 --- 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 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,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 fe8ceb3c1287..1c78c4106ef0 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 8a7f47a47d21..971b8f168282 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 @@ */ 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 b4eba3b10c54..3ba730fceaf1 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 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { 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 80/98] 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 616fc1bc9be5..d821fcc182cd 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 1c78c4106ef0..7afaf3bc17e7 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 b03b94a858fc..b664600ee54c 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 510a8b3ebf18..e5543c665f7c 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 c449c5085540..9efcf95a385a 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 @@ 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 26b5ed8cdb58..5200e01b7cba 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 2f316929809b..2b2ef95eb7a6 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.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 int getSectionCount() { } } + @VisibleForTesting + public void clear() { + filteredSyncFolderItems.clear(); + syncFolderItems.clear(); + } + public int getUnfilteredSectionCount() { if (syncFolderItems.size() > 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 ff99ef902406..f8921d60f94f 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 32a141ac4103..01bdc92a34fb 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 81/98] 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 82/98] 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 abfa60bbdb30..63d815dfdb0b 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 e33eed864575..f273523c6610 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 e5543c665f7c..579fc3067e99 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 83/98] 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 63d815dfdb0b..512659c6a8b8 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 84/98] 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 85/98] 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 af620133b1dc..e9f713979ed9 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 8da505303bd6..334ebeaabca8 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 86/98] 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 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 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 87/98] 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 d1da7a4406fd..61b9b1a823c2 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 000000000000..708ca00a730e --- /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 57560286dd25..f18aa7fc5579 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 971b8f168282..8a7f47a47d21 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 @@ */ 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 88/98] 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 f18aa7fc5579..32f54a775d77 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 89/98] 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 61b9b1a823c2..9ac5f2e87f7f 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 90/98] 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 b49f0db0c9c0..30a59a7b5a7e 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 14e71f39dee2..a8dd78b66e1d 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 91/98] 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 30a59a7b5a7e..c4388d3a480e 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 c2e059ce424f..a69ec393d219 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 b875deb94689..b82d8eb412cd 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 92/98] 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 8c812791ad5c..008312b7c6bd 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 3ba730fceaf1..525741194220 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 void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 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 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { } } - 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 a09099800ec0..8ac2280a87d7 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 static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capa } } - 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 8ec80c5da58f..f4f07bf5c650 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 93/98] 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 3383df42bbc5..63852523b012 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() + onCompleted() + } else { + _errorMessage.update { + result.message + } + } } } @@ -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 c4388d3a480e..37714bfffe77 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 94/98] Add Screen State Signed-off-by: alperozturk --- .../client/assistant/AssistantViewModel.kt | 75 +++++++++---------- .../client/assistant/AsssistantScreen.kt | 66 ++++++---------- app/src/main/res/values/strings.xml | 3 + 3 files changed, 63 insertions(+), 81 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 63852523b012..2a0d7c842865 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) - } - - result.isSuccess + val messageId = if (result.isSuccess) { + R.string.assistant_screen_task_delete_success_message + } else { + R.string.assistant_screen_task_delete_fail_message } - } - } - fun resetErrorState() { - _errorMessage.update { - null - } - } + _state.update { + State.TaskDeleted(messageId) + } - fun resetTaskDeletionState() { - _isTaskDeleted.update { - null + if (result.isSuccess) { + removeTaskFromList(id) + } } } - 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 37714bfffe77..d605dedb7987 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 } - 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() - } -} + is AssistantViewModel.State.TaskCreated -> { + state.messageId + } -@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 + is AssistantViewModel.State.TaskDeleted -> { + state.messageId } + else -> { + null + } + } + messageId?.let { 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 35265bf9d76b..a20f4feb62d8 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 95/98] 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 2a0d7c842865..c9b75829ade4 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 d605dedb7987..4204d3594a34 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 a8dd78b66e1d..4933ee8debc1 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 32f54a775d77..fc051c483ebf 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 008312b7c6bd..97ea319a68ec 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 525741194220..d28c2011bc54 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 void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 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 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { } } - 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 8ac2280a87d7..a09099800ec0 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 static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capa } } + 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 f4f07bf5c650..0113835777a6 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 96/98] 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 d28c2011bc54..4c3611e74868 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 void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 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 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { 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 a09099800ec0..8c2414b14a38 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 static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capa } } - 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 97/98] 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 4c3611e74868..158e05953c97 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 void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 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 8c2414b14a38..96bd6cd7f463 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 static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capa } 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 98/98] Reduce spotbugs Signed-off-by: alperozturk --- .../android/ui/activity/DrawerActivity.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 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 158e05953c97..7205a2540302 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 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 void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 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 void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { } else { iconColor = getColor(R.color.grey_800_transparent); } + for (LinearLayout view : views) { ImageView imageView = (ImageView) view.getChildAt(0); imageView.setImageTintList(ColorStateList.valueOf(iconColor));