diff --git a/app/build.gradle b/app/build.gradle
index 94d9c09b2c5..402cc0e0256 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -314,7 +314,8 @@ dependencies {
implementation 'androidx.core:core-ktx:1.17.0'
implementation 'androidx.activity:activity-ktx:1.12.2'
- implementation 'com.github.nextcloud.android-common:ui:0.31.0'
+ implementation 'com.github.nextcloud.android-common:ui:0.33.0'
+ implementation 'com.github.nextcloud.android-common:core:0.33.0'
implementation 'com.github.nextcloud-deps:android-talk-webrtc:132.6834.0'
gplayImplementation 'com.google.android.gms:play-services-base:18.10.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a6ef2dc6bb5..fc0c1a7d67c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -110,6 +110,11 @@
+
+
+
+
+
diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
index e9627981f8b..1e0c5e78cf6 100644
--- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
@@ -4292,6 +4292,7 @@ class ChatActivity :
}
}
+ @Suppress("Detekt.TooGenericExceptionCaught")
private fun shareToNotes(
shareUri: Uri?,
roomToken: String,
diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
index 402b266a0b2..27050f040d2 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
@@ -756,10 +756,15 @@ class ConversationsListActivity :
}
private fun showChooseAccountDialog() {
+ val brandedClient = getResources().getBoolean(R.bool.is_branded_client)
binding.genericComposeView.apply {
val shouldDismiss = mutableStateOf(false)
setContent {
- ChooseAccountDialogCompose().GetChooseAccountDialog(shouldDismiss, this@ConversationsListActivity)
+ ChooseAccountDialogCompose().GetChooseAccountDialog(
+ shouldDismiss,
+ this@ConversationsListActivity,
+ appPreferences.isShowEcosystem && !brandedClient
+ )
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt
index 8be8943739a..316efb57afe 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt
@@ -281,6 +281,7 @@ class ConversationsListViewModel @Inject constructor(
return differenceMillis > checkIntervalInMillies
}
+ @Suppress("Detekt.TooGenericExceptionCaught")
fun checkIfFollowedThreadsExist() {
val threadsUrl = ApiUtils.getUrlForSubscribedThreads(
version = 1,
diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
index 1de5203983a..ef0089b93fc 100644
--- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
@@ -216,6 +216,7 @@ class SettingsActivity :
}
setupCheckables(isOnline.value)
+ setupEcosystemSetting()
setupScreenLockSetting()
setupNotificationSettings()
setupProxyTypeSettings()
@@ -252,6 +253,14 @@ class SettingsActivity :
}
}
+ private fun setupEcosystemSetting() {
+ if (getResources().getBoolean(R.bool.is_branded_client)) {
+ binding.settingsShowEcosystem.visibility = View.GONE
+ } else {
+ binding.settingsShowEcosystem.visibility = View.VISIBLE
+ }
+ }
+
@Suppress("MagicNumber")
private fun scrollToNotificationCategory() {
binding.scrollView.post {
@@ -771,6 +780,7 @@ class SettingsActivity :
private fun themeSwitchPreferences() {
binding.run {
listOf(
+ settingsShowEcosystemSwitch,
settingsShowNotificationWarningSwitch,
settingsScreenLockSwitch,
settingsScreenSecuritySwitch,
@@ -967,6 +977,8 @@ class SettingsActivity :
}
private fun setupCheckables(isOnline: Boolean) {
+ setupShowEcosystemCheckable()
+
binding.settingsShowNotificationWarningSwitch.isChecked =
appPreferences.showRegularNotificationWarning
@@ -1031,6 +1043,15 @@ class SettingsActivity :
}
}
+ private fun setupShowEcosystemCheckable() {
+ binding.settingsShowEcosystemSwitch.isChecked = appPreferences.isShowEcosystem
+ binding.settingsShowEcosystem.setOnClickListener {
+ val isChecked = binding.settingsShowEcosystemSwitch.isChecked
+ binding.settingsShowEcosystemSwitch.isChecked = !isChecked
+ appPreferences.setShowEcosystem(!isChecked)
+ }
+ }
+
private fun setupPhoneBookIntegrationSetting() {
binding.settingsPhoneBookIntegrationSwitch.isChecked = appPreferences.isPhoneBookIntegrationEnabled
binding.settingsPhoneBookIntegration.setOnClickListener {
diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogCompose.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogCompose.kt
index 71b5c26efff..6b666f3d7cf 100644
--- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogCompose.kt
+++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogCompose.kt
@@ -10,10 +10,13 @@ package com.nextcloud.talk.ui.dialog
import android.app.Activity
import android.content.Context
import android.content.Intent
+import android.content.res.Configuration
import android.util.Log
import android.widget.ImageView
import androidx.compose.foundation.background
+import androidx.compose.foundation.border
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
@@ -21,12 +24,16 @@ 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.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -45,6 +52,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
@@ -59,6 +67,9 @@ import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity
import autodagger.AutoInjector
import coil.compose.AsyncImage
+import com.nextcloud.android.common.core.utils.ecosystem.EcosystemApp
+import com.nextcloud.android.common.core.utils.ecosystem.EcosystemManager
+import com.nextcloud.android.common.core.utils.ecosystem.LinkHelper
import com.nextcloud.talk.R
import com.nextcloud.talk.account.ServerSelectionActivity
import com.nextcloud.talk.account.data.model.AccountItem
@@ -110,11 +121,13 @@ class ChooseAccountDialogCompose {
@Inject
lateinit var networkMonitor: NetworkMonitor
+ lateinit var ecosystemManager: EcosystemManager
+
private val userItems = mutableStateListOf()
@Composable
@Suppress("LongMethod")
- fun GetChooseAccountDialog(shouldDismiss: MutableState, activity: Activity) {
+ fun GetChooseAccountDialog(shouldDismiss: MutableState, activity: Activity, showEcosystem: Boolean) {
if (shouldDismiss.value) return
val colorScheme = viewThemeUtils.getColorScheme(activity)
val status = remember { mutableStateOf(null) }
@@ -124,6 +137,7 @@ class ChooseAccountDialogCompose {
val isOnline by networkMonitor.isOnline.collectAsState()
val currentUser = currentUserProvider.currentUser.blockingGet()!!
val isStatusAvailable = CapabilitiesUtil.isUserStatusAvailable(currentUser)
+ ecosystemManager = EcosystemManager(activity)
LaunchedEffect(currentUser) {
val users = userManager.users.blockingGet()
@@ -167,12 +181,25 @@ class ChooseAccountDialogCompose {
shouldDismiss.value = true
openSettings(activity)
},
+ onEcosystemFilesClick = {
+ shouldDismiss.value = true
+ openEcosystemFiles(currentUser)
+ },
+ onEcosystemNotesClick = {
+ shouldDismiss.value = true
+ openEcosystemNotes(currentUser)
+ },
+ onEcosystemMoreClick = {
+ shouldDismiss.value = true
+ openEcosystemMore(activity)
+ },
accountRowContent = { user ->
AccountRow(user, activity) { shouldDismiss.value = true }
},
statusIndicator = { modifier ->
StatusIndicator(modifier = modifier, status = status.value, context = context)
},
+ showEcosystem = showEcosystem,
context = context
)
}
@@ -229,6 +256,20 @@ class ChooseAccountDialogCompose {
activity.startActivity(intent)
}
+ private fun openEcosystemFiles(currentUser: User) {
+ ecosystemManager.openApp(EcosystemApp.FILES, getAccountHandle(currentUser))
+ }
+
+ private fun openEcosystemNotes(currentUser: User) {
+ ecosystemManager.openApp(EcosystemApp.NOTES, getAccountHandle(currentUser))
+ }
+
+ private fun getAccountHandle(user: User): String = user.username + "@" + user.baseUrl?.toUri()?.host
+
+ private fun openEcosystemMore(activity: Activity) {
+ LinkHelper.openAppStore("Nextcloud", true, activity)
+ }
+
@Composable
private fun AccountRow(userItem: AccountItem, activity: Activity, onSelected: () -> Unit) {
Row(
@@ -258,7 +299,11 @@ class ChooseAccountDialogCompose {
.size(40.dp)
.clip(CircleShape)
)
- Column(modifier = Modifier.padding(start = 12.dp).weight(1f)) {
+ Column(
+ modifier = Modifier
+ .padding(start = 12.dp)
+ .weight(1f)
+ ) {
Text(
text = userItem.user.displayName ?: userItem.user.username ?: ""
)
@@ -306,6 +351,7 @@ class ChooseAccountDialogCompose {
}
}
}
+
companion object {
private const val STATUS_SIZE_DP = 9f
private val TAG = ChooseAccountDialogCompose::class.simpleName
@@ -331,8 +377,12 @@ private fun ChooseAccountDialogContent(
onSetStatusMessageClick: () -> Unit,
onAddAccountClick: () -> Unit,
onOpenSettingsClick: () -> Unit,
+ onEcosystemFilesClick: () -> Unit,
+ onEcosystemNotesClick: () -> Unit,
+ onEcosystemMoreClick: () -> Unit,
accountRowContent: @Composable (AccountItem) -> Unit,
statusIndicator: @Composable (Modifier) -> Unit,
+ showEcosystem: Boolean = true,
context: Context
) {
Dialog(onDismissRequest = { shouldDismiss.value = true }) {
@@ -358,7 +408,21 @@ private fun ChooseAccountDialogContent(
HorizontalDivider(
modifier = Modifier.padding(vertical = 8.dp)
)
- LazyColumn(modifier = Modifier.padding(start = 8.dp).weight(1f, fill = false)) {
+ if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT && showEcosystem) {
+ EcosystemAppsSection(
+ onFilesClick = onEcosystemFilesClick,
+ onNotesClick = onEcosystemNotesClick,
+ onMoreClick = onEcosystemMoreClick
+ )
+ HorizontalDivider(
+ modifier = Modifier.padding(vertical = 8.dp)
+ )
+ }
+ LazyColumn(
+ modifier = Modifier
+ .padding(start = 8.dp)
+ .weight(1f, fill = false)
+ ) {
items(accountItems) { account ->
if (account.user.userId + account.user.baseUrl != currentUser.userId + currentUser.baseUrl) {
accountRowContent(account)
@@ -407,7 +471,8 @@ private fun CurrentUserSection(
statusIndicator(Modifier.align(Alignment.BottomEnd))
}
Column(
- modifier = Modifier.padding(start = 12.dp)
+ modifier = Modifier
+ .padding(start = 12.dp)
.weight(1f)
) {
Text(text = currentUser.displayName ?: currentUser.username ?: "")
@@ -444,6 +509,69 @@ private fun CurrentUserSection(
}
}
+@Composable
+private fun EcosystemAppsSection(onFilesClick: () -> Unit, onNotesClick: () -> Unit, onMoreClick: () -> Unit) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ EcosystemAppItem(
+ iconRes = R.drawable.ic_mimetype_folder,
+ label = stringResource(R.string.ecosystem_apps_files),
+ contentDescription = stringResource(R.string.ecosystem_apps_files),
+ onClick = onFilesClick
+ )
+ EcosystemAppItem(
+ iconRes = R.drawable.ic_notes,
+ label = stringResource(R.string.ecosystem_apps_notes),
+ contentDescription = stringResource(R.string.ecosystem_apps_notes),
+ onClick = onNotesClick
+ )
+ EcosystemAppItem(
+ iconRes = R.drawable.ic_more_apps,
+ label = stringResource(R.string.ecosystem_apps_more),
+ contentDescription = stringResource(R.string.ecosystem_apps_more),
+ onClick = onMoreClick
+ )
+ }
+}
+
+@Composable
+private fun EcosystemAppItem(iconRes: Int, label: String, contentDescription: String, onClick: () -> Unit) {
+ Column(
+ modifier = Modifier
+ .widthIn(max = 80.dp)
+ .clickable(onClick = onClick)
+ .padding(8.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Icon(
+ painter = painterResource(id = iconRes),
+ contentDescription = contentDescription,
+ modifier = Modifier
+ .size(40.dp)
+ .border(
+ width = 1.dp,
+ color = MaterialTheme.colorScheme.outline,
+ shape = CircleShape
+ )
+ .padding(8.dp),
+ tint = MaterialTheme.colorScheme.onSurface
+ )
+ Text(
+ text = label,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.labelSmall,
+ modifier = Modifier.padding(top = 4.dp),
+ fontWeight = FontWeight.Bold,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+}
+
@Composable
private fun StatusActionButtons(onSetOnlineStatusClick: () -> Unit, onSetStatusMessageClick: () -> Unit) {
Row {
@@ -482,7 +610,9 @@ private fun OnlineActions(onAddAccountClick: () -> Unit, onOpenSettingsClick: ()
if (isOnline) {
TextButton(onClick = onAddAccountClick, modifier = Modifier.fillMaxWidth()) {
Row(
- modifier = Modifier.padding(start = 16.dp, top = 8.dp).fillMaxWidth(),
+ modifier = Modifier
+ .padding(start = 16.dp, top = 8.dp)
+ .fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
@@ -502,7 +632,9 @@ private fun OnlineActions(onAddAccountClick: () -> Unit, onOpenSettingsClick: ()
TextButton(onClick = onOpenSettingsClick, modifier = Modifier.fillMaxWidth()) {
Row(
- modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 16.dp).fillMaxWidth(),
+ modifier = Modifier
+ .padding(start = 16.dp, top = 8.dp, bottom = 16.dp)
+ .fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
@@ -520,7 +652,17 @@ private fun OnlineActions(onAddAccountClick: () -> Unit, onOpenSettingsClick: ()
}
}
-@Preview(showBackground = true)
+@Preview(name = "Light Mode", showBackground = true)
+@Preview(
+ name = "Dark Mode",
+ showBackground = true,
+ uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL
+)
+@Preview(
+ name = "R-t-L",
+ showBackground = true,
+ locale = "ar"
+)
@Composable
private fun ChooseAccountDialogContentPreview() {
val shouldDismiss = remember { mutableStateOf(false) }
@@ -540,11 +682,13 @@ private fun ChooseAccountDialogContentPreview() {
status = "online",
statusIsUserDefined = true
)
- MaterialTheme {
+ val isDark = isSystemInDarkTheme()
+ val colorScheme = if (isDark) darkColorScheme() else lightColorScheme()
+ MaterialTheme(colorScheme = colorScheme) {
val context = LocalContext.current
ChooseAccountDialogContent(
shouldDismiss = shouldDismiss,
- colorScheme = MaterialTheme.colorScheme,
+ colorScheme = colorScheme,
currentUser = sampleUser,
status = sampleStatus,
isStatusAvailable = true,
@@ -555,6 +699,9 @@ private fun ChooseAccountDialogContentPreview() {
onSetStatusMessageClick = {},
onAddAccountClick = {},
onOpenSettingsClick = {},
+ onEcosystemFilesClick = {},
+ onEcosystemNotesClick = {},
+ onEcosystemMoreClick = {},
accountRowContent = {},
statusIndicator = { modifier -> SampleStatusIndicator(modifier) },
context = context
diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java
index 43f0e5e9651..8dea169ad58 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java
+++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java
@@ -116,6 +116,12 @@ public interface AppPreferences {
void removeIncognitoKeyboard();
+ boolean getIsShowEcosystem();
+
+ void setShowEcosystem(boolean value);
+
+ void removeShowEcosystem();
+
boolean isPhoneBookIntegrationEnabled();
void setPhoneBookIntegration(boolean value);
diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt
index adc8fb1f35e..16de5234d5a 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt
@@ -301,6 +301,22 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
setIncognitoKeyboard(false)
}
+ override fun getIsShowEcosystem(): Boolean {
+ val read = runBlocking { async { readBoolean(SHOW_ECOSYSTEM, true).first() } }.getCompleted()
+ return read
+ }
+
+ override fun setShowEcosystem(value: Boolean) =
+ runBlocking {
+ async {
+ writeBoolean(SHOW_ECOSYSTEM, value)
+ }
+ }
+
+ override fun removeShowEcosystem() {
+ setShowEcosystem(false)
+ }
+
override fun isPhoneBookIntegrationEnabled(): Boolean =
runBlocking {
async { readBoolean(PHONE_BOOK_INTEGRATION).first() }
@@ -607,6 +623,7 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
const val SCREEN_SECURITY = "screen_security"
const val SCREEN_LOCK = "screen_lock"
const val INCOGNITO_KEYBOARD = "incognito_keyboard"
+ const val SHOW_ECOSYSTEM = "SHOW_ECOSYSTEM"
const val PHONE_BOOK_INTEGRATION = "phone_book_integration"
const val LINK_PREVIEWS = "link_previews"
const val SCREEN_LOCK_TIMEOUT = "screen_lock_timeout"
diff --git a/app/src/main/res/drawable/ic_more_apps.xml b/app/src/main/res/drawable/ic_more_apps.xml
new file mode 100644
index 00000000000..b5c11ed15c4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_more_apps.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_notes.xml b/app/src/main/res/drawable/ic_notes.xml
new file mode 100644
index 00000000000..bd5b22a6059
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notes.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index a3191b81575..d9511c644a4 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -1,5 +1,4 @@
-
-
+ false
https://nextcloud.com/privacy/
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a44d2027577..7ee10c06f6a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -172,6 +172,10 @@ How to translate with transifex:
Add account
Active user
+ Files
+ Notes
+ More
+
Personal Info
Diagnosis
@@ -933,4 +937,6 @@ How to translate with transifex:
Send later without notification
In %1$s
No connection to server - Scheduled messages could not be loaded
+ Show app switcher
+ Nextcloud app suggestions in account chooser dialog
diff --git a/detekt.yml b/detekt.yml
index 7e293c8f900..a516d586a5c 100644
--- a/detekt.yml
+++ b/detekt.yml
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: GPL-3.0-or-later
build:
- maxIssues: 81
+ maxIssues: 80
weights:
# complexity: 2
# LongParameterList: 1
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 92936fdc3ad..4f329ad302f 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -1345,6 +1345,11 @@
+
+
+
+
+
@@ -1358,6 +1363,11 @@
+
+
+
+
+
@@ -1419,6 +1429,14 @@
+
+
+
+
+
+
+
+
@@ -1477,6 +1495,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1538,6 +1566,14 @@
+
+
+
+
+
+
+
+
@@ -1596,6 +1632,11 @@
+
+
+
+
+
@@ -1660,6 +1701,14 @@
+
+
+
+
+
+
+
+
@@ -1721,6 +1770,11 @@
+
+
+
+
+
@@ -1785,6 +1839,14 @@
+
+
+
+
+
+
+
+
@@ -1843,6 +1905,11 @@
+
+
+
+
+
@@ -1884,6 +1951,14 @@
+
+
+
+
+
+
+
+
@@ -1972,6 +2047,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1982,6 +2070,11 @@
+
+
+
+
+
@@ -2038,6 +2131,14 @@
+
+
+
+
+
+
+
+
@@ -2144,6 +2245,11 @@
+
+
+
+
+
@@ -2208,6 +2314,14 @@
+
+
+
+
+
+
+
+
@@ -2269,6 +2383,11 @@
+
+
+
+
+
@@ -2301,6 +2420,14 @@
+
+
+
+
+
+
+
+
@@ -2330,6 +2457,11 @@
+
+
+
+
+
@@ -2346,6 +2478,14 @@
+
+
+
+
+
+
+
+
@@ -2364,6 +2504,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -2433,6 +2583,14 @@
+
+
+
+
+
+
+
+
@@ -2502,6 +2660,11 @@
+
+
+
+
+
@@ -2571,6 +2734,14 @@
+
+
+
+
+
+
+
+
@@ -2637,6 +2808,11 @@
+
+
+
+
+
@@ -2701,6 +2877,14 @@
+
+
+
+
+
+
+
+
@@ -2762,6 +2946,11 @@
+
+
+
+
+
@@ -2831,6 +3020,14 @@
+
+
+
+
+
+
+
+
@@ -2897,6 +3094,11 @@
+
+
+
+
+
@@ -2961,6 +3163,14 @@
+
+
+
+
+
+
+
+
@@ -3022,6 +3232,11 @@
+
+
+
+
+
@@ -3086,6 +3301,14 @@
+
+
+
+
+
+
+
+
@@ -3150,6 +3373,14 @@
+
+
+
+
+
+
+
+
@@ -3216,6 +3447,11 @@
+
+
+
+
+
@@ -3285,6 +3521,14 @@
+
+
+
+
+
+
+
+
@@ -3348,6 +3592,11 @@
+
+
+
+
+
@@ -3399,6 +3648,14 @@
+
+
+
+
+
+
+
+
@@ -3462,6 +3719,11 @@
+
+
+
+
+
@@ -3513,6 +3775,14 @@
+
+
+
+
+
+
+
+
@@ -3579,6 +3849,11 @@
+
+
+
+
+
@@ -3648,6 +3923,14 @@
+
+
+
+
+
+
+
+
@@ -3714,6 +3997,11 @@
+
+
+
+
+
@@ -3778,6 +4066,14 @@
+
+
+
+
+
+
+
+
@@ -3839,6 +4135,11 @@
+
+
+
+
+
@@ -3903,6 +4204,14 @@
+
+
+
+
+
+
+
+
@@ -16749,6 +17058,14 @@
+
+
+
+
+
+
+
+
@@ -16873,6 +17190,14 @@
+
+
+
+
+
+
+
+
@@ -16993,6 +17318,14 @@
+
+
+
+
+
+
+
+
@@ -18512,6 +18845,14 @@
+
+
+
+
+
+
+
+
@@ -18602,6 +18943,11 @@
+
+
+
+
+
@@ -19594,6 +19940,14 @@
+
+
+
+
+
+
+
+
@@ -21864,6 +22218,14 @@
+
+
+
+
+
+
+
+
@@ -25192,6 +25554,14 @@
+
+
+
+
+
+
+
+
@@ -25294,6 +25664,11 @@
+
+
+
+
+
@@ -26198,6 +26573,11 @@
+
+
+
+
+
@@ -26223,6 +26603,11 @@
+
+
+
+
+
@@ -26265,6 +26650,14 @@
+
+
+
+
+
+
+
+
@@ -26313,6 +26706,11 @@
+
+
+
+
+
@@ -26347,6 +26745,14 @@
+
+
+
+
+
+
+
+
@@ -26612,6 +27018,9 @@
+
+
+
@@ -27084,6 +27493,14 @@
+
+
+
+
+
+
+
+
@@ -27382,6 +27799,14 @@
+
+
+
+
+
+
+
+
@@ -27406,6 +27831,14 @@
+
+
+
+
+
+
+
+
@@ -27430,6 +27863,14 @@
+
+
+
+
+
+
+
+
@@ -27478,6 +27919,14 @@
+
+
+
+
+
+
+
+
@@ -27502,6 +27951,14 @@
+
+
+
+
+
+
+
+
@@ -27526,6 +27983,14 @@
+
+
+
+
+
+
+
+
@@ -27550,6 +28015,14 @@
+
+
+
+
+
+
+
+
@@ -27574,6 +28047,14 @@
+
+
+
+
+
+
+
+
@@ -27598,6 +28079,14 @@
+
+
+
+
+
+
+
+
@@ -27622,6 +28111,14 @@
+
+
+
+
+
+
+
+
@@ -27646,6 +28143,14 @@
+
+
+
+
+
+
+
+
@@ -27670,6 +28175,14 @@
+
+
+
+
+
+
+
+