Pārlūkot izejas kodu

Merge pull request #13664 from nextcloud/bugfix/assistant-screen

Update Dependency & Fix UI - Assistant Screen
Tobias Kaminsky 7 mēneši atpakaļ
vecāks
revīzija
0801284f23

+ 3 - 3
app/build.gradle

@@ -269,12 +269,12 @@ dependencies {
     }
 
     // Jetpack Compose
-    implementation(platform("androidx.compose:compose-bom:2024.08.00"))
+    implementation(platform("androidx.compose:compose-bom:2024.09.02"))
     implementation("androidx.compose.ui:ui")
     implementation("androidx.compose.ui:ui-graphics")
     implementation("androidx.compose.material3:material3")
-    implementation("androidx.compose.ui:ui-tooling-preview:1.7.2")
-    debugImplementation 'androidx.compose.ui:ui-tooling:1.6.8'
+    debugImplementation("androidx.compose.ui:ui-tooling")
+    implementation("androidx.compose.ui:ui-tooling-preview")
 
     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

+ 12 - 3
app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt

@@ -49,6 +49,9 @@ class AssistantViewModel(
     private val _filteredTaskList = MutableStateFlow<List<Task>?>(null)
     val filteredTaskList: StateFlow<List<Task>?> = _filteredTaskList
 
+    private val _isRefreshing = MutableStateFlow(false)
+    val isRefreshing: StateFlow<Boolean> = _isRefreshing
+
     init {
         fetchTaskTypes()
         fetchTaskList()
@@ -104,8 +107,12 @@ class AssistantViewModel(
         }
     }
 
-    fun fetchTaskList(appId: String = "assistant", onCompleted: () -> Unit = {}) {
+    fun fetchTaskList(appId: String = "assistant") {
         viewModelScope.launch(Dispatchers.IO) {
+            _isRefreshing.update {
+                true
+            }
+
             val result = repository.getTaskList(appId)
             if (result.isSuccess) {
                 taskList = result.resultData.tasks
@@ -115,13 +122,15 @@ class AssistantViewModel(
                 _state.update {
                     State.Idle
                 }
-
-                onCompleted()
             } else {
                 _state.update {
                     State.Error(R.string.assistant_screen_task_list_error_state_message)
                 }
             }
+
+            _isRefreshing.update {
+                false
+            }
         }
     }
 

+ 21 - 17
app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt

@@ -25,26 +25,26 @@ 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.pullToRefresh
 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.rememberCoroutineScope
 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.taskTypes.TaskTypesRow
-import com.nextcloud.client.assistant.task.TaskView
 import com.nextcloud.client.assistant.repository.AssistantMockRepository
+import com.nextcloud.client.assistant.task.TaskView
+import com.nextcloud.client.assistant.taskTypes.TaskTypesRow
 import com.nextcloud.ui.composeActivity.ComposeActivity
 import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog
 import com.owncloud.android.R
@@ -52,6 +52,7 @@ 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
+import kotlinx.coroutines.launch
 import java.lang.ref.WeakReference
 
 @Suppress("LongMethod")
@@ -61,26 +62,26 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) {
     val state by viewModel.state.collectAsState()
     val selectedTaskType by viewModel.selectedTaskType.collectAsState()
     val filteredTaskList by viewModel.filteredTaskList.collectAsState()
+    val isRefreshing by viewModel.isRefreshing.collectAsState()
     val taskTypes by viewModel.taskTypes.collectAsState()
     var showAddTaskAlertDialog by remember { mutableStateOf(false) }
     var showDeleteTaskAlertDialog by remember { mutableStateOf(false) }
     var taskIdToDeleted: Long? by remember {
         mutableStateOf(null)
     }
+    val scope = rememberCoroutineScope()
     val pullRefreshState = rememberPullToRefreshState()
 
     @Suppress("MagicNumber")
-    if (pullRefreshState.isRefreshing) {
-        LaunchedEffect(true) {
-            delay(1500)
-            viewModel.fetchTaskList(onCompleted = {
-                pullRefreshState.endRefresh()
-            })
-        }
-    }
-
-    Box(Modifier.nestedScroll(pullRefreshState.nestedScrollConnection)) {
-        if (state == AssistantViewModel.State.Loading || pullRefreshState.isRefreshing) {
+    Box(
+        modifier = Modifier.pullToRefresh(isRefreshing, pullRefreshState, onRefresh = {
+            scope.launch {
+                delay(1500)
+                viewModel.fetchTaskList()
+            }
+        })
+    ) {
+        if (state == AssistantViewModel.State.Loading || isRefreshing) {
             CenterText(text = stringResource(id = R.string.assistant_screen_loading))
         } else {
             if (filteredTaskList.isNullOrEmpty()) {
@@ -99,10 +100,13 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) {
             }
         }
 
-        if (pullRefreshState.isRefreshing) {
+        if (isRefreshing) {
             LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
         } else {
-            LinearProgressIndicator(progress = { pullRefreshState.progress }, modifier = Modifier.fillMaxWidth())
+            LinearProgressIndicator(
+                progress = { pullRefreshState.distanceFraction },
+                modifier = Modifier.fillMaxWidth()
+            )
         }
 
         if (selectedTaskType?.name != stringResource(id = R.string.assistant_screen_all_task_type)) {

+ 3 - 1
app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt

@@ -9,6 +9,7 @@ package com.nextcloud.client.assistant.component
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
@@ -22,7 +23,8 @@ fun CenterText(text: String) {
         Text(
             text = text,
             fontSize = 18.sp,
-            textAlign = TextAlign.Center
+            textAlign = TextAlign.Center,
+            color = MaterialTheme.colorScheme.onPrimaryContainer
         )
     }
 }

+ 9 - 9
app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt

@@ -25,6 +25,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.ModalBottomSheet
 import androidx.compose.material3.Text
 import androidx.compose.material3.rememberModalBottomSheetState
@@ -34,7 +35,6 @@ 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.colorResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
@@ -54,7 +54,7 @@ fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
 
     ModalBottomSheet(
         modifier = Modifier.padding(top = 32.dp),
-        containerColor = Color.White,
+        containerColor = MaterialTheme.colorScheme.surface,
         onDismissRequest = { dismiss() },
         sheetState = sheetState
     ) {
@@ -91,7 +91,7 @@ fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
 
                 Column(
                     modifier = Modifier.fillMaxSize().background(
-                        color = colorResource(id = R.color.light_grey),
+                        color = MaterialTheme.colorScheme.secondaryContainer,
                         shape = RoundedCornerShape(8.dp)
                     ).padding(16.dp)
                 ) {
@@ -99,10 +99,10 @@ fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
                         text = if (showInput) {
                             task.input ?: ""
                         } else {
-                            task.output ?: ""
+                            task.output ?: stringResource(R.string.assistant_screen_task_output_empty_text)
                         },
                         fontSize = 12.sp,
-                        color = Color.Black,
+                        color = MaterialTheme.colorScheme.onPrimaryContainer,
                         modifier = Modifier
                             .animateContentSize(
                                 animationSpec = spring(
@@ -113,7 +113,7 @@ fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
                     )
                 }
 
-                TaskStatus(task, foregroundColor = Color.Black)
+                TaskStatus(task, foregroundColor = MaterialTheme.colorScheme.onPrimaryContainer)
 
                 Spacer(modifier = Modifier.height(32.dp))
             }
@@ -127,15 +127,15 @@ private fun TextInputSelectButton(modifier: Modifier, titleId: Int, highlightCon
         onClick = onClick,
         shape = RoundedCornerShape(8.dp),
         colors = if (highlightCondition) {
-            ButtonDefaults.buttonColors(containerColor = Color.White)
+            ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary)
         } else {
-            ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.light_grey))
+            ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
         },
         modifier = modifier
             .widthIn(min = 0.dp, max = 200.dp)
             .padding(horizontal = 4.dp)
     ) {
-        Text(text = stringResource(id = titleId), color = Color.Black)
+        Text(text = stringResource(id = titleId), color = MaterialTheme.colorScheme.surface)
     }
 }
 

+ 5 - 0
app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt

@@ -14,6 +14,7 @@ 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.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
@@ -41,6 +42,10 @@ fun SimpleAlertDialog(
     }
 
     AlertDialog(
+        containerColor = MaterialTheme.colorScheme.surface,
+        iconContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+        titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+        textContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
         onDismissRequest = { dismiss() },
         title = {
             Text(text = title)

+ 5 - 0
app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java

@@ -44,6 +44,7 @@ import androidx.annotation.NonNull;
 import androidx.annotation.StringRes;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.widget.AppCompatSpinner;
+import androidx.core.content.ContextCompat;
 
 /**
  * Base class providing toolbar registration functionality, see {@link #setupToolbar(boolean, boolean)}.
@@ -118,6 +119,10 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable
         MaterialButton menuButton = findViewById(R.id.toolbar_menu_button);
         MaterialTextView titleTextView = findViewById(R.id.toolbar_title);
         titleTextView.setText(title);
+
+        titleTextView.setTextColor(ContextCompat.getColor(this, R.color.foreground_highlight));
+        menuButton.setIconTint(ContextCompat.getColorStateList(this, R.color.foreground_highlight));
+
         toolbar.setVisibility(View.VISIBLE);
         menuButton.setOnClickListener(toggleDrawer);
     }

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -42,6 +42,7 @@
 
     <string name="assistant_screen_top_bar_title">Assistant</string>
     <string name="assistant_screen_loading">Task List are loading, please wait</string>
+    <string name="assistant_screen_task_output_empty_text">The task output isn’t ready yet.</string>
     <string name="assistant_screen_no_task_available_for_all_task_filter_text">No task available. Select a task type to create a new task.</string>
     <string name="assistant_screen_no_task_available_text">No task available for %s task type, you can create a new task from bottom right.</string>
     <string name="assistant_screen_delete_task_alert_dialog_title">Delete Task</string>

+ 5 - 0
gradle/verification-metadata.xml

@@ -860,6 +860,11 @@
             <sha256 value="8043e0a70d594c8e7bb9626aa23a3b3d6e550d17292718672262576d9e25a579" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+      <component group="androidx.compose" name="compose-bom" version="2024.09.02">
+         <artifact name="compose-bom-2024.09.02.pom">
+            <sha256 value="9b90a15e3f137f56fb7a7a7d680d3164eb425e0271b860fee092bba4307a8789" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
       <component group="androidx.compose.animation" name="animation-core" version="1.0.0-beta03">
          <artifact name="animation-core-1.0.0-beta03.aar">
             <sha256 value="4626086855eb6582dda9c3050c05bb56632f5a0308a3bb71a0f58def958602ca" origin="Generated by Gradle" reason="Artifact is not signed"/>