diff --git a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt index 8feaebd460f..3269baf821b 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt @@ -57,10 +57,62 @@ data class UserProfileData( var website: String?, @JsonField(name = ["websiteScope"], typeConverter = ScopeConverter::class) - var websiteScope: Scope? + var websiteScope: Scope?, + + @JsonField(name = ["biography"]) + var biography: String?, + + @JsonField(name = ["biographyScope"], typeConverter = ScopeConverter::class) + var biographyScope: Scope?, + + @JsonField(name = ["fediverse"]) + var fediverse: String?, + + @JsonField(name = ["fediverseScope"], typeConverter = ScopeConverter::class) + var fediverseScope: Scope?, + + @JsonField(name = ["headline"]) + var headline: String?, + + @JsonField(name = ["headlineScope"], typeConverter = ScopeConverter::class) + var headlineScope: Scope?, + + @JsonField(name = ["organisation"]) + var organisation: String?, + + @JsonField(name = ["organisationScope"], typeConverter = ScopeConverter::class) + var organisationScope: Scope?, + + @JsonField(name = ["profile_enabled"]) + var profileEnabled: String?, + + @JsonField(name = ["profile_enabledScope"], typeConverter = ScopeConverter::class) + var profileEnabledScope: Scope?, + + @JsonField(name = ["pronouns"]) + var pronouns: String?, + + @JsonField(name = ["pronounsScope"], typeConverter = ScopeConverter::class) + var pronounsScope: Scope?, + + @JsonField(name = ["role"]) + var role: String?, + + @JsonField(name = ["roleScope"], typeConverter = ScopeConverter::class) + var roleScope: Scope?, + + @JsonField(name = ["bluesky"]) + var bluesky: String?, + + @JsonField(name = ["blueskyScope"], typeConverter = ScopeConverter::class) + var blueskyScope: Scope? ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null, null, null, null, null, null, null, null, null, null, null, null, null) + constructor() : this( + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null + ) fun getValueByField(field: ProfileActivity.Field?): String? = when (field) { @@ -70,6 +122,14 @@ data class UserProfileData( ProfileActivity.Field.ADDRESS -> address ProfileActivity.Field.WEBSITE -> website ProfileActivity.Field.TWITTER -> twitter + ProfileActivity.Field.BIOGRAPHY -> biography + ProfileActivity.Field.FEDIVERSE -> fediverse + ProfileActivity.Field.HEADLINE -> headline + ProfileActivity.Field.ORGANISATION -> organisation + ProfileActivity.Field.PROFILE_ENABLED -> profileEnabled + ProfileActivity.Field.PRONOUNS -> pronouns + ProfileActivity.Field.ROLE -> role + ProfileActivity.Field.BLUESKY -> bluesky else -> "" } @@ -81,6 +141,14 @@ data class UserProfileData( ProfileActivity.Field.ADDRESS -> addressScope ProfileActivity.Field.WEBSITE -> websiteScope ProfileActivity.Field.TWITTER -> twitterScope + ProfileActivity.Field.BIOGRAPHY -> biographyScope + ProfileActivity.Field.FEDIVERSE -> fediverseScope + ProfileActivity.Field.HEADLINE -> headlineScope + ProfileActivity.Field.ORGANISATION -> organisationScope + ProfileActivity.Field.PROFILE_ENABLED -> profileEnabledScope + ProfileActivity.Field.PRONOUNS -> pronounsScope + ProfileActivity.Field.ROLE -> roleScope + ProfileActivity.Field.BLUESKY -> blueskyScope else -> null } } diff --git a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt index 212f416513c..e5324b5c3bf 100644 --- a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt @@ -22,6 +22,9 @@ import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.DrawableRes import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.core.content.ContextCompat @@ -83,6 +86,7 @@ class ProfileActivity : BaseActivity() { private var adapter: UserInfoAdapter? = null private var userInfo: UserProfileData? = null private var editableFields = ArrayList() + private var isProfileEnabled by mutableStateOf(false) private lateinit var pickImage: PickImage @@ -111,13 +115,30 @@ class ProfileActivity : BaseActivity() { } } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(KEY_EDIT_MODE, edit) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) binding = ActivityProfileBinding.inflate(layoutInflater) + edit = savedInstanceState?.getBoolean(KEY_EDIT_MODE) ?: false setupActionBar() setContentView(binding.root) initSystemBars() + val colorScheme = viewThemeUtils.getColorScheme(this) + binding.profileSettingEnabledProfile.apply { + setContent { + MaterialTheme(colorScheme = colorScheme) { + ProfileEnabledCard( + isEnabled = isProfileEnabled, + onCheckedChange = { isProfileEnabled = it } + ) + } + } + } } override fun onResume() { @@ -169,6 +190,43 @@ class ProfileActivity : BaseActivity() { }) } binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") } + + if (CapabilitiesUtil.canEditScopes(currentUser!!)) { + ncApi.getEditableUserProfileFields( + credentials, + ApiUtils.getUrlForUserFields(currentUser!!.baseUrl!!) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(userProfileFieldsOverall: UserProfileFieldsOverall) { + editableFields = userProfileFieldsOverall.ocs!!.data!! + invalidateOptionsMenu() + fetchUserProfile(credentials) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error loading editable user profile from server", e) + edit = false + fetchUserProfile(credentials) + } + + override fun onComplete() { + // unused atm + } + }) + } else { + fetchUserProfile(credentials) + } + + colorIcons() + } + + private fun fetchUserProfile(credentials: String?) { ncApi.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl!!)) .retry(DEFAULT_RETRIES) .subscribeOn(Schedulers.io()) @@ -195,8 +253,6 @@ class ProfileActivity : BaseActivity() { // unused atm } }) - - colorIcons() } private fun setupActionBar() { @@ -241,6 +297,7 @@ class ProfileActivity : BaseActivity() { item.icon = ContextCompat.getDrawable(this, R.drawable.ic_check) binding.emptyList.root.visibility = View.GONE binding.userinfoList.visibility = View.VISIBLE + binding.profileSettingEnabledProfile.visibility = View.VISIBLE if (CapabilitiesUtil.hasSpreedFeatureCapability( currentUser?.capabilities?.spreedCapability, SpreedFeatures.TEMP_USER_AVATAR_API @@ -278,7 +335,8 @@ class ProfileActivity : BaseActivity() { item.setTitle(R.string.edit) item.icon = ContextCompat.getDrawable(this, R.drawable.ic_edit) - binding.avatarButtons.visibility = View.INVISIBLE + binding.avatarButtons.visibility = View.GONE + binding.profileSettingEnabledProfile.visibility = View.GONE if (adapter!!.filteredDisplayList.isEmpty()) { binding.emptyList.root.visibility = View.VISIBLE binding.userinfoList.visibility = View.GONE @@ -318,6 +376,9 @@ class ProfileActivity : BaseActivity() { binding.userinfoFullName.text = userInfo?.displayName } binding.loadingContent.visibility = View.VISIBLE + + isProfileEnabled = "1" == userInfo?.profileEnabled + adapter!!.setData(createUserInfoDetails(userInfo)) if ( isAllEmpty( @@ -327,11 +388,19 @@ class ProfileActivity : BaseActivity() { userInfo?.email, userInfo?.address, userInfo?.twitter, - userInfo?.website + userInfo?.website, + userInfo?.biography, + userInfo?.bluesky, + userInfo?.fediverse, + userInfo?.headline, + userInfo?.organisation, + userInfo?.pronouns, + userInfo?.role ) ) ) { binding.userinfoList.visibility = View.GONE + binding.profileSettingEnabledProfile.visibility = if (edit) View.VISIBLE else View.GONE binding.loadingContent.visibility = View.GONE binding.emptyList.root.visibility = View.VISIBLE setErrorMessageForMultiList( @@ -342,37 +411,22 @@ class ProfileActivity : BaseActivity() { } else { binding.emptyList.root.visibility = View.GONE binding.loadingContent.visibility = View.GONE + binding.profileSettingEnabledProfile.visibility = if (edit) { + View.VISIBLE + } else { + View.GONE + } binding.userinfoList.visibility = View.VISIBLE } - - // show edit button - if (CapabilitiesUtil.canEditScopes(currentUser!!)) { - ncApi.getEditableUserProfileFields( - ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserFields(currentUser!!.baseUrl!!) + binding.avatarButtons.visibility = if (edit && + CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser?.capabilities?.spreedCapability, + SpreedFeatures.TEMP_USER_AVATAR_API ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(userProfileFieldsOverall: UserProfileFieldsOverall) { - editableFields = userProfileFieldsOverall.ocs!!.data!! - invalidateOptionsMenu() - adapter!!.notifyDataSetChanged() - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Error loading editable user profile from server", e) - edit = false - } - - override fun onComplete() { - // unused atm - } - }) + ) { + View.VISIBLE + } else { + View.GONE } } @@ -383,11 +437,11 @@ class ProfileActivity : BaseActivity() { binding.emptyList.emptyListIcon.setImageResource(errorResource) binding.emptyList.emptyListIcon.visibility = View.VISIBLE binding.emptyList.emptyListViewText.visibility = View.VISIBLE + binding.profileSettingEnabledProfile.visibility = View.GONE binding.userinfoList.visibility = View.GONE binding.loadingContent.visibility = View.GONE } - @Suppress("Detekt.LongMethod") private fun createUserInfoDetails(userInfo: UserProfileData?): List { val result: MutableList = LinkedList() @@ -403,11 +457,11 @@ class ProfileActivity : BaseActivity() { ) result.add( UserInfoDetailsItem( - R.drawable.ic_phone, - userInfo.phone, - resources!!.getString(R.string.user_info_phone), - Field.PHONE, - userInfo.phoneScope + R.drawable.ic_record_voice_over_24px, + userInfo.pronouns, + resources!!.getString(R.string.user_info_pronouns), + Field.PRONOUNS, + userInfo.pronounsScope ) ) result.add( @@ -419,6 +473,15 @@ class ProfileActivity : BaseActivity() { userInfo.emailScope ) ) + result.add( + UserInfoDetailsItem( + R.drawable.ic_phone, + userInfo.phone, + resources!!.getString(R.string.user_info_phone), + Field.PHONE, + userInfo.phoneScope + ) + ) result.add( UserInfoDetailsItem( R.drawable.ic_map_marker, @@ -446,6 +509,60 @@ class ProfileActivity : BaseActivity() { userInfo.twitterScope ) ) + result.add( + UserInfoDetailsItem( + R.drawable.ic_cloud_24px, + userInfo.bluesky, + resources!!.getString(R.string.user_info_bluesky), + Field.BLUESKY, + userInfo.blueskyScope + ) + ) + result.add( + UserInfoDetailsItem( + R.drawable.ic_fediverse_24px, + userInfo.fediverse, + resources!!.getString(R.string.user_info_fediverse), + Field.FEDIVERSE, + userInfo.fediverseScope + ) + ) + result.add( + UserInfoDetailsItem( + R.drawable.ic_home_work_24px, + userInfo.organisation, + resources!!.getString(R.string.user_info_organisation), + Field.ORGANISATION, + userInfo.organisationScope + ) + ) + result.add( + UserInfoDetailsItem( + R.drawable.ic_work_24px, + userInfo.role, + resources!!.getString(R.string.user_info_role), + Field.ROLE, + userInfo.roleScope + ) + ) + result.add( + UserInfoDetailsItem( + R.drawable.ic_info_24px, + userInfo.headline, + resources!!.getString(R.string.user_info_headline), + Field.HEADLINE, + userInfo.headlineScope + ) + ) + result.add( + UserInfoDetailsItem( + R.drawable.ic_article_24px, + userInfo.biography, + resources!!.getString(R.string.user_info_biography), + Field.BIOGRAPHY, + userInfo.biographyScope + ) + ) } return result } @@ -488,7 +605,7 @@ class ProfileActivity : BaseActivity() { ).show() adapter!!.updateFilteredList() adapter!!.notifyDataSetChanged() - Log.e(TAG, "Failed to saved: " + item.text + " as " + item.field, e) + Log.e(TAG, "Failed to save: " + item.text + " as " + item.field, e) } override fun onComplete() { @@ -503,6 +620,49 @@ class ProfileActivity : BaseActivity() { } adapter!!.updateFilteredList() } + + // Profile enabled + val originalProfileEnabled = "1" == userInfo?.profileEnabled + if (isProfileEnabled != originalProfileEnabled) { + val newValue = if (isProfileEnabled) "1" else "0" + val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) + ncApi.setUserData( + credentials, + ApiUtils.getUrlForUserData(currentUser!!.baseUrl!!, currentUser!!.userId!!), + Field.PROFILE_ENABLED.fieldName, + newValue + ) + .retry(DEFAULT_RETRIES) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + Log.d(TAG, "Successfully saved: $newValue as ${Field.PROFILE_ENABLED.fieldName}") + userInfo?.profileEnabled = newValue + } + + override fun onError(e: Throwable) { + isProfileEnabled = originalProfileEnabled + Snackbar.make( + binding.root, + String.format( + resources!!.getString(R.string.failed_to_save), + Field.PROFILE_ENABLED.fieldName + ), + Snackbar.LENGTH_LONG + ).show() + Log.e(TAG, "Failed to save: $newValue as ${Field.PROFILE_ENABLED.fieldName}", e) + } + + override fun onComplete() { + // unused atm + } + }) + } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { @@ -695,7 +855,8 @@ class ProfileActivity : BaseActivity() { data = itemData, listeners = listeners, position = itemPosition, - enabled = profileActivity.editableFields.contains(item.field.toString().lowercase()) + enabled = profileActivity.editableFields.contains(item.field.toString().lowercase()), + multiLine = item.field == Field.BIOGRAPHY ) } } @@ -741,12 +902,21 @@ class ProfileActivity : BaseActivity() { PHONE("phone", "phoneScope"), ADDRESS("address", "addressScope"), WEBSITE("website", "websiteScope"), - TWITTER("twitter", "twitterScope") + TWITTER("twitter", "twitterScope"), + BIOGRAPHY("biography", "biographyScope"), + FEDIVERSE("fediverse", "fediverseScope"), + HEADLINE("headline", "headlineScope"), + ORGANISATION("organisation", "organisationScope"), + PROFILE_ENABLED("profile_enabled", "profile_enabledScope"), + PRONOUNS("pronouns", "pronounsScope"), + ROLE("role", "roleScope"), + BLUESKY("bluesky", "blueskyScope") } companion object { private val TAG = ProfileActivity::class.java.simpleName private const val DEFAULT_CACHE_SIZE: Int = 20 private const val DEFAULT_RETRIES: Long = 3 + private const val KEY_EDIT_MODE = "edit_mode" } } diff --git a/app/src/main/java/com/nextcloud/talk/profile/ProfileEnabledCard.kt b/app/src/main/java/com/nextcloud/talk/profile/ProfileEnabledCard.kt new file mode 100644 index 00000000000..5358880570e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/profile/ProfileEnabledCard.kt @@ -0,0 +1,99 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.nextcloud.talk.profile + +import android.content.res.Configuration +import androidx.compose.foundation.isSystemInDarkTheme +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.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.nextcloud.talk.R + +@Composable +fun ProfileEnabledCard(isEnabled: Boolean, onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(R.drawable.ic_id_card_24px), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.width(16.dp)) + Card(modifier = Modifier.weight(1f), onClick = { onCheckedChange(!isEnabled) }) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource( + if (isEnabled) { + R.string.user_info_profile_disable + } else { + R.string.user_info_profile_enable + } + ), + style = MaterialTheme.typography.titleMedium, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f) + ) + Spacer(modifier = Modifier.width(16.dp)) + Switch( + checked = isEnabled, + onCheckedChange = onCheckedChange + ) + } + } + Spacer(modifier = Modifier.width(32.dp)) + } +} + +@Preview(name = "Light", showBackground = true) +@Preview(name = "Dark", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewProfileEnabledCard() { + val colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme() + MaterialTheme(colorScheme = colorScheme) { + Surface { + ProfileEnabledCard(isEnabled = true, onCheckedChange = {}, modifier = Modifier.padding(16.dp)) + } + } +} + +@Preview(name = "RTL · Arabic", showBackground = true, locale = "ar") +@Composable +private fun PreviewProfileEnabledCardRtl() { + MaterialTheme(colorScheme = lightColorScheme()) { + Surface { + ProfileEnabledCard(isEnabled = true, onCheckedChange = {}, modifier = Modifier.padding(16.dp)) + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/profile/UserInfoDetailItemEditable.kt b/app/src/main/java/com/nextcloud/talk/profile/UserInfoDetailItemEditable.kt index cb31de91ffd..8891ef19e57 100644 --- a/app/src/main/java/com/nextcloud/talk/profile/UserInfoDetailItemEditable.kt +++ b/app/src/main/java/com/nextcloud/talk/profile/UserInfoDetailItemEditable.kt @@ -50,7 +50,8 @@ fun UserInfoDetailItemEditable( data: UserInfoDetailItemData, listeners: UserInfoDetailListeners, position: UserInfoDetailItemPosition, - enabled: Boolean = true + enabled: Boolean = true, + multiLine: Boolean = false ) { val topPadding = if (position == UserInfoDetailItemPosition.FIRST) 0.dp else 8.dp val bottomPadding = if (position == UserInfoDetailItemPosition.LAST) 16.dp else 8.dp @@ -80,9 +81,9 @@ fun UserInfoDetailItemEditable( }, modifier = Modifier.weight(1f), label = { Text(data.hint) }, - singleLine = true, enabled = enabled, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + singleLine = !multiLine, + keyboardOptions = KeyboardOptions(imeAction = if (multiLine) ImeAction.Default else ImeAction.Next), colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = MaterialTheme.colorScheme.primary, focusedLabelColor = MaterialTheme.colorScheme.primary, diff --git a/app/src/main/res/drawable/ic_article_24px.xml b/app/src/main/res/drawable/ic_article_24px.xml new file mode 100644 index 00000000000..b7b5a842ca9 --- /dev/null +++ b/app/src/main/res/drawable/ic_article_24px.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/drawable/ic_cloud_24px.xml b/app/src/main/res/drawable/ic_cloud_24px.xml new file mode 100644 index 00000000000..316ed9b34c5 --- /dev/null +++ b/app/src/main/res/drawable/ic_cloud_24px.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_fediverse_24px.xml b/app/src/main/res/drawable/ic_fediverse_24px.xml new file mode 100644 index 00000000000..5e22e90da63 --- /dev/null +++ b/app/src/main/res/drawable/ic_fediverse_24px.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/drawable/ic_home_work_24px.xml b/app/src/main/res/drawable/ic_home_work_24px.xml new file mode 100644 index 00000000000..b58a69bc3b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_work_24px.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_id_card_24px.xml b/app/src/main/res/drawable/ic_id_card_24px.xml new file mode 100644 index 00000000000..afe75351193 --- /dev/null +++ b/app/src/main/res/drawable/ic_id_card_24px.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_record_voice_over_24px.xml b/app/src/main/res/drawable/ic_record_voice_over_24px.xml new file mode 100644 index 00000000000..a9fb675ced6 --- /dev/null +++ b/app/src/main/res/drawable/ic_record_voice_over_24px.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_work_24px.xml b/app/src/main/res/drawable/ic_work_24px.xml new file mode 100644 index 00000000000..564cbd67d8c --- /dev/null +++ b/app/src/main/res/drawable/ic_work_24px.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/layout-land/activity_profile.xml b/app/src/main/res/layout-land/activity_profile.xml index f66f4a44f4b..db6d7ddf4fe 100644 --- a/app/src/main/res/layout-land/activity_profile.xml +++ b/app/src/main/res/layout-land/activity_profile.xml @@ -47,8 +47,8 @@ @@ -148,6 +148,16 @@ + + + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> + + 40dp 30dp 96dp + 72dp 52dp 16sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c6dd2cc04d..869ba9ee80c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -682,6 +682,15 @@ How to translate with transifex: Website Twitter Full name + Biography + Fediverse + Headline + Organization + Enable profile + Disable profile + Pronouns + Role + Bluesky folder Loading … Edit diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 9dc6fc19c47..45c850189f0 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 99 warnings + Lint Report: 98 warnings