Skip to content

Commit 2a9c481

Browse files
feat(ecosystem): Add ecosystem bar to account chooser dialog
Resolves: #3248 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
1 parent 423e70c commit 2a9c481

15 files changed

Lines changed: 503 additions & 191 deletions

File tree

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ dependencies {
315315
implementation 'androidx.core:core-ktx:1.17.0'
316316
implementation 'androidx.activity:activity-ktx:1.12.4'
317317
implementation 'com.github.nextcloud.android-common:ui:0.32.0'
318+
implementation 'com.github.nextcloud.android-common:core:0.32.0'
318319
implementation 'com.github.nextcloud-deps:android-talk-webrtc:132.6834.0'
319320

320321
gplayImplementation 'com.google.android.gms:play-services-base:18.10.0'

app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4296,6 +4296,7 @@ class ChatActivity :
42964296
}
42974297
}
42984298

4299+
@Suppress("Detekt.TooGenericExceptionCaught")
42994300
private fun shareToNotes(
43004301
shareUri: Uri?,
43014302
roomToken: String,

app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,10 +756,15 @@ class ConversationsListActivity :
756756
}
757757

758758
private fun showChooseAccountDialog() {
759+
val brandedClient = getResources().getBoolean(R.bool.is_branded_client)
759760
binding.genericComposeView.apply {
760761
val shouldDismiss = mutableStateOf(false)
761762
setContent {
762-
ChooseAccountDialogCompose().GetChooseAccountDialog(shouldDismiss, this@ConversationsListActivity)
763+
ChooseAccountDialogCompose().GetChooseAccountDialog(
764+
shouldDismiss,
765+
this@ConversationsListActivity,
766+
appPreferences.isShowEcosystem && !brandedClient
767+
)
763768
}
764769
}
765770
}

app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ class ConversationsListViewModel @Inject constructor(
281281
return differenceMillis > checkIntervalInMillies
282282
}
283283

284+
@Suppress("Detekt.TooGenericExceptionCaught")
284285
fun checkIfFollowedThreadsExist() {
285286
val threadsUrl = ApiUtils.getUrlForSubscribedThreads(
286287
version = 1,

app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ class SettingsActivity :
216216
}
217217

218218
setupCheckables(isOnline.value)
219+
setupEcosystemSetting()
219220
setupScreenLockSetting()
220221
setupNotificationSettings()
221222
setupProxyTypeSettings()
@@ -252,6 +253,14 @@ class SettingsActivity :
252253
}
253254
}
254255

256+
private fun setupEcosystemSetting() {
257+
if (getResources().getBoolean(R.bool.is_branded_client)) {
258+
binding.settingsShowEcosystem.visibility = View.GONE
259+
} else {
260+
binding.settingsShowEcosystem.visibility = View.VISIBLE
261+
}
262+
}
263+
255264
@Suppress("MagicNumber")
256265
private fun scrollToNotificationCategory() {
257266
binding.scrollView.post {
@@ -771,6 +780,7 @@ class SettingsActivity :
771780
private fun themeSwitchPreferences() {
772781
binding.run {
773782
listOf(
783+
settingsShowEcosystemSwitch,
774784
settingsShowNotificationWarningSwitch,
775785
settingsScreenLockSwitch,
776786
settingsScreenSecuritySwitch,
@@ -967,6 +977,8 @@ class SettingsActivity :
967977
}
968978

969979
private fun setupCheckables(isOnline: Boolean) {
980+
setupShowEcosystemCheckable()
981+
970982
binding.settingsShowNotificationWarningSwitch.isChecked =
971983
appPreferences.showRegularNotificationWarning
972984

@@ -1031,6 +1043,15 @@ class SettingsActivity :
10311043
}
10321044
}
10331045

1046+
private fun setupShowEcosystemCheckable() {
1047+
binding.settingsShowEcosystemSwitch.isChecked = appPreferences.isShowEcosystem
1048+
binding.settingsShowEcosystem.setOnClickListener {
1049+
val isChecked = binding.settingsShowEcosystemSwitch.isChecked
1050+
binding.settingsShowEcosystemSwitch.isChecked = !isChecked
1051+
appPreferences.setShowEcosystem(!isChecked)
1052+
}
1053+
}
1054+
10341055
private fun setupPhoneBookIntegrationSetting() {
10351056
binding.settingsPhoneBookIntegrationSwitch.isChecked = appPreferences.isPhoneBookIntegrationEnabled
10361057
binding.settingsPhoneBookIntegration.setOnClickListener {

app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogCompose.kt

Lines changed: 143 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@ package com.nextcloud.talk.ui.dialog
1010
import android.app.Activity
1111
import android.content.Context
1212
import android.content.Intent
13+
import android.content.res.Configuration
1314
import android.util.Log
1415
import android.widget.ImageView
1516
import androidx.compose.foundation.background
17+
import androidx.compose.foundation.border
1618
import androidx.compose.foundation.clickable
19+
import androidx.compose.foundation.layout.Arrangement
1720
import androidx.compose.foundation.layout.Box
1821
import androidx.compose.foundation.layout.Column
1922
import androidx.compose.foundation.layout.Row
2023
import androidx.compose.foundation.layout.Spacer
2124
import androidx.compose.foundation.layout.fillMaxWidth
2225
import androidx.compose.foundation.layout.padding
2326
import androidx.compose.foundation.layout.size
27+
import androidx.compose.foundation.layout.widthIn
2428
import androidx.compose.foundation.lazy.LazyColumn
2529
import androidx.compose.foundation.lazy.items
2630
import androidx.compose.foundation.shape.CircleShape
@@ -45,6 +49,7 @@ import androidx.compose.ui.Modifier
4549
import androidx.compose.ui.draw.clip
4650
import androidx.compose.ui.graphics.Color
4751
import androidx.compose.ui.layout.ContentScale
52+
import androidx.compose.ui.platform.LocalConfiguration
4853
import androidx.compose.ui.platform.LocalContext
4954
import androidx.compose.ui.res.colorResource
5055
import androidx.compose.ui.res.painterResource
@@ -59,6 +64,8 @@ import androidx.core.net.toUri
5964
import androidx.fragment.app.FragmentActivity
6065
import autodagger.AutoInjector
6166
import coil.compose.AsyncImage
67+
import com.nextcloud.android.common.core.utils.ecosystem.EcosystemApp
68+
import com.nextcloud.android.common.core.utils.ecosystem.EcosystemManager
6269
import com.nextcloud.talk.R
6370
import com.nextcloud.talk.account.ServerSelectionActivity
6471
import com.nextcloud.talk.account.data.model.AccountItem
@@ -110,11 +117,13 @@ class ChooseAccountDialogCompose {
110117
@Inject
111118
lateinit var networkMonitor: NetworkMonitor
112119

120+
lateinit var ecosystemManager: EcosystemManager
121+
113122
private val userItems = mutableStateListOf<AccountItem>()
114123

115124
@Composable
116125
@Suppress("LongMethod")
117-
fun GetChooseAccountDialog(shouldDismiss: MutableState<Boolean>, activity: Activity) {
126+
fun GetChooseAccountDialog(shouldDismiss: MutableState<Boolean>, activity: Activity, showEcosystem: Boolean) {
118127
if (shouldDismiss.value) return
119128
val colorScheme = viewThemeUtils.getColorScheme(activity)
120129
val status = remember { mutableStateOf<Status?>(null) }
@@ -124,6 +133,7 @@ class ChooseAccountDialogCompose {
124133
val isOnline by networkMonitor.isOnline.collectAsState()
125134
val currentUser = currentUserProvider.currentUser.blockingGet()!!
126135
val isStatusAvailable = CapabilitiesUtil.isUserStatusAvailable(currentUser)
136+
ecosystemManager = EcosystemManager(activity)
127137

128138
LaunchedEffect(currentUser) {
129139
val users = userManager.users.blockingGet()
@@ -167,12 +177,25 @@ class ChooseAccountDialogCompose {
167177
shouldDismiss.value = true
168178
openSettings(activity)
169179
},
180+
onEcosystemFilesClick = {
181+
shouldDismiss.value = true
182+
openEcosystemFiles(currentUser)
183+
},
184+
onEcosystemNotesClick = {
185+
shouldDismiss.value = true
186+
openEcosystemNotes(currentUser)
187+
},
188+
onEcosystemMoreClick = {
189+
shouldDismiss.value = true
190+
openEcosystemMore(activity)
191+
},
170192
accountRowContent = { user ->
171193
AccountRow(user, activity) { shouldDismiss.value = true }
172194
},
173195
statusIndicator = { modifier ->
174196
StatusIndicator(modifier = modifier, status = status.value, context = context)
175197
},
198+
showEcosystem = showEcosystem,
176199
context = context
177200
)
178201
}
@@ -229,6 +252,20 @@ class ChooseAccountDialogCompose {
229252
activity.startActivity(intent)
230253
}
231254

255+
private fun openEcosystemFiles(currentUser: User) {
256+
ecosystemManager.openApp(EcosystemApp.FILES, getAccountHandle(currentUser))
257+
}
258+
259+
private fun openEcosystemNotes(currentUser: User) {
260+
ecosystemManager.openApp(EcosystemApp.NOTES, getAccountHandle(currentUser))
261+
}
262+
263+
private fun getAccountHandle(user: User): String = user.username + "@" + user.baseUrl?.toUri()?.host
264+
265+
private fun openEcosystemMore(activity: Activity) {
266+
LinkHelper.openAppStore("Nextcloud", true, activity)
267+
}
268+
232269
@Composable
233270
private fun AccountRow(userItem: AccountItem, activity: Activity, onSelected: () -> Unit) {
234271
Row(
@@ -258,7 +295,11 @@ class ChooseAccountDialogCompose {
258295
.size(40.dp)
259296
.clip(CircleShape)
260297
)
261-
Column(modifier = Modifier.padding(start = 12.dp).weight(1f)) {
298+
Column(
299+
modifier = Modifier
300+
.padding(start = 12.dp)
301+
.weight(1f)
302+
) {
262303
Text(
263304
text = userItem.user.displayName ?: userItem.user.username ?: ""
264305
)
@@ -306,6 +347,7 @@ class ChooseAccountDialogCompose {
306347
}
307348
}
308349
}
350+
309351
companion object {
310352
private const val STATUS_SIZE_DP = 9f
311353
private val TAG = ChooseAccountDialogCompose::class.simpleName
@@ -331,8 +373,12 @@ private fun ChooseAccountDialogContent(
331373
onSetStatusMessageClick: () -> Unit,
332374
onAddAccountClick: () -> Unit,
333375
onOpenSettingsClick: () -> Unit,
376+
onEcosystemFilesClick: () -> Unit,
377+
onEcosystemNotesClick: () -> Unit,
378+
onEcosystemMoreClick: () -> Unit,
334379
accountRowContent: @Composable (AccountItem) -> Unit,
335380
statusIndicator: @Composable (Modifier) -> Unit,
381+
showEcosystem: Boolean = true,
336382
context: Context
337383
) {
338384
Dialog(onDismissRequest = { shouldDismiss.value = true }) {
@@ -358,7 +404,21 @@ private fun ChooseAccountDialogContent(
358404
HorizontalDivider(
359405
modifier = Modifier.padding(vertical = 8.dp)
360406
)
361-
LazyColumn(modifier = Modifier.padding(start = 8.dp).weight(1f, fill = false)) {
407+
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT && showEcosystem) {
408+
EcosystemAppsSection(
409+
onFilesClick = onEcosystemFilesClick,
410+
onNotesClick = onEcosystemNotesClick,
411+
onMoreClick = onEcosystemMoreClick
412+
)
413+
HorizontalDivider(
414+
modifier = Modifier.padding(vertical = 8.dp)
415+
)
416+
}
417+
LazyColumn(
418+
modifier = Modifier
419+
.padding(start = 8.dp)
420+
.weight(1f, fill = false)
421+
) {
362422
items(accountItems) { account ->
363423
if (account.user.userId + account.user.baseUrl != currentUser.userId + currentUser.baseUrl) {
364424
accountRowContent(account)
@@ -407,7 +467,8 @@ private fun CurrentUserSection(
407467
statusIndicator(Modifier.align(Alignment.BottomEnd))
408468
}
409469
Column(
410-
modifier = Modifier.padding(start = 12.dp)
470+
modifier = Modifier
471+
.padding(start = 12.dp)
411472
.weight(1f)
412473
) {
413474
Text(text = currentUser.displayName ?: currentUser.username ?: "")
@@ -444,6 +505,69 @@ private fun CurrentUserSection(
444505
}
445506
}
446507

508+
@Composable
509+
private fun EcosystemAppsSection(onFilesClick: () -> Unit, onNotesClick: () -> Unit, onMoreClick: () -> Unit) {
510+
Row(
511+
modifier = Modifier
512+
.fillMaxWidth()
513+
.padding(horizontal = 16.dp),
514+
horizontalArrangement = Arrangement.SpaceEvenly
515+
) {
516+
EcosystemAppItem(
517+
iconRes = R.drawable.ic_mimetype_folder,
518+
label = stringResource(R.string.ecosystem_apps_files),
519+
contentDescription = stringResource(R.string.ecosystem_apps_files),
520+
onClick = onFilesClick
521+
)
522+
EcosystemAppItem(
523+
iconRes = R.drawable.ic_notes,
524+
label = stringResource(R.string.ecosystem_apps_notes),
525+
contentDescription = stringResource(R.string.ecosystem_apps_notes),
526+
onClick = onNotesClick
527+
)
528+
EcosystemAppItem(
529+
iconRes = R.drawable.ic_more_apps,
530+
label = stringResource(R.string.ecosystem_apps_more),
531+
contentDescription = stringResource(R.string.ecosystem_apps_more),
532+
onClick = onMoreClick
533+
)
534+
}
535+
}
536+
537+
@Composable
538+
private fun EcosystemAppItem(iconRes: Int, label: String, contentDescription: String, onClick: () -> Unit) {
539+
Column(
540+
modifier = Modifier
541+
.widthIn(max = 80.dp)
542+
.clickable(onClick = onClick)
543+
.padding(8.dp),
544+
horizontalAlignment = Alignment.CenterHorizontally
545+
) {
546+
Icon(
547+
painter = painterResource(id = iconRes),
548+
contentDescription = contentDescription,
549+
modifier = Modifier
550+
.size(40.dp)
551+
.border(
552+
width = 1.dp,
553+
color = MaterialTheme.colorScheme.outline,
554+
shape = CircleShape
555+
)
556+
.padding(8.dp),
557+
tint = MaterialTheme.colorScheme.onSurface
558+
)
559+
Text(
560+
text = label,
561+
color = MaterialTheme.colorScheme.onSurface,
562+
style = MaterialTheme.typography.labelSmall,
563+
modifier = Modifier.padding(top = 4.dp),
564+
fontWeight = FontWeight.Bold,
565+
maxLines = 1,
566+
overflow = TextOverflow.Ellipsis
567+
)
568+
}
569+
}
570+
447571
@Composable
448572
private fun StatusActionButtons(onSetOnlineStatusClick: () -> Unit, onSetStatusMessageClick: () -> Unit) {
449573
Row {
@@ -482,7 +606,9 @@ private fun OnlineActions(onAddAccountClick: () -> Unit, onOpenSettingsClick: ()
482606
if (isOnline) {
483607
TextButton(onClick = onAddAccountClick, modifier = Modifier.fillMaxWidth()) {
484608
Row(
485-
modifier = Modifier.padding(start = 16.dp, top = 8.dp).fillMaxWidth(),
609+
modifier = Modifier
610+
.padding(start = 16.dp, top = 8.dp)
611+
.fillMaxWidth(),
486612
verticalAlignment = Alignment.CenterVertically
487613
) {
488614
Icon(
@@ -502,7 +628,9 @@ private fun OnlineActions(onAddAccountClick: () -> Unit, onOpenSettingsClick: ()
502628

503629
TextButton(onClick = onOpenSettingsClick, modifier = Modifier.fillMaxWidth()) {
504630
Row(
505-
modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 16.dp).fillMaxWidth(),
631+
modifier = Modifier
632+
.padding(start = 16.dp, top = 8.dp, bottom = 16.dp)
633+
.fillMaxWidth(),
506634
verticalAlignment = Alignment.CenterVertically
507635
) {
508636
Icon(
@@ -520,7 +648,12 @@ private fun OnlineActions(onAddAccountClick: () -> Unit, onOpenSettingsClick: ()
520648
}
521649
}
522650

523-
@Preview(showBackground = true)
651+
@Preview(name = "Light Mode", showBackground = true)
652+
@Preview(
653+
name = "R-t-L",
654+
showBackground = true,
655+
locale = "ar"
656+
)
524657
@Composable
525658
private fun ChooseAccountDialogContentPreview() {
526659
val shouldDismiss = remember { mutableStateOf(false) }
@@ -555,6 +688,9 @@ private fun ChooseAccountDialogContentPreview() {
555688
onSetStatusMessageClick = {},
556689
onAddAccountClick = {},
557690
onOpenSettingsClick = {},
691+
onEcosystemFilesClick = {},
692+
onEcosystemNotesClick = {},
693+
onEcosystemMoreClick = {},
558694
accountRowContent = {},
559695
statusIndicator = { modifier -> SampleStatusIndicator(modifier) },
560696
context = context

0 commit comments

Comments
 (0)