Merge branch 'develop' into feature/bma/detekt_end_period

This commit is contained in:
Benoit Marty 2022-05-16 13:14:37 +02:00 committed by GitHub
commit 12eb23b198
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 187 additions and 109 deletions

View File

@ -325,5 +325,5 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
hookshot_url: ${{ secrets.ELEMENT_ANDROID_HOOKSHOT_URL }}
text_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
html_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion}} {{name}} <font color='{{color conclusion}}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"
text_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
html_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion}} {{name}} <font color='{{color conclusion}}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"

View File

@ -14,6 +14,16 @@ jobs:
- name: Run code quality check suite
run: ./tools/check/check_code_quality.sh
# Knit for all the modules (https://github.com/Kotlin/kotlinx-knit)
knit:
name: Knit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run knit
run: |
./gradlew knit
# ktlint for all the modules
ktlint:
name: Kotlin Linter

View File

@ -30,6 +30,7 @@ buildscript {
classpath "com.likethesalad.android:stem-plugin:2.0.0"
classpath 'org.owasp:dependency-check-gradle:7.1.0.1'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.21"
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

1
changelog.d/5887.sdk Normal file
View File

@ -0,0 +1 @@
Small change in the Matrix class: deprecated methods have been removed and the constructor is now public. Also the fun `workerFactory()` has been renamed to `getWorkerFactory()`

1
changelog.d/5973.doc Normal file
View File

@ -0,0 +1 @@
Note public_baseurl requirement in integration tests documentation.

1
changelog.d/6047.feature Normal file
View File

@ -0,0 +1 @@
Add presence indicator busy and away.

View File

@ -37,9 +37,9 @@ Wording: "We've sent you an email to verify your address. Please follow the inst
}
```
## User receive an e-mail
## User receives an e-mail
> [homeserver.org] Validate your email
> `homeserver.org` Validate your email
>
> A request to add an email address to your Matrix account has been received. If this was you, please click the link below to confirm adding this email:
https://homeserver.org/_matrix/client/unstable/add_threepid/email/submit_token?token=WUnEhQAmJrXupdEbXgdWvnVIKaGYZFsU&client_secret=TixzvOnw7nLEUdiQEmkHzkXKrY4HhiGh&sid=bxyDHuJKsdkjMlTJ

View File

@ -43,14 +43,17 @@ virtualenv -p python3 env
source env/bin/activate
pip install -e .
demo/start.sh --no-rate-limit
```
Alternatively, to install the latest Synapse release package (and not a cloned branch) you can run the following instead of `pip install -e .`:
Alternatively, to install the latest Synapse release package (and not a cloned branch) you can run the following instead of `git clone` and `pip install -e .`:
```bash
pip install matrix-synapse
```
On your first run, you will want to stop the demo and edit the config to correct the `public_baseurl` to http://10.0.2.2:8080 and restart the server.
You should now have 3 running federated Synapse instances 🎉, at http://127.0.0.1:8080/, http://127.0.0.1:8081/ and http://127.0.0.1:8082/, which should display a "It Works! Synapse is running" message.
## Run the test
@ -87,6 +90,18 @@ You'll need python3 to be able to run synapse
Try on the Emulator browser to open "http://10.0.2.2:8080". You should see the "Synapse is running" message.
### Tests partially run but some fail with "Unable to contact localhost:8080"
This is because the `public_baseurl` of synapse is not consistent with the endpoint that the tests are connecting to.
Ensure you have the following configuration in `demo/etc/8080.config`.
```
public_baseurl: http://10.0.2.2:8080/
```
After changing this you will need to restart synapse using `demo/stop.sh` and `demo/start.sh` to load the new configuration.
### virtualenv command fails
You can try using

View File

@ -116,7 +116,7 @@ Review such PR is the same recipe than for PR from Dependabot
##### Sync analytics plan
This tools imports any update in the analytics plan. See instruction in the PR itself to handle it.
More info can be found in the file [analytics.md]
More info can be found in the file [analytics.md](./analytics.md)
## Reviewing PR

View File

@ -126,6 +126,14 @@
<color name="vctr_presence_indicator_online_light">@color/palette_element_green</color>
<color name="vctr_presence_indicator_online_dark">@color/palette_element_green</color>
<attr name="vctr_presence_indicator_busy" format="color" />
<color name="vctr_presence_indicator_busy_light">@color/element_alert_light</color>
<color name="vctr_presence_indicator_busy_dark">@color/element_alert_dark</color>
<attr name="vctr_presence_indicator_away" format="color" />
<color name="vctr_presence_indicator_away_light">@color/palette_element_orange</color>
<color name="vctr_presence_indicator_away_dark">@color/palette_element_orange</color>
<!-- Location sharing colors -->
<attr name="vctr_live_location" format="color" />
<color name="vctr_live_location_light">@color/palette_prune</color>

View File

@ -17,6 +17,7 @@
<color name="palette_melon">#FF812D</color>
<color name="palette_element_green">#0DBD8B</color>
<color name="palette_element_orange">#D9B072</color>
<color name="palette_white">#FFFFFF</color>
<color name="palette_vermilion">#FF5B55</color>
<!-- (unused) -->

View File

@ -44,6 +44,8 @@
<!-- Presence Indicator colors -->
<item name="vctr_presence_indicator_offline">@color/vctr_presence_indicator_offline_dark</item>
<item name="vctr_presence_indicator_online">@color/vctr_presence_indicator_online_dark</item>
<item name="vctr_presence_indicator_busy">@color/vctr_presence_indicator_busy_dark</item>
<item name="vctr_presence_indicator_away">@color/vctr_presence_indicator_away_dark</item>
<!-- Some aliases -->
<item name="vctr_header_background">?vctr_system</item>

View File

@ -44,6 +44,8 @@
<!-- Presence Indicator colors -->
<item name="vctr_presence_indicator_offline">@color/vctr_presence_indicator_offline_light</item>
<item name="vctr_presence_indicator_online">@color/vctr_presence_indicator_online_light</item>
<item name="vctr_presence_indicator_busy">@color/vctr_presence_indicator_busy_light</item>
<item name="vctr_presence_indicator_away">@color/vctr_presence_indicator_away_light</item>
<!-- Some aliases -->
<item name="vctr_header_background">?vctr_system</item>

View File

@ -21,7 +21,7 @@ dokkaHtml {
dokkaSourceSets {
configureEach {
// Emit warnings about not documented members.
reportUndocumented.set(true)
// reportUndocumented.set(true)
// Suppress legacy Riot's packages.
perPackageOption {
matchingRegex.set("org.matrix.android.sdk.internal.legacy.riot")

View File

@ -11,11 +11,11 @@ This pages list the complete API that this SDK is exposing to a client applicati
A few entry points:
- **Matrix**: The app will have to create and manage a Matrix object.
- From this **Matrix** object, you will be able to get various services, including the **AuthenticationService**.
- With this **AuthenticationService** you will be able to get an existing **Session**, or create one using a **LoginWizard** or a **RegistrationWizard**, which will finally give you a **Session**.
- From the **Session**, you will be able to retrieve many Services, including the **RoomService**.
- From the **RoomService**, you will be able to list the rooms, create a **Room**, and get a specific **Room**.
- And from a **Room**, you will be able to do many things, including get a **Timeline**, send messages, etc.
- **[Matrix](org.matrix.android.sdk.api.Matrix)**: The app will have to create and manage a **[Matrix](org.matrix.android.sdk.api.Matrix)** object.
- From this **[Matrix](org.matrix.android.sdk.api.Matrix)** object, you will be able to get various services, including the **[AuthenticationService](org.matrix.android.sdk.api.auth.AuthenticationService)**.
- With this **[AuthenticationService](org.matrix.android.sdk.api.auth.AuthenticationService)** you will be able to get an existing **[Session](org.matrix.android.sdk.api.session.Session)**, or create one using a **[LoginWizard](org.matrix.android.sdk.api.auth.login.LoginWizard)** or a **[RegistrationWizard](org.matrix.android.sdk.api.auth.registration.RegistrationWizard)**, which will finally give you a **[Session](org.matrix.android.sdk.api.session.Session)**.
- From the **[Session](org.matrix.android.sdk.api.session.Session)**, you will be able to retrieve many Services, including the **[RoomService](org.matrix.android.sdk.api.session.room.RoomService)**.
- From the **[RoomService](org.matrix.android.sdk.api.session.room.RoomService)**, you will be able to list the rooms, create a **[Room](org.matrix.android.sdk.api.session.room.Room)**, and get a specific **[Room](org.matrix.android.sdk.api.session.room.Room)**.
- And from a **[Room](org.matrix.android.sdk.api.session.room.Room)**, you will be able to do many things, including get a **[Timeline](org.matrix.android.sdk.api.session.room.timeline.Timeline)**, send messages, etc.
Please read the whole documentation to learn more!

View File

@ -63,8 +63,9 @@ class CommonTestHelper(context: Context) {
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
init {
var _matrix: TestMatrix? = null
UiThreadStatement.runOnUiThread {
TestMatrix.initialize(
_matrix = TestMatrix(
context,
MatrixConfiguration(
applicationFlavor = "TestFlavor",
@ -72,7 +73,7 @@ class CommonTestHelper(context: Context) {
)
)
}
matrix = TestMatrix.getInstance()
matrix = _matrix!!
}
fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {

View File

@ -38,13 +38,12 @@ import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
/**
* This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent.
*/
internal class TestMatrix constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
internal class TestMatrix(context: Context, matrixConfiguration: MatrixConfiguration) {
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService
@ -60,13 +59,14 @@ internal class TestMatrix constructor(context: Context, matrixConfiguration: Mat
private val uiHandler = Handler(Looper.getMainLooper())
init {
Monarchy.init(context)
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
val appContext = context.applicationContext
Monarchy.init(appContext)
DaggerTestMatrixComponent.factory().create(appContext, matrixConfiguration).inject(this)
val configuration = Configuration.Builder()
.setExecutor(Executors.newCachedThreadPool())
.setWorkerFactory(matrixWorkerFactory)
.build()
WorkManager.initialize(context, configuration)
WorkManager.initialize(appContext, configuration)
uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
@ -95,23 +95,6 @@ internal class TestMatrix constructor(context: Context, matrixConfiguration: Mat
}
companion object {
private lateinit var instance: TestMatrix
private val isInit = AtomicBoolean(false)
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
if (isInit.compareAndSet(false, true)) {
instance = TestMatrix(context.applicationContext, matrixConfiguration)
}
}
fun getInstance(): TestMatrix {
if (isInit.compareAndSet(false, false)) {
throw IllegalStateException("Matrix is not initialized properly. You should call TestMatrix.initialize first")
}
return instance
}
fun getSdkVersion(): String {
return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")"
}

View File

@ -38,15 +38,18 @@ import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
/**
* This is the main entry point to the matrix sdk.
* <br/>
* See [Companion.createInstance] to create an instance. The app should create and manage the instance itself.
*
* The constructor creates a new instance of Matrix, it's recommended to manage this instance as a singleton.
*
* @param context the application context
* @param matrixConfiguration global configuration that will be used for every [org.matrix.android.sdk.api.session.Session]
*/
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService
@ -61,89 +64,72 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var lightweightSettingsStorage: LightweightSettingsStorage
init {
Monarchy.init(context)
DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) {
val appContext = context.applicationContext
Monarchy.init(appContext)
DaggerMatrixComponent.factory().create(appContext, matrixConfiguration).inject(this)
if (appContext !is Configuration.Provider) {
val configuration = Configuration.Builder()
.setExecutor(Executors.newCachedThreadPool())
.setWorkerFactory(matrixWorkerFactory)
.build()
WorkManager.initialize(context, configuration)
WorkManager.initialize(appContext, configuration)
}
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
/**
* Return the User Agent used for any request that the SDK is making to the homeserver.
* There is no way to change the user agent at the moment.
*/
fun getUserAgent() = userAgentHolder.userAgent
/**
* Return the AuthenticationService.
*/
fun authenticationService() = authenticationService
/**
* Return the RawService.
*/
fun rawService() = rawService
/**
* Return the LightweightSettingsStorage.
*/
fun lightweightSettingsStorage() = lightweightSettingsStorage
/**
* Return the HomeServerHistoryService.
*/
fun homeServerHistoryService() = homeServerHistoryService
/**
* Return the legacy session importer, useful if you want to migrate an app, which was using the legacy Matrix Android Sdk.
*/
fun legacySessionImporter() = legacySessionImporter
fun workerFactory(): WorkerFactory = matrixWorkerFactory
/**
* Get the worker factory. The returned value has to be provided to `WorkConfiguration.Builder()`.
*/
fun getWorkerFactory(): WorkerFactory = matrixWorkerFactory
/**
* Register an API interceptor, to be able to be notified when the specified API got a response.
*/
fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) {
apiInterceptor.addListener(path, listener)
}
/**
* Un-register an API interceptor.
*/
fun unregisterApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) {
apiInterceptor.removeListener(path, listener)
}
companion object {
private lateinit var instance: Matrix
private val isInit = AtomicBoolean(false)
/**
* Creates a new instance of Matrix, it's recommended to manage this instance as a singleton.
* To make use of the built in singleton use Matrix.initialize() and/or Matrix.getInstance(context) instead
**/
fun createInstance(context: Context, matrixConfiguration: MatrixConfiguration): Matrix {
return Matrix(context.applicationContext, matrixConfiguration)
}
/**
* Initializes a singleton instance of Matrix for the given MatrixConfiguration.
* This instance will be returned by Matrix.getInstance(context).
*/
@Deprecated("Use Matrix.createInstance and manage the instance manually")
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
if (isInit.compareAndSet(false, true)) {
instance = Matrix(context.applicationContext, matrixConfiguration)
}
}
/**
* Either provides an already initialized singleton Matrix instance or queries the application context for a MatrixConfiguration.Provider
* to lazily create and store the instance.
*/
@Suppress("deprecation") // suppressing warning as this method is unused but is still provided for SDK clients
@Deprecated("Use Matrix.createInstance and manage the instance manually")
fun getInstance(context: Context): Matrix {
if (isInit.compareAndSet(false, true)) {
val appContext = context.applicationContext
if (appContext is MatrixConfiguration.Provider) {
val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
instance = Matrix(appContext, matrixConfiguration)
} else {
throw IllegalStateException(
"Matrix is not initialized properly." +
" If you want to manage your own Matrix instance use Matrix.createInstance" +
" otherwise you should call Matrix.initialize or let your application implement MatrixConfiguration.Provider."
)
}
}
return instance
}
/**
* @return a String with details about the Matrix SDK version
* @return a String with details about the Matrix SDK version.
*/
fun getSdkVersion(): String {
return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")"

View File

@ -65,13 +65,4 @@ data class MatrixConfiguration(
* Thread messages default enable/disabled value.
*/
val threadMessagesEnabledDefault: Boolean = false,
) {
/**
* Can be implemented by your Application class.
*/
@Deprecated("Use Matrix.createInstance and manage the instance manually instead of Matrix.getInstance")
interface Provider {
fun providesMatrixConfiguration(): MatrixConfiguration
}
}
)

View File

@ -28,7 +28,10 @@ enum class PresenceEnum(val value: String) {
OFFLINE("offline"),
@Json(name = "unavailable")
UNAVAILABLE("unavailable");
UNAVAILABLE("unavailable"),
@Json(name = "org.matrix.msc3026.busy")
BUSY("busy");
companion object {
fun from(s: String): PresenceEnum? = values().find { it.value == s }

View File

@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.presence.model.PresenceEnum
internal enum class SyncPresence(val value: String) {
Offline("offline"),
Online("online"),
Busy("busy"),
Unavailable("unavailable");
companion object {
@ -36,6 +37,7 @@ internal enum class SyncPresence(val value: String) {
return when (presenceEnum) {
PresenceEnum.ONLINE -> Online
PresenceEnum.OFFLINE -> Offline
PresenceEnum.BUSY -> Busy
PresenceEnum.UNAVAILABLE -> Unavailable
}
}

View File

@ -7,11 +7,24 @@ apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.likethesalad.stem'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'kotlinx-knit'
kapt {
correctErrorTypes = true
}
knit {
files = fileTree(project.rootDir) {
include '**/*.md'
include '**/*.kt'
include '**/*.kts'
exclude '**/build/**'
exclude '**/.gradle/**'
exclude '**/towncrier/template.md'
exclude '**/CHANGES.md'
}
}
// Note: 2 digits max for each value
ext.versionMajor = 1
ext.versionMinor = 4

View File

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- Ensure this file does not contain unknown Ids -->
<issue id="UnknownIssueId" severity="error" />
<!-- Modify some severity -->
<!-- Resource -->

View File

@ -26,5 +26,5 @@ fun getMatrixInstance(): Matrix {
val configuration = MatrixConfiguration(
roomDisplayNameFallbackProvider = VectorRoomDisplayNameFallbackProvider(context)
)
return Matrix.createInstance(context, configuration)
return Matrix(context, configuration)
}

View File

@ -224,7 +224,7 @@ class VectorApplication :
override fun getWorkManagerConfiguration(): WorkConfiguration {
return WorkConfiguration.Builder()
.setWorkerFactory(matrix.workerFactory())
.setWorkerFactory(matrix.getWorkerFactory())
.setExecutor(Executors.newCachedThreadPool())
.build()
}

View File

@ -129,7 +129,7 @@ object VectorStaticModule {
@Provides
@Singleton
fun providesMatrix(context: Context, configuration: MatrixConfiguration): Matrix {
return Matrix.createInstance(context, configuration)
return Matrix(context, configuration)
}
@Provides

View File

@ -42,13 +42,17 @@ class PresenceStateImageView @JvmOverloads constructor(
contentDescription = context.getString(R.string.a11y_presence_online)
}
PresenceEnum.UNAVAILABLE -> {
setImageResource(R.drawable.ic_presence_offline)
setImageResource(R.drawable.ic_presence_away)
contentDescription = context.getString(R.string.a11y_presence_unavailable)
}
PresenceEnum.OFFLINE -> {
setImageResource(R.drawable.ic_presence_offline)
contentDescription = context.getString(R.string.a11y_presence_offline)
}
PresenceEnum.BUSY -> {
setImageResource(R.drawable.ic_presence_busy)
contentDescription = context.getString(R.string.a11y_presence_busy)
}
null -> Unit
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="11.89dp"
android:height="12dp"
android:viewportWidth="11.89"
android:viewportHeight="12"
>
<group>
<clip-path
android:pathData="M11.8857 6C11.8857 9.31371 9.225 12 5.94286 12C2.66071 12 0 9.31371 0 6C0 2.68629 2.66071 0 5.94286 0C9.225 0 11.8857 2.68629 11.8857 6Z"
/>
<path
android:pathData="M0 0V12H11.8857V0"
android:fillColor="?vctr_presence_indicator_away"
/>
</group>
</vector>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="11.89dp"
android:height="12dp"
android:viewportWidth="11.89"
android:viewportHeight="12"
>
<group>
<clip-path
android:pathData="M11.8857 6C11.8857 9.31371 9.225 12 5.94286 12C2.66071 12 0 9.31371 0 6C0 2.68629 2.66071 0 5.94286 0C9.225 0 11.8857 2.68629 11.8857 6Z"
/>
<path
android:pathData="M0 0V12H11.8857V0"
android:fillColor="?vctr_presence_indicator_busy"
/>
</group>
</vector>

View File

@ -2730,7 +2730,8 @@
<string name="a11y_public_room">Public room</string>
<string name="a11y_presence_online">Online</string>
<string name="a11y_presence_offline">Offline</string>
<string name="a11y_presence_unavailable">Unavailable</string>
<string name="a11y_presence_busy">Busy</string>
<string name="a11y_presence_unavailable">Away</string>
<string name="dev_tools_menu_name">Dev Tools</string>
<string name="dev_tools_explore_room_state">Explore Room State</string>