Merge branch 'develop' into fix-6232
This commit is contained in:
@@ -57,6 +57,7 @@ dependencies {
|
|||||||
|
|
||||||
testCommonDependencies(libs)
|
testCommonDependencies(libs)
|
||||||
testImplementation(projects.features.login.test)
|
testImplementation(projects.features.login.test)
|
||||||
|
testImplementation(projects.features.share.test)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.oidc.test)
|
testImplementation(projects.libraries.oidc.test)
|
||||||
testImplementation(projects.libraries.preferences.test)
|
testImplementation(projects.libraries.preferences.test)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
package io.element.android.appnav
|
package io.element.android.appnav
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -63,6 +62,7 @@ import io.element.android.features.roomdirectory.api.RoomDescription
|
|||||||
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
|
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
|
||||||
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
||||||
import io.element.android.features.share.api.ShareEntryPoint
|
import io.element.android.features.share.api.ShareEntryPoint
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
import io.element.android.features.startchat.api.StartChatEntryPoint
|
import io.element.android.features.startchat.api.StartChatEntryPoint
|
||||||
import io.element.android.features.userprofile.api.UserProfileEntryPoint
|
import io.element.android.features.userprofile.api.UserProfileEntryPoint
|
||||||
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
|
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
|
||||||
@@ -307,7 +307,7 @@ class LoggedInFlowNode(
|
|||||||
data object RoomDirectory : NavTarget
|
data object RoomDirectory : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class IncomingShare(val intent: Intent) : NavTarget
|
data class IncomingShare(val shareIntentData: ShareIntentData) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class IncomingVerificationRequest(val data: VerificationRequest.Incoming) : NavTarget
|
data class IncomingVerificationRequest(val data: VerificationRequest.Incoming) : NavTarget
|
||||||
@@ -570,7 +570,7 @@ class LoggedInFlowNode(
|
|||||||
shareEntryPoint.createNode(
|
shareEntryPoint.createNode(
|
||||||
parentNode = this,
|
parentNode = this,
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
params = ShareEntryPoint.Params(intent = navTarget.intent),
|
params = ShareEntryPoint.Params(shareIntentData = navTarget.shareIntentData),
|
||||||
callback = object : ShareEntryPoint.Callback {
|
callback = object : ShareEntryPoint.Callback {
|
||||||
override fun onDone(roomIds: List<RoomId>) {
|
override fun onDone(roomIds: List<RoomId>) {
|
||||||
// Remove the incoming share screen
|
// Remove the incoming share screen
|
||||||
@@ -649,13 +649,13 @@ class LoggedInFlowNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun attachIncomingShare(intent: Intent) {
|
internal suspend fun attachIncomingShare(shareIntentData: ShareIntentData) {
|
||||||
waitForNavTargetAttached { navTarget ->
|
waitForNavTargetAttached { navTarget ->
|
||||||
navTarget is NavTarget.Home
|
navTarget is NavTarget.Home
|
||||||
}
|
}
|
||||||
attachChild<Node> {
|
attachChild<Node> {
|
||||||
backstack.push(
|
backstack.push(
|
||||||
NavTarget.IncomingShare(intent)
|
NavTarget.IncomingShare(shareIntentData)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import io.element.android.features.announcement.api.AnnouncementService
|
|||||||
import io.element.android.features.login.api.LoginParams
|
import io.element.android.features.login.api.LoginParams
|
||||||
import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl
|
import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl
|
||||||
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
import io.element.android.features.signedout.api.SignedOutEntryPoint
|
import io.element.android.features.signedout.api.SignedOutEntryPoint
|
||||||
import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint
|
import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackView
|
||||||
@@ -265,7 +266,7 @@ class RootFlowNode(
|
|||||||
|
|
||||||
@Parcelize data class AccountSelect(
|
@Parcelize data class AccountSelect(
|
||||||
val currentSessionId: SessionId,
|
val currentSessionId: SessionId,
|
||||||
val intent: Intent?,
|
val shareIntentData: ShareIntentData?,
|
||||||
val permalinkData: PermalinkData?,
|
val permalinkData: PermalinkData?,
|
||||||
) : NavTarget
|
) : NavTarget
|
||||||
|
|
||||||
@@ -357,8 +358,8 @@ class RootFlowNode(
|
|||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
attachSession(sessionId).apply {
|
attachSession(sessionId).apply {
|
||||||
if (navTarget.intent != null) {
|
if (navTarget.shareIntentData != null) {
|
||||||
attachIncomingShare(navTarget.intent)
|
attachIncomingShare(navTarget.shareIntentData)
|
||||||
} else if (navTarget.permalinkData != null) {
|
} else if (navTarget.permalinkData != null) {
|
||||||
attachPermalinkData(navTarget.permalinkData)
|
attachPermalinkData(navTarget.permalinkData)
|
||||||
}
|
}
|
||||||
@@ -392,7 +393,7 @@ class RootFlowNode(
|
|||||||
is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params)
|
is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params)
|
||||||
is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction)
|
is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction)
|
||||||
is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData)
|
is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData)
|
||||||
is ResolvedIntent.IncomingShare -> onIncomingShare(resolvedIntent.intent)
|
is ResolvedIntent.IncomingShare -> onIncomingShare(resolvedIntent.shareIntentData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,7 +424,7 @@ class RootFlowNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun onIncomingShare(intent: Intent) {
|
private suspend fun onIncomingShare(shareIntentData: ShareIntentData) {
|
||||||
// Is there a session already?
|
// Is there a session already?
|
||||||
val latestSessionId = sessionStore.getLatestSessionId()
|
val latestSessionId = sessionStore.getLatestSessionId()
|
||||||
if (latestSessionId == null) {
|
if (latestSessionId == null) {
|
||||||
@@ -437,13 +438,13 @@ class RootFlowNode(
|
|||||||
backstack.push(
|
backstack.push(
|
||||||
NavTarget.AccountSelect(
|
NavTarget.AccountSelect(
|
||||||
currentSessionId = latestSessionId,
|
currentSessionId = latestSessionId,
|
||||||
intent = intent,
|
shareIntentData = shareIntentData,
|
||||||
permalinkData = null,
|
permalinkData = null,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Only one account, directly attach the incoming share node.
|
// Only one account, directly attach the incoming share node.
|
||||||
loggedInFlowNode.attachIncomingShare(intent)
|
loggedInFlowNode.attachIncomingShare(shareIntentData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -467,7 +468,7 @@ class RootFlowNode(
|
|||||||
backstack.push(
|
backstack.push(
|
||||||
NavTarget.AccountSelect(
|
NavTarget.AccountSelect(
|
||||||
currentSessionId = latestSessionId,
|
currentSessionId = latestSessionId,
|
||||||
intent = null,
|
shareIntentData = null,
|
||||||
permalinkData = permalinkData,
|
permalinkData = permalinkData,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import android.content.Intent
|
|||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.features.login.api.LoginIntentResolver
|
import io.element.android.features.login.api.LoginIntentResolver
|
||||||
import io.element.android.features.login.api.LoginParams
|
import io.element.android.features.login.api.LoginParams
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
|
import io.element.android.features.share.api.ShareIntentHandler
|
||||||
import io.element.android.libraries.deeplink.api.DeeplinkData
|
import io.element.android.libraries.deeplink.api.DeeplinkData
|
||||||
import io.element.android.libraries.deeplink.api.DeeplinkParser
|
import io.element.android.libraries.deeplink.api.DeeplinkParser
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
@@ -25,7 +27,7 @@ sealed interface ResolvedIntent {
|
|||||||
data class Oidc(val oidcAction: OidcAction) : ResolvedIntent
|
data class Oidc(val oidcAction: OidcAction) : ResolvedIntent
|
||||||
data class Permalink(val permalinkData: PermalinkData) : ResolvedIntent
|
data class Permalink(val permalinkData: PermalinkData) : ResolvedIntent
|
||||||
data class Login(val params: LoginParams) : ResolvedIntent
|
data class Login(val params: LoginParams) : ResolvedIntent
|
||||||
data class IncomingShare(val intent: Intent) : ResolvedIntent
|
data class IncomingShare(val shareIntentData: ShareIntentData) : ResolvedIntent
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -34,6 +36,7 @@ class IntentResolver(
|
|||||||
private val loginIntentResolver: LoginIntentResolver,
|
private val loginIntentResolver: LoginIntentResolver,
|
||||||
private val oidcIntentResolver: OidcIntentResolver,
|
private val oidcIntentResolver: OidcIntentResolver,
|
||||||
private val permalinkParser: PermalinkParser,
|
private val permalinkParser: PermalinkParser,
|
||||||
|
private val shareIntentHandler: ShareIntentHandler,
|
||||||
) {
|
) {
|
||||||
fun resolve(intent: Intent): ResolvedIntent? {
|
fun resolve(intent: Intent): ResolvedIntent? {
|
||||||
if (intent.canBeIgnored()) return null
|
if (intent.canBeIgnored()) return null
|
||||||
@@ -62,7 +65,8 @@ class IntentResolver(
|
|||||||
if (permalinkData != null) return ResolvedIntent.Permalink(permalinkData)
|
if (permalinkData != null) return ResolvedIntent.Permalink(permalinkData)
|
||||||
|
|
||||||
if (intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE) {
|
if (intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE) {
|
||||||
return ResolvedIntent.IncomingShare(intent)
|
val data = shareIntentHandler.handleIncomingShareIntent(intent) ?: return null
|
||||||
|
return ResolvedIntent.IncomingShare(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown intent
|
// Unknown intent
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import androidx.core.net.toUri
|
|||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.api.LoginParams
|
import io.element.android.features.login.api.LoginParams
|
||||||
import io.element.android.features.login.test.FakeLoginIntentResolver
|
import io.element.android.features.login.test.FakeLoginIntentResolver
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
|
import io.element.android.features.share.api.UriToShare
|
||||||
|
import io.element.android.features.share.test.FakeShareIntentHandler
|
||||||
import io.element.android.libraries.deeplink.api.DeeplinkData
|
import io.element.android.libraries.deeplink.api.DeeplinkData
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
@@ -239,26 +242,34 @@ class IntentResolverTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test incoming share simple`() {
|
fun `test incoming share simple`() {
|
||||||
|
val shareIntentData = ShareIntentData.PlainText("Hello")
|
||||||
val sut = createIntentResolver(
|
val sut = createIntentResolver(
|
||||||
oidcIntentResolverResult = { null },
|
oidcIntentResolverResult = { null },
|
||||||
|
onIncomingShareIntent = { shareIntentData },
|
||||||
)
|
)
|
||||||
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
|
putExtra(Intent.EXTRA_TEXT, "Hello")
|
||||||
}
|
}
|
||||||
val result = sut.resolve(intent)
|
val result = sut.resolve(intent)
|
||||||
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(intent = intent))
|
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(shareIntentData))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test incoming share multiple`() {
|
fun `test incoming share multiple`() {
|
||||||
|
val fileUri = "content://com.example.app/file1.jpg".toUri()
|
||||||
|
val shareIntentData = ShareIntentData.Uris(text = "Hello", uris = listOf(UriToShare(fileUri, "image/jpg")))
|
||||||
val sut = createIntentResolver(
|
val sut = createIntentResolver(
|
||||||
oidcIntentResolverResult = { null },
|
oidcIntentResolverResult = { null },
|
||||||
|
onIncomingShareIntent = { shareIntentData },
|
||||||
)
|
)
|
||||||
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
||||||
action = Intent.ACTION_SEND_MULTIPLE
|
action = Intent.ACTION_SEND_MULTIPLE
|
||||||
|
putExtra(Intent.EXTRA_TEXT, "Hello")
|
||||||
|
data = fileUri
|
||||||
}
|
}
|
||||||
val result = sut.resolve(intent)
|
val result = sut.resolve(intent)
|
||||||
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(intent = intent))
|
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(shareIntentData))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -296,6 +307,7 @@ class IntentResolverTest {
|
|||||||
permalinkParserResult: (String) -> PermalinkData = { lambdaError() },
|
permalinkParserResult: (String) -> PermalinkData = { lambdaError() },
|
||||||
loginIntentResolverResult: (String) -> LoginParams? = { lambdaError() },
|
loginIntentResolverResult: (String) -> LoginParams? = { lambdaError() },
|
||||||
oidcIntentResolverResult: (Intent) -> OidcAction? = { lambdaError() },
|
oidcIntentResolverResult: (Intent) -> OidcAction? = { lambdaError() },
|
||||||
|
onIncomingShareIntent: (Intent) -> ShareIntentData? = { null },
|
||||||
): IntentResolver {
|
): IntentResolver {
|
||||||
return IntentResolver(
|
return IntentResolver(
|
||||||
deeplinkParser = { deeplinkParserResult },
|
deeplinkParser = { deeplinkParserResult },
|
||||||
@@ -308,6 +320,9 @@ class IntentResolverTest {
|
|||||||
permalinkParser = FakePermalinkParser(
|
permalinkParser = FakePermalinkParser(
|
||||||
result = permalinkParserResult
|
result = permalinkParserResult
|
||||||
),
|
),
|
||||||
|
shareIntentHandler = FakeShareIntentHandler(
|
||||||
|
onIncomingShareIntent = onIncomingShareIntent,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,8 +144,8 @@ Prerequisites:
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can then build the Rust SDK by running the script
|
You can then build the Rust SDK by running the script
|
||||||
[`tools/sdk/build_rust_sdk.sh`](../tools/sdk/build_rust_sdk.sh) and just answering
|
[`tools/sdk/build-rust-sdk`](../tools/sdk/build-rust-sdk). Type
|
||||||
the questions.
|
`./tools/sdk/build-rust-sdk --help` for help.
|
||||||
|
|
||||||
This will prompt you for the path to the Rust SDK, then build it and
|
This will prompt you for the path to the Rust SDK, then build it and
|
||||||
`matrix-rust-components-kotlin`, eventually producing an aar file at
|
`matrix-rust-components-kotlin`, eventually producing an aar file at
|
||||||
|
|||||||
@@ -189,9 +189,13 @@ class LinkNewDeviceFlowNode(
|
|||||||
is ErrorType.InvalidCheckCode -> ErrorScreenType.InsecureChannelDetected
|
is ErrorType.InvalidCheckCode -> ErrorScreenType.InsecureChannelDetected
|
||||||
is ErrorType.MissingSecretsBackup -> ErrorScreenType.UnknownError
|
is ErrorType.MissingSecretsBackup -> ErrorScreenType.UnknownError
|
||||||
is ErrorType.NotFound -> ErrorScreenType.Expired
|
is ErrorType.NotFound -> ErrorScreenType.Expired
|
||||||
is ErrorType.UnableToCreateDevice -> ErrorScreenType.UnknownError
|
is ErrorType.DeviceNotFound -> ErrorScreenType.UnknownError
|
||||||
is ErrorType.Unknown -> ErrorScreenType.UnknownError
|
is ErrorType.Unknown -> ErrorScreenType.UnknownError
|
||||||
is ErrorType.UnsupportedProtocol -> ErrorScreenType.UnknownError
|
is ErrorType.UnsupportedProtocol -> ErrorScreenType.UnknownError
|
||||||
|
is ErrorType.Cancelled -> ErrorScreenType.UnknownError
|
||||||
|
is ErrorType.ConnectionInsecure -> ErrorScreenType.InsecureChannelDetected
|
||||||
|
is ErrorType.Expired -> ErrorScreenType.Expired
|
||||||
|
is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.UnknownError
|
||||||
}
|
}
|
||||||
// It is OK to push on backstack, since when user leaves the error screen, a new root will be set,
|
// It is OK to push on backstack, since when user leaves the error screen, a new root will be set,
|
||||||
// or the whole flow will be popped.
|
// or the whole flow will be popped.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
|||||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||||
|
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
||||||
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
|
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
@@ -149,7 +150,7 @@ class LogoutPresenterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - logout then confirm`() = runTest {
|
fun `present - logout then confirm`() = runTest {
|
||||||
val cancelWorkManagerJobsLambda = lambdaRecorder<SessionId, Unit> {}
|
val cancelWorkManagerJobsLambda = lambdaRecorder<SessionId, WorkManagerRequestType?, Unit> { _, _ -> }
|
||||||
val workManagerScheduler = FakeWorkManagerScheduler(cancelLambda = cancelWorkManagerJobsLambda)
|
val workManagerScheduler = FakeWorkManagerScheduler(cancelLambda = cancelWorkManagerJobsLambda)
|
||||||
val presenter = createLogoutPresenter(workManagerScheduler = workManagerScheduler)
|
val presenter = createLogoutPresenter(workManagerScheduler = workManagerScheduler)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
@@ -238,7 +239,7 @@ class LogoutPresenterTest {
|
|||||||
internal fun createLogoutPresenter(
|
internal fun createLogoutPresenter(
|
||||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||||
encryptionService: EncryptionService = FakeEncryptionService(),
|
encryptionService: EncryptionService = FakeEncryptionService(),
|
||||||
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(cancelLambda = {}),
|
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(cancelLambda = { _, _ -> }),
|
||||||
): LogoutPresenter = LogoutPresenter(
|
): LogoutPresenter = LogoutPresenter(
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
encryptionService = encryptionService,
|
encryptionService = encryptionService,
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ private fun ViolationAlert(
|
|||||||
},
|
},
|
||||||
submitText = stringResource(submitTextId),
|
submitText = stringResource(submitTextId),
|
||||||
onSubmitClick = onSubmitClick,
|
onSubmitClick = onSubmitClick,
|
||||||
level = if (isCritical) ComposerAlertLevel.Critical else ComposerAlertLevel.Default,
|
level = if (isCritical) ComposerAlertLevel.Critical else ComposerAlertLevel.Info,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,5 +30,5 @@ data class TimelineItemStickerContent(
|
|||||||
|
|
||||||
/* Stickers are supposed to be small images so
|
/* Stickers are supposed to be small images so
|
||||||
we allow using the mediaSource (unless the url is empty) */
|
we allow using the mediaSource (unless the url is empty) */
|
||||||
val preferredMediaSource = if (mediaSource.url.isEmpty()) thumbnailSource else mediaSource
|
val preferredMediaSource = if (mediaSource.safeUrl.isEmpty()) thumbnailSource else mediaSource
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ dependencies {
|
|||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.libraries.sessionStorage.api)
|
implementation(projects.libraries.sessionStorage.api)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
|
implementation(projects.libraries.workmanager.api)
|
||||||
|
|
||||||
testCommonDependencies(libs)
|
testCommonDependencies(libs)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.migration.impl.migrations
|
||||||
|
|
||||||
|
import dev.zacsweers.metro.AppScope
|
||||||
|
import dev.zacsweers.metro.ContributesIntoSet
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
||||||
|
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove existing fetch notifications work manager requests since their format has changed.
|
||||||
|
*/
|
||||||
|
@ContributesIntoSet(AppScope::class)
|
||||||
|
class AppMigration10(
|
||||||
|
private val sessionStore: SessionStore,
|
||||||
|
private val workManagerScheduler: WorkManagerScheduler,
|
||||||
|
) : AppMigration {
|
||||||
|
override val order: Int = 10
|
||||||
|
|
||||||
|
override suspend fun migrate(isFreshInstall: Boolean) {
|
||||||
|
if (isFreshInstall) return
|
||||||
|
|
||||||
|
val sessions = sessionStore.getAllSessions()
|
||||||
|
|
||||||
|
for (session in sessions) {
|
||||||
|
workManagerScheduler.cancel(
|
||||||
|
sessionId = SessionId(session.userId),
|
||||||
|
requestType = WorkManagerRequestType.NOTIFICATION_SYNC
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.share.api
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post-processing to be done once a [ShareIntentData] has been consumed.
|
||||||
|
*/
|
||||||
|
fun interface OnSharedData {
|
||||||
|
operator fun invoke(data: ShareIntentData)
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
package io.element.android.features.share.api
|
package io.element.android.features.share.api
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
@@ -16,7 +15,7 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
|
|||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
interface ShareEntryPoint : FeatureEntryPoint {
|
interface ShareEntryPoint : FeatureEntryPoint {
|
||||||
data class Params(val intent: Intent)
|
data class Params(val shareIntentData: ShareIntentData)
|
||||||
|
|
||||||
fun createNode(
|
fun createNode(
|
||||||
parentNode: Node,
|
parentNode: Node,
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.share.api
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share intent data, mapped from the original [android.content.Intent].
|
||||||
|
*/
|
||||||
|
sealed interface ShareIntentData : Parcelable {
|
||||||
|
/**
|
||||||
|
* A list of [Uri]s to share and their mime types, with an optional [text] to be used as caption.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class Uris(val text: String?, val uris: List<UriToShare>) : ShareIntentData
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plain text to share.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class PlainText(val content: String) : ShareIntentData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Uri] coming from an external share intent, with its associated [mimeType].
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class UriToShare(
|
||||||
|
val uri: Uri,
|
||||||
|
val mimeType: String,
|
||||||
|
) : Parcelable
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.share.api
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
|
||||||
|
interface ShareIntentHandler {
|
||||||
|
/**
|
||||||
|
* This methods aims to handle incoming share intents and parse its data.
|
||||||
|
*
|
||||||
|
* @return the [ShareIntentData] if it could be resolved, or null.
|
||||||
|
*/
|
||||||
|
fun handleIncomingShareIntent(
|
||||||
|
intent: Intent
|
||||||
|
): ShareIntentData?
|
||||||
|
}
|
||||||
@@ -44,6 +44,7 @@ dependencies {
|
|||||||
api(projects.features.share.api)
|
api(projects.features.share.api)
|
||||||
|
|
||||||
testCommonDependencies(libs, true)
|
testCommonDependencies(libs, true)
|
||||||
|
testImplementation(projects.features.share.test)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.mediaupload.test)
|
testImplementation(projects.libraries.mediaupload.test)
|
||||||
testImplementation(projects.libraries.preferences.test)
|
testImplementation(projects.libraries.preferences.test)
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.share.impl
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import dev.zacsweers.metro.AppScope
|
||||||
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
|
import io.element.android.features.share.api.OnSharedData
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
|
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||||
|
import timber.log.Timber
|
||||||
|
import kotlin.collections.forEach
|
||||||
|
|
||||||
|
@ContributesBinding(AppScope::class)
|
||||||
|
class DefaultOnSharedData(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
) : OnSharedData {
|
||||||
|
override fun invoke(data: ShareIntentData) {
|
||||||
|
when (data) {
|
||||||
|
is ShareIntentData.PlainText -> {
|
||||||
|
// No-op, there is nothing to do for plain text intents.
|
||||||
|
}
|
||||||
|
is ShareIntentData.Uris -> {
|
||||||
|
revokeUriPermissions(data.uris.map { it.uri })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun revokeUriPermissions(uris: List<Uri>) {
|
||||||
|
uris.forEach { uri ->
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
context.revokeUriPermission(context.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
} else {
|
||||||
|
context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.w(e, "Unable to revoke Uri permission")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ class DefaultShareEntryPoint : ShareEntryPoint {
|
|||||||
return parentNode.createNode<ShareNode>(
|
return parentNode.createNode<ShareNode>(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = listOf(
|
plugins = listOf(
|
||||||
ShareNode.Inputs(intent = params.intent),
|
ShareNode.Inputs(shareIntentData = params.shareIntentData),
|
||||||
callback,
|
callback,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
* Copyright 2024, 2025 New Vector Ltd.
|
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -14,10 +13,12 @@ import android.content.Intent
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.ResolveInfo
|
import android.content.pm.ResolveInfo
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import androidx.core.content.IntentCompat
|
import androidx.core.content.IntentCompat
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
|
import io.element.android.features.share.api.ShareIntentHandler
|
||||||
|
import io.element.android.features.share.api.UriToShare
|
||||||
import io.element.android.libraries.androidutils.compat.queryIntentActivitiesCompat
|
import io.element.android.libraries.androidutils.compat.queryIntentActivitiesCompat
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAny
|
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAny
|
||||||
@@ -30,37 +31,17 @@ import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
|
|||||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
interface ShareIntentHandler {
|
|
||||||
data class UriToShare(
|
|
||||||
val uri: Uri,
|
|
||||||
val mimeType: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This methods aims to handle incoming share intents.
|
|
||||||
*
|
|
||||||
* @return true if it can handle the intent data, false otherwise
|
|
||||||
*/
|
|
||||||
suspend fun handleIncomingShareIntent(
|
|
||||||
intent: Intent,
|
|
||||||
onUris: suspend (List<UriToShare>) -> Boolean,
|
|
||||||
onPlainText: suspend (String) -> Boolean,
|
|
||||||
): Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultShareIntentHandler(
|
class DefaultShareIntentHandler(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
) : ShareIntentHandler {
|
) : ShareIntentHandler {
|
||||||
override suspend fun handleIncomingShareIntent(
|
override fun handleIncomingShareIntent(
|
||||||
intent: Intent,
|
intent: Intent,
|
||||||
onUris: suspend (List<ShareIntentHandler.UriToShare>) -> Boolean,
|
): ShareIntentData? {
|
||||||
onPlainText: suspend (String) -> Boolean,
|
val type = intent.resolveType(context) ?: return null
|
||||||
): Boolean {
|
|
||||||
val type = intent.resolveType(context) ?: return false
|
|
||||||
val uris = getIncomingUris(intent, type)
|
val uris = getIncomingUris(intent, type)
|
||||||
return when {
|
return when {
|
||||||
uris.isEmpty() && type == MimeTypes.PlainText -> handlePlainText(intent, onPlainText)
|
uris.isEmpty() && type == MimeTypes.PlainText -> handlePlainText(intent)
|
||||||
type.isMimeTypeImage() ||
|
type.isMimeTypeImage() ||
|
||||||
type.isMimeTypeVideo() ||
|
type.isMimeTypeVideo() ||
|
||||||
type.isMimeTypeAudio() ||
|
type.isMimeTypeAudio() ||
|
||||||
@@ -68,20 +49,21 @@ class DefaultShareIntentHandler(
|
|||||||
type.isMimeTypeFile() ||
|
type.isMimeTypeFile() ||
|
||||||
type.isMimeTypeText() ||
|
type.isMimeTypeText() ||
|
||||||
type.isMimeTypeAny() -> {
|
type.isMimeTypeAny() -> {
|
||||||
val result = onUris(uris)
|
ShareIntentData.Uris(
|
||||||
revokeUriPermissions(uris.map { it.uri })
|
text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)?.toString()?.takeIf { it.isNotEmpty() },
|
||||||
result
|
uris = uris,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else -> false
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handlePlainText(intent: Intent, onPlainText: suspend (String) -> Boolean): Boolean {
|
private fun handlePlainText(intent: Intent): ShareIntentData.PlainText? {
|
||||||
val content = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)?.toString()
|
val content = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)?.toString()
|
||||||
return if (content?.isNotEmpty() == true) {
|
return if (content?.isNotEmpty() == true) {
|
||||||
onPlainText(content)
|
ShareIntentData.PlainText(content)
|
||||||
} else {
|
} else {
|
||||||
false
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +71,7 @@ class DefaultShareIntentHandler(
|
|||||||
* Use this function to retrieve files which are shared from another application or internally
|
* Use this function to retrieve files which are shared from another application or internally
|
||||||
* by using android.intent.action.SEND or android.intent.action.SEND_MULTIPLE actions.
|
* by using android.intent.action.SEND or android.intent.action.SEND_MULTIPLE actions.
|
||||||
*/
|
*/
|
||||||
private fun getIncomingUris(intent: Intent, fallbackMimeType: String): List<ShareIntentHandler.UriToShare> {
|
private fun getIncomingUris(intent: Intent, fallbackMimeType: String): List<UriToShare> {
|
||||||
val uriList = mutableListOf<Uri>()
|
val uriList = mutableListOf<Uri>()
|
||||||
if (intent.action == Intent.ACTION_SEND) {
|
if (intent.action == Intent.ACTION_SEND) {
|
||||||
IntentCompat.getParcelableExtra(intent, Intent.EXTRA_STREAM, Uri::class.java)
|
IntentCompat.getParcelableExtra(intent, Intent.EXTRA_STREAM, Uri::class.java)
|
||||||
@@ -118,24 +100,10 @@ class DefaultShareIntentHandler(
|
|||||||
// The value in fallbackMimeType can be wrong, especially if several uris were received
|
// The value in fallbackMimeType can be wrong, especially if several uris were received
|
||||||
// in the same intent (i.e. 'image/*'). We need to check the mime type of each uri.
|
// in the same intent (i.e. 'image/*'). We need to check the mime type of each uri.
|
||||||
val mimeType = context.contentResolver.getType(uri) ?: fallbackMimeType
|
val mimeType = context.contentResolver.getType(uri) ?: fallbackMimeType
|
||||||
ShareIntentHandler.UriToShare(
|
UriToShare(
|
||||||
uri = uri,
|
uri = uri,
|
||||||
mimeType = mimeType,
|
mimeType = mimeType,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun revokeUriPermissions(uris: List<Uri>) {
|
|
||||||
uris.forEach { uri ->
|
|
||||||
try {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
context.revokeUriPermission(context.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
} else {
|
|
||||||
context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.w(e, "Unable to revoke Uri permission")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
package io.element.android.features.share.impl
|
package io.element.android.features.share.impl
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -23,6 +22,7 @@ import dev.zacsweers.metro.Assisted
|
|||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.share.api.ShareEntryPoint
|
import io.element.android.features.share.api.ShareEntryPoint
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.callback
|
import io.element.android.libraries.architecture.callback
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
@@ -50,10 +50,10 @@ class ShareNode(
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
object NavTarget : Parcelable
|
object NavTarget : Parcelable
|
||||||
|
|
||||||
data class Inputs(val intent: Intent) : NodeInputs
|
data class Inputs(val shareIntentData: ShareIntentData) : NodeInputs
|
||||||
|
|
||||||
private val inputs = inputs<Inputs>()
|
private val inputs = inputs<Inputs>()
|
||||||
private val presenter = presenterFactory.create(inputs.intent)
|
private val presenter = presenterFactory.create(inputs.shareIntentData)
|
||||||
private val callback: ShareEntryPoint.Callback = callback()
|
private val callback: ShareEntryPoint.Callback = callback()
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
|
|||||||
@@ -8,13 +8,14 @@
|
|||||||
|
|
||||||
package io.element.android.features.share.impl
|
package io.element.android.features.share.impl
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
|
import io.element.android.features.share.api.OnSharedData
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
@@ -32,24 +33,24 @@ import kotlin.coroutines.cancellation.CancellationException
|
|||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class SharePresenter(
|
class SharePresenter(
|
||||||
@Assisted private val intent: Intent,
|
@Assisted private val shareIntentData: ShareIntentData,
|
||||||
@SessionCoroutineScope
|
@SessionCoroutineScope
|
||||||
private val sessionCoroutineScope: CoroutineScope,
|
private val sessionCoroutineScope: CoroutineScope,
|
||||||
private val shareIntentHandler: ShareIntentHandler,
|
|
||||||
private val matrixClient: MatrixClient,
|
private val matrixClient: MatrixClient,
|
||||||
private val mediaSenderRoomFactory: MediaSenderRoomFactory,
|
private val mediaSenderRoomFactory: MediaSenderRoomFactory,
|
||||||
private val activeRoomsHolder: ActiveRoomsHolder,
|
private val activeRoomsHolder: ActiveRoomsHolder,
|
||||||
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
|
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
|
||||||
|
private val onSharedData: OnSharedData,
|
||||||
) : Presenter<ShareState> {
|
) : Presenter<ShareState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
fun interface Factory {
|
fun interface Factory {
|
||||||
fun create(intent: Intent): SharePresenter
|
fun create(shareIntentData: ShareIntentData): SharePresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
private val shareActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized)
|
private val shareActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized)
|
||||||
|
|
||||||
fun onRoomSelected(roomIds: List<RoomId>) {
|
fun onRoomSelected(roomIds: List<RoomId>) {
|
||||||
sessionCoroutineScope.share(intent, roomIds)
|
sessionCoroutineScope.share(shareIntentData, roomIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -73,13 +74,24 @@ class SharePresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.share(
|
private fun CoroutineScope.share(
|
||||||
intent: Intent,
|
shareIntentData: ShareIntentData,
|
||||||
roomIds: List<RoomId>,
|
roomIds: List<RoomId>,
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
val result = shareIntentHandler.handleIncomingShareIntent(
|
val result = when (shareIntentData) {
|
||||||
intent,
|
is ShareIntentData.PlainText -> {
|
||||||
onUris = { filesToShare ->
|
roomIds
|
||||||
|
.map { roomId ->
|
||||||
|
getJoinedRoom(roomId)?.liveTimeline?.sendMessage(
|
||||||
|
body = shareIntentData.content,
|
||||||
|
htmlBody = null,
|
||||||
|
intentionalMentions = emptyList(),
|
||||||
|
)?.isSuccess.orFalse()
|
||||||
|
}
|
||||||
|
.all { it }
|
||||||
|
}
|
||||||
|
is ShareIntentData.Uris -> {
|
||||||
|
val filesToShare = shareIntentData.uris
|
||||||
if (filesToShare.isEmpty()) {
|
if (filesToShare.isEmpty()) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
@@ -90,6 +102,7 @@ class SharePresenter(
|
|||||||
filesToShare
|
filesToShare
|
||||||
.map { fileToShare ->
|
.map { fileToShare ->
|
||||||
val result = mediaSender.sendMedia(
|
val result = mediaSender.sendMedia(
|
||||||
|
caption = shareIntentData.text,
|
||||||
uri = fileToShare.uri,
|
uri = fileToShare.uri,
|
||||||
mimeType = fileToShare.mimeType,
|
mimeType = fileToShare.mimeType,
|
||||||
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
|
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
|
||||||
@@ -113,19 +126,12 @@ class SharePresenter(
|
|||||||
}
|
}
|
||||||
.all { it }
|
.all { it }
|
||||||
}
|
}
|
||||||
},
|
|
||||||
onPlainText = { text ->
|
|
||||||
roomIds
|
|
||||||
.map { roomId ->
|
|
||||||
getJoinedRoom(roomId)?.liveTimeline?.sendMessage(
|
|
||||||
body = text,
|
|
||||||
htmlBody = null,
|
|
||||||
intentionalMentions = emptyList(),
|
|
||||||
)?.isSuccess.orFalse()
|
|
||||||
}
|
|
||||||
.all { it }
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
// Handle post-processing of shared data
|
||||||
|
onSharedData(shareIntentData)
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
error("Failed to handle incoming share intent")
|
error("Failed to handle incoming share intent")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@
|
|||||||
|
|
||||||
package io.element.android.features.share.impl
|
package io.element.android.features.share.impl
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.share.api.ShareEntryPoint
|
import io.element.android.features.share.api.ShareEntryPoint
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||||
import io.element.android.libraries.roomselect.test.FakeRoomSelectEntryPoint
|
import io.element.android.libraries.roomselect.test.FakeRoomSelectEntryPoint
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.node.TestParentNode
|
import io.element.android.tests.testutils.node.TestParentNode
|
||||||
@@ -44,7 +45,7 @@ class DefaultShareEntryPointTest {
|
|||||||
override fun onDone(roomIds: List<RoomId>) = lambdaError()
|
override fun onDone(roomIds: List<RoomId>) = lambdaError()
|
||||||
}
|
}
|
||||||
val params = ShareEntryPoint.Params(
|
val params = ShareEntryPoint.Params(
|
||||||
intent = Intent(),
|
shareIntentData = ShareIntentData.PlainText(A_MESSAGE),
|
||||||
)
|
)
|
||||||
val result = entryPoint.createNode(
|
val result = entryPoint.createNode(
|
||||||
parentNode = parentNode,
|
parentNode = parentNode,
|
||||||
@@ -53,7 +54,7 @@ class DefaultShareEntryPointTest {
|
|||||||
callback = callback,
|
callback = callback,
|
||||||
)
|
)
|
||||||
assertThat(result).isInstanceOf(ShareNode::class.java)
|
assertThat(result).isInstanceOf(ShareNode::class.java)
|
||||||
assertThat(result.plugins).contains(ShareNode.Inputs(params.intent))
|
assertThat(result.plugins).contains(ShareNode.Inputs(params.shareIntentData))
|
||||||
assertThat(result.plugins).contains(callback)
|
assertThat(result.plugins).contains(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
|
||||||
* Copyright 2024, 2025 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.share.impl
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
|
|
||||||
class FakeShareIntentHandler(
|
|
||||||
private val onIncomingShareIntent: suspend (
|
|
||||||
Intent,
|
|
||||||
suspend (List<ShareIntentHandler.UriToShare>) -> Boolean,
|
|
||||||
suspend (String) -> Boolean,
|
|
||||||
) -> Boolean = { _, _, _ -> false },
|
|
||||||
) : ShareIntentHandler {
|
|
||||||
override suspend fun handleIncomingShareIntent(
|
|
||||||
intent: Intent,
|
|
||||||
onUris: suspend (List<ShareIntentHandler.UriToShare>) -> Boolean,
|
|
||||||
onPlainText: suspend (String) -> Boolean,
|
|
||||||
): Boolean {
|
|
||||||
return onIncomingShareIntent(intent, onUris, onPlainText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,12 +8,14 @@
|
|||||||
|
|
||||||
package io.element.android.features.share.impl
|
package io.element.android.features.share.impl
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import app.cash.molecule.RecompositionMode
|
import app.cash.molecule.RecompositionMode
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.share.api.OnSharedData
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
|
import io.element.android.features.share.api.UriToShare
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
@@ -72,8 +74,17 @@ class SharePresenterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - on room selected ok`() = runTest {
|
fun `present - on room selected ok`() = runTest {
|
||||||
|
val joinedRoom = FakeJoinedRoom(
|
||||||
|
liveTimeline = FakeTimeline().apply {
|
||||||
|
sendMessageLambda = { _, _, _ -> Result.success(Unit) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
|
givenGetRoomResult(A_ROOM_ID, joinedRoom)
|
||||||
|
}
|
||||||
val presenter = createSharePresenter(
|
val presenter = createSharePresenter(
|
||||||
shareIntentHandler = FakeShareIntentHandler { _, _, _ -> true }
|
matrixClient = matrixClient,
|
||||||
|
shareIntentData = ShareIntentData.PlainText(A_MESSAGE),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
@@ -100,9 +111,7 @@ class SharePresenterTest {
|
|||||||
}
|
}
|
||||||
val presenter = createSharePresenter(
|
val presenter = createSharePresenter(
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
shareIntentHandler = FakeShareIntentHandler { _, _, onText ->
|
shareIntentData = ShareIntentData.PlainText(A_MESSAGE),
|
||||||
onText(A_MESSAGE)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
@@ -131,16 +140,15 @@ class SharePresenterTest {
|
|||||||
)
|
)
|
||||||
val presenter = createSharePresenter(
|
val presenter = createSharePresenter(
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
shareIntentHandler = FakeShareIntentHandler { _, onFile, _ ->
|
shareIntentData = ShareIntentData.Uris(
|
||||||
onFile(
|
text = A_MESSAGE,
|
||||||
listOf(
|
listOf(
|
||||||
ShareIntentHandler.UriToShare(
|
UriToShare(
|
||||||
uri = Uri.parse("content://image.jpg"),
|
uri = Uri.parse("content://image.jpg"),
|
||||||
mimeType = MimeTypes.Jpeg,
|
mimeType = MimeTypes.Jpeg,
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
),
|
||||||
mediaSenderRoomFactory = MediaSenderRoomFactory { mediaSender },
|
mediaSenderRoomFactory = MediaSenderRoomFactory { mediaSender },
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
@@ -159,20 +167,20 @@ class SharePresenterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun TestScope.createSharePresenter(
|
internal fun TestScope.createSharePresenter(
|
||||||
intent: Intent = Intent(),
|
shareIntentData: ShareIntentData = ShareIntentData.PlainText(A_MESSAGE),
|
||||||
shareIntentHandler: ShareIntentHandler = FakeShareIntentHandler(),
|
|
||||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||||
activeRoomsHolder: ActiveRoomsHolder = DefaultActiveRoomsHolder(),
|
activeRoomsHolder: ActiveRoomsHolder = DefaultActiveRoomsHolder(),
|
||||||
mediaSenderRoomFactory: MediaSenderRoomFactory = MediaSenderRoomFactory { FakeMediaSender() },
|
mediaSenderRoomFactory: MediaSenderRoomFactory = MediaSenderRoomFactory { FakeMediaSender() },
|
||||||
mediaOptimizationConfigProvider: MediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
mediaOptimizationConfigProvider: MediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
||||||
|
onSharedData: OnSharedData = OnSharedData {},
|
||||||
): SharePresenter {
|
): SharePresenter {
|
||||||
return SharePresenter(
|
return SharePresenter(
|
||||||
intent = intent,
|
shareIntentData = shareIntentData,
|
||||||
sessionCoroutineScope = this,
|
sessionCoroutineScope = this,
|
||||||
shareIntentHandler = shareIntentHandler,
|
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
activeRoomsHolder = activeRoomsHolder,
|
activeRoomsHolder = activeRoomsHolder,
|
||||||
mediaSenderRoomFactory = mediaSenderRoomFactory,
|
mediaSenderRoomFactory = mediaSenderRoomFactory,
|
||||||
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
|
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
|
||||||
|
onSharedData = onSharedData,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
21
features/share/test/build.gradle.kts
Normal file
21
features/share/test/build.gradle.kts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.share.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.features.share.api)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.share.test
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import io.element.android.features.share.api.ShareIntentData
|
||||||
|
import io.element.android.features.share.api.ShareIntentHandler
|
||||||
|
|
||||||
|
class FakeShareIntentHandler(
|
||||||
|
private val onIncomingShareIntent: (Intent) -> ShareIntentData? = { null },
|
||||||
|
) : ShareIntentHandler {
|
||||||
|
override fun handleIncomingShareIntent(
|
||||||
|
intent: Intent,
|
||||||
|
): ShareIntentData? {
|
||||||
|
return onIncomingShareIntent(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,10 +51,10 @@ telephoto = "0.18.0"
|
|||||||
haze = "1.7.2"
|
haze = "1.7.2"
|
||||||
|
|
||||||
# Dependency analysis
|
# Dependency analysis
|
||||||
dependencyAnalysis = "3.6.0"
|
dependencyAnalysis = "3.6.1"
|
||||||
|
|
||||||
# DI
|
# DI
|
||||||
metro = "0.11.1"
|
metro = "0.11.2"
|
||||||
|
|
||||||
# Auto service
|
# Auto service
|
||||||
autoservice = "1.1.1"
|
autoservice = "1.1.1"
|
||||||
@@ -178,7 +178,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version
|
|||||||
# https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt
|
# https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt
|
||||||
# All new features should not be implemented in the pull request that upgrades the version, developers should
|
# All new features should not be implemented in the pull request that upgrades the version, developers should
|
||||||
# only fix API breaks and may add some TODOs.
|
# only fix API breaks and may add some TODOs.
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.03.0"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.03.1"
|
||||||
|
|
||||||
# Others
|
# Others
|
||||||
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
||||||
@@ -220,7 +220,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0"
|
|||||||
|
|
||||||
# Analytics
|
# Analytics
|
||||||
posthog = "com.posthog:posthog-android:3.34.3"
|
posthog = "com.posthog:posthog-android:3.34.3"
|
||||||
sentry = "io.sentry:sentry-android:8.33.0"
|
sentry = "io.sentry:sentry-android:8.34.0"
|
||||||
# main branch can be tested replacing the version with main-SNAPSHOT
|
# main branch can be tested replacing the version with main-SNAPSHOT
|
||||||
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.29.2"
|
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.29.2"
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -155,12 +155,10 @@ private fun getSemanticColors(): ImmutableMap<String, Color> {
|
|||||||
"gradientActionStop2" to gradientActionStop2,
|
"gradientActionStop2" to gradientActionStop2,
|
||||||
"gradientActionStop3" to gradientActionStop3,
|
"gradientActionStop3" to gradientActionStop3,
|
||||||
"gradientActionStop4" to gradientActionStop4,
|
"gradientActionStop4" to gradientActionStop4,
|
||||||
|
"gradientCriticalStop1" to gradientCriticalStop1,
|
||||||
|
"gradientCriticalStop2" to gradientCriticalStop2,
|
||||||
"gradientInfoStop1" to gradientInfoStop1,
|
"gradientInfoStop1" to gradientInfoStop1,
|
||||||
"gradientInfoStop2" to gradientInfoStop2,
|
"gradientInfoStop2" to gradientInfoStop2,
|
||||||
"gradientInfoStop3" to gradientInfoStop3,
|
|
||||||
"gradientInfoStop4" to gradientInfoStop4,
|
|
||||||
"gradientInfoStop5" to gradientInfoStop5,
|
|
||||||
"gradientInfoStop6" to gradientInfoStop6,
|
|
||||||
"gradientSubtleStop1" to gradientSubtleStop1,
|
"gradientSubtleStop1" to gradientSubtleStop1,
|
||||||
"gradientSubtleStop2" to gradientSubtleStop2,
|
"gradientSubtleStop2" to gradientSubtleStop2,
|
||||||
"gradientSubtleStop3" to gradientSubtleStop3,
|
"gradientSubtleStop3" to gradientSubtleStop3,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -25,6 +25,9 @@ object CompoundIcons {
|
|||||||
@Composable fun Admin(): ImageVector {
|
@Composable fun Admin(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_admin)
|
return ImageVector.vectorResource(R.drawable.ic_compound_admin)
|
||||||
}
|
}
|
||||||
|
@Composable fun AdvancedSettings(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_advanced_settings)
|
||||||
|
}
|
||||||
@Composable fun ArrowDown(): ImageVector {
|
@Composable fun ArrowDown(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_arrow_down)
|
return ImageVector.vectorResource(R.drawable.ic_compound_arrow_down)
|
||||||
}
|
}
|
||||||
@@ -64,6 +67,9 @@ object CompoundIcons {
|
|||||||
@Composable fun Bold(): ImageVector {
|
@Composable fun Bold(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_bold)
|
return ImageVector.vectorResource(R.drawable.ic_compound_bold)
|
||||||
}
|
}
|
||||||
|
@Composable fun Bug(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_bug)
|
||||||
|
}
|
||||||
@Composable fun Calendar(): ImageVector {
|
@Composable fun Calendar(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_calendar)
|
return ImageVector.vectorResource(R.drawable.ic_compound_calendar)
|
||||||
}
|
}
|
||||||
@@ -460,6 +466,9 @@ object CompoundIcons {
|
|||||||
@Composable fun RaisedHandSolid(): ImageVector {
|
@Composable fun RaisedHandSolid(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_raised_hand_solid)
|
return ImageVector.vectorResource(R.drawable.ic_compound_raised_hand_solid)
|
||||||
}
|
}
|
||||||
|
@Composable fun ReOrder(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_re_order)
|
||||||
|
}
|
||||||
@Composable fun Reaction(): ImageVector {
|
@Composable fun Reaction(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_reaction)
|
return ImageVector.vectorResource(R.drawable.ic_compound_reaction)
|
||||||
}
|
}
|
||||||
@@ -478,9 +487,18 @@ object CompoundIcons {
|
|||||||
@Composable fun Room(): ImageVector {
|
@Composable fun Room(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_room)
|
return ImageVector.vectorResource(R.drawable.ic_compound_room)
|
||||||
}
|
}
|
||||||
|
@Composable fun RotateLeft(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_rotate_left)
|
||||||
|
}
|
||||||
|
@Composable fun RotateRight(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_rotate_right)
|
||||||
|
}
|
||||||
@Composable fun Search(): ImageVector {
|
@Composable fun Search(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_search)
|
return ImageVector.vectorResource(R.drawable.ic_compound_search)
|
||||||
}
|
}
|
||||||
|
@Composable fun Section(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_section)
|
||||||
|
}
|
||||||
@Composable fun Send(): ImageVector {
|
@Composable fun Send(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_send)
|
return ImageVector.vectorResource(R.drawable.ic_compound_send)
|
||||||
}
|
}
|
||||||
@@ -535,6 +553,12 @@ object CompoundIcons {
|
|||||||
@Composable fun Sticker(): ImageVector {
|
@Composable fun Sticker(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_sticker)
|
return ImageVector.vectorResource(R.drawable.ic_compound_sticker)
|
||||||
}
|
}
|
||||||
|
@Composable fun Stop(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_stop)
|
||||||
|
}
|
||||||
|
@Composable fun StopSolid(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_stop_solid)
|
||||||
|
}
|
||||||
@Composable fun Strikethrough(): ImageVector {
|
@Composable fun Strikethrough(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_strikethrough)
|
return ImageVector.vectorResource(R.drawable.ic_compound_strikethrough)
|
||||||
}
|
}
|
||||||
@@ -550,6 +574,9 @@ object CompoundIcons {
|
|||||||
@Composable fun TextFormatting(): ImageVector {
|
@Composable fun TextFormatting(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_text_formatting)
|
return ImageVector.vectorResource(R.drawable.ic_compound_text_formatting)
|
||||||
}
|
}
|
||||||
|
@Composable fun Theme(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_theme)
|
||||||
|
}
|
||||||
@Composable fun Threads(): ImageVector {
|
@Composable fun Threads(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_threads)
|
return ImageVector.vectorResource(R.drawable.ic_compound_threads)
|
||||||
}
|
}
|
||||||
@@ -559,6 +586,12 @@ object CompoundIcons {
|
|||||||
@Composable fun Time(): ImageVector {
|
@Composable fun Time(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_time)
|
return ImageVector.vectorResource(R.drawable.ic_compound_time)
|
||||||
}
|
}
|
||||||
|
@Composable fun Translate(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_translate)
|
||||||
|
}
|
||||||
|
@Composable fun Tree(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_tree)
|
||||||
|
}
|
||||||
@Composable fun Underline(): ImageVector {
|
@Composable fun Underline(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_underline)
|
return ImageVector.vectorResource(R.drawable.ic_compound_underline)
|
||||||
}
|
}
|
||||||
@@ -607,6 +640,9 @@ object CompoundIcons {
|
|||||||
@Composable fun VideoCallOffSolid(): ImageVector {
|
@Composable fun VideoCallOffSolid(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_video_call_off_solid)
|
return ImageVector.vectorResource(R.drawable.ic_compound_video_call_off_solid)
|
||||||
}
|
}
|
||||||
|
@Composable fun VideoCallOutgoingSolid(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_video_call_outgoing_solid)
|
||||||
|
}
|
||||||
@Composable fun VideoCallSolid(): ImageVector {
|
@Composable fun VideoCallSolid(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_video_call_solid)
|
return ImageVector.vectorResource(R.drawable.ic_compound_video_call_solid)
|
||||||
}
|
}
|
||||||
@@ -619,6 +655,15 @@ object CompoundIcons {
|
|||||||
@Composable fun VoiceCall(): ImageVector {
|
@Composable fun VoiceCall(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_voice_call)
|
return ImageVector.vectorResource(R.drawable.ic_compound_voice_call)
|
||||||
}
|
}
|
||||||
|
@Composable fun VoiceCallDeclinedSolid(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_voice_call_declined_solid)
|
||||||
|
}
|
||||||
|
@Composable fun VoiceCallMissedSolid(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_voice_call_missed_solid)
|
||||||
|
}
|
||||||
|
@Composable fun VoiceCallOutgoingSolid(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_voice_call_outgoing_solid)
|
||||||
|
}
|
||||||
@Composable fun VoiceCallSolid(): ImageVector {
|
@Composable fun VoiceCallSolid(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_voice_call_solid)
|
return ImageVector.vectorResource(R.drawable.ic_compound_voice_call_solid)
|
||||||
}
|
}
|
||||||
@@ -643,9 +688,16 @@ object CompoundIcons {
|
|||||||
@Composable fun Windows(): ImageVector {
|
@Composable fun Windows(): ImageVector {
|
||||||
return ImageVector.vectorResource(R.drawable.ic_compound_windows)
|
return ImageVector.vectorResource(R.drawable.ic_compound_windows)
|
||||||
}
|
}
|
||||||
|
@Composable fun ZoomIn(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_zoom_in)
|
||||||
|
}
|
||||||
|
@Composable fun ZoomOut(): ImageVector {
|
||||||
|
return ImageVector.vectorResource(R.drawable.ic_compound_zoom_out)
|
||||||
|
}
|
||||||
|
|
||||||
val all @Composable get() = persistentListOf<ImageVector>(
|
val all @Composable get() = persistentListOf<ImageVector>(
|
||||||
Admin(),
|
Admin(),
|
||||||
|
AdvancedSettings(),
|
||||||
ArrowDown(),
|
ArrowDown(),
|
||||||
ArrowLeft(),
|
ArrowLeft(),
|
||||||
ArrowRight(),
|
ArrowRight(),
|
||||||
@@ -659,6 +711,7 @@ object CompoundIcons {
|
|||||||
BackspaceSolid(),
|
BackspaceSolid(),
|
||||||
Block(),
|
Block(),
|
||||||
Bold(),
|
Bold(),
|
||||||
|
Bug(),
|
||||||
Calendar(),
|
Calendar(),
|
||||||
Chart(),
|
Chart(),
|
||||||
Chat(),
|
Chat(),
|
||||||
@@ -791,13 +844,17 @@ object CompoundIcons {
|
|||||||
QrCode(),
|
QrCode(),
|
||||||
Quote(),
|
Quote(),
|
||||||
RaisedHandSolid(),
|
RaisedHandSolid(),
|
||||||
|
ReOrder(),
|
||||||
Reaction(),
|
Reaction(),
|
||||||
ReactionAdd(),
|
ReactionAdd(),
|
||||||
ReactionSolid(),
|
ReactionSolid(),
|
||||||
Reply(),
|
Reply(),
|
||||||
Restart(),
|
Restart(),
|
||||||
Room(),
|
Room(),
|
||||||
|
RotateLeft(),
|
||||||
|
RotateRight(),
|
||||||
Search(),
|
Search(),
|
||||||
|
Section(),
|
||||||
Send(),
|
Send(),
|
||||||
SendSolid(),
|
SendSolid(),
|
||||||
Settings(),
|
Settings(),
|
||||||
@@ -816,14 +873,19 @@ object CompoundIcons {
|
|||||||
Spotlight(),
|
Spotlight(),
|
||||||
SpotlightView(),
|
SpotlightView(),
|
||||||
Sticker(),
|
Sticker(),
|
||||||
|
Stop(),
|
||||||
|
StopSolid(),
|
||||||
Strikethrough(),
|
Strikethrough(),
|
||||||
SwitchCameraSolid(),
|
SwitchCameraSolid(),
|
||||||
TakePhoto(),
|
TakePhoto(),
|
||||||
TakePhotoSolid(),
|
TakePhotoSolid(),
|
||||||
TextFormatting(),
|
TextFormatting(),
|
||||||
|
Theme(),
|
||||||
Threads(),
|
Threads(),
|
||||||
ThreadsSolid(),
|
ThreadsSolid(),
|
||||||
Time(),
|
Time(),
|
||||||
|
Translate(),
|
||||||
|
Tree(),
|
||||||
Underline(),
|
Underline(),
|
||||||
Unknown(),
|
Unknown(),
|
||||||
UnknownSolid(),
|
UnknownSolid(),
|
||||||
@@ -840,10 +902,14 @@ object CompoundIcons {
|
|||||||
VideoCallMissedSolid(),
|
VideoCallMissedSolid(),
|
||||||
VideoCallOff(),
|
VideoCallOff(),
|
||||||
VideoCallOffSolid(),
|
VideoCallOffSolid(),
|
||||||
|
VideoCallOutgoingSolid(),
|
||||||
VideoCallSolid(),
|
VideoCallSolid(),
|
||||||
VisibilityOff(),
|
VisibilityOff(),
|
||||||
VisibilityOn(),
|
VisibilityOn(),
|
||||||
VoiceCall(),
|
VoiceCall(),
|
||||||
|
VoiceCallDeclinedSolid(),
|
||||||
|
VoiceCallMissedSolid(),
|
||||||
|
VoiceCallOutgoingSolid(),
|
||||||
VoiceCallSolid(),
|
VoiceCallSolid(),
|
||||||
VolumeOff(),
|
VolumeOff(),
|
||||||
VolumeOffSolid(),
|
VolumeOffSolid(),
|
||||||
@@ -852,10 +918,13 @@ object CompoundIcons {
|
|||||||
Warning(),
|
Warning(),
|
||||||
WebBrowser(),
|
WebBrowser(),
|
||||||
Windows(),
|
Windows(),
|
||||||
|
ZoomIn(),
|
||||||
|
ZoomOut(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val allResIds get() = persistentListOf(
|
val allResIds get() = persistentListOf(
|
||||||
R.drawable.ic_compound_admin,
|
R.drawable.ic_compound_admin,
|
||||||
|
R.drawable.ic_compound_advanced_settings,
|
||||||
R.drawable.ic_compound_arrow_down,
|
R.drawable.ic_compound_arrow_down,
|
||||||
R.drawable.ic_compound_arrow_left,
|
R.drawable.ic_compound_arrow_left,
|
||||||
R.drawable.ic_compound_arrow_right,
|
R.drawable.ic_compound_arrow_right,
|
||||||
@@ -869,6 +938,7 @@ object CompoundIcons {
|
|||||||
R.drawable.ic_compound_backspace_solid,
|
R.drawable.ic_compound_backspace_solid,
|
||||||
R.drawable.ic_compound_block,
|
R.drawable.ic_compound_block,
|
||||||
R.drawable.ic_compound_bold,
|
R.drawable.ic_compound_bold,
|
||||||
|
R.drawable.ic_compound_bug,
|
||||||
R.drawable.ic_compound_calendar,
|
R.drawable.ic_compound_calendar,
|
||||||
R.drawable.ic_compound_chart,
|
R.drawable.ic_compound_chart,
|
||||||
R.drawable.ic_compound_chat,
|
R.drawable.ic_compound_chat,
|
||||||
@@ -1001,13 +1071,17 @@ object CompoundIcons {
|
|||||||
R.drawable.ic_compound_qr_code,
|
R.drawable.ic_compound_qr_code,
|
||||||
R.drawable.ic_compound_quote,
|
R.drawable.ic_compound_quote,
|
||||||
R.drawable.ic_compound_raised_hand_solid,
|
R.drawable.ic_compound_raised_hand_solid,
|
||||||
|
R.drawable.ic_compound_re_order,
|
||||||
R.drawable.ic_compound_reaction,
|
R.drawable.ic_compound_reaction,
|
||||||
R.drawable.ic_compound_reaction_add,
|
R.drawable.ic_compound_reaction_add,
|
||||||
R.drawable.ic_compound_reaction_solid,
|
R.drawable.ic_compound_reaction_solid,
|
||||||
R.drawable.ic_compound_reply,
|
R.drawable.ic_compound_reply,
|
||||||
R.drawable.ic_compound_restart,
|
R.drawable.ic_compound_restart,
|
||||||
R.drawable.ic_compound_room,
|
R.drawable.ic_compound_room,
|
||||||
|
R.drawable.ic_compound_rotate_left,
|
||||||
|
R.drawable.ic_compound_rotate_right,
|
||||||
R.drawable.ic_compound_search,
|
R.drawable.ic_compound_search,
|
||||||
|
R.drawable.ic_compound_section,
|
||||||
R.drawable.ic_compound_send,
|
R.drawable.ic_compound_send,
|
||||||
R.drawable.ic_compound_send_solid,
|
R.drawable.ic_compound_send_solid,
|
||||||
R.drawable.ic_compound_settings,
|
R.drawable.ic_compound_settings,
|
||||||
@@ -1026,14 +1100,19 @@ object CompoundIcons {
|
|||||||
R.drawable.ic_compound_spotlight,
|
R.drawable.ic_compound_spotlight,
|
||||||
R.drawable.ic_compound_spotlight_view,
|
R.drawable.ic_compound_spotlight_view,
|
||||||
R.drawable.ic_compound_sticker,
|
R.drawable.ic_compound_sticker,
|
||||||
|
R.drawable.ic_compound_stop,
|
||||||
|
R.drawable.ic_compound_stop_solid,
|
||||||
R.drawable.ic_compound_strikethrough,
|
R.drawable.ic_compound_strikethrough,
|
||||||
R.drawable.ic_compound_switch_camera_solid,
|
R.drawable.ic_compound_switch_camera_solid,
|
||||||
R.drawable.ic_compound_take_photo,
|
R.drawable.ic_compound_take_photo,
|
||||||
R.drawable.ic_compound_take_photo_solid,
|
R.drawable.ic_compound_take_photo_solid,
|
||||||
R.drawable.ic_compound_text_formatting,
|
R.drawable.ic_compound_text_formatting,
|
||||||
|
R.drawable.ic_compound_theme,
|
||||||
R.drawable.ic_compound_threads,
|
R.drawable.ic_compound_threads,
|
||||||
R.drawable.ic_compound_threads_solid,
|
R.drawable.ic_compound_threads_solid,
|
||||||
R.drawable.ic_compound_time,
|
R.drawable.ic_compound_time,
|
||||||
|
R.drawable.ic_compound_translate,
|
||||||
|
R.drawable.ic_compound_tree,
|
||||||
R.drawable.ic_compound_underline,
|
R.drawable.ic_compound_underline,
|
||||||
R.drawable.ic_compound_unknown,
|
R.drawable.ic_compound_unknown,
|
||||||
R.drawable.ic_compound_unknown_solid,
|
R.drawable.ic_compound_unknown_solid,
|
||||||
@@ -1050,10 +1129,14 @@ object CompoundIcons {
|
|||||||
R.drawable.ic_compound_video_call_missed_solid,
|
R.drawable.ic_compound_video_call_missed_solid,
|
||||||
R.drawable.ic_compound_video_call_off,
|
R.drawable.ic_compound_video_call_off,
|
||||||
R.drawable.ic_compound_video_call_off_solid,
|
R.drawable.ic_compound_video_call_off_solid,
|
||||||
|
R.drawable.ic_compound_video_call_outgoing_solid,
|
||||||
R.drawable.ic_compound_video_call_solid,
|
R.drawable.ic_compound_video_call_solid,
|
||||||
R.drawable.ic_compound_visibility_off,
|
R.drawable.ic_compound_visibility_off,
|
||||||
R.drawable.ic_compound_visibility_on,
|
R.drawable.ic_compound_visibility_on,
|
||||||
R.drawable.ic_compound_voice_call,
|
R.drawable.ic_compound_voice_call,
|
||||||
|
R.drawable.ic_compound_voice_call_declined_solid,
|
||||||
|
R.drawable.ic_compound_voice_call_missed_solid,
|
||||||
|
R.drawable.ic_compound_voice_call_outgoing_solid,
|
||||||
R.drawable.ic_compound_voice_call_solid,
|
R.drawable.ic_compound_voice_call_solid,
|
||||||
R.drawable.ic_compound_volume_off,
|
R.drawable.ic_compound_volume_off,
|
||||||
R.drawable.ic_compound_volume_off_solid,
|
R.drawable.ic_compound_volume_off_solid,
|
||||||
@@ -1062,5 +1145,7 @@ object CompoundIcons {
|
|||||||
R.drawable.ic_compound_warning,
|
R.drawable.ic_compound_warning,
|
||||||
R.drawable.ic_compound_web_browser,
|
R.drawable.ic_compound_web_browser,
|
||||||
R.drawable.ic_compound_windows,
|
R.drawable.ic_compound_windows,
|
||||||
|
R.drawable.ic_compound_zoom_in,
|
||||||
|
R.drawable.ic_compound_zoom_out,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -121,18 +121,14 @@ data class SemanticColors(
|
|||||||
val gradientActionStop3: Color,
|
val gradientActionStop3: Color,
|
||||||
/** Background gradient stop for super and send buttons */
|
/** Background gradient stop for super and send buttons */
|
||||||
val gradientActionStop4: Color,
|
val gradientActionStop4: Color,
|
||||||
|
/** Subtle background gradient stop for critical */
|
||||||
|
val gradientCriticalStop1: Color,
|
||||||
|
/** Subtle background gradient stop for critical */
|
||||||
|
val gradientCriticalStop2: Color,
|
||||||
/** Subtle background gradient stop for info */
|
/** Subtle background gradient stop for info */
|
||||||
val gradientInfoStop1: Color,
|
val gradientInfoStop1: Color,
|
||||||
/** Subtle background gradient stop for info */
|
/** Subtle background gradient stop for info */
|
||||||
val gradientInfoStop2: Color,
|
val gradientInfoStop2: Color,
|
||||||
/** Subtle background gradient stop for info */
|
|
||||||
val gradientInfoStop3: Color,
|
|
||||||
/** Subtle background gradient stop for info */
|
|
||||||
val gradientInfoStop4: Color,
|
|
||||||
/** Subtle background gradient stop for info */
|
|
||||||
val gradientInfoStop5: Color,
|
|
||||||
/** Subtle background gradient stop for info */
|
|
||||||
val gradientInfoStop6: Color,
|
|
||||||
/** Subtle background gradient stop for message highlight and bloom */
|
/** Subtle background gradient stop for message highlight and bloom */
|
||||||
val gradientSubtleStop1: Color,
|
val gradientSubtleStop1: Color,
|
||||||
/** Subtle background gradient stop for message highlight and bloom */
|
/** Subtle background gradient stop for message highlight and bloom */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -73,12 +73,10 @@ val compoundColorsDark = SemanticColors(
|
|||||||
gradientActionStop2 = DarkColorTokens.colorGreen900,
|
gradientActionStop2 = DarkColorTokens.colorGreen900,
|
||||||
gradientActionStop3 = DarkColorTokens.colorGreen700,
|
gradientActionStop3 = DarkColorTokens.colorGreen700,
|
||||||
gradientActionStop4 = DarkColorTokens.colorGreen500,
|
gradientActionStop4 = DarkColorTokens.colorGreen500,
|
||||||
gradientInfoStop1 = DarkColorTokens.colorAlphaBlue500,
|
gradientCriticalStop1 = DarkColorTokens.colorRed200,
|
||||||
gradientInfoStop2 = DarkColorTokens.colorAlphaBlue400,
|
gradientCriticalStop2 = DarkColorTokens.colorThemeBg,
|
||||||
gradientInfoStop3 = DarkColorTokens.colorAlphaBlue300,
|
gradientInfoStop1 = DarkColorTokens.colorBlue200,
|
||||||
gradientInfoStop4 = DarkColorTokens.colorAlphaBlue200,
|
gradientInfoStop2 = DarkColorTokens.colorThemeBg,
|
||||||
gradientInfoStop5 = DarkColorTokens.colorAlphaBlue100,
|
|
||||||
gradientInfoStop6 = DarkColorTokens.colorTransparent,
|
|
||||||
gradientSubtleStop1 = DarkColorTokens.colorAlphaGreen500,
|
gradientSubtleStop1 = DarkColorTokens.colorAlphaGreen500,
|
||||||
gradientSubtleStop2 = DarkColorTokens.colorAlphaGreen400,
|
gradientSubtleStop2 = DarkColorTokens.colorAlphaGreen400,
|
||||||
gradientSubtleStop3 = DarkColorTokens.colorAlphaGreen300,
|
gradientSubtleStop3 = DarkColorTokens.colorAlphaGreen300,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -73,12 +73,10 @@ val compoundColorsHcDark = SemanticColors(
|
|||||||
gradientActionStop2 = DarkHcColorTokens.colorGreen900,
|
gradientActionStop2 = DarkHcColorTokens.colorGreen900,
|
||||||
gradientActionStop3 = DarkHcColorTokens.colorGreen700,
|
gradientActionStop3 = DarkHcColorTokens.colorGreen700,
|
||||||
gradientActionStop4 = DarkHcColorTokens.colorGreen500,
|
gradientActionStop4 = DarkHcColorTokens.colorGreen500,
|
||||||
gradientInfoStop1 = DarkHcColorTokens.colorAlphaBlue500,
|
gradientCriticalStop1 = DarkHcColorTokens.colorRed200,
|
||||||
gradientInfoStop2 = DarkHcColorTokens.colorAlphaBlue400,
|
gradientCriticalStop2 = DarkHcColorTokens.colorThemeBg,
|
||||||
gradientInfoStop3 = DarkHcColorTokens.colorAlphaBlue300,
|
gradientInfoStop1 = DarkHcColorTokens.colorBlue200,
|
||||||
gradientInfoStop4 = DarkHcColorTokens.colorAlphaBlue200,
|
gradientInfoStop2 = DarkHcColorTokens.colorThemeBg,
|
||||||
gradientInfoStop5 = DarkHcColorTokens.colorAlphaBlue100,
|
|
||||||
gradientInfoStop6 = DarkHcColorTokens.colorTransparent,
|
|
||||||
gradientSubtleStop1 = DarkHcColorTokens.colorAlphaGreen500,
|
gradientSubtleStop1 = DarkHcColorTokens.colorAlphaGreen500,
|
||||||
gradientSubtleStop2 = DarkHcColorTokens.colorAlphaGreen400,
|
gradientSubtleStop2 = DarkHcColorTokens.colorAlphaGreen400,
|
||||||
gradientSubtleStop3 = DarkHcColorTokens.colorAlphaGreen300,
|
gradientSubtleStop3 = DarkHcColorTokens.colorAlphaGreen300,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -73,12 +73,10 @@ val compoundColorsLight = SemanticColors(
|
|||||||
gradientActionStop2 = LightColorTokens.colorGreen700,
|
gradientActionStop2 = LightColorTokens.colorGreen700,
|
||||||
gradientActionStop3 = LightColorTokens.colorGreen900,
|
gradientActionStop3 = LightColorTokens.colorGreen900,
|
||||||
gradientActionStop4 = LightColorTokens.colorGreen1100,
|
gradientActionStop4 = LightColorTokens.colorGreen1100,
|
||||||
gradientInfoStop1 = LightColorTokens.colorAlphaBlue500,
|
gradientCriticalStop1 = LightColorTokens.colorRed200,
|
||||||
gradientInfoStop2 = LightColorTokens.colorAlphaBlue400,
|
gradientCriticalStop2 = LightColorTokens.colorThemeBg,
|
||||||
gradientInfoStop3 = LightColorTokens.colorAlphaBlue300,
|
gradientInfoStop1 = LightColorTokens.colorBlue200,
|
||||||
gradientInfoStop4 = LightColorTokens.colorAlphaBlue200,
|
gradientInfoStop2 = LightColorTokens.colorThemeBg,
|
||||||
gradientInfoStop5 = LightColorTokens.colorAlphaBlue100,
|
|
||||||
gradientInfoStop6 = LightColorTokens.colorTransparent,
|
|
||||||
gradientSubtleStop1 = LightColorTokens.colorAlphaGreen500,
|
gradientSubtleStop1 = LightColorTokens.colorAlphaGreen500,
|
||||||
gradientSubtleStop2 = LightColorTokens.colorAlphaGreen400,
|
gradientSubtleStop2 = LightColorTokens.colorAlphaGreen400,
|
||||||
gradientSubtleStop3 = LightColorTokens.colorAlphaGreen300,
|
gradientSubtleStop3 = LightColorTokens.colorAlphaGreen300,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -73,12 +73,10 @@ val compoundColorsHcLight = SemanticColors(
|
|||||||
gradientActionStop2 = LightHcColorTokens.colorGreen700,
|
gradientActionStop2 = LightHcColorTokens.colorGreen700,
|
||||||
gradientActionStop3 = LightHcColorTokens.colorGreen900,
|
gradientActionStop3 = LightHcColorTokens.colorGreen900,
|
||||||
gradientActionStop4 = LightHcColorTokens.colorGreen1100,
|
gradientActionStop4 = LightHcColorTokens.colorGreen1100,
|
||||||
gradientInfoStop1 = LightHcColorTokens.colorAlphaBlue500,
|
gradientCriticalStop1 = LightHcColorTokens.colorRed200,
|
||||||
gradientInfoStop2 = LightHcColorTokens.colorAlphaBlue400,
|
gradientCriticalStop2 = LightHcColorTokens.colorThemeBg,
|
||||||
gradientInfoStop3 = LightHcColorTokens.colorAlphaBlue300,
|
gradientInfoStop1 = LightHcColorTokens.colorBlue200,
|
||||||
gradientInfoStop4 = LightHcColorTokens.colorAlphaBlue200,
|
gradientInfoStop2 = LightHcColorTokens.colorThemeBg,
|
||||||
gradientInfoStop5 = LightHcColorTokens.colorAlphaBlue100,
|
|
||||||
gradientInfoStop6 = LightHcColorTokens.colorTransparent,
|
|
||||||
gradientSubtleStop1 = LightHcColorTokens.colorAlphaGreen500,
|
gradientSubtleStop1 = LightHcColorTokens.colorAlphaGreen500,
|
||||||
gradientSubtleStop2 = LightHcColorTokens.colorAlphaGreen400,
|
gradientSubtleStop2 = LightHcColorTokens.colorAlphaGreen400,
|
||||||
gradientSubtleStop3 = LightHcColorTokens.colorAlphaGreen300,
|
gradientSubtleStop3 = LightHcColorTokens.colorAlphaGreen300,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2m0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8"
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M13.49,11.38c0.43,-1.22 0.17,-2.64 -0.81,-3.62a3.47,3.47 0,0 0,-4.1 -0.59l2.35,2.35 -1.41,1.41 -2.35,-2.35c-0.71,1.32 -0.52,2.99 0.59,4.1 0.98,0.98 2.4,1.24 3.62,0.81l3.41,3.41c0.2,0.2 0.51,0.2 0.71,0l1.4,-1.4c0.2,-0.2 0.2,-0.51 0,-0.71z"
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
||||||
@@ -4,11 +4,11 @@
|
|||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<group>
|
<path
|
||||||
<clip-path
|
android:pathData="M15.043,8.457a1,1 0,0 1,1.414 1.414l-2.043,2.043 2.129,2.129a1,1 0,1 1,-1.414 1.414l-2.13,-2.129 -2.127,2.129a1,1 0,0 1,-1.415 -1.414l2.129,-2.129 -2.043,-2.043a1,1 0,0 1,1.414 -1.414L13,10.5z"
|
||||||
android:pathData="M0,0h24v24H0z"/>
|
android:fillColor="#FF000000"/>
|
||||||
<path
|
<path
|
||||||
android:pathData="M21.167,3.75L7.417,3.75c-0.633,0 -1.128,0.32 -1.458,0.807L1,12l4.96,7.434c0.33,0.486 0.824,0.816 1.457,0.816h13.75A1.84,1.84 0,0 0,23 18.417L23,5.583a1.84,1.84 0,0 0,-1.833 -1.833m0,14.667L7.48,18.417L3.2,12l4.272,-6.417h13.695zM10.542,16.583 L13.833,13.293 17.124,16.583 18.417,15.291L15.126,12l3.29,-3.29 -1.292,-1.293 -3.29,3.29 -3.291,-3.29L9.25,8.709 12.54,12l-3.29,3.29z"
|
android:pathData="M20,4a2,2 0,0 1,2 2v12a2,2 0,0 1,-2 2H7.28a2,2 0,0 1,-1.655 -0.877l-4.072,-6a2,2 0,0 1,0 -2.246l4.072,-6A2,2 0,0 1,7.28 4zM3.208,12l4.072,6H20V6H7.28z"
|
||||||
android:fillColor="#FF000000"/>
|
android:fillColor="#FF000000"
|
||||||
</group>
|
android:fillType="evenOdd"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:pathData="M21.15,4H7.283c-0.638,0 -1.137,0.311 -1.47,0.782l-4.66,6.73a0.87,0.87 0,0 0,0 0.986l4.66,6.72c0.333,0.462 0.832,0.782 1.47,0.782h13.869C22.168,20 23,19.2 23,18.222V5.778C23,4.8 22.168,4 21.15,4m-3.42,11.822a0.947,0.947 0,0 1,-1.304 0l-2.672,-2.569 -2.672,2.57a0.947,0.947 0,0 1,-1.303 0,0.86 0.86,0 0,1 0,-1.254L12.45,12 9.779,9.431a0.86,0.86 0,0 1,0 -1.253,0.947 0.947,0 0,1 1.303,0l2.672,2.569 2.672,-2.57a0.947,0.947 0,0 1,1.304 0c0.36,0.347 0.36,0.907 0,1.254L15.058,12l2.672,2.569a0.877,0.877 0,0 1,0 1.253"
|
android:pathData="M20,4a2,2 0,0 1,2 2v12a2,2 0,0 1,-2 2L7.33,20a2,2 0,0 1,-1.673 -0.902l-3.937,-6a2,2 0,0 1,0 -2.196l3.937,-6A2,2 0,0 1,7.33 4zM16.457,8.457a1,1 0,0 0,-1.414 0L13,10.5l-2.043,-2.043a1,1 0,0 0,-1.414 1.414l2.043,2.043 -2.129,2.129a1,1 0,0 0,1.414 1.414l2.13,-2.129 2.128,2.129a1,1 0,0 0,1.414 -1.414l-2.129,-2.129 2.043,-2.043a1,1 0,0 0,0 -1.414"
|
||||||
android:fillColor="#FF000000"/>
|
android:fillColor="#FF000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M19,8h-1.81a6,6 0,0 0,-1.82 -1.96l0.93,-0.93a0.996,0.996 0,1 0,-1.41 -1.41l-1.47,1.47C12.96,5.06 12.49,5 12,5s-0.96,0.06 -1.41,0.17L9.11,3.7A0.996,0.996 0,1 0,7.7 5.11l0.92,0.93C7.88,6.55 7.26,7.22 6.81,8H5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1.09c-0.05,0.33 -0.09,0.66 -0.09,1v1H5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1v1c0,0.34 0.04,0.67 0.09,1H5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3H19c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-1.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h1c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-1v-1c0,-0.34 -0.04,-0.67 -0.09,-1H19c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1m-6,8h-2c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h2c0.55,0 1,0.45 1,1s-0.45,1 -1,1m0,-4h-2c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h2c0.55,0 1,0.45 1,1s-0.45,1 -1,1"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:pathData="M16.099,2.4a4.1,4.1 0,0 1,-1.057 3.074c-0.747,0.862 -1.878,1.358 -3.07,1.347 -0.075,-1.081 0.315,-2.146 1.085,-2.96 0.78,-0.825 1.866,-1.346 3.042,-1.461m3.767,6.54c-1.37,0.783 -2.213,2.163 -2.234,3.657 0.002,1.69 1.092,3.215 2.768,3.873a9.4,9.4 0,0 1,-1.44 2.723c-0.848,1.178 -1.737,2.329 -3.149,2.35 -0.67,0.015 -1.124,-0.165 -1.596,-0.351 -0.493,-0.195 -1.006,-0.398 -1.809,-0.398 -0.851,0 -1.388,0.21 -1.905,0.412 -0.447,0.174 -0.88,0.343 -1.49,0.367 -1.343,0.046 -2.37,-1.258 -3.25,-2.425 -1.756,-2.383 -3.124,-6.716 -1.29,-9.664 0.861,-1.437 2.471,-2.349 4.241,-2.402 0.763,-0.015 1.494,0.258 2.136,0.497 0.49,0.183 0.928,0.347 1.286,0.347 0.315,0 0.74,-0.157 1.237,-0.34 0.78,-0.288 1.737,-0.64 2.71,-0.545 1.514,0.044 2.917,0.748 3.785,1.9"
|
android:pathData="M16.099,2.4a4.1,4.1 0,0 1,-1.057 3.073c-0.747,0.863 -1.878,1.36 -3.07,1.348 -0.075,-1.081 0.315,-2.146 1.085,-2.96 0.78,-0.825 1.866,-1.346 3.042,-1.461m3.767,6.54c-1.37,0.783 -2.214,2.163 -2.234,3.657 0.002,1.69 1.092,3.215 2.768,3.873a9.4,9.4 0,0 1,-1.44 2.723c-0.848,1.178 -1.737,2.329 -3.149,2.35 -0.671,0.015 -1.124,-0.165 -1.596,-0.351 -0.493,-0.195 -1.006,-0.398 -1.809,-0.398 -0.852,0 -1.388,0.21 -1.905,0.412 -0.447,0.174 -0.88,0.343 -1.49,0.367 -1.343,0.046 -2.37,-1.258 -3.25,-2.425 -1.756,-2.383 -3.124,-6.716 -1.29,-9.664 0.86,-1.437 2.471,-2.349 4.241,-2.402 0.763,-0.015 1.494,0.258 2.135,0.497 0.49,0.183 0.929,0.347 1.287,0.347 0.315,0 0.74,-0.157 1.237,-0.34 0.78,-0.288 1.737,-0.64 2.71,-0.545 1.514,0.044 2.917,0.748 3.785,1.9"
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="#FF000000"
|
||||||
android:fillType="evenOdd"/>
|
android:fillType="evenOdd"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M9,4c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2m0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2m0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2m6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2m0,-6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2m0,-6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M6.56,7.98C6.1,7.52 5.31,7.6 5,8.17c-0.28,0.51 -0.5,1.03 -0.67,1.58 -0.19,0.63 0.31,1.25 0.96,1.25h0.01c0.43,0 0.82,-0.28 0.94,-0.7q0.18,-0.6 0.48,-1.17c0.22,-0.37 0.15,-0.84 -0.16,-1.15M5.31,13h-0.02c-0.65,0 -1.15,0.62 -0.96,1.25 0.16,0.54 0.38,1.07 0.66,1.58 0.31,0.57 1.11,0.66 1.57,0.2 0.3,-0.31 0.38,-0.77 0.17,-1.15 -0.2,-0.37 -0.36,-0.76 -0.48,-1.16a0.97,0.97 0,0 0,-0.94 -0.72m2.85,6.02q0.765,0.42 1.59,0.66c0.62,0.18 1.24,-0.32 1.24,-0.96v-0.03c0,-0.43 -0.28,-0.82 -0.7,-0.94 -0.4,-0.12 -0.78,-0.28 -1.15,-0.48a0.97,0.97 0,0 0,-1.16 0.17l-0.03,0.03c-0.45,0.45 -0.36,1.24 0.21,1.55M13,4.07v-0.66c0,-0.89 -1.08,-1.34 -1.71,-0.71L9.17,4.83c-0.4,0.4 -0.4,1.04 0,1.43l2.13,2.08c0.63,0.62 1.7,0.17 1.7,-0.72V6.09c2.84,0.48 5,2.94 5,5.91 0,2.73 -1.82,5.02 -4.32,5.75a0.97,0.97 0,0 0,-0.68 0.94v0.02c0,0.65 0.61,1.14 1.23,0.96A7.976,7.976 0,0 0,20 12c0,-4.08 -3.05,-7.44 -7,-7.93"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h24v24H0z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M14.83,4.83 L12.7,2.7c-0.62,-0.62 -1.7,-0.18 -1.7,0.71v0.66C7.06,4.56 4,7.92 4,12c0,3.64 2.43,6.71 5.77,7.68 0.62,0.18 1.23,-0.32 1.23,-0.96v-0.03a0.97,0.97 0,0 0,-0.68 -0.94A5.98,5.98 0,0 1,6 12c0,-2.97 2.16,-5.43 5,-5.91v1.53c0,0.89 1.07,1.33 1.7,0.71l2.13,-2.08a0.99,0.99 0,0 0,0 -1.42m4.84,4.93q-0.24,-0.825 -0.66,-1.59c-0.31,-0.57 -1.1,-0.66 -1.56,-0.2l-0.01,0.01c-0.31,0.31 -0.38,0.78 -0.17,1.16 0.2,0.37 0.36,0.76 0.48,1.16 0.12,0.42 0.51,0.7 0.94,0.7h0.02c0.65,0 1.15,-0.62 0.96,-1.24M13,18.68v0.02c0,0.65 0.62,1.14 1.24,0.96q0.825,-0.24 1.59,-0.66c0.57,-0.31 0.66,-1.1 0.2,-1.56l-0.02,-0.02a0.97,0.97 0,0 0,-1.16 -0.17c-0.37,0.21 -0.76,0.37 -1.16,0.49 -0.41,0.12 -0.69,0.51 -0.69,0.94m4.44,-2.65c0.46,0.46 1.25,0.37 1.56,-0.2 0.28,-0.51 0.5,-1.04 0.67,-1.59 0.18,-0.62 -0.31,-1.24 -0.96,-1.24h-0.02c-0.44,0 -0.82,0.28 -0.94,0.7q-0.18,0.6 -0.48,1.17c-0.21,0.38 -0.13,0.86 0.17,1.16"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M16,7a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,15 8q0,0.424 0.287,0.713Q15.576,9 16,9t0.712,-0.287A0.97,0.97 0,0 0,17 8a0.97,0.97 0,0 0,-0.288 -0.713A0.97,0.97 0,0 0,16 7m0,4a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,15 12q0,0.424 0.287,0.713 0.288,0.287 0.713,0.287 0.424,0 0.712,-0.287A0.97,0.97 0,0 0,17 12a0.97,0.97 0,0 0,-0.288 -0.713A0.97,0.97 0,0 0,16 11m0,4a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,15 16q0,0.424 0.287,0.712 0.288,0.288 0.713,0.288 0.424,0 0.712,-0.288A0.97,0.97 0,0 0,17 16a0.97,0.97 0,0 0,-0.288 -0.713A0.97,0.97 0,0 0,16 15m-4,-8L8,7a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,7 8q0,0.424 0.287,0.713Q7.576,9 8,9h4q0.424,0 0.713,-0.287A0.97,0.97 0,0 0,13 8a0.97,0.97 0,0 0,-0.287 -0.713A0.97,0.97 0,0 0,12 7m0,4L8,11a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,7 12q0,0.424 0.287,0.713Q7.576,13 8,13h4q0.424,0 0.713,-0.287A0.97,0.97 0,0 0,13 12a0.97,0.97 0,0 0,-0.287 -0.713A0.97,0.97 0,0 0,12 11m0,4L8,15a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,7 16q0,0.424 0.287,0.712Q7.576,17 8,17h4q0.424,0 0.713,-0.288A0.97,0.97 0,0 0,13 16a0.97,0.97 0,0 0,-0.287 -0.713A0.97,0.97 0,0 0,12 15m7,-12q0.824,0 1.413,0.587Q21,4.176 21,5v14q0,0.824 -0.587,1.413A1.93,1.93 0,0 1,19 21L5,21q-0.824,0 -1.412,-0.587A1.93,1.93 0,0 1,3 19L3,5q0,-0.824 0.587,-1.412A1.93,1.93 0,0 1,5 3zM19,5L5,5v14h14z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M16,18v2L8,20v-2zM18,16L18,8a2,2 0,0 0,-2 -2L8,6a2,2 0,0 0,-2 2v8a2,2 0,0 0,2 2v2a4,4 0,0 1,-4 -4L4,8a4,4 0,0 1,4 -4h8a4,4 0,0 1,4 4v8a4,4 0,0 1,-4 4v-2a2,2 0,0 0,2 -2"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M4,8a4,4 0,0 1,4 -4h8a4,4 0,0 1,4 4v8a4,4 0,0 1,-4 4H8a4,4 0,0 1,-4 -4z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,22c5.52,0 10,-4.48 10,-10S17.52,2 12,2 2,6.48 2,12s4.48,10 10,10m1,-17.93c3.94,0.49 7,3.85 7,7.93s-3.05,7.44 -7,7.93z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -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:pathData="M13,2a2,2 0,0 1,2 2v4h6a2,2 0,0 1,2 2v12.586c0,0.89 -1.077,1.337 -1.707,0.707L19,21h-8a2,2 0,0 1,-2 -2v-4L5,15l-2.293,2.293c-0.63,0.63 -1.707,0.184 -1.707,-0.707L1,4a2,2 0,0 1,2 -2zM15.5,12.125L12,12.125v1.25h4.37c-0.031,0.73 -0.325,1.457 -0.871,2.151a4.4,4.4 0,0 1,-0.613 -1.026h-1.33c0.202,0.69 0.57,1.335 1.067,1.932 -0.524,0.448 -1.162,0.873 -1.912,1.263l0.578,1.11a11.3,11.3 0,0 0,2.21 -1.483c0.633,0.553 1.382,1.05 2.212,1.483l0.578,-1.11c-0.75,-0.39 -1.388,-0.815 -1.912,-1.263 0.758,-0.912 1.213,-1.939 1.245,-3.057L20,13.375v-1.25h-3.25L16.75,10.25L15.5,10.25zM3,14.172l0.586,-0.586A2,2 0,0 1,5 13h4v-2.47L6.96,10.53L6.563,12L5,12l2.031,-7L8.97,5l0.96,3.312A2,2 0,0 1,11 8h2L13,4L3,4zM7.306,9.245h1.386l-0.67,-2.481h-0.047z"
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M9.01,5v1H11c0.333,0 1,0 1.5,0.5S13,7.667 13,8v7.01c0,0.54 0.45,0.99 0.99,0.99H15v-1a2,2 0,0 1,2 -2h3a2,2 0,0 1,2 2v4a2,2 0,0 1,-2 2h-3a2,2 0,0 1,-2 -2v-1h-1.01C12.34,18 11,16.66 11,15.01V9c0,-1 0,-1 -1,-1H9v1a2,2 0,0 1,-2 2H4a2,2 0,0 1,-2 -2V5c0,-1.1 0.9,-2 2,-2h3.01a2,2 0,0 1,2 2"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M16,4a2,2 0,0 1,2 2v4.286l3.35,-2.871a1,1 0,0 1,1.65 0.759v7.652a1,1 0,0 1,-1.65 0.759L18,13.714V18a2,2 0,0 1,-2 2H6a4,4 0,0 1,-4 -4V8a4,4 0,0 1,4 -4zM9.55,9l-0.103,0.005a1,1 0,0 0,0 1.99L9.55,11h0.571l-2.828,2.828a1,1 0,0 0,1.414 1.414L11.55,12.4v0.6l0.005,0.102a1,1 0,0 0,1.99 0L13.55,13v-3l-0.005,-0.103A1,1 0,0 0,12.55 9z"
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
||||||
@@ -4,12 +4,8 @@
|
|||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<group>
|
<path
|
||||||
<clip-path
|
android:pathData="M8.929,15.1a13.6,13.6 0,0 0,4.654 3.066q2.62,1.036 5.492,0.923h0.008l0.003,-0.004 0.003,-0.002 -0.034,-3.124 -3.52,-0.483 -1.791,1.792 -0.645,-0.322a13.5,13.5 0,0 1,-3.496 -2.52,13.4 13.4,0 0,1 -2.52,-3.496l-0.322,-0.645 1.792,-1.791 -0.483,-3.52 -3.123,-0.033 -0.003,0.002 -0.003,0.004v0.002a13.65,13.65 0,0 0,0.932 5.492A13.4,13.4 0,0 0,8.93 15.1m3.92,4.926a15.6,15.6 0,0 1,-5.334 -3.511,15.4 15.4,0 0,1 -3.505,-5.346 15.6,15.6 0,0 1,-1.069 -6.274,1.93 1.93,0 0,1 0.589,-1.366c0.366,-0.366 0.84,-0.589 1.386,-0.589h0.01l3.163,0.035a1.96,1.96 0,0 1,1.958 1.694v0.005l0.487,3.545v0.003c0.043,0.297 0.025,0.605 -0.076,0.907a2,2 0,0 1,-0.485 0.773l-0.762,0.762a11.3,11.3 0,0 0,1.806 2.348,11.4 11.4,0 0,0 2.348,1.806l0.762,-0.762a2,2 0,0 1,0.774 -0.485c0.302,-0.1 0.61,-0.118 0.907,-0.076l3.553,0.487a1.96,1.96 0,0 1,1.694 1.958l0.034,3.174c0,0.546 -0.223,1.02 -0.588,1.386 -0.36,0.36 -0.827,0.582 -1.363,0.588a15.3,15.3 0,0 1,-6.29 -1.062"
|
||||||
android:pathData="M0,0h24v24H0z"/>
|
android:fillColor="#FF000000"
|
||||||
<path
|
android:fillType="evenOdd"/>
|
||||||
android:pathData="M8.929,15.1a13.6,13.6 0,0 0,4.654 3.066q2.62,1.036 5.492,0.923h0.008l0.003,-0.004 0.003,-0.002 -0.034,-3.124 -3.52,-0.483 -1.791,1.792 -0.645,-0.322a13.5,13.5 0,0 1,-3.496 -2.52,13.4 13.4,0 0,1 -2.52,-3.496l-0.322,-0.644 1.792,-1.792 -0.483,-3.519 -3.123,-0.034 -0.003,0.002 -0.003,0.004v0.002a13.65,13.65 0,0 0,0.932 5.492A13.4,13.4 0,0 0,8.93 15.1m3.92,4.926a15.6,15.6 0,0 1,-5.334 -3.511,15.4 15.4,0 0,1 -3.505,-5.346 15.6,15.6 0,0 1,-1.069 -6.274,1.93 1.93,0 0,1 0.589,-1.366c0.366,-0.366 0.84,-0.589 1.386,-0.589h0.01l3.163,0.035a1.96,1.96 0,0 1,1.958 1.694v0.005l0.487,3.545v0.003c0.043,0.297 0.025,0.605 -0.076,0.907a2,2 0,0 1,-0.485 0.773l-0.762,0.762a11.4,11.4 0,0 0,3.206 3.54q0.457,0.33 0.948,0.614l0.762,-0.761a2,2 0,0 1,0.774 -0.486c0.302,-0.1 0.61,-0.118 0.907,-0.076l3.553,0.487a1.96,1.96 0,0 1,1.694 1.958l0.034,3.174c0,0.546 -0.223,1.02 -0.588,1.386 -0.361,0.36 -0.827,0.582 -1.363,0.588a15.3,15.3 0,0 1,-6.29 -1.062"
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:fillType="evenOdd"/>
|
|
||||||
</group>
|
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M7.623,3.04a1.07,1.07 0,0 1,1.086 0.929l0.542,3.954q0.039,0.27 -0.038,0.504a1.1,1.1 0,0 1,-0.272 0.427l-1.64,1.64Q7.806,11.5 8.456,12.4c0.433,0.601 1.444,1.697 1.444,1.697 0.013,0.012 1.098,1.014 1.696,1.444q0.9,0.65 1.909,1.153l1.64,-1.64q0.194,-0.194 0.426,-0.27a1.1,1.1 0,0 1,0.504 -0.04l3.953,0.543q0.407,0.058 0.67,0.358 0.26,0.301 0.26,0.728l0.04,3.527q0,0.427 -0.33,0.756 -0.33,0.33 -0.756,0.33a16,16 0,0 1,-6.57 -1.105,16.2 16.2,0 0,1 -5.563,-3.663 16.1,16.1 0,0 1,-3.653 -5.573,16.3 16.3,0 0,1 -1.116,-6.56q0,-0.426 0.329,-0.756Q3.67,3 4.095,3zM20.25,3q0.405,0 0.707,0.3 0.3,0.301 0.3,0.708t-0.3,0.707l-1.414,1.414 1.414,1.414q0.3,0.3 0.3,0.707t-0.3,0.707 -0.707,0.3 -0.707,-0.3l-1.414,-1.414 -1.414,1.414q-0.3,0.3 -0.707,0.3t-0.707,-0.3T15,8.25q0,-0.406 0.3,-0.707l1.415,-1.414L15.3,4.715q-0.3,-0.3 -0.301,-0.707 0,-0.407 0.3,-0.707t0.71,-0.301q0.405,0 0.707,0.3l1.414,1.415L19.543,3.3q0.3,-0.3 0.707,-0.301"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M7.623,3.04a1.07,1.07 0,0 1,1.086 0.929l0.542,3.954q0.039,0.27 -0.038,0.504a1.1,1.1 0,0 1,-0.272 0.427l-1.64,1.64Q7.806,11.5 8.456,12.4c0.433,0.601 1.444,1.697 1.444,1.697 0.013,0.012 1.098,1.014 1.696,1.444q0.9,0.65 1.909,1.153l1.64,-1.64q0.194,-0.194 0.426,-0.27a1.1,1.1 0,0 1,0.504 -0.04l3.953,0.543q0.407,0.058 0.67,0.358 0.26,0.301 0.26,0.728l0.04,3.527q0,0.427 -0.33,0.756 -0.33,0.33 -0.756,0.33a16,16 0,0 1,-6.57 -1.105,16.2 16.2,0 0,1 -5.563,-3.663 16.1,16.1 0,0 1,-3.653 -5.573,16.3 16.3,0 0,1 -1.116,-6.56q0,-0.426 0.329,-0.756Q3.67,3 4.095,3z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M16,5q0.425,0 0.713,0.287Q17,5.575 17,6a0.97,0.97 0,0 1,-0.287 0.713A0.97,0.97 0,0 1,16 7h-0.5l2.2,2.15 2.4,-2.4a0.95,0.95 0,0 1,0.7 -0.275,0.95 0.95,0 0,1 0.7,0.275q0.3,0.3 0.3,0.7a0.92,0.92 0,0 1,-0.275 0.675l-3.125,3.15a0.8,0.8 0,0 1,-0.312 0.225,1.04 1.04,0 0,1 -0.776,0 0.9,0.9 0,0 1,-0.312 -0.2l-3,-3V9a0.97,0.97 0,0 1,-0.287 0.713A0.97,0.97 0,0 1,13 10a0.97,0.97 0,0 1,-0.713 -0.287A0.97,0.97 0,0 1,12 9V6q0,-0.425 0.287,-0.713A0.97,0.97 0,0 1,13 5z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M7.623,3.04a1.07,1.07 0,0 1,1.086 0.929l0.542,3.954q0.039,0.27 -0.038,0.504a1.1,1.1 0,0 1,-0.272 0.427l-1.64,1.64Q7.806,11.5 8.456,12.4c0.433,0.601 1.444,1.697 1.444,1.697 0.013,0.012 1.098,1.014 1.696,1.444q0.9,0.65 1.909,1.153l1.64,-1.64q0.194,-0.194 0.426,-0.27a1.1,1.1 0,0 1,0.504 -0.04l3.953,0.543q0.407,0.058 0.67,0.358 0.26,0.301 0.26,0.728l0.04,3.527q0,0.427 -0.33,0.756 -0.33,0.33 -0.756,0.33a16,16 0,0 1,-6.57 -1.105,16.2 16.2,0 0,1 -5.563,-3.663 16.1,16.1 0,0 1,-3.653 -5.573,16.3 16.3,0 0,1 -1.116,-6.56q0,-0.426 0.329,-0.756Q3.67,3 4.095,3z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.964,3a1,1 0,0 1,0.995 0.897l0.005,0.103v3l-0.005,0.103a1,1 0,0 1,-1.99 0L18.964,7v-0.605l-4.05,4.02A1,1 0,0 1,13.5 9l4.03,-4h-0.566l-0.103,-0.005a1,1 0,0 1,0 -1.99L16.964,3z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</vector>
|
||||||
@@ -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">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h24v24H0z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.5,6.5q0.425,0 0.713,0.287 0.288,0.288 0.287,0.713v2h2q0.425,0 0.713,0.287 0.288,0.288 0.287,0.713a0.97,0.97 0,0 1,-0.287 0.713,0.97 0.97,0 0,1 -0.713,0.287h-2v2a0.97,0.97 0,0 1,-0.287 0.713,0.97 0.97,0 0,1 -0.713,0.287 0.97,0.97 0,0 1,-0.713 -0.287,0.97 0.97,0 0,1 -0.287,-0.713v-2h-2a0.97,0.97 0,0 1,-0.713 -0.287,0.97 0.97,0 0,1 -0.287,-0.713q0,-0.425 0.287,-0.713A0.97,0.97 0,0 1,7.5 9.5h2v-2q0,-0.425 0.287,-0.713A0.97,0.97 0,0 1,10.5 6.5"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.5,3a7.5,7.5 0,0 1,5.963 12.049l3.244,3.244a1,1 0,1 1,-1.414 1.414l-3.244,-3.244A7.5,7.5 0,1 1,10.5 3m0,2a5.5,5.5 0,1 0,0 11,5.5 5.5,0 0,0 0,-11"
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15.05,16.463a7.5,7.5 0,1 1,1.414 -1.414l3.243,3.244a1,1 0,0 1,-1.414 1.414zM16,10.5a5.5,5.5 0,1 0,-11 0,5.5 5.5,0 0,0 11,0"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M7.875,11.375h1.75v1.75q0,0.372 0.252,0.623A0.85,0.85 0,0 0,10.5 14a0.85,0.85 0,0 0,0.623 -0.252,0.85 0.85,0 0,0 0.252,-0.623v-1.75h1.75a0.85,0.85 0,0 0,0.623 -0.252A0.85,0.85 0,0 0,14 10.5a0.85,0.85 0,0 0,-0.252 -0.623,0.85 0.85,0 0,0 -0.623,-0.252h-1.75v-1.75a0.85,0.85 0,0 0,-0.252 -0.623A0.85,0.85 0,0 0,10.5 7a0.85,0.85 0,0 0,-0.623 0.252,0.85 0.85,0 0,0 -0.252,0.623v1.75h-1.75a0.85,0.85 0,0 0,-0.623 0.252A0.85,0.85 0,0 0,7 10.5q0,0.372 0.252,0.623a0.85,0.85 0,0 0,0.623 0.252"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M13.5,9.5q0.425,0 0.713,0.287 0.288,0.288 0.287,0.713a0.97,0.97 0,0 1,-0.287 0.713,0.97 0.97,0 0,1 -0.713,0.287h-6a0.97,0.97 0,0 1,-0.713 -0.287,0.97 0.97,0 0,1 -0.287,-0.713q0,-0.425 0.287,-0.713A0.97,0.97 0,0 1,7.5 9.5z"
|
||||||
|
android:fillColor="#FF000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.5,3a7.5,7.5 0,0 1,5.963 12.049l3.244,3.244a1,1 0,1 1,-1.414 1.414l-3.244,-3.244A7.5,7.5 0,1 1,10.5 3m0,2a5.5,5.5 0,1 0,0 11,5.5 5.5,0 0,0 0,-11"
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
||||||
@@ -26,6 +26,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
|
import io.element.android.libraries.designsystem.colors.gradientCriticalColors
|
||||||
|
import io.element.android.libraries.designsystem.colors.gradientInfoColors
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||||
@@ -38,13 +40,16 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
|||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=2392-6721
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ComposerAlertMolecule(
|
fun ComposerAlertMolecule(
|
||||||
avatar: AvatarData?,
|
avatar: AvatarData?,
|
||||||
content: AnnotatedString,
|
content: AnnotatedString,
|
||||||
onSubmitClick: () -> Unit,
|
onSubmitClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
level: ComposerAlertLevel = ComposerAlertLevel.Default,
|
level: ComposerAlertLevel = ComposerAlertLevel.Info,
|
||||||
showIcon: Boolean = false,
|
showIcon: Boolean = false,
|
||||||
submitText: String = stringResource(CommonStrings.action_ok),
|
submitText: String = stringResource(CommonStrings.action_ok),
|
||||||
) {
|
) {
|
||||||
@@ -52,20 +57,12 @@ fun ComposerAlertMolecule(
|
|||||||
modifier.fillMaxWidth()
|
modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
val lineColor = when (level) {
|
val lineColor = when (level) {
|
||||||
ComposerAlertLevel.Default -> ElementTheme.colors.borderInfoSubtle
|
|
||||||
ComposerAlertLevel.Info -> ElementTheme.colors.borderInfoSubtle
|
ComposerAlertLevel.Info -> ElementTheme.colors.borderInfoSubtle
|
||||||
ComposerAlertLevel.Critical -> ElementTheme.colors.borderCriticalSubtle
|
ComposerAlertLevel.Critical -> ElementTheme.colors.borderCriticalSubtle
|
||||||
}
|
}
|
||||||
|
|
||||||
val startColor = when (level) {
|
|
||||||
ComposerAlertLevel.Default -> ElementTheme.colors.bgInfoSubtle
|
|
||||||
ComposerAlertLevel.Info -> ElementTheme.colors.bgInfoSubtle
|
|
||||||
ComposerAlertLevel.Critical -> ElementTheme.colors.bgCriticalSubtle
|
|
||||||
}
|
|
||||||
|
|
||||||
val textColor = when (level) {
|
val textColor = when (level) {
|
||||||
ComposerAlertLevel.Default -> ElementTheme.colors.textPrimary
|
ComposerAlertLevel.Info -> ElementTheme.colors.textPrimary
|
||||||
ComposerAlertLevel.Info -> ElementTheme.colors.textInfoPrimary
|
|
||||||
ComposerAlertLevel.Critical -> ElementTheme.colors.textCriticalPrimary
|
ComposerAlertLevel.Critical -> ElementTheme.colors.textCriticalPrimary
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +72,13 @@ fun ComposerAlertMolecule(
|
|||||||
.height(1.dp)
|
.height(1.dp)
|
||||||
.background(lineColor)
|
.background(lineColor)
|
||||||
)
|
)
|
||||||
val brush = Brush.verticalGradient(
|
val gradientColors = when (level) {
|
||||||
listOf(startColor, ElementTheme.colors.bgCanvasDefault),
|
ComposerAlertLevel.Info -> gradientInfoColors()
|
||||||
)
|
ComposerAlertLevel.Critical -> gradientCriticalColors()
|
||||||
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(brush)
|
.background(Brush.verticalGradient(gradientColors))
|
||||||
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
|
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -96,12 +94,10 @@ fun ComposerAlertMolecule(
|
|||||||
)
|
)
|
||||||
} else if (showIcon) {
|
} else if (showIcon) {
|
||||||
val icon = when (level) {
|
val icon = when (level) {
|
||||||
ComposerAlertLevel.Default -> CompoundIcons.Info()
|
|
||||||
ComposerAlertLevel.Info -> CompoundIcons.Info()
|
ComposerAlertLevel.Info -> CompoundIcons.Info()
|
||||||
ComposerAlertLevel.Critical -> CompoundIcons.Error()
|
ComposerAlertLevel.Critical -> CompoundIcons.Error()
|
||||||
}
|
}
|
||||||
val iconTint = when (level) {
|
val iconTint = when (level) {
|
||||||
ComposerAlertLevel.Default -> ElementTheme.colors.iconPrimary
|
|
||||||
ComposerAlertLevel.Info -> ElementTheme.colors.iconInfoPrimary
|
ComposerAlertLevel.Info -> ElementTheme.colors.iconInfoPrimary
|
||||||
ComposerAlertLevel.Critical -> ElementTheme.colors.iconCriticalPrimary
|
ComposerAlertLevel.Critical -> ElementTheme.colors.iconCriticalPrimary
|
||||||
}
|
}
|
||||||
@@ -131,7 +127,6 @@ fun ComposerAlertMolecule(
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class ComposerAlertLevel {
|
enum class ComposerAlertLevel {
|
||||||
Default,
|
|
||||||
Info,
|
Info,
|
||||||
Critical
|
Critical
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ internal data class ComposerAlertMoleculeParams(
|
|||||||
|
|
||||||
internal class ComposerAlertMoleculeParamsProvider : PreviewParameterProvider<ComposerAlertMoleculeParams> {
|
internal class ComposerAlertMoleculeParamsProvider : PreviewParameterProvider<ComposerAlertMoleculeParams> {
|
||||||
private val allLevels = sequenceOf(
|
private val allLevels = sequenceOf(
|
||||||
ComposerAlertLevel.Default,
|
|
||||||
ComposerAlertLevel.Info,
|
ComposerAlertLevel.Info,
|
||||||
ComposerAlertLevel.Critical
|
ComposerAlertLevel.Critical
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -38,8 +38,11 @@ fun gradientSubtleColors(): List<Color> = listOf(
|
|||||||
fun gradientInfoColors(): List<Color> = listOf(
|
fun gradientInfoColors(): List<Color> = listOf(
|
||||||
ElementTheme.colors.gradientInfoStop1,
|
ElementTheme.colors.gradientInfoStop1,
|
||||||
ElementTheme.colors.gradientInfoStop2,
|
ElementTheme.colors.gradientInfoStop2,
|
||||||
ElementTheme.colors.gradientInfoStop3,
|
)
|
||||||
ElementTheme.colors.gradientInfoStop4,
|
|
||||||
ElementTheme.colors.gradientInfoStop5,
|
@Composable
|
||||||
ElementTheme.colors.gradientInfoStop6,
|
@ReadOnlyComposable
|
||||||
|
fun gradientCriticalColors(): List<Color> = listOf(
|
||||||
|
ElementTheme.colors.gradientCriticalStop1,
|
||||||
|
ElementTheme.colors.gradientCriticalStop2,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import io.element.android.libraries.designsystem.R
|
|||||||
// All the icons should be defined in Compound.
|
// All the icons should be defined in Compound.
|
||||||
internal val iconsOther = listOf(
|
internal val iconsOther = listOf(
|
||||||
R.drawable.ic_notification,
|
R.drawable.ic_notification,
|
||||||
R.drawable.ic_stop,
|
|
||||||
R.drawable.pin,
|
R.drawable.pin,
|
||||||
R.drawable.ic_winner,
|
R.drawable.ic_winner,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:pathData="M6,16V8C6,7.45 6.196,6.979 6.588,6.588C6.979,6.196 7.45,6 8,6H16C16.55,6 17.021,6.196 17.413,6.588C17.804,6.979 18,7.45 18,8V16C18,16.55 17.804,17.021 17.413,17.413C17.021,17.804 16.55,18 16,18H8C7.45,18 6.979,17.804 6.588,17.413C6.196,17.021 6,16.55 6,16Z"
|
|
||||||
android:fillColor="#ffffff"/>
|
|
||||||
</vector>
|
|
||||||
@@ -22,6 +22,11 @@ sealed class NotificationResolverException : Exception() {
|
|||||||
*/
|
*/
|
||||||
data object EventFilteredOut : NotificationResolverException()
|
data object EventFilteredOut : NotificationResolverException()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The event was found but it has been redacted.
|
||||||
|
*/
|
||||||
|
data object EventRedacted : NotificationResolverException()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unexpected error occurred while trying to resolve the event.
|
* An unexpected error occurred while trying to resolve the event.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,13 +33,33 @@ sealed class ErrorType(message: String) : Exception(message) {
|
|||||||
*/
|
*/
|
||||||
class NotFound(message: String) : ErrorType(message)
|
class NotFound(message: String) : ErrorType(message)
|
||||||
|
|
||||||
/**
|
|
||||||
* The device could not be created.
|
|
||||||
*/
|
|
||||||
class UnableToCreateDevice(message: String) : ErrorType(message)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unknown error has happened.
|
* An unknown error has happened.
|
||||||
*/
|
*/
|
||||||
class Unknown(message: String) : ErrorType(message)
|
class Unknown(message: String) : ErrorType(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The requested device was not returned by the homeserver.
|
||||||
|
*/
|
||||||
|
class DeviceNotFound(message: String) : ErrorType(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The other device is already signed in and so does not need to sign in.
|
||||||
|
*/
|
||||||
|
class OtherDeviceAlreadySignedIn(message: String) : ErrorType(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sign in was cancelled.
|
||||||
|
*/
|
||||||
|
class Cancelled(message: String) : ErrorType(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sign in was not completed in the required time.
|
||||||
|
*/
|
||||||
|
class Expired(message: String) : ErrorType(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A secure connection could not have been established between the two devices.
|
||||||
|
*/
|
||||||
|
class ConnectionInsecure(message: String) : ErrorType(message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package io.element.android.libraries.matrix.api.media
|
package io.element.android.libraries.matrix.api.media
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -16,9 +17,20 @@ data class MediaSource(
|
|||||||
/**
|
/**
|
||||||
* Url of the media.
|
* Url of the media.
|
||||||
*/
|
*/
|
||||||
val url: String,
|
private val url: String,
|
||||||
/**
|
/**
|
||||||
* This is used to hold data for encrypted media.
|
* This is used to hold data for encrypted media.
|
||||||
*/
|
*/
|
||||||
val json: String? = null,
|
val json: String? = null,
|
||||||
) : Parcelable
|
) : Parcelable {
|
||||||
|
/**
|
||||||
|
* A URL with invalid parts (like `#fragment`, if it's an MXC url) removed.
|
||||||
|
*/
|
||||||
|
@IgnoredOnParcel
|
||||||
|
val safeUrl = if (url.startsWith("mxc")) {
|
||||||
|
// We've seen some MXC urls in the wild having some `mxc://foo/bar#auto` fragment suffix, which is invalid
|
||||||
|
url.substringBefore("#")
|
||||||
|
} else {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ interface RoomListService {
|
|||||||
data object Hide : SyncIndicator
|
data object Hide : SyncIndicator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the initial sliding sync request is done or not.
|
||||||
|
*/
|
||||||
|
val isInitialSyncDone: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a room list that can be used to load more rooms and filter them dynamically.
|
* Creates a room list that can be used to load more rooms and filter them dynamically.
|
||||||
* @param pageSize the number of rooms to load at once.
|
* @param pageSize the number of rooms to load at once.
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.api.media
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.matrix.test.media.aMediaSource
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class MediaSourceTest {
|
||||||
|
@Test
|
||||||
|
fun `safeUrl removes the fragment part in MXC urls`() {
|
||||||
|
val mediaSource = aMediaSource(url = "mxc://matrix.org/url#fragment")
|
||||||
|
assertThat(mediaSource.safeUrl).isEqualTo("mxc://matrix.org/url")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `safeUrl keeps the fragment part in a non-MXC url`() {
|
||||||
|
val mediaSource = aMediaSource(url = "https://matrix.org/url#fragment")
|
||||||
|
assertThat(mediaSource.safeUrl).isEqualTo("https://matrix.org/url#fragment")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -83,7 +83,7 @@ import io.element.android.libraries.matrix.impl.util.SessionPathsProvider
|
|||||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||||
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
|
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
|
||||||
import io.element.android.libraries.matrix.impl.workmanager.PerformDatabaseVacuumWorkManagerRequest
|
import io.element.android.libraries.matrix.impl.workmanager.PerformDatabaseVacuumRequestBuilder
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
||||||
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
||||||
@@ -832,8 +832,8 @@ class RustMatrixClient(
|
|||||||
if (workManagerScheduler.hasPendingWork(sessionId, WorkManagerRequestType.DB_VACUUM)) return
|
if (workManagerScheduler.hasPendingWork(sessionId, WorkManagerRequestType.DB_VACUUM)) return
|
||||||
|
|
||||||
Timber.i("Scheduling periodic database vacuuming for session $sessionId")
|
Timber.i("Scheduling periodic database vacuuming for session $sessionId")
|
||||||
val request = PerformDatabaseVacuumWorkManagerRequest(sessionId)
|
val request = PerformDatabaseVacuumRequestBuilder(sessionId)
|
||||||
workManagerScheduler.submit(request)
|
sessionCoroutineScope.launch { workManagerScheduler.submit(request) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ internal fun HumanQrGrantLoginException.map() = when (this) {
|
|||||||
is HumanQrGrantLoginException.InvalidCheckCode -> ErrorType.InvalidCheckCode(message.orEmpty())
|
is HumanQrGrantLoginException.InvalidCheckCode -> ErrorType.InvalidCheckCode(message.orEmpty())
|
||||||
is HumanQrGrantLoginException.MissingSecretsBackup -> ErrorType.MissingSecretsBackup(message.orEmpty())
|
is HumanQrGrantLoginException.MissingSecretsBackup -> ErrorType.MissingSecretsBackup(message.orEmpty())
|
||||||
is HumanQrGrantLoginException.NotFound -> ErrorType.NotFound(message.orEmpty())
|
is HumanQrGrantLoginException.NotFound -> ErrorType.NotFound(message.orEmpty())
|
||||||
is HumanQrGrantLoginException.UnableToCreateDevice -> ErrorType.UnableToCreateDevice(message.orEmpty())
|
is HumanQrGrantLoginException.Cancelled -> ErrorType.Cancelled(message.orEmpty())
|
||||||
|
is HumanQrGrantLoginException.ConnectionInsecure -> ErrorType.ConnectionInsecure(message.orEmpty())
|
||||||
|
is HumanQrGrantLoginException.DeviceNotFound -> ErrorType.DeviceNotFound(message.orEmpty())
|
||||||
|
is HumanQrGrantLoginException.Expired -> ErrorType.Expired(message.orEmpty())
|
||||||
|
is HumanQrGrantLoginException.OtherDeviceAlreadySignedIn -> ErrorType.OtherDeviceAlreadySignedIn(message.orEmpty())
|
||||||
is HumanQrGrantLoginException.Unknown -> ErrorType.Unknown(message.orEmpty())
|
is HumanQrGrantLoginException.Unknown -> ErrorType.Unknown(message.orEmpty())
|
||||||
is HumanQrGrantLoginException.UnsupportedProtocol -> ErrorType.UnsupportedProtocol(message.orEmpty())
|
is HumanQrGrantLoginException.UnsupportedProtocol -> ErrorType.UnsupportedProtocol(message.orEmpty())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class RustMediaLoader(
|
|||||||
return if (json != null) {
|
return if (json != null) {
|
||||||
RustMediaSource.fromJson(json)
|
RustMediaSource.fromJson(json)
|
||||||
} else {
|
} else {
|
||||||
RustMediaSource.fromUrl(url)
|
RustMediaSource.fromUrl(safeUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ class RustNotificationService(
|
|||||||
Timber.d("Could not retrieve event for notification with $eventId - event filtered out")
|
Timber.d("Could not retrieve event for notification with $eventId - event filtered out")
|
||||||
put(eventId, Result.failure(NotificationResolverException.EventFilteredOut))
|
put(eventId, Result.failure(NotificationResolverException.EventFilteredOut))
|
||||||
}
|
}
|
||||||
|
NotificationStatus.EventRedacted -> {
|
||||||
|
Timber.d("Could not retrieve event for notification with $eventId - event redacted")
|
||||||
|
put(eventId, Result.failure(NotificationResolverException.EventRedacted))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BatchNotificationResult.Error -> {
|
is BatchNotificationResult.Error -> {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
|
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
|
||||||
|
|
||||||
internal class RustRoomListService(
|
internal class RustRoomListService(
|
||||||
@@ -33,6 +34,9 @@ internal class RustRoomListService(
|
|||||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||||
private val sessionCoroutineScope: CoroutineScope,
|
private val sessionCoroutineScope: CoroutineScope,
|
||||||
) : RoomListService {
|
) : RoomListService {
|
||||||
|
private val _isInitialSyncDone = AtomicBoolean(false)
|
||||||
|
override val isInitialSyncDone: Boolean get() = _isInitialSyncDone.get()
|
||||||
|
|
||||||
override fun createRoomList(
|
override fun createRoomList(
|
||||||
pageSize: Int,
|
pageSize: Int,
|
||||||
source: RoomList.Source,
|
source: RoomList.Source,
|
||||||
@@ -75,6 +79,9 @@ internal class RustRoomListService(
|
|||||||
.map { it.toRoomListState() }
|
.map { it.toRoomListState() }
|
||||||
.onEach { state ->
|
.onEach { state ->
|
||||||
Timber.d("RoomList state=$state")
|
Timber.d("RoomList state=$state")
|
||||||
|
if (state == RoomListService.State.Running) {
|
||||||
|
_isInitialSyncDone.set(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
|
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import org.matrix.rustcomponents.sdk.TimelineDiff
|
|||||||
import org.matrix.rustcomponents.sdk.TimelineInterface
|
import org.matrix.rustcomponents.sdk.TimelineInterface
|
||||||
import org.matrix.rustcomponents.sdk.TimelineListener
|
import org.matrix.rustcomponents.sdk.TimelineListener
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uniffi.matrix_sdk.RoomPaginationStatus
|
import uniffi.matrix_sdk.PaginationStatus
|
||||||
|
|
||||||
internal fun TimelineInterface.liveBackPaginationStatus(): Flow<RoomPaginationStatus> = callbackFlow {
|
internal fun TimelineInterface.liveBackPaginationStatus(): Flow<PaginationStatus> = callbackFlow {
|
||||||
val listener = object : PaginationStatusListener {
|
val listener = object : PaginationStatusListener {
|
||||||
override fun onUpdate(status: RoomPaginationStatus) {
|
override fun onUpdate(status: PaginationStatus) {
|
||||||
trySend(status)
|
trySend(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ import org.matrix.rustcomponents.sdk.UploadParameters
|
|||||||
import org.matrix.rustcomponents.sdk.UploadSource
|
import org.matrix.rustcomponents.sdk.UploadSource
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uniffi.matrix_sdk.RoomPaginationStatus
|
import uniffi.matrix_sdk.PaginationStatus
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import org.matrix.rustcomponents.sdk.EventOrTransactionId as RustEventOrTransactionId
|
import org.matrix.rustcomponents.sdk.EventOrTransactionId as RustEventOrTransactionId
|
||||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||||
@@ -147,8 +147,8 @@ class RustTimeline(
|
|||||||
.onEach { backPaginationStatus ->
|
.onEach { backPaginationStatus ->
|
||||||
updatePaginationStatus(Timeline.PaginationDirection.BACKWARDS) {
|
updatePaginationStatus(Timeline.PaginationDirection.BACKWARDS) {
|
||||||
when (backPaginationStatus) {
|
when (backPaginationStatus) {
|
||||||
is RoomPaginationStatus.Idle -> it.copy(isPaginating = false, hasMoreToLoad = !backPaginationStatus.hitTimelineStart)
|
is PaginationStatus.Idle -> it.copy(isPaginating = false, hasMoreToLoad = !backPaginationStatus.hitTimelineStart)
|
||||||
is RoomPaginationStatus.Paginating -> it.copy(isPaginating = true, hasMoreToLoad = true)
|
is PaginationStatus.Paginating -> it.copy(isPaginating = true, hasMoreToLoad = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ package io.element.android.libraries.matrix.impl.workmanager
|
|||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.Data
|
import androidx.work.Data
|
||||||
import androidx.work.PeriodicWorkRequest
|
import androidx.work.PeriodicWorkRequest
|
||||||
import androidx.work.WorkRequest
|
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.impl.workmanager.VacuumDatabaseWorker.Companion.SESSION_ID_PARAM
|
import io.element.android.libraries.matrix.impl.workmanager.VacuumDatabaseWorker.Companion.SESSION_ID_PARAM
|
||||||
import io.element.android.libraries.workmanager.api.WorkManagerRequest
|
import io.element.android.libraries.workmanager.api.WorkManagerRequestBuilder
|
||||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
||||||
|
import io.element.android.libraries.workmanager.api.WorkManagerRequestWrapper
|
||||||
import io.element.android.libraries.workmanager.api.workManagerTag
|
import io.element.android.libraries.workmanager.api.workManagerTag
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class PerformDatabaseVacuumWorkManagerRequest(
|
class PerformDatabaseVacuumRequestBuilder(
|
||||||
private val sessionId: SessionId,
|
private val sessionId: SessionId,
|
||||||
) : WorkManagerRequest {
|
) : WorkManagerRequestBuilder {
|
||||||
override fun build(): Result<List<WorkRequest>> {
|
override suspend fun build(): Result<List<WorkManagerRequestWrapper>> {
|
||||||
val data = Data.Builder().putString(SESSION_ID_PARAM, sessionId.value).build()
|
val data = Data.Builder().putString(SESSION_ID_PARAM, sessionId.value).build()
|
||||||
val workRequest = PeriodicWorkRequest.Builder(
|
val workRequest = PeriodicWorkRequest.Builder(
|
||||||
workerClass = VacuumDatabaseWorker::class,
|
workerClass = VacuumDatabaseWorker::class,
|
||||||
@@ -41,6 +41,6 @@ class PerformDatabaseVacuumWorkManagerRequest(
|
|||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return Result.success(listOf(workRequest))
|
return Result.success(listOf(WorkManagerRequestWrapper(workRequest)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
|
|||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||||
import io.element.android.libraries.workmanager.api.WorkManagerRequest
|
import io.element.android.libraries.workmanager.api.WorkManagerRequestBuilder
|
||||||
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
|
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||||
@@ -33,7 +33,7 @@ import java.io.File
|
|||||||
class RustMatrixClientFactoryTest {
|
class RustMatrixClientFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
fun test() = runTest {
|
fun test() = runTest {
|
||||||
val scheduleVacuumLambda = lambdaRecorder<WorkManagerRequest, Unit> {}
|
val scheduleVacuumLambda = lambdaRecorder<WorkManagerRequestBuilder, Unit> {}
|
||||||
val workManagerScheduler = FakeWorkManagerScheduler(submitLambda = scheduleVacuumLambda)
|
val workManagerScheduler = FakeWorkManagerScheduler(submitLambda = scheduleVacuumLambda)
|
||||||
val sut = createRustMatrixClientFactory(workManagerScheduler = workManagerScheduler)
|
val sut = createRustMatrixClientFactory(workManagerScheduler = workManagerScheduler)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import org.matrix.rustcomponents.sdk.TaskHandle
|
|||||||
import org.matrix.rustcomponents.sdk.Timeline
|
import org.matrix.rustcomponents.sdk.Timeline
|
||||||
import org.matrix.rustcomponents.sdk.TimelineDiff
|
import org.matrix.rustcomponents.sdk.TimelineDiff
|
||||||
import org.matrix.rustcomponents.sdk.TimelineListener
|
import org.matrix.rustcomponents.sdk.TimelineListener
|
||||||
import uniffi.matrix_sdk.RoomPaginationStatus
|
import uniffi.matrix_sdk.PaginationStatus
|
||||||
|
|
||||||
class FakeFfiTimeline : Timeline(NoHandle) {
|
class FakeFfiTimeline : Timeline(NoHandle) {
|
||||||
private var listener: TimelineListener? = null
|
private var listener: TimelineListener? = null
|
||||||
@@ -33,7 +33,7 @@ class FakeFfiTimeline : Timeline(NoHandle) {
|
|||||||
return FakeFfiTaskHandle()
|
return FakeFfiTaskHandle()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun emitPaginationStatus(status: RoomPaginationStatus) {
|
fun emitPaginationStatus(status: PaginationStatus) {
|
||||||
paginationStatusListener!!.onUpdate(status)
|
paginationStatusListener!!.onUpdate(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import kotlinx.coroutines.test.runCurrent
|
|||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.rustcomponents.sdk.TimelineDiff
|
import org.matrix.rustcomponents.sdk.TimelineDiff
|
||||||
import uniffi.matrix_sdk.RoomPaginationStatus
|
import uniffi.matrix_sdk.PaginationStatus
|
||||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||||
|
|
||||||
class RustTimelineTest {
|
class RustTimelineTest {
|
||||||
@@ -68,10 +68,10 @@ class RustTimelineTest {
|
|||||||
// Start pagination
|
// Start pagination
|
||||||
sut.paginate(Timeline.PaginationDirection.BACKWARDS)
|
sut.paginate(Timeline.PaginationDirection.BACKWARDS)
|
||||||
// Simulate SDK starting pagination
|
// Simulate SDK starting pagination
|
||||||
inner.emitPaginationStatus(RoomPaginationStatus.Paginating)
|
inner.emitPaginationStatus(PaginationStatus.Paginating)
|
||||||
// No new events received
|
// No new events received
|
||||||
// Simulate SDK stopping pagination, more event to load
|
// Simulate SDK stopping pagination, more event to load
|
||||||
inner.emitPaginationStatus(RoomPaginationStatus.Idle(hitTimelineStart = false))
|
inner.emitPaginationStatus(PaginationStatus.Idle(hitTimelineStart = false))
|
||||||
// expect an item to be emitted, with an updated timestamp
|
// expect an item to be emitted, with an updated timestamp
|
||||||
with(awaitItem()) {
|
with(awaitItem()) {
|
||||||
assertThat(size).isEqualTo(2)
|
assertThat(size).isEqualTo(2)
|
||||||
|
|||||||
@@ -20,10 +20,14 @@ class FakeRoomListService(
|
|||||||
private val subscribeToVisibleRoomsLambda: (List<RoomId>) -> Unit = {},
|
private val subscribeToVisibleRoomsLambda: (List<RoomId>) -> Unit = {},
|
||||||
private val createRoomListLambda: (pageSize: Int) -> DynamicRoomList = { pageSize -> FakeDynamicRoomList(pageSize = pageSize) },
|
private val createRoomListLambda: (pageSize: Int) -> DynamicRoomList = { pageSize -> FakeDynamicRoomList(pageSize = pageSize) },
|
||||||
override val allRooms: RoomList = createRoomListLambda(Int.MAX_VALUE),
|
override val allRooms: RoomList = createRoomListLambda(Int.MAX_VALUE),
|
||||||
|
private val isInitialSyncLambda: () -> Boolean = { true },
|
||||||
) : RoomListService {
|
) : RoomListService {
|
||||||
private val roomListStateFlow = MutableStateFlow<RoomListService.State>(RoomListService.State.Idle)
|
private val roomListStateFlow = MutableStateFlow<RoomListService.State>(RoomListService.State.Idle)
|
||||||
private val syncIndicatorStateFlow = MutableStateFlow<RoomListService.SyncIndicator>(RoomListService.SyncIndicator.Hide)
|
private val syncIndicatorStateFlow = MutableStateFlow<RoomListService.SyncIndicator>(RoomListService.SyncIndicator.Hide)
|
||||||
|
|
||||||
|
override val isInitialSyncDone: Boolean
|
||||||
|
get() = isInitialSyncLambda()
|
||||||
|
|
||||||
suspend fun postState(state: RoomListService.State) {
|
suspend fun postState(state: RoomListService.State) {
|
||||||
roomListStateFlow.emit(state)
|
roomListStateFlow.emit(state)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,15 +27,15 @@ internal class CoilMediaFetcher(
|
|||||||
private val mediaData: MediaRequestData,
|
private val mediaData: MediaRequestData,
|
||||||
) : Fetcher {
|
) : Fetcher {
|
||||||
override suspend fun fetch(): FetchResult? {
|
override suspend fun fetch(): FetchResult? {
|
||||||
val source = mediaData.source
|
val mediaSource = mediaData.source
|
||||||
if (source == null) {
|
if (mediaSource == null) {
|
||||||
Timber.e("MediaData source is null")
|
Timber.e("MediaData source is null")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return when (val kind = mediaData.kind) {
|
return when (val kind = mediaData.kind) {
|
||||||
is MediaRequestData.Kind.Content -> fetchContent(source)
|
is MediaRequestData.Kind.Content -> fetchContent(mediaSource)
|
||||||
is MediaRequestData.Kind.Thumbnail -> fetchThumbnail(source, kind)
|
is MediaRequestData.Kind.Thumbnail -> fetchThumbnail(mediaSource, kind)
|
||||||
is MediaRequestData.Kind.File -> fetchFile(source, kind)
|
is MediaRequestData.Kind.File -> fetchFile(mediaSource, kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,5 +25,5 @@ internal class MediaRequestDataKeyer : Keyer<MediaRequestData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun MediaRequestData.toKey(): String? {
|
private fun MediaRequestData.toKey(): String? {
|
||||||
return source?.let { "${it.url}_$kind" }
|
return source?.let { "${it.safeUrl}_$kind" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class MediaViewerDataSource(
|
|||||||
when (mediaItem) {
|
when (mediaItem) {
|
||||||
is MediaItem.DateSeparator -> Unit
|
is MediaItem.DateSeparator -> Unit
|
||||||
is MediaItem.Event -> {
|
is MediaItem.Event -> {
|
||||||
val sourceUrl = mediaItem.mediaSource().url
|
val sourceUrl = mediaItem.mediaSource().safeUrl
|
||||||
val localMedia = localMediaStates.getOrPut(sourceUrl) {
|
val localMedia = localMediaStates.getOrPut(sourceUrl) {
|
||||||
mutableStateOf(AsyncData.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ class MediaViewerDataSource(
|
|||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
|
|
||||||
fun clearLoadingError(data: MediaViewerPageData.MediaViewerData) {
|
fun clearLoadingError(data: MediaViewerPageData.MediaViewerData) {
|
||||||
localMediaStates[data.mediaSource.url]?.value = AsyncData.Uninitialized
|
localMediaStates[data.mediaSource.safeUrl]?.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadMore(direction: Timeline.PaginationDirection) {
|
suspend fun loadMore(direction: Timeline.PaginationDirection) {
|
||||||
@@ -162,7 +162,7 @@ class MediaViewerDataSource(
|
|||||||
|
|
||||||
suspend fun loadMedia(data: MediaViewerPageData.MediaViewerData) {
|
suspend fun loadMedia(data: MediaViewerPageData.MediaViewerData) {
|
||||||
Timber.d("loadMedia for ${data.eventId}")
|
Timber.d("loadMedia for ${data.eventId}")
|
||||||
val localMediaState = localMediaStates.getOrPut(data.mediaSource.url) {
|
val localMediaState = localMediaStates.getOrPut(data.mediaSource.safeUrl) {
|
||||||
mutableStateOf(AsyncData.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
localMediaState.value = AsyncData.Loading()
|
localMediaState.value = AsyncData.Loading()
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ package io.element.android.libraries.permissions.impl.troubleshoot
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.ContributesIntoSet
|
import dev.zacsweers.metro.ContributesIntoSet
|
||||||
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.permissions.api.PermissionStateProvider
|
import io.element.android.libraries.permissions.api.PermissionStateProvider
|
||||||
import io.element.android.libraries.permissions.impl.R
|
import io.element.android.libraries.permissions.impl.R
|
||||||
import io.element.android.libraries.permissions.impl.action.PermissionActions
|
import io.element.android.libraries.permissions.impl.action.PermissionActions
|
||||||
@@ -24,7 +24,7 @@ import io.element.android.services.toolbox.api.strings.StringProvider
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
@ContributesIntoSet(AppScope::class)
|
@ContributesIntoSet(SessionScope::class)
|
||||||
class NotificationTroubleshootCheckPermissionTest(
|
class NotificationTroubleshootCheckPermissionTest(
|
||||||
private val permissionStateProvider: PermissionStateProvider,
|
private val permissionStateProvider: PermissionStateProvider,
|
||||||
private val sdkVersionProvider: BuildVersionSdkIntProvider,
|
private val sdkVersionProvider: BuildVersionSdkIntProvider,
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
|
||||||
* Copyright 2025 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.libraries.push.api.push
|
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
|
||||||
|
|
||||||
data class NotificationEventRequest(
|
|
||||||
val sessionId: SessionId,
|
|
||||||
val roomId: RoomId,
|
|
||||||
val eventId: EventId,
|
|
||||||
val providerInfo: String,
|
|
||||||
)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
|
||||||
* Copyright 2025 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.libraries.push.api.push
|
|
||||||
|
|
||||||
fun interface SyncOnNotifiableEvent {
|
|
||||||
suspend operator fun invoke(requests: List<NotificationEventRequest>)
|
|
||||||
}
|
|
||||||
@@ -97,6 +97,7 @@ sqldelight {
|
|||||||
databases {
|
databases {
|
||||||
create("PushDatabase") {
|
create("PushDatabase") {
|
||||||
schemaOutputDirectory = File("src/main/sqldelight/databases")
|
schemaOutputDirectory = File("src/main/sqldelight/databases")
|
||||||
|
verifyMigrations = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user