浏览代码

Merge pull request #12880 from nextcloud/bugfix/assistant-ui-ux

Assistant Feature UI/UX Fixes
Tobias Kaminsky 1 年之前
父节点
当前提交
f050c340af

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

@@ -42,8 +42,8 @@ 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.taskTypes.TaskTypesRow
+import com.nextcloud.client.assistant.task.TaskView
 import com.nextcloud.client.assistant.repository.AssistantMockRepository
 import com.nextcloud.ui.composeActivity.ComposeActivity
 import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog

+ 5 - 0
app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt

@@ -35,3 +35,8 @@ fun Task.statusData(): Pair<Int, Int> {
         }
     }
 }
+
+// TODO add
+fun Task.completionDateRepresentation(): String {
+    return completionExpectedAt ?: "TODO IMPLEMENT IT"
+}

+ 60 - 11
app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt

@@ -7,12 +7,14 @@
  */
 package com.nextcloud.client.assistant.repository
 
+import com.nextcloud.utils.extensions.getRandomString
 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
 
+@Suppress("MagicNumber")
 class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : AssistantRepositoryType {
     override fun getTaskTypes(): RemoteOperationResult<TaskTypes> {
         return RemoteOperationResult<TaskTypes>(RemoteOperationResult.ResultCode.OK).apply {
@@ -41,16 +43,8 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass
                         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",
-                        "",
+                        "Give me some long text 1",
+                        "Lorem ipsum".getRandomString(100),
                         ""
                     ),
                     Task(
@@ -60,7 +54,62 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass
                         "12",
                         "",
                         "Give me some text 2",
-                        "Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
+                        "Lorem".getRandomString(100),
+                        "",
+                        ""
+                    ),
+                    Task(
+                        3,
+                        "FreePrompt",
+                        null,
+                        "12",
+                        "",
+                        "Give me some text 3",
+                        "Lorem".getRandomString(300),
+                        "",
+                        ""
+                    ),
+                    Task(
+                        4,
+                        "FreePrompt",
+                        null,
+                        "12",
+                        "",
+                        "Give me some text 4",
+                        "Lorem".getRandomString(300),
+                        "",
+                        ""
+                    ),
+                    Task(
+                        5,
+                        "FreePrompt",
+                        null,
+                        "12",
+                        "",
+                        "Give me some text 5",
+                        "Lorem".getRandomString(300),
+                        "",
+                        ""
+                    ),
+                    Task(
+                        6,
+                        "FreePrompt",
+                        null,
+                        "12",
+                        "",
+                        "Give me some text 6",
+                        "Lorem".getRandomString(300),
+                        "",
+                        ""
+                    ),
+                    Task(
+                        7,
+                        "FreePrompt",
+                        null,
+                        "12",
+                        "",
+                        "Give me some text 7",
+                        "Lorem".getRandomString(300),
                         "",
                         ""
                     )

+ 58 - 0
app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt

@@ -0,0 +1,58 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.assistant.task
+
+import androidx.compose.foundation.Image
+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.layout.size
+import androidx.compose.foundation.layout.width
+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.graphics.ColorFilter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.nextcloud.client.assistant.extensions.statusData
+import com.owncloud.android.lib.resources.assistant.model.Task
+
+@Composable
+fun TaskStatus(task: Task, foregroundColor: Color) {
+    Row(
+        modifier = Modifier
+            .fillMaxWidth()
+            .padding(vertical = 16.dp),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        val (iconId, descriptionId) = task.statusData()
+
+        Image(
+            painter = painterResource(id = iconId),
+            modifier = Modifier.size(16.dp),
+            colorFilter = ColorFilter.tint(foregroundColor),
+            contentDescription = "status icon"
+        )
+
+        Spacer(modifier = Modifier.width(6.dp))
+
+        Text(text = stringResource(id = descriptionId), color = foregroundColor)
+
+        /*
+        Spacer(modifier = Modifier.weight(1f))
+
+        Text(text = task.completionDateRepresentation(), color = foregroundColor)
+
+        Spacer(modifier = Modifier.width(6.dp))
+         */
+    }
+}

+ 17 - 63
app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt → app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt

@@ -1,27 +1,22 @@
 /*
  * Nextcloud - Android Client
  *
- * SPDX-FileCopyrightText: 2024 Alper Ozturk <alper_ozturk@proton.me>
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH
+ * SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
  * SPDX-License-Identifier: AGPL-3.0-or-later
  */
-package com.nextcloud.client.assistant.component
+package com.nextcloud.client.assistant.task
 
 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.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.combinedClickable
 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.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.HorizontalDivider
 import androidx.compose.material3.MaterialTheme
@@ -31,18 +26,15 @@ 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.draw.clip
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
-import com.nextcloud.client.assistant.extensions.statusData
+import com.nextcloud.client.assistant.taskDetail.TaskDetailBottomSheet
 import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet
+import com.nextcloud.utils.extensions.getRandomString
 import com.owncloud.android.R
 import com.owncloud.android.lib.resources.assistant.model.Task
 
@@ -53,7 +45,7 @@ fun TaskView(
     task: Task,
     showDeleteTaskAlertDialog: (Long) -> Unit
 ) {
-    var expanded by remember { mutableStateOf(false) }
+    var showTaskDetailBottomSheet by remember { mutableStateOf(false) }
     var showMoreActionsBottomSheet by remember { mutableStateOf(false) }
 
     Column(
@@ -62,7 +54,7 @@ fun TaskView(
             .clip(RoundedCornerShape(16.dp))
             .background(MaterialTheme.colorScheme.primary)
             .combinedClickable(onClick = {
-                expanded = !expanded
+                showTaskDetailBottomSheet = true
             }, onLongClick = {
                 showMoreActionsBottomSheet = true
             })
@@ -84,10 +76,11 @@ fun TaskView(
             HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp))
 
             Text(
-                text = if (expanded) it else it.take(100) + "...",
+                text = it.take(100),
                 fontSize = 12.sp,
                 color = Color.White,
                 modifier = Modifier
+                    .height(100.dp)
                     .animateContentSize(
                         animationSpec = spring(
                             dampingRatio = Spring.DampingRatioLowBouncy,
@@ -97,39 +90,7 @@ fun TaskView(
             )
         }
 
-        Row(
-            modifier = Modifier
-                .fillMaxWidth()
-                .padding(vertical = 16.dp),
-            verticalAlignment = Alignment.CenterVertically
-        ) {
-            val (iconId, descriptionId) = task.statusData()
-
-            Image(
-                painter = painterResource(id = iconId),
-                modifier = Modifier.size(16.dp),
-                colorFilter = ColorFilter.tint(Color.White),
-                contentDescription = "status icon"
-            )
-
-            Spacer(modifier = Modifier.width(6.dp))
-
-            Text(text = stringResource(id = descriptionId), color = Color.White)
-
-            Spacer(modifier = Modifier.weight(1f))
-
-            if ((task.output?.length ?: 0) >= 100) {
-                Image(
-                    painter = painterResource(
-                        id = if (!expanded) R.drawable.ic_expand_more else R.drawable.ic_expand_less
-                    ),
-                    contentDescription = "expand content icon",
-                    colorFilter = ColorFilter.tint(Color.White)
-                )
-            }
-
-            Spacer(modifier = Modifier.width(8.dp))
-        }
+        TaskStatus(task, foregroundColor = Color.White)
 
         if (showMoreActionsBottomSheet) {
             val bottomSheetAction = listOf(
@@ -147,27 +108,20 @@ fun TaskView(
                 dismiss = { showMoreActionsBottomSheet = false }
             )
         }
+
+        if (showTaskDetailBottomSheet) {
+            TaskDetailBottomSheet(task) {
+                showTaskDetailBottomSheet = false
+            }
+        }
     }
 }
 
+@Suppress("MagicNumber")
 @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."
+    val output = "Lorem".getRandomString(100)
 
     TaskView(
         task = Task(

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

@@ -0,0 +1,165 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.assistant.taskDetail
+
+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.layout.Column
+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.layout.widthIn
+import androidx.compose.foundation.lazy.LazyColumn
+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.ModalBottomSheet
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberModalBottomSheetState
+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.graphics.Color
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.nextcloud.client.assistant.task.TaskStatus
+import com.nextcloud.utils.extensions.getRandomString
+import com.owncloud.android.R
+import com.owncloud.android.lib.resources.assistant.model.Task
+
+@Suppress("LongMethod")
+@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
+@Composable
+fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
+    var showInput by remember { mutableStateOf(true) }
+    val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
+
+    ModalBottomSheet(
+        modifier = Modifier.padding(top = 32.dp),
+        containerColor = Color.White,
+        onDismissRequest = {
+            dismiss()
+        },
+        sheetState = sheetState
+    ) {
+        LazyColumn(
+            modifier = Modifier
+                .fillMaxSize()
+                .padding(16.dp)
+        ) {
+            stickyHeader {
+                Row(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp))
+                ) {
+                    TextInputSelectButton(
+                        Modifier.weight(1f),
+                        R.string.assistant_task_detail_screen_input_button_title,
+                        showInput,
+                        onClick = {
+                            showInput = true
+                        }
+                    )
+
+                    TextInputSelectButton(
+                        Modifier.weight(1f),
+                        R.string.assistant_task_detail_screen_output_button_title,
+                        !showInput,
+                        onClick = {
+                            showInput = false
+                        }
+                    )
+                }
+            }
+
+            item {
+                Spacer(modifier = Modifier.height(16.dp))
+
+                Column(
+                    modifier = Modifier
+                        .fillMaxSize()
+                        .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp))
+                        .padding(16.dp)
+                ) {
+                    Text(
+                        text = if (showInput) {
+                            task.input ?: ""
+                        } else {
+                            task.output ?: ""
+                        },
+                        fontSize = 12.sp,
+                        color = Color.Black,
+                        modifier = Modifier
+                            .animateContentSize(
+                                animationSpec = spring(
+                                    dampingRatio = Spring.DampingRatioLowBouncy,
+                                    stiffness = Spring.StiffnessLow
+                                )
+                            )
+                    )
+                }
+
+                TaskStatus(task, foregroundColor = Color.Black)
+
+                Spacer(modifier = Modifier.height(32.dp))
+            }
+        }
+    }
+}
+
+@Composable
+private fun TextInputSelectButton(modifier: Modifier, titleId: Int, highlightCondition: Boolean, onClick: () -> Unit) {
+    Button(
+        onClick = onClick,
+        shape = RoundedCornerShape(8.dp),
+        colors = if (highlightCondition) {
+            ButtonDefaults.buttonColors(containerColor = Color.White)
+        } else {
+            ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.light_grey))
+        },
+        modifier = modifier
+            .widthIn(min = 0.dp, max = 200.dp)
+            .padding(horizontal = 4.dp)
+    ) {
+        Text(text = stringResource(id = titleId), color = Color.Black)
+    }
+}
+
+@Suppress("MagicNumber")
+@Preview
+@Composable
+private fun TaskDetailScreenPreview() {
+    TaskDetailBottomSheet(
+        task = Task(
+            1,
+            "Free Prompt",
+            0,
+            "1",
+            "1",
+            "Give me text".getRandomString(100),
+            "output".getRandomString(300),
+            "",
+            ""
+        )
+    ) {
+    }
+}

+ 1 - 1
app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt → app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt

@@ -5,7 +5,7 @@
  * SPDX-FileCopyrightText: 2024 Nextcloud GmbH
  * SPDX-License-Identifier: AGPL-3.0-or-later
  */
-package com.nextcloud.client.assistant.component
+package com.nextcloud.client.assistant.taskTypes
 
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Row

+ 7 - 10
app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt

@@ -36,7 +36,7 @@ import java.lang.ref.WeakReference
 class ComposeActivity : DrawerActivity() {
 
     lateinit var binding: ActivityComposeBinding
-    private var menuItemId: Int? = null
+    private var menuItemId: Int = R.id.nav_all_files
 
     companion object {
         const val DESTINATION = "DESTINATION"
@@ -51,13 +51,12 @@ 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)
+        menuItemId = intent.getIntExtra(MENU_ITEM, R.id.nav_all_files)
 
-        setupToolbar()
-        updateActionBarTitleAndHomeButtonByString(getString(titleId))
+        setupDrawer(menuItemId)
 
-        if (menuItemId != -1) {
-            setupDrawer(menuItemId!!)
+        setupToolbarShowOnlyMenuButtonAndTitle(getString(titleId)) {
+            openDrawer()
         }
 
         binding.composeView.setContent {
@@ -72,15 +71,13 @@ class ComposeActivity : DrawerActivity() {
 
     override fun onResume() {
         super.onResume()
-        if (menuItemId != -1) {
-            setDrawerMenuItemChecked(R.id.nav_assistant)
-        }
+        setDrawerMenuItemChecked(menuItemId)
     }
 
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
         return when (item.itemId) {
             android.R.id.home -> {
-                if (isDrawerOpen) closeDrawer() else openDrawer()
+                toggleDrawer()
                 true
             }
             else -> super.onOptionsItemSelected(item)

+ 17 - 0
app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt

@@ -0,0 +1,17 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.utils.extensions
+
+fun String.getRandomString(length: Int): String {
+    val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
+    val result = (1..length)
+        .map { allowedChars.random() }
+        .joinToString("")
+
+    return this + result
+}

+ 75 - 40
app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -286,10 +286,10 @@ public abstract class DrawerActivity extends ToolbarActivity
 
     public void updateHeader() {
         int primaryColor = themeColorUtils.unchangedPrimaryColor(getAccount(), this);
+        boolean isClientBranded = getResources().getBoolean(R.bool.is_branded_client);
 
         if (getAccount() != null &&
-            getCapabilities().getServerBackground() != null &&
-            !getResources().getBoolean(R.bool.is_branded_client)) {
+            getCapabilities().getServerBackground() != null && !isClientBranded) {
 
             OCCapability capability = getCapabilities();
             String logo = capability.getServerLogo();
@@ -339,45 +339,55 @@ public abstract class DrawerActivity extends ToolbarActivity
         }
 
         // hide ecosystem apps according to user preference or in branded client
-        LinearLayout ecosystemApps = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps);
-        if (getResources().getBoolean(R.bool.is_branded_client) || !preferences.isShowEcosystemApps()) {
-            ecosystemApps.setVisibility(View.GONE);
+        LinearLayout banner = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps);
+        boolean shouldHideTopBanner = isClientBranded || !preferences.isShowEcosystemApps();
+
+        if (shouldHideTopBanner) {
+            hideTopBanner(banner);
         } else {
-            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()) {
-                assistantView.setVisibility(View.VISIBLE);
-            } else {
-                assistantView.setVisibility(View.GONE);
-            }
+            showTopBanner(banner, primaryColor);
+        }
+    }
 
-            List<LinearLayout> views = Arrays.asList(notesView, talkView, moreView, assistantView);
+    private void hideTopBanner(LinearLayout banner) {
+        banner.setVisibility(View.GONE);
+    }
 
-            int iconColor;
-            if (Hct.fromInt(primaryColor).getTone() < 80.0) {
-                iconColor = Color.WHITE;
-            } else {
-                iconColor = getColor(R.color.grey_800_transparent);
-            }
+    private void showTopBanner(LinearLayout banner, int primaryColor) {
+        LinearLayout notesView = banner.findViewById(R.id.drawer_ecosystem_notes);
+        LinearLayout talkView = banner.findViewById(R.id.drawer_ecosystem_talk);
+        LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more);
+        LinearLayout assistantView = banner.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()) {
+            assistantView.setVisibility(View.VISIBLE);
+        } else {
+            assistantView.setVisibility(View.GONE);
+        }
 
-            for (LinearLayout view : views) {
-                ImageView imageView = (ImageView) view.getChildAt(0);
-                imageView.setImageTintList(ColorStateList.valueOf(iconColor));
-                GradientDrawable background = (GradientDrawable) imageView.getBackground();
-                background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor);
-                TextView textView = (TextView) view.getChildAt(1);
-                textView.setTextColor(iconColor);
-            }
+        List<LinearLayout> views = Arrays.asList(notesView, talkView, moreView, assistantView);
+
+        int iconColor;
+        if (Hct.fromInt(primaryColor).getTone() < 80.0) {
+            iconColor = Color.WHITE;
+        } else {
+            iconColor = getColor(R.color.grey_800_transparent);
+        }
 
-            ecosystemApps.setVisibility(View.VISIBLE);
+        for (LinearLayout view : views) {
+            ImageView imageView = (ImageView) view.getChildAt(0);
+            imageView.setImageTintList(ColorStateList.valueOf(iconColor));
+            GradientDrawable background = (GradientDrawable) imageView.getBackground();
+            background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor);
+            TextView textView = (TextView) view.getChildAt(1);
+            textView.setTextColor(iconColor);
         }
+
+        banner.setVisibility(View.VISIBLE);
     }
 
     /**
@@ -463,9 +473,7 @@ public abstract class DrawerActivity extends ToolbarActivity
         DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability);
         DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources());
         DrawerMenuUtil.setupHomeMenuItem(menu, getResources());
-
-        DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community,
-                                      !getResources().getBoolean(R.bool.participate_enabled));
+        DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !getResources().getBoolean(R.bool.participate_enabled));
         DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled));
         DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !getResources().getBoolean(R.bool.show_drawer_logout));
     }
@@ -494,10 +502,17 @@ public abstract class DrawerActivity extends ToolbarActivity
                 MainApp.showOnlyPersonalFiles(itemId == R.id.nav_personal_files);
                 Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
                 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+                if (this instanceof ComposeActivity) {
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                }
+
                 intent.setAction(FileDisplayActivity.ALL_FILES);
                 intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
                 startActivity(intent);
             }
+
+            closeDrawer();
         } else if (itemId == R.id.nav_favorites) {
             handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH),
                                menuItem.getItemId());
@@ -520,7 +535,8 @@ public abstract class DrawerActivity extends ToolbarActivity
             startActivity(CommunityActivity.class);
         } else if (itemId == R.id.nav_logout) {
             mCheckedMenuItem = -1;
-            menuItem.setChecked(false);
+            MenuItem isNewMenuItemChecked = menuItem.setChecked(false);
+            Log_OC.d(TAG,"onNavigationItemClicked nav_logout setChecked " + isNewMenuItemChecked);
             final Optional<User> optionalUser = getUser();
             if (optionalUser.isPresent()) {
                 UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager());
@@ -627,6 +643,11 @@ public abstract class DrawerActivity extends ToolbarActivity
     private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) {
         Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+        if (this instanceof ComposeActivity) {
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        }
+
         intent.setAction(Intent.ACTION_SEARCH);
         intent.putExtra(OCFileListFragment.SEARCH_EVENT, searchEvent);
         intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId);
@@ -673,6 +694,14 @@ public abstract class DrawerActivity extends ToolbarActivity
         return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START);
     }
 
+    public void toggleDrawer() {
+        if (isDrawerOpen()) {
+            closeDrawer();
+        } else {
+            openDrawer();
+        }
+    }
+
     /**
      * closes the drawer.
      */
@@ -1115,6 +1144,11 @@ public abstract class DrawerActivity extends ToolbarActivity
         MainApp.showOnlyPersonalFiles(onlyPersonalFiles);
         Intent fileDisplayActivity = new Intent(getApplicationContext(), FileDisplayActivity.class);
         fileDisplayActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+        if (this instanceof ComposeActivity) {
+            fileDisplayActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        }
+
         fileDisplayActivity.setAction(FileDisplayActivity.ALL_FILES);
         startActivity(fileDisplayActivity);
     }
@@ -1122,7 +1156,8 @@ public abstract class DrawerActivity extends ToolbarActivity
     @Override
     public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
         if (callContext instanceof MenuItem menuItem) {
-            menuItem.setIcon(avatarDrawable);
+            MenuItem newIcon = menuItem.setIcon(avatarDrawable);
+            Log_OC.d(TAG,"avatarGenerated new icon: " + newIcon);
         } else if (callContext instanceof ImageView imageView) {
             imageView.setImageDrawable(avatarDrawable);
         } else if (callContext instanceof MaterialButton materialButton) {

+ 17 - 1
app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java

@@ -104,6 +104,22 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable
         viewThemeUtils.material.colorMaterialTextButton(mSwitchAccountButton);
     }
 
+    public void setupToolbarShowOnlyMenuButtonAndTitle(String title, View.OnClickListener toggleDrawer) {
+        setupToolbar(false, false);
+
+        ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayShowTitleEnabled(false);
+        }
+
+        LinearLayout toolbar = findViewById(R.id.toolbar_linear_layout);
+        MaterialButton menuButton = findViewById(R.id.toolbar_menu_button);
+        MaterialTextView titleTextView = findViewById(R.id.toolbar_title);
+        titleTextView.setText(title);
+        toolbar.setVisibility(View.VISIBLE);
+        menuButton.setOnClickListener(toggleDrawer);
+    }
+
     public void setupToolbar() {
         setupToolbar(false, false);
     }
@@ -278,7 +294,7 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable
 
     public void clearToolbarSubtitle() {
         ActionBar actionBar = getSupportActionBar();
-        if(actionBar != null){
+        if (actionBar != null) {
             actionBar.setSubtitle(null);
         }
     }

+ 11 - 8
app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java

@@ -27,7 +27,7 @@ public final class DrawerMenuUtil {
                                              User user,
                                              Resources resources) {
         if (user.isAnonymous()) {
-            filterMenuItems(menu, R.id.nav_gallery, R.id.nav_favorites);
+            removeMenuItem(menu, R.id.nav_gallery, R.id.nav_favorites);
         }
 
         if (!resources.getBoolean(R.bool.recently_modified_enabled)) {
@@ -38,26 +38,29 @@ public final class DrawerMenuUtil {
     public static void filterTrashbinMenuItem(Menu menu, @Nullable OCCapability capability) {
         if (capability != null && capability.getFilesUndelete().isFalse() ||
             capability != null && capability.getFilesUndelete().isUnknown()) {
-            filterMenuItems(menu, R.id.nav_trashbin);
+            removeMenuItem(menu, R.id.nav_trashbin);
         }
     }
 
     public static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capability) {
         if (capability != null && capability.getActivity().isFalse()) {
-            filterMenuItems(menu, R.id.nav_activity);
+            removeMenuItem(menu, R.id.nav_activity);
         }
     }
 
     public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources) {
-        boolean showCondition = capability != null && capability.getAssistant().isTrue() && !resources.getBoolean(R.bool.is_branded_client);
-        if (!showCondition) {
-            filterMenuItems(menu, R.id.nav_assistant);
+        if (resources.getBoolean(R.bool.is_branded_client)) {
+            if (capability != null && capability.getAssistant().isFalse()) {
+                removeMenuItem(menu, R.id.nav_assistant);
+            }
+        } else {
+            removeMenuItem(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);
+            removeMenuItem(menu, R.id.nav_groupfolders);
         }
     }
 
@@ -74,7 +77,7 @@ public final class DrawerMenuUtil {
         }
     }
 
-    private static void filterMenuItems(Menu menu, int... menuIds) {
+    private static void removeMenuItem(Menu menu, int... menuIds) {
         if (menuIds != null) {
             for (int menuId : menuIds) {
                 menu.removeItem(menuId);

+ 0 - 15
app/src/main/res/drawable/ic_expand_less.xml

@@ -1,15 +0,0 @@
-<!--
-  ~ Nextcloud - Android Client
-  ~
-  ~ SPDX-FileCopyrightText: 2018-2024 Google LLC
-  ~ SPDX-License-Identifier: Apache-2.0
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="960"
-    android:viewportHeight="960">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="m296,615 l-56,-56 240,-240 240,240 -56,56 -184,-184 -184,184Z" />
-</vector>

+ 36 - 0
app/src/main/res/layout/toolbar_standard.xml

@@ -114,6 +114,42 @@
 
         </RelativeLayout>
 
+        <LinearLayout
+            android:id="@+id/toolbar_linear_layout"
+            android:visibility="gone"
+            tools:visibility="visible"
+            android:orientation="horizontal"
+            android:layout_marginStart="8dp"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="16dp"
+            android:layout_marginBottom="4dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <com.google.android.material.button.MaterialButton
+                android:id="@+id/toolbar_menu_button"
+                style="@style/Widget.AppTheme.Button.IconButton"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                app:cornerRadius="@dimen/button_corner_radius"
+                app:icon="@drawable/ic_menu"
+                app:iconTint="@color/black" />
+
+            <com.google.android.material.textview.MaterialTextView
+                android:id="@+id/toolbar_title"
+                android:gravity="center"
+                android:textColor="@color/black"
+                android:lines="1"
+                android:textSize="16sp"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"/>
+
+        </LinearLayout>
+
+
         <!-- home/search toolbar -->
         <com.google.android.material.card.MaterialCardView
             android:id="@+id/home_toolbar"

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

@@ -15,6 +15,7 @@
     <color name="white">#ffffff</color>
     <color name="white_helper_text">#B3FFFFFF</color>
     <color name="text_color">#333333</color>
+    <color name="light_grey">#F5F5F5</color>
     <color name="grid_file_features_icon_color">#303034</color>
     <color name="grid_file_features_background_color">#E9E8EB</color>
     <color name="drawer_text_color">@color/secondary_text_color</color>

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

@@ -52,6 +52,9 @@
     <string name="assistant_screen_failed_task_text">Failed</string>
     <string name="assistant_screen_running_task_text">In Progress</string>
 
+    <string name="assistant_task_detail_screen_input_button_title">Input</string>
+    <string name="assistant_task_detail_screen_output_button_title">Output</string>
+
     <string name="assistant_screen_all_task_type">All</string>
 
     <string name="drawer_item_assistant">Assistant</string>