Merge branch 'develop' into feature/bma/avoid_useless_codegen

This commit is contained in:
Benoit Marty 2021-12-10 16:09:47 +01:00 committed by GitHub
commit ba8121641f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 827 additions and 395 deletions

View file

@ -34,7 +34,7 @@ jobs:
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:String!,$contentid:String!) {
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
@ -47,45 +47,30 @@ jobs:
PROJECT_ID: "PN_kwDOAM0swc0sUA"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
spaces_issues_to_old_board:
name: Spaces issues to old Delight project board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Spaces') ||
contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
contains(github.event.issue.labels.*.name, 'A-Subspaces')
steps:
- uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
with:
action-token: "${{ secrets.ELEMENT_BOT_TOKEN }}"
project-url: "https://github.com/orgs/vector-im/projects/6"
column-name: "📥 Inbox"
label-name: "A-Spaces"
spaces_issues_to_new_board:
name: Spaces issues to new Delight project board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Spaces') ||
contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
contains(github.event.issue.labels.*.name, 'A-Subspaces')
steps:
- uses: octokit/graphql-action@v2.x
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:String!,$contentid:String!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc1HvQ"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
# delight_issues_to_board:
# name: Spaces issues to new Delight project board
# runs-on: ubuntu-latest
# if: >
# contains(github.event.issue.labels.*.name, 'A-Spaces') ||
# contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
# contains(github.event.issue.labels.*.name, 'A-Subspaces')
# steps:
# - uses: octokit/graphql-action@v2.x
# with:
# headers: '{"GraphQL-Features": "projects_next_graphql"}'
# query: |
# mutation add_to_project($projectid:ID!,$contentid:ID!) {
# addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
# projectNextItem {
# id
# }
# }
# }
# projectid: ${{ env.PROJECT_ID }}
# contentid: ${{ github.event.issue.node_id }}
# env:
# PROJECT_ID: "PN_kwDOAM0swc1HvQ"
# GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_voice-message_issues:
name: A-Voice Messages to voice message board
@ -97,7 +82,7 @@ jobs:
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:String!,$contentid:String!) {
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
@ -120,7 +105,7 @@ jobs:
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:String!,$contentid:String!) {
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
@ -132,3 +117,26 @@ jobs:
env:
PROJECT_ID: "PN_kwDOAM0swc0rRA"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_message_bubbles_issues:
name: A-Message-Bubbles to Message bubbles board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles')
steps:
- uses: octokit/graphql-action@v2.x
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc3m-g"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

1
changelog.d/4278.feature Normal file
View file

@ -0,0 +1 @@
Updates URL previews to match latest designs

1
changelog.d/4638.feature Normal file
View file

@ -0,0 +1 @@
Add a help section in the settings.

1
changelog.d/4660.feature Normal file
View file

@ -0,0 +1 @@
Create a legal screen in the setting to group all the different policies.

1
changelog.d/4666.misc Normal file
View file

@ -0,0 +1 @@
Add automation to move message bubbles issues to message bubbles board.

1
changelog.d/4671.misc Normal file
View file

@ -0,0 +1 @@
Fix graphql warning in issue workflow automation

View file

@ -7,7 +7,7 @@ ext.versions = [
'targetCompat' : JavaVersion.VERSION_11,
]
def gradle = "7.0.3"
def gradle = "7.0.4"
// Ref: https://kotlinlang.org/releases.html
def kotlin = "1.5.31"
def kotlinCoroutines = "1.5.2"
@ -19,7 +19,7 @@ def moshi = "1.12.0"
def lifecycle = "2.4.0"
def flowBinding = "1.2.0"
def epoxy = "4.6.2"
def mavericks = "2.4.0"
def mavericks = "2.5.0"
def glide = "4.12.0"
def bigImageViewer = "1.8.1"
def jjwt = "0.11.2"

View file

@ -39,4 +39,7 @@
<!-- Navigation Drawer -->
<dimen name="navigation_drawer_max_width">320dp</dimen>
<!-- Preview Url -->
<dimen name="preview_url_view_corner_radius">8dp</dimen>
</resources>

View file

@ -9,7 +9,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:10.8.1"
classpath "io.realm:realm-gradle-plugin:10.9.0"
}
}
@ -158,7 +158,7 @@ dependencies {
implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.38'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.39'
testImplementation libs.tests.junit
testImplementation 'org.robolectric:robolectric:4.7.3'

View file

@ -16,6 +16,8 @@
package org.matrix.android.sdk.api.session.terms
import org.matrix.android.sdk.internal.session.terms.TermsResponse
interface TermsService {
enum class ServiceType {
IntegrationManager,
@ -28,4 +30,10 @@ interface TermsService {
baseUrl: String,
agreedUrls: List<String>,
token: String?)
/**
* Get the homeserver terms, from the register API.
* Will be updated once https://github.com/matrix-org/matrix-doc/pull/3012 will be implemented.
*/
suspend fun getHomeserverTerms(baseUrl: String): TermsResponse
}

View file

@ -18,10 +18,13 @@ package org.matrix.android.sdk.internal.session.terms
import dagger.Lazy
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
import org.matrix.android.sdk.internal.network.NetworkConstants
import org.matrix.android.sdk.internal.network.RetrofitFactory
@ -55,6 +58,27 @@ internal class DefaultTermsService @Inject constructor(
return GetTermsResponse(termsResponse, getAlreadyAcceptedTermUrlsFromAccountData())
}
/**
* We use a trick here to get the homeserver T&C, we use the register API
*/
override suspend fun getHomeserverTerms(baseUrl: String): TermsResponse {
return try {
executeRequest(null) {
termsAPI.register(baseUrl + NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
}
// Return empty result if it succeed, but it should never happen
TermsResponse()
} catch (throwable: Throwable) {
@Suppress("UNCHECKED_CAST")
TermsResponse(
policies = (throwable.toRegistrationFlowResponse()
?.params
?.get(LoginFlowTypes.TERMS) as? JsonDict)
?.get("policies") as? JsonDict
)
}
}
override suspend fun agreeToTerms(serviceType: TermsService.ServiceType,
baseUrl: String,
agreedUrls: List<String>,
@ -91,7 +115,7 @@ internal class DefaultTermsService @Inject constructor(
private fun buildUrl(baseUrl: String, serviceType: TermsService.ServiceType): String {
val servicePath = when (serviceType) {
TermsService.ServiceType.IntegrationManager -> NetworkConstants.URI_INTEGRATION_MANAGER_PATH
TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2
TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2
}
return "${baseUrl.ensureTrailingSlash()}$servicePath"
}

View file

@ -16,6 +16,8 @@
package org.matrix.android.sdk.internal.session.terms
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.emptyJsonDict
import org.matrix.android.sdk.internal.network.HttpHeaders
import retrofit2.http.Body
import retrofit2.http.GET
@ -37,4 +39,12 @@ internal interface TermsAPI {
suspend fun agreeToTerms(@Url url: String,
@Body params: AcceptTermsBody,
@Header(HttpHeaders.Authorization) token: String)
/**
* API to retrieve the terms for a homeserver. The API /terms does not exist yet, so retrieve the terms from the login flow.
* We do not care about the result (Credentials)
*/
@POST
suspend fun register(@Url url: String,
@Body body: JsonDict = emptyJsonDict)
}

View file

@ -141,7 +141,6 @@ android {
resValue "string", "build_number", "\"${buildNumber}\""
buildConfigField "im.vector.app.features.VectorFeatures.LoginVersion", "LOGIN_VERSION", "im.vector.app.features.VectorFeatures.LoginVersion.V1"
buildConfigField "im.vector.app.features.VectorFeatures.NotificationSettingsVersion", "NOTIFICATION_SETTINGS_VERSION", "im.vector.app.features.VectorFeatures.NotificationSettingsVersion.V2"
buildConfigField "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy", "outboundSessionKeySharingStrategy", "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy.WhenTyping"
@ -365,7 +364,7 @@ dependencies {
implementation 'com.facebook.stetho:stetho:1.6.0'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.38'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.39'
// FlowBinding
implementation libs.github.flowBinding

View file

@ -18,29 +18,19 @@ package im.vector.app.ui.robot.settings
import androidx.test.espresso.Espresso.pressBack
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.espresso.tools.clickOnPreference
import im.vector.app.features.VectorFeatures
class SettingsNotificationsRobot {
fun crawl() {
when (BuildConfig.NOTIFICATION_SETTINGS_VERSION!!) {
VectorFeatures.NotificationSettingsVersion.V1 -> {
clickOn(R.string.settings_notification_advanced)
pressBack()
}
VectorFeatures.NotificationSettingsVersion.V2 -> {
clickOn(R.string.settings_notification_default)
pressBack()
clickOn(R.string.settings_notification_mentions_and_keywords)
// TODO Test adding a keyword?
pressBack()
clickOn(R.string.settings_notification_other)
pressBack()
}
}
clickOn(R.string.settings_notification_default)
pressBack()
clickOn(R.string.settings_notification_mentions_and_keywords)
// TODO Test adding a keyword?
pressBack()
clickOn(R.string.settings_notification_other)
pressBack()
/*
clickOn(R.string.settings_noisy_notifications_preferences)

View file

@ -133,6 +133,7 @@ import im.vector.app.features.settings.devtools.KeyRequestsFragment
import im.vector.app.features.settings.devtools.OutgoingKeyRequestListFragment
import im.vector.app.features.settings.homeserver.HomeserverSettingsFragment
import im.vector.app.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.app.features.settings.legals.LegalsFragment
import im.vector.app.features.settings.locale.LocalePickerFragment
import im.vector.app.features.settings.notifications.VectorSettingsAdvancedNotificationPreferenceFragment
import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment
@ -699,6 +700,11 @@ interface FragmentModule {
@FragmentKey(DiscoverySettingsFragment::class)
fun bindDiscoverySettingsFragment(fragment: DiscoverySettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LegalsFragment::class)
fun bindLegalsFragment(fragment: LegalsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(ReviewTermsFragment::class)

View file

@ -85,6 +85,7 @@ import im.vector.app.features.settings.devtools.KeyRequestListViewModel
import im.vector.app.features.settings.devtools.KeyRequestViewModel
import im.vector.app.features.settings.homeserver.HomeserverSettingsViewModel
import im.vector.app.features.settings.ignored.IgnoredUsersViewModel
import im.vector.app.features.settings.legals.LegalsViewModel
import im.vector.app.features.settings.locale.LocalePickerViewModel
import im.vector.app.features.settings.push.PushGatewaysViewModel
import im.vector.app.features.settings.threepids.ThreePidsSettingsViewModel
@ -504,6 +505,11 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(DiscoverySettingsViewModel::class)
fun discoverySettingsViewModelFactory(factory: DiscoverySettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(LegalsViewModel::class)
fun legalsViewModelFactory(factory: LegalsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(RoomDetailViewModel::class)

View file

@ -20,7 +20,7 @@ import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.isVisible
import androidx.core.view.isInvisible
import im.vector.app.R
import im.vector.app.features.home.room.detail.timeline.item.SendStateDecoration
import im.vector.app.features.themes.ThemeUtils
@ -38,28 +38,28 @@ class SendStateImageView @JvmOverloads constructor(
}
fun render(sendState: SendStateDecoration) {
isVisible = when (sendState) {
isInvisible = when (sendState) {
SendStateDecoration.SENDING_NON_MEDIA -> {
setImageResource(R.drawable.ic_sending_message)
imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(context, R.attr.vctr_content_tertiary))
contentDescription = context.getString(R.string.event_status_a11y_sending)
true
false
}
SendStateDecoration.SENT -> {
setImageResource(R.drawable.ic_message_sent)
imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(context, R.attr.vctr_content_tertiary))
contentDescription = context.getString(R.string.event_status_a11y_sent)
true
false
}
SendStateDecoration.FAILED -> {
setImageResource(R.drawable.ic_sending_message_failed)
imageTintList = null
contentDescription = context.getString(R.string.event_status_a11y_failed)
true
false
}
SendStateDecoration.SENDING_MEDIA,
SendStateDecoration.NONE -> {
false
true
}
}
}

View file

@ -23,7 +23,7 @@ import android.webkit.WebViewClient
import android.widget.TextView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R
import im.vector.app.features.discovery.IdentityServerWithTerms
import im.vector.app.features.discovery.ServerAndPolicies
import me.gujun.android.span.link
import me.gujun.android.span.span
@ -45,7 +45,7 @@ fun Context.displayInWebView(url: String) {
.show()
}
fun Context.showIdentityServerConsentDialog(identityServerWithTerms: IdentityServerWithTerms?,
fun Context.showIdentityServerConsentDialog(identityServerWithTerms: ServerAndPolicies?,
consentCallBack: (() -> Unit)) {
// Build the message
val content = span {

View file

@ -21,7 +21,6 @@ import im.vector.app.BuildConfig
interface VectorFeatures {
fun loginVersion(): LoginVersion
fun notificationSettingsVersion(): NotificationSettingsVersion
enum class LoginVersion {
V1,
@ -36,5 +35,4 @@ interface VectorFeatures {
class DefaultVectorFeatures : VectorFeatures {
override fun loginVersion(): VectorFeatures.LoginVersion = BuildConfig.LOGIN_VERSION
override fun notificationSettingsVersion(): VectorFeatures.NotificationSettingsVersion = BuildConfig.NOTIFICATION_SETTINGS_VERSION
}

View file

@ -17,9 +17,9 @@
package im.vector.app.features.contactsbook
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.features.discovery.IdentityServerWithTerms
import im.vector.app.features.discovery.ServerAndPolicies
sealed class ContactsBookViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : ContactsBookViewEvents()
data class OnPoliciesRetrieved(val identityServerWithTerms: IdentityServerWithTerms?) : ContactsBookViewEvents()
data class OnPoliciesRetrieved(val identityServerWithTerms: ServerAndPolicies?) : ContactsBookViewEvents()
}

View file

@ -24,6 +24,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_discovery_policy)
abstract class DiscoveryPolicyItem : EpoxyModelWithHolder<DiscoveryPolicyItem.Holder>() {
@ -40,7 +41,7 @@ abstract class DiscoveryPolicyItem : EpoxyModelWithHolder<DiscoveryPolicyItem.Ho
override fun bind(holder: Holder) {
super.bind(holder)
holder.title.text = name
holder.url.text = url
holder.url.setTextOrHide(url)
holder.view.onClick(clickListener)
}

View file

@ -433,6 +433,6 @@ class DiscoverySettingsController @Inject constructor(
fun onTapUpdateUserConsent(newValue: Boolean)
fun onTapRetryToRetrieveBindings()
fun onPolicyUrlsExpandedStateToggled(newExpandedState: Boolean)
fun onPolicyTapped(policy: IdentityServerPolicy)
fun onPolicyTapped(policy: ServerPolicy)
}
}

View file

@ -167,10 +167,11 @@ class DiscoverySettingsFragment @Inject constructor(
val pidList = state.emailList().orEmpty() + state.phoneNumbersList().orEmpty()
val hasBoundIds = pidList.any { it.isShared() == SharedState.SHARED }
val serverUrl = state.identityServer()?.serverUrl.orEmpty()
val message = if (hasBoundIds) {
getString(R.string.settings_discovery_disconnect_with_bound_pid, state.identityServer(), state.identityServer())
getString(R.string.settings_discovery_disconnect_with_bound_pid, serverUrl, serverUrl)
} else {
getString(R.string.disconnect_identity_server_dialog_content, state.identityServer())
getString(R.string.disconnect_identity_server_dialog_content, serverUrl)
}
MaterialAlertDialogBuilder(requireActivity())
@ -203,7 +204,7 @@ class DiscoverySettingsFragment @Inject constructor(
viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = newExpandedState))
}
override fun onPolicyTapped(policy: IdentityServerPolicy) {
override fun onPolicyTapped(policy: ServerPolicy) {
openUrlInChromeCustomTab(requireContext(), null, policy.url)
}

View file

@ -21,7 +21,7 @@ import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized
data class DiscoverySettingsState(
val identityServer: Async<IdentityServerWithTerms?> = Uninitialized,
val identityServer: Async<ServerAndPolicies?> = Uninitialized,
val emailList: Async<List<PidInfo>> = Uninitialized,
val phoneNumbersList: Async<List<PidInfo>> = Uninitialized,
// Can be true if terms are updated

View file

@ -78,7 +78,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
init {
setState {
copy(
identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { IdentityServerWithTerms(it, emptyList()) }),
identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { ServerAndPolicies(it, emptyList()) }),
userConsent = identityService.getUserConsent()
)
}
@ -151,7 +151,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
val data = session.identityService().setNewIdentityServer(action.url)
setState {
copy(
identityServer = Success(IdentityServerWithTerms(data, emptyList())),
identityServer = Success(ServerAndPolicies(data, emptyList())),
userConsent = false
)
}
@ -401,7 +401,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
}
}
private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms? {
private suspend fun fetchIdentityServerWithTerms(): ServerAndPolicies? {
return session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language))
}
}

View file

@ -19,22 +19,35 @@ package im.vector.app.features.discovery
import im.vector.app.core.utils.ensureProtocol
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.internal.session.terms.TermsResponse
suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): IdentityServerWithTerms? {
val identityServerUrl = identityService().getCurrentIdentityServerUrl()
return identityServerUrl?.let {
val terms = getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol())
.serverResponse
.getLocalizedTerms(userLanguage)
val policyUrls = terms.mapNotNull {
val name = it.localizedName ?: it.policyName
val url = it.localizedUrl
if (name == null || url == null) {
null
} else {
IdentityServerPolicy(name = name, url = url)
suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): ServerAndPolicies? {
return identityService().getCurrentIdentityServerUrl()
?.let { identityServerUrl ->
val termsResponse = getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol())
.serverResponse
buildServerAndPolicies(identityServerUrl, termsResponse, userLanguage)
}
}
IdentityServerWithTerms(identityServerUrl, policyUrls)
}
}
suspend fun Session.fetchHomeserverWithTerms(userLanguage: String): ServerAndPolicies {
val homeserverUrl = sessionParams.homeServerUrl
val terms = getHomeserverTerms(homeserverUrl.ensureProtocol())
return buildServerAndPolicies(homeserverUrl, terms, userLanguage)
}
private fun buildServerAndPolicies(serviceUrl: String,
termsResponse: TermsResponse,
userLanguage: String): ServerAndPolicies {
val terms = termsResponse.getLocalizedTerms(userLanguage)
val policyUrls = terms.mapNotNull {
val name = it.localizedName ?: it.policyName
val url = it.localizedUrl
if (name == null || url == null) {
null
} else {
ServerPolicy(name = name, url = url)
}
}
return ServerAndPolicies(serviceUrl, policyUrls)
}

View file

@ -16,12 +16,12 @@
package im.vector.app.features.discovery
data class IdentityServerWithTerms(
data class ServerAndPolicies(
val serverUrl: String,
val policies: List<IdentityServerPolicy>
val policies: List<ServerPolicy>
)
data class IdentityServerPolicy(
data class ServerPolicy(
val name: String,
val url: String
)

View file

@ -43,22 +43,12 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
lateinit var dimensionConverter: DimensionConverter
protected var ignoreSendStatusVisibility = false
@CallSuper
override fun bind(holder: H) {
super.bind(holder)
holder.leftGuideline.updateLayoutParams<RelativeLayout.LayoutParams> {
this.marginStart = leftGuideline
}
// Ignore visibility of the send status icon?
holder.contentContainer.updateLayoutParams<RelativeLayout.LayoutParams> {
if (ignoreSendStatusVisibility) {
addRule(RelativeLayout.ALIGN_PARENT_END)
} else {
removeRule(RelativeLayout.ALIGN_PARENT_END)
}
}
holder.checkableBackground.isChecked = highlighted
}

View file

@ -33,10 +33,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlayb
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
init {
ignoreSendStatusVisibility = true
}
@EpoxyAttribute
var mxcUrl: String = ""

View file

@ -19,13 +19,14 @@ package im.vector.app.features.home.room.detail.timeline.url
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import com.google.android.material.card.MaterialCardView
import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.databinding.ViewUrlPreviewBinding
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.themes.ThemeUtils
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.media.PreviewUrlData
@ -36,7 +37,7 @@ class PreviewUrlView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener {
) : MaterialCardView(context, attrs, defStyleAttr), View.OnClickListener {
private lateinit var views: ViewUrlPreviewBinding
@ -44,6 +45,9 @@ class PreviewUrlView @JvmOverloads constructor(
init {
setupView()
radius = resources.getDimensionPixelSize(R.dimen.preview_url_view_corner_radius).toFloat()
cardElevation = 0f
setCardBackgroundColor(ThemeUtils.getColor(context, R.attr.vctr_system))
}
private var state: PreviewUrlUiState = PreviewUrlUiState.Unknown
@ -121,9 +125,15 @@ class PreviewUrlView @JvmOverloads constructor(
private fun renderData(previewUrlData: PreviewUrlData, imageContentRenderer: ImageContentRenderer) {
isVisible = true
views.urlPreviewTitle.setTextOrHide(previewUrlData.title)
views.urlPreviewImage.isVisible = previewUrlData.mxcUrl?.let { imageContentRenderer.render(it, views.urlPreviewImage) }.orFalse()
views.urlPreviewDescription.setTextOrHide(previewUrlData.description)
views.urlPreviewDescription.maxLines = when {
previewUrlData.mxcUrl != null -> 2
previewUrlData.title != null -> 3
else -> 5
}
views.urlPreviewSite.setTextOrHide(previewUrlData.siteName.takeIf { it != previewUrlData.title })
}

View file

@ -44,7 +44,6 @@ import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.databinding.FragmentRoomListBinding
import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
@ -476,7 +475,7 @@ class RoomListFragment @Inject constructor(
footerController.setData(it)
}
RoomListQuickActionsBottomSheet
.newInstance(room.roomId, RoomListActionsArgs.Mode.FULL)
.newInstance(room.roomId)
.show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS")
return true
}

View file

@ -43,15 +43,8 @@ import javax.inject.Inject
@Parcelize
data class RoomListActionsArgs(
val roomId: String,
val mode: Mode
) : Parcelable {
enum class Mode {
FULL,
NOTIFICATIONS
}
}
val roomId: String
) : Parcelable
/**
* Bottom sheet fragment that shows room information with list of contextual actions
@ -124,9 +117,9 @@ class RoomListQuickActionsBottomSheet :
}
companion object {
fun newInstance(roomId: String, mode: RoomListActionsArgs.Mode): RoomListQuickActionsBottomSheet {
fun newInstance(roomId: String): RoomListQuickActionsBottomSheet {
return RoomListQuickActionsBottomSheet().apply {
setArguments(RoomListActionsArgs(roomId, mode))
setArguments(RoomListActionsArgs(roomId))
}
}
}

View file

@ -24,7 +24,6 @@ import im.vector.app.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem
import im.vector.app.core.epoxy.profiles.notifications.radioButtonItem
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.VectorFeatures
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.roomprofile.notifications.notificationOptions
import im.vector.app.features.roomprofile.notifications.notificationStateMapped
@ -39,7 +38,6 @@ class RoomListQuickActionsEpoxyController @Inject constructor(
private val avatarRenderer: AvatarRenderer,
private val colorProvider: ColorProvider,
private val stringProvider: StringProvider,
private val features: VectorFeatures
) : TypedEpoxyController<RoomListQuickActionViewState>() {
var listener: Listener? = null
@ -48,54 +46,38 @@ class RoomListQuickActionsEpoxyController @Inject constructor(
val notificationViewState = state.notificationSettingsViewState
val roomSummary = notificationViewState.roomSummary() ?: return
val host = this
val isV2 = features.notificationSettingsVersion() == VectorFeatures.NotificationSettingsVersion.V2
// V2 always shows full details as we no longer display the sheet from RoomProfile > Notifications
val showFull = state.roomListActionsArgs.mode == RoomListActionsArgs.Mode.FULL || isV2
if (showFull) {
// Preview, favorite, settings
bottomSheetRoomPreviewItem {
id("room_preview")
avatarRenderer(host.avatarRenderer)
matrixItem(roomSummary.toMatrixItem())
stringProvider(host.stringProvider)
colorProvider(host.colorProvider)
izLowPriority(roomSummary.isLowPriority)
izFavorite(roomSummary.isFavorite)
settingsClickListener { host.listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) }
favoriteClickListener { host.listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Favorite(roomSummary.roomId)) }
lowPriorityClickListener { host.listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.LowPriority(roomSummary.roomId)) }
}
// Notifications
bottomSheetDividerItem {
id("notifications_separator")
}
// Preview, favorite, settings
bottomSheetRoomPreviewItem {
id("room_preview")
avatarRenderer(host.avatarRenderer)
matrixItem(roomSummary.toMatrixItem())
stringProvider(host.stringProvider)
colorProvider(host.colorProvider)
izLowPriority(roomSummary.isLowPriority)
izFavorite(roomSummary.isFavorite)
settingsClickListener { host.listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) }
favoriteClickListener { host.listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Favorite(roomSummary.roomId)) }
lowPriorityClickListener { host.listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.LowPriority(roomSummary.roomId)) }
}
if (isV2) {
notificationViewState.notificationOptions.forEach { notificationState ->
val title = titleForNotificationState(notificationState)
radioButtonItem {
id(notificationState.name)
titleRes(title)
selected(notificationViewState.notificationStateMapped() == notificationState)
listener {
host.listener?.didSelectRoomNotificationState(notificationState)
}
// Notifications
bottomSheetDividerItem {
id("notifications_separator")
}
notificationViewState.notificationOptions.forEach { notificationState ->
val title = titleForNotificationState(notificationState)
radioButtonItem {
id(notificationState.name)
titleRes(title)
selected(notificationViewState.notificationStateMapped() == notificationState)
listener {
host.listener?.didSelectRoomNotificationState(notificationState)
}
}
} else {
val selectedRoomState = notificationViewState.notificationState()
RoomListQuickActionsSharedAction.NotificationsAllNoisy(roomSummary.roomId).toBottomSheetItem(0, selectedRoomState)
RoomListQuickActionsSharedAction.NotificationsAll(roomSummary.roomId).toBottomSheetItem(1, selectedRoomState)
RoomListQuickActionsSharedAction.NotificationsMentionsOnly(roomSummary.roomId).toBottomSheetItem(2, selectedRoomState)
RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState)
}
if (showFull) {
RoomListQuickActionsSharedAction.Leave(roomSummary.roomId, showIcon = !isV2).toBottomSheetItem(5)
}
RoomListQuickActionsSharedAction.Leave(roomSummary.roomId, showIcon = !true).toBottomSheetItem()
}
@StringRes
@ -106,18 +88,11 @@ class RoomListQuickActionsEpoxyController @Inject constructor(
else -> null
}
private fun RoomListQuickActionsSharedAction.toBottomSheetItem(index: Int, roomNotificationState: RoomNotificationState? = null) {
private fun RoomListQuickActionsSharedAction.Leave.toBottomSheetItem() {
val host = this@RoomListQuickActionsEpoxyController
val selected = when (this) {
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> roomNotificationState == RoomNotificationState.ALL_MESSAGES_NOISY
is RoomListQuickActionsSharedAction.NotificationsAll -> roomNotificationState == RoomNotificationState.ALL_MESSAGES
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> roomNotificationState == RoomNotificationState.MENTIONS_ONLY
is RoomListQuickActionsSharedAction.NotificationsMute -> roomNotificationState == RoomNotificationState.MUTE
else -> false
}
return bottomSheetActionItem {
id("action_$index")
selected(selected)
id("action_leave")
selected(false)
if (iconResId != null) {
iconRes(iconResId)
} else {

View file

@ -44,13 +44,10 @@ import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentMatrixProfileBinding
import im.vector.app.databinding.ViewStubRoomProfileHeaderBinding
import im.vector.app.features.VectorFeatures
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailPendingAction
import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
import kotlinx.coroutines.flow.launchIn
@ -69,8 +66,7 @@ data class RoomProfileArgs(
class RoomProfileFragment @Inject constructor(
private val roomProfileController: RoomProfileController,
private val avatarRenderer: AvatarRenderer,
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
private val features: VectorFeatures
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
) :
VectorBaseFragment<FragmentMatrixProfileBinding>(),
RoomProfileController.Callback {
@ -259,16 +255,7 @@ class RoomProfileFragment @Inject constructor(
}
override fun onNotificationsClicked() {
when (features.notificationSettingsVersion()) {
VectorFeatures.NotificationSettingsVersion.V1 -> {
RoomListQuickActionsBottomSheet
.newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS)
.show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS")
}
VectorFeatures.NotificationSettingsVersion.V2 -> {
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificationSettings)
}
}
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificationSettings)
}
override fun onUploadsClicked() {

View file

@ -35,6 +35,7 @@ import javax.inject.Inject
class VectorPreferences @Inject constructor(private val context: Context) {
companion object {
const val SETTINGS_HELP_PREFERENCE_KEY = "SETTINGS_HELP_PREFERENCE_KEY"
const val SETTINGS_CHANGE_PASSWORD_PREFERENCE_KEY = "SETTINGS_CHANGE_PASSWORD_PREFERENCE_KEY"
const val SETTINGS_VERSION_PREFERENCE_KEY = "SETTINGS_VERSION_PREFERENCE_KEY"
const val SETTINGS_SDK_VERSION_PREFERENCE_KEY = "SETTINGS_SDK_VERSION_PREFERENCE_KEY"
@ -42,17 +43,8 @@ class VectorPreferences @Inject constructor(private val context: Context) {
const val SETTINGS_LOGGED_IN_PREFERENCE_KEY = "SETTINGS_LOGGED_IN_PREFERENCE_KEY"
const val SETTINGS_HOME_SERVER_PREFERENCE_KEY = "SETTINGS_HOME_SERVER_PREFERENCE_KEY"
const val SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY = "SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY"
const val SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY = "SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY"
const val SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY = "SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY"
const val SETTINGS_DISCOVERY_PREFERENCE_KEY = "SETTINGS_DISCOVERY_PREFERENCE_KEY"
const val SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY"
const val SETTINGS_NOTIFICATION_DEFAULT_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_DEFAULT_PREFERENCE_KEY"
const val SETTINGS_NOTIFICATION_KEYWORD_AND_MENTIONS_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_KEYWORD_AND_MENTIONS_PREFERENCE_KEY"
const val SETTINGS_NOTIFICATION_OTHER_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_OTHER_PREFERENCE_KEY"
const val SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY"
const val SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY"
const val SETTINGS_COPYRIGHT_PREFERENCE_KEY = "SETTINGS_COPYRIGHT_PREFERENCE_KEY"
const val SETTINGS_CLEAR_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_CACHE_PREFERENCE_KEY"
const val SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY"
const val SETTINGS_USER_SETTINGS_PREFERENCE_KEY = "SETTINGS_USER_SETTINGS_PREFERENCE_KEY"

View file

@ -22,11 +22,9 @@ import im.vector.app.R
import im.vector.app.core.preference.VectorPreference
import im.vector.app.core.utils.FirstThrottler
import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.displayInWebView
import im.vector.app.core.utils.openAppSettingsPage
import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.features.version.VersionProvider
import im.vector.app.openOssLicensesMenuActivity
import org.matrix.android.sdk.api.Matrix
import javax.inject.Inject
@ -40,6 +38,15 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
private val firstThrottler = FirstThrottler(1000)
override fun bindPref() {
// Help
findPreference<VectorPreference>(VectorPreferences.SETTINGS_HELP_PREFERENCE_KEY)!!
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.HELP)
}
false
}
// preference to start the App info screen, to facilitate App permissions access
findPreference<VectorPreference>(APP_INFO_LINK_PREFERENCE_KEY)!!
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
@ -76,44 +83,6 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
// olm version
findPreference<VectorPreference>(VectorPreferences.SETTINGS_OLM_VERSION_PREFERENCE_KEY)!!
.summary = session.cryptoService().getCryptoVersion(requireContext(), false)
// copyright
findPreference<VectorPreference>(VectorPreferences.SETTINGS_COPYRIGHT_PREFERENCE_KEY)!!
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.COPYRIGHT)
false
}
// terms & conditions
findPreference<VectorPreference>(VectorPreferences.SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY)!!
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.TAC)
false
}
// privacy policy
findPreference<VectorPreference>(VectorPreferences.SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY)!!
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.PRIVACY_POLICY)
false
}
// third party notice
findPreference<VectorPreference>(VectorPreferences.SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!!
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
activity?.displayInWebView(VectorSettingsUrls.THIRD_PARTY_LICENSES)
}
false
}
// Note: preference is not visible on F-Droid build
findPreference<VectorPreference>(VectorPreferences.SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!!
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
// See https://developers.google.com/android/guides/opensource
openOssLicensesMenuActivity(requireActivity())
false
}
}
companion object {

View file

@ -17,7 +17,7 @@
package im.vector.app.features.settings
object VectorSettingsUrls {
const val HELP = "https://element.io/help"
const val COPYRIGHT = "https://element.io/copyright"
const val TAC = "https://element.io/terms-of-service"
const val PRIVACY_POLICY = "https://element.io/privacy"

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.legals
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.discovery.ServerPolicy
import im.vector.app.features.settings.VectorSettingsUrls
import javax.inject.Inject
class ElementLegals @Inject constructor(
private val stringProvider: StringProvider
) {
/**
* Use ServerPolicy model
*/
fun getData(): List<ServerPolicy> {
return listOf(
ServerPolicy(stringProvider.getString(R.string.settings_copyright), VectorSettingsUrls.COPYRIGHT),
ServerPolicy(stringProvider.getString(R.string.settings_app_term_conditions), VectorSettingsUrls.TAC),
ServerPolicy(stringProvider.getString(R.string.settings_privacy_policy), VectorSettingsUrls.PRIVACY_POLICY)
)
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.legals
import im.vector.app.core.platform.VectorViewModelAction
sealed interface LegalsAction : VectorViewModelAction {
object Refresh : LegalsAction
}

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.legals
import android.content.res.Resources
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.app.R
import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.discovery.ServerAndPolicies
import im.vector.app.features.discovery.ServerPolicy
import im.vector.app.features.discovery.discoveryPolicyItem
import im.vector.app.features.discovery.settingsInfoItem
import im.vector.app.features.discovery.settingsSectionTitleItem
import javax.inject.Inject
class LegalsController @Inject constructor(
private val stringProvider: StringProvider,
private val resources: Resources,
private val elementLegals: ElementLegals,
private val errorFormatter: ErrorFormatter
) : TypedEpoxyController<LegalsState>() {
var listener: Listener? = null
override fun buildModels(data: LegalsState) {
buildAppSection()
buildHomeserverSection(data)
buildIdentityServerSection(data)
buildThirdPartyNotices()
}
private fun buildAppSection() {
settingsSectionTitleItem {
id("appTitle")
titleResId(R.string.legals_application_title)
}
buildPolicies("el", elementLegals.getData())
}
private fun buildHomeserverSection(data: LegalsState) {
settingsSectionTitleItem {
id("hsServerTitle")
titleResId(R.string.legals_home_server_title)
}
buildPolicyAsync("hs", data.homeServer)
}
private fun buildIdentityServerSection(data: LegalsState) {
if (data.hasIdentityServer) {
settingsSectionTitleItem {
id("idServerTitle")
titleResId(R.string.legals_identity_server_title)
}
buildPolicyAsync("is", data.identityServer)
}
}
private fun buildPolicyAsync(tag: String, serverAndPolicies: Async<ServerAndPolicies?>) {
val host = this
when (serverAndPolicies) {
Uninitialized,
is Loading -> loadingItem {
id("loading_$tag")
}
is Success -> {
val policies = serverAndPolicies()?.policies
if (policies.isNullOrEmpty()) {
settingsInfoItem {
id("emptyPolicy")
helperText(host.stringProvider.getString(R.string.legals_no_policy_provided))
}
} else {
buildPolicies(tag, policies)
}
}
is Fail -> {
errorWithRetryItem {
id("errorRetry_$tag")
text(host.errorFormatter.toHumanReadable(serverAndPolicies.error))
listener { host.listener?.onTapRetry() }
}
}
}
}
private fun buildPolicies(tag: String, policies: List<ServerPolicy>) {
val host = this
policies.forEach { policy ->
discoveryPolicyItem {
id(tag + policy.url)
name(policy.name)
url(policy.url.takeIf { it.startsWith("http") })
clickListener { host.listener?.openPolicy(policy) }
}
}
}
private fun buildThirdPartyNotices() {
val host = this
settingsSectionTitleItem {
id("thirdTitle")
titleResId(R.string.legals_third_party_notices)
}
discoveryPolicyItem {
id("eltpn1")
name(host.stringProvider.getString(R.string.settings_third_party_notices))
clickListener { host.listener?.openThirdPartyNotice() }
}
// Only on Gplay
if (resources.getBoolean(R.bool.isGplay)) {
discoveryPolicyItem {
id("eltpn2")
name(host.stringProvider.getString(R.string.settings_other_third_party_notices))
clickListener { host.listener?.openThirdPartyNoticeGplay() }
}
}
}
interface Listener {
fun onTapRetry()
fun openPolicy(policy: ServerPolicy)
fun openThirdPartyNotice()
fun openThirdPartyNoticeGplay()
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.legals
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.FirstThrottler
import im.vector.app.core.utils.displayInWebView
import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.discovery.ServerPolicy
import im.vector.app.features.settings.VectorSettingsUrls
import im.vector.app.openOssLicensesMenuActivity
import javax.inject.Inject
class LegalsFragment @Inject constructor(
private val controller: LegalsController
) : VectorBaseFragment<FragmentGenericRecyclerBinding>(),
LegalsController.Listener {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding {
return FragmentGenericRecyclerBinding.inflate(inflater, container, false)
}
private val viewModel by fragmentViewModel(LegalsViewModel::class)
private val firstThrottler = FirstThrottler(1000)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
controller.listener = this
views.genericRecyclerView.configureWith(controller)
}
override fun onDestroyView() {
views.genericRecyclerView.cleanup()
controller.listener = null
super.onDestroyView()
}
override fun invalidate() = withState(viewModel) { state ->
controller.setData(state)
}
override fun onResume() {
super.onResume()
(activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.preference_root_legals)
viewModel.handle(LegalsAction.Refresh)
}
override fun onTapRetry() {
viewModel.handle(LegalsAction.Refresh)
}
override fun openPolicy(policy: ServerPolicy) {
openUrl(policy.url)
}
override fun openThirdPartyNotice() {
openUrl(VectorSettingsUrls.THIRD_PARTY_LICENSES)
}
private fun openUrl(url: String) {
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
if (url.startsWith("file://")) {
activity?.displayInWebView(url)
} else {
openUrlInChromeCustomTab(requireContext(), null, url)
}
}
}
override fun openThirdPartyNoticeGplay() {
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
// See https://developers.google.com/android/guides/opensource
openOssLicensesMenuActivity(requireActivity())
}
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.legals
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.discovery.ServerAndPolicies
data class LegalsState(
val homeServer: Async<ServerAndPolicies?> = Uninitialized,
val hasIdentityServer: Boolean = false,
val identityServer: Async<ServerAndPolicies?> = Uninitialized
) : MavericksState

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.legals
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.discovery.fetchHomeserverWithTerms
import im.vector.app.features.discovery.fetchIdentityServerWithTerms
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
class LegalsViewModel @AssistedInject constructor(
@Assisted initialState: LegalsState,
private val session: Session,
private val stringProvider: StringProvider
) : VectorViewModel<LegalsState, LegalsAction, EmptyViewEvents>(initialState) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<LegalsViewModel, LegalsState> {
override fun create(initialState: LegalsState): LegalsViewModel
}
companion object : MavericksViewModelFactory<LegalsViewModel, LegalsState> by hiltMavericksViewModelFactory()
override fun handle(action: LegalsAction) {
when (action) {
LegalsAction.Refresh -> loadData()
}.exhaustive
}
private fun loadData() = withState { state ->
loadHomeserver(state)
val url = session.identityService().getCurrentIdentityServerUrl()
if (url.isNullOrEmpty()) {
setState { copy(hasIdentityServer = false) }
} else {
setState { copy(hasIdentityServer = true) }
loadIdentityServer(state)
}
}
private fun loadHomeserver(state: LegalsState) {
if (state.homeServer !is Success) {
setState { copy(homeServer = Loading()) }
viewModelScope.launch {
runCatching { session.fetchHomeserverWithTerms(stringProvider.getString(R.string.resources_language)) }
.fold(
onSuccess = { setState { copy(homeServer = Success(it)) } },
onFailure = { setState { copy(homeServer = Fail(it)) } }
)
}
}
}
private fun loadIdentityServer(state: LegalsState) {
if (state.identityServer !is Success) {
setState { copy(identityServer = Loading()) }
viewModelScope.launch {
runCatching { session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language)) }
.fold(
onSuccess = { setState { copy(identityServer = Success(it)) } },
onFailure = { setState { copy(identityServer = Fail(it)) } }
)
}
}
}
}

View file

@ -40,7 +40,6 @@ import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.services.GuardServiceStarter
import im.vector.app.core.utils.isIgnoringBatteryOptimizations
import im.vector.app.core.utils.requestDisablingBatteryOptimization
import im.vector.app.features.VectorFeatures
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.BackgroundSyncModeChooserDialog
@ -64,8 +63,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
private val pushManager: PushersManager,
private val activeSessionHolder: ActiveSessionHolder,
private val vectorPreferences: VectorPreferences,
private val guardServiceStarter: GuardServiceStarter,
private val features: VectorFeatures
private val guardServiceStarter: GuardServiceStarter
) : VectorSettingsBaseFragment(),
BackgroundSyncModeChooserDialog.InteractionListener {
@ -147,7 +145,6 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
refreshBackgroundSyncPrefs()
handleSystemPreference()
handleVersionedSettings()
}
private fun bindEmailNotifications() {
@ -312,15 +309,6 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
}
}
private fun handleVersionedSettings() {
val isNotificationSettingsV2Enabled = features.notificationSettingsVersion() == VectorFeatures.NotificationSettingsVersion.V2
findPreference<Preference>(VectorPreferences.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY)?.isVisible = !isNotificationSettingsV2Enabled
findPreference<Preference>(VectorPreferences.SETTINGS_NOTIFICATION_DEFAULT_PREFERENCE_KEY)?.isVisible = isNotificationSettingsV2Enabled
findPreference<Preference>(VectorPreferences.SETTINGS_NOTIFICATION_KEYWORD_AND_MENTIONS_PREFERENCE_KEY)?.isVisible = isNotificationSettingsV2Enabled
findPreference<Preference>(VectorPreferences.SETTINGS_NOTIFICATION_OTHER_PREFERENCE_KEY)?.isVisible = isNotificationSettingsV2Enabled
}
override fun onResume() {
super.onResume()
activeSessionHolder.getSafeActiveSession()?.refreshPushers()

View file

@ -17,13 +17,13 @@
package im.vector.app.features.userdirectory
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.features.discovery.IdentityServerWithTerms
import im.vector.app.features.discovery.ServerAndPolicies
/**
* Transient events for invite users to room screen
*/
sealed class UserListViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : UserListViewEvents()
data class OnPoliciesRetrieved(val identityServerWithTerms: IdentityServerWithTerms?) : UserListViewEvents()
data class OnPoliciesRetrieved(val identityServerWithTerms: ServerAndPolicies?) : UserListViewEvents()
data class OpenShareMatrixToLink(val link: String) : UserListViewEvents()
}

View file

@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillAlpha="0.9"
android:fillColor="?vctr_content_quinary"
android:pathData="M24,12C24,18.6274 18.6274,24 12,24C5.3726,24 0,18.6274 0,12C0,5.3726 5.3726,0 12,0C18.6274,0 24,5.3726 24,12Z"
android:strokeAlpha="0.9" />
<path
android:fillColor="#00000000"
android:pathData="M7.9998,7.9998L15.9998,15.9998"
android:strokeWidth="1.33333"
android:strokeColor="?vctr_content_secondary"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:pathData="M16.0005,7.9998L8.0006,15.9998"
android:strokeWidth="1.33333"
android:strokeColor="?vctr_content_secondary"
android:strokeLineCap="round" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:fillType="evenOdd"
android:pathData="M4,5C4,3.3431 5.3432,2 7,2H17C18.6569,2 20,3.3431 20,5V19C20,20.6569 18.6569,22 17,22H7C5.3432,22 4,20.6569 4,19V5ZM7,14.25C7,13.8358 7.3358,13.5 7.75,13.5H16.25C16.6642,13.5 17,13.8358 17,14.25C17,14.6642 16.6642,15 16.25,15H7.75C7.3358,15 7,14.6642 7,14.25ZM7.75,17C7.3358,17 7,17.3358 7,17.75C7,18.1642 7.3358,18.5 7.75,18.5H12.25C12.6642,18.5 13,18.1642 13,17.75C13,17.3358 12.6642,17 12.25,17H7.75Z" />
</vector>

View file

@ -5,9 +5,21 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:minHeight="80dp"
android:padding="16dp"
tools:viewBindingIgnore="true">
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:constraint_referenced_ids="discovery_policy_name,discovery_policy_url"
app:flow_verticalGap="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/discovery_policy_arrow"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/discovery_policy_name"
style="@style/Widget.Vector.TextView.Body"
@ -17,10 +29,7 @@
android:paddingEnd="8dp"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/discovery_policy_arrow"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Integration manager" />
tools:text="Copyright" />
<TextView
android:id="@+id/discovery_policy_url"
@ -29,11 +38,8 @@
android:layout_height="wrap_content"
android:paddingStart="0dp"
android:paddingEnd="8dp"
android:textColor="?vctr_content_secondary"
app:layout_constraintEnd_toStartOf="@id/discovery_policy_arrow"
app:layout_constraintStart_toStartOf="@id/discovery_policy_name"
app:layout_constraintTop_toBottomOf="@id/discovery_policy_name"
tools:text="Use bots, bridges, widget and sticker packs." />
android:textColor="?android:textColorLink"
tools:text="https://element.io/copyright" />
<!-- Do not use drawableEnd on the TextView because of RTL support -->
<ImageView

View file

@ -81,6 +81,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/messageMemberNameView"
android:layout_marginEnd="8dp"
android:layout_toStartOf="@id/messageSendStateImageView"
android:layout_toEndOf="@id/messageStartGuideline"
android:addStatesFromChildren="true">
@ -116,27 +117,23 @@
android:id="@+id/messageContentRedactedStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_redacted_stub" />
<ViewStub
android:id="@+id/messagePollStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_poll_stub" />
<ViewStub
android:id="@+id/messageOptionsStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_option_buttons_stub" />
<ViewStub
android:id="@+id/messageContentVoiceStub"
style="@style/TimelineContentStubBaseParams"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_voice_stub"
tools:visibility="visible" />
@ -153,7 +150,7 @@
android:layout_marginBottom="4dp"
android:contentDescription="@string/event_status_a11y_sending"
android:src="@drawable/ic_sending_message"
android:visibility="gone"
android:visibility="invisible"
tools:tint="?vctr_content_tertiary"
tools:visibility="visible" />

View file

@ -17,7 +17,7 @@
<im.vector.app.features.home.room.detail.timeline.url.PreviewUrlView
android:id="@+id/messageUrlPreview"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"

View file

@ -1,89 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/informationUrlPreviewContainer"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
tools:parentTag="com.google.android.material.card.MaterialCardView">
<View
android:id="@+id/url_preview_left_border"
android:layout_width="2dp"
android:layout_height="0dp"
android:background="?vctr_content_tertiary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/url_preview_title"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/url_preview_close"
app:layout_constraintStart_toStartOf="@id/url_preview_left_border"
app:layout_constraintTop_toTopOf="parent"
tools:text="Jo Malone denounces her former brand's John Boyega decision" />
android:orientation="vertical">
<ImageView
android:id="@+id/url_preview_image"
android:layout_width="0dp"
android:layout_height="157dp"
android:layout_marginTop="16dp"
android:importantForAccessibility="no"
android:scaleType="fitStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/url_preview_title"
app:layout_constraintTop_toBottomOf="@id/url_preview_title"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageView
android:id="@+id/url_preview_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:maxHeight="200dp"
android:scaleType="fitXY"
tools:src="@tools:sample/backgrounds/scenic" />
<TextView
android:id="@+id/url_preview_description"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="8dp"
android:ellipsize="end"
android:maxLines="4"
android:textColor="?vctr_content_secondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/url_preview_left_border"
app:layout_constraintTop_toBottomOf="@id/url_preview_image"
tools:text="The British perfumer says removing actor John Boyega from his own advert was “utterly despicable”." />
<TextView
android:id="@+id/url_preview_site"
style="@style/Widget.Vector.TextView.Caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:singleLine="true"
android:textColor="?vctr_content_secondary"
tools:text="BBC News" />
<TextView
android:id="@+id/url_preview_site"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
android:singleLine="true"
android:textColor="?vctr_content_tertiary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/url_preview_left_border"
app:layout_constraintTop_toBottomOf="@id/url_preview_description"
tools:text="BBC News" />
<TextView
android:id="@+id/url_preview_title"
style="@style/Widget.Vector.TextView.Body.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="@dimen/layout_touch_size"
android:ellipsize="end"
android:maxLines="2"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
tools:text="Jo Malone denounces her former brand's John Boyega decision" />
<TextView
android:id="@+id/url_preview_description"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:ellipsize="end"
android:textColor="?vctr_content_secondary"
tools:text="The British perfumer says removing actor John Boyega from his own advert was “utterly despicable”." />
</LinearLayout>
<ImageView
android:id="@+id/url_preview_close"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:layout_gravity="top|end"
android:contentDescription="@string/action_close"
android:scaleType="center"
android:src="@drawable/ic_close_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?vctr_content_secondary"
android:src="@drawable/ic_close_with_circular_bg"
tools:ignore="MissingPrefix" />
</merge>

View file

@ -1397,6 +1397,12 @@
<string name="settings_integration_allow">Allow integrations</string>
<string name="settings_integration_manager">Integration manager</string>
<string name="template_legals_application_title">${app_name} policy</string>
<string name="legals_home_server_title">Your homeserver policy</string>
<string name="legals_identity_server_title">Your identity server policy</string>
<string name="legals_third_party_notices">Third party libraries</string>
<string name="legals_no_policy_provided">This server does not provide any policy.</string>
<string name="disabled_integration_dialog_title">Integrations are disabled</string>
<string name="disabled_integration_dialog_content">"Enable 'Allow integrations' in Settings to do this."</string>
@ -2270,7 +2276,13 @@
<string name="preference_voice_and_video">Voice &amp; Video</string>
<string name="preference_root_help_about">Help &amp; About</string>
<string name="preference_root_legals">Legals</string>
<string name="preference_help">Help</string>
<string name="preference_help_title">Help and support</string>
<string name="preference_help_summary">Get help with using Element</string>
<string name="preference_versions">Versions</string>
<string name="preference_system_settings">System settings</string>
<string name="settings_troubleshoot_test_token_registration_quick_fix">Register token</string>
@ -3566,7 +3578,6 @@
<string name="space_manage_rooms_and_spaces">Manage rooms and spaces</string>
<string name="preference_show_all_rooms_in_home">Show all rooms in Home</string>
<string name="all_rooms_youre_in_will_be_shown_in_home">All rooms youre in will be shown in Home.</string>

View file

@ -1,47 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<im.vector.app.core.preference.VectorPreference
android:key="APP_INFO_LINK_PREFERENCE_KEY"
android:summary="@string/settings_app_info_link_summary"
android:title="@string/settings_app_info_link_title" />
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/preference_help">
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_VERSION_PREFERENCE_KEY"
android:title="@string/settings_version"
tools:summary="1.2.3" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_HELP_PREFERENCE_KEY"
android:summary="@string/preference_help_summary"
android:title="@string/preference_help_title" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_SDK_VERSION_PREFERENCE_KEY"
android:title="@string/settings_sdk_version"
tools:summary="4.5.6" />
</im.vector.app.core.preference.VectorPreferenceCategory>
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_OLM_VERSION_PREFERENCE_KEY"
android:title="@string/settings_olm_version"
tools:summary="7.8.9" />
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/preference_versions">
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_COPYRIGHT_PREFERENCE_KEY"
android:title="@string/settings_copyright" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_VERSION_PREFERENCE_KEY"
android:title="@string/settings_version"
tools:summary="1.2.3" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY"
android:title="@string/settings_app_term_conditions" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_SDK_VERSION_PREFERENCE_KEY"
android:title="@string/settings_sdk_version"
tools:summary="4.5.6" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY"
android:title="@string/settings_privacy_policy" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_OLM_VERSION_PREFERENCE_KEY"
android:title="@string/settings_olm_version"
tools:summary="7.8.9" />
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY"
android:title="@string/settings_third_party_notices" />
</im.vector.app.core.preference.VectorPreferenceCategory>
<im.vector.app.core.preference.VectorPreference
android:key="SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY"
android:title="@string/settings_other_third_party_notices"
app:isPreferenceVisible="@bool/isGplay" />
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/preference_system_settings">
<im.vector.app.core.preference.VectorPreference
android:key="APP_INFO_LINK_PREFERENCE_KEY"
android:summary="@string/settings_app_info_link_summary"
android:title="@string/settings_app_info_link_title" />
</im.vector.app.core.preference.VectorPreferenceCategory>
</androidx.preference.PreferenceScreen>

View file

@ -20,14 +20,6 @@
<!--android:key="SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY"-->
<!--android:title="@string/settings_turn_screen_on" />-->
<im.vector.app.core.preference.VectorPreference
android:dependency="SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY"
android:key="SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY"
android:persistent="false"
android:summary="@string/settings_notification_advanced_summary"
android:title="@string/settings_notification_advanced"
app:fragment="im.vector.app.features.settings.notifications.VectorSettingsAdvancedNotificationPreferenceFragment" />
<im.vector.app.core.preference.VectorPreference
android:dependency="SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY"
android:key="SETTINGS_NOTIFICATION_DEFAULT_PREFERENCE_KEY"

View file

@ -53,4 +53,9 @@
android:title="@string/preference_root_help_about"
app:fragment="im.vector.app.features.settings.VectorSettingsHelpAboutFragment" />
<im.vector.app.core.preference.VectorPreference
android:icon="@drawable/ic_settings_root_legals"
android:title="@string/preference_root_legals"
app:fragment="im.vector.app.features.settings.legals.LegalsFragment" />
</androidx.preference.PreferenceScreen>