From 2324bf5d054ec96e8d49c6e8927590483619c9e4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 30 Sep 2021 19:52:37 +0200 Subject: [PATCH 001/144] Mavericks 2: include the libs and make it compile --- build.gradle | 2 +- dependencies.gradle | 4 +++- vector/build.gradle | 7 +++++-- .../main/java/im/vector/app/VectorApplication.kt | 3 ++- .../im/vector/app/core/extensions/Parcelable.kt | 3 ++- .../vector/app/core/platform/VectorBaseActivity.kt | 5 ++++- .../platform/VectorBaseBottomSheetDialogFragment.kt | 13 ------------- .../im/vector/app/core/platform/VectorViewModel.kt | 2 +- .../vector/app/features/call/VectorCallActivity.kt | 4 ++-- .../createdirect/CreateDirectRoomActivity.kt | 2 +- .../keysbackup/settings/KeysBackupManageActivity.kt | 2 +- .../vector/app/features/home/HomeDetailFragment.kt | 6 +++--- .../room/detail/JoinReplacementRoomBottomSheet.kt | 2 +- .../room/detail/widget/RoomWidgetsBottomSheet.kt | 2 +- .../app/features/home/room/list/RoomListFragment.kt | 2 +- .../vector/app/features/login/LoginWebFragment.kt | 3 ++- .../vector/app/features/login2/LoginWebFragment2.kt | 3 ++- .../settings/joinrule/RoomJoinRuleActivity.kt | 2 +- .../features/signout/soft/SoftLogoutViewModel.kt | 1 - .../spaces/explore/SpaceDirectoryFragment.kt | 2 +- .../features/spaces/manage/SpaceAddRoomFragment.kt | 10 +++++----- .../app/features/usercode/UserCodeActivity.kt | 2 +- .../app/features/userdirectory/UserListFragment.kt | 2 +- .../vector/app/features/widgets/WidgetActivity.kt | 9 ++++----- 24 files changed, 45 insertions(+), 48 deletions(-) diff --git a/build.gradle b/build.gradle index 49c3e07ece..c2ed966dae 100644 --- a/build.gradle +++ b/build.gradle @@ -73,7 +73,7 @@ allprojects { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { // Warnings are potential errors, so stop ignoring them // You can override by passing `-PallWarningsAsErrors=false` in the command line - kotlinOptions.allWarningsAsErrors = project.getProperties().getOrDefault("allWarningsAsErrors", "true").toBoolean() + kotlinOptions.allWarningsAsErrors = false //project.getProperties().getOrDefault("allWarningsAsErrors", "true").toBoolean() } } diff --git a/dependencies.gradle b/dependencies.gradle index 37486b8d91..e3e77e4cd8 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -19,6 +19,7 @@ def moshi = "1.12.0" def lifecycle = "2.2.0" def rxBinding = "3.1.0" def epoxy = "4.6.2" +def mavericks = "2.3.0" def glide = "4.12.0" def bigImageViewer = "1.8.1" def jjwt = "0.11.2" @@ -98,7 +99,8 @@ ext.libs = [ 'epoxyGlide' : "com.airbnb.android:epoxy-glide-preloading:$epoxy", 'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy", 'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy", - 'mvrx' : "com.airbnb.android:mvrx:1.5.1" + 'mavericks' : "com.airbnb.android:mavericks:$mavericks", + 'mavericksRx' : "com.airbnb.android:mavericks-rxjava2:$mavericks" ], mockk : [ 'mockk' : "io.mockk:mockk:$mockk", diff --git a/vector/build.gradle b/vector/build.gradle index f2cb2831bd..a9c6a407d8 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -376,7 +376,8 @@ dependencies { implementation libs.airbnb.epoxyGlide kapt libs.airbnb.epoxyProcessor implementation libs.airbnb.epoxyPaging - implementation libs.airbnb.mvrx + implementation libs.airbnb.mavericks + implementation libs.airbnb.mavericksRx // Work implementation libs.androidx.work @@ -458,7 +459,9 @@ dependencies { implementation "androidx.emoji:emoji-appcompat:1.1.0" - implementation 'com.github.BillCarsonFr:JsonViewer:0.6' + implementation ('com.github.BillCarsonFr:JsonViewer:0.6'){ + exclude group: 'com.airbnb.android' + } // WebRTC // org.webrtc:google-webrtc is for development purposes only diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index be8447d409..3e4cf3ff85 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -34,6 +34,7 @@ import androidx.lifecycle.ProcessLifecycleOwner import androidx.multidex.MultiDex import com.airbnb.epoxy.EpoxyAsyncUtil import com.airbnb.epoxy.EpoxyController +import com.airbnb.mvrx.Mavericks import com.facebook.stetho.Stetho import com.gabrielittner.threetenbp.LazyThreeTen import com.vanniktech.emoji.EmojiManager @@ -137,7 +138,7 @@ class VectorApplication : } logInfo() LazyThreeTen.init(this) - + Mavericks.initialize(this) EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler() EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler() registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager)) diff --git a/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt b/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt index 6ca0144601..87f5ac84f7 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt @@ -18,8 +18,9 @@ package im.vector.app.core.extensions import android.os.Bundle import android.os.Parcelable +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MvRx fun Parcelable?.toMvRxBundle(): Bundle? { - return this?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } } + return this?.let { Bundle().apply { putParcelable(Mavericks.KEY_ARG, it) } } } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index dc19520865..e9973e82b8 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -40,6 +40,7 @@ import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentManager import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding +import com.airbnb.mvrx.MvRxView import com.bumptech.glide.util.Util import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.snackbar.Snackbar @@ -88,7 +89,7 @@ import timber.log.Timber import java.util.concurrent.TimeUnit import kotlin.system.measureTimeMillis -abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { +abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector, MvRxView { /* ========================================================================================== * View * ========================================================================================== */ @@ -576,6 +577,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc open fun initUiAndData() = Unit + override fun invalidate() = Unit + @StringRes open fun getTitleRes() = -1 diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt index b9b5bc8ca5..9adaed288a 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -29,7 +29,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRxView -import com.airbnb.mvrx.MvRxViewId import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment @@ -48,8 +47,6 @@ import java.util.concurrent.TimeUnit */ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment(), MvRxView { - private val mvrxViewIdProperty = MvRxViewId() - final override val mvrxViewId: String by mvrxViewIdProperty private lateinit var screenComponent: ScreenComponent /* ========================================================================================== @@ -133,11 +130,6 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomShe protected open fun injectWith(injector: ScreenComponent) = Unit - override fun onCreate(savedInstanceState: Bundle?) { - mvrxViewIdProperty.restoreFrom(savedInstanceState) - super.onCreate(savedInstanceState) - } - override fun onResume() { super.onResume() Timber.i("onResume BottomSheet ${javaClass.simpleName}") @@ -154,11 +146,6 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomShe } } - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - mvrxViewIdProperty.saveTo(outState) - } - override fun onStart() { super.onStart() // This ensures that invalidate() is called for static screens that don't diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index d6f43beaf7..c066dacc08 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -28,7 +28,7 @@ import io.reactivex.Observable import io.reactivex.Single abstract class VectorViewModel(initialState: S) - : BaseMvRxViewModel(initialState, false) { + : BaseMvRxViewModel(initialState) { interface Factory { fun create(state: S): BaseMvRxViewModel diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index f71dcc0635..64abdef72a 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -138,7 +138,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro renderState(it) } - callViewModel.asyncSubscribe(this, VectorCallViewState::callState) { + callViewModel.asyncSubscribe(VectorCallViewState::callState) { if (it is CallState.Ended) { handleCallEnded(it) } @@ -152,7 +152,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } .disposeOnDestroy() - callViewModel.selectSubscribe(this, VectorCallViewState::callId, VectorCallViewState::isVideoCall) { _, isVideoCall -> + callViewModel.selectSubscribe(VectorCallViewState::callId, VectorCallViewState::isVideoCall) { _, isVideoCall -> if (isVideoCall) { if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, this, permissionCameraLauncher, R.string.permissions_rationale_msg_camera_and_audio)) { setupRenderersIfNeeded() diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index 68123d5e82..752a7d0d83 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -102,7 +102,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Fac ) ) } - viewModel.selectSubscribe(this, CreateDirectRoomViewState::createAndInviteState) { + viewModel.selectSubscribe(CreateDirectRoomViewState::createAndInviteState) { renderCreateAndInviteState(it) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt index d78fa37d62..c03488ab5d 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt @@ -55,7 +55,7 @@ class KeysBackupManageActivity : SimpleFragmentActivity() { } // Observe the deletion of keys backup - viewModel.selectSubscribe(this, KeysBackupSettingViewState::deleteBackupRequest) { asyncDelete -> + viewModel.selectSubscribe(KeysBackupSettingViewState::deleteBackupRequest) { asyncDelete -> when (asyncDelete) { is Fail -> { updateWaitingView(null) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 627f4b4581..b6d628071b 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -134,7 +134,7 @@ class HomeDetailFragment @Inject constructor( views.bottomNavigationView.selectedItemId = it.currentTab.toMenuId() } - viewModel.selectSubscribe(this, HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod -> + viewModel.selectSubscribe(HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod -> when (roomGroupingMethod) { is RoomGroupingMethod.ByLegacyGroup -> { onGroupChange(roomGroupingMethod.groupSummary) @@ -145,11 +145,11 @@ class HomeDetailFragment @Inject constructor( } } - viewModel.selectSubscribe(this, HomeDetailViewState::currentTab) { currentTab -> + viewModel.selectSubscribe(HomeDetailViewState::currentTab) { currentTab -> updateUIForTab(currentTab) } - viewModel.selectSubscribe(this, HomeDetailViewState::showDialPadTab) { showDialPadTab -> + viewModel.selectSubscribe(HomeDetailViewState::showDialPadTab) { showDialPadTab -> updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt index 54681366e0..9cfca9ecd2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt @@ -61,7 +61,7 @@ class JoinReplacementRoomBottomSheet : } } - viewModel.selectSubscribe(this, RoomDetailViewState::joinUpgradedRoomAsync) { joinState -> + viewModel.selectSubscribe(RoomDetailViewState::joinUpgradedRoomAsync) { joinState -> when (joinState) { // it should never be Uninitialized Uninitialized, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt index 4e77873522..3b305abbe4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt @@ -65,7 +65,7 @@ class RoomWidgetsBottomSheet : views.bottomSheetTitle.textSize = 20f views.bottomSheetTitle.setTextColor(colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) epoxyController.listener = this - roomDetailViewModel.asyncSubscribe(this, RoomDetailViewState::activeRoomWidgets) { + roomDetailViewModel.asyncSubscribe(RoomDetailViewState::activeRoomWidgets) { epoxyController.setData(it) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index fdfe171439..2df90a6c16 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -123,7 +123,7 @@ class RoomListFragment @Inject constructor( .subscribe { handleQuickActions(it) } .disposeOnDestroyView() - roomListViewModel.selectSubscribe(viewLifecycleOwner, RoomListViewState::roomMembershipChanges) { ms -> + roomListViewModel.selectSubscribe(RoomListViewState::roomMembershipChanges) { ms -> // it's for invites local echo adapterInfosList.filter { it.section.notifyOfLocalEcho } .onEach { diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt index f01bf01029..67bc1ddd1c 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt @@ -52,6 +52,8 @@ class LoginWebFragment @Inject constructor( private val assetReader: AssetReader ) : AbstractLoginFragment() { + val softLogoutViewModel: SoftLogoutViewModel by activityViewModel() + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginWebBinding { return FragmentLoginWebBinding.inflate(inflater, container, false) } @@ -233,7 +235,6 @@ class LoginWebFragment @Inject constructor( private fun notifyViewModel(credentials: Credentials) { if (isForSessionRecovery) { - val softLogoutViewModel: SoftLogoutViewModel by activityViewModel() softLogoutViewModel.handle(SoftLogoutAction.WebLoginSuccess(credentials)) } else { loginViewModel.handle(LoginAction.WebLoginSuccess(credentials)) diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt index b1e3eaf098..7ea938e310 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt @@ -57,6 +57,8 @@ class LoginWebFragment2 @Inject constructor( return FragmentLoginWebBinding.inflate(inflater, container, false) } + val softLogoutViewModel: SoftLogoutViewModel by activityViewModel() + private var isWebViewLoaded = false private var isForSessionRecovery = false @@ -234,7 +236,6 @@ class LoginWebFragment2 @Inject constructor( private fun notifyViewModel(credentials: Credentials) { if (isForSessionRecovery) { - val softLogoutViewModel: SoftLogoutViewModel by activityViewModel() softLogoutViewModel.handle(SoftLogoutAction.WebLoginSuccess(credentials)) } else { loginViewModel.handle(LoginAction2.WebLoginSuccess(credentials)) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt index 21c39ad49d..dc0d06ac86 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt @@ -78,7 +78,7 @@ class RoomJoinRuleActivity : VectorBaseActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewModel.selectSubscribe(this, RoomJoinRuleChooseRestrictedState::updatingStatus) { + viewModel.selectSubscribe(RoomJoinRuleChooseRestrictedState::updatingStatus) { when (it) { Uninitialized -> { // nop diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt index f49527bd1d..f17847fdfd 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.signout.soft -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt index 3af66cbb6b..b03d1e0b14 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt @@ -110,7 +110,7 @@ class SpaceDirectoryFragment @Inject constructor( views.spaceDirectoryList.configureWith(epoxyController) epoxyVisibilityTracker.attach(views.spaceDirectoryList) - viewModel.selectSubscribe(this, SpaceDirectoryState::canAddRooms) { + viewModel.selectSubscribe(SpaceDirectoryState::canAddRooms) { invalidateOptionsMenu() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt index e192ec3c88..cea4d0059e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt @@ -91,17 +91,17 @@ class SpaceAddRoomFragment @Inject constructor( invalidateOptionsMenu() } - viewModel.selectSubscribe(this, SpaceAddRoomsState::spaceName) { + viewModel.selectSubscribe(SpaceAddRoomsState::spaceName) { views.appBarSpaceInfo.text = it }.disposeOnDestroyView() - viewModel.selectSubscribe(this, SpaceAddRoomsState::ignoreRooms) { + viewModel.selectSubscribe(SpaceAddRoomsState::ignoreRooms) { spaceEpoxyController.ignoreRooms = it roomEpoxyController.ignoreRooms = it dmEpoxyController.ignoreRooms = it }.disposeOnDestroyView() - viewModel.selectSubscribe(this, SpaceAddRoomsState::isSaving) { + viewModel.selectSubscribe(SpaceAddRoomsState::isSaving) { if (it is Loading) { sharedViewModel.handle(SpaceManagedSharedAction.ShowLoading) } else { @@ -109,11 +109,11 @@ class SpaceAddRoomFragment @Inject constructor( } }.disposeOnDestroyView() - viewModel.selectSubscribe(this, SpaceAddRoomsState::shouldShowDMs) { + viewModel.selectSubscribe(SpaceAddRoomsState::shouldShowDMs) { dmEpoxyController.disabled = !it }.disposeOnDestroyView() - viewModel.selectSubscribe(this, SpaceAddRoomsState::onlyShowSpaces) { + viewModel.selectSubscribe(SpaceAddRoomsState::onlyShowSpaces) { spaceEpoxyController.disabled = !it roomEpoxyController.disabled = it views.createNewRoom.text = if (it) getString(R.string.create_space) else getString(R.string.create_new_room) diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt index 0771a5d238..064905f188 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt @@ -68,7 +68,7 @@ class UserCodeActivity : VectorBaseActivity(), showFragment(ShowUserCodeFragment::class, Bundle.EMPTY) } - sharedViewModel.selectSubscribe(this, UserCodeState::mode) { mode -> + sharedViewModel.selectSubscribe(UserCodeState::mode) { mode -> when (mode) { UserCodeState.Mode.SHOW -> showFragment(ShowUserCodeFragment::class, Bundle.EMPTY) UserCodeState.Mode.SCAN -> showFragment(ScanUserCodeFragment::class, Bundle.EMPTY) diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index f251a672b8..843ead0eb4 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -85,7 +85,7 @@ class UserListFragment @Inject constructor( views.userListE2EbyDefaultDisabled.isVisible = !it.isE2EByDefault } - viewModel.selectSubscribe(this, UserListViewState::pendingSelections) { + viewModel.selectSubscribe(UserListViewState::pendingSelections) { renderSelectedUsers(it) } diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index 9072957a95..31895dda16 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -19,10 +19,10 @@ package im.vector.app.features.widgets import android.app.Activity import android.content.Context import android.content.Intent -import com.google.android.material.appbar.MaterialToolbar import androidx.core.view.isVisible import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment @@ -33,7 +33,6 @@ import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomShee import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewEvents import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState - import org.matrix.android.sdk.api.session.events.model.Content import java.io.Serializable import javax.inject.Inject @@ -103,7 +102,7 @@ class WidgetActivity : VectorBaseActivity(), } } - viewModel.selectSubscribe(this, WidgetViewState::status) { ws -> + viewModel.selectSubscribe(WidgetViewState::status) { ws -> when (ws) { WidgetStatus.UNKNOWN -> { } @@ -125,11 +124,11 @@ class WidgetActivity : VectorBaseActivity(), } } - viewModel.selectSubscribe(this, WidgetViewState::widgetName) { name -> + viewModel.selectSubscribe(WidgetViewState::widgetName) { name -> supportActionBar?.title = name } - viewModel.selectSubscribe(this, WidgetViewState::canManageWidgets) { + viewModel.selectSubscribe(WidgetViewState::canManageWidgets) { invalidateOptionsMenu() } } From f6b81b36d08584e7aeeb3db7ff151dc7873bfe1d Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 10:08:06 +0200 Subject: [PATCH 002/144] Mavericks 2: switch from MvRxState to MavericksState --- .../ElementFeature/root/src/app_package/ViewState.kt.ftl | 4 ++-- .../java/im/vector/app/core/platform/VectorViewModel.kt | 6 +++--- .../app/core/ui/bottomsheet/BottomSheetGenericState.kt | 4 ++-- .../app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt | 4 ++-- .../attachments/preview/AttachmentsPreviewViewState.kt | 4 ++-- .../main/java/im/vector/app/features/auth/ReAuthState.kt | 4 ++-- .../java/im/vector/app/features/call/VectorCallViewState.kt | 4 ++-- .../app/features/call/conference/JitsiCallViewState.kt | 4 ++-- .../app/features/call/transfer/CallTransferViewState.kt | 4 ++-- .../app/features/contactsbook/ContactsBookViewState.kt | 4 ++-- .../app/features/createdirect/CreateDirectRoomViewState.kt | 4 ++-- .../keysbackup/settings/KeysBackupSettingViewState.kt | 4 ++-- .../features/crypto/quads/SharedSecureStorageViewModel.kt | 4 ++-- .../app/features/crypto/recover/BootstrapViewState.kt | 4 ++-- .../crypto/verification/VerificationBottomSheetViewModel.kt | 4 ++-- .../choose/VerificationChooseMethodViewModel.kt | 4 ++-- .../conclusion/VerificationConclusionViewModel.kt | 4 ++-- .../verification/emoji/VerificationEmojiCodeViewModel.kt | 4 ++-- .../im/vector/app/features/devtools/RoomDevToolViewState.kt | 4 ++-- .../vector/app/features/discovery/DiscoverySettingsState.kt | 4 ++-- .../app/features/discovery/change/SetIdentityServerState.kt | 4 ++-- .../im/vector/app/features/home/HomeActivityViewState.kt | 4 ++-- .../java/im/vector/app/features/home/HomeDetailViewState.kt | 4 ++-- .../vector/app/features/home/PromoteRestrictedViewModel.kt | 4 ++-- .../features/home/UnknownDeviceDetectorSharedViewModel.kt | 4 ++-- .../app/features/home/UnreadMessagesSharedViewModel.kt | 4 ++-- .../features/home/room/breadcrumbs/BreadcrumbsViewState.kt | 4 ++-- .../app/features/home/room/detail/RoomDetailViewState.kt | 4 ++-- .../app/features/home/room/detail/search/SearchViewState.kt | 4 ++-- .../home/room/detail/timeline/action/MessageActionState.kt | 4 ++-- .../detail/timeline/edithistory/ViewEditHistoryViewState.kt | 4 ++-- .../detail/timeline/reactions/ViewReactionsViewModel.kt | 4 ++-- .../home/room/detail/upgrade/MigrateRoomViewState.kt | 4 ++-- .../vector/app/features/home/room/list/RoomListViewState.kt | 4 ++-- .../features/homeserver/HomeServerCapabilitiesViewState.kt | 4 ++-- .../app/features/invite/InviteUsersToRoomViewState.kt | 4 ++-- .../java/im/vector/app/features/login/LoginViewState.kt | 4 ++-- .../vector/app/features/login/terms/LoginTermsViewState.kt | 4 ++-- .../java/im/vector/app/features/login2/LoginViewState2.kt | 4 ++-- .../app/features/login2/created/AccountCreatedViewState.kt | 4 ++-- .../app/features/matrixto/MatrixToBottomSheetState.kt | 4 ++-- .../java/im/vector/app/features/rageshake/BugReportState.kt | 4 ++-- .../app/features/reactions/EmojiSearchResultViewModel.kt | 4 ++-- .../app/features/room/RequireActiveMembershipViewState.kt | 4 ++-- .../app/features/roomdirectory/PublicRoomsViewState.kt | 4 ++-- .../roomdirectory/createroom/CreateRoomViewState.kt | 4 ++-- .../roomdirectory/picker/RoomDirectoryPickerViewState.kt | 4 ++-- .../roomdirectory/roompreview/RoomPreviewViewState.kt | 4 ++-- .../roommemberprofile/RoomMemberProfileViewState.kt | 4 ++-- .../devices/DeviceListBottomSheetViewModel.kt | 4 ++-- .../vector/app/features/roomprofile/RoomProfileViewState.kt | 4 ++-- .../app/features/roomprofile/alias/RoomAliasViewState.kt | 4 ++-- .../roomprofile/alias/detail/RoomAliasBottomSheetState.kt | 4 ++-- .../roomprofile/banned/RoomBannedMemberListViewState.kt | 4 ++-- .../features/roomprofile/members/RoomMemberListViewState.kt | 4 ++-- .../notifications/RoomNotificationSettingsViewState.kt | 4 ++-- .../roomprofile/permissions/RoomPermissionsViewState.kt | 4 ++-- .../features/roomprofile/settings/RoomSettingsViewState.kt | 4 ++-- .../joinrule/advanced/RoomJoinRuleChooseRestrictedState.kt | 4 ++-- .../features/roomprofile/uploads/RoomUploadsViewState.kt | 4 ++-- .../account/deactivation/DeactivateAccountViewModel.kt | 4 ++-- .../settings/crosssigning/CrossSigningSettingsViewState.kt | 4 ++-- .../devices/DeviceVerificationInfoBottomSheetViewState.kt | 4 ++-- .../app/features/settings/devices/DevicesViewModel.kt | 4 ++-- .../app/features/settings/devtools/AccountDataViewModel.kt | 4 ++-- .../settings/devtools/GossipingEventsPaperTrailViewModel.kt | 4 ++-- .../features/settings/devtools/KeyRequestListViewModel.kt | 4 ++-- .../app/features/settings/devtools/KeyRequestViewModel.kt | 4 ++-- .../settings/homeserver/HomeServerSettingsViewState.kt | 4 ++-- .../app/features/settings/ignored/IgnoredUsersViewModel.kt | 4 ++-- .../app/features/settings/locale/LocalePickerViewState.kt | 4 ++-- .../app/features/settings/push/PushGatewaysViewModel.kt | 4 ++-- .../vector/app/features/settings/push/PushRulesViewModel.kt | 4 ++-- .../settings/threepids/ThreePidsSettingsViewState.kt | 4 ++-- .../im/vector/app/features/share/IncomingShareViewState.kt | 4 ++-- .../vector/app/features/signout/soft/SoftLogoutViewState.kt | 4 ++-- .../im/vector/app/features/spaces/SpaceListViewState.kt | 4 ++-- .../java/im/vector/app/features/spaces/SpaceMenuState.kt | 4 ++-- .../vector/app/features/spaces/create/CreateSpaceState.kt | 4 ++-- .../app/features/spaces/explore/SpaceDirectoryState.kt | 4 ++-- .../features/spaces/invite/SpaceInviteBottomSheetState.kt | 4 ++-- .../app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt | 4 ++-- .../vector/app/features/spaces/manage/SpaceAddRoomsState.kt | 4 ++-- .../app/features/spaces/manage/SpaceManageRoomViewState.kt | 4 ++-- .../app/features/spaces/manage/SpaceManageViewState.kt | 4 ++-- .../app/features/spaces/people/SpacePeopleViewState.kt | 4 ++-- .../vector/app/features/spaces/preview/SpacePreviewState.kt | 4 ++-- .../vector/app/features/spaces/share/ShareSpaceViewState.kt | 4 ++-- .../im/vector/app/features/terms/ReviewTermsViewState.kt | 4 ++-- .../java/im/vector/app/features/usercode/UserCodeState.kt | 4 ++-- .../vector/app/features/userdirectory/UserListViewState.kt | 4 ++-- .../java/im/vector/app/features/widgets/WidgetViewState.kt | 4 ++-- .../widgets/permissions/RoomWidgetPermissionViewState.kt | 4 ++-- .../features/workers/signout/ServerBackupStatusViewModel.kt | 4 ++-- .../app/features/workers/signout/SignoutCheckViewModel.kt | 4 ++-- vector/src/test/java/im/vector/app/test/Extensions.kt | 4 ++-- 96 files changed, 193 insertions(+), 193 deletions(-) diff --git a/tools/templates/ElementFeature/root/src/app_package/ViewState.kt.ftl b/tools/templates/ElementFeature/root/src/app_package/ViewState.kt.ftl index 55e1f5f549..0acb3f78da 100644 --- a/tools/templates/ElementFeature/root/src/app_package/ViewState.kt.ftl +++ b/tools/templates/ElementFeature/root/src/app_package/ViewState.kt.ftl @@ -1,5 +1,5 @@ package ${escapeKotlinIdentifiers(packageName)} -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState -data class ${viewStateClass}() : MvRxState \ No newline at end of file +data class ${viewStateClass}() : MavericksState \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index c066dacc08..1a77a00fab 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -20,17 +20,17 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.BaseMvRxViewModel import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Success import im.vector.app.core.utils.DataSource import im.vector.app.core.utils.PublishDataSource import io.reactivex.Observable import io.reactivex.Single -abstract class VectorViewModel(initialState: S) +abstract class VectorViewModel(initialState: S) : BaseMvRxViewModel(initialState) { - interface Factory { + interface Factory { fun create(state: S): BaseMvRxViewModel } diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt index 38c81a7ef6..0f57600b13 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt @@ -16,6 +16,6 @@ package im.vector.app.core.ui.bottomsheet -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState -abstract class BottomSheetGenericState : MvRxState +abstract class BottomSheetGenericState : MavericksState diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt index 6cc2c4c981..bde87a5588 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt @@ -16,12 +16,12 @@ package im.vector.app.core.ui.bottomsheet -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -abstract class BottomSheetGenericViewModel(initialState: State) : +abstract class BottomSheetGenericViewModel(initialState: State) : VectorViewModel(initialState) { override fun handle(action: EmptyAction) { diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewState.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewState.kt index cee2a7ddd7..8b8208fc4f 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewState.kt @@ -17,14 +17,14 @@ package im.vector.app.features.attachments.preview -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.api.session.content.ContentAttachmentData data class AttachmentsPreviewViewState( val attachments: List, val currentAttachmentIndex: Int = 0, val sendImagesWithOriginalSize: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments) } diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthState.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthState.kt index 075ef758b8..733516c691 100644 --- a/vector/src/main/java/im/vector/app/features/auth/ReAuthState.kt +++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthState.kt @@ -16,7 +16,7 @@ package im.vector.app.features.auth -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState data class ReAuthState( val title: String? = null, @@ -25,7 +25,7 @@ data class ReAuthState( val ssoFallbackPageWasShown: Boolean = false, val lastErrorCode: String? = null, val resultKeyStoreAlias: String = "" -) : MvRxState { +) : MavericksState { constructor(args: ReAuthActivity.Args) : this( args.title, args.session, diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt index a351806e1a..2d33cbf9b9 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.call import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.call.audio.CallAudioManager import org.matrix.android.sdk.api.session.call.CallState @@ -43,7 +43,7 @@ data class VectorCallViewState( val formattedDuration: String = "", val canOpponentBeTransferred: Boolean = false, val transferee: TransfereeState = TransfereeState.NoTransferee -) : MvRxState { +) : MavericksState { sealed class TransfereeState { object NoTransferee : TransfereeState() diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt index 1fd04542e0..d4c70d7333 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.call.conference import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.widgets.model.Widget @@ -26,4 +26,4 @@ data class JitsiCallViewState( val widgetId: String = "", val enableVideo: Boolean = false, val widget: Async = Uninitialized -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewState.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewState.kt index 2b29d9f6f2..babda1dd19 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewState.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewState.kt @@ -16,11 +16,11 @@ package im.vector.app.features.call.transfer -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState data class CallTransferViewState( val callId: String -) : MvRxState { +) : MavericksState { constructor(args: CallTransferArgs) : this(callId = args.callId) } diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt index d2ee684c4d..d4f279787d 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.contactsbook import com.airbnb.mvrx.Async import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import im.vector.app.core.contacts.MappedContact data class ContactsBookViewState( @@ -36,4 +36,4 @@ data class ContactsBookViewState( val identityServerUrl: String? = null, // User consent to perform lookup (send emails to the identity server) val userConsent: Boolean = false -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt index 27a8cc0a97..41366a7110 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt @@ -17,9 +17,9 @@ package im.vector.app.features.createdirect import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class CreateDirectRoomViewState( val createAndInviteState: Async = Uninitialized -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt index 874e3765ca..f5541d532e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.crypto.keysbackup.settings import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust @@ -27,4 +27,4 @@ data class KeysBackupSettingViewState(val keysBackupVersionTrust: Async = Uninitialized) - : MvRxState + : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index b4ff9ab22c..3f3b3a751a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRx -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -55,7 +55,7 @@ data class SharedSecureStorageViewState( val activeDeviceCount: Int = 0, val showResetAllAction: Boolean = false, val userId: String = "" -) : MvRxState { +) : MavericksState { enum class Step { EnterPassphrase, EnterKey, diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt index 4e94ddf0bf..b8c9f10b49 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.crypto.recover import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import com.nulabinc.zxcvbn.Strength import im.vector.app.core.platform.WaitingViewData @@ -34,4 +34,4 @@ data class BootstrapViewState( val recoveryKeyCreationInfo: SsssKeyCreationInfo? = null, val initializationWaitingViewData: WaitingViewData? = null, val recoverySaveFileProcess: Async = Uninitialized -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index 8e21412715..b2f259772e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -20,7 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -79,7 +79,7 @@ data class VerificationBottomSheetViewState( val quadSContainsSecrets: Boolean = true, val quadSHasBeenReset: Boolean = false, val hasAnyOtherSession: Boolean = false -) : MvRxState +) : MavericksState class VerificationBottomSheetViewModel @AssistedInject constructor( @Assisted initialState: VerificationBottomSheetViewState, diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt index 7b9acd2f57..79878fcf83 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.crypto.verification.choose import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -43,7 +43,7 @@ data class VerificationChooseMethodViewState( val sasModeAvailable: Boolean = false, val isMe: Boolean = false, val canCrossSign: Boolean = false -) : MvRxState +) : MavericksState class VerificationChooseMethodViewModel @AssistedInject constructor( @Assisted initialState: VerificationChooseMethodViewState, diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt index 50912af1c2..93133184e9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt @@ -15,7 +15,7 @@ */ package im.vector.app.features.crypto.verification.conclusion -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.app.core.platform.EmptyAction @@ -27,7 +27,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf data class VerificationConclusionViewState( val conclusionState: ConclusionState = ConclusionState.CANCELLED, val isSelfVerification: Boolean = false -) : MvRxState +) : MavericksState enum class ConclusionState { SUCCESS, diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt index 44f0e752c8..c8dd9b34d8 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt @@ -19,7 +19,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -48,7 +48,7 @@ data class VerificationEmojiCodeViewState( val emojiDescription: Async> = Uninitialized, val decimalDescription: Async = Uninitialized, val isWaitingFromOther: Boolean = false -) : MvRxState +) : MavericksState class VerificationEmojiCodeViewModel @AssistedInject constructor( @Assisted initialState: VerificationEmojiCodeViewState, diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewState.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewState.kt index 885de005b0..a5b7db9821 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewState.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.devtools import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.events.model.Event @@ -31,7 +31,7 @@ data class RoomDevToolViewState( val editedContent: String? = null, val modalLoading: Async = Uninitialized, val sendEventDraft: SendEventDraft? = null -) : MvRxState { +) : MavericksState { constructor(args: RoomDevToolActivity.Args) : this(roomId = args.roomId, displayMode = Mode.Root) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index 21fbcf1ca7..46c516331f 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.discovery import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class DiscoverySettingsState( @@ -27,4 +27,4 @@ data class DiscoverySettingsState( // Can be true if terms are updated val termsNotSigned: Boolean = false, val userConsent: Boolean = false -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerState.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerState.kt index 4c2f437e07..558cc4dcf7 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerState.kt @@ -16,10 +16,10 @@ package im.vector.app.features.discovery.change -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState data class SetIdentityServerState( val homeServerUrl: String = "", // Will contain the default identity server url if any val defaultIdentityServerUrl: String? = null -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt index f3bddaf0d2..68131e8569 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt @@ -16,9 +16,9 @@ package im.vector.app.features.home -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.api.session.initsync.SyncStatusService data class HomeActivityViewState( val syncStatusServiceStatus: SyncStatusService.Status = SyncStatusService.Status.Idle -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt index 4022a0d9fb..7665dd41e5 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.home import androidx.annotation.StringRes import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.R import im.vector.app.RoomGroupingMethod @@ -43,7 +43,7 @@ data class HomeDetailViewState( val incrementalSyncStatus: SyncStatusService.Status.IncrementalSyncStatus = SyncStatusService.Status.IncrementalSyncIdle, val pushCounter: Int = 0, val showDialPadTab: Boolean = false -) : MvRxState +) : MavericksState sealed class HomeTab(@StringRes val titleRes: Int) { data class RoomList(val displayMode: RoomListDisplayMode) : HomeTab(displayMode.titleRes) diff --git a/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt index ae7b495aa2..da29d4c5fc 100644 --- a/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.home import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -41,7 +41,7 @@ data class ActiveSpaceViewState( val isInSpaceMode: Boolean = false, val activeSpaceSummary: RoomSummary? = null, val canUserManageSpace: Boolean = false -) : MvRxState +) : MavericksState class PromoteRestrictedViewModel @AssistedInject constructor( @Assisted initialState: ActiveSpaceViewState, diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index 988b4fbabe..7b25f20f77 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -48,7 +48,7 @@ import java.util.concurrent.TimeUnit data class UnknownDevicesState( val myMatrixItem: MatrixItem.UserItem? = null, val unknownSessions: Async> = Uninitialized -) : MvRxState +) : MavericksState data class DeviceDetectionInfo( val deviceInfo: DeviceInfo, diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt index ac983a9f0c..1e2b9e542a 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.home import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -46,7 +46,7 @@ import java.util.concurrent.TimeUnit data class UnreadMessagesState( val homeSpaceUnread: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0), val otherSpacesUnread: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) -) : MvRxState +) : MavericksState data class CountInfo( val homeCount: RoomAggregateNotificationCount, diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewState.kt index f067591e83..f2971db093 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewState.kt @@ -17,10 +17,10 @@ package im.vector.app.features.home.room.breadcrumbs import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomSummary data class BreadcrumbsViewState( val asyncBreadcrumbs: Async> = Uninitialized -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 8f4ad97b72..6dee6d2b00 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.Event @@ -90,7 +90,7 @@ data class RoomDetailViewState( val isAllowedToStartWebRTCCall: Boolean = true, val hasFailedSending: Boolean = false, val jitsiState: JitsiState = JitsiState() -) : MvRxState { +) : MavericksState { constructor(args: RoomDetailArgs) : this( roomId = args.roomId, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt index 41fecbb5e2..4b272b6c4c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail.search import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.search.EventAndSender @@ -32,7 +32,7 @@ data class SearchViewState( val roomId: String = "", // Current pagination request val asyncSearchRequest: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: SearchArgs) : this(roomId = args.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt index caee485aba..c1c145040e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail.timeline.action import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.core.extensions.canReact import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData @@ -50,7 +50,7 @@ data class MessageActionState( val actions: List = emptyList(), val expendedReportContentMenu: Boolean = false, val actionPermissions: ActionPermissions = ActionPermissions() -) : MvRxState { +) : MavericksState { constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt index 62f08eef7f..d367dff835 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail.timeline.edithistory import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import org.matrix.android.sdk.api.session.events.model.Event @@ -27,7 +27,7 @@ data class ViewEditHistoryViewState( val roomId: String, val isOriginalAReply: Boolean = false, val editList: Async> = Uninitialized) - : MvRxState { + : MavericksState { constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt index 35d208e30e..6a78161d28 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.reactions import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -42,7 +42,7 @@ data class DisplayReactionsViewState( val eventId: String, val roomId: String, val mapReactionKeyToMemberList: Async> = Uninitialized) - : MvRxState { + : MavericksState { constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewState.kt index e3ea98682b..6a155aa22a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail.upgrade import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class MigrateRoomViewState( @@ -36,7 +36,7 @@ data class MigrateRoomViewState( val upgradingProgressIndeterminate: Boolean = true, val migrationReason: MigrateRoomBottomSheet.MigrationReason = MigrateRoomBottomSheet.MigrationReason.MANUAL, val autoMigrateMembersAndParents: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: MigrateRoomBottomSheet.Args) : this( roomId = args.roomId, newVersion = args.newVersion, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt index 68a8b9e515..46ff6c242b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.list import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.RoomGroupingMethod import im.vector.app.features.home.RoomListDisplayMode @@ -31,7 +31,7 @@ data class RoomListViewState( val asyncSuggestedRooms: Async> = Uninitialized, val currentUserName: String? = null, val currentRoomGrouping: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: RoomListParams) : this(displayMode = args.displayMode) } diff --git a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt index 14d19b2e6a..d7ced5e632 100644 --- a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt +++ b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt @@ -16,10 +16,10 @@ package im.vector.app.features.homeserver -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities data class HomeServerCapabilitiesViewState( val capabilities: HomeServerCapabilities = HomeServerCapabilities(), val isE2EByDefault: Boolean = true -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewState.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewState.kt index cd688f097b..37f0861a83 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewState.kt @@ -17,13 +17,13 @@ package im.vector.app.features.invite import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class InviteUsersToRoomViewState( val roomId: String, val inviteState: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: InviteUsersToRoomArgs) : this(roomId = args.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt index 187d39d9eb..8d3a3040ff 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.login import com.airbnb.mvrx.Async import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.PersistState import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -55,7 +55,7 @@ data class LoginViewState( // Supported types for the login. We cannot use a sealed class for LoginType because it is not serializable val loginModeSupportedTypes: List = emptyList(), val knownCustomHomeServersUrls: List = emptyList() -) : MvRxState { +) : MavericksState { fun isLoading(): Boolean { return asyncLoginAction is Loading diff --git a/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsViewState.kt b/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsViewState.kt index c09c76efc3..3641b443e3 100644 --- a/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsViewState.kt @@ -16,12 +16,12 @@ package im.vector.app.features.login.terms -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms data class LoginTermsViewState( val localizedFlowDataLoginTermsChecked: List -) : MvRxState { +) : MavericksState { fun check(data: LocalizedFlowDataLoginTerms) { localizedFlowDataLoginTermsChecked.find { it.localizedFlowDataLoginTerms == data }?.checked = true } diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt index d629c6dfe7..276d1111bb 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt @@ -17,7 +17,7 @@ package im.vector.app.features.login2 import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.PersistState import com.airbnb.mvrx.Uninitialized import im.vector.app.core.extensions.toReducedUrl @@ -55,7 +55,7 @@ data class LoginViewState2( // From database val knownCustomHomeServersUrls: List = emptyList() -) : MvRxState { +) : MavericksState { // Pending user identifier fun userIdentifier(): String { diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewState.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewState.kt index 80211b3da2..0ae60e910c 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewState.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.login2.created import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.util.MatrixItem @@ -26,4 +26,4 @@ data class AccountCreatedViewState( val isLoading: Boolean = false, val currentUser: Async = Uninitialized, val hasBeenModified: Boolean = false -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetState.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetState.kt index 40213dc0ee..0fff2495f9 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetState.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.matrixto import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkParser @@ -31,7 +31,7 @@ data class MatrixToBottomSheetState( val startChattingState: Async = Uninitialized, val roomPeekResult: Async = Uninitialized, val peopleYouKnow: Async> = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: MatrixToBottomSheet.MatrixToArgs) : this( deepLink = args.matrixToLink, diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt index a5019115fb..37a379104b 100644 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt @@ -16,8 +16,8 @@ package im.vector.app.features.rageshake -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState data class BugReportState( val serverVersion: String = "" -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt index 8e12dd2cca..04ee95f68e 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.reactions import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -32,7 +32,7 @@ import kotlinx.coroutines.launch data class EmojiSearchResultViewState( val query: String = "", val results: List = emptyList() -) : MvRxState +) : MavericksState class EmojiSearchResultViewModel @AssistedInject constructor( @Assisted initialState: EmojiSearchResultViewState, diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt index 2ca68b5e8b..dbf399bdf2 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt @@ -16,13 +16,13 @@ package im.vector.app.features.room -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import im.vector.app.features.roommemberprofile.RoomMemberProfileArgs import im.vector.app.features.roomprofile.RoomProfileArgs data class RequireActiveMembershipViewState( val roomId: String? = null -) : MvRxState { +) : MavericksState { // No constructor for RoomDetailArgs because of intent for Shortcut diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt index fdab72caba..3b9995a13d 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomdirectory import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom @@ -36,4 +36,4 @@ data class PublicRoomsViewState( // keys are room alias or roomId val changeMembershipStates: Map = emptyMap(), val roomDirectoryData: RoomDirectoryData = RoomDirectoryData() -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt index db56a19904..f605150ee0 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roomdirectory.createroom import android.net.Uri import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -39,7 +39,7 @@ data class CreateRoomViewState( val supportsRestricted: Boolean = false, val aliasLocalPart: String? = null, val isSubSpace: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: CreateRoomArgs) : this( roomName = args.initialName, diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt index 5cdee862ab..56aa807e41 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomdirectory.picker import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomdirectory.RoomDirectoryServer import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol @@ -30,4 +30,4 @@ data class RoomDirectoryPickerViewState( val addServerAsync: Async = Uninitialized, // computed val directories: List = emptyList() -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt index 75286ea24f..725498c4fc 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomdirectory.roompreview import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomdirectory.JoinState import org.matrix.android.sdk.api.session.permalinks.PermalinkData @@ -48,7 +48,7 @@ data class RoomPreviewViewState( val fromEmailInvite: PermalinkData.RoomEmailInviteLink? = null, // used only if it's an email invite val isEmailBoundToAccount: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: RoomPreviewData) : this( roomId = args.roomId, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt index 5c2751f0dc..a4730153c2 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roommemberprofile import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.room.model.Membership @@ -42,7 +42,7 @@ data class RoomMemberProfileViewState( val asyncMembership: Async = Uninitialized, val hasReadReceipt: Boolean = false, val actionPermissions: ActionPermissions = ActionPermissions() -) : MvRxState { +) : MavericksState { constructor(args: RoomMemberProfileArgs) : this(userId = args.userId, roomId = args.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index 7d31a5c811..15c62af35b 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.roommemberprofile.devices import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -42,7 +42,7 @@ data class DeviceListViewState( val memberCrossSigningKey: MXCrossSigningInfo? = null, val cryptoDevices: Async> = Loading(), val selectedDevice: CryptoDeviceInfo? = null -) : MvRxState +) : MavericksState class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted private val initialState: DeviceListViewState, @Assisted private val args: DeviceListBottomSheet.Args, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt index 999b6540bd..14b415c53a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roomprofile import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -35,7 +35,7 @@ data class RoomProfileViewState( val recommendedRoomVersion: String? = null, val canUpgradeRoom: Boolean = false, val isTombstoned: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index f6341f4f64..aabdb7530f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.alias import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility @@ -35,7 +35,7 @@ data class RoomAliasViewState( val publishManuallyState: AddAliasState = AddAliasState.Hidden, val localAliases: Async> = Uninitialized, val newLocalAliasState: AddAliasState = AddAliasState.Closed -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt index a61075cef6..1accc85e45 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt @@ -16,7 +16,7 @@ package im.vector.app.features.roomprofile.alias.detail -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState data class RoomAliasBottomSheetState( val alias: String, @@ -25,7 +25,7 @@ data class RoomAliasBottomSheetState( val isMainAlias: Boolean, val isLocal: Boolean, val canEditCanonicalAlias: Boolean -) : MvRxState { +) : MavericksState { constructor(args: RoomAliasBottomSheetArgs) : this( alias = args.alias, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt index 2861b30222..e36de58f97 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.banned import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary @@ -30,7 +30,7 @@ data class RoomBannedMemberListViewState( val filter: String = "", val onGoingModerationAction: List = emptyList(), val canUserBan: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt index 63d07cc4dd..d736260f10 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roomprofile.members import androidx.annotation.StringRes import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.R import im.vector.app.core.platform.GenericIdArgs @@ -36,7 +36,7 @@ data class RoomMemberListViewState( val threePidInvites: Async> = Uninitialized, val trustLevelMap: Async> = Uninitialized, val actionsPermissions: ActionPermissions = ActionPermissions() -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt index 72e61fba70..236c5c36fc 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.notifications import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import im.vector.app.features.home.room.list.actions.RoomListActionsArgs @@ -30,7 +30,7 @@ data class RoomNotificationSettingsViewState( val roomSummary: Async = Uninitialized, val isLoading: Boolean = false, val notificationState: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) constructor(args: RoomListActionsArgs) : this(roomId = args.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt index ce38ab87e5..9a5ac4c19f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.permissions import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent @@ -30,7 +30,7 @@ data class RoomPermissionsViewState( val showAdvancedPermissions: Boolean = false, val currentPowerLevelsContent: Async = Uninitialized, val isLoading: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt index 403836b268..122e0034c6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roomprofile.settings import android.net.Uri import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.R import im.vector.app.core.resources.StringProvider @@ -46,7 +46,7 @@ data class RoomSettingsViewState( val actionPermissions: ActionPermissions = ActionPermissions(), val supportsRestricted: Boolean = false, val canUpgradeToRestricted: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedState.kt index 8a107ce8f1..157f53b56e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.settings.joinrule.advanced import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.settings.joinrule.JoinRulesOptionSupport @@ -45,6 +45,6 @@ data class RoomJoinRuleChooseRestrictedState( val restrictedSupportedByThisVersion: Boolean = false, val restrictedVersionNeeded: String? = null, val didSwitchToReplacementRoom: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt index b85e4f04de..a2b5aa99df 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.uploads import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -33,7 +33,7 @@ data class RoomUploadsViewState( val asyncEventsRequest: Async = Uninitialized, // True if more result are available server side val hasMore: Boolean = true -) : MvRxState { +) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt index 80c64220c0..bc030775e0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.account.deactivation import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -42,7 +42,7 @@ import kotlin.coroutines.resumeWithException data class DeactivateAccountViewState( val dummy: Boolean = false -) : MvRxState +) : MavericksState class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private val initialState: DeactivateAccountViewState, private val session: Session) diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewState.kt index 8a371ada68..9e349253ca 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewState.kt @@ -16,7 +16,7 @@ package im.vector.app.features.settings.crosssigning -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo data class CrossSigningSettingsViewState( @@ -24,4 +24,4 @@ data class CrossSigningSettingsViewState( val xSigningIsEnableInAccount: Boolean = false, val xSigningKeysAreTrusted: Boolean = false, val xSigningKeyCanSign: Boolean = true -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt index a736b0442c..e320642ed0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.devices import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo @@ -30,7 +30,7 @@ data class DeviceVerificationInfoBottomSheetViewState( val isMine: Boolean = false, val hasOtherSessions: Boolean = false, val isRecoverySetup: Boolean = false -) : MvRxState { +) : MavericksState { val canVerifySession: Boolean get() = hasOtherSessions || isRecoverySetup diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index c48b08e806..ec2d6dd53e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -75,7 +75,7 @@ data class DevicesViewState( val request: Async = Uninitialized, val hasAccountCrossSigning: Boolean = false, val accountCrossSigningIsTrusted: Boolean = false -) : MvRxState +) : MavericksState data class DeviceFullInfo( val deviceInfo: DeviceInfo, diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index d50caea579..c42e2440c1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.settings.devtools import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -36,7 +36,7 @@ import org.matrix.android.sdk.rx.rx data class AccountDataViewState( val accountData: Async> = Uninitialized -) : MvRxState +) : MavericksState class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: AccountDataViewState, private val session: Session) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt index 325538ee5e..25834e5580 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt @@ -20,7 +20,7 @@ import androidx.paging.PagedList import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -36,7 +36,7 @@ import org.matrix.android.sdk.rx.asObservable data class GossipingEventsPaperTrailState( val events: Async> = Uninitialized -) : MvRxState +) : MavericksState class GossipingEventsPaperTrailViewModel @AssistedInject constructor(@Assisted initialState: GossipingEventsPaperTrailState, private val session: Session) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt index c0a791233f..b4e872000e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import androidx.paging.PagedList import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -39,7 +39,7 @@ import org.matrix.android.sdk.rx.asObservable data class KeyRequestListViewState( val incomingRequests: Async> = Uninitialized, val outgoingRoomKeyRequests: Async> = Uninitialized -) : MvRxState +) : MavericksState class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState: KeyRequestListViewState, private val session: Session) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt index e7a56ef9df..e1554cb0a4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt @@ -22,7 +22,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -48,7 +48,7 @@ sealed class KeyRequestEvents : VectorViewEvents { data class KeyRequestViewState( val exporting: Async = Uninitialized -) : MvRxState +) : MavericksState class KeyRequestViewModel @AssistedInject constructor( @Assisted initialState: KeyRequestViewState, diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt index 87ad637ca5..3acd79d768 100644 --- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.homeserver import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.federation.FederationVersion import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities @@ -27,4 +27,4 @@ data class HomeServerSettingsViewState( val homeserverClientServerApiUrl: String = "", val homeServerCapabilities: HomeServerCapabilities = HomeServerCapabilities(), val federationVersion: Async = Uninitialized -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index aa00f71542..34d2bc4821 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -39,7 +39,7 @@ import org.matrix.android.sdk.rx.rx data class IgnoredUsersViewState( val ignoredUsers: List = emptyList(), val unIgnoreRequest: Async = Uninitialized -) : MvRxState +) : MavericksState sealed class IgnoredUsersAction : VectorViewModelAction { data class UnIgnore(val userId: String) : IgnoredUsersAction() diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewState.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewState.kt index 64c95468f0..8cb5978393 100644 --- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.locale import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.settings.VectorLocale import java.util.Locale @@ -25,4 +25,4 @@ import java.util.Locale data class LocalePickerViewState( val currentLocale: Locale = VectorLocale.applicationLocale, val locales: Async> = Uninitialized -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt index 9a47fa2a15..509e38b0f3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.settings.push import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -35,7 +35,7 @@ import org.matrix.android.sdk.rx.RxSession data class PushGatewayViewState( val pushGateways: Async> = Uninitialized -) : MvRxState +) : MavericksState class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState, private val session: Session) diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt index 64ddc275eb..dd21cfb5c9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt @@ -15,7 +15,7 @@ */ package im.vector.app.features.settings.push -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.app.core.di.HasScreenInjector @@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.pushrules.rest.PushRule data class PushRulesViewState( val rules: List = emptyList() -) : MvRxState +) : MavericksState class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel(initialState) { diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewState.kt index b080c06cbd..dbc81fd8f3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.threepids import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.core.utils.ReadOnceTrue import org.matrix.android.sdk.api.session.identity.ThreePid @@ -30,4 +30,4 @@ data class ThreePidsSettingsViewState( val msisdnValidationRequests: Map> = emptyMap(), val editTextReinitiator: ReadOnceTrue = ReadOnceTrue(), val msisdnValidationReinitiator: Map = emptyMap() -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewState.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewState.kt index 751dc999a2..620709a515 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewState.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.share import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -27,4 +27,4 @@ data class IncomingShareViewState( val filteredRoomSummaries: Async> = Uninitialized, val selectedRoomIds: Set = emptySet(), val isInMultiSelectionMode: Boolean = false -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt index ccc186aba7..d209d13176 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.signout.soft import com.airbnb.mvrx.Async import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import im.vector.app.features.login.LoginMode @@ -32,7 +32,7 @@ data class SoftLogoutViewState( val userDisplayName: String, val hasUnsavedKeys: Boolean, val enteredPassword: String = "" -) : MvRxState { +) : MavericksState { fun isLoading(): Boolean { return asyncLoginAction is Loading diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt index 7482f4881e..e46bcc30b1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.RoomGroupingMethod import org.matrix.android.sdk.api.session.group.model.GroupSummary @@ -35,4 +35,4 @@ data class SpaceListViewState( val legacyGroups: List? = null, val expandedStates: Map = emptyMap(), val homeAggregateCount : RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt index 395fcc9df1..7ac844d297 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -30,7 +30,7 @@ data class SpaceMenuState( val isLastAdmin: Boolean = false, val leaveMode: LeaveMode = LeaveMode.LEAVE_NONE, val leavingState: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: SpaceBottomSheetSettingsArgs) : this(spaceId = args.spaceId) enum class LeaveMode { diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt index 6fb5853269..f7433dca7c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.spaces.create import android.net.Uri import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class CreateSpaceState( @@ -38,7 +38,7 @@ data class CreateSpaceState( val emailValidationResult: Map? = null, val creationResult: Async = Uninitialized, val canInviteByMail: Boolean = false -) : MvRxState { +) : MavericksState { enum class Step { ChooseType, diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt index 33b494075d..1467b69659 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces.explore import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo @@ -39,7 +39,7 @@ data class SpaceDirectoryState( // cached room summaries of known rooms, we use it because computed room name would be better using it val knownRoomSummaries: List = emptyList(), val paginationStatus: Map> = emptyMap() -) : MvRxState { +) : MavericksState { constructor(args: SpaceDirectoryArgs) : this( spaceId = args.spaceId ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetState.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetState.kt index d712cf9e8a..5963d491d5 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces.invite import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.user.model.User @@ -29,7 +29,7 @@ data class SpaceInviteBottomSheetState( val peopleYouKnow: Async> = Uninitialized, val joinActionState: Async = Uninitialized, val rejectActionState: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: SpaceInviteBottomSheet.Args) : this( spaceId = args.spaceId ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt index f7802d2a31..b8dcd3f7a2 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces.leave import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -29,7 +29,7 @@ data class SpaceLeaveAdvanceViewState( val selectedRooms: List = emptyList(), val currentFilter: String = "", val leaveState: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: SpaceBottomSheetSettingsArgs) : this( spaceId = args.spaceId ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt index e941d04b22..bec8b905d8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces.manage import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class SpaceAddRoomsState( @@ -30,7 +30,7 @@ data class SpaceAddRoomsState( val shouldShowDMs: Boolean = false, val onlyShowSpaces: Boolean = false // val selectionList: Map = emptyMap() -) : MvRxState { +) : MavericksState { constructor(args: SpaceManageArgs) : this( spaceId = args.spaceId, onlyShowSpaces = args.manageType == ManageType.AddRoomsOnlySpaces diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt index 34173828a7..211d3645f5 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces.manage import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.space.SpaceHierarchyData @@ -32,7 +32,7 @@ data class SpaceManageRoomViewState( val paginationStatus: Async = Uninitialized, // cached room summaries of known rooms, we use it because computed room name would be better using it val knownRoomSummaries: List = emptyList() -) : MvRxState { +) : MavericksState { constructor(args: SpaceManageArgs) : this( spaceId = args.spaceId ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt index 35596f0884..82abc823c3 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt @@ -16,7 +16,7 @@ package im.vector.app.features.spaces.manage -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState enum class ManageType { AddRooms, @@ -27,7 +27,7 @@ enum class ManageType { data class SpaceManageViewState( val spaceId: String = "", val manageType: ManageType -) : MvRxState { +) : MavericksState { constructor(args: SpaceManageArgs) : this( spaceId = args.spaceId, manageType = args.manageType diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt index ea322e3fbd..b24636a9d4 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt @@ -17,14 +17,14 @@ package im.vector.app.features.spaces.people import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.core.platform.GenericIdArgs data class SpacePeopleViewState( val spaceId: String, val createAndInviteState: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: GenericIdArgs) : this( spaceId = args.id ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt index d31d05cf96..14f9a45d68 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces.preview import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class SpacePreviewState( @@ -28,7 +28,7 @@ data class SpacePreviewState( val spaceInfo: Async = Uninitialized, val childInfoList: Async> = Uninitialized, val inviteTermination: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(args: SpacePreviewArgs) : this(idOrAlias = args.idOrAlias) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewState.kt index 97606e9506..826719b762 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.spaces.share import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -27,7 +27,7 @@ data class ShareSpaceViewState( val canInviteByMxId: Boolean = false, val canShareLink: Boolean = false, val postCreation: Boolean = false -) : MvRxState { +) : MavericksState { constructor(args: ShareSpaceBottomSheet.Args) : this( spaceId = args.spaceId, postCreation = args.postCreation diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewState.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewState.kt index 20b09f0cd7..e87fd9620c 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewState.kt @@ -17,9 +17,9 @@ package im.vector.app.features.terms import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class ReviewTermsViewState( val termsList: Async> = Uninitialized -) : MvRxState +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeState.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeState.kt index c26da7c0a4..a323609344 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeState.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeState.kt @@ -16,7 +16,7 @@ package im.vector.app.features.usercode -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.api.util.MatrixItem data class UserCodeState( @@ -24,7 +24,7 @@ data class UserCodeState( val matrixItem: MatrixItem? = null, val shareLink: String? = null, val mode: Mode = Mode.SHOW -) : MvRxState { +) : MavericksState { sealed class Mode { object SHOW : Mode() object SCAN : Mode() diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt index b66d36c5f0..e389bbefee 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.userdirectory import androidx.paging.PagedList import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.core.contacts.MappedContact import org.matrix.android.sdk.api.session.user.model.User @@ -35,7 +35,7 @@ data class UserListViewState( val configuredIdentityServer: String? = null, private val showInviteActions: Boolean, val showContactBookAction: Boolean -) : MvRxState { +) : MavericksState { constructor(args: UserListFragmentArgs) : this( excludedUserIds = args.excludedUserIds, diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt index 845ee81a2d..2d98f734dd 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt @@ -18,7 +18,7 @@ package im.vector.app.features.widgets import androidx.annotation.StringRes import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.R import org.matrix.android.sdk.api.session.widgets.model.Widget @@ -52,7 +52,7 @@ data class WidgetViewState( val widgetName: String = "", val canManageWidgets: Boolean = false, val asyncWidget: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(widgetArgs: WidgetArgs) : this( widgetKind = widgetArgs.kind, diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt index 1cc14a91c2..79f9b8cee3 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt @@ -17,7 +17,7 @@ package im.vector.app.features.widgets.permissions import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.widgets.WidgetArgs import org.matrix.android.sdk.api.session.widgets.model.Widget @@ -26,7 +26,7 @@ data class RoomWidgetPermissionViewState( val roomId: String, val widgetId: String?, val permissionData: Async = Uninitialized -) : MvRxState { +) : MavericksState { constructor(widgetArgs: WidgetArgs) : this( roomId = widgetArgs.roomId, diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index 6d95911bdb..619f14e559 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.MutableLiveData import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -49,7 +49,7 @@ import java.util.concurrent.TimeUnit data class ServerBackupStatusViewState( val bannerState: Async = Uninitialized -) : MvRxState +) : MavericksState /** * The state representing the view diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt index df7a826b48..7916caca66 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt @@ -22,7 +22,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized @@ -51,7 +51,7 @@ data class SignoutCheckViewState( val crossSigningSetupAllKeysKnown: Boolean = false, val keysBackupState: KeysBackupState = KeysBackupState.Unknown, val hasBeenExportedToFile: Async = Uninitialized -) : MvRxState +) : MavericksState class SignoutCheckViewModel @AssistedInject constructor( @Assisted initialState: SignoutCheckViewState, diff --git a/vector/src/test/java/im/vector/app/test/Extensions.kt b/vector/src/test/java/im/vector/app/test/Extensions.kt index 0d89208b2e..f2a087fd52 100644 --- a/vector/src/test/java/im/vector/app/test/Extensions.kt +++ b/vector/src/test/java/im/vector/app/test/Extensions.kt @@ -16,7 +16,7 @@ package im.vector.app.test -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MavericksState import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction @@ -25,7 +25,7 @@ import org.amshove.kluent.shouldBeEqualTo fun String.trimIndentOneLine() = trimIndent().replace("\n", "") -fun VectorViewModel.test(): ViewModelTest { +fun VectorViewModel.test(): ViewModelTest { val state = { com.airbnb.mvrx.withState(this) { it } } val viewEvents = viewEvents.observe().test() return ViewModelTest(state, viewEvents) From 9337e0e76da5ca14e305ee54d982d5d585648f5b Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 15:03:02 +0200 Subject: [PATCH 003/144] Mavericks 2: create sdk flow module --- matrix-sdk-android-flow/.gitignore | 1 + matrix-sdk-android-flow/build.gradle | 49 +++++++ matrix-sdk-android-flow/consumer-rules.pro | 0 matrix-sdk-android-flow/proguard-rules.pro | 21 +++ .../sdk/flow/ExampleInstrumentedTest.kt | 40 +++++ .../src/main/AndroidManifest.xml | 5 + .../org/matrix/android/sdk/flow/FlowRoom.kt | 83 +++++++++++ .../matrix/android/sdk/flow/FlowSession.kt | 138 ++++++++++++++++++ .../matrix/android/sdk/flow/OptionalFlow.kt | 32 ++++ .../android/sdk/flow/ExampleUnitTest.kt | 33 +++++ settings.gradle | 1 + vector/build.gradle | 1 + 12 files changed, 404 insertions(+) create mode 100644 matrix-sdk-android-flow/.gitignore create mode 100644 matrix-sdk-android-flow/build.gradle create mode 100644 matrix-sdk-android-flow/consumer-rules.pro create mode 100644 matrix-sdk-android-flow/proguard-rules.pro create mode 100644 matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt create mode 100644 matrix-sdk-android-flow/src/main/AndroidManifest.xml create mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt create mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt create mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt create mode 100644 matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt diff --git a/matrix-sdk-android-flow/.gitignore b/matrix-sdk-android-flow/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/matrix-sdk-android-flow/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/matrix-sdk-android-flow/build.gradle b/matrix-sdk-android-flow/build.gradle new file mode 100644 index 0000000000..4aecec169b --- /dev/null +++ b/matrix-sdk-android-flow/build.gradle @@ -0,0 +1,49 @@ + +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 31 + + defaultConfig { + minSdk 21 + targetSdk 31 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation project(":matrix-sdk-android") + implementation libs.androidx.appCompat + + implementation libs.jetbrains.kotlinStdlibJdk7 + implementation libs.jetbrains.coroutinesCore + implementation libs.jetbrains.coroutinesAndroid + implementation libs.androidx.lifecycleLivedata + + // Paging + implementation libs.androidx.pagingRuntimeKtx + + // Logging + implementation libs.jakewharton.timber + +} \ No newline at end of file diff --git a/matrix-sdk-android-flow/consumer-rules.pro b/matrix-sdk-android-flow/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/matrix-sdk-android-flow/proguard-rules.pro b/matrix-sdk-android-flow/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/matrix-sdk-android-flow/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..41799955a4 --- /dev/null +++ b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.matrix.android.sdk.flow.test", appContext.packageName) + } +} diff --git a/matrix-sdk-android-flow/src/main/AndroidManifest.xml b/matrix-sdk-android-flow/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..2392c0bfcb --- /dev/null +++ b/matrix-sdk-android-flow/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt new file mode 100644 index 0000000000..a3e476ce08 --- /dev/null +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import androidx.lifecycle.asFlow +import kotlinx.coroutines.flow.Flow +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams +import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary +import org.matrix.android.sdk.api.session.room.model.ReadReceipt +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState +import org.matrix.android.sdk.api.session.room.send.UserDraft +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.util.Optional + +class FlowRoom(private val room: Room) { + + fun liveRoomSummary(): Flow> { + return room.getRoomSummaryLive().asFlow() + } + + fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow> { + return room.getRoomMembersLive(queryParams).asFlow() + } + + fun liveAnnotationSummary(eventId: String): Flow> { + return room.getEventAnnotationsSummaryLive(eventId).asFlow() + } + + fun liveTimelineEvent(eventId: String): Flow> { + return room.getTimeLineEventLive(eventId).asFlow() + } + + fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow> { + return room.getStateEventLive(eventType, stateKey).asFlow() + } + + fun liveStateEvents(eventTypes: Set): Flow> { + return room.getStateEventsLive(eventTypes).asFlow() + } + + fun liveReadMarker(): Flow> { + return room.getReadMarkerLive().asFlow() + } + + fun liveReadReceipt(): Flow> { + return room.getMyReadReceiptLive().asFlow() + } + + fun liveEventReadReceipts(eventId: String): Flow> { + return room.getEventReadReceiptsLive(eventId).asFlow() + } + + fun liveDraft(): Flow> { + return room.getDraftLive().asFlow() + } + + fun liveNotificationState(): Flow { + return room.getLiveRoomNotificationState().asFlow() + } +} + +fun Room.flow(): FlowRoom { + return FlowRoom(this) +} diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt new file mode 100644 index 0000000000..affcd4a65d --- /dev/null +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt @@ -0,0 +1,138 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import androidx.lifecycle.asFlow +import androidx.paging.PagedList +import kotlinx.coroutines.flow.Flow +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent +import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo +import org.matrix.android.sdk.api.session.group.GroupSummaryQueryParams +import org.matrix.android.sdk.api.session.group.model.GroupSummary +import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.api.session.pushers.Pusher +import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams +import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent +import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams +import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.session.widgets.model.Widget +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo +import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo + +class RxFlow(private val session: Session) { + + fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Flow> { + return session.getRoomSummariesLive(queryParams).asFlow() + } + + fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow> { + return session.getGroupSummariesLive(queryParams).asFlow() + } + + fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Flow> { + return session.spaceService().getSpaceSummariesLive(queryParams).asFlow() + } + + fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow> { + return session.getBreadcrumbsLive(queryParams).asFlow() + } + + fun liveMyDevicesInfo(): Flow> { + return session.cryptoService().getLiveMyDevicesInfo().asFlow() + } + + fun liveSyncState(): Flow { + return session.getSyncStateLive().asFlow() + } + + fun livePushers(): Flow> { + return session.getPushersLive().asFlow() + } + + fun liveUser(userId: String): Flow> { + return session.getUserLive(userId).asFlow() + } + + fun liveRoomMember(userId: String, roomId: String): Flow> { + return session.getRoomMemberLive(userId, roomId).asFlow() + } + + fun liveUsers(): Flow> { + return session.getUsersLive().asFlow() + } + + fun liveIgnoredUsers(): Flow> { + return session.getIgnoredUsersLive().asFlow() + } + + fun livePagedUsers(filter: String? = null, excludedUserIds: Set? = null): Flow> { + return session.getPagedUsersLive(filter, excludedUserIds).asFlow() + } + + fun liveThreePIds(refreshData: Boolean): Flow> { + return session.getThreePidsLive(refreshData).asFlow() + } + + fun livePendingThreePIds(): Flow> { + return session.getPendingThreePidsLive().asFlow() + } + + fun liveUserCryptoDevices(userId: String): Flow> { + return session.cryptoService().getLiveCryptoDeviceInfo(userId).asFlow() + } + + fun liveCrossSigningInfo(userId: String): Flow> { + return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asFlow() + } + + fun liveCrossSigningPrivateKeys(): Flow> { + return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() + } + + fun liveUserAccountData(types: Set): Flow> { + return session.accountDataService().getLiveUserAccountDataEvents(types).asFlow() + } + + fun liveRoomAccountData(types: Set): Flow> { + return session.accountDataService().getLiveRoomAccountDataEvents(types).asFlow() + } + + fun liveRoomWidgets( + roomId: String, + widgetId: QueryStringValue, + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): Flow> { + return session.widgetService().getRoomWidgetsLive(roomId, widgetId, widgetTypes, excludedTypes).asFlow() + } + + fun liveRoomChangeMembershipState(): Flow> { + return session.getChangeMembershipsLive().asFlow() + } +} + +fun Session.flow(): RxFlow { + return RxFlow(this) +} diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt new file mode 100644 index 0000000000..a9f062f379 --- /dev/null +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.util.Optional + +fun Flow>.unwrap(): Flow { + return filter { it.hasValue() }.map { it.get() } +} + +fun Flow>.mapOptional(fn: (T) -> U?): Flow> { + return map { + it.map(fn) + } +} diff --git a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt new file mode 100644 index 0000000000..bd627b2041 --- /dev/null +++ b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/settings.gradle b/settings.gradle index b88ea99b05..e3b84b4733 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,3 +5,4 @@ include ':diff-match-patch' include ':attachment-viewer' include ':multipicker' include ':library:ui-styles' +include ':matrix-sdk-android-flow' diff --git a/vector/build.gradle b/vector/build.gradle index a9c6a407d8..76bc71b2d4 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -323,6 +323,7 @@ dependencies { implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android-rx") + implementation project(":matrix-sdk-android-flow") implementation project(":diff-match-patch") implementation project(":multipicker") implementation project(":attachment-viewer") From 19ae2987079220c418a8d1fe0ad043c5ba8ef6f9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 1 Oct 2021 15:57:41 +0200 Subject: [PATCH 004/144] Add documentation about Figma -- very first draft --- docs/design.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/design.md diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000000..959ca40864 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,104 @@ +# Element Android design + +## Introduction + +Design at element.io is done using Figma - https://www.figma.com + +## How to import from Figma to the Element Android project + +Integration should be done using the Android development best practice, and should follow the existing convention in the code. + +### Colors + +Element Android already contains all the colors which can be used by the designer, in the module `ui-style`. +Some of them depend on the theme, so ensure to use theme attributes and not colors directly. + +### Text + + - click on a text on Figma + - on the right panel, information about the style and colors are displayed + - in Element Android, text style are already defined, generally you should not create new style + - apply the style and the color to the layout + +### Dimension, position and margin + + - click on an item on Figma + - dimensions of the item will be displayed. + - move the mouse to other items to get relative positioning, margin, etc. + +### Icons + +#### Export drawable from Figma + + - click on the element to export + - ensure that the correct layer is selected. Sometimes the parent layer has to be selected on the left panel + - on the right panel, click on "export" + - select SVG + - you cqn check the preview of what will be exported + - click on "export" and save the file locally + - unzip the file if necessary + +It's also possible for any icon to go to the main component by right-clicking on the icon. + +#### Import in Android Studio + + - right click on the drawable folder where the drawable will be created + - click on "New"/"Vector Asset" + - select the exported file + - update the filename if necessary + - click on "Next" and click on "Finish" + - open the created vector drawable + - optionally update the color(s) to "#FF0000" (red) to ensure that the drawable is correctly tinted at runtime. + +## Figma links + +Figma links can be included in the layout, for future reference, but it is also OK to add a paragraph below here, to centralize the information + +Main entry point: https://www.figma.com/files/project/5612863/Element?fuid=779371459522484071 + +Note: all the Figma links are not publicly available. + +### Coumpound + +Coumpound contains the theme of the application, with all the components, in Light and Dark theme: palette (colors), typography, iconography, etc. + +https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound + +### Login + +TBD + +#### Login v2 + +https://www.figma.com/file/xdV4PuI3DlzA1EiBvbrggz/Login-Flow-v2 + +### Room list + +TBD + +### Timeline + +https://www.figma.com/file/x1HYYLYMmbYnhfoz2c2nGD/%5BRiotX%5D-Misc?node-id=0%3A1 + +### Voice message + +https://www.figma.com/file/uaWc62Ux2DkZC4OGtAGcNc/Voice-Messages?node-id=473%3A12 + +### Room settings + +TBD + +### VoIP + +https://www.figma.com/file/V6m2z0oAtUV1l8MdyIrAep/VoIP?node-id=4254%3A25767 + +### Presence + +https://www.figma.com/file/qmvEskET5JWva8jZJ4jX8o/Presence---User-Status?node-id=114%3A9174 +(Option B is chosen) + +### Spaces + +https://www.figma.com/file/m7L63aGPW7iHnIYStfdxCe/Spaces?node-id=192%3A30161 + +### List to be continued... From bbce37e694923c57a7c0147f5e0d14974a6ff3e0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 16:34:47 +0200 Subject: [PATCH 005/144] Mavericks 2: start replacing rx by flow --- .../quads/SharedSecureStorageViewModel.kt | 4 +- .../features/devtools/RoomDevToolViewModel.kt | 3 +- .../home/room/detail/RoomDetailViewModel.kt | 43 ++++++------ .../action/MessageActionsViewModel.kt | 20 ++++-- .../home/room/list/RoomListViewModel.kt | 24 +++---- .../invite/InviteUsersToRoomViewModel.kt | 66 +++++++++++-------- .../permissions/RoomPermissionsViewModel.kt | 9 ++- .../settings/VectorSettingsGeneralFragment.kt | 28 ++++---- .../settings/devtools/AccountDataViewModel.kt | 10 +-- 9 files changed, 113 insertions(+), 94 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 3f3b3a751a..49059391f0 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -35,6 +35,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.resources.StringProvider import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.listeners.ProgressListener @@ -42,6 +43,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.IntegrityResult import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding import org.matrix.android.sdk.rx.rx import timber.log.Timber @@ -114,7 +116,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( } } - session.rx() + session.flow() .liveUserCryptoDevices(session.myUserId) .distinctUntilChanged() .execute { diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt index 9fffe70872..cf4b54c7fa 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.util.JsonDict +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.rx.rx @@ -69,7 +70,7 @@ class RoomDevToolViewModel @AssistedInject constructor( init { session.getRoom(initialState.roomId) - ?.rx() + ?.flow() ?.liveStateEvents(emptySet()) ?.execute { async -> copy(stateEvents = async) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index cacf9b8902..c64fb89f28 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.home.room.detail import android.net.Uri import androidx.annotation.IdRes -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -65,6 +65,10 @@ import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.commonmark.parser.Parser @@ -104,8 +108,9 @@ import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode -import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap import timber.log.Timber @@ -252,7 +257,7 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeActiveRoomWidgets() { - session.rx() + session.flow() .liveRoomWidgets( roomId = initialState.roomId, widgetId = QueryStringValue.NoCondition @@ -285,7 +290,7 @@ class RoomDetailViewModel @AssistedInject constructor( val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(session.myUserId, QueryStringValue.Case.SENSITIVE) } - room.rx() + room.flow() .liveRoomMembers(queryParams) .map { it.firstOrNull().toOptional() @@ -1503,29 +1508,22 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeSyncState() { - session.rx() + session.flow() .liveSyncState() - .subscribe { syncState -> - setState { - copy(syncState = syncState) - } + .setOnEach { syncState -> + copy(syncState = syncState) } - .disposeOnClear() session.getSyncStatusLive() - .asObservable() - .subscribe { it -> - if (it is SyncStatusService.Status.IncrementalSyncStatus) { - setState { - copy(incrementalSyncStatus = it) - } - } + .asFlow() + .filterIsInstance() + .setOnEach { + copy(incrementalSyncStatus = it) } - .disposeOnClear() } private fun observeRoomSummary() { - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy( @@ -1587,16 +1585,15 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.rx() + session.flow() .liveRoomChangeMembershipState() .map { it[initialState.roomId] ?: ChangeMembershipState.Unknown } .distinctUntilChanged() - .subscribe { - setState { copy(changeMembershipState = it) } + .setOnEach { + copy(changeMembershipState = it) } - .disposeOnClear() } private fun observeSummaryState() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 6e6c7c1dbe..fd80a56870 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -36,6 +36,11 @@ import im.vector.app.features.html.VectorHtmlCompressor import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import im.vector.app.features.reactions.data.EmojiDataSource import im.vector.app.features.settings.VectorPreferences +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.switchMap import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState @@ -54,8 +59,9 @@ import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap import java.util.ArrayList /** @@ -79,7 +85,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted pillsPostProcessorFactory.create(initialState.roomId) } - private val eventIdObservable = BehaviorRelay.createDefault(initialState.eventId) + private val eventIdFlow = MutableStateFlow(initialState.eventId) @AssistedFactory interface Factory { @@ -135,7 +141,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun observeEvent() { if (room == null) return - room.rx() + room.flow() .liveTimelineEvent(initialState.eventId) .unwrap() .execute { @@ -145,9 +151,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun observeReactions() { if (room == null) return - eventIdObservable - .switchMap { eventId -> - room.rx() + eventIdFlow + .flatMapLatest { eventId -> + room.flow() .liveAnnotationSummary(eventId) .map { annotations -> EmojiDataSource.quickEmojis.map { emoji -> @@ -163,7 +169,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun observeTimelineEventState() { selectSubscribe(MessageActionState::timelineEvent, MessageActionState::actionPermissions) { timelineEvent, permissions -> val nonNullTimelineEvent = timelineEvent() ?: return@selectSubscribe - eventIdObservable.accept(nonNullTimelineEvent.eventId) + eventIdFlow.tryEmit(nonNullTimelineEvent.eventId) setState { copy( eventId = nonNullTimelineEvent.eventId, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 11f92284a6..b54d2b1b4f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -17,12 +17,11 @@ package im.vector.app.features.home.room.list import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import im.vector.app.AppStateHandler @@ -33,6 +32,8 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.query.QueryStringValue @@ -41,7 +42,7 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.state.isPublic -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject @@ -72,11 +73,13 @@ class RoomListViewModel @Inject constructor( * If current space is null, will return orphan rooms only */ ORPHANS_IF_SPACE_NULL, + /** * Special case when we don't want to discriminate rooms when current space is null. * In this case return all. */ ALL_IF_SPACE_NULL, + /** Do not filter based on space*/ NONE } @@ -92,7 +95,7 @@ class RoomListViewModel @Inject constructor( ) } - session.rx().liveUser(session.myUserId) + session.flow().liveUser(session.myUserId) .map { it.getOrNull()?.getBestName() } .distinctUntilChanged() .execute { @@ -103,18 +106,17 @@ class RoomListViewModel @Inject constructor( } private fun observeMembershipChanges() { - session.rx() + session.flow() .liveRoomChangeMembershipState() - .subscribe { - setState { copy(roomMembershipChanges = it) } + .setOnEach { + copy(roomMembershipChanges = it) } - .disposeOnClear() } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel? { + override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel { val fragment: RoomListFragment = (viewModelContext as FragmentViewModelContext).fragment() return fragment.roomListViewModelFactory.create(state) } @@ -320,7 +322,7 @@ class RoomListViewModel @Inject constructor( private fun String.otherTag(): String? { return when (this) { - RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY + RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY RoomTag.ROOM_TAG_LOW_PRIORITY -> RoomTag.ROOM_TAG_FAVOURITE else -> null } diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt index 2f20fef3c9..1f723104cf 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt @@ -18,16 +18,23 @@ package im.vector.app.features.invite import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.userdirectory.PendingSelection -import io.reactivex.Observable +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.rx.rx @@ -44,7 +51,7 @@ class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted fun create(initialState: InviteUsersToRoomViewState): InviteUsersToRoomViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: InviteUsersToRoomViewState): InviteUsersToRoomViewModel? { @@ -63,32 +70,33 @@ class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted } private fun inviteUsersToRoom(selections: Set) { - _viewEvents.post(InviteUsersToRoomViewEvents.Loading) - - Observable.fromIterable(selections).flatMapCompletable { user -> - when (user) { - is PendingSelection.UserPendingSelection -> room.rx().invite(user.user.userId, null) - is PendingSelection.ThreePidPendingSelection -> room.rx().invite3pid(user.threePid) - } - }.subscribe( - { - val successMessage = when (selections.size) { - 1 -> stringProvider.getString(R.string.invitation_sent_to_one_user, - selections.first().getBestName()) - 2 -> stringProvider.getString(R.string.invitations_sent_to_two_users, - selections.first().getBestName(), - selections.last().getBestName()) - else -> stringProvider.getQuantityString(R.plurals.invitations_sent_to_one_and_more_users, - selections.size - 1, - selections.first().getBestName(), - selections.size - 1) + viewModelScope.launch { + _viewEvents.post(InviteUsersToRoomViewEvents.Loading) + selections.asFlow() + .map { user -> + when (user) { + is PendingSelection.UserPendingSelection -> room.invite(user.user.userId, null) + is PendingSelection.ThreePidPendingSelection -> room.invite3pid(user.threePid) + } } - _viewEvents.post(InviteUsersToRoomViewEvents.Success(successMessage)) - }, - { - _viewEvents.post(InviteUsersToRoomViewEvents.Failure(it)) - }) - .disposeOnClear() + .catch { cause -> + _viewEvents.post(InviteUsersToRoomViewEvents.Failure(cause)) + } + .collect { + val successMessage = when (selections.size) { + 1 -> stringProvider.getString(R.string.invitation_sent_to_one_user, + selections.first().getBestName()) + 2 -> stringProvider.getString(R.string.invitations_sent_to_two_users, + selections.first().getBestName(), + selections.last().getBestName()) + else -> stringProvider.getQuantityString(R.plurals.invitations_sent_to_one_and_more_users, + selections.size - 1, + selections.first().getBestName(), + selections.size - 1) + } + _viewEvents.post(InviteUsersToRoomViewEvents.Success(successMessage)) + } + } } fun getUserIdsOfRoomMembers(): Set { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index f8d2f8315f..56c3940523 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -16,14 +16,13 @@ package im.vector.app.features.roomprofile.permissions -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsObservableFactory @@ -33,8 +32,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialState: RoomPermissionsViewState, private val session: Session) @@ -62,7 +61,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat } private fun observeRoomSummary() { - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy( diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index f40079c615..8eb9e2cdf3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -51,15 +51,19 @@ import im.vector.app.databinding.DialogChangePasswordBinding import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs import im.vector.app.features.workers.signout.SignOutUiWorker -import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.failure.isInvalidPassword import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import java.io.File import java.util.UUID import javax.inject.Inject @@ -118,29 +122,29 @@ class VectorSettingsGeneralFragment @Inject constructor( } private fun observeUserAvatar() { - session.rx() + session.flow() .liveUser(session.myUserId) .unwrap() - .distinctUntilChanged { user -> user.avatarUrl } - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { mUserAvatarPreference.refreshAvatar(it) } - .disposeOnDestroyView() + .distinctUntilChangedBy { user -> user.avatarUrl } + .onEach { + mUserAvatarPreference.refreshAvatar(it) + } + .launchIn(viewLifecycleOwner.lifecycleScope) } private fun observeUserDisplayName() { - session.rx() + session.flow() .liveUser(session.myUserId) .unwrap() .map { it.displayName ?: "" } .distinctUntilChanged() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { displayName -> + .onEach { displayName -> mDisplayNamePreference.let { it.summary = displayName it.text = displayName } } - .disposeOnDestroyView() + .launchIn(viewLifecycleOwner.lifecycleScope) } override fun bindPref() { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index c42e2440c1..d226b14b23 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -16,23 +16,23 @@ package im.vector.app.features.settings.devtools -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow data class AccountDataViewState( val accountData: Async> = Uninitialized @@ -43,7 +43,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A : VectorViewModel(initialState) { init { - session.rx().liveUserAccountData(emptySet()) + session.flow().liveUserAccountData(emptySet()) .execute { copy(accountData = it) } @@ -66,7 +66,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A fun create(initialState: AccountDataViewState): AccountDataViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: AccountDataViewState): AccountDataViewModel? { From d63e1ecfeacd0f0dc064ad66dc970abe3be93b57 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 17:08:09 +0200 Subject: [PATCH 006/144] Mavericks 2: replacing rx by flow --- .../home/room/detail/RoomDetailViewModel.kt | 12 ++-- .../action/MessageActionsViewModel.kt | 16 ++--- ...leFactory.kt => PowerLevelsFlowFactory.kt} | 19 +++--- .../RoomMemberProfileViewModel.kt | 52 ++++++++-------- .../roomprofile/RoomProfileViewModel.kt | 12 ++-- .../roomprofile/alias/RoomAliasViewModel.kt | 33 +++++----- .../banned/RoomBannedMemberListViewModel.kt | 10 ++-- .../members/RoomMemberListViewModel.kt | 14 ++--- .../permissions/RoomPermissionsViewModel.kt | 13 ++-- .../settings/RoomSettingsViewModel.kt | 60 +++++++++---------- .../app/features/spaces/SpaceMenuViewModel.kt | 13 ++-- .../spaces/explore/SpaceDirectoryViewModel.kt | 11 ++-- .../spaces/share/ShareSpaceViewModel.kt | 12 ++-- 13 files changed, 137 insertions(+), 140 deletions(-) rename vector/src/main/java/im/vector/app/features/powerlevel/{PowerLevelsObservableFactory.kt => PowerLevelsFlowFactory.kt} (73%) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index c64fb89f28..b35e7c2607 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -55,7 +55,7 @@ import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandle import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever import im.vector.app.features.home.room.typing.TypingHelper -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences @@ -66,9 +66,10 @@ import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.commonmark.parser.Parser @@ -238,8 +239,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observePowerLevel() { - PowerLevelsObservableFactory(room).createObservable() - .subscribe { + PowerLevelsFlowFactory(room).createFlow() + .onEach { val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId) val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId) @@ -252,8 +253,7 @@ class RoomDetailViewModel @AssistedInject constructor( isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall ) } - } - .disposeOnClear() + }.launchIn(viewModelScope) } private fun observeActiveRoomWidgets() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index fd80a56870..1723e893ad 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -18,7 +18,6 @@ package im.vector.app.features.home.room.detail.timeline.action import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext -import com.jakewharton.rxrelay2.BehaviorRelay import dagger.Lazy import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -33,14 +32,14 @@ import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormat import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.html.VectorHtmlCompressor -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.reactions.data.EmojiDataSource import im.vector.app.features.settings.VectorPreferences -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.switchMap +import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState @@ -61,8 +60,6 @@ import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import java.util.ArrayList /** * Information related to an event and used to display preview in contextual bottom sheet. @@ -125,8 +122,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted if (room == null) { return } - PowerLevelsObservableFactory(room).createObservable() - .subscribe { + PowerLevelsFlowFactory(room).createFlow() + .onEach { val powerLevelsHelper = PowerLevelsHelper(it) val canReact = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.REACTION) val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId) @@ -135,8 +132,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted setState { copy(actionPermissions = permissions) } - } - .disposeOnClear() + }.launchIn(viewModelScope) } private fun observeEvent() { diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsObservableFactory.kt b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt similarity index 73% rename from vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsObservableFactory.kt rename to vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt index a9d4578bf0..767d6f1ba7 100644 --- a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsObservableFactory.kt +++ b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt @@ -16,23 +16,24 @@ package im.vector.app.features.powerlevel -import io.reactivex.Observable -import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOn import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.rx.mapOptional -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap -class PowerLevelsObservableFactory(private val room: Room) { +class PowerLevelsFlowFactory(private val room: Room) { - fun createObservable(): Observable { - return room.rx() + fun createFlow(): Flow { + return room.flow() .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) - .observeOn(Schedulers.computation()) + .flowOn(Dispatchers.Default) .mapOptional { it.content.toModel() } .unwrap() } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 4b57bdc6aa..ace53c60b7 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.roommemberprofile -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -32,10 +31,15 @@ import im.vector.app.R import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import io.reactivex.Observable import io.reactivex.functions.BiFunction import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.combineLatest +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.query.QueryStringValue @@ -54,6 +58,8 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap @@ -286,11 +292,11 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } private fun observeRoomSummaryAndPowerLevels(room: Room) { - val roomSummaryLive = room.rx().liveRoomSummary().unwrap() - val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable() + val roomSummaryLive = room.flow().liveRoomSummary().unwrap() + val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive - .subscribe { + .onEach { val powerLevelsHelper = PowerLevelsHelper(it) val permissions = ActionPermissions( canKick = powerLevelsHelper.isUserAbleToKick(session.myUserId), @@ -298,30 +304,26 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId), canEditPowerLevel = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_POWER_LEVELS) ) - setState { copy(powerLevelsContent = it, actionPermissions = permissions) } - } - .disposeOnClear() + setState { + copy(powerLevelsContent = it, actionPermissions = permissions) + } + }.launchIn(viewModelScope) roomSummaryLive.execute { copy(isRoomEncrypted = it.invoke()?.isEncrypted == true) } - Observable - .combineLatest( - roomSummaryLive, - powerLevelsContentLive, - BiFunction { roomSummary, powerLevelsContent -> - val roomName = roomSummary.toMatrixItem().getBestName() - val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) - when (val userPowerLevel = powerLevelsHelper.getUserRole(initialState.userId)) { - Role.Admin -> stringProvider.getString(R.string.room_member_power_level_admin_in, roomName) - Role.Moderator -> stringProvider.getString(R.string.room_member_power_level_moderator_in, roomName) - Role.Default -> stringProvider.getString(R.string.room_member_power_level_default_in, roomName) - is Role.Custom -> stringProvider.getString(R.string.room_member_power_level_custom_in, userPowerLevel.value, roomName) - } - } - ).execute { - copy(userPowerLevelString = it) - } + roomSummaryLive.combine(powerLevelsContentLive){roomSummary, powerLevelsContent -> + val roomName = roomSummary.toMatrixItem().getBestName() + val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) + when (val userPowerLevel = powerLevelsHelper.getUserRole(initialState.userId)) { + Role.Admin -> stringProvider.getString(R.string.room_member_power_level_admin_in, roomName) + Role.Moderator -> stringProvider.getString(R.string.room_member_power_level_moderator_in, roomName) + Role.Default -> stringProvider.getString(R.string.room_member_power_level_default_in, roomName) + is Role.Custom -> stringProvider.getString(R.string.room_member_power_level_custom_in, userPowerLevel.value, roomName) + } + }.execute { + copy(userPowerLevelString = it) + } } private fun handleIgnoreAction() = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index d35e8f3ad5..e6b416bcb6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.roomprofile -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -29,7 +28,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.ShortcutCreator -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue @@ -109,16 +108,15 @@ class RoomProfileViewModel @AssistedInject constructor( } private fun observePermissions() { - PowerLevelsObservableFactory(room) - .createObservable() - .subscribe { + PowerLevelsFlowFactory(room) + .createFlow() + .setOnEach { val powerLevelsHelper = PowerLevelsHelper(it) val permissions = RoomProfileViewState.ActionPermissions( canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) ) - setState { copy(actionPermissions = permissions) } + copy(actionPermissions = permissions) } - .disposeOnClear() } override fun handle(action: RoomProfileAction) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index aa9981997c..7ebd82a5c7 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.alias -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -29,7 +28,9 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getDomain import org.matrix.android.sdk.api.query.QueryStringValue @@ -38,7 +39,9 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper -import org.matrix.android.sdk.rx.mapOptional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap @@ -138,9 +141,9 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun observePowerLevel() { - PowerLevelsObservableFactory(room) - .createObservable() - .subscribe { + PowerLevelsFlowFactory(room) + .createFlow() + .onEach { val powerLevelsHelper = PowerLevelsHelper(it) val permissions = RoomAliasViewState.ActionPermissions( canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend( @@ -163,27 +166,23 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo publishManuallyState = newPublishManuallyState ) } - } - .disposeOnClear() + }.launchIn(viewModelScope) } /** * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ private fun observeRoomCanonicalAlias() { - room.rx() + room.flow() .liveStateEvent(EventType.STATE_ROOM_CANONICAL_ALIAS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() - .subscribe { - setState { - copy( - canonicalAlias = it.canonicalAlias, - alternativeAliases = it.alternativeAliases.orEmpty().sorted() - ) - } + .setOnEach { + copy( + canonicalAlias = it.canonicalAlias, + alternativeAliases = it.alternativeAliases.orEmpty().sorted() + ) } - .disposeOnClear() } override fun handle(action: RoomAliasAction) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 1b64261fcb..450c4ee545 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.banned -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -27,7 +26,7 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue @@ -70,14 +69,13 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initia ) } - val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable() + val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive - .subscribe { + .setOnEach { val powerLevelsHelper = PowerLevelsHelper(it) - setState { copy(canUserBan = powerLevelsHelper.isUserAbleToBan(session.myUserId)) } + copy(canUserBan = powerLevelsHelper.isUserAbleToBan(session.myUserId)) } - .disposeOnClear() } companion object : MvRxViewModelFactory { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 5c6ff48403..26f2bc4ebe 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.members -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory @@ -27,9 +26,11 @@ import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse @@ -129,8 +130,8 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } private fun observePowerLevel() { - PowerLevelsObservableFactory(room).createObservable() - .subscribe { + PowerLevelsFlowFactory(room).createFlow() + .onEach { val permissions = ActionPermissions( canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId), canRevokeThreePidInvite = PowerLevelsHelper(it).isUserAllowedToSend( @@ -142,8 +143,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState setState { copy(actionsPermissions = permissions) } - } - .disposeOnClear() + }.launchIn(viewModelScope) } private fun observeRoomSummary() { @@ -192,7 +192,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState override fun handle(action: RoomMemberListAction) { when (action) { is RoomMemberListAction.RevokeThreePidInvite -> handleRevokeThreePidInvite(action) - is RoomMemberListAction.FilterMemberList -> handleFilterMemberList(action) + is RoomMemberListAction.FilterMemberList -> handleFilterMemberList(action) }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index 56c3940523..92df11ff57 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -25,7 +25,9 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -71,9 +73,9 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat } private fun observePowerLevel() { - PowerLevelsObservableFactory(room) - .createObservable() - .subscribe { powerLevelContent -> + PowerLevelsFlowFactory(room) + .createFlow() + .onEach { powerLevelContent -> val powerLevelsHelper = PowerLevelsHelper(powerLevelContent) val permissions = RoomPermissionsViewState.ActionPermissions( canChangePowerLevels = powerLevelsHelper.isUserAllowedToSend( @@ -88,8 +90,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat currentPowerLevelsContent = Success(powerLevelContent) ) } - } - .disposeOnClear() + }.launchIn(viewModelScope) } override fun handle(action: RoomPermissionsAction) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index e872a04d80..112fe39903 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -26,10 +26,13 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.settings.VectorPreferences import io.reactivex.Completable import io.reactivex.Observable +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -41,9 +44,10 @@ import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper -import org.matrix.android.sdk.rx.mapOptional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState, private val vectorPreferences: VectorPreferences, @@ -123,7 +127,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeRoomSummary() { - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> val roomSummary = async.invoke() @@ -134,10 +138,10 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: ) } - val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable() + val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive - .subscribe { + .onEach { val powerLevelsHelper = PowerLevelsHelper(it) val permissions = RoomSettingsViewState.ActionPermissions( canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR), @@ -152,62 +156,56 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: canAddChildren = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) ) - setState { copy(actionPermissions = permissions) } - } - .disposeOnClear() + setState { + copy(actionPermissions = permissions) + } + }.launchIn(viewModelScope) } private fun observeRoomHistoryVisibility() { - room.rx() + room.flow() .liveStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() - .subscribe { - it.historyVisibility?.let { - setState { copy(currentHistoryVisibility = it) } - } + .mapNotNull { it.historyVisibility } + .setOnEach { + copy(currentHistoryVisibility = it) } - .disposeOnClear() } private fun observeJoinRule() { - room.rx() + room.flow() .liveStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() - .subscribe { - it.joinRules?.let { - setState { copy(currentRoomJoinRules = it) } - } + .mapNotNull { it.joinRules } + .setOnEach { + copy(currentRoomJoinRules = it) } - .disposeOnClear() } private fun observeGuestAccess() { - room.rx() + room.flow() .liveStateEvent(EventType.STATE_ROOM_GUEST_ACCESS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() - .subscribe { - it.guestAccess?.let { - setState { copy(currentGuestAccess = it) } - } + .mapNotNull { it.guestAccess } + .setOnEach { + copy(currentGuestAccess = it) } - .disposeOnClear() } /** * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ private fun observeRoomAvatar() { - room.rx() + room.flow() .liveStateEvent(EventType.STATE_ROOM_AVATAR, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() - .subscribe { - setState { copy(currentRoomAvatarUrl = it.avatarUrl) } + .setOnEach { + copy(currentRoomAvatarUrl = it.avatarUrl) } - .disposeOnClear() } override fun handle(action: RoomSettingsAction) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index 24ca218942..1627fdacae 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -30,8 +30,10 @@ import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.session.coroutineScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.session.Session @@ -87,9 +89,9 @@ class SpaceMenuViewModel @AssistedInject constructor( } }.disposeOnClear() - PowerLevelsObservableFactory(room) - .createObservable() - .subscribe { + PowerLevelsFlowFactory(room) + .createFlow() + .onEach { val powerLevelsHelper = PowerLevelsHelper(it) val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId) @@ -114,8 +116,7 @@ class SpaceMenuViewModel @AssistedInject constructor( isLastAdmin = isLastAdmin ) } - } - .disposeOnClear() + }.launchIn(viewModelScope) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index 1006f5a570..f25241d2dc 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.spaces.explore -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -29,8 +28,10 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -83,17 +84,17 @@ class SpaceDirectoryViewModel @AssistedInject constructor( private fun observePermissions() { val room = session.getRoom(initialState.spaceId) ?: return - val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable() + val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive - .subscribe { + .onEach { val powerLevelsHelper = PowerLevelsHelper(it) setState { copy(canAddRooms = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD)) } } - .disposeOnClear() + .launchIn(viewModelScope) } private fun refreshFromApi(rootId: String?) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt index dfce534a7a..2cc4fd388c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt @@ -26,7 +26,9 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper @@ -63,9 +65,9 @@ class ShareSpaceViewModel @AssistedInject constructor( private fun observePowerLevel() { val room = session.getRoom(initialState.spaceId) ?: return - PowerLevelsObservableFactory(room) - .createObservable() - .subscribe { powerLevelContent -> + PowerLevelsFlowFactory(room) + .createFlow() + .onEach { powerLevelContent -> val powerLevelsHelper = PowerLevelsHelper(powerLevelContent) setState { copy( @@ -73,7 +75,7 @@ class ShareSpaceViewModel @AssistedInject constructor( ) } } - .disposeOnClear() + .launchIn(viewModelScope) } override fun handle(action: ShareSpaceAction) { From 606cddc82683687eafa4f502ffb7c25cc61cbc19 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 17:11:53 +0200 Subject: [PATCH 007/144] Mavericks 2: replace MvRxViewModelFactory by MavericksViewModelFactory --- .../ElementFeature/root/src/app_package/ViewModel.kt.ftl | 4 ++-- .../main/java/im/vector/app/features/auth/ReAuthViewModel.kt | 4 ++-- .../java/im/vector/app/features/call/VectorCallViewModel.kt | 4 ++-- .../vector/app/features/call/conference/JitsiCallViewModel.kt | 4 ++-- .../app/features/call/transfer/CallTransferViewModel.kt | 4 ++-- .../vector/app/features/contactsbook/ContactsBookViewModel.kt | 4 ++-- .../app/features/createdirect/CreateDirectRoomViewModel.kt | 4 ++-- .../crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt | 4 ++-- .../app/features/crypto/quads/SharedSecureStorageViewModel.kt | 4 ++-- .../app/features/crypto/recover/BootstrapSharedViewModel.kt | 4 ++-- .../crypto/verification/VerificationBottomSheetViewModel.kt | 4 ++-- .../verification/choose/VerificationChooseMethodViewModel.kt | 4 ++-- .../conclusion/VerificationConclusionViewModel.kt | 4 ++-- .../verification/emoji/VerificationEmojiCodeViewModel.kt | 4 ++-- .../im/vector/app/features/devtools/RoomDevToolViewModel.kt | 4 ++-- .../app/features/discovery/DiscoverySettingsViewModel.kt | 4 ++-- .../features/discovery/change/SetIdentityServerViewModel.kt | 4 ++-- .../java/im/vector/app/features/home/HomeActivityViewModel.kt | 4 ++-- .../java/im/vector/app/features/home/HomeDetailViewModel.kt | 4 ++-- .../im/vector/app/features/home/PromoteRestrictedViewModel.kt | 4 ++-- .../app/features/home/UnknownDeviceDetectorSharedViewModel.kt | 4 ++-- .../vector/app/features/home/UnreadMessagesSharedViewModel.kt | 4 ++-- .../features/home/room/breadcrumbs/BreadcrumbsViewModel.kt | 4 ++-- .../app/features/home/room/detail/RoomDetailViewModel.kt | 4 ++-- .../app/features/home/room/detail/search/SearchViewModel.kt | 4 ++-- .../room/detail/timeline/action/MessageActionsViewModel.kt | 4 ++-- .../detail/timeline/edithistory/ViewEditHistoryViewModel.kt | 4 ++-- .../room/detail/timeline/reactions/ViewReactionsViewModel.kt | 4 ++-- .../features/home/room/detail/upgrade/MigrateRoomViewModel.kt | 4 ++-- .../features/homeserver/HomeServerCapabilitiesViewModel.kt | 4 ++-- .../main/java/im/vector/app/features/login/LoginViewModel.kt | 4 ++-- .../java/im/vector/app/features/login2/LoginViewModel2.kt | 4 ++-- .../app/features/login2/created/AccountCreatedViewModel.kt | 4 ++-- .../app/features/matrixto/MatrixToBottomSheetViewModel.kt | 4 ++-- .../im/vector/app/features/rageshake/BugReportViewModel.kt | 4 ++-- .../app/features/reactions/EmojiSearchResultViewModel.kt | 4 ++-- .../app/features/room/RequireActiveMembershipViewModel.kt | 4 ++-- .../app/features/roomdirectory/RoomDirectoryViewModel.kt | 4 ++-- .../features/roomdirectory/createroom/CreateRoomViewModel.kt | 4 ++-- .../roomdirectory/picker/RoomDirectoryPickerViewModel.kt | 4 ++-- .../roomdirectory/roompreview/RoomPreviewViewModel.kt | 4 ++-- .../features/roommemberprofile/RoomMemberProfileViewModel.kt | 4 ++-- .../devices/DeviceListBottomSheetViewModel.kt | 4 ++-- .../vector/app/features/roomprofile/RoomProfileViewModel.kt | 4 ++-- .../app/features/roomprofile/alias/RoomAliasViewModel.kt | 4 ++-- .../roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt | 4 ++-- .../roomprofile/banned/RoomBannedMemberListViewModel.kt | 4 ++-- .../features/roomprofile/members/RoomMemberListViewModel.kt | 4 ++-- .../notifications/RoomNotificationSettingsViewModel.kt | 4 ++-- .../roomprofile/permissions/RoomPermissionsViewModel.kt | 4 ++-- .../features/roomprofile/settings/RoomSettingsViewModel.kt | 4 ++-- .../advanced/RoomJoinRuleChooseRestrictedViewModel.kt | 4 ++-- .../app/features/roomprofile/uploads/RoomUploadsViewModel.kt | 4 ++-- .../account/deactivation/DeactivateAccountViewModel.kt | 4 ++-- .../settings/crosssigning/CrossSigningSettingsViewModel.kt | 4 ++-- .../devices/DeviceVerificationInfoBottomSheetViewModel.kt | 4 ++-- .../vector/app/features/settings/devices/DevicesViewModel.kt | 4 ++-- .../app/features/settings/devtools/AccountDataViewModel.kt | 1 - .../settings/devtools/GossipingEventsPaperTrailViewModel.kt | 4 ++-- .../app/features/settings/devtools/KeyRequestListViewModel.kt | 4 ++-- .../app/features/settings/devtools/KeyRequestViewModel.kt | 4 ++-- .../settings/homeserver/HomeserverSettingsViewModel.kt | 4 ++-- .../app/features/settings/ignored/IgnoredUsersViewModel.kt | 4 ++-- .../app/features/settings/locale/LocalePickerViewModel.kt | 4 ++-- .../app/features/settings/push/PushGatewaysViewModel.kt | 4 ++-- .../vector/app/features/settings/push/PushRulesViewModel.kt | 4 ++-- .../features/settings/threepids/ThreePidsSettingsViewModel.kt | 4 ++-- .../im/vector/app/features/share/IncomingShareViewModel.kt | 4 ++-- .../vector/app/features/signout/soft/SoftLogoutViewModel.kt | 4 ++-- .../java/im/vector/app/features/spaces/SpaceMenuViewModel.kt | 4 ++-- .../java/im/vector/app/features/spaces/SpacesListViewModel.kt | 4 ++-- .../vector/app/features/spaces/create/CreateSpaceViewModel.kt | 4 ++-- .../app/features/spaces/explore/SpaceDirectoryViewModel.kt | 4 ++-- .../features/spaces/invite/SpaceInviteBottomSheetViewModel.kt | 4 ++-- .../app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt | 4 ++-- .../app/features/spaces/manage/SpaceAddRoomsViewModel.kt | 4 ++-- .../app/features/spaces/manage/SpaceManageRoomsViewModel.kt | 4 ++-- .../app/features/spaces/manage/SpaceManageSharedViewModel.kt | 4 ++-- .../vector/app/features/spaces/people/SpacePeopleViewModel.kt | 4 ++-- .../app/features/spaces/preview/SpacePreviewViewModel.kt | 4 ++-- .../vector/app/features/spaces/share/ShareSpaceViewModel.kt | 4 ++-- .../java/im/vector/app/features/terms/ReviewTermsViewModel.kt | 4 ++-- .../vector/app/features/usercode/UserCodeSharedViewModel.kt | 4 ++-- .../im/vector/app/features/userdirectory/UserListViewModel.kt | 4 ++-- .../java/im/vector/app/features/widgets/WidgetViewModel.kt | 4 ++-- .../widgets/permissions/RoomWidgetPermissionViewModel.kt | 4 ++-- .../features/workers/signout/ServerBackupStatusViewModel.kt | 4 ++-- .../app/features/workers/signout/SignoutCheckViewModel.kt | 4 ++-- 88 files changed, 174 insertions(+), 175 deletions(-) diff --git a/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl b/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl index 8c25f9f9a8..64e6a0f83f 100644 --- a/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl +++ b/tools/templates/ElementFeature/root/src/app_package/ViewModel.kt.ftl @@ -2,7 +2,7 @@ package ${escapeKotlinIdentifiers(packageName)} import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -27,7 +27,7 @@ class ${viewModelClass} @AssistedInject constructor(@Assisted initialState: ${vi fun create(initialState: ${viewStateClass}): ${viewModelClass} } - companion object : MvRxViewModelFactory<${viewModelClass}, ${viewStateClass}> { + companion object : MavericksViewModelFactory<${viewModelClass}, ${viewStateClass}> { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: ${viewStateClass}): ${viewModelClass}? { diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt index edbceae20f..449270644e 100644 --- a/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.auth import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -39,7 +39,7 @@ class ReAuthViewModel @AssistedInject constructor( fun create(initialState: ReAuthState): ReAuthViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: ReAuthState): ReAuthViewModel? { val factory = when (viewModelContext) { diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 90df595f8f..d8aaca9bd8 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.call import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -345,7 +345,7 @@ class VectorCallViewModel @AssistedInject constructor( fun create(initialState: VectorCallViewState): VectorCallViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: VectorCallViewState): VectorCallViewModel { diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt index 0fc85cb58c..b4bb01d374 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.call.conference import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -140,7 +140,7 @@ class JitsiCallViewModel @AssistedInject constructor( } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { const val ENABLE_VIDEO_OPTION = "ENABLE_VIDEO_OPTION" diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt index 0217551260..8b66ec736c 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.call.transfer import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -45,7 +45,7 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: fun create(initialState: CallTransferViewState): CallTransferViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: CallTransferViewState): CallTransferViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index cfbdef8ffb..e37fc94b51 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -49,7 +49,7 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted fun create(initialState: ContactsBookViewState): ContactsBookViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: ContactsBookViewState): ContactsBookViewModel? { val factory = when (viewModelContext) { diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt index 88e8d68155..21bf8016ae 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -49,7 +49,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted fun create(initialState: CreateDirectRoomViewState): CreateDirectRoomViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: CreateDirectRoomViewState): CreateDirectRoomViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt index cb8a6ce4e9..bba472f407 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.crypto.keysbackup.settings import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -45,7 +45,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS fun create(initialState: KeysBackupSettingViewState): KeysBackupSettingsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: KeysBackupSettingViewState): KeysBackupSettingsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 49059391f0..ecaf89a1e5 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -22,7 +22,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -322,7 +322,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( _viewEvents.post(SharedSecureStorageViewEvent.Dismiss) } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: SharedSecureStorageViewState): SharedSecureStorageViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index 866a87d7f9..403ba8c62b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -552,7 +552,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( // Companion, view model assisted creation // ====================================== - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? { val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index b2f259772e..1953f8d367 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -223,7 +223,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( _viewEvents.post(VerificationBottomSheetViewEvents.GoToSettings) } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: VerificationBottomSheetViewState): VerificationBottomSheetViewModel? { val fragment: VerificationBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt index 79878fcf83..038762cda7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.crypto.verification.choose import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -94,7 +94,7 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( super.onCleared() } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: VerificationChooseMethodViewState): VerificationChooseMethodViewModel? { val fragment: VerificationChooseMethodFragment = (viewModelContext as FragmentViewModelContext).fragment() return fragment.verificationChooseMethodViewModelFactory.create(state) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt index 93133184e9..5e03e276c0 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.crypto.verification.conclusion import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents @@ -38,7 +38,7 @@ enum class ConclusionState { class VerificationConclusionViewModel(initialState: VerificationConclusionViewState) : VectorViewModel(initialState) { - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun initialState(viewModelContext: ViewModelContext): VerificationConclusionViewState? { val args = viewModelContext.args() diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt index c8dd9b34d8..dd084ef718 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt @@ -20,7 +20,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -155,7 +155,7 @@ class VerificationEmojiCodeViewModel @AssistedInject constructor( fun create(initialState: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel? { val factory = (viewModelContext as FragmentViewModelContext).fragment().viewModelFactory diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt index cf4b54c7fa..4bab65fd5d 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import com.squareup.moshi.Types @@ -56,7 +56,7 @@ class RoomDevToolViewModel @AssistedInject constructor( fun create(initialState: RoomDevToolViewState): RoomDevToolViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomDevToolViewState): RoomDevToolViewModel { diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 3cd6c31ab2..412a1ab5b4 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -20,7 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -47,7 +47,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( fun create(initialState: DiscoverySettingsState): DiscoverySettingsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: DiscoverySettingsState): DiscoverySettingsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt index 08632a2bd1..275ff67e89 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.discovery.change import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -46,7 +46,7 @@ class SetIdentityServerViewModel @AssistedInject constructor( fun create(initialState: SetIdentityServerState): SetIdentityServerViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun initialState(viewModelContext: ViewModelContext): SetIdentityServerState? { val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 1aa2f59337..297cffd627 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.home import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.MvRx -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -67,7 +67,7 @@ class HomeActivityViewModel @AssistedInject constructor( fun create(initialState: HomeActivityViewState, args: HomeActivityArgs): HomeActivityViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: HomeActivityViewState): HomeActivityViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 460975c2c2..01eb6e3c31 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.home import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -72,7 +72,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho fun create(initialState: HomeDetailViewState): HomeDetailViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun initialState(viewModelContext: ViewModelContext): HomeDetailViewState? { val uiStateRepository = (viewModelContext.activity as HasScreenInjector).injector().uiStateRepository() diff --git a/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt index da29d4c5fc..0c8c9e480c 100644 --- a/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -76,7 +76,7 @@ class PromoteRestrictedViewModel @AssistedInject constructor( fun create(initialState: ActiveSpaceViewState): PromoteRestrictedViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: ActiveSpaceViewState): PromoteRestrictedViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index 7b25f20f77..db3317a214 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -20,7 +20,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -70,7 +70,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt index 1e2b9e542a..4c277ecc01 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -65,7 +65,7 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia fun create(initialState: UnreadMessagesState): UnreadMessagesSharedViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: UnreadMessagesState): UnreadMessagesSharedViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index d3825de4ef..a945e4bbb1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.breadcrumbs import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -41,7 +41,7 @@ class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: B fun create(initialState: BreadcrumbsViewState): BreadcrumbsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: BreadcrumbsViewState): BreadcrumbsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index b35e7c2607..f565794094 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -23,7 +23,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -165,7 +165,7 @@ class RoomDetailViewModel @AssistedInject constructor( fun create(initialState: RoomDetailViewState): RoomDetailViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { const val PAGINATION_COUNT = 50 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index 26111e4f2b..e4832b3876 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -50,7 +50,7 @@ class SearchViewModel @AssistedInject constructor( fun create(initialState: SearchViewState): SearchViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: SearchViewState): SearchViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 1723e893ad..5792187eb5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.action import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.Lazy import dagger.assisted.Assisted @@ -89,7 +89,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted fun create(initialState: MessageActionState): MessageActionsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: MessageActionState): MessageActionsViewModel? { val fragment: MessageActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt index 5732326f2e..699f3cf02d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt @@ -19,7 +19,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -51,7 +51,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor( fun create(initialState: ViewEditHistoryViewState): ViewEditHistoryViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: ViewEditHistoryViewState): ViewEditHistoryViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt index 6a78161d28..fde823cb35 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home.room.detail.timeline.reactions import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -74,7 +74,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted fun create(initialState: DisplayReactionsViewState): ViewReactionsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: DisplayReactionsViewState): ViewReactionsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt index 3dc5d0e3ba..7b444629fa 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home.room.detail.upgrade import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -55,7 +55,7 @@ class MigrateRoomViewModel @AssistedInject constructor( fun create(initialState: MigrateRoomViewState): MigrateRoomViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: MigrateRoomViewState): MigrateRoomViewModel? { val factory = when (viewModelContext) { diff --git a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt index 083b843b33..eab5ce569a 100644 --- a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.homeserver import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -48,7 +48,7 @@ class HomeServerCapabilitiesViewModel @AssistedInject constructor( fun create(initialState: HomeServerCapabilitiesViewState): HomeServerCapabilitiesViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: HomeServerCapabilitiesViewState): HomeServerCapabilitiesViewModel? { val fragment: UserListFragment = (viewModelContext as FragmentViewModelContext).fragment() diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt index d4fd3101aa..24222e9770 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt @@ -23,7 +23,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -86,7 +86,7 @@ class LoginViewModel @AssistedInject constructor( } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: LoginViewState): LoginViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt index e3f97a1c01..e38fabae5f 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt @@ -22,7 +22,7 @@ import androidx.fragment.app.FragmentActivity import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -86,7 +86,7 @@ class LoginViewModel2 @AssistedInject constructor( } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: LoginViewState2): LoginViewModel2? { diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt index 1acec968b6..c95434a548 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.login2.created import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -43,7 +43,7 @@ class AccountCreatedViewModel @AssistedInject constructor( fun create(initialState: AccountCreatedViewState): AccountCreatedViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: AccountCreatedViewState): AccountCreatedViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt index 566cf769f2..eeb4814f1b 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -245,7 +245,7 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor( return session.peekRoom(roomIdOrAlias) } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: MatrixToBottomSheetState): MatrixToBottomSheetViewModel? { val fragment: MatrixToBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt index c71a89553e..a1ec6b2e76 100644 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.rageshake import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -40,7 +40,7 @@ class BugReportViewModel @AssistedInject constructor( fun create(initialState: BugReportState): BugReportViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: BugReportState): BugReportViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt index 04ee95f68e..7f54b96c36 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.reactions import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -44,7 +44,7 @@ class EmojiSearchResultViewModel @AssistedInject constructor( fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index c8b0037311..2b299b014e 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.room import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted @@ -54,7 +54,7 @@ class RequireActiveMembershipViewModel @AssistedInject constructor( fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RequireActiveMembershipViewState): RequireActiveMembershipViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt index dc1cbfc58d..8955167e50 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.appendAt @@ -53,7 +53,7 @@ class RoomDirectoryViewModel @AssistedInject constructor( fun create(initialState: PublicRoomsViewState): RoomDirectoryViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { private const val PUBLIC_ROOMS_LIMIT = 20 @JvmStatic diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt index 9a9812933b..f3d45a1ec5 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -21,7 +21,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -116,7 +116,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: CreateRoomViewState): CreateRoomViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt index 2558715834..a5f5801885 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -50,7 +50,7 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor( fun create(initialState: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index a559b0554d..6e70ff2593 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.roomdirectory.roompreview import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -52,7 +52,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini fun create(initialState: RoomPreviewViewState): RoomPreviewViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomPreviewViewState): RoomPreviewViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index ace53c60b7..ab5337d7cd 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -20,7 +20,7 @@ package im.vector.app.features.roommemberprofile import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -73,7 +73,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v fun create(initialState: RoomMemberProfileViewState): RoomMemberProfileViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomMemberProfileViewState): RoomMemberProfileViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index 15c62af35b..2baf27e694 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -20,7 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -108,7 +108,7 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: DeviceListViewState): DeviceListBottomSheetViewModel? { val fragment: DeviceListBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index e6b416bcb6..c8570d67dc 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roomprofile import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -57,7 +57,7 @@ class RoomProfileViewModel @AssistedInject constructor( fun create(initialState: RoomProfileViewState): RoomProfileViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomProfileViewState): RoomProfileViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 7ebd82a5c7..cb26d6407b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.roomprofile.alias import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -54,7 +54,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo fun create(initialState: RoomAliasViewState): RoomAliasViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomAliasViewState): RoomAliasViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt index e762b52025..d294706096 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.roomprofile.alias.detail import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -36,7 +36,7 @@ class RoomAliasBottomSheetViewModel @AssistedInject constructor( fun create(initialState: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 450c4ee545..b92d1a4bd3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.banned import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -78,7 +78,7 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initia } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomBannedMemberListViewState): RoomBannedMemberListViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 26f2bc4ebe..cd6d6f1610 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roomprofile.members import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -60,7 +60,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomMemberListViewState): RoomMemberListViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index dd0535f51b..51cd4a4ecc 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.roomprofile.notifications import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -41,7 +41,7 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( fun create(initialState: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index 92df11ff57..71e8a313b5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.roomprofile.permissions import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -46,7 +46,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat fun create(initialState: RoomPermissionsViewState): RoomPermissionsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomPermissionsViewState): RoomPermissionsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 112fe39903..4491ff8606 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.roomprofile.settings import androidx.core.net.toFile import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -59,7 +59,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomSettingsViewState): RoomSettingsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt index eaa435f853..d6da4be3e9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt @@ -23,7 +23,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -390,7 +390,7 @@ class RoomJoinRuleChooseRestrictedViewModel @AssistedInject constructor( } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: RoomJoinRuleChooseRestrictedState) : RoomJoinRuleChooseRestrictedViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index 1d6b056816..2ae27f7799 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -45,7 +45,7 @@ class RoomUploadsViewModel @AssistedInject constructor( fun create(initialState: RoomUploadsViewState): RoomUploadsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomUploadsViewState): RoomUploadsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt index bc030775e0..5b6b9bdc38 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.account.deactivation import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -114,7 +114,7 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: DeactivateAccountViewState): DeactivateAccountViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index 8bdf97b6ec..6f1058ec7c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.crosssigning import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -159,7 +159,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( _viewEvents.post(CrossSigningSettingsViewEvents.Failure(Exception(stringProvider.getString(R.string.failed_to_initialize_cross_signing)))) } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: CrossSigningSettingsViewState): CrossSigningSettingsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index ee5b0a6092..975236beb1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.devices import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -88,7 +88,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: DeviceVerificationInfoBottomSheetViewState) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index ec2d6dd53e..04e743b8df 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -22,7 +22,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -97,7 +97,7 @@ class DevicesViewModel @AssistedInject constructor( fun create(initialState: DevicesViewState): DevicesViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: DevicesViewState): DevicesViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index d226b14b23..e5739ec446 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -20,7 +20,6 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt index 25834e5580..e07da1065c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -63,7 +63,7 @@ class GossipingEventsPaperTrailViewModel @AssistedInject constructor(@Assisted i fun create(initialState: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt index b4e872000e..362f2768fc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt @@ -21,7 +21,7 @@ import androidx.paging.PagedList import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -71,7 +71,7 @@ class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState fun create(initialState: KeyRequestListViewState): KeyRequestListViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: KeyRequestListViewState): KeyRequestListViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt index e1554cb0a4..8925921b81 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt @@ -23,7 +23,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -60,7 +60,7 @@ class KeyRequestViewModel @AssistedInject constructor( fun create(initialState: KeyRequestViewState): KeyRequestViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: KeyRequestViewState): KeyRequestViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt index 623ac37aa4..91ad34f1b6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -41,7 +41,7 @@ class HomeserverSettingsViewModel @AssistedInject constructor( fun create(initialState: HomeServerSettingsViewState): HomeserverSettingsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: HomeServerSettingsViewState): HomeserverSettingsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index 34d2bc4821..1cf150395a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -22,7 +22,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -54,7 +54,7 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: fun create(initialState: IgnoredUsersViewState): IgnoredUsersViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: IgnoredUsersViewState): IgnoredUsersViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt index 2e59b0ef7d..ef15049a8f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.settings.locale import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -53,7 +53,7 @@ class LocalePickerViewModel @AssistedInject constructor( } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: LocalePickerViewState): LocalePickerViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt index 509e38b0f3..bc159e3fe0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -46,7 +46,7 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: fun create(initialState: PushGatewayViewState): PushGatewaysViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: PushGatewayViewState): PushGatewaysViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt index dd21cfb5c9..fbd28630ef 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.settings.push import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyAction @@ -31,7 +31,7 @@ data class PushRulesViewState( class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel(initialState) { - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun initialState(viewModelContext: ViewModelContext): PushRulesViewState? { val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index ac565e72a1..4d94389850 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -84,7 +84,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( fun create(initialState: ThreePidsSettingsViewState): ThreePidsSettingsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: ThreePidsSettingsViewState): ThreePidsSettingsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index 4d211d11de..665d3b28fa 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.share import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted @@ -48,7 +48,7 @@ class IncomingShareViewModel @AssistedInject constructor( fun create(initialState: IncomingShareViewState): IncomingShareViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: IncomingShareViewState): IncomingShareViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt index f17847fdfd..4cf20fe551 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.signout.soft import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -51,7 +51,7 @@ class SoftLogoutViewModel @AssistedInject constructor( fun create(initialState: SoftLogoutViewState): SoftLogoutViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun initialState(viewModelContext: ViewModelContext): SoftLogoutViewState? { val activity: SoftLogoutActivity = (viewModelContext as ActivityViewModelContext).activity() diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index 1627fdacae..ad764363db 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -20,7 +20,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -56,7 +56,7 @@ class SpaceMenuViewModel @AssistedInject constructor( fun create(initialState: SpaceMenuState): SpaceMenuViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: SpaceMenuState): SpaceMenuViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index 12c4ddbfc4..dc69fb5ba0 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.spaces import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -71,7 +71,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp fun create(initialState: SpaceListViewState): SpacesListViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: SpaceListViewState): SpacesListViewModel { diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index e6ead2294e..fd385c413a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -93,7 +93,7 @@ class CreateSpaceViewModel @AssistedInject constructor( super.onCleared() } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: CreateSpaceState): CreateSpaceViewModel? { val factory = when (viewModelContext) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index f25241d2dc..d07b486fee 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -20,7 +20,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -55,7 +55,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor( fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpaceDirectoryState): SpaceDirectoryViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt index 55b265f244..00fb6c4054 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -97,7 +97,7 @@ class SpaceInviteBottomSheetViewModel @AssistedInject constructor( fun create(initialState: SpaceInviteBottomSheetState): SpaceInviteBottomSheetViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpaceInviteBottomSheetState): SpaceInviteBottomSheetViewModel? { val factory = when (viewModelContext) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index 7461d09b8b..0daa2522e9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -129,7 +129,7 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt index 9f5cd7d35e..bf062ce0a8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt @@ -23,7 +23,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -132,7 +132,7 @@ class SpaceAddRoomsViewModel @AssistedInject constructor( } } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpaceAddRoomsState): SpaceAddRoomsViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt index b1f6d5c3c3..d36e62db13 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -60,7 +60,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( fun create(initialState: SpaceManageRoomViewState): SpaceManageRoomsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpaceManageRoomViewState): SpaceManageRoomsViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt index 8f23788d19..133054236e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.spaces.manage import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -37,7 +37,7 @@ class SpaceManageSharedViewModel @AssistedInject constructor( fun create(initialState: SpaceManageViewState): SpaceManageSharedViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpaceManageViewState): SpaceManageSharedViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt index 95cf5fb461..efa7d97e9c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -48,7 +48,7 @@ class SpacePeopleViewModel @AssistedInject constructor( fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpacePeopleViewState): SpacePeopleViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt index 0f1afd8371..d71a4bef46 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -62,7 +62,7 @@ class SpacePreviewViewModel @AssistedInject constructor( fun create(initialState: SpacePreviewState): SpacePreviewViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: SpacePreviewState): SpacePreviewViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt index 2cc4fd388c..d96cecbfbb 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.spaces.share import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -42,7 +42,7 @@ class ShareSpaceViewModel @AssistedInject constructor( fun create(initialState: ShareSpaceViewState): ShareSpaceViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: ShareSpaceViewState): ShareSpaceViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt index 4ecad80876..48d6d7dc45 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.terms import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -41,7 +41,7 @@ class ReviewTermsViewModel @AssistedInject constructor( fun create(initialState: ReviewTermsViewState): ReviewTermsViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: ReviewTermsViewState): ReviewTermsViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt index 071044fc8a..63623d519c 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt @@ -19,7 +19,7 @@ package im.vector.app.features.usercode import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -45,7 +45,7 @@ class UserCodeSharedViewModel @AssistedInject constructor( private val directRoomHelper: DirectRoomHelper, private val rawService: RawService) : VectorViewModel(initialState) { - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: UserCodeState): UserCodeSharedViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index dead957795..7eb7ce95ad 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.userdirectory import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay @@ -63,7 +63,7 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User fun create(initialState: UserListViewState): UserListViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: UserListViewState): UserListViewModel? { val factory = when (viewModelContext) { diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index bf27173d83..ced9d45d92 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -22,7 +22,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -60,7 +60,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi fun create(initialState: WidgetViewState): WidgetViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: WidgetViewState): WidgetViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index 844a6619b4..0ed4e7d771 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -18,7 +18,7 @@ package im.vector.app.features.widgets.permissions import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -144,7 +144,7 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index 619f14e559..0076bf580e 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -75,7 +75,7 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS fun create(initialState: ServerBackupStatusViewState): ServerBackupStatusViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: ServerBackupStatusViewState): ServerBackupStatusViewModel? { diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt index 7916caca66..8190a37056 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt @@ -23,7 +23,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext @@ -69,7 +69,7 @@ class SignoutCheckViewModel @AssistedInject constructor( fun create(initialState: SignoutCheckViewState): SignoutCheckViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: SignoutCheckViewState): SignoutCheckViewModel? { From 2ef4cd276bdd8e0b00b98df3a953bd771545c0a5 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 18:04:06 +0200 Subject: [PATCH 008/144] Mavericks 2: replace selectSubscribe by onEach --- .../app/features/call/VectorCallActivity.kt | 2 +- .../contactsbook/ContactsBookViewModel.kt | 2 +- .../createdirect/CreateDirectRoomActivity.kt | 2 +- .../settings/KeysBackupManageActivity.kt | 2 +- .../app/features/home/HomeDetailFragment.kt | 6 +++--- .../detail/JoinReplacementRoomBottomSheet.kt | 2 +- .../home/room/detail/RoomDetailFragment.kt | 8 ++++---- .../home/room/detail/RoomDetailViewModel.kt | 2 +- .../home/room/detail/RoomDetailViewState.kt | 2 +- .../action/MessageActionsViewModel.kt | 4 ++-- .../home/room/list/RoomListFragment.kt | 2 +- .../picker/RoomDirectoryPickerViewModel.kt | 2 +- .../settings/RoomSettingsViewModel.kt | 2 +- .../settings/joinrule/RoomJoinRuleActivity.kt | 2 +- .../spaces/explore/SpaceDirectoryFragment.kt | 2 +- .../spaces/manage/SpaceAddRoomFragment.kt | 20 +++++++++---------- .../spaces/manage/SpaceManageRoomsFragment.kt | 2 +- .../app/features/usercode/UserCodeActivity.kt | 2 +- .../userdirectory/UserListFragment.kt | 2 +- .../app/features/widgets/WidgetActivity.kt | 8 ++++---- 20 files changed, 38 insertions(+), 38 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 64abdef72a..1a6e8d162a 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -152,7 +152,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } .disposeOnDestroy() - callViewModel.selectSubscribe(VectorCallViewState::callId, VectorCallViewState::isVideoCall) { _, isVideoCall -> + callViewModel.onEach(VectorCallViewState::callId, VectorCallViewState::isVideoCall) { _, isVideoCall -> if (isVideoCall) { if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, this, permissionCameraLauncher, R.string.permissions_rationale_msg_camera_and_audio)) { setupRenderersIfNeeded() diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index e37fc94b51..4123841ab9 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -66,7 +66,7 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted init { loadContacts() - selectSubscribe(ContactsBookViewState::searchTerm, ContactsBookViewState::onlyBoundContacts) { _, _ -> + onEach(ContactsBookViewState::searchTerm, ContactsBookViewState::onlyBoundContacts) { _, _ -> updateFilteredMappedContacts() } } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index 752a7d0d83..32395144f2 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -102,7 +102,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Fac ) ) } - viewModel.selectSubscribe(CreateDirectRoomViewState::createAndInviteState) { + viewModel.onEach(CreateDirectRoomViewState::createAndInviteState) { renderCreateAndInviteState(it) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt index c03488ab5d..716f02369a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt @@ -55,7 +55,7 @@ class KeysBackupManageActivity : SimpleFragmentActivity() { } // Observe the deletion of keys backup - viewModel.selectSubscribe(KeysBackupSettingViewState::deleteBackupRequest) { asyncDelete -> + viewModel.onEach(KeysBackupSettingViewState::deleteBackupRequest) { asyncDelete -> when (asyncDelete) { is Fail -> { updateWaitingView(null) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index b6d628071b..790dd6e466 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -134,7 +134,7 @@ class HomeDetailFragment @Inject constructor( views.bottomNavigationView.selectedItemId = it.currentTab.toMenuId() } - viewModel.selectSubscribe(HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod -> + viewModel.onEach(HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod -> when (roomGroupingMethod) { is RoomGroupingMethod.ByLegacyGroup -> { onGroupChange(roomGroupingMethod.groupSummary) @@ -145,11 +145,11 @@ class HomeDetailFragment @Inject constructor( } } - viewModel.selectSubscribe(HomeDetailViewState::currentTab) { currentTab -> + viewModel.onEach(HomeDetailViewState::currentTab) { currentTab -> updateUIForTab(currentTab) } - viewModel.selectSubscribe(HomeDetailViewState::showDialPadTab) { showDialPadTab -> + viewModel.onEach(HomeDetailViewState::showDialPadTab) { showDialPadTab -> updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt index 9cfca9ecd2..7159f7b33a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt @@ -61,7 +61,7 @@ class JoinReplacementRoomBottomSheet : } } - viewModel.selectSubscribe(RoomDetailViewState::joinUpgradedRoomAsync) { joinState -> + viewModel.onEach(RoomDetailViewState::joinUpgradedRoomAsync) { joinState -> when (joinState) { // it should never be Uninitialized Uninitialized, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index c6eda584ad..776b02d814 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -375,13 +375,13 @@ class RoomDetailFragment @Inject constructor( invalidateOptionsMenu() } - roomDetailViewModel.selectSubscribe(RoomDetailViewState::canShowJumpToReadMarker, RoomDetailViewState::unreadState) { _, _ -> + roomDetailViewModel.onEach(RoomDetailViewState::canShowJumpToReadMarker, RoomDetailViewState::unreadState) { _, _ -> updateJumpToReadMarkerViewVisibility() } - roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode, RoomDetailViewState::canSendMessage) { mode, canSend -> + roomDetailViewModel.onEach(RoomDetailViewState::sendMode, RoomDetailViewState::canSendMessage) { mode, canSend -> if (!canSend) { - return@selectSubscribe + return@onEach } when (mode) { is SendMode.REGULAR -> renderRegularMode(mode.text) @@ -391,7 +391,7 @@ class RoomDetailFragment @Inject constructor( } } - roomDetailViewModel.selectSubscribe( + roomDetailViewModel.onEach( RoomDetailViewState::syncState, RoomDetailViewState::incrementalSyncStatus, RoomDetailViewState::pushCounter diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index f565794094..9e1a362fc6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -1576,7 +1576,7 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeUnreadState() { - selectSubscribe(RoomDetailViewState::unreadState) { + onEach(RoomDetailViewState::unreadState) { Timber.v("Unread state: $it") if (it is UnreadState.HasNoUnread) { startTrackingUnreadMessages() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 6dee6d2b00..a2462c798d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -42,7 +42,7 @@ sealed class SendMode(open val text: String) { data class REGULAR( override val text: String, val fromSharing: Boolean, - // This is necessary for forcing refresh on selectSubscribe + // This is necessary for forcing refresh on onEach private val ts: Long = System.currentTimeMillis() ) : SendMode(text) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 5792187eb5..f63366482b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -163,8 +163,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } private fun observeTimelineEventState() { - selectSubscribe(MessageActionState::timelineEvent, MessageActionState::actionPermissions) { timelineEvent, permissions -> - val nonNullTimelineEvent = timelineEvent() ?: return@selectSubscribe + onEach(MessageActionState::timelineEvent, MessageActionState::actionPermissions) { timelineEvent, permissions -> + val nonNullTimelineEvent = timelineEvent() ?: return@onEach eventIdFlow.tryEmit(nonNullTimelineEvent.eventId) setState { copy( diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index 2df90a6c16..19ea856da6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -123,7 +123,7 @@ class RoomListFragment @Inject constructor( .subscribe { handleQuickActions(it) } .disposeOnDestroyView() - roomListViewModel.selectSubscribe(RoomListViewState::roomMembershipChanges) { ms -> + roomListViewModel.onEach(RoomListViewState::roomMembershipChanges) { ms -> // it's for invites local echo adapterInfosList.filter { it.section.notifyOfLocalEcho } .onEach { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt index a5f5801885..3f73b80bc6 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt @@ -66,7 +66,7 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor( } private fun observeAndCompute() { - selectSubscribe( + onEach( RoomDirectoryPickerViewState::asyncThirdPartyRequest, RoomDirectoryPickerViewState::customHomeservers ) { async, custom -> diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 4491ff8606..ea939c153e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -101,7 +101,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeState() { - selectSubscribe( + onEach( RoomSettingsViewState::avatarAction, RoomSettingsViewState::newName, RoomSettingsViewState::newTopic, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt index dc0d06ac86..06ebd01532 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt @@ -78,7 +78,7 @@ class RoomJoinRuleActivity : VectorBaseActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewModel.selectSubscribe(RoomJoinRuleChooseRestrictedState::updatingStatus) { + viewModel.onEach(RoomJoinRuleChooseRestrictedState::updatingStatus) { when (it) { Uninitialized -> { // nop diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt index b03d1e0b14..6cf4f9e0f6 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt @@ -110,7 +110,7 @@ class SpaceDirectoryFragment @Inject constructor( views.spaceDirectoryList.configureWith(epoxyController) epoxyVisibilityTracker.attach(views.spaceDirectoryList) - viewModel.selectSubscribe(SpaceDirectoryState::canAddRooms) { + viewModel.onEach(SpaceDirectoryState::canAddRooms) { invalidateOptionsMenu() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt index cea4d0059e..0512a478a1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt @@ -91,35 +91,35 @@ class SpaceAddRoomFragment @Inject constructor( invalidateOptionsMenu() } - viewModel.selectSubscribe(SpaceAddRoomsState::spaceName) { + viewModel.onEach(SpaceAddRoomsState::spaceName) { views.appBarSpaceInfo.text = it - }.disposeOnDestroyView() + } - viewModel.selectSubscribe(SpaceAddRoomsState::ignoreRooms) { + viewModel.onEach(SpaceAddRoomsState::ignoreRooms) { spaceEpoxyController.ignoreRooms = it roomEpoxyController.ignoreRooms = it dmEpoxyController.ignoreRooms = it - }.disposeOnDestroyView() + } - viewModel.selectSubscribe(SpaceAddRoomsState::isSaving) { + viewModel.onEach(SpaceAddRoomsState::isSaving) { if (it is Loading) { sharedViewModel.handle(SpaceManagedSharedAction.ShowLoading) } else { sharedViewModel.handle(SpaceManagedSharedAction.HideLoading) } - }.disposeOnDestroyView() + } - viewModel.selectSubscribe(SpaceAddRoomsState::shouldShowDMs) { + viewModel.onEach(SpaceAddRoomsState::shouldShowDMs) { dmEpoxyController.disabled = !it - }.disposeOnDestroyView() + } - viewModel.selectSubscribe(SpaceAddRoomsState::onlyShowSpaces) { + viewModel.onEach(SpaceAddRoomsState::onlyShowSpaces) { spaceEpoxyController.disabled = !it roomEpoxyController.disabled = it views.createNewRoom.text = if (it) getString(R.string.create_space) else getString(R.string.create_new_room) val title = if (it) getString(R.string.space_add_existing_spaces) else getString(R.string.space_add_existing_rooms_only) views.appBarTitle.text = title - }.disposeOnDestroyView() + } views.createNewRoom.debouncedClicks { withState(viewModel) { state -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt index 186d733982..8e16784a6d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt @@ -80,7 +80,7 @@ class SpaceManageRoomsFragment @Inject constructor( } .disposeOnDestroyView() - viewModel.selectSubscribe(SpaceManageRoomViewState::actionState) { actionState -> + viewModel.onEach(SpaceManageRoomViewState::actionState) { actionState -> when (actionState) { is Loading -> { sharedViewModel.handle(SpaceManagedSharedAction.ShowLoading) diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt index 064905f188..d1a666546e 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt @@ -68,7 +68,7 @@ class UserCodeActivity : VectorBaseActivity(), showFragment(ShowUserCodeFragment::class, Bundle.EMPTY) } - sharedViewModel.selectSubscribe(UserCodeState::mode) { mode -> + sharedViewModel.onEach(UserCodeState::mode) { mode -> when (mode) { UserCodeState.Mode.SHOW -> showFragment(ShowUserCodeFragment::class, Bundle.EMPTY) UserCodeState.Mode.SCAN -> showFragment(ScanUserCodeFragment::class, Bundle.EMPTY) diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 843ead0eb4..6a9dc4e47d 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -85,7 +85,7 @@ class UserListFragment @Inject constructor( views.userListE2EbyDefaultDisabled.isVisible = !it.isE2EByDefault } - viewModel.selectSubscribe(UserListViewState::pendingSelections) { + viewModel.onEach(UserListViewState::pendingSelections) { renderSelectedUsers(it) } diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index 31895dda16..82d426999b 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -102,14 +102,14 @@ class WidgetActivity : VectorBaseActivity(), } } - viewModel.selectSubscribe(WidgetViewState::status) { ws -> + viewModel.onEach(WidgetViewState::status) { ws -> when (ws) { WidgetStatus.UNKNOWN -> { } WidgetStatus.WIDGET_NOT_ALLOWED -> { val dFrag = supportFragmentManager.findFragmentByTag(WIDGET_PERMISSION_FRAGMENT_TAG) as? RoomWidgetPermissionBottomSheet if (dFrag != null && dFrag.dialog?.isShowing == true && !dFrag.isRemoving) { - return@selectSubscribe + return@onEach } else { RoomWidgetPermissionBottomSheet .newInstance(widgetArgs) @@ -124,11 +124,11 @@ class WidgetActivity : VectorBaseActivity(), } } - viewModel.selectSubscribe(WidgetViewState::widgetName) { name -> + viewModel.onEach(WidgetViewState::widgetName) { name -> supportActionBar?.title = name } - viewModel.selectSubscribe(WidgetViewState::canManageWidgets) { + viewModel.onEach(WidgetViewState::canManageWidgets) { invalidateOptionsMenu() } } From 96b5d1c96bda1ec222f0791a27ae3de14db2289f Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 18:11:14 +0200 Subject: [PATCH 009/144] Mavericks 2: initialize with debug instead of context --- vector/src/main/java/im/vector/app/VectorApplication.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index 3e4cf3ff85..8a8efed567 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -138,7 +138,7 @@ class VectorApplication : } logInfo() LazyThreeTen.init(this) - Mavericks.initialize(this) + Mavericks.initialize(debugMode = false) EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler() EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler() registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager)) From 43c75bdae7647ab590a34020c2010d4fc1155f7d Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 18:11:41 +0200 Subject: [PATCH 010/144] Mavericks 2: replace asyncSubscribe by onAsync --- .../java/im/vector/app/features/call/VectorCallActivity.kt | 2 +- .../app/features/home/room/detail/RoomDetailViewModel.kt | 4 ++-- .../home/room/detail/widget/RoomWidgetsBottomSheet.kt | 2 +- .../java/im/vector/app/features/widgets/WidgetViewModel.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 1a6e8d162a..e41cb261ca 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -138,7 +138,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro renderState(it) } - callViewModel.asyncSubscribe(VectorCallViewState::callState) { + callViewModel.onAsync(VectorCallViewState::callState) { if (it is CallState.Ended) { handleCallEnded(it) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 9e1a362fc6..1f6fec0410 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -269,7 +269,7 @@ class RoomDetailViewModel @AssistedInject constructor( copy(activeRoomWidgets = widgets) } - asyncSubscribe(RoomDetailViewState::activeRoomWidgets) { widgets -> + onAsync(RoomDetailViewState::activeRoomWidgets) { widgets -> setState { val jitsiWidget = widgets.firstOrNull { it.type == WidgetType.Jitsi } val jitsiConfId = jitsiWidget?.let { @@ -1597,7 +1597,7 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeSummaryState() { - asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary -> + onAsync(RoomDetailViewState::asyncRoomSummary) { summary -> setState { val typingMessage = typingHelper.getTypingMessage(summary.typingUsers) copy( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt index 3b305abbe4..cba2635ede 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt @@ -65,7 +65,7 @@ class RoomWidgetsBottomSheet : views.bottomSheetTitle.textSize = 20f views.bottomSheetTitle.setTextColor(colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) epoxyController.listener = this - roomDetailViewModel.asyncSubscribe(RoomDetailViewState::activeRoomWidgets) { + roomDetailViewModel.onAsync(RoomDetailViewState::activeRoomWidgets) { epoxyController.setData(it) } } diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index ced9d45d92..44d246885a 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -101,7 +101,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi } private fun subscribeToWidget() { - asyncSubscribe(WidgetViewState::asyncWidget) { + onAsync(WidgetViewState::asyncWidget) { setState { copy(widgetName = it.name) } } } From 0e01c64f69020504015d6a2fe93db3d0306968db Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 1 Oct 2021 18:27:36 +0200 Subject: [PATCH 011/144] Mavericks: continue removing reference to old MvRx API --- .../vector/app/core/extensions/Parcelable.kt | 1 - .../VectorBaseBottomSheetDialogFragment.kt | 9 +++--- .../app/core/platform/VectorBaseFragment.kt | 5 +-- .../vector/app/core/services/CallService.kt | 4 +-- .../app/features/auth/ReAuthActivity.kt | 6 ++-- .../app/features/call/VectorCallActivity.kt | 10 +++--- .../call/conference/VectorJitsiActivity.kt | 8 ++--- .../call/transfer/CallTransferActivity.kt | 4 +-- .../quads/SharedSecureStorageActivity.kt | 4 +-- .../quads/SharedSecureStorageViewModel.kt | 4 +-- .../verification/VerificationBottomSheet.kt | 32 +++++++++---------- .../VerificationQRWaitingFragment.kt | 4 +-- .../features/devtools/RoomDevToolActivity.kt | 4 +-- .../vector/app/features/home/HomeActivity.kt | 8 ++--- .../features/home/HomeActivityViewModel.kt | 4 +-- .../home/room/detail/RoomDetailFragment.kt | 4 +-- .../DisplayReadReceiptsBottomSheet.kt | 4 +-- .../home/room/detail/search/SearchActivity.kt | 6 ++-- .../edithistory/ViewEditHistoryBottomSheet.kt | 4 +-- .../reactions/ViewReactionsBottomSheet.kt | 4 +-- .../invite/InviteUsersToRoomActivity.kt | 4 +-- .../features/matrixto/MatrixToBottomSheet.kt | 4 +-- .../im/vector/app/features/pin/PinActivity.kt | 6 ++-- .../RoomMemberProfileActivity.kt | 6 ++-- .../devices/DeviceListBottomSheet.kt | 4 +-- .../roomprofile/RoomProfileActivity.kt | 6 ++-- .../settings/joinrule/RoomJoinRuleActivity.kt | 6 ++-- .../DeviceVerificationInfoBottomSheet.kt | 4 +-- .../features/spaces/SpaceCreationActivity.kt | 2 +- .../features/spaces/SpaceExploreActivity.kt | 8 ++--- .../features/spaces/SpacePreviewActivity.kt | 8 ++--- .../leave/SpaceLeaveAdvancedActivity.kt | 8 ++--- .../spaces/manage/SpaceManageActivity.kt | 10 +++--- .../spaces/people/SpacePeopleActivity.kt | 8 ++--- .../app/features/usercode/UserCodeActivity.kt | 4 +-- .../app/features/widgets/WidgetActivity.kt | 6 ++-- .../RoomWidgetPermissionBottomSheet.kt | 4 +-- 37 files changed, 114 insertions(+), 113 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt b/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt index 87f5ac84f7..65f59f7b73 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Parcelable.kt @@ -19,7 +19,6 @@ package im.vector.app.core.extensions import android.os.Bundle import android.os.Parcelable import com.airbnb.mvrx.Mavericks -import com.airbnb.mvrx.MvRx fun Parcelable?.toMvRxBundle(): Bundle? { return this?.let { Bundle().apply { putParcelable(Mavericks.KEY_ARG, it) } } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt index 9adaed288a..d13e446ed4 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -27,7 +27,8 @@ import android.widget.FrameLayout import androidx.annotation.CallSuper import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.MavericksView +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MvRxView import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog @@ -43,9 +44,9 @@ import timber.log.Timber import java.util.concurrent.TimeUnit /** - * Add MvRx capabilities to bottomsheetdialog (like BaseMvRxFragment) + * Add Mavericks capabilities, handle DI and bindings. */ -abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment(), MvRxView { +abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment(), MavericksView { private lateinit var screenComponent: ScreenComponent @@ -166,7 +167,7 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomShe } protected fun setArguments(args: Parcelable? = null) { - arguments = args?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } } + arguments = args?.let { Bundle().apply { putParcelable(Mavericks.KEY_ARG, it) } } } /* ========================================================================================== diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index 2c9452c4fd..5077982460 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -27,9 +27,10 @@ import android.view.ViewGroup import androidx.annotation.CallSuper import androidx.annotation.MainThread import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding -import com.airbnb.mvrx.BaseMvRxFragment +import com.airbnb.mvrx.MvRxView import com.bumptech.glide.util.Util.assertMainThread import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -49,7 +50,7 @@ import io.reactivex.disposables.Disposable import timber.log.Timber import java.util.concurrent.TimeUnit -abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { +abstract class VectorBaseFragment : Fragment(), MvRxView, HasScreenInjector { protected val vectorBaseActivity: VectorBaseActivity<*> by lazy { activity as VectorBaseActivity<*> diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt index cd3845f41b..3c7cef5ce1 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt @@ -25,7 +25,7 @@ import android.view.KeyEvent import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.media.session.MediaButtonReceiver -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import im.vector.app.core.extensions.vectorComponent import im.vector.app.features.call.CallArgs import im.vector.app.features.call.VectorCallActivity @@ -165,7 +165,7 @@ class CallService : VectorService() { val incomingCallAlert = IncomingCallAlert(callId, shouldBeDisplayedIn = { activity -> if (activity is VectorCallActivity) { - activity.intent.getParcelableExtra(MvRx.KEY_ARG)?.callId != call.callId + activity.intent.getParcelableExtra(Mavericks.KEY_ARG)?.callId != call.callId } else true } ).apply { diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt index ce23111a95..b7f570672b 100644 --- a/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt +++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt @@ -26,7 +26,7 @@ import androidx.browser.customtabs.CustomTabsCallback import androidx.browser.customtabs.CustomTabsClient import androidx.browser.customtabs.CustomTabsServiceConnection import androidx.browser.customtabs.CustomTabsSession -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import im.vector.app.R @@ -78,7 +78,7 @@ class ReAuthActivity : SimpleFragmentActivity(), ReAuthViewModel.Factory { val title = intent.extras?.getString(EXTRA_REASON_TITLE) ?: getString(R.string.re_authentication_activity_title) supportActionBar?.setTitle(title) ?: run { setTitle(title) } -// val authArgs = intent.getParcelableExtra(MvRx.KEY_ARG) +// val authArgs = intent.getParcelableExtra(Mavericks.KEY_ARG) // For the sso flow we can for now only rely on the fallback flow, that handles all // the UI, due to the sandbox nature of CCT (chrome custom tab) we cannot get much information @@ -221,7 +221,7 @@ class ReAuthActivity : SimpleFragmentActivity(), ReAuthViewModel.Factory { } } return Intent(context, ReAuthActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, Args(authType, reasonTitle, fromError.session, lastErrorCode, resultKeyStoreAlias)) + putExtra(Mavericks.KEY_ARG, Args(authType, reasonTitle, fromError.session, lastErrorCode, resultKeyStoreAlias)) } } } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index e41cb261ca..5222ee842b 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -36,7 +36,7 @@ import androidx.core.content.getSystemService import androidx.core.view.isInvisible import androidx.core.view.isVisible import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import com.google.android.material.card.MaterialCardView @@ -167,8 +167,8 @@ class VectorCallActivity : VectorBaseActivity(), CallContro override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - intent?.takeIf { it.hasExtra(MvRx.KEY_ARG) } - ?.let { intent.getParcelableExtra(MvRx.KEY_ARG) } + intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) } + ?.let { intent.getParcelableExtra(Mavericks.KEY_ARG) } ?.let { callViewModel.handle(VectorCallViewActions.SwitchCall(it)) } @@ -633,7 +633,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? flags = Intent.FLAG_ACTIVITY_NEW_TASK - putExtra(MvRx.KEY_ARG, CallArgs(call.nativeRoomId, call.callId, call.mxCall.opponentUserId, !call.mxCall.isOutgoing, call.mxCall.isVideoCall)) + putExtra(Mavericks.KEY_ARG, CallArgs(call.nativeRoomId, call.callId, call.mxCall.opponentUserId, !call.mxCall.isOutgoing, call.mxCall.isVideoCall)) putExtra(EXTRA_MODE, mode) } } @@ -648,7 +648,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? flags = Intent.FLAG_ACTIVITY_NEW_TASK - putExtra(MvRx.KEY_ARG, CallArgs(signalingRoomId, callId, otherUserId, isIncomingCall, isVideoCall)) + putExtra(Mavericks.KEY_ARG, CallArgs(signalingRoomId, callId, otherUserId, isIncomingCall, isVideoCall)) putExtra(EXTRA_MODE, mode) } } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index e7fd541f3d..62d017467f 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -28,7 +28,7 @@ import android.widget.Toast import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import com.facebook.react.modules.core.PermissionListener @@ -205,8 +205,8 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee JitsiMeetActivityDelegate.onNewIntent(intent) // Is it a switch to another conf? - intent?.takeIf { it.hasExtra(MvRx.KEY_ARG) } - ?.let { intent.getParcelableExtra(MvRx.KEY_ARG) } + intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) } + ?.let { intent.getParcelableExtra(Mavericks.KEY_ARG) } ?.let { jitsiViewModel.handle(JitsiCallViewActions.SwitchTo(it, true)) } @@ -242,7 +242,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee companion object { fun newIntent(context: Context, roomId: String, widgetId: String, enableVideo: Boolean): Intent { return Intent(context, VectorJitsiActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, Args(roomId, widgetId, enableVideo)) + putExtra(Mavericks.KEY_ARG, Args(roomId, widgetId, enableVideo)) } } } diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt index c80b21334a..2a50dc85f9 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.os.Parcelable -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.tabs.TabLayoutMediator import im.vector.app.R @@ -121,7 +121,7 @@ class CallTransferActivity : VectorBaseActivity(), fun newIntent(context: Context, callId: String): Intent { return Intent(context, CallTransferActivity::class.java).also { - it.putExtra(MvRx.KEY_ARG, CallTransferArgs(callId)) + it.putExtra(Mavericks.KEY_ARG, CallTransferArgs(callId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt index 51159e62bf..bd7195ad1e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt @@ -25,7 +25,7 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentOnAttachListener -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R @@ -159,7 +159,7 @@ class SharedSecureStorageActivity : resultKeyStoreAlias: String = DEFAULT_RESULT_KEYSTORE_ALIAS): Intent { require(requestedSecrets.isNotEmpty()) return Intent(context, SharedSecureStorageActivity::class.java).also { - it.putExtra(MvRx.KEY_ARG, Args( + it.putExtra(Mavericks.KEY_ARG, Args( keyId, requestedSecrets, resultKeyStoreAlias diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index ecaf89a1e5..151b73ff32 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success @@ -327,7 +327,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( @JvmStatic override fun create(viewModelContext: ViewModelContext, state: SharedSecureStorageViewState): SharedSecureStorageViewModel? { val activity: SharedSecureStorageActivity = viewModelContext.activity() - val args: SharedSecureStorageActivity.Args = activity.intent.getParcelableExtra(MvRx.KEY_ARG) ?: error("Missing args") + val args: SharedSecureStorageActivity.Args = activity.intent.getParcelableExtra(Mavericks.KEY_ARG) ?: error("Missing args") return activity.viewModelFactory.create(state, args) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index 47033199f4..dd6cf6cbd6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -24,7 +24,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -184,7 +184,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment { showFragment(VerificationEmojiCodeFragment::class, Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationArgs( + putParcelable(Mavericks.KEY_ARG, VerificationArgs( state.otherUserMxItem?.id ?: "", // If it was outgoing it.transaction id would be null, but the pending request // would be updated (from localId to txId) @@ -244,12 +244,12 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment { showFragment(VerificationConclusionFragment::class, Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) + putParcelable(Mavericks.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) }) } is VerificationTxState.Cancelled -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, state.sasTransactionState.cancelCode.value, state.isMe)) + putParcelable(Mavericks.KEY_ARG, VerificationConclusionFragment.Args(false, state.sasTransactionState.cancelCode.value, state.isMe)) }) } } @@ -265,7 +265,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment { showFragment(VerificationQRWaitingFragment::class, Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationQRWaitingFragment.Args( + putParcelable(Mavericks.KEY_ARG, VerificationQRWaitingFragment.Args( isMe = state.isMe, otherUserName = state.otherUserMxItem?.getBestName() ?: "" )) @@ -274,13 +274,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment { showFragment(VerificationConclusionFragment::class, Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) + putParcelable(Mavericks.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) }) return@withState } is VerificationTxState.Cancelled -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe)) + putParcelable(Mavericks.KEY_ARG, VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe)) }) return@withState } @@ -294,7 +294,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment(MvRx.KEY_ARG) + val args = intent.getParcelableExtra(Mavericks.KEY_ARG) if (args?.clearNotification == true) { notificationDrawerManager.clearAllEvents() @@ -433,7 +433,7 @@ class HomeActivity : override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - val parcelableExtra = intent?.getParcelableExtra(MvRx.KEY_ARG) + val parcelableExtra = intent?.getParcelableExtra(Mavericks.KEY_ARG) if (parcelableExtra?.clearNotification == true) { notificationDrawerManager.clearAllEvents() } @@ -580,7 +580,7 @@ class HomeActivity : return Intent(context, HomeActivity::class.java) .apply { - putExtra(MvRx.KEY_ARG, args) + putExtra(Mavericks.KEY_ARG, args) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 297cffd627..fb2401edc1 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -72,7 +72,7 @@ class HomeActivityViewModel @AssistedInject constructor( @JvmStatic override fun create(viewModelContext: ViewModelContext, state: HomeActivityViewState): HomeActivityViewModel? { val activity: HomeActivity = viewModelContext.activity() - val args: HomeActivityArgs? = activity.intent.getParcelableExtra(MvRx.KEY_ARG) + val args: HomeActivityArgs? = activity.intent.getParcelableExtra(Mavericks.KEY_ARG) return activity.viewModelFactory.create(state, args ?: HomeActivityArgs(clearNotification = false, accountCreation = false)) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 776b02d814..5da00b7e2f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -61,7 +61,7 @@ import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.epoxy.addGlidePreloader import com.airbnb.epoxy.glidePreloader -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState @@ -1582,7 +1582,7 @@ class RoomDetailFragment @Inject constructor( val otherUserId = data.otherUserId ?: return VerificationBottomSheet().apply { arguments = Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationBottomSheet.VerificationArgs( + putParcelable(Mavericks.KEY_ARG, VerificationBottomSheet.VerificationArgs( otherUserId, data.transactionId, roomId = roomDetailArgs.roomId)) } }.show(parentFragmentManager, "REQ") diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt index 95b2333b43..9f98d655cc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt @@ -21,7 +21,7 @@ import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.args import im.vector.app.R import im.vector.app.core.di.ScreenComponent @@ -88,7 +88,7 @@ class DisplayReadReceiptsBottomSheet : val parcelableArgs = DisplayReadReceiptArgs( readReceipts ) - args.putParcelable(MvRx.KEY_ARG, parcelableArgs) + args.putParcelable(Mavericks.KEY_ARG, parcelableArgs) return DisplayReadReceiptsBottomSheet().apply { arguments = args } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt index d9a2f98e32..88ed101252 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.appcompat.widget.SearchView -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment @@ -49,7 +49,7 @@ class SearchActivity : VectorBaseActivity() { override fun initUiAndData() { if (isFirstCreation()) { - val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return + val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return addFragment(R.id.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG) } views.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { @@ -73,7 +73,7 @@ class SearchActivity : VectorBaseActivity() { return Intent(context, SearchActivity::class.java).apply { // If we do that we will have the same room two times on the stack. Let's allow infinite stack for the moment. // flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT - putExtra(MvRx.KEY_ARG, args) + putExtra(Mavericks.KEY_ARG, args) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index 7be4be4b57..d9fd80f02f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -19,7 +19,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R @@ -79,7 +79,7 @@ class ViewEditHistoryBottomSheet: roomId, informationData ) - args.putParcelable(MvRx.KEY_ARG, parcelableArgs) + args.putParcelable(Mavericks.KEY_ARG, parcelableArgs) return ViewEditHistoryBottomSheet().apply { arguments = args } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt index 9cc3be9ac1..552ef9552d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt @@ -20,7 +20,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R @@ -93,7 +93,7 @@ class ViewReactionsBottomSheet : roomId, informationData ) - args.putParcelable(MvRx.KEY_ARG, parcelableArgs) + args.putParcelable(Mavericks.KEY_ARG, parcelableArgs) return ViewReactionsBottomSheet().apply { arguments = args } } } diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt index 88998861bc..dd07319e9f 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt @@ -21,7 +21,7 @@ import android.content.Intent import android.os.Bundle import android.os.Parcelable import android.view.View -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R @@ -165,7 +165,7 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity(), UserListViewModel.Fa fun getIntent(context: Context, roomId: String): Intent { return Intent(context, InviteUsersToRoomActivity::class.java).also { - it.putExtra(MvRx.KEY_ARG, InviteUsersToRoomArgs(roomId)) + it.putExtra(Mavericks.KEY_ARG, InviteUsersToRoomArgs(roomId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt index 3e75b96c32..e53cf1da98 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt @@ -24,7 +24,7 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.Fragment import com.airbnb.mvrx.Incomplete -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -134,7 +134,7 @@ class MatrixToBottomSheet : fun withLink(matrixToLink: String, listener: InteractionListener?): MatrixToBottomSheet { return MatrixToBottomSheet().apply { arguments = Bundle().apply { - putParcelable(MvRx.KEY_ARG, MatrixToArgs( + putParcelable(Mavericks.KEY_ARG, MatrixToArgs( matrixToLink = matrixToLink )) } diff --git a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt index 6866afa0a6..a335f1a1f2 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt @@ -19,7 +19,7 @@ package im.vector.app.features.pin import android.content.Context import android.content.Intent import com.google.android.material.appbar.MaterialToolbar -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable @@ -31,7 +31,7 @@ class PinActivity : VectorBaseActivity(), ToolbarConfigur companion object { fun newIntent(context: Context, args: PinArgs): Intent { return Intent(context, PinActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, args) + putExtra(Mavericks.KEY_ARG, args) } } } @@ -42,7 +42,7 @@ class PinActivity : VectorBaseActivity(), ToolbarConfigur override fun initUiAndData() { if (isFirstCreation()) { - val fragmentArgs: PinArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return + val fragmentArgs: PinArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return addFragment(R.id.simpleFragmentContainer, PinFragment::class.java, fragmentArgs) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt index 37ad2741ca..02082f7751 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt @@ -21,7 +21,7 @@ import android.content.Context import android.content.Intent import android.widget.Toast import com.google.android.material.appbar.MaterialToolbar -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import im.vector.app.R import im.vector.app.core.di.ScreenComponent @@ -42,7 +42,7 @@ class RoomMemberProfileActivity : companion object { fun newIntent(context: Context, args: RoomMemberProfileArgs): Intent { return Intent(context, RoomMemberProfileActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, args) + putExtra(Mavericks.KEY_ARG, args) } } } @@ -66,7 +66,7 @@ class RoomMemberProfileActivity : override fun initUiAndData() { if (isFirstCreation()) { - val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return + val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return addFragment(R.id.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt index f0cdb1cdd1..05ccc57b10 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt @@ -24,7 +24,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R @@ -122,7 +122,7 @@ class DeviceListBottomSheet : companion object { fun newInstance(userId: String, allowDeviceAction: Boolean = true): DeviceListBottomSheet { val args = Bundle() - args.putParcelable(MvRx.KEY_ARG, Args(userId, allowDeviceAction)) + args.putParcelable(Mavericks.KEY_ARG, Args(userId, allowDeviceAction)) return DeviceListBottomSheet().apply { arguments = args } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index d28878283f..d93d278614 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -21,7 +21,7 @@ import android.content.Context import android.content.Intent import android.widget.Toast import com.google.android.material.appbar.MaterialToolbar -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import im.vector.app.R import im.vector.app.core.di.ScreenComponent @@ -60,7 +60,7 @@ class RoomProfileActivity : fun newIntent(context: Context, roomId: String, directAccess: Int?): Intent { val roomProfileArgs = RoomProfileArgs(roomId) return Intent(context, RoomProfileActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, roomProfileArgs) + putExtra(Mavericks.KEY_ARG, roomProfileArgs) putExtra(EXTRA_DIRECT_ACCESS, directAccess) } } @@ -91,7 +91,7 @@ class RoomProfileActivity : override fun initUiAndData() { sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java) - roomProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return + roomProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return if (isFirstCreation()) { when (intent?.extras?.getInt(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOM_ROOT)) { EXTRA_DIRECT_ACCESS_ROOM_SETTINGS -> { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt index 06ebd01532..dcce7b2384 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt @@ -22,7 +22,7 @@ import android.os.Bundle import androidx.core.view.isVisible import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.viewModel @@ -66,7 +66,7 @@ class RoomJoinRuleActivity : VectorBaseActivity(), } override fun initUiAndData() { - roomProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return + roomProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return if (isFirstCreation()) { addFragment( R.id.simpleFragmentContainer, @@ -142,7 +142,7 @@ class RoomJoinRuleActivity : VectorBaseActivity(), fun newIntent(context: Context, roomId: String): Intent { val roomProfileArgs = RoomProfileArgs(roomId) return Intent(context, RoomJoinRuleActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, roomProfileArgs) + putExtra(Mavericks.KEY_ARG, roomProfileArgs) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt index 9bfd2df0d6..7ba6042027 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt @@ -21,7 +21,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState @@ -83,7 +83,7 @@ class DeviceVerificationInfoBottomSheet : fun newInstance(userId: String, deviceId: String): DeviceVerificationInfoBottomSheet { val args = Bundle() val parcelableArgs = DeviceVerificationInfoArgs(userId, deviceId) - args.putParcelable(MvRx.KEY_ARG, parcelableArgs) + args.putParcelable(Mavericks.KEY_ARG, parcelableArgs) return DeviceVerificationInfoBottomSheet().apply { arguments = args } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt index 344559bc81..1844e8e9ca 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt @@ -171,7 +171,7 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac fun newIntent(context: Context): Intent { return Intent(context, SpaceCreationActivity::class.java).apply { - // putExtra(MvRx.KEY_ARG, SpaceDirectoryArgs(spaceId)) + // putExtra(Mavericks.KEY_ARG, SpaceDirectoryArgs(spaceId)) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt index dbe92d4d93..45c520e37e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt @@ -21,7 +21,7 @@ import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import im.vector.app.R import im.vector.app.core.di.ScreenComponent @@ -72,12 +72,12 @@ class SpaceExploreActivity : VectorBaseActivity(), SpaceD if (isFirstCreation()) { val simpleName = SpaceDirectoryFragment::class.java.simpleName - val args = intent?.getParcelableExtra(MvRx.KEY_ARG) + val args = intent?.getParcelableExtra(Mavericks.KEY_ARG) if (supportFragmentManager.findFragmentByTag(simpleName) == null) { supportFragmentManager.commitTransaction { replace(R.id.simpleFragmentContainer, SpaceDirectoryFragment::class.java, - Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) }, + Bundle().apply { this.putParcelable(Mavericks.KEY_ARG, args) }, simpleName ) } @@ -107,7 +107,7 @@ class SpaceExploreActivity : VectorBaseActivity(), SpaceD companion object { fun newIntent(context: Context, spaceId: String): Intent { return Intent(context, SpaceExploreActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, SpaceDirectoryArgs(spaceId)) + putExtra(Mavericks.KEY_ARG, SpaceDirectoryArgs(spaceId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt index 0dcaf9d754..59166529b9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt @@ -19,7 +19,7 @@ package im.vector.app.features.spaces import android.content.Context import android.content.Intent import android.os.Bundle -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import im.vector.app.R import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.platform.VectorBaseActivity @@ -50,12 +50,12 @@ class SpacePreviewActivity : VectorBaseActivity() { if (isFirstCreation()) { val simpleName = SpacePreviewFragment::class.java.simpleName - val args = intent?.getParcelableExtra(MvRx.KEY_ARG) + val args = intent?.getParcelableExtra(Mavericks.KEY_ARG) if (supportFragmentManager.findFragmentByTag(simpleName) == null) { supportFragmentManager.commitTransaction { replace(R.id.simpleFragmentContainer, SpacePreviewFragment::class.java, - Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) }, + Bundle().apply { this.putParcelable(Mavericks.KEY_ARG, args) }, simpleName ) } @@ -66,7 +66,7 @@ class SpacePreviewActivity : VectorBaseActivity() { companion object { fun newIntent(context: Context, spaceIdOrAlias: String): Intent { return Intent(context, SpacePreviewActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, SpacePreviewArgs(spaceIdOrAlias)) + putExtra(Mavericks.KEY_ARG, SpacePreviewArgs(spaceIdOrAlias)) } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt index cb66708324..762abf10cb 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt @@ -23,7 +23,7 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar @@ -74,7 +74,7 @@ class SpaceLeaveAdvancedActivity : VectorBaseActivity(MvRx.KEY_ARG) + val args = intent?.getParcelableExtra(Mavericks.KEY_ARG) if (isFirstCreation()) { val simpleName = SpaceLeaveAdvancedFragment::class.java.simpleName @@ -83,7 +83,7 @@ class SpaceLeaveAdvancedActivity : VectorBaseActivity(), } .disposeOnDestroy() - val args = intent?.getParcelableExtra(MvRx.KEY_ARG) + val args = intent?.getParcelableExtra(Mavericks.KEY_ARG) if (isFirstCreation()) { withState(sharedViewModel) { when (it.manageType) { @@ -106,7 +106,7 @@ class SpaceManageActivity : VectorBaseActivity(), supportFragmentManager.commitTransaction { replace(R.id.simpleFragmentContainer, SpaceAddRoomFragment::class.java, - Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) }, + Bundle().apply { this.putParcelable(Mavericks.KEY_ARG, args) }, simpleName ) } @@ -118,7 +118,7 @@ class SpaceManageActivity : VectorBaseActivity(), supportFragmentManager.commitTransaction { replace(R.id.simpleFragmentContainer, SpaceSettingsFragment::class.java, - Bundle().apply { this.putParcelable(MvRx.KEY_ARG, RoomProfileArgs(args.spaceId)) }, + Bundle().apply { this.putParcelable(Mavericks.KEY_ARG, RoomProfileArgs(args.spaceId)) }, simpleName ) } @@ -189,7 +189,7 @@ class SpaceManageActivity : VectorBaseActivity(), companion object { fun newIntent(context: Context, spaceId: String, manageType: ManageType): Intent { return Intent(context, SpaceManageActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, SpaceManageArgs(spaceId, manageType)) + putExtra(Mavericks.KEY_ARG, SpaceManageArgs(spaceId, manageType)) } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt index b4b48d7710..3b84a12bc1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt @@ -21,7 +21,7 @@ import android.content.Intent import android.os.Bundle import androidx.core.view.isGone import androidx.core.view.isVisible -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import im.vector.app.R import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.hideKeyboard @@ -57,14 +57,14 @@ class SpacePeopleActivity : VectorBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val args = intent?.getParcelableExtra(MvRx.KEY_ARG) + val args = intent?.getParcelableExtra(Mavericks.KEY_ARG) if (isFirstCreation()) { val simpleName = SpacePeopleFragment::class.java.simpleName if (supportFragmentManager.findFragmentByTag(simpleName) == null) { supportFragmentManager.commitTransaction { replace(R.id.simpleFragmentContainer, SpacePeopleFragment::class.java, - Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) }, + Bundle().apply { this.putParcelable(Mavericks.KEY_ARG, args) }, simpleName ) } @@ -97,7 +97,7 @@ class SpacePeopleActivity : VectorBaseActivity() { companion object { fun newIntent(context: Context, spaceId: String): Intent { return Intent(context, SpacePeopleActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, GenericIdArgs(spaceId)) + putExtra(Mavericks.KEY_ARG, GenericIdArgs(spaceId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt index d1a666546e..359fa04093 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt @@ -24,7 +24,7 @@ import android.widget.Toast import androidx.core.app.ActivityCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import im.vector.app.R @@ -128,7 +128,7 @@ class UserCodeActivity : VectorBaseActivity(), companion object { fun newIntent(context: Context, userId: String): Intent { return Intent(context, UserCodeActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, Args(userId)) + putExtra(Mavericks.KEY_ARG, Args(userId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index 82d426999b..23f1cfe119 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -20,7 +20,7 @@ import android.app.Activity import android.content.Context import android.content.Intent import androidx.core.view.isVisible -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R @@ -50,7 +50,7 @@ class WidgetActivity : VectorBaseActivity(), fun newIntent(context: Context, args: WidgetArgs): Intent { return Intent(context, WidgetActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, args) + putExtra(Mavericks.KEY_ARG, args) } } @@ -83,7 +83,7 @@ class WidgetActivity : VectorBaseActivity(), } override fun initUiAndData() { - val widgetArgs: WidgetArgs? = intent?.extras?.getParcelable(MvRx.KEY_ARG) + val widgetArgs: WidgetArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG) if (widgetArgs == null) { finish() return diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt index 4036195b65..38c3b914d0 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt @@ -23,7 +23,7 @@ import android.text.style.BulletSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.app.R @@ -116,7 +116,7 @@ class RoomWidgetPermissionBottomSheet : companion object { fun newInstance(widgetArgs: WidgetArgs) = RoomWidgetPermissionBottomSheet().withArgs { - putParcelable(MvRx.KEY_ARG, widgetArgs) + putParcelable(Mavericks.KEY_ARG, widgetArgs) } } } From ca64e9511ee6a70184417230b1ddce9264b882b9 Mon Sep 17 00:00:00 2001 From: Linerly Date: Fri, 1 Oct 2021 13:57:44 +0000 Subject: [PATCH 012/144] Translated using Weblate (Indonesian) Currently translated at 100.0% (2658 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/id/ --- vector/src/main/res/values-in/strings.xml | 694 ++++++++++++++++++++-- 1 file changed, 657 insertions(+), 37 deletions(-) diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 81c4371ec5..d0a46aee5e 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -1,14 +1,14 @@ Undang dari %s - Undangan Ruang + Undangan Ruangan %1$s dan %2$s - Ruang kosong + Ruangan kosong %1$s dan %2$d yang lain Pesan - Ruang + Ruangan Pengaturan Detail Anggota OK @@ -39,7 +39,7 @@ Cari ruang Direktori Pengguna Tidak ada hasil - Ruang umum belum tersedia + Ruangan umum belum tersedia Direktori ruang Kirim log Deskripsikan kendala Anda di sini @@ -64,8 +64,8 @@ Alamat Email (pilihan) Nomor Telpon Nomor Telpon (piihan) - Ulangi password - Konfirmasi password baru + Ulangi kata sandi + Konfirmasi kata sandi baru Kirim Ulang Teruskan Tampilkan Sumber @@ -79,16 +79,16 @@ Pencarian global Tandai semua sudah dibaca Orang - Ruang + Ruangan Undangan Percakapan Buku alamat lokal Prioritas rendah Hanya kontak Matrix - Ruang + Ruangan Laporan bug Aplikasi gagal saat terakhir digunakan. Apakah Anda ingin membuka halaman laporan kegagalan\? - Gabung di Ruang + Gabung di Ruangan URL Server Identitas Mulai Panggilan Suara Masuk @@ -97,7 +97,7 @@ Kirim file Password terlalu pendek (min 6) Alamat email ini sudah terdefinisi. - Lupa password? + Lupa kata sandi\? Token tidak berlaku Sepertinya alamat email tidak benar Sepertinya nomor telepon tidak benar @@ -235,8 +235,8 @@ \n \nHarap berikan akses pada halaman berikut ini untuk melakukan panggilan. Tema Terang - Tema Kelam - Tema Gelap + Tema Gelap + Tema Hitam Menyinkronkan… Pemberitahuan Berisik Pemberitahuan Tenteram @@ -390,7 +390,7 @@ Ijinkan akses lewat halaman selanjutnya untuk menemukan pengguna ${app_name} yan Sertifikat ini tidak lagi sesuai dengan yang dipercayai oleh perangkat Anda sebelumnya. Ini SANGAT JANGGAL. Kami rekomendasikan Anda untuk TIDAK MENERIMA sertifikat baru ini. Terdapat perubahan sertifikat yang tidak lagi dipercayai perangkat. Server mungkin telah memperbaharui sertifikatnya. Hubungi administrator server untuk pencocokan sidik jari. Hanya terima sertifikat ini apabila administrator server telah menerbitkan sidik jari yang cocok dengan yang tertera di atas. - Detail Ruang + Detail Ruangan Orang Berkas Pengaturan @@ -483,7 +483,7 @@ Ijinkan akses lewat halaman selanjutnya untuk menemukan pengguna ${app_name} yan Batas waktu permohonan sinkronisasi Masa tunda sebelum permohonan berikutnya Versi - versi olm + Versi Olm Syarat & ketentuan %d ruangan @@ -541,11 +541,11 @@ Ijinkan akses lewat halaman selanjutnya untuk menemukan pengguna ${app_name} yan Contoh Id Komunitas contoh - Pangkal + Beranda Orang - Ruang + Ruangan Tidak ada pengguna - Ruang + Ruangan Telah bergabung Telah Diundang Saring anggota grup @@ -561,7 +561,7 @@ Ijinkan akses lewat halaman selanjutnya untuk menemukan pengguna ${app_name} yan Anda telah dilarang dari %1$s oleh %2$s Alasan: %1$s Gabung lagi - Lupakan ruang + Lupakan ruangan Untuk terus menggunakan homeserver %1$s Anda harus membaca dan menyetujui syarat dan ketentuan. Avatar Baca sekarang @@ -571,13 +571,13 @@ Ijinkan akses lewat halaman selanjutnya untuk menemukan pengguna ${app_name} yan \nMenonaktifkan akun Anda tidak membuat kami melupakan pesan-pesan yang Anda kirim secara default. Jika Anda ingin kami melupakan pesan-pesan Anda, mohon centang kotak berikut. \n \nKeterbacaan pesan di Matrix serupa dengan email. Dengan kami melupakan pesan-pesan Anda berarti pesan-pesan yang Anda kirim tidak akan dibagikan kepada pengguna baru ataupun yang belum terdaftar, tetapi pengguna yang terdaftar yang mempunyai mengakses pesan-pesan tersebut masih bisa mengakses salinan mereka. - Mohon lupakan semua pesan yang telah kukirim ketika akunku dideaktivasi (Peringatan: ini akan mengakibatkan pengguna mendatang membaca percakapan yang tidak lengkap) + Mohon lupakan semua pesan yang telah saya kirim ketika akun saya dideaktivasi (Peringatan: ini akan mengakibatkan pengguna di masa depan melihat percakapan yang tidak lengkap) Untuk melanjutkan, masukkan kata sandi Anda: Deaktivasi Akun Mohon masukkan kata sandi Anda. - Ruang ini telah berubah dan tidak lagi aktif. + Ruangan ini telah berubah dan tidak lagi aktif. Percakapan berlanjut di sini - Ruang ini adalah kelanjutan percakapan lain + Ruangan ini adalah kelanjutan percakapan lain Klik di sini untuk melihat pesan lama Melampaui Batasan Sumber Daya Kontak Administrator @@ -606,7 +606,7 @@ Ijinkan akses lewat halaman selanjutnya untuk menemukan pengguna ${app_name} yan Apabila cocok, tekan tombol verifikasi berikut. Apabila tidak, seseorang sedang menyadap perangkat ini dan mungkin perlu diblokir. Di masa mendatang proses verifikasi ini akan dimutakhirkan. - Ruang ini terdapat sesi tidak dikenal yang belum diverifikasi. + Ruangan ini terdapat sesi tidak dikenal yang belum diverifikasi. \nIni artinya tidak ada jaminan pengguna sesi tersebut sesuai dengan klaim mereka. \nKami sarankan Anda untuk memverifikasi untuk setiap sesi terlebih dahulu sebelum melanjutkan, namun Anda juga boleh mengirim ulang pesan tanpa verifikasi bila Anda memilih demikian. \n @@ -627,15 +627,15 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Akses dan visibilitas Daftarkan ruang ini di direktori ruang Pemberitahuan - Akses Ruang - Singkapan Sejarah Ruang + Akses Ruangan + Singkapan Sejarah Ruangan Siapa yang bisa membaca sejarah? Siapa yang bisa mengakses ruang ini? Siapapun Hanya anggota (dimulai sejak opsi ini dipilih) Hanya anggota (dimulai sejak mereka diundang) Hanya anggota (dimulai sejak mereka bergabung) - Ruang harus memiliki alamat agar dapat ditautkan. + Ruangan harus memiliki alamat agar dapat ditautkan. Hanya orang yang telah diundang Siapapun yang tahu tautan ruang, selain tamu Siapapun yang tahu tautan ruang, termasuk tamu @@ -650,9 +650,9 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Anda perlu keluar dulu untuk mengaktifkan enkripsi. Enkripsi ke perangkat terverifikasi saja Jangan mengirim pesan terenkripsi ke perangkat yang belum diverifikasi di ruang ini dengan perangkat ini. - Ruang ini tidak punya alamat lokal + Ruangan ini tidak punya alamat lokal Alamat baru (misalnya #foo:matrix.org) - Ruang ini tidak menunjukkan flair untuk komunitas manapun + Ruangan ini tidak menunjukkan flair untuk komunitas manapun ID komunitas baru (misalnya +foo:matrix.org) ID komunitas tidak valid \'%s\' bukan ID komunitas yang valid @@ -662,8 +662,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Peringatan alamat utama Tentukan sebagai alamat utama Tidak tentukan sebagai alamat utama - Salin ID Ruang - Salin Alamat Ruang + Salin ID Ruangan + Salin Alamat Ruangan Enkripsi diaktifkan untuk ruang ini. Enkripsi dinonaktifkan untuk ruang ini. Aktifkan enkripsi @@ -783,10 +783,10 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. 1 minggu 1 bulan Selamanya - Foto Ruang - Nama Ruang + Foto Ruangan + Nama Ruangan Topik - Tanda Ruang + Tanda Ruangan Ditandai sebagai: Favorit Avatar pemberitahu @@ -978,7 +978,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. %1$s menciptakan ruangan Undangan Anda Undangan %s - Kamu mengirim sticker. + Anda mengirim sticker. %1$s mengirim stiker. Anda mengirim gambar. %1$s mengirim gambar. @@ -1001,7 +1001,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Hapus %s\? Diperlukan otentikasi Anda tidak dapat melakukan ini dari ponsel ${app_name} - Konfirmasi password Anda + Konfirmasi kata sandi Anda Tidak ada nomor telepon yang ditambahkan ke akun Anda Pencarian di ruangan terenkripsi belum didukung. Meng-filter pengguna yang dicekal @@ -1095,7 +1095,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Speaker Ponsel Pilih Perangkat Suara - Gagal membangun koneksi real-time. + Gagal membuat koneksi real-time. \nSilakan minta administrator homeserver Anda untuk mengkonfigurasi server TURN agar panggilan untuk bekerja dengan andal. ${app_name} Panggilan Gagal Jangan tanya saya lagi @@ -1217,8 +1217,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. %1$s mencekal %2$s. Alasan: %3$s %1$s membatalkan pencekalan %2$s. Alasan: %3$s Anda membatalkan pencekalan %1$s. Alasan: %2$s - Anda meng-kick %1$s. Alasan: %2$s - %1$s meng-kick %2$s. Alasan: %3$s + Anda mengeluarkan %1$s. Alasan: %2$s + %1$s mengeluarkan %2$s. Alasan: %3$s Anda menolak undangan. Alasan: %1$s %1$s menolak undangan. Alasan: %2$s Anda meninggalkan ruangan. Alasan: %1$s @@ -2319,4 +2319,624 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Email Lanjut Email verifikasi akan dikirim ke kotak masuk Anda untuk mengkonfirmasi pengaturan kata sandi baru Anda. + Ini membuatnya mudah untuk ruangan tetap privat di ruangan, sambil membiarkan orang-orang di space dapat menemukan dan bergabung ke ruangannya. Semua ruangan baru di space akan memiliki opsi ini. + Bantu orang-orang di space untuk menemukan dan bergabung ruangan privat sendiri, tidak perlu mengundang semua secara manual. + Baru: Izinkan orang-orang di space untuk menemukan dan bergabung ruangan privat + Dicatat bahwa meningkatkan akan membuat versi baru dari ruangannya. Semua pesan saat ini akan tetap di ruangan yang diarsip. + Siapa saja di induk ruangan dapat menemukan dan bergabung ke ruangan ini - tidak perlu mengundang semua secara manual. Anda dapat mengubahnya di pengaturan ruangan kapan saja. + Siapa saja di %s dapat menemukan dan bergabung ke ruangan ini - tidak perlu mengundang semua secara manual. Anda dapat mengubahnya di pengaturan ruangan kapan saja. + Pesan Suara (%1$s) + Tidak dapat membalas atau mengedit saat pesan suara aktif + Tidak dapat merekam sebuah pesan suara + Tidak dapat memainkan pesan suara ini + Aktifkan pesan suara + Ketuk pada rekaman Anda untuk dihentikan atau didengarkan + %1$dd lagi + Tahan untuk merekam, lepaskan untuk mengirimnya + Hapus pesan suara yang telah direkam + Merekam pesan suara + Jeda Pesan Suara + Mainkan Pesan Suara + Kunci Pesan Suara + Geser untuk membatalkan + Mulai Pesan Suara + Panggilan grup dimulai + Maaf, sebuah kesalahan terjadi saat bergabung: %s + Tingkatkan ke versi ruangan yang disarankan + Kamar ini menjalankan versi %s, yang ditandai oleh homeserver ini sebagai tidak stabil. + Izinkan siapa saja di %s untuk mencari dan mengakses. Anda dapat memilih space yang lain juga. + Anda membutuhkan izin untuk meningkatkan sebuah ruangan + Otomatis memperbarui induk space + Otomatis undang pengguna + Anda akan meningkatkan ruangan ini dari %1$s ke %2$s. + Meningkatkan ruangan adalah aksi lanjutan dan hanya disarankan ketika ruangan tidak stabil karena bug yang ada, fitur yang kurang atau rentanan keamanan. +\nIni biasanya hanya mempengaruhi bagaimana ruangan itu diproses di servernya. + Tingkatkan ruangan privat + Tingkatkan ruangan publik + Peningkatan dibutuhkan + Tingkatkan + Mohon sabar, ini mungkin memakan waktu yang lama. + Bergabung ke ruangan yang diganti + Saat ini orang-orang mungkin tidak dapat bergabung ke ruangan privat yang Anda buat. +\n +\nKami akan meningkatkannya sebagai bagian dari beta, tetapi kami ingin memberitahu Anda saja. + Space untuk tim belum siap tetapi Anda masih bisa mencobanya + Ruangan Tanpa Nama + Beberapa ruangan mungkin disembunyikan karena mereka privat dan Anda membutuhkan undangan. + Beberapa ruangan mungkin disembunyikan karena mereka privat dan Anda membutuhkan undangan. +\nAnda tidak memiliki izin untuk menambahkan ruangan. + Space ini belum ada ruangan + Mohon kontak admin homeserver Anda untuk informasi lanjut + Sepertinya homeserver Anda belum mendukung Spaces + Merasa eksperimental\? +\nAnda dapat menambahkan space yang sudah ada ke space. + Semua ruangan yang Anda berada akan ditampilkan juga di Beranda. + Tampilkan semua ruangan di Beranda + Kelola ruangan dan space + Tandai sebagai tidak disarankan + Tandai sebagai disarankan + Disarankan + Buat space ini publik + Kelola ruangan + Mencari seseorang yang tidak ada di %s\? + %s mengundang Anda + Peringatan membutuhkan dukungan server dan versi ruangan yang eksperimental + Anda telah diundang + Space adalah cara baru untuk mengelompokkan ruangan dan pengguna. + Selamat Datang ke Space! + Tambahkan ruangan + Tambahkan space yang sudah ada + Tambahkan ruangan yang sudah ada + Tambahkan ruangan dan space yang sudah ada + Pilih hal-hal yang ditinggalkan + Tinggalkan ruangan dan space yang dipilih… + Jangan tinggalkan ruangan dan space apa saja + Anda akan meninggalkan semua ruangan dan space di %s. + Tinggalkan semua ruangan dan space + Anda adalah admin satu-satunya di space ini. Meninggalkannya berarti siapa saja tidak akan mempunyai kontrol atas space-nya. + Anda tidak akan bisa bergabung lagi kecuali jika Anda diundang lagi. + Anda hanya salah satu orang di sini. Jika Anda tinggalkan, siapa saja tidak dapat bergabung di masa depan, termasuk Anda. + Apakah Anda yakin untuk meninggalkan space ini\? + Apakah Anda yakin untuk meninggalkan %s\? + Tinggalkan Space + Tambahkan ruangan + Jelajah ruangan + + %d orang yang Anda tahu telah bergabung + + Selamat datang ke %1$s, %2$s. + Anda tidak berada di ruangan apapun saat ini. Di bawah adalah ruangan yang disarankan, tetapi Anda bisa mencari lebih banyak dengan tombol hijau di bawah kanan. + Penjelajahan (%s) + Selesaikan penyiapan + Undang dari email, cari kontak dan lain lagi… + Selesai menyiapkan penjelajahan. + Anda tidak menggunakan server identitas. Supaya bisa mengundang tim dan dapat dijelajahi oleh mereka, konfigurasi sebuah server identitas di bawah. + Alias ini tidak tersedia sekarang. +\nCoba lagi nanti, atau tanyakan ke admin ruangan untuk mengecek jika Anda punya akses. + Bergabung Saja + Bergabung ke Space + Buat Space + Lewat untuk sekarang + Bergabung ke space saya %1$s %2$s + Mereka tidak akan menjadi bagian dari %s + Hanya ke ruangan ini + Mereka akan dapat menjelajahi %s + Undang ke %s + Bagikan tautan + Undang dari nama pengguna atau email + Undang dari nama pengguna + Undang dari email + Hanya Anda saja saat ini. %s akan lebih baik dengan orang lain. + Undang ke %s + Undang orang + Undang orang ke space Anda + Deskripsi + Membuat space… + Sembarangan + Umum + Mari kita buat ruangan untuk masing-masing. Anda dapat menambahkan lebih banyak nanti, termasuk yang sudah ada. + Apa saja yang Anda sedang kerjakan\? + Pastikan orang yang tepat memiliki akses ke %s. Anda bisa menambahkan lagi nanti. + Siapa tim Anda\? + Kami akan membuat ruangannya. Anda bisa menambahkan lagi juga nanti. + Diskusi apa yang Anda ingin punya di %s\? + Tambahkan detail supaya orang-orang bisa mengidentifikasinya. Anda dapat mengubahnya di waktu yang mendatang. + Tambahkan detail untuk membuatnya unik. Anda dapat mengubahnya di waktu yang mendatang. + Buat sebuah space + Memakai undangan, baik untuk Anda sendiri atau tim + Privat + Terbuka untuk siapa saja, baik untuk komunitas + Publik + Space privat untuk Anda & tim Anda + Saya dan tim saya + Space yang privat untuk mengorganisir ruangan Anda + Saya saja + Pastikan orang yang tepat memiliki akses ke %s. Anda dapat mengubahnya nanti. + Dengan siapa Anda bekerja\? + Untuk bergabung ke space yang sudah ada, Anda membutuhkan undangan ke space itu. + Anda bisa mengubahnya nanti + Tipe space apa yang Anda ingin buat\? + Space adalah cara baru untuk mengelompokkan ruangan dan pengguna + Space privat Anda + Space publik Anda + Tambahkan Space + Space privat + Space publik + Apakah Anda yakin untuk menghapus semua pesan yang belum dikirim di ruangan ini\? + Hapus pesan yang belum dikirim + Pesan gagal untuk dikirim + Apakah Anda ingin membatalkan pengiriman pesan\? + Hapus semua pesan yang gagal dikirim + Gagal + Terkirim + Mengirim + Meningkatkan ruangan ke versi baru + Tinggalkan ruangan dengan ID yang diberikan (atau ruangan saat ini jika null) + Bergabung ke Space dengan ID yang diberikan + Tambah ke Space yang dicantumkan + Buat Space + Konten peristiwa + Peristiwa keadaan dikirim! + Peristiwa dikirim! + Peristiwa cacat + Tipe pesan tidak ditemukan + Tidak ada konten + Konten Peristiwa + Kunci Keadaan + Tipe + Kirim Peristiwa Keadaan Kustom + Edit Konten + Peristiwa Keadaan + Kirim Peristiwa Keadaan + Lihat Keadaan Ruangan + Kirim Peristiwa Kustom + Alat Pengembang + Space publik + Ruangan publik + Lihat laporan dibaca + Jangan beritahu + Beritahu tanpa suara + Beritahu dengan suara + Pesan tidak terkirim karena kesalahan + Tidak dicentang + Dicentang + Tutup picker emoji + Buka picker emoji + Ruangannya belum dibuat. Batalkan pembuatan ruangan\? + Tautannya cacat + Kode QR tidak dipindai! + Kode QR tidak valid (URI tidak valid)! + Tidak dapat membuat pesan langsung dengan Anda sendiri! + Bagikan melalui teks + Tidak dapat mencari ruangan ini. Pastikan ruangannya sudah ada. + Tidak dapat mengakses ruangan dimana Anda dicekal. + Konfirmasi PIN untuk menonaktifkan PIN + Ubah PIN sekarang + Ubah PIN + Kode PIN dibutuhkan setiap kali Anda buka ${app_name}. + Kode PIN dibutuhkan setelah 2 menit tidak menggunakan ${app_name}. + Membutuhkan PIN setelah 2 menit + Hanya tampilkan berapa pesan yang belum dibaca dalam notifikasi sederhana. + Tampilkan detail seperti nama ruangan dan konten pesan. + Tampilkan konten di notifikasi + Kode PIN hanya cara satu-satunya untuk membuka ${app_name}. + Aktifkan biometrik spesifik perangkat, seperti sidik jari dan pengenalan wajah. + Aktifkan biometrik + Jika Anda ingin mengatur ulang PIN Anda, ketuk Lupa PIN untuk keluar dan mengatur ulang. + Aktifkan PIN + Atur proteksi + Lindungi akses menggunakan PIN dan biometrik. + Lindungi akses + Untuk mengatur ulang PIN, Anda harus masuk kembali dan buat yang baru. + PIN Baru + Atur Ulang PIN + Lupa PIN\? + Masukkan PIN Anda + Konfirmasi PIN + Buat PIN untuk keamanan + Terlalu banyak kesalahan, Anda telah keluar + Peringatan! Upaya terakhir sebelum keluar! + + %d entri + + + Kode salah, %d upaya tersisa + + Lihat pengaturan Anda untuk mengaktifkan notifikasi push + Notifikasi push dinonaktifkan + Gagal untuk menghapus cekalan + Dicekal oleh %1$s + Batalkan undangan ke %1$s\? + Batalkan undangan + Cari kontak di Matrix + Kontak + Kontak Anda kosong + Mendapatkan kontak Anda… + Cari di kontak saya + Bagikan kode ini dengan orang-orang supaya mereka dapat memindainya dan mulai mengobrol. + Konfirmasi identitas Anda dengan memverifikasi login ini, memberikannya akses ke pesan terenkripsi. + Konfirmasi identitas Anda dengan memverifikasi login ini dari salah satu sesi Anda yang lain, memberikannya akses ke pesan terenkripsi. + Enkripsi ini yang digunakan oleh ruangan ini tidak didukung + Gunakan sesi ini untuk memverifikasi sesi Anda yang baru, memberikan akses ke pesan terenkripsi. + Gunakan %1$s Anda atau gunakan %2$s Anda untuk melanjutkan. + Kontak + Kontak Anda kosong + Tambahkan dari kontak + Simpan kunci cadangan di + PELAJARI LEBIH LANJUT + MENGERTI + Kami senang mengumumkan kami telah mengubah nama! Aplikasi Anda mutakhir dan Anda telah masuk ke akun Anda. + Riot sekarang telah menjadi Element! + Menunggu untuk sejarah enkripsi + Anda tidak dapat mengakses pesan ini karena pengirim telah sengaja tidak mengirim kuncinya + Anda tidak dapat mengakses pesan ini karena sesi Anda tidak dipercayai oleh pengirim + Anda tidak dapat mengakses pesan ini karena Anda telah diblokir oleh pengirim + Karena enkripsi ujung-ke-ujung, Anda mungkin harus menunggu untuk pesan dari seseorang untuk datang karena kunci enkripsinya tidak dikirim secara benar ke Anda. + Tidak Dapat Mendekripsi + Menunggu untuk pesan ini, mungkin memakan beberapa waktu + Anda tidak dapat mengakses pesan ini + Atur avatar + Anda berhasil mengubah pengaturan ruangan + Topik + Nama Ruangan + Simpan Kunci Keamanan Anda di tempat yang aman seperti manajer kata sandi atau kotak penyimpanan aman. + Simpan Kunci Keamanan Anda + Masukkan Frasa Keamanan lagi untuk mengkonfirmasinya. + Frasa Keamanan + Masukkan frasa rahasia yang hanya Anda tahu, dipakai untuk mengamankan rahasia di server Anda. + Atur Frasa Keamanan + Simpan Kunci Keamanan Anda di tempat yang aman seperti manajer kata sandi atau kotak penyimpanan aman. + Simpan Kunci Keamanan Anda + Masukkan frasa rahasia yang hanya Anda tahu, dan buat kunci untuk cadangan. + Gunakan Frasa Keamanan + Buat kunci keamanan untuk disimpan di tempat yang aman seperti manajer kata sandi atau kotak penyimpanan aman. + Gunakan Kunci Keamanan + Siapkan + Lindungi terhadap kehilangan akses ke pesan terenkripsi & data dengan mencadangkan kunci enkripsi di server Anda. + Cadangan aman + Atur Cadangan Aman + Matikan kamera + Nyalakan kamera + Bunyikan mikrofon + Bisukan mikrofon + Buka chat + Peran + Atur peran + Kirim + Masukkan URL server identitas + Sebagai alternatif, Anda bisa masukkan URL server identitas yang lain + Gunakan %1$s + Homeserver Anda (%1$s) meminta untuk menggunakan %2$s sebagai server identitas Anda + Izin pengguna belum diberikan. + Tidak ada asosiasi saat ini dengan pengenal ini. + Asosiasi telah gagal. + Untuk pricvasi Anda, ${app_name} hanya mendukung pengiriman email pengguna yang telah di-hash dan nomor telepon. + Mohon terima ketentuan server identitas ini di pengaturan. + Mohon konfigurasi server identitas. + Server identitas ini telah usang. ${app_name} hanya mendukung API V2. + Operasi ini tidak memungkinkan. Homeserver ini telah usang. + Putuskan koneksi dari server identitas %s\? + Buka ketentuan %s + Memuat bahasa yang tersedia… + Bahasa lainnya + Bahasa saat ini + Kode saya + Bagikan kode saya + Pindai sebuah kode QR + Kami tidak dapat mengundang pengguna. Mohon cek pengguna yang Anda ingin undang dan coba lagi. + + Undangan terkirim ke %1$s dan %2$d lagi + + Bukan kode QR Matrix yang valid + Undangan terkirim ke %1$s dan %2$s + Undangan terkirim ke %1$s + 🔐️ Bergabung dengan saya di ${app_name] + Hi, bicara dengan saya di ${app_name}: %s + Undang teman + Undang Pengguna + Mengundang pengguna… + UNDANGAN + Tambahkan orang + Tambahkan anggota + Kami tidak dapat membuat pesan langsung Anda. Mohon cek pengguna yang Anda ingin undang dan coba lagi. + Tautan ini %1$s akan membawa Anda ke situs lain: %2$s. +\n +\nApakah Anda yakin untuk melanjutkan\? + Space Eksperimental - Ruangan yang Dibatasi. + Tambahkan sebuah space ke space apa saja yang Anda bisa kelola. + Beri nama untuk melanjutkan. + Gagal untuk memvalidasi PIN, mohon ketuk yang baru. + %s di Pengaturan untuk menerima undangan secara langsung di Element. + Tautkan email ini ke akun Anda + Undangan space ini telah dikirim ke %s yang tidak diasosiasikan dengan akun Anda + Undangan ruangan ini telah dikirim ke %s yang tidak diasosiasikan dengan akun Anda + Untuk membantu anggota space menemukan dan bergabung ke ruangan privat, pergilah ke pengaturan ruangannya dengan mengetuk pada avatarnya. + Bantu anggota space untuk menemukan ruangan privat + Periksa ulang tautan ini + Mohon pilih sebuah kata sandi. + Mohon pilih sebuah nama pengguna. + Gagal menyiapkan Tanda Tangan Silang + Tandai sebagai Terpercayai + Verifikasi oleh Emoji secara Interaktif + Verifikasi secara Manual oleh Teks + Verifikasi login + Verifikasi login baru yang mengakses akun Anda: %1$s + Verifikasi semua sesi Anda untuk memastikan akun & pesan Anda aman + Lihat mana Anda masuk + Dienkripsi oleh perangkat yang tidak diverifikasi + Tidak Dienkripsi + mengirim salju ❄️ + mengirim konfeti 🎉 + Mengirim pesan dengan salju + Mengirim pesan dengan konfeti + + Tampilkan %d perangkat Anda dapat memverifikasi sekarang + + Anda akan memulai ulang dengan tidak ada sejarah, tidak ada pesan, perangkat yang dipercayai atau pengguna yang dipercayai + Jika Anda mengatur ulang semuanya + Hanya lakukan ini jika Anda tidak memiliki perangkat yang lain Anda yang bisa pakai untuk memverifikasi perangkat ini. + Atur ulang semuanya + Lupa atau kehilangan semua opsi pemulihan\? Atur ulang semuanya + Gagal mengakses penyimpanan aman + Cadangan tidak dapat didekripsikan dengan Kunci Pemulihan ini: mohon verifikasi jika Anda telah memasukkan Kunci Pemulihan yang benar. + Pilih Kunci Pemulihan Anda, atau masukkan secara manual dengan mengetiknya atau menyalinnya dari papan klip Anda + Gunakan Kunci Pemulihan + Hanya didukung di ruangan terenkripsi + Memaksa sesi kelompok outbound saat ini di ruang terenkripsi untuk dihapus + Gunakan ${app_name} di perangkat Anda yang lain: + Gunakan ${app_name} di perangkat Anda yang lain, ${app_name} Web, ${app_name} Desktop, ${app_name} iOS, ${app_name} untuk Android, atau client Matrix lainnya yang mendukung tanda tangan silang + atau client Matrix lainnya yang mendukung tanda tangan silang + ${app_name} iOS +\n${app_name} Android + ${app_name} Web +\n${app_name} Desktop + Atur kata sandi akun baru… + Tidak dapat menyimpan file media + Tidak dapat menambahkan file media ke Galeri + File media ditambahkan ke Galeri + Mengaktifkan pengaturan ini menambahkan FLAG_SECURE ke semua Aktifitas. Mulai ulang aplikasi ini untuk berpengaruh pada perubahannya. + Mencegah tangkapan layar dari aplikasi + Kunci pemulihan kunci cadangan + Tidak tahu Kunci Frasa Sandi Cadangan, Anda dapat %s. + gunakan kunci pemulihan Kunci Cadangan + Masukkan Kunci Frasa Sandi Pemulihan untuk melanjutkan. + %1$s (%2$s) + Menyimpan rahasi cadangan kunci di SSSS + Membuat kunci SSSS dari kunci pemulihan + Membuat kunci SSSS dari frasa sandi (%s) + Membuat kunci SSSS dari frasa sandi + Mendapatkan kunci curve + Memeriksa kunci cadangan (%s) + Memeriksa kunci cadangan + Mohon masukkan sebuah kunci pemulihan + Bukan kunci pemulihan yang valid + Frasa Sandi Pemulihan + Masukkan %s + Gunakan File + Masukkan %s Anda untuk melanjutkan + Verifikasi diri Anda dan lainnya untuk tetap membuat pesan Anda aman + Aktifkan Tanda Tangan Silang + Peningkatan enkripsi tersedia + Pesan… + Akun ini telah dinonaktifkan. + Nama pengguna dan/atau kata sandi salah. Kata sandi dimulai atau berakhir dengan spasi, mohon dicek. + Mengirim pesan sebagai teks biasa, tanpa mengimpretasikannya sebagai markdown + Atur kepentingan notifikasi dari peristiwa + Pemecahan Masalah + Konfigurasi notifikasi + Gagal mengimpor kunci + Menunggu untuk %s… + Hampir selesai! Menunggu untuk konfirmasi… + Hampir selesai! Apakah perangkat yang lain menunjukkan perisai yang sama\? + "Topik: " + Tambahkan topik + %s untuk memberi tahu orang-orang tentang ruangan ini. + Ini adalah awalnya dari sejarah pesan langsung dengan %s. + Ini adalah awalnya dari percakapan ini. + Ini adalah awalnya dari %s. + Anda bergabung. + %s bergabung. + Anda membuat dan mengatur ruangan ini. + %s membuat dan mengatur ruangan ini. + Enkripsi tidak diaktifkan + Pesan di ruangan ini dienkripsi secara ujung-ke-ujung. + Pesan di ruangan ini dienkripsi secara ujung-ke-ujung. Pelajari lebih lanjut & verifikasi pengguna di profil mereka. + Enkripsi diaktifkan + Jika Anda batalkan, Anda mungkin kehilangan pesan terenkripsi dan data Anda jika Anda kehilangan akses ke login Anda. +\n +\nAnda juga dapat mengatur Cadangan Aman dan kelola kunci Anda di Pengaturan. + Mengatur sebuah Frasa Sandi Pemulihan memungkinkan Anda mengamankan dan mengakses pesan terenkripsi dan kepercayaan. +\n +\nJika Anda tidak ingin mengatur Kata Sandi Pesan, buat sebuah Kunci Pesan saja. + Mengatur sebuah Frasa Sandi Pemulihan memungkinkan Anda mengamankan dan mengakses pesan terenkripsi dan kepercayaan. + Anda tidak bisa melakukannya dari ponsel + Salin ke penyimpanan awan pribadi Anda + Simpan di flashdisk atau penyimpanan cadangan + Cetak dan simpan di tempat yang aman + %2$s & %1$s Anda telah diatur. +\n +\nSimpan mereka di tempat yang aman! Anda membutuhkannya untuk mengakses pesan terenkripsi dan mengamankan informasi jika Anda kehilangan sesi aktif Anda. + Mengatur Cadangan Kunci + Mengsinkronisasikan Kunci Penandatanganan Diri + Mengsinkronisasikan Kunci Pengguna + Mengsinkronisasikan Kunci Utama + Mendefinisikan Kunci SSSS default + Membuat kunci aman dari frasa sandi + Mempublikasikan kunci identitas yang telah dibuat + Gunakan %1$s ini sebagai jaring pengaman jika Anda lupa %2$s Anda. + Selesai + Simpan di tempat yang aman + Anda telah selesai! + Kunci pemulihan Anda + Mengatur pemulihan. + Ini mungkin memakan beberapa detik, mohon sabar. + Masukkan frasa keamanan yang Anda tahu, digunakan untuk mengamankan rahasia di server Anda. + Jangan menggunakan kata sandi Akun Anda. + Masukkan %s Anda lagi untuk mengkonfirmasinya. + Amankan & akses pesan terenkripsi dan kepercayaan dengan sebuah %s. + Masukkan %s Anda untuk melanjutkan. + Konfirmasi %s + Buat Kunci Pesan + Atur sebuah %s + Kata Sandi Akun + Kunci Pesan + Frasa Sandi Pemulihan + Verifikasi Dibatalkan + Verifikasi telah dibatalkan. Anda dapat memulai verifikasi lagi. + Salah satu dari yang berikut ini dapat dikompromikan: +\n +\n- Kata sandi Anda +\n- Homeserver Anda +\n- Perangkat ini, atau perangkat lainnya +\n- Koneksi internet yang digunakan oleh perangkat Anda +\n +\nKami menyarankan Anda untuk segera mengubah kata sandi & kunci pemulihan Anda dalam Pengaturan. + Anda tidak akan memverifikasi %1$s (%2$s) jika Anda membatalkan. Mulai lagi di profil pengguna. + Jika Anda batalkan, Anda tidak dapat membaca pesan terenkripsi di perangkat baru Anda dan pengguna lain tidak akan mempercayainya + Jika Anda batalkan, Anda tidak dapat membaca pesan terenkripsi di perangkat ini dan pengguna lain tidak akan mempercayainya + Akun Anda mungkin dikompromikan + Ini bukan saya + Ketuk untuk menampilkan & verifikasi + Login baru. Apakah itu Anda\? + Segarkan + Akses sejarah pesan terenkripsi + Ekspor Audit + Permintaan Kunci + ${app_name} Android + Kunci telah mutakhir! + Peristiwa dimoderasi oleh admin ruangan, alasannya: %1$s + Peristiwa dihapus oleh pengguna, alasannya: %1$s + Alasan untuk menghapus + Sertakan alasannya + Apakah Anda yakin untuk menghapus peristiwa ini\? Perhatikan bahwa jika Anda menghapus sebuah nama ruangan atau perubahan topik, itu bisa merubah perubahannya. + Konfirmasi Penghapusan + Kirim media dengan ukuran asli + + Kirim video dengan ukuran asli + + + Kirim gambar dengan ukuran asli + + Apakah Anda mau mengirim lampiran ini ke %1$s\? + Hapus… + Anda seharusnya hanya mengakses penyimpanan rahasia di perangkat yang dipercayai + Peringatan: + Masukkan frasa sandi penyimpanan rahasia + Tidak dapat menemukan rahasia di penyimpanan + Jika Anda tidak dapat mengakses sesi yang sudah ada + Peringatan tingkat kepercayaan + Level kepercayaan peringatan + Level kepercayaan default + Dipilih + Video + mempunyai draf yang belum dikirim + Beberapa pesan tidak terkirim + Hapus avatar + Ubah avatar + Gambar + Impor kunci dari file + Buka widget + Tangkap layar + Gagal mengotentikasi + ${app_name} meminta Anda untuk memasukkan kredential untuk melakukan aksi ini. + Otentikasi Ulang Dibutuhkan + Geser untuk mengakhirkan panggilan + Orang tak dikenal + Pindah ke %1$s + Mengkonsultasikan dengan %1$s + Pengguna + Sebuah kesalahan terjadi ketika memindahkan panggilan + Pindahkan + Sambungkan + Konsultasikan dulu + %1$s Ketuk untuk kembali + Panggilan aktif (%1$s) · + + %1$d panggilan aktif · + + + %1$d panggilan yang dijeda + + + 1 panggilan aktif (%1$s) · %2$d panggilan yang dijeda + + Panggilan aktif (%1$s) + Ada sebuah kesalahan saat mencari nomor telepon + Tombol penyetel + Koneksi gagal + Tidak ada jawaban + Panggilan video yang terlewat + Panggilan suara yang terlewat + Panggilan video ditolak + Panggilan suara ditolak + Panggilan video berakhir • %1$s + Panggilan suara berakhir • %1$s + Panggilan video yang aktif + Panggilan suara yang aktif + Panggilan video yang masuk + Panggilan suara yang masuk + Panggil lagi + Panggilan ini telah berakhir + %1$s menolak panggilan ini + Anda menolak panggilan ini + Anda menolak panggilan ini %s + Anda sedang di panggilan ini + %1$s memulai panggilan + Anda memulai panggilan + Tautan Matrix + Buang perubahan + Ada perubahan yang belum disimpan. Buang perubahannya\? + Sign In Baru + Gunakan Frasa Sandi Pemulihan atau Kunci + Membuat poll sederhana + Opsi yang Dipilih + + %d suara - Hasil akhir + + + %d suara + + Hapus data akun dengan tipe %1$s\? +\n +\nHati-hati menggunakannya, ini dapat menyebabkan perilaku yang tidak terduga. + Data Akun + Alat Pengembang + Mode pesawat diaktifkan + Koneksi ke server telah hilang + Tidak + Ya + Hampir selesai! Apakah %s menampilkan perisai yang sama\? + Kode QR + Atur Ulang Kunci + Memulai Tanda Tangan Silang + Sampai pengguna ini mempercayai sesi ini, pesan yang dikirim ke dan dari itu diberi peringatan. Atau, Anda dapat memverifikasinya secara manual. + %1$s (%2$s) masuk dengan menggunakan sesi baru: + Sesi ini telah dipercaya untuk perpesanan yang aman karena %1$s (%2$s) memverifikasinya: + Membuat space… + Tampilkan info yang berguna untuk membantu debugging aplikasi + Tampilkan info debug di layar + Tidak terlihat sebagai alamat email yang valid + Buka Pengaturan Penemuan + Cari dengan nama, ID atau email + Buat Space Baru + Siapa saja bisa menemukan space ini dan bergabung + Akses space + Siapa yang bisa akses\? + Aktifkan notifikasi email untuk %s + Untuk menerima email dengan notifikasi, mohon tautkan sebuah email ke akun Matrix Anda + Notifikasi email + Tingkatkan space ini + Ubah nama space + Aktifkan enkripsi space + Ubah alamat utama untuk space ini + Ubah avatar space + Anda tidak memiliki izin untuk memperbarui peran yang dibutuhkan untuk mengubah bagian dari space ini + Pilih peran yang dibutuhkan untuk mengubah bagian dari space ini + Lihat dan perbarui peran yang dibutuhkan untuk mengubah bagian dari space. + Izin space + Menghapus cekalan akan mengizinkan pengguna untuk bergabung ke space lagi. + Mencekal pengguna akan mengeluarkan pengguna dari space ini dan mencegah pengguna untuk bergabung lagi. + mengeluarkan pengguna akan mengeluarkannya dari space ini. +\n +\nUntuk mencegah pengguna untuk bergabung lagi, Anda seharusnya cekal pengguna itu saja. \ No newline at end of file From 98ba9d63421ee32cf3bb0627779976aa0b65d80d Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Sat, 2 Oct 2021 05:41:28 +0000 Subject: [PATCH 013/144] Translated using Weblate (Ukrainian) Currently translated at 88.1% (2344 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- vector/src/main/res/values-uk/strings.xml | 195 ++++++++++++++++++++-- 1 file changed, 184 insertions(+), 11 deletions(-) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index b62bbd8bd0..8c0c3927af 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -280,7 +280,7 @@ Надіслати в Прочитано Увійти до Кімнати - Логін + Ім\'я користувача Зареєструватися Увійти Вийти @@ -329,9 +329,9 @@ Логін вже використовується Домашній сервер: Сервер ідентифікації: - Я перевірив(ла) свою email адресу - Для скидання паролю введіть email прив\'язаний до облікового запису: - Необхідно ввести email прив\'язаний до вашого облікового запису\'. + Мною перевірено адресу е-пошти + Для скидання паролю введіть е-пошту прив\'язану до облікового запису: + Необхідно ввести е-пошту прив\'язану до вашого облікового запису. Необхідно ввести новий пароль. На адресу %s надіслано електронний лист. Як тільки ви перейдете за посиланням у ньому, натисніть нижче. Не вдалося перевірити email: переконайтеся, що ви перейшли за посиланням у листі @@ -543,8 +543,8 @@ Аватар Показуване ім\'я - Email - Додати email + Е-пошта + Додати адресу е-пошти Телефон Додати номер телефону Екран системної інформації застосунку. @@ -1101,7 +1101,7 @@ Виберіть мелодію викликів: Прийняти Будь ласка, ознайомтесь та прийміть правила цього серверу: - Позначити як прочитане + Позначити прочитаним Запуск сервісу Резервна копія ключа Використати резервну копію ключа @@ -1298,7 +1298,7 @@ Виявні електронні адреси Недійсна відповідь виявлення домашнього сервера Налаштуйте свою виявність. - Видимість + Виявність Не вдалося встановити зв’язок у режимі реального часу. \nПопросіть адміністратора вашого домашнього сервера налаштувати сервер TURN для надійної роботи викликів. ${app_name} не вдалося здійснити виклик @@ -1616,7 +1616,7 @@ Скасувати запрошення Якщо перестати нехтувати цього користувача, усі його повідомлення стануть знову видимими. Рознехтувати користувача - Нехтування цього користувача призведе до видалення його повідомлень з усіх кімнат, де ви обидва є учасниками. + Нехтування цього користувача призведе до вилучення його повідомлень з усіх спільних кімнат. \n \nВи можете будь-коли змінити цю дію в загальних налаштуваннях. Нехтувати користувача @@ -2017,10 +2017,10 @@ Лютоcтрус Режим розробника вмикає приховані функції та може зробити застосунок менш стабільним. Лише для розробників! Відкликати згоду - Надати згоду + Погодитися Обраний вами сервер ідентифікації не має жодних умов використання. Продовжуйте лише якщо довіряєте власнику сервісу Сервер ідентифікації не має умов використання - Зазначте, будь ласка, адресу сервера ідентифікації + Зазначте адресу сервера ідентифікації Неможливо під\'єднатись до сервера ідентифікації Зазначте адресу сервера ідентифікації Чи погоджуєтесь ви надіслати дані ваших контактів (номери телефонів та/або електронні адреси) на налаштований сервер ідентифікації(%1$s) для виявлення відомих вам наявних контактів\? @@ -2578,4 +2578,177 @@ Шукати у моїх контактах Телефонна книга Не вдалося розшифрувати + ${app_name} потребує дозволу, щоб зберегти ваші ключі E2E на диск. +\n +\nДозвольте доступ у наступному спливному вікні, щоб могти експортувати ключі власноруч. + Закрити нагадування про резервне копіювання ключів + Схоже, що відповідь сервера надто тривала, це може бути спричинено або поганим з’єднанням, або помилкою сервера. Повторіть спробу через деякий час. + Повторіть спробу, коли погодитесь з умовами свого домашнього сервера. + Текстове повідомлення надіслано на %s. Введіть код підтвердження, який воно містить. + Ми надіслали вам електронний лист для підтвердження на %s, відкрийте свою е-пошту та клацніть на посилання для підтвердження + Ми надіслали вам електронний лист для підтвердження на %s, відкрийте свою е-пошту та клацніть на посилання для підтвердження + Ви + Кнопки бота + Опитування + Файл + Голосове + Аудіо + Зображення. + Відео. + Не безпечно + Вони відрізняються + Вони збігаються + Створення простору… + Деякі символи заборонені + Вкажіть адресу кімнати + Ця адреса вже використовується + Адреса простору + Адреса кімнати + Закороткий опис + Очистити дані + Очистити дані + Очистити всі дані + Ви вийшли + Переглянуто + Matrix ID + Застарілий домашній сервер + Введений код неправильний. Перевірте його. + Перевірте свою е-пошту + Погодьтеся з усіма умовами, щоб продовжити + Пройдіть перевірку Captcha + Вибрати власний домашній сервер + Вибрати Element Matrix Services + Вибрати matrix.org + Очистити особисті дані + Пароль + Пароль + Ім\'я користувача + Ім\'я користувача або е-пошта + Зареєструватися у %1$s + Схоже, що номер телефону неправильний. Перевірте його + Міжнародний номер телефону повинен починатися з «+» + Використовуйте міжнародний формат (номер телефону повинен починатися з «+») + Надіслати повторно + Введіть код + Ми надіслали код на %1$s. Введіть його внизу, щоб підтвердити його. + Підтвердити телефонний номер + Використовуйте міжнародний формат. + За бажання вкажіть номер телефону, щоб знайомі люди могли виявити вас. + На вашу адресу е-пошти буде надіслано лист для підтвердження встановлення нового пароля. + Е-пошта + Е-пошта (необов\'язково) + Е-пошта + Вказати адресу е-пошти + Увімкнено режим «У літаку» + Попередження + Попередження + Попередження + Вказати номер телефону + Це не схоже на правильна адресу е-пошти + Це ім\'я користувача вже зайнято + Далі + Далі + Далі + Далі + Далі + Скинути пароль на %1$s + Ця е-пошта не пов\'язана з жодним обліковим записом. + Перепрошуємо, сервер не приймає нових облікових записів. + Сталася помилка під час завантаження сторінки: %1$s (%2$d) + Введіть адресу сервера, який ви хочете використовувати + Введіть адресу Modular Element або сервера, який ви хочете використовувати + Успіх! + Ваш пароль скинуто. + Мною перевірено адресу е-пошти + Торкніться посилання, щоб підтвердити новий пароль. Перейшовши за посиланням, яке він містить, натисніть внизу. + Лист для підтвердження е-пошти надіслано на %1$s. + Адреса Element Matrix Services + Очистити історію + Продовжити за допомогою єдиного входу + Зареєструватися + Під\'єднатися до власного сервера + Під\'єднатися до Element Matrix Services + Під\'єднатися до %1$s + Перевірти вхідні + Ця е-пошта не пов\'язана з жодним обліковим записом + Продовжити + Продовжити + єдиний вхід + Зареєструватися за допомогою %s + Продовжити за допомогою %s + Або + Преміумрозміщення для організацій + Преміумрозміщення для організацій + Приєднуйтесь до мільйонів безплатно на найбільшому загальнодоступному сервері + Як і е-пошта, облікові записи мають одну домівку, хоча ви можете спілкуватися з ким завгодно + Оберіть сервер + Почати + Розширте й налаштуйте свою роботу + Це ваша бесіда. Керуйте нею. + Ви дозволили доступ лише за запрошенням. + %1$s дозволяє доступ лише за запрошенням. + Ви зробили кімнату доступною лише за запрошенням. + %1$s робить кімнату доступною лише за запрошенням. + Ви зробили кімнату загальнодоступною для тих, хто має посилання. + %1$s робить кімнату загальнодоступною для тих, хто має посилання. + Тривале натискання на кімнату відкриє додаткові опції + Введіть ключові слова, щоб знайти реакцію. + Спойлер + Надсилає це повідомлення у вигляді спойлеру + Ви не внесли жодних змін + %1$s не вносить жодних змін + Вилучити з неважливих + Додати до неважливих + Вилучити з обраних + Додати до обраних + Немає з\'єднання з мережею + Не вдалося обробити спільні дані + Сталася помилка завантаження вкладення. + + Прочитано %d користувачем + Прочитали %d користувачі + Прочитали %d користувачів + Прочитали %d користувачів + + Прочитано %s + %1$s і %2$s прочитали + %1$s, %2$s і %3$s прочитали + + %1$s, %2$s і %3$d інший прочитали + %1$s, %2$s і %3$d інших прочитали + %1$s, %2$s і %3$d інших прочитали + %1$s, %2$s і %3$d інших прочитали + + Вниз + Створити нову особисту бесіду скануванням QR-коду + Створити нову особисту бесіду за допомогою Matrix ID + Виявлення (%s) + Запрошення за е-поштою, пошук контактів і багато іншого… + Завершити налаштування виявності. + Виявні номери телефону + Опції виявності з\'являться після додавання номера телефону. + Опції виявності з\'являться після додавання е-пошти. + Відкрити налаштування виявності + Пошук за іменем, ID або е-поштою + Створити новий простір + Будь-хто може знайти простір і приєднатися + Доступ до простору + Хто може мати доступ\? + Увімкнути сповіщення е-поштою для %s + Щоб отримувати сповіщення е-поштою, пов’яжіть її зі своїм обліковим записом Matrix + Сповіщення е-поштою + Оновити простір + Змінити назву простору + Увімкнути шифрування простору + Змінити основну адресу простору + Змінити аватар простору + Ви не маєте дозволу оновлювати ролі, необхідні для зміни різних частин цього простору + Виберіть ролі, необхідні для зміни різних частин цього простору + Перегляд та оновлення ролей, необхідних для зміни різних частин простору. + Дозволи простору + Розблокування користувачів дозволить їм знову приєднатися до простору. + Блокування користувачів викине їх із цього простору та не дозволить їм знову приєднатися. + викидання користувачів прибере їх з цього простору. +\n +\nЩоб запобігти їх повторному приєднанню, замість цього слід заблокувати їх. \ No newline at end of file From 8376a946a8d360bef3c408a3e337b772c06f797d Mon Sep 17 00:00:00 2001 From: Szimszon Date: Wed, 29 Sep 2021 12:43:24 +0000 Subject: [PATCH 014/144] Translated using Weblate (Hungarian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/hu/ --- fastlane/metadata/android/hu-HU/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/hu-HU/changelogs/40103000.txt diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103000.txt b/fastlane/metadata/android/hu-HU/changelogs/40103000.txt new file mode 100644 index 0000000000..40673b30b1 --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Szobák terekbe szervezése +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From b117d313a641ed732f028953d3a788d3c423e170 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Wed, 29 Sep 2021 15:58:10 +0000 Subject: [PATCH 015/144] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/pt_BR/ --- fastlane/metadata/android/pt-BR/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40103000.txt diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103000.txt b/fastlane/metadata/android/pt-BR/changelogs/40103000.txt new file mode 100644 index 0000000000..c046c1cbc9 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Organize suas salas usando Espaços! +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From 72ef6bffbd3018821db8dc4a9985536fc8e08f13 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Thu, 30 Sep 2021 02:41:11 +0000 Subject: [PATCH 016/144] Translated using Weblate (Ukrainian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/uk/ --- fastlane/metadata/android/uk/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/uk/changelogs/40103000.txt diff --git a/fastlane/metadata/android/uk/changelogs/40103000.txt b/fastlane/metadata/android/uk/changelogs/40103000.txt new file mode 100644 index 0000000000..64a168cbe9 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Упорядковуйте свої кімнати за допомогою Просторів! +Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From 495bdae9df5d36e428a3234513b69fd12637ee4e Mon Sep 17 00:00:00 2001 From: sr093906 Date: Wed, 29 Sep 2021 14:53:08 +0000 Subject: [PATCH 017/144] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hans/ --- fastlane/metadata/android/zh-CN/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/zh-CN/changelogs/40103000.txt diff --git a/fastlane/metadata/android/zh-CN/changelogs/40103000.txt b/fastlane/metadata/android/zh-CN/changelogs/40103000.txt new file mode 100644 index 0000000000..96ec8b3322 --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/40103000.txt @@ -0,0 +1,2 @@ +此版本主要更改:使用空间组织你的聊天室! +完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.0 From 1bd575f6db9db845ee91a83649d2d69965c65397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 29 Sep 2021 20:45:43 +0000 Subject: [PATCH 018/144] Translated using Weblate (Estonian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/et/ --- fastlane/metadata/android/et/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/et/changelogs/40103000.txt diff --git a/fastlane/metadata/android/et/changelogs/40103000.txt b/fastlane/metadata/android/et/changelogs/40103000.txt new file mode 100644 index 0000000000..643ae1ce0e --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: halda oma jututubasid koondades neid uut tüüpi kogukondadesse! +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From af4be85c34bedc2a4dbcb51d6594d480ebc3e34c Mon Sep 17 00:00:00 2001 From: random Date: Thu, 30 Sep 2021 10:10:05 +0000 Subject: [PATCH 019/144] Translated using Weblate (Italian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/it/ --- fastlane/metadata/android/it-IT/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/it-IT/changelogs/40103000.txt diff --git a/fastlane/metadata/android/it-IT/changelogs/40103000.txt b/fastlane/metadata/android/it-IT/changelogs/40103000.txt new file mode 100644 index 0000000000..6ad9001bfd --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: organizza le tue stanze usando gli Spazi! +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From 720154572fd64c7e0ac1165ce70cf083629dcd10 Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Fri, 1 Oct 2021 11:35:43 +0000 Subject: [PATCH 020/144] Translated using Weblate (Persian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/fa/ --- fastlane/metadata/android/fa/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/fa/changelogs/40103000.txt diff --git a/fastlane/metadata/android/fa/changelogs/40103000.txt b/fastlane/metadata/android/fa/changelogs/40103000.txt new file mode 100644 index 0000000000..ba43459c0a --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40103000.txt @@ -0,0 +1,2 @@ +تغییرات عمده در این نگارش: سازمان‌دهی اتاق‌هایتان با استفاده از فضاها +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From f0cba97bd88286f08be599303a20a12c9ccf7f4b Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 30 Sep 2021 01:39:38 +0000 Subject: [PATCH 021/144] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hant/ --- fastlane/metadata/android/zh-TW/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40103000.txt diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103000.txt b/fastlane/metadata/android/zh-TW/changelogs/40103000.txt new file mode 100644 index 0000000000..fbae69cd21 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103000.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:使用空間來整理您的聊天室! +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.0 From 9b8837f4267a3e9e330e329187b394b83f3ea904 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Wed, 29 Sep 2021 15:51:36 +0000 Subject: [PATCH 022/144] Translated using Weblate (Czech) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/cs/ --- fastlane/metadata/android/cs-CZ/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40103000.txt diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103000.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103000.txt new file mode 100644 index 0000000000..f97ff3ef3a --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Uspořádejte si místnosti pomocí Prostorů! +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From d8bbd17eca1cee8cd445ce9d4df7c9fbb37078eb Mon Sep 17 00:00:00 2001 From: Linerly Date: Fri, 1 Oct 2021 12:09:20 +0000 Subject: [PATCH 023/144] Translated using Weblate (Indonesian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/id/ --- fastlane/metadata/android/id/changelogs/40102000.txt | 2 +- fastlane/metadata/android/id/changelogs/40102010.txt | 2 ++ fastlane/metadata/android/id/changelogs/40103000.txt | 2 ++ fastlane/metadata/android/id/full_description.txt | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/id/changelogs/40102010.txt create mode 100644 fastlane/metadata/android/id/changelogs/40103000.txt diff --git a/fastlane/metadata/android/id/changelogs/40102000.txt b/fastlane/metadata/android/id/changelogs/40102000.txt index 2258b114e8..f7d93e2e4f 100644 --- a/fastlane/metadata/android/id/changelogs/40102000.txt +++ b/fastlane/metadata/android/id/changelogs/40102000.txt @@ -1,2 +1,2 @@ Perubahan utama dalam versi ini: Pesan Suara diaktifkan secara default -Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.16 +Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/id/changelogs/40102010.txt b/fastlane/metadata/android/id/changelogs/40102010.txt new file mode 100644 index 0000000000..e77f0327b0 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Perubahan utama di versi ini: Banyak perbaikan di VoIP dan Space (masih beta). +Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/id/changelogs/40103000.txt b/fastlane/metadata/android/id/changelogs/40103000.txt new file mode 100644 index 0000000000..b68383fd49 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Perubahan utama di versi ini: Rapikan ruangan Anda menggunakan sebuah Space! +Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.0 diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt index 75249c6a20..4182df3396 100644 --- a/fastlane/metadata/android/id/full_description.txt +++ b/fastlane/metadata/android/id/full_description.txt @@ -11,7 +11,7 @@ Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas ya Element benar-benar berbeda dari aplikasi perpesanan dan kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi. Matrix memungkinkan hosting sendiri untuk memberi pengguna kepemilikan maksimum dan kontrol data dan pesan mereka. Pesan privasi dan terenkripsi -Element melindungi Anda dari iklan yang tidak diinginkan, data penambangan dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang ditanda tangani silang. +Element melindungi Anda dari iklan yang tidak diinginkan, data penambangan dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang ditandatangani silang Element memberi Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan aman dengan siapa pun di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan dengan aplikasi seperti Slack. From c5bbda24c4e261a16bb3f4872a4c000f73b26af6 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Thu, 30 Sep 2021 11:19:10 +0000 Subject: [PATCH 024/144] Translated using Weblate (Albanian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sq/ --- fastlane/metadata/android/sq/changelogs/40103000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/sq/changelogs/40103000.txt diff --git a/fastlane/metadata/android/sq/changelogs/40103000.txt b/fastlane/metadata/android/sq/changelogs/40103000.txt new file mode 100644 index 0000000000..ecd5568c02 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Ndryshime kryesore në këtë version: Sistemoni dhomat tuaja duke përdorur Hapësira! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From 01206195ad88ea2c42c67853270ff52bc465a601 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sat, 2 Oct 2021 16:27:28 +0000 Subject: [PATCH 025/144] Translated using Weblate (Indonesian) Currently translated at 100.0% (2658 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/id/ --- vector/src/main/res/values-in/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index d0a46aee5e..295d7427a2 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -1754,7 +1754,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Bisa ditemukan oleh lain Lihat Persyaratan Persyaratan Layanan - Tampilkan Sejarah Suntingan + Tampilkan Sejarah Editan Menggabung ruangan… Saran Kontak @@ -1778,7 +1778,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Tidak bisa menemukan apa yang Anda cari\? Saring obrolan… Tidak ada suntingan yang ditemukan - Suntingan Pesan + Editan Pesan (diedit) File %1$s telah diunduh! Mengompresi video %d%% @@ -1819,7 +1819,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Gabung ruangan untuk memulai menggunakan aplikasi. Coba Lagi Balas - Sunting + Edit Sepertinya Anda mencoba menyambung ke homeserver lain. Apakah Anda ingin keluar\? Tidak ada server identitas yang dikonfigurasikan, dibutuhkan untuk mengatur ulang kata sandi Anda. Anda tidak menggunakan server identitas apapun From 32a39ad461f397cb956995a6ba2636f05ec68102 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sat, 2 Oct 2021 16:38:47 +0000 Subject: [PATCH 026/144] Translated using Weblate (Indonesian) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/id/ --- fastlane/metadata/android/id/changelogs/40103000.txt | 2 +- fastlane/metadata/android/id/full_description.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fastlane/metadata/android/id/changelogs/40103000.txt b/fastlane/metadata/android/id/changelogs/40103000.txt index b68383fd49..bf7b5d8d5d 100644 --- a/fastlane/metadata/android/id/changelogs/40103000.txt +++ b/fastlane/metadata/android/id/changelogs/40103000.txt @@ -1,2 +1,2 @@ -Perubahan utama di versi ini: Rapikan ruangan Anda menggunakan sebuah Space! +Perubahan utama di versi ini: Organisir ruangan Anda menggunakan sebuah Space! Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.0 diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt index 4182df3396..bd357bb161 100644 --- a/fastlane/metadata/android/id/full_description.txt +++ b/fastlane/metadata/android/id/full_description.txt @@ -11,7 +11,7 @@ Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas ya Element benar-benar berbeda dari aplikasi perpesanan dan kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi. Matrix memungkinkan hosting sendiri untuk memberi pengguna kepemilikan maksimum dan kontrol data dan pesan mereka. Pesan privasi dan terenkripsi -Element melindungi Anda dari iklan yang tidak diinginkan, data penambangan dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang ditandatangani silang +Element melindungi Anda dari iklan yang tidak diinginkan, penambangan data dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang ditandatangani secara silang. Element memberi Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan aman dengan siapa pun di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan dengan aplikasi seperti Slack. From 724bd7dd1a6bb83ac3fd072c23aa886ad827a3e1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Oct 2021 10:12:03 +0200 Subject: [PATCH 027/144] Update docs/design.md --- docs/design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design.md b/docs/design.md index 959ca40864..2e27f00ebf 100644 --- a/docs/design.md +++ b/docs/design.md @@ -34,7 +34,7 @@ Some of them depend on the theme, so ensure to use theme attributes and not colo - ensure that the correct layer is selected. Sometimes the parent layer has to be selected on the left panel - on the right panel, click on "export" - select SVG - - you cqn check the preview of what will be exported + - you can check the preview of what will be exported - click on "export" and save the file locally - unzip the file if necessary From f72a34ed0877b5b2de3db8db43f713456709d422 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 4 Oct 2021 14:09:21 +0200 Subject: [PATCH 028/144] Mavericks 2: continue replacing Rx --- .../app/features/home/HomeDetailViewModel.kt | 4 +- .../UnknownDeviceDetectorSharedViewModel.kt | 70 ++++++++-------- .../home/room/detail/RoomDetailViewModel.kt | 30 +++---- .../RoomMemberProfileViewModel.kt | 21 ++--- .../devices/DeviceListBottomSheetViewModel.kt | 5 +- .../roomprofile/alias/RoomAliasViewModel.kt | 2 +- .../banned/RoomBannedMemberListViewModel.kt | 7 +- .../members/RoomMemberListViewModel.kt | 52 ++++++------ .../RoomNotificationSettingsViewModel.kt | 4 +- .../uploads/RoomUploadsViewModel.kt | 9 +-- .../CrossSigningSettingsViewModel.kt | 50 ++++++------ ...iceVerificationInfoBottomSheetViewModel.kt | 10 ++- .../settings/devices/DevicesViewModel.kt | 54 +++++++------ .../threepids/ThreePidsSettingsViewModel.kt | 25 +++--- .../features/share/IncomingShareViewModel.kt | 3 +- .../app/features/spaces/SpaceMenuViewModel.kt | 5 +- .../leave/SpaceLeaveAdvancedViewModel.kt | 26 +++--- .../app/features/widgets/WidgetViewModel.kt | 16 ++-- .../signout/ServerBackupStatusViewModel.kt | 79 ++++++++----------- .../workers/signout/SignoutCheckViewModel.kt | 7 +- 20 files changed, 236 insertions(+), 243 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 01eb6e3c31..0f50b82aa8 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -47,6 +46,7 @@ import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx import timber.log.Timber @@ -95,7 +95,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho updateShowDialPadTab() observeDataStore() callManager.addProtocolsCheckerListener(this) - session.rx().liveUser(session.myUserId).execute { + session.flow().liveUser(session.myUserId).execute { copy( myMatrixItem = it.invoke()?.getOrNull()?.toMatrixItem() ) diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index db3317a214..143f843954 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -25,25 +25,25 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.settings.VectorPreferences -import io.reactivex.Observable +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.sample import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.MatrixItem -import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo -import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo -import org.matrix.android.sdk.rx.rx import timber.log.Timber -import java.util.concurrent.TimeUnit data class UnknownDevicesState( val myMatrixItem: MatrixItem.UserItem? = null, @@ -98,31 +98,31 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted } ) - Observable.combineLatest, List, Optional, List>( - session.rx().liveUserCryptoDevices(session.myUserId), - session.rx().liveMyDevicesInfo(), - session.rx().liveCrossSigningPrivateKeys(), - { cryptoList, infoList, pInfo -> - // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") -// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}") - infoList - .filter { info -> - // filter verified session, by checking the crypto device info - cryptoList.firstOrNull { info.deviceId == it.deviceId }?.isVerified?.not().orFalse() - } - // filter out ignored devices - .filter { !ignoredDeviceList.contains(it.deviceId) } - .sortedByDescending { it.lastSeenTs } - .map { deviceInfo -> - val deviceKnownSince = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }?.firstTimeSeenLocalTs ?: 0 - DeviceDetectionInfo( - deviceInfo, - deviceKnownSince > currentSessionTs + 60_000, // short window to avoid false positive, - pInfo.getOrNull()?.selfSigned != null // adding this to pass distinct when cross sign change - ) - } - } + combine( + session.flow().liveUserCryptoDevices(session.myUserId), + session.flow().liveMyDevicesInfo(), + session.flow().liveCrossSigningPrivateKeys() ) + { cryptoList, infoList, pInfo -> + // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") +// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}") + infoList + .filter { info -> + // filter verified session, by checking the crypto device info + cryptoList.firstOrNull { info.deviceId == it.deviceId }?.isVerified?.not().orFalse() + } + // filter out ignored devices + .filter { !ignoredDeviceList.contains(it.deviceId) } + .sortedByDescending { it.lastSeenTs } + .map { deviceInfo -> + val deviceKnownSince = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }?.firstTimeSeenLocalTs ?: 0 + DeviceDetectionInfo( + deviceInfo, + deviceKnownSince > currentSessionTs + 60_000, // short window to avoid false positive, + pInfo.getOrNull()?.selfSigned != null // adding this to pass distinct when cross sign change + ) + } + } .distinctUntilChanged() .execute { async -> // Timber.v("## Detector trigger passed distinct") @@ -132,14 +132,14 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted ) } - session.rx().liveUserCryptoDevices(session.myUserId) + session.flow().liveUserCryptoDevices(session.myUserId) .distinctUntilChanged() - .throttleLast(5_000, TimeUnit.MILLISECONDS) - .subscribe { + .sample(5_000) + .onEach { // If we have a new crypto device change, we might want to trigger refresh of device info session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) } - .disposeOnClear() + .launchIn(viewModelScope) // trigger a refresh of lastSeen / last Ip session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 1f6fec0410..ddb1c51b5b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -28,7 +28,6 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay -import com.jakewharton.rxrelay2.PublishRelay import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -60,11 +59,11 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.voice.VoicePlayerHelper -import io.reactivex.Observable import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn @@ -112,8 +111,6 @@ import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap import timber.log.Timber import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -143,7 +140,7 @@ class RoomDetailViewModel @AssistedInject constructor( private val eventId = initialState.eventId private val invisibleEventsObservable = BehaviorRelay.create() private val visibleEventsObservable = BehaviorRelay.create() - private var timelineEvents = PublishRelay.create>() + private var timelineEvents = MutableSharedFlow>(0) val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId) // Same lifecycle than the ViewModel (survive to screen rotation) @@ -1533,14 +1530,12 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun getUnreadState() { - Observable - .combineLatest, RoomSummary, UnreadState>( - timelineEvents.observeOn(Schedulers.computation()), - room.rx().liveRoomSummary().unwrap(), - { timelineEvents, roomSummary -> - computeUnreadState(timelineEvents, roomSummary) - } - ) + combine( + timelineEvents, + room.flow().liveRoomSummary().unwrap() + ) { timelineEvents, roomSummary -> + computeUnreadState(timelineEvents, roomSummary) + } // We don't want live update of unread so we skip when we already had a HasUnread or HasNoUnread .distinctUntilChanged { previous, current -> when { @@ -1549,10 +1544,9 @@ class RoomDetailViewModel @AssistedInject constructor( else -> false } } - .subscribe { - setState { copy(unreadState = it) } + .setOnEach { + copy(unreadState = it) } - .disposeOnClear() } private fun computeUnreadState(events: List, roomSummary: RoomSummary): UnreadState { @@ -1619,7 +1613,7 @@ class RoomDetailViewModel @AssistedInject constructor( } override fun onTimelineUpdated(snapshot: List) { - timelineEvents.accept(snapshot) + timelineEvents.tryEmit(snapshot) // PreviewUrl if (vectorPreferences.showUrlPreviews()) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index ab5337d7cd..14624b800e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -25,20 +25,17 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.powerlevel.PowerLevelsFlowFactory -import io.reactivex.Observable -import io.reactivex.functions.BiFunction import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.combineLatest import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -50,8 +47,6 @@ import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role @@ -60,8 +55,6 @@ import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, private val stringProvider: StringProvider, @@ -114,7 +107,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } } - session.rx().liveUserCryptoDevices(initialState.userId) + session.flow().liveUserCryptoDevices(initialState.userId) .map { Pair( it.fold(true, { prev, dev -> prev && dev.isVerified }), @@ -128,14 +121,14 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v ) } - session.rx().liveCrossSigningInfo(initialState.userId) + session.flow().liveCrossSigningInfo(initialState.userId) .execute { copy(userMXCrossSigningInfo = it.invoke()?.getOrNull()) } } private fun observeIgnoredState() { - session.rx().liveIgnoredUsers() + session.flow().liveIgnoredUsers() .map { ignored -> ignored.find { it.userId == initialState.userId @@ -252,7 +245,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE) } - room.rx().liveRoomMembers(queryParams) + room.flow().liveRoomMembers(queryParams) .map { it.firstOrNull().toOptional() } .unwrap() .execute { @@ -312,7 +305,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v roomSummaryLive.execute { copy(isRoomEncrypted = it.invoke()?.isEncrypted == true) } - roomSummaryLive.combine(powerLevelsContentLive){roomSummary, powerLevelsContent -> + roomSummaryLive.combine(powerLevelsContentLive) { roomSummary, powerLevelsContent -> val roomName = roomSummary.toMatrixItem().getBestName() val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) when (val userPowerLevel = powerLevelsHelper.getUserRole(initialState.userId)) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index 2baf27e694..b638d84181 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.rx.rx @@ -55,14 +56,14 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } init { - session.rx().liveUserCryptoDevices(args.userId) + session.flow().liveUserCryptoDevices(args.userId) .execute { copy(cryptoDevices = it).also { refreshSelectedId() } } - session.rx().liveCrossSigningInfo(args.userId) + session.flow().liveCrossSigningInfo(args.userId) .execute { copy(memberCrossSigningKey = it.invoke()?.getOrNull()) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index cb26d6407b..28a1804fab 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -131,7 +131,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun observeRoomSummary() { - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index b92d1a4bd3..813d50c6bb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -38,6 +38,8 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap @@ -54,15 +56,14 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initia private val room = session.getRoom(initialState.roomId)!! init { - val rxRoom = room.rx() - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) } - rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) + room.flow().liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) .execute { copy( bannedMemberSummaries = it diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index cd6d6f1610..2873b20400 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile.members +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory @@ -27,10 +28,16 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory -import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.switchMap import kotlinx.coroutines.launch import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse @@ -44,10 +51,9 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role -import org.matrix.android.sdk.rx.asObservable -import org.matrix.android.sdk.rx.mapOptional -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, @@ -87,28 +93,28 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState memberships = Membership.activeMemberships() } - Observable - .combineLatest, PowerLevelsContent, RoomMemberSummaries>( - room.rx().liveRoomMembers(roomMemberQueryParams), - room.rx() - .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) - .mapOptional { it.content.toModel() } - .unwrap(), - { roomMembers, powerLevelsContent -> - buildRoomMemberSummaries(powerLevelsContent, roomMembers) - } - ) + combine( + room.flow().liveRoomMembers(roomMemberQueryParams), + room.flow() + .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + .mapOptional { it.content.toModel() } + .unwrap() + ) + { roomMembers, powerLevelsContent -> + buildRoomMemberSummaries(powerLevelsContent, roomMembers) + } + .execute { async -> copy(roomMemberSummaries = async) } if (room.isEncrypted()) { - room.rx().liveRoomMembers(roomMemberQueryParams) - .observeOn(AndroidSchedulers.mainThread()) - .switchMap { membersSummary -> + room.flow().liveRoomMembers(roomMemberQueryParams) + .flowOn(Dispatchers.Main) + .flatMapLatest { membersSummary -> session.cryptoService().getLiveCryptoDeviceInfo(membersSummary.map { it.userId }) - .asObservable() - .doOnError { Timber.e(it) } + .asFlow() + .catch { Timber.e(it) } .map { deviceList -> // If any key change, emit the userIds list deviceList.groupBy { it.userId }.mapValues { @@ -147,7 +153,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } private fun observeRoomSummary() { - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) @@ -155,7 +161,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } private fun observeThirdPartyInvites() { - room.rx().liveStateEvents(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE)) + room.flow().liveStateEvents(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE)) .execute { async -> copy(threePidInvites = async) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index 51cd4a4ecc..d944b77f7d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -28,6 +28,8 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap @@ -64,7 +66,7 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( } private fun observeSummary() { - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index 2ae27f7799..4526024143 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.uploads -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -25,15 +24,15 @@ import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.message.MessageType -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap class RoomUploadsViewModel @AssistedInject constructor( @Assisted initialState: RoomUploadsViewState, @@ -66,7 +65,7 @@ class RoomUploadsViewModel @AssistedInject constructor( } private fun observeRoomSummary() { - room.rx().liveRoomSummary() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index 6f1058ec7c..a8fafb096a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.settings.crosssigning -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -28,8 +27,8 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.login.ReAuthHelper -import io.reactivex.Observable import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor @@ -38,14 +37,11 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo -import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth -import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.util.awaitCallback -import org.matrix.android.sdk.rx.rx import timber.log.Timber import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -59,26 +55,26 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { init { - Observable.combineLatest, Optional, Pair, Optional>>( - session.rx().liveMyDevicesInfo(), - session.rx().liveCrossSigningInfo(session.myUserId), - { myDevicesInfo, mxCrossSigningInfo -> - myDevicesInfo to mxCrossSigningInfo - } + combine( + session.flow().liveMyDevicesInfo(), + session.flow().liveCrossSigningInfo(session.myUserId) ) - .execute { data -> - val crossSigningKeys = data.invoke()?.second?.getOrNull() - val xSigningIsEnableInAccount = crossSigningKeys != null - val xSigningKeysAreTrusted = session.cryptoService().crossSigningService().checkUserTrust(session.myUserId).isVerified() - val xSigningKeyCanSign = session.cryptoService().crossSigningService().canCrossSign() + { myDevicesInfo, mxCrossSigningInfo -> + myDevicesInfo to mxCrossSigningInfo + } + .execute { data -> + val crossSigningKeys = data.invoke()?.second?.getOrNull() + val xSigningIsEnableInAccount = crossSigningKeys != null + val xSigningKeysAreTrusted = session.cryptoService().crossSigningService().checkUserTrust(session.myUserId).isVerified() + val xSigningKeyCanSign = session.cryptoService().crossSigningService().canCrossSign() - copy( - crossSigningInfo = crossSigningKeys, - xSigningIsEnableInAccount = xSigningIsEnableInAccount, - xSigningKeysAreTrusted = xSigningKeysAreTrusted, - xSigningKeyCanSign = xSigningKeyCanSign - ) - } + copy( + crossSigningInfo = crossSigningKeys, + xSigningIsEnableInAccount = xSigningIsEnableInAccount, + xSigningKeysAreTrusted = xSigningKeysAreTrusted, + xSigningKeyCanSign = xSigningKeyCanSign + ) + } } var uiaContinuation: Continuation? = null @@ -126,7 +122,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( } Unit } - is CrossSigningSettingsAction.SsoAuthDone -> { + is CrossSigningSettingsAction.SsoAuthDone -> { Timber.d("## UIA - FallBack success") if (pendingAuth != null) { uiaContinuation?.resume(pendingAuth!!) @@ -134,7 +130,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( uiaContinuation?.resumeWithException(IllegalArgumentException()) } } - is CrossSigningSettingsAction.PasswordAuthDone -> { + is CrossSigningSettingsAction.PasswordAuthDone -> { val decryptedPass = session.loadSecureSecret(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS) uiaContinuation?.resume( UserPasswordAuth( @@ -144,7 +140,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( ) ) } - CrossSigningSettingsAction.ReAuthCancelled -> { + CrossSigningSettingsAction.ReAuthCancelled -> { Timber.d("## UIA - Reauth cancelled") _viewEvents.post(CrossSigningSettingsViewEvents.HideModalWaitingView) uiaContinuation?.resumeWithException(Exception()) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index 975236beb1..38342efc46 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -25,7 +25,9 @@ import dagger.assisted.AssistedFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.flow.map import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.rx.rx @@ -48,7 +50,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As isRecoverySetup = session.sharedSecretStorageService.isRecoverySetup() ) } - session.rx().liveCrossSigningInfo(session.myUserId) + session.flow().liveCrossSigningInfo(session.myUserId) .execute { copy( hasAccountCrossSigning = it.invoke()?.getOrNull() != null, @@ -56,7 +58,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As ) } - session.rx().liveUserCryptoDevices(session.myUserId) + session.flow().liveUserCryptoDevices(session.myUserId) .map { list -> list.firstOrNull { it.deviceId == deviceId } } @@ -67,7 +69,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As ) } - session.rx().liveUserCryptoDevices(session.myUserId) + session.flow().liveUserCryptoDevices(session.myUserId) .map { it.size } .execute { copy( @@ -79,7 +81,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As copy(deviceInfo = Loading()) } - session.rx().liveMyDevicesInfo() + session.flow().liveMyDevicesInfo() .map { devices -> devices.firstOrNull { it.deviceId == deviceId } ?: DeviceInfo(deviceId = deviceId) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index 04e743b8df..b2b4c0c396 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.devices -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -34,31 +33,36 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.login.ReAuthHelper -import io.reactivex.Observable import io.reactivex.subjects.PublishSubject import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.data.LoginFlowTypes +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState -import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo -import org.matrix.android.sdk.api.auth.UIABaseAuth -import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.internal.util.awaitCallback -import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.util.concurrent.TimeUnit import javax.net.ssl.HttpsURLConnection @@ -118,18 +122,18 @@ class DevicesViewModel @AssistedInject constructor( ) } - Observable.combineLatest, List, List>( - session.rx().liveUserCryptoDevices(session.myUserId), - session.rx().liveMyDevicesInfo(), - { cryptoList, infoList -> - infoList - .sortedByDescending { it.lastSeenTs } - .map { deviceInfo -> - val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId } - DeviceFullInfo(deviceInfo, cryptoDeviceInfo) - } - } + combine( + session.flow().liveUserCryptoDevices(session.myUserId), + session.flow().liveMyDevicesInfo() ) + { cryptoList, infoList -> + infoList + .sortedByDescending { it.lastSeenTs } + .map { deviceInfo -> + val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId } + DeviceFullInfo(deviceInfo, cryptoDeviceInfo) + } + } .distinctUntilChanged() .execute { async -> copy( @@ -137,7 +141,7 @@ class DevicesViewModel @AssistedInject constructor( ) } - session.rx().liveCrossSigningInfo(session.myUserId) + session.flow().liveCrossSigningInfo(session.myUserId) .execute { copy( hasAccountCrossSigning = it.invoke()?.getOrNull() != null, @@ -146,24 +150,24 @@ class DevicesViewModel @AssistedInject constructor( } session.cryptoService().verificationService().addListener(this) -// session.rx().liveMyDeviceInfo() +// session.flow().liveMyDeviceInfo() // .execute { // copy( // devices = it // ) // } - session.rx().liveUserCryptoDevices(session.myUserId) + session.flow().liveUserCryptoDevices(session.myUserId) .map { it.size } .distinctUntilChanged() - .throttleLast(5_000, TimeUnit.MILLISECONDS) - .subscribe { + .sample(5_000) + .onEach { // If we have a new crypto device change, we might want to trigger refresh of device info session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) } - .disposeOnClear() + .launchIn(viewModelScope) -// session.rx().liveUserCryptoDevices(session.myUserId) +// session.flow().liveUserCryptoDevices(session.myUserId) // .execute { // copy( // cryptoDevices = it diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index 4d94389850..cd0d74a288 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.threepids -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -33,15 +32,15 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ReadOnceTrue import im.vector.app.features.auth.ReAuthActivity import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth -import org.matrix.android.sdk.api.auth.UIABaseAuth -import org.matrix.android.sdk.api.auth.UserPasswordAuth -import org.matrix.android.sdk.rx.rx import timber.log.Timber import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -102,7 +101,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } private fun observeThreePids() { - session.rx() + session.flow() .liveThreePIds(true) .execute { copy( @@ -112,7 +111,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } private fun observePendingThreePids() { - session.rx() + session.flow() .livePendingThreePIds() .execute { copy( @@ -131,13 +130,13 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( override fun handle(action: ThreePidsSettingsAction) { when (action) { - is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action) + is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action) is ThreePidsSettingsAction.ContinueThreePid -> handleContinueThreePid(action) - is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action) - is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action) - is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) - is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action) - ThreePidsSettingsAction.SsoAuthDone -> { + is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action) + is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action) + is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) + is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action) + ThreePidsSettingsAction.SsoAuthDone -> { Timber.d("## UIA - FallBack success") if (pendingAuth != null) { uiaContinuation?.resume(pendingAuth!!) @@ -155,7 +154,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( ) ) } - ThreePidsSettingsAction.ReAuthCancelled -> { + ThreePidsSettingsAction.ReAuthCancelled -> { Timber.d("## UIA - Reauth cancelled") uiaContinuation?.resumeWithException(Exception()) uiaContinuation = null diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index 665d3b28fa..5fdd10e742 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.rx import java.util.concurrent.TimeUnit @@ -68,7 +69,7 @@ class IncomingShareViewModel @AssistedInject constructor( memberships = listOf(Membership.JOIN) } session - .rx().liveRoomSummaries(queryParams) + .flow().liveRoomSummaries(queryParams) .execute { copy(roomSummaries = it) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index ad764363db..bb30670da9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -42,6 +42,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.rx import timber.log.Timber @@ -77,7 +78,7 @@ class SpaceMenuViewModel @AssistedInject constructor( session.getRoom(initialState.spaceId)?.let { room -> - room.rx().liveRoomSummary().subscribe { + room.flow().liveRoomSummary().onEach { it.getOrNull()?.let { if (it.membership == Membership.LEAVE) { setState { copy(leavingState = Success(Unit)) } @@ -87,7 +88,7 @@ class SpaceMenuViewModel @AssistedInject constructor( } } } - }.disposeOnClear() + }.launchIn(viewModelScope) PowerLevelsFlowFactory(room) .createFlow() diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index 0daa2522e9..3d24cf6225 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.spaces.leave -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -31,6 +30,8 @@ import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import okhttp3.internal.toImmutableList import org.matrix.android.sdk.api.query.ActiveSpaceFilter @@ -38,7 +39,8 @@ import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class SpaceLeaveAdvancedViewModel @AssistedInject constructor( @@ -95,17 +97,17 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( val spaceSummary = session.getRoomSummary(initialState.spaceId) setState { copy(spaceSummary = spaceSummary) } session.getRoom(initialState.spaceId)?.let { room -> - room.rx().liveRoomSummary().subscribe { - it.getOrNull()?.let { - if (it.membership == Membership.LEAVE) { - setState { copy(leaveState = Success(Unit)) } - if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { - // switch to home? - appStateHandler.setCurrentSpace(null, session) + room.flow().liveRoomSummary() + .unwrap() + .onEach { + if (it.membership == Membership.LEAVE) { + setState { copy(leaveState = Success(Unit)) } + if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { + // switch to home? + appStateHandler.setCurrentSpace(null, session) + } } - } - } - } + }.launchIn(viewModelScope) } viewModelScope.launch { diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index 44d246885a..f88bf6ef56 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.widgets import android.net.Uri -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -26,11 +25,12 @@ import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -41,9 +41,10 @@ import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerS import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure -import org.matrix.android.sdk.rx.mapOptional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap import timber.log.Timber import javax.net.ssl.HttpsURLConnection @@ -118,16 +119,15 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi if (room == null) { return } - room.rx().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + room.flow().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .map { PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, null) } - .subscribe { - setState { copy(canManageWidgets = it) } + .setOnEach { + copy(canManageWidgets = it) } - .disposeOnClear() } private fun observeWidgetIfNeeded() { diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index 0076bf580e..c3719ffd8e 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -25,27 +25,23 @@ import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import io.reactivex.Observable -import io.reactivex.functions.Function4 -import io.reactivex.subjects.PublishSubject +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.sample import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME -import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener -import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo -import org.matrix.android.sdk.rx.rx -import java.util.concurrent.TimeUnit +import org.matrix.android.sdk.flow.flow data class ServerBackupStatusViewState( val bannerState: Async = Uninitialized @@ -91,43 +87,38 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS val keysExportedToFile = MutableLiveData() val keysBackupState = MutableLiveData() - private val keyBackupPublishSubject: PublishSubject = PublishSubject.create() + private val keyBackupFlow = MutableSharedFlow(0) init { session.cryptoService().keysBackupService().addListener(this) - keysBackupState.value = session.cryptoService().keysBackupService().state - - Observable.combineLatest, Optional, KeysBackupState, Optional, BannerState>( - session.rx().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)), - session.rx().liveCrossSigningInfo(session.myUserId), - keyBackupPublishSubject, - session.rx().liveCrossSigningPrivateKeys(), - Function4 { _, crossSigningInfo, keyBackupState, pInfo -> - // first check if 4S is already setup - if (session.sharedSecretStorageService.isRecoverySetup()) { - // 4S is already setup sp we should not display anything - return@Function4 when (keyBackupState) { - KeysBackupState.BackingUp -> BannerState.BackingUp - else -> BannerState.Hidden - } - } - - // So recovery is not setup - // Check if cross signing is enabled and local secrets known - if ( - crossSigningInfo.getOrNull() == null - || (crossSigningInfo.getOrNull()?.isTrusted() == true - && pInfo.getOrNull()?.allKnown().orFalse()) - ) { - // So 4S is not setup and we have local secrets, - return@Function4 BannerState.Setup(numberOfKeys = getNumberOfKeysToBackup()) - } - - BannerState.Hidden + val liveUserAccountData = session.flow().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) + val liveCrossSigningInfo = session.flow().liveCrossSigningInfo(session.myUserId) + val liveCrossSigningPrivateKeys = session.flow().liveCrossSigningPrivateKeys() + combine(liveUserAccountData, liveCrossSigningInfo, keyBackupFlow, liveCrossSigningPrivateKeys) { _, crossSigningInfo, keyBackupState, pInfo -> + // first check if 4S is already setup + if (session.sharedSecretStorageService.isRecoverySetup()) { + // 4S is already setup sp we should not display anything + return@combine when (keyBackupState) { + KeysBackupState.BackingUp -> BannerState.BackingUp + else -> BannerState.Hidden } - ) - .throttleLast(1000, TimeUnit.MILLISECONDS) // we don't want to flicker or catch transient states + } + + // So recovery is not setup + // Check if cross signing is enabled and local secrets known + if ( + crossSigningInfo.getOrNull() == null + || (crossSigningInfo.getOrNull()?.isTrusted() == true + && pInfo.getOrNull()?.allKnown().orFalse()) + ) { + // So 4S is not setup and we have local secrets, + return@combine BannerState.Setup(numberOfKeys = getNumberOfKeysToBackup()) + } + BannerState.Hidden + + } + .sample(1000) // we don't want to flicker or catch transient states .distinctUntilChanged() .execute { async -> copy( @@ -135,7 +126,7 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS ) } - keyBackupPublishSubject.onNext(session.cryptoService().keysBackupService().state) + keyBackupFlow.tryEmit(session.cryptoService().keysBackupService().state) } /** @@ -165,7 +156,7 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS } override fun onStateChange(newState: KeysBackupState) { - keyBackupPublishSubject.onNext(session.cryptoService().keysBackupService().state) + keyBackupFlow.tryEmit(session.cryptoService().keysBackupService().state) keysBackupState.value = newState } diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt index 8190a37056..057d9e31f8 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.workers.signout import android.net.Uri -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -35,6 +34,8 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.crypto.keys.KeysExporter +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME @@ -42,7 +43,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_S import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow import timber.log.Timber data class SignoutCheckViewState( @@ -97,7 +98,7 @@ class SignoutCheckViewModel @AssistedInject constructor( ) } - session.rx().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) + session.flow().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) .map { session.sharedSecretStorageService.isRecoverySetup() } From fadbb60f90901ded9fe6e4e950a172be0208090b Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 4 Oct 2021 17:50:45 +0200 Subject: [PATCH 029/144] Mavericks 2: continue replacing Rx --- .../discovery/DiscoverySettingsViewModel.kt | 13 ++- .../vector/app/features/home/HomeActivity.kt | 39 ++++--- .../features/home/HomeActivityViewModel.kt | 16 ++- .../app/features/home/HomeDetailViewModel.kt | 24 ++-- .../room/breadcrumbs/BreadcrumbsViewModel.kt | 4 +- .../home/room/detail/RoomDetailFragment.kt | 105 +++++++++--------- .../app/features/invite/InvitesAcceptor.kt | 55 ++++----- .../features/permalink/PermalinkHandler.kt | 81 ++++++-------- .../room/RequireActiveMembershipViewModel.kt | 41 ++++--- .../roomdirectory/PublicRoomsFragment.kt | 30 ++--- .../roomdirectory/RoomDirectoryViewModel.kt | 30 +++-- .../roompreview/RoomPreviewViewModel.kt | 15 ++- .../roomprofile/RoomProfileViewModel.kt | 28 ++--- .../settings/RoomSettingsViewModel.kt | 66 +++++------ .../settings/SecretsSynchronisationInfo.kt | 71 ++++++++++++ .../VectorSettingsSecurityPrivacyFragment.kt | 14 ++- .../settings/ignored/IgnoredUsersViewModel.kt | 7 +- .../features/share/IncomingShareViewModel.kt | 19 ++-- .../features/spaces/SpacesListViewModel.kt | 30 ++--- .../spaces/explore/SpaceDirectoryFragment.kt | 52 ++++----- .../spaces/explore/SpaceDirectoryViewModel.kt | 12 +- .../userdirectory/UserListViewModel.kt | 7 +- .../app/features/widgets/WidgetViewModel.kt | 4 +- .../RoomWidgetPermissionViewModel.kt | 9 +- 24 files changed, 417 insertions(+), 355 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 412a1ab5b4..b248bcd065 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.discovery -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -25,17 +24,19 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow class DiscoverySettingsViewModel @AssistedInject constructor( @Assisted initialState: DiscoverySettingsState, @@ -84,12 +85,12 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private fun observeThreePids() { - session.rx() + session.flow() .liveThreePIds(true) - .subscribe { + .onEach { retrieveBinding(it) } - .disposeOnClear() + .launchIn(viewModelScope) } override fun onCleared() { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 942ada9982..7e02c043dc 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -27,6 +27,7 @@ import android.view.MenuItem import androidx.core.view.GravityCompat import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout +import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar @@ -72,6 +73,7 @@ import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.push.fcm.FcmHelper import io.reactivex.android.schedulers.AndroidSchedulers +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.permalinks.PermalinkService @@ -288,26 +290,23 @@ class HomeActivity : } else -> deepLink } - permalinkHandler.launch( - context = this, - deepLink = resolvedLink, - navigationInterceptor = this, - buildTask = true - ) - // .delay(500, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { isHandled -> - if (!isHandled) { - val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) - || deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) - MaterialAlertDialogBuilder(this) - .setTitle(R.string.dialog_title_error) - .setMessage(if (isMatrixToLink) R.string.permalink_malformed else R.string.universal_link_malformed) - .setPositiveButton(R.string.ok, null) - .show() - } - } - .disposeOnDestroy() + lifecycleScope.launch { + val isHandled = permalinkHandler.launch( + context = this@HomeActivity, + deepLink = resolvedLink, + navigationInterceptor = this@HomeActivity, + buildTask = true + ) + if (!isHandled) { + val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) + || deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) + MaterialAlertDialogBuilder(this@HomeActivity) + .setTitle(R.string.dialog_title_error) + .setMessage(if (isMatrixToLink) R.string.permalink_malformed else R.string.universal_link_malformed) + .setPositiveButton(R.string.ok, null) + .show() + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index fb2401edc1..fa3df1ca97 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MavericksViewModelFactory @@ -31,6 +32,8 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor @@ -44,6 +47,7 @@ import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.util.awaitCallback @@ -100,9 +104,9 @@ class HomeActivityViewModel @AssistedInject constructor( .crossSigningService().allPrivateKeysKnown() safeActiveSession - .rx() + .flow() .liveCrossSigningInfo(safeActiveSession.myUserId) - .subscribe { + .onEach { val isVerified = it.getOrNull()?.isTrusted() ?: false if (!isVerified && onceTrusted) { // cross signing keys have been reset @@ -116,15 +120,15 @@ class HomeActivityViewModel @AssistedInject constructor( } onceTrusted = isVerified } - .disposeOnClear() + .launchIn(viewModelScope) } private fun observeInitialSync() { val session = activeSessionHolder.getSafeActiveSession() ?: return session.getSyncStatusLive() - .asObservable() - .subscribe { status -> + .asFlow() + .onEach { status -> when (status) { is SyncStatusService.Status.Progressing -> { // Schedule a check of the bootstrap when the init sync will be finished @@ -145,7 +149,7 @@ class HomeActivityViewModel @AssistedInject constructor( ) } } - .disposeOnClear() + .launchIn(viewModelScope) } /** diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 0f50b82aa8..316c1791fe 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -37,6 +38,7 @@ import im.vector.app.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter @@ -48,7 +50,6 @@ import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.asObservable -import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.util.concurrent.TimeUnit @@ -182,25 +183,18 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } private fun observeSyncState() { - session.rx() + session.flow() .liveSyncState() - .subscribe { syncState -> - setState { - copy(syncState = syncState) - } + .setOnEach { syncState -> + copy(syncState = syncState) } - .disposeOnClear() session.getSyncStatusLive() - .asObservable() - .subscribe { - if (it is SyncStatusService.Status.IncrementalSyncStatus) { - setState { - copy(incrementalSyncStatus = it) - } - } + .asFlow() + .filterIsInstance() + .setOnEach { + copy(incrementalSyncStatus = it) } - .disposeOnClear() } private fun observeRoomGroupingMethod() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index a945e4bbb1..f32f132fe9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.rx class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, @@ -61,12 +62,11 @@ class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: B // PRIVATE METHODS ***************************************************************************** private fun observeBreadcrumbs() { - session.rx() + session.flow() .liveBreadcrumbs(roomSummaryQueryParams { displayName = QueryStringValue.NoCondition memberships = listOf(Membership.JOIN) }) - .observeOn(Schedulers.computation()) .execute { asyncBreadcrumbs -> copy(asyncBreadcrumbs = asyncBreadcrumbs) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 5da00b7e2f..3420b52de1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -1590,57 +1590,54 @@ class RoomDetailFragment @Inject constructor( } } -// TimelineEventController.Callback ************************************************************ + // TimelineEventController.Callback ************************************************************ override fun onUrlClicked(url: String, title: String): Boolean { - permalinkHandler - .launch(requireActivity(), url, object : NavigationInterceptor { - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { - // Same room? - if (roomId == roomDetailArgs.roomId) { - // Navigation to same room - if (eventId == null) { - showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room)) - } else { - // Highlight and scroll to this event - roomDetailViewModel.handle(RoomDetailAction.NavigateToEvent(eventId, true)) + viewLifecycleOwner.lifecycleScope.launch { + val isManaged = permalinkHandler + .launch(requireActivity(), url, object : NavigationInterceptor { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { + // Same room? + if (roomId == roomDetailArgs.roomId) { + // Navigation to same room + if (eventId == null) { + showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room)) + } else { + // Highlight and scroll to this event + roomDetailViewModel.handle(RoomDetailAction.NavigateToEvent(eventId, true)) + } + return true } + // Not handled + return false + } + + override fun navToMemberProfile(userId: String, deepLink: Uri): Boolean { + openRoomMemberProfile(userId) return true } - // Not handled - return false - } - - override fun navToMemberProfile(userId: String, deepLink: Uri): Boolean { - openRoomMemberProfile(userId) - return true - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { managed -> - if (!managed) { - if (title.isValidUrl() && url.isValidUrl() && URL(title).host != URL(url).host) { - MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive) - .setTitle(R.string.external_link_confirmation_title) - .setMessage( - getString(R.string.external_link_confirmation_message, title, url) - .toSpannable() - .colorizeMatchingText(url, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) - .colorizeMatchingText(title, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) - ) - .setPositiveButton(R.string._continue) { _, _ -> - openUrlInExternalBrowser(requireContext(), url) - } - .setNegativeButton(R.string.cancel, null) - .show() - } else { - // Open in external browser, in a new Tab - openUrlInExternalBrowser(requireContext(), url) - } - } + }) + if (!isManaged) { + if (title.isValidUrl() && url.isValidUrl() && URL(title).host != URL(url).host) { + MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive) + .setTitle(R.string.external_link_confirmation_title) + .setMessage( + getString(R.string.external_link_confirmation_message, title, url) + .toSpannable() + .colorizeMatchingText(url, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) + .colorizeMatchingText(title, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) + ) + .setPositiveButton(R.string._continue) { _, _ -> + openUrlInExternalBrowser(requireContext(), url) + } + .setNegativeButton(R.string.cancel, null) + .show() + } else { + // Open in external browser, in a new Tab + openUrlInExternalBrowser(requireContext(), url) } - .disposeOnDestroyView() + } + } // In fact it is always managed return true } @@ -1799,15 +1796,15 @@ class RoomDetailFragment @Inject constructor( } override fun onRoomCreateLinkClicked(url: String) { - permalinkHandler - .launch(requireContext(), url, object : NavigationInterceptor { - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { - requireActivity().finish() - return false - } - }) - .subscribe() - .disposeOnDestroyView() + viewLifecycleOwner.lifecycleScope.launchWhenResumed { + permalinkHandler + .launch(requireContext(), url, object : NavigationInterceptor { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { + requireActivity().finish() + return false + } + }) + } } override fun onReadReceiptsClicked(readReceipts: List) { diff --git a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt index 6e7de1c35b..09eff756d5 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt @@ -18,10 +18,14 @@ package im.vector.app.features.invite import im.vector.app.ActiveSessionDataSource import im.vector.app.features.session.coroutineScope -import io.reactivex.Observable import io.reactivex.disposables.Disposable import kotlinx.coroutines.async -import kotlinx.coroutines.launch +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import org.matrix.android.sdk.api.extensions.orFalse @@ -31,9 +35,8 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow import timber.log.Timber -import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton @@ -50,7 +53,7 @@ class InvitesAcceptor @Inject constructor( private lateinit var activeSessionDisposable: Disposable private val shouldRejectRoomIds = mutableSetOf() - private val invitedRoomDisposables = HashMap() + private val activeSessionIds = mutableSetOf() private val semaphore = Semaphore(1) fun initialize() { @@ -71,34 +74,32 @@ class InvitesAcceptor @Inject constructor( if (!autoAcceptInvites.isEnabled) { return } - if (invitedRoomDisposables.containsKey(session.sessionId)) { + if (activeSessionIds.contains(session.sessionId)) { return } + activeSessionIds.add(session.sessionId) session.addListener(this) val roomQueryParams = roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } - val rxSession = session.rx() - Observable - .combineLatest( - rxSession.liveRoomSummaries(roomQueryParams), - rxSession.liveRoomChangeMembershipState().debounce(1, TimeUnit.SECONDS), - { invitedRooms, _ -> invitedRooms.map { it.roomId } } - ) + val flowSession = session.flow() + combine( + flowSession.liveRoomSummaries(roomQueryParams), + flowSession.liveRoomChangeMembershipState().debounce(1000) + ) { invitedRooms, _ -> invitedRooms.map { it.roomId } } .filter { it.isNotEmpty() } - .subscribe { invitedRoomIds -> - session.coroutineScope.launch { - semaphore.withPermit { - Timber.v("Invited roomIds: $invitedRoomIds") - for (roomId in invitedRoomIds) { - async { session.joinRoomSafely(roomId) }.start() - } - } - } - } - .also { - invitedRoomDisposables[session.sessionId] = it - } + .onEach { invitedRoomIds -> + joinInvitedRooms(session, invitedRoomIds) + }.launchIn(session.coroutineScope) + } + + private suspend fun joinInvitedRooms(session: Session, invitedRoomIds: List) = coroutineScope { + semaphore.withPermit { + Timber.v("Invited roomIds: $invitedRoomIds") + for (roomId in invitedRoomIds) { + async { session.joinRoomSafely(roomId) }.start() + } + } } private suspend fun Session.joinRoomSafely(roomId: String) { @@ -138,6 +139,6 @@ class InvitesAcceptor @Inject constructor( override fun onSessionStopped(session: Session) { session.removeListener(this) - invitedRoomDisposables.remove(session.sessionId)?.dispose() + activeSessionIds.remove(session.sessionId) } } diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index fd5fea0fe8..f41abeff08 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -23,10 +23,10 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.toast import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkParser import org.matrix.android.sdk.api.session.permalinks.PermalinkService @@ -34,80 +34,71 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.rx.rx import javax.inject.Inject class PermalinkHandler @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, private val navigator: Navigator) { - fun launch( + suspend fun launch( context: Context, deepLink: String?, navigationInterceptor: NavigationInterceptor? = null, buildTask: Boolean = false - ): Single { + ): Boolean { val uri = deepLink?.let { Uri.parse(it) } return launch(context, uri, navigationInterceptor, buildTask) } - fun launch( + suspend fun launch( context: Context, deepLink: Uri?, navigationInterceptor: NavigationInterceptor? = null, buildTask: Boolean = false - ): Single { + ): Boolean { if (deepLink == null || !isPermalinkSupported(context, deepLink.toString())) { - return Single.just(false) + return false } - return Single - .fromCallable { - PermalinkParser.parse(deepLink) - } - .subscribeOn(Schedulers.computation()) - .observeOn(AndroidSchedulers.mainThread()) - .flatMap { permalinkData -> - handlePermalink(permalinkData, deepLink, context, navigationInterceptor, buildTask) - } - .onErrorReturnItem(false) + return tryOrNull { + withContext(Dispatchers.Default) { + val permalinkData = PermalinkParser.parse(deepLink) + handlePermalink(permalinkData, deepLink, context, navigationInterceptor, buildTask) + } + } ?: false } - private fun handlePermalink( + private suspend fun handlePermalink( permalinkData: PermalinkData, rawLink: Uri, context: Context, navigationInterceptor: NavigationInterceptor?, buildTask: Boolean - ): Single { + ): Boolean { return when (permalinkData) { is PermalinkData.RoomLink -> { - permalinkData.getRoomId() - .observeOn(AndroidSchedulers.mainThread()) - .map { - val roomId = it.getOrNull() - if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId, rawLink) != true) { - openRoom( - context = context, - roomId = roomId, - permalinkData = permalinkData, - rawLink = rawLink, - buildTask = buildTask - ) - } - true - } + val roomId = permalinkData.getRoomId() + if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId, rawLink) != true) { + openRoom( + context = context, + roomId = roomId, + permalinkData = permalinkData, + rawLink = rawLink, + buildTask = buildTask + ) + } + true } is PermalinkData.GroupLink -> { navigator.openGroupDetail(permalinkData.groupId, context, buildTask) - Single.just(true) + true } is PermalinkData.UserLink -> { if (navigationInterceptor?.navToMemberProfile(permalinkData.userId, rawLink) != true) { navigator.openRoomMemberProfile(userId = permalinkData.userId, roomId = null, context = context, buildTask = buildTask) } - Single.just(true) + true } is PermalinkData.FallbackLink -> { - Single.just(false) + false } is PermalinkData.RoomEmailInviteLink -> { val data = RoomPreviewData( @@ -118,7 +109,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti roomType = permalinkData.roomType ) navigator.openRoomPreview(context, data) - Single.just(true) + true } } } @@ -130,15 +121,13 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti } } - private fun PermalinkData.RoomLink.getRoomId(): Single> { + private suspend fun PermalinkData.RoomLink.getRoomId(): String? { val session = activeSessionHolder.getSafeActiveSession() return if (isRoomAlias && session != null) { - session.rx() - .getRoomIdByAlias(roomIdOrAlias, true) - .map { it.getOrNull()?.roomId.toOptional() } - .subscribeOn(Schedulers.io()) + val roomIdByAlias = session.getRoomIdByAlias(roomIdOrAlias, true) + roomIdByAlias.getOrNull()?.roomId } else { - Single.just(Optional.from(roomIdOrAlias)) + roomIdOrAlias } } diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index 2b299b014e..62519336f5 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -20,16 +20,26 @@ import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext -import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import io.reactivex.Observable -import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.subscribe +import kotlinx.coroutines.flow.switchMap import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -37,8 +47,8 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap /** * This ViewModel observe a room summary and notify when the room is left @@ -66,28 +76,31 @@ class RequireActiveMembershipViewModel @AssistedInject constructor( } } - private val roomIdObservable = BehaviorRelay.createDefault(Optional.from(initialState.roomId)) + private val roomIdFlow = MutableStateFlow(Optional.from(initialState.roomId)) init { observeRoomSummary() } private fun observeRoomSummary() { - roomIdObservable + roomIdFlow .unwrap() - .switchMap { roomId -> - val room = session.getRoom(roomId) ?: return@switchMap Observable.just(Optional.empty()) - room.rx() + .flatMapLatest { roomId -> + val room = session.getRoom(roomId) ?: return@flatMapLatest flow{ + val emptyResult = Optional.empty() + emit(emptyResult) + } + room.flow() .liveRoomSummary() .unwrap() - .observeOn(Schedulers.computation()) + .flowOn(Dispatchers.Default) .map { mapToLeftViewEvent(room, it) } } .unwrap() - .subscribe { event -> + .onEach { event -> _viewEvents.post(event) } - .disposeOnClear() + .launchIn(viewModelScope) } private fun mapToLeftViewEvent(room: Room, roomSummary: RoomSummary): Optional { @@ -128,7 +141,7 @@ class RequireActiveMembershipViewModel @AssistedInject constructor( setState { copy(roomId = action.roomId) } - roomIdObservable.accept(Optional.from(action.roomId)) + roomIdFlow.tryEmit(Optional.from(action.roomId)) } }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt index 8214b26fea..d34dacf045 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt @@ -22,6 +22,7 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.appcompat.queryTextChanges @@ -37,6 +38,7 @@ import im.vector.app.databinding.FragmentPublicRoomsBinding import im.vector.app.features.permalink.NavigationInterceptor import im.vector.app.features.permalink.PermalinkHandler import io.reactivex.rxkotlin.subscribeBy +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom @@ -125,20 +127,20 @@ class PublicRoomsFragment @Inject constructor( } override fun onUnknownRoomClicked(roomIdOrAlias: String) { - val permalink = session.permalinkService().createPermalink(roomIdOrAlias) - permalinkHandler - .launch(requireContext(), permalink, object : NavigationInterceptor { - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { - requireActivity().finish() - return false - } - }) - .subscribe { isSuccessful -> - if (!isSuccessful) { - requireContext().toast(R.string.room_error_not_found) - } - } - .disposeOnDestroyView() + viewLifecycleOwner.lifecycleScope.launch { + val permalink = session.permalinkService().createPermalink(roomIdOrAlias) + val isHandled = permalinkHandler + .launch(requireContext(), permalink, object : NavigationInterceptor { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { + requireActivity().finish() + return false + } + }) + + if (!isHandled) { + requireContext().toast(R.string.room_error_not_found) + } + } } override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt index 8955167e50..a2089e6cd5 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomdirectory -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading @@ -31,6 +30,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session @@ -38,7 +38,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFilter import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow import timber.log.Timber class RoomDirectoryViewModel @AssistedInject constructor( @@ -80,28 +80,24 @@ class RoomDirectoryViewModel @AssistedInject constructor( memberships = listOf(Membership.JOIN) } session - .rx() + .flow() .liveRoomSummaries(queryParams) - .subscribe { list -> - val joinedRoomIds = list - ?.map { it.roomId } - ?.toSet() - .orEmpty() - - setState { - copy(joinedRoomsIds = joinedRoomIds) - } + .map { roomSummaries -> + roomSummaries + .map { it.roomId } + .toSet() + } + .setOnEach { + copy(joinedRoomsIds = it) } - .disposeOnClear() } private fun observeMembershipChanges() { - session.rx() + session.flow() .liveRoomChangeMembershipState() - .subscribe { - setState { copy(changeMembershipStates = it) } + .setOnEach { + copy(changeMembershipStates = it) } - .disposeOnClear() } override fun handle(action: RoomDirectoryAction) { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 6e70ff2593..2635307e95 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -30,6 +30,8 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.roomdirectory.JoinState import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue @@ -40,6 +42,7 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.rx import timber.log.Timber @@ -165,9 +168,9 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini excludeType = null } session - .rx() + .flow() .liveRoomSummaries(queryParams) - .subscribe { list -> + .onEach { list -> val isRoomJoined = list.any { it.membership == Membership.JOIN } @@ -180,13 +183,13 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini setState { copy(roomJoinState = JoinState.JOINED) } } } - .disposeOnClear() + .launchIn(viewModelScope) } private fun observeMembershipChanges() { - session.rx() + session.flow() .liveRoomChangeMembershipState() - .subscribe { + .onEach { val changeMembership = it[initialState.roomId] ?: ChangeMembershipState.Unknown val joinState = when (changeMembership) { is ChangeMembershipState.Joining -> JoinState.JOINING @@ -198,7 +201,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini setState { copy(roomJoinState = joinState) } } } - .disposeOnClear() + .launchIn(viewModelScope) } override fun handle(action: RoomPreviewAction) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index c8570d67dc..c4fc2bc7bb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -40,10 +40,10 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.state.isPublic -import org.matrix.android.sdk.rx.RxRoom -import org.matrix.android.sdk.rx.mapOptional -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.FlowRoom +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap class RoomProfileViewModel @AssistedInject constructor( @Assisted private val initialState: RoomProfileViewState, @@ -69,15 +69,15 @@ class RoomProfileViewModel @AssistedInject constructor( private val room = session.getRoom(initialState.roomId)!! init { - val rxRoom = room.rx() - observeRoomSummary(rxRoom) - observeRoomCreateContent(rxRoom) - observeBannedRoomMembers(rxRoom) + val flowRoom = room.flow() + observeRoomSummary(flowRoom) + observeRoomCreateContent(flowRoom) + observeBannedRoomMembers(flowRoom) observePermissions() } - private fun observeRoomCreateContent(rxRoom: RxRoom) { - rxRoom.liveStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.NoCondition) + private fun observeRoomCreateContent(flowRoom: FlowRoom) { + flowRoom.liveStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .execute { async -> @@ -92,16 +92,16 @@ class RoomProfileViewModel @AssistedInject constructor( } } - private fun observeRoomSummary(rxRoom: RxRoom) { - rxRoom.liveRoomSummary() + private fun observeRoomSummary(flowRoom: FlowRoom) { + flowRoom.liveRoomSummary() .unwrap() .execute { copy(roomSummary = it) } } - private fun observeBannedRoomMembers(rxRoom: RxRoom) { - rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) + private fun observeBannedRoomMembers(flowRoom: FlowRoom) { + flowRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) .execute { copy(bannedMembership = it) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index ea939c153e..7b28ced130 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -28,11 +28,10 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.settings.VectorPreferences -import io.reactivex.Completable -import io.reactivex.Observable import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -47,7 +46,6 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState, private val vectorPreferences: VectorPreferences, @@ -259,61 +257,57 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun saveSettings() = withState { state -> - postLoading(true) - - val operationList = mutableListOf() + val operationList = mutableListOf Unit>() val summary = state.roomSummary.invoke() when (val avatarAction = state.avatarAction) { RoomSettingsViewState.AvatarAction.None -> Unit RoomSettingsViewState.AvatarAction.DeleteAvatar -> { - operationList.add(room.rx().deleteAvatar()) + operationList.add { room.deleteAvatar() } } is RoomSettingsViewState.AvatarAction.UpdateAvatar -> { - operationList.add(room.rx().updateAvatar(avatarAction.newAvatarUri, avatarAction.newAvatarFileName)) + operationList.add { room.updateAvatar(avatarAction.newAvatarUri, avatarAction.newAvatarFileName) } } } if (summary?.name != state.newName) { - operationList.add(room.rx().updateName(state.newName ?: "")) + operationList.add { room.updateName(state.newName ?: "") } } if (summary?.topic != state.newTopic) { - operationList.add(room.rx().updateTopic(state.newTopic ?: "")) + operationList.add { room.updateTopic(state.newTopic ?: "") } } if (state.newHistoryVisibility != null) { - operationList.add(room.rx().updateHistoryReadability(state.newHistoryVisibility)) + operationList.add { room.updateHistoryReadability(state.newHistoryVisibility) } } if (state.newRoomJoinRules.hasChanged()) { - operationList.add(room.rx().updateJoinRule(state.newRoomJoinRules.newJoinRules, state.newRoomJoinRules.newGuestAccess)) + operationList.add { room.updateJoinRule(state.newRoomJoinRules.newJoinRules, state.newRoomJoinRules.newGuestAccess) } + } + viewModelScope.launch { + updateLoadingState(isLoading = true) + try { + for (operation in operationList) { + operation.invoke() + } + setState { + deletePendingAvatar(this) + copy( + avatarAction = RoomSettingsViewState.AvatarAction.None, + newHistoryVisibility = null, + newRoomJoinRules = RoomSettingsViewState.NewJoinRule() + ) + } + _viewEvents.post(RoomSettingsViewEvents.Success) + } catch (failure: Throwable) { + _viewEvents.post(RoomSettingsViewEvents.Failure(failure)) + }finally { + updateLoadingState(isLoading = false) + } } - - Observable - .fromIterable(operationList) - .concatMapCompletable { it } - .subscribe( - { - postLoading(false) - setState { - deletePendingAvatar(this) - copy( - avatarAction = RoomSettingsViewState.AvatarAction.None, - newHistoryVisibility = null, - newRoomJoinRules = RoomSettingsViewState.NewJoinRule() - ) - } - _viewEvents.post(RoomSettingsViewEvents.Success) - }, - { - postLoading(false) - _viewEvents.post(RoomSettingsViewEvents.Failure(it)) - } - ) - .disposeOnClear() } - private fun postLoading(isLoading: Boolean) { + private fun updateLoadingState(isLoading: Boolean) { setState { copy(isLoading = isLoading) } diff --git a/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt b/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt new file mode 100644 index 0000000000..5afcb77587 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME +import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME +import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME +import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.rx.SecretsSynchronisationInfo + +data class SecretsSynchronisationInfo( + val isBackupSetup: Boolean, + val isCrossSigningEnabled: Boolean, + val isCrossSigningTrusted: Boolean, + val allPrivateKeysKnown: Boolean, + val megolmBackupAvailable: Boolean, + val megolmSecretKnown: Boolean, + val isMegolmKeyIn4S: Boolean +) + +fun Session.liveSecretSynchronisationInfo(): Flow { + val sessionFlow = flow() + return combine( + sessionFlow.liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME)), + sessionFlow.liveCrossSigningInfo(myUserId), + sessionFlow.liveCrossSigningPrivateKeys() + ) { _, crossSigningInfo, pInfo -> + // first check if 4S is already setup + val is4SSetup = sharedSecretStorageService.isRecoverySetup() + val isCrossSigningEnabled = crossSigningInfo.getOrNull() != null + val isCrossSigningTrusted = crossSigningInfo.getOrNull()?.isTrusted() == true + val allPrivateKeysKnown = pInfo.getOrNull()?.allKnown().orFalse() + + val keysBackupService = cryptoService().keysBackupService() + val currentBackupVersion = keysBackupService.currentBackupVersion + val megolmBackupAvailable = currentBackupVersion != null + val savedBackupKey = keysBackupService.getKeyBackupRecoveryKeyInfo() + + val megolmKeyKnown = savedBackupKey?.version == currentBackupVersion + SecretsSynchronisationInfo( + isBackupSetup = is4SSetup, + isCrossSigningEnabled = isCrossSigningEnabled, + isCrossSigningTrusted = isCrossSigningTrusted, + allPrivateKeysKnown = allPrivateKeysKnown, + megolmBackupAvailable = megolmBackupAvailable, + megolmSecretKnown = megolmKeyKnown, + isMegolmKeyIn4S = sharedSecretStorageService.isMegolmKeyInBackup() + ) + } + .distinctUntilChanged() +} diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 0075be6e25..103c4ab06d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -60,6 +60,10 @@ import im.vector.app.features.raw.wellknown.isE2EByDefault import im.vector.app.features.themes.ThemeUtils import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import me.gujun.android.span.span import org.matrix.android.sdk.api.MatrixCallback @@ -144,14 +148,12 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( // My device name may have been updated refreshMyDevice() refreshXSigningStatus() - session.rx().liveSecretSynchronisationInfo() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { + session.liveSecretSynchronisationInfo() + .flowOn(Dispatchers.Main) + .onEach { refresh4SSection(it) refreshXSigningStatus() - }.also { - disposables.add(it) - } + }.launchIn(viewLifecycleOwner.lifecycleScope) lifecycleScope.launchWhenResumed { findPreference(VectorPreferences.SETTINGS_CRYPTOGRAPHY_HS_ADMIN_DISABLED_E2E_DEFAULT)?.isVisible = diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index 1cf150395a..7b7b5d0570 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.ignored -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -27,14 +26,14 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow data class IgnoredUsersViewState( val ignoredUsers: List = emptyList(), @@ -68,7 +67,7 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeIgnoredUsers() { - session.rx() + session.flow() .liveIgnoredUsers() .execute { async -> copy( diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index 5fdd10e742..44e5ca39f9 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -19,24 +19,25 @@ package im.vector.app.features.share import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext -import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.attachments.isPreviewable import im.vector.app.features.attachments.toGroupedContentAttachmentData import im.vector.app.features.home.room.list.BreadcrumbsRoomComparator +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.sample import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.rx -import java.util.concurrent.TimeUnit class IncomingShareViewModel @AssistedInject constructor( @Assisted initialState: IncomingShareViewState, @@ -58,7 +59,7 @@ class IncomingShareViewModel @AssistedInject constructor( } } - private val filterStream: BehaviorRelay = BehaviorRelay.createDefault("") + private val filterStream = MutableStateFlow("") init { observeRoomSummaries() @@ -75,7 +76,7 @@ class IncomingShareViewModel @AssistedInject constructor( } filterStream - .switchMap { filter -> + .flatMapLatest { filter -> val displayNameQuery = if (filter.isEmpty()) { QueryStringValue.NoCondition } else { @@ -85,9 +86,9 @@ class IncomingShareViewModel @AssistedInject constructor( displayName = displayNameQuery memberships = listOf(Membership.JOIN) } - session.rx().liveRoomSummaries(filterQueryParams) + session.flow().liveRoomSummaries(filterQueryParams) } - .throttleLast(300, TimeUnit.MILLISECONDS) + .sample(300) .map { it.sortedWith(breadcrumbsRoomComparator) } .execute { copy(filteredRoomSummaries = it) @@ -110,7 +111,7 @@ class IncomingShareViewModel @AssistedInject constructor( } private fun handleFilter(action: IncomingShareAction.FilterWith) { - filterStream.accept(action.filter) + filterStream.tryEmit(action.filter) } private fun handleShareToSelectedRooms() = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index dc69fb5ba0..46293da209 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.spaces -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory @@ -33,8 +33,9 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import im.vector.app.group import im.vector.app.space -import io.reactivex.Observable import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.ActiveSpaceFilter @@ -44,19 +45,16 @@ import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.group.groupSummaryQueryParams import org.matrix.android.sdk.api.session.room.RoomSortOrder -import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.session.space.SpaceOrderUtils import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator -import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.asObservable -import org.matrix.android.sdk.rx.rx import java.util.concurrent.TimeUnit class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, @@ -286,21 +284,23 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp null) } - val rxSession = session.rx() + val flowSession = session.flow() - Observable.combineLatest, List, List>( - rxSession + combine( + flowSession .liveUser(session.myUserId) .map { it.getOrNull() }, - rxSession + flowSession .liveSpaceSummaries(spaceSummaryQueryParams), - session.accountDataService().getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)).asObservable(), - { _, communityGroups, _ -> - communityGroups - } - ) + session + .accountDataService() + .getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)) + .asFlow() + ) { _, communityGroups, _ -> + communityGroups + } .execute { async -> val rootSpaces = session.spaceService().getRootSpaceSummaries() val orders = rootSpaces.map { diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt index 6cf4f9e0f6..cd7d6a379a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt @@ -25,6 +25,7 @@ import android.view.View import android.view.ViewGroup import androidx.core.text.toSpannable import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState @@ -46,8 +47,7 @@ import im.vector.app.features.permalink.PermalinkHandler import im.vector.app.features.spaces.manage.ManageType import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.manage.SpaceManageActivity -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import java.net.URL @@ -200,33 +200,29 @@ class SpaceDirectoryFragment @Inject constructor( } override fun onUrlClicked(url: String, title: String): Boolean { - permalinkHandler - .launch(requireActivity(), url, null) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { managed -> - if (!managed) { - if (title.isValidUrl() && url.isValidUrl() && URL(title).host != URL(url).host) { - MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive) - .setTitle(R.string.external_link_confirmation_title) - .setMessage( - getString(R.string.external_link_confirmation_message, title, url) - .toSpannable() - .colorizeMatchingText(url, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) - .colorizeMatchingText(title, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) - ) - .setPositiveButton(R.string._continue) { _, _ -> - openUrlInExternalBrowser(requireContext(), url) - } - .setNegativeButton(R.string.cancel, null) - .show() - } else { - // Open in external browser, in a new Tab - openUrlInExternalBrowser(requireContext(), url) - } - } + viewLifecycleOwner.lifecycleScope.launch { + val isHandled = permalinkHandler.launch(requireActivity(), url, null) + if (!isHandled) { + if (title.isValidUrl() && url.isValidUrl() && URL(title).host != URL(url).host) { + MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive) + .setTitle(R.string.external_link_confirmation_title) + .setMessage( + getString(R.string.external_link_confirmation_message, title, url) + .toSpannable() + .colorizeMatchingText(url, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) + .colorizeMatchingText(title, colorProvider.getColorFromAttribute(R.attr.vctr_content_tertiary)) + ) + .setPositiveButton(R.string._continue) { _, _ -> + openUrlInExternalBrowser(requireContext(), url) + } + .setNegativeButton(R.string.cancel, null) + .show() + } else { + // Open in external browser, in a new Tab + openUrlInExternalBrowser(requireContext(), url) } - .disposeOnDestroyView() + } + } // In fact it is always managed return true } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index d07b486fee..5e2537f587 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -31,6 +31,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session @@ -42,7 +43,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow import timber.log.Timber class SpaceDirectoryViewModel @AssistedInject constructor( @@ -147,7 +148,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor( excludeType = null } session - .rx() + .flow() .liveRoomSummaries(queryParams) .map { it.map { it.roomId }.toSet() @@ -158,12 +159,11 @@ class SpaceDirectoryViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.rx() + session.flow() .liveRoomChangeMembershipState() - .subscribe { - setState { copy(changeMembershipStates = it) } + .setOnEach { + copy(changeMembershipStates = it) } - .disposeOnClear() } override fun handle(action: SpaceDirectoryViewAction) { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 7eb7ce95ad..69b98200c1 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -160,16 +160,15 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User } private fun observeUsers() = withState { state -> - identityServerUsersSearch .filter { it.isEmail() } .throttleLast(300, TimeUnit.MILLISECONDS) .switchMapSingle { search -> - val rx = session.rx() + val flowSession = session.rx() val stream = - rx.lookupThreePid(ThreePid.Email(search)).flatMap { + flowSession.lookupThreePid(ThreePid.Email(search)).flatMap { it.getOrNull()?.let { foundThreePid -> - rx.getProfileInfo(foundThreePid.matrixId) + flowSession.getProfileInfo(foundThreePid.matrixId) .map { json -> ThreePidUser( email = search, diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index f88bf6ef56..c88750e6e1 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -30,6 +30,7 @@ import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue @@ -44,7 +45,6 @@ import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx import timber.log.Timber import javax.net.ssl.HttpsURLConnection @@ -135,7 +135,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi return } val widgetId = initialState.widgetId ?: return - session.rx() + session.flow() .liveRoomWidgets(initialState.roomId, QueryStringValue.Equals(widgetId)) .filter { it.isNotEmpty() } .map { it.first() } diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index 0ed4e7d771..bbfeea6a76 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -15,23 +15,24 @@ */ package im.vector.app.features.widgets.permissions -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.widgets.model.WidgetType -import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.flow.flow import timber.log.Timber import java.net.URL @@ -48,7 +49,7 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in private fun observeWidget() { val widgetId = initialState.widgetId ?: return - session.rx() + session.flow() .liveRoomWidgets(initialState.roomId, QueryStringValue.Equals(widgetId)) .filter { it.isNotEmpty() } .map { From d9b02a20d84840a8549c48ee2dff8188c3ce5dcf Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 5 Oct 2021 18:57:34 +0200 Subject: [PATCH 030/144] Mavericks 2: remove matrix-sdk-android-flow as it will be easier when entirely migrating to flow --- matrix-sdk-android-flow/.gitignore | 1 - matrix-sdk-android-flow/build.gradle | 49 ----- matrix-sdk-android-flow/consumer-rules.pro | 0 matrix-sdk-android-flow/proguard-rules.pro | 21 -- .../sdk/flow/ExampleInstrumentedTest.kt | 40 ---- .../src/main/AndroidManifest.xml | 5 - .../org/matrix/android/sdk/flow/FlowRoom.kt | 83 ------- .../matrix/android/sdk/flow/FlowSession.kt | 138 ------------ .../android/sdk/flow/ExampleUnitTest.kt | 33 --- settings.gradle | 1 - vector/build.gradle | 1 - .../app/core/platform/VectorViewModel.kt | 32 --- .../im/vector/app/core/utils}/OptionalFlow.kt | 2 +- .../quads/SharedSecureStorageViewModel.kt | 8 +- .../features/devtools/RoomDevToolViewModel.kt | 7 +- .../discovery/DiscoverySettingsViewModel.kt | 7 +- .../features/home/HomeActivityViewModel.kt | 9 +- .../app/features/home/HomeDetailViewModel.kt | 17 +- .../UnknownDeviceDetectorSharedViewModel.kt | 10 +- .../room/breadcrumbs/BreadcrumbsViewModel.kt | 8 +- .../home/room/detail/RoomDetailViewModel.kt | 26 +-- .../action/MessageActionsViewModel.kt | 12 +- .../home/room/list/RoomListViewModel.kt | 9 +- .../app/features/invite/InvitesAcceptor.kt | 7 +- .../login2/created/AccountCreatedViewModel.kt | 10 +- .../powerlevel/PowerLevelsFlowFactory.kt | 11 +- .../room/RequireActiveMembershipViewModel.kt | 8 +- .../roomdirectory/RoomDirectoryViewModel.kt | 10 +- .../roompreview/RoomPreviewViewModel.kt | 11 +- .../RoomMemberProfileViewModel.kt | 14 +- .../devices/DeviceListBottomSheetViewModel.kt | 10 +- .../roomprofile/RoomProfileViewModel.kt | 26 ++- .../roomprofile/alias/RoomAliasViewModel.kt | 15 +- .../banned/RoomBannedMemberListViewModel.kt | 12 +- .../members/RoomMemberListViewModel.kt | 20 +- .../RoomNotificationSettingsViewModel.kt | 14 +- .../permissions/RoomPermissionsViewModel.kt | 7 +- .../settings/RoomSettingsViewModel.kt | 25 +-- .../uploads/RoomUploadsViewModel.kt | 7 +- .../settings/SecretsSynchronisationInfo.kt | 11 +- .../settings/VectorSettingsGeneralFragment.kt | 12 +- .../CrossSigningSettingsViewModel.kt | 6 +- ...iceVerificationInfoBottomSheetViewModel.kt | 14 +- .../settings/devices/DevicesViewModel.kt | 18 +- .../settings/devtools/AccountDataViewModel.kt | 6 +- .../settings/ignored/IgnoredUsersViewModel.kt | 6 +- .../threepids/ThreePidsSettingsViewModel.kt | 10 +- .../features/share/IncomingShareViewModel.kt | 8 +- .../app/features/spaces/SpaceMenuViewModel.kt | 25 +-- .../features/spaces/SpacesListViewModel.kt | 28 ++- .../spaces/explore/SpaceDirectoryViewModel.kt | 11 +- .../leave/SpaceLeaveAdvancedViewModel.kt | 7 +- .../userdirectory/UserListViewModel.kt | 204 ++++++++---------- .../app/features/widgets/WidgetViewModel.kt | 14 +- .../RoomWidgetPermissionViewModel.kt | 7 +- .../signout/ServerBackupStatusViewModel.kt | 18 +- .../workers/signout/SignoutCheckViewModel.kt | 6 +- 57 files changed, 370 insertions(+), 767 deletions(-) delete mode 100644 matrix-sdk-android-flow/.gitignore delete mode 100644 matrix-sdk-android-flow/build.gradle delete mode 100644 matrix-sdk-android-flow/consumer-rules.pro delete mode 100644 matrix-sdk-android-flow/proguard-rules.pro delete mode 100644 matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt delete mode 100644 matrix-sdk-android-flow/src/main/AndroidManifest.xml delete mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt delete mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt delete mode 100644 matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt rename {matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow => vector/src/main/java/im/vector/app/core/utils}/OptionalFlow.kt (96%) diff --git a/matrix-sdk-android-flow/.gitignore b/matrix-sdk-android-flow/.gitignore deleted file mode 100644 index 42afabfd2a..0000000000 --- a/matrix-sdk-android-flow/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/matrix-sdk-android-flow/build.gradle b/matrix-sdk-android-flow/build.gradle deleted file mode 100644 index 4aecec169b..0000000000 --- a/matrix-sdk-android-flow/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ - -plugins { - id 'com.android.library' - id 'org.jetbrains.kotlin.android' -} - -android { - compileSdk 31 - - defaultConfig { - minSdk 21 - targetSdk 31 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - - implementation project(":matrix-sdk-android") - implementation libs.androidx.appCompat - - implementation libs.jetbrains.kotlinStdlibJdk7 - implementation libs.jetbrains.coroutinesCore - implementation libs.jetbrains.coroutinesAndroid - implementation libs.androidx.lifecycleLivedata - - // Paging - implementation libs.androidx.pagingRuntimeKtx - - // Logging - implementation libs.jakewharton.timber - -} \ No newline at end of file diff --git a/matrix-sdk-android-flow/consumer-rules.pro b/matrix-sdk-android-flow/consumer-rules.pro deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/matrix-sdk-android-flow/proguard-rules.pro b/matrix-sdk-android-flow/proguard-rules.pro deleted file mode 100644 index 481bb43481..0000000000 --- a/matrix-sdk-android-flow/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt deleted file mode 100644 index 41799955a4..0000000000 --- a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.flow - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.matrix.android.sdk.flow.test", appContext.packageName) - } -} diff --git a/matrix-sdk-android-flow/src/main/AndroidManifest.xml b/matrix-sdk-android-flow/src/main/AndroidManifest.xml deleted file mode 100644 index 2392c0bfcb..0000000000 --- a/matrix-sdk-android-flow/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt deleted file mode 100644 index a3e476ce08..0000000000 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.flow - -import androidx.lifecycle.asFlow -import kotlinx.coroutines.flow.Flow -import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.room.Room -import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams -import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary -import org.matrix.android.sdk.api.session.room.model.ReadReceipt -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState -import org.matrix.android.sdk.api.session.room.send.UserDraft -import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -import org.matrix.android.sdk.api.util.Optional - -class FlowRoom(private val room: Room) { - - fun liveRoomSummary(): Flow> { - return room.getRoomSummaryLive().asFlow() - } - - fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow> { - return room.getRoomMembersLive(queryParams).asFlow() - } - - fun liveAnnotationSummary(eventId: String): Flow> { - return room.getEventAnnotationsSummaryLive(eventId).asFlow() - } - - fun liveTimelineEvent(eventId: String): Flow> { - return room.getTimeLineEventLive(eventId).asFlow() - } - - fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow> { - return room.getStateEventLive(eventType, stateKey).asFlow() - } - - fun liveStateEvents(eventTypes: Set): Flow> { - return room.getStateEventsLive(eventTypes).asFlow() - } - - fun liveReadMarker(): Flow> { - return room.getReadMarkerLive().asFlow() - } - - fun liveReadReceipt(): Flow> { - return room.getMyReadReceiptLive().asFlow() - } - - fun liveEventReadReceipts(eventId: String): Flow> { - return room.getEventReadReceiptsLive(eventId).asFlow() - } - - fun liveDraft(): Flow> { - return room.getDraftLive().asFlow() - } - - fun liveNotificationState(): Flow { - return room.getLiveRoomNotificationState().asFlow() - } -} - -fun Room.flow(): FlowRoom { - return FlowRoom(this) -} diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt deleted file mode 100644 index affcd4a65d..0000000000 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.flow - -import androidx.lifecycle.asFlow -import androidx.paging.PagedList -import kotlinx.coroutines.flow.Flow -import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent -import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo -import org.matrix.android.sdk.api.session.group.GroupSummaryQueryParams -import org.matrix.android.sdk.api.session.group.model.GroupSummary -import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.api.session.pushers.Pusher -import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams -import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent -import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams -import org.matrix.android.sdk.api.session.sync.SyncState -import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.api.session.widgets.model.Widget -import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo -import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo -import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo - -class RxFlow(private val session: Session) { - - fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Flow> { - return session.getRoomSummariesLive(queryParams).asFlow() - } - - fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow> { - return session.getGroupSummariesLive(queryParams).asFlow() - } - - fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Flow> { - return session.spaceService().getSpaceSummariesLive(queryParams).asFlow() - } - - fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow> { - return session.getBreadcrumbsLive(queryParams).asFlow() - } - - fun liveMyDevicesInfo(): Flow> { - return session.cryptoService().getLiveMyDevicesInfo().asFlow() - } - - fun liveSyncState(): Flow { - return session.getSyncStateLive().asFlow() - } - - fun livePushers(): Flow> { - return session.getPushersLive().asFlow() - } - - fun liveUser(userId: String): Flow> { - return session.getUserLive(userId).asFlow() - } - - fun liveRoomMember(userId: String, roomId: String): Flow> { - return session.getRoomMemberLive(userId, roomId).asFlow() - } - - fun liveUsers(): Flow> { - return session.getUsersLive().asFlow() - } - - fun liveIgnoredUsers(): Flow> { - return session.getIgnoredUsersLive().asFlow() - } - - fun livePagedUsers(filter: String? = null, excludedUserIds: Set? = null): Flow> { - return session.getPagedUsersLive(filter, excludedUserIds).asFlow() - } - - fun liveThreePIds(refreshData: Boolean): Flow> { - return session.getThreePidsLive(refreshData).asFlow() - } - - fun livePendingThreePIds(): Flow> { - return session.getPendingThreePidsLive().asFlow() - } - - fun liveUserCryptoDevices(userId: String): Flow> { - return session.cryptoService().getLiveCryptoDeviceInfo(userId).asFlow() - } - - fun liveCrossSigningInfo(userId: String): Flow> { - return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asFlow() - } - - fun liveCrossSigningPrivateKeys(): Flow> { - return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() - } - - fun liveUserAccountData(types: Set): Flow> { - return session.accountDataService().getLiveUserAccountDataEvents(types).asFlow() - } - - fun liveRoomAccountData(types: Set): Flow> { - return session.accountDataService().getLiveRoomAccountDataEvents(types).asFlow() - } - - fun liveRoomWidgets( - roomId: String, - widgetId: QueryStringValue, - widgetTypes: Set? = null, - excludedTypes: Set? = null - ): Flow> { - return session.widgetService().getRoomWidgetsLive(roomId, widgetId, widgetTypes, excludedTypes).asFlow() - } - - fun liveRoomChangeMembershipState(): Flow> { - return session.getChangeMembershipsLive().asFlow() - } -} - -fun Session.flow(): RxFlow { - return RxFlow(this) -} diff --git a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt deleted file mode 100644 index bd627b2041..0000000000 --- a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.flow - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} diff --git a/settings.gradle b/settings.gradle index e3b84b4733..b88ea99b05 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,4 +5,3 @@ include ':diff-match-patch' include ':attachment-viewer' include ':multipicker' include ':library:ui-styles' -include ':matrix-sdk-android-flow' diff --git a/vector/build.gradle b/vector/build.gradle index 76bc71b2d4..a9c6a407d8 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -323,7 +323,6 @@ dependencies { implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android-rx") - implementation project(":matrix-sdk-android-flow") implementation project(":diff-match-patch") implementation project(":multipicker") implementation project(":attachment-viewer") diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index 1a77a00fab..01700049c1 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -16,16 +16,10 @@ package im.vector.app.core.platform -import com.airbnb.mvrx.Async import com.airbnb.mvrx.BaseMvRxViewModel -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.Success import im.vector.app.core.utils.DataSource import im.vector.app.core.utils.PublishDataSource -import io.reactivex.Observable -import io.reactivex.Single abstract class VectorViewModel(initialState: S) : BaseMvRxViewModel(initialState) { @@ -38,31 +32,5 @@ abstract class VectorViewModel() val viewEvents: DataSource = _viewEvents - /** - * This method does the same thing as the execute function, but it doesn't subscribe to the stream - * so you can use this in a switchMap or a flatMap - */ - // False positive - @Suppress("USELESS_CAST", "NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER") - fun Single.toAsync(stateReducer: S.(Async) -> S): Single> { - setState { stateReducer(Loading()) } - return map { Success(it) as Async } - .onErrorReturn { Fail(it) } - .doOnSuccess { setState { stateReducer(it) } } - } - - /** - * This method does the same thing as the execute function, but it doesn't subscribe to the stream - * so you can use this in a switchMap or a flatMap - */ - // False positive - @Suppress("USELESS_CAST", "NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER") - fun Observable.toAsync(stateReducer: S.(Async) -> S): Observable> { - setState { stateReducer(Loading()) } - return map { Success(it) as Async } - .onErrorReturn { Fail(it) } - .doOnNext { setState { stateReducer(it) } } - } - abstract fun handle(action: VA) } diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt b/vector/src/main/java/im/vector/app/core/utils/OptionalFlow.kt similarity index 96% rename from matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt rename to vector/src/main/java/im/vector/app/core/utils/OptionalFlow.kt index a9f062f379..8e37ccfe8d 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt +++ b/vector/src/main/java/im/vector/app/core/utils/OptionalFlow.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.flow +package im.vector.app.core.utils import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 151b73ff32..2a0ce1da2b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.crypto.quads +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail @@ -43,9 +44,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.IntegrityResult import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding -import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.io.ByteArrayOutputStream @@ -116,8 +115,9 @@ class SharedSecureStorageViewModel @AssistedInject constructor( } } - session.flow() - .liveUserCryptoDevices(session.myUserId) + session.cryptoService() + .getLiveCryptoDeviceInfo(session.myUserId) + .asFlow() .distinctUntilChanged() .execute { copy( diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt index 4bab65fd5d..0aed35fd55 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.devtools +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail @@ -40,9 +41,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.util.JsonDict -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.di.MoshiProvider -import org.matrix.android.sdk.rx.rx class RoomDevToolViewModel @AssistedInject constructor( @Assisted val initialState: RoomDevToolViewState, @@ -70,8 +69,8 @@ class RoomDevToolViewModel @AssistedInject constructor( init { session.getRoom(initialState.roomId) - ?.flow() - ?.liveStateEvents(emptySet()) + ?.getStateEventsLive(emptySet()) + ?.asFlow() ?.execute { async -> copy(stateEvents = async) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index b248bcd065..ddbe2e151a 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.discovery +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -36,7 +37,6 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.flow.flow class DiscoverySettingsViewModel @AssistedInject constructor( @Assisted initialState: DiscoverySettingsState, @@ -85,8 +85,9 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private fun observeThreePids() { - session.flow() - .liveThreePIds(true) + session + .getThreePidsLive(true) + .asFlow() .onEach { retrieveBinding(it) } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index fa3df1ca97..52ea898367 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -47,12 +47,9 @@ import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.util.awaitCallback -import org.matrix.android.sdk.rx.asObservable -import org.matrix.android.sdk.rx.rx import timber.log.Timber import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -104,8 +101,10 @@ class HomeActivityViewModel @AssistedInject constructor( .crossSigningService().allPrivateKeysKnown() safeActiveSession - .flow() - .liveCrossSigningInfo(safeActiveSession.myUserId) + .cryptoService() + .crossSigningService() + .getLiveCrossSigningKeys(safeActiveSession.myUserId) + .asFlow() .onEach { val isVerified = it.getOrNull()?.isTrusted() ?: false if (!isVerified && onceTrusted) { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 316c1791fe..85a0716a77 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -48,7 +48,6 @@ import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.asObservable import timber.log.Timber import java.util.concurrent.TimeUnit @@ -96,11 +95,13 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho updateShowDialPadTab() observeDataStore() callManager.addProtocolsCheckerListener(this) - session.flow().liveUser(session.myUserId).execute { - copy( - myMatrixItem = it.invoke()?.getOrNull()?.toMatrixItem() - ) - } + session.getUserLive(session.myUserId) + .asFlow() + .execute { + copy( + myMatrixItem = it.invoke()?.getOrNull()?.toMatrixItem() + ) + } } private fun observeDataStore() { @@ -183,8 +184,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } private fun observeSyncState() { - session.flow() - .liveSyncState() + session.getSyncStateLive() + .asFlow() .setOnEach { syncState -> copy(syncState = syncState) } diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index 143f843954..ef22875eaa 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -41,7 +42,6 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import timber.log.Timber @@ -99,9 +99,9 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted ) combine( - session.flow().liveUserCryptoDevices(session.myUserId), - session.flow().liveMyDevicesInfo(), - session.flow().liveCrossSigningPrivateKeys() + session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow(), + session.cryptoService().getLiveMyDevicesInfo().asFlow(), + session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() ) { cryptoList, infoList, pInfo -> // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") @@ -132,7 +132,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted ) } - session.flow().liveUserCryptoDevices(session.myUserId) + session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow() .distinctUntilChanged() .sample(5_000) .onEach { diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index f32f132fe9..0e838db525 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.breadcrumbs +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -25,13 +26,10 @@ import dagger.assisted.AssistedFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.rx class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, private val session: Session) @@ -62,11 +60,11 @@ class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: B // PRIVATE METHODS ***************************************************************************** private fun observeBreadcrumbs() { - session.flow() - .liveBreadcrumbs(roomSummaryQueryParams { + session.getBreadcrumbsLive(roomSummaryQueryParams { displayName = QueryStringValue.NoCondition memberships = listOf(Membership.JOIN) }) + .asFlow() .execute { asyncBreadcrumbs -> copy(asyncBreadcrumbs = asyncBreadcrumbs) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index ddb1c51b5b..b1d18d7df4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -37,6 +37,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.unwrap import im.vector.app.features.attachments.toContentAttachmentData import im.vector.app.features.call.conference.ConferenceEvent import im.vector.app.features.call.conference.JitsiActiveConferenceHolder @@ -108,8 +109,6 @@ import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode import timber.log.Timber import java.util.concurrent.TimeUnit @@ -254,11 +253,12 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeActiveRoomWidgets() { - session.flow() - .liveRoomWidgets( + session.widgetService() + .getRoomWidgetsLive( roomId = initialState.roomId, widgetId = QueryStringValue.NoCondition ) + .asFlow() .map { widgets -> widgets.filter { it.isActive } } @@ -287,8 +287,9 @@ class RoomDetailViewModel @AssistedInject constructor( val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(session.myUserId, QueryStringValue.Case.SENSITIVE) } - room.flow() - .liveRoomMembers(queryParams) + room + .getRoomMembersLive(queryParams) + .asFlow() .map { it.firstOrNull().toOptional() } @@ -1505,8 +1506,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeSyncState() { - session.flow() - .liveSyncState() + session.getSyncStateLive() + .asFlow() .setOnEach { syncState -> copy(syncState = syncState) } @@ -1520,7 +1521,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeRoomSummary() { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> copy( @@ -1532,7 +1534,7 @@ class RoomDetailViewModel @AssistedInject constructor( private fun getUnreadState() { combine( timelineEvents, - room.flow().liveRoomSummary().unwrap() + room.getRoomSummaryLive().asFlow().unwrap() ) { timelineEvents, roomSummary -> computeUnreadState(timelineEvents, roomSummary) } @@ -1579,8 +1581,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.flow() - .liveRoomChangeMembershipState() + session.getChangeMembershipsLive() + .asFlow() .map { it[initialState.roomId] ?: ChangeMembershipState.Unknown } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index f63366482b..b04fa98ab1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.home.room.detail.timeline.action +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -28,6 +29,7 @@ import im.vector.app.core.extensions.canReact import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.unwrap import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillsPostProcessor @@ -58,8 +60,6 @@ import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap /** * Information related to an event and used to display preview in contextual bottom sheet. @@ -137,8 +137,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun observeEvent() { if (room == null) return - room.flow() - .liveTimelineEvent(initialState.eventId) + room.getTimeLineEventLive(initialState.eventId) + .asFlow() .unwrap() .execute { copy(timelineEvent = it) @@ -149,8 +149,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted if (room == null) return eventIdFlow .flatMapLatest { eventId -> - room.flow() - .liveAnnotationSummary(eventId) + room.getEventAnnotationsSummaryLive(eventId) + .asFlow() .map { annotations -> EmojiDataSource.quickEmojis.map { emoji -> ToggleState(emoji, annotations.getOrNull()?.reactionsSummary?.firstOrNull { it.key == emoji }?.addedByMe ?: false) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index b54d2b1b4f..59e73462bc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.list import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -42,7 +43,6 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.state.isPublic -import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject @@ -95,7 +95,8 @@ class RoomListViewModel @Inject constructor( ) } - session.flow().liveUser(session.myUserId) + session.getUserLive(session.myUserId) + .asFlow() .map { it.getOrNull()?.getBestName() } .distinctUntilChanged() .execute { @@ -106,8 +107,8 @@ class RoomListViewModel @Inject constructor( } private fun observeMembershipChanges() { - session.flow() - .liveRoomChangeMembershipState() + session.getChangeMembershipsLive() + .asFlow() .setOnEach { copy(roomMembershipChanges = it) } diff --git a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt index 09eff756d5..b22390c4d5 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt @@ -16,6 +16,7 @@ package im.vector.app.features.invite +import androidx.lifecycle.asFlow import im.vector.app.ActiveSessionDataSource import im.vector.app.features.session.coroutineScope import io.reactivex.disposables.Disposable @@ -35,7 +36,6 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -82,10 +82,9 @@ class InvitesAcceptor @Inject constructor( val roomQueryParams = roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } - val flowSession = session.flow() combine( - flowSession.liveRoomSummaries(roomQueryParams), - flowSession.liveRoomChangeMembershipState().debounce(1000) + session.getRoomSummariesLive(roomQueryParams).asFlow(), + session.getChangeMembershipsLive().asFlow().debounce(1000) ) { invitedRooms, _ -> invitedRooms.map { it.roomId } } .filter { it.isNotEmpty() } .onEach { invitedRoomIds -> diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt index c95434a548..01ee3a7a23 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.login2.created -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -24,13 +24,13 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.unwrap +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap import timber.log.Timber class AccountCreatedViewModel @AssistedInject constructor( @@ -62,8 +62,8 @@ class AccountCreatedViewModel @AssistedInject constructor( } private fun observeUser() { - session.rx() - .liveUser(session.myUserId) + session.getUserLive(session.myUserId) + .asFlow() .unwrap() .map { if (MatrixPatterns.isUserId(it.userId)) { diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt index 767d6f1ba7..e1992ec572 100644 --- a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt +++ b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt @@ -16,6 +16,9 @@ package im.vector.app.features.powerlevel +import androidx.lifecycle.asFlow +import im.vector.app.core.utils.mapOptional +import im.vector.app.core.utils.unwrap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn @@ -24,15 +27,13 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap class PowerLevelsFlowFactory(private val room: Room) { fun createFlow(): Flow { - return room.flow() - .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + return room + .getStateEventLive(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + .asFlow() .flowOn(Dispatchers.Default) .mapOptional { it.content.toModel() } .unwrap() diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index 62519336f5..a24d2df72c 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.room +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory @@ -27,6 +28,7 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.unwrap import io.reactivex.Observable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -47,8 +49,6 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap /** * This ViewModel observe a room summary and notify when the room is left @@ -90,8 +90,8 @@ class RequireActiveMembershipViewModel @AssistedInject constructor( val emptyResult = Optional.empty() emit(emptyResult) } - room.flow() - .liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .flowOn(Dispatchers.Default) .map { mapToLeftViewEvent(room, it) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt index a2089e6cd5..ff5e3ac3f5 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomdirectory +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading @@ -38,7 +39,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFilter import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow import timber.log.Timber class RoomDirectoryViewModel @AssistedInject constructor( @@ -80,8 +80,8 @@ class RoomDirectoryViewModel @AssistedInject constructor( memberships = listOf(Membership.JOIN) } session - .flow() - .liveRoomSummaries(queryParams) + .getRoomSummariesLive(queryParams) + .asFlow() .map { roomSummaries -> roomSummaries .map { it.roomId } @@ -93,8 +93,8 @@ class RoomDirectoryViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.flow() - .liveRoomChangeMembershipState() + session.getChangeMembershipsLive() + .asFlow() .setOnEach { copy(changeMembershipStates = it) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 2635307e95..5fa7f34aca 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomdirectory.roompreview +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -42,8 +43,6 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.rx import timber.log.Timber class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val initialState: RoomPreviewViewState, @@ -168,8 +167,8 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini excludeType = null } session - .flow() - .liveRoomSummaries(queryParams) + .getRoomSummariesLive(queryParams) + .asFlow() .onEach { list -> val isRoomJoined = list.any { it.membership == Membership.JOIN @@ -187,8 +186,8 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini } private fun observeMembershipChanges() { - session.flow() - .liveRoomChangeMembershipState() + session.getChangeMembershipsLive() + .asFlow() .onEach { val changeMembership = it[initialState.roomId] ?: ChangeMembershipState.Unknown val joinState = when (changeMembership) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 14624b800e..b424d62b1c 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roommemberprofile +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -31,6 +32,7 @@ import im.vector.app.R import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine @@ -53,8 +55,6 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, private val stringProvider: StringProvider, @@ -107,7 +107,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } } - session.flow().liveUserCryptoDevices(initialState.userId) + session.cryptoService().getLiveCryptoDeviceInfo(initialState.userId).asFlow() .map { Pair( it.fold(true, { prev, dev -> prev && dev.isVerified }), @@ -121,14 +121,14 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v ) } - session.flow().liveCrossSigningInfo(initialState.userId) + session.cryptoService().crossSigningService().getLiveCrossSigningKeys(initialState.userId).asFlow() .execute { copy(userMXCrossSigningInfo = it.invoke()?.getOrNull()) } } private fun observeIgnoredState() { - session.flow().liveIgnoredUsers() + session.getIgnoredUsersLive().asFlow() .map { ignored -> ignored.find { it.userId == initialState.userId @@ -245,7 +245,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE) } - room.flow().liveRoomMembers(queryParams) + room.getRoomMembersLive(queryParams).asFlow() .map { it.firstOrNull().toOptional() } .unwrap() .execute { @@ -285,7 +285,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } private fun observeRoomSummaryAndPowerLevels(room: Room) { - val roomSummaryLive = room.flow().liveRoomSummary().unwrap() + val roomSummaryLive = room.getRoomSummaryLive().asFlow().unwrap() val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index b638d84181..1de96a9ef4 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -16,6 +16,7 @@ */ package im.vector.app.features.roommemberprofile.devices +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -33,9 +34,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo -import org.matrix.android.sdk.rx.rx data class DeviceListViewState( val userItem: MatrixItem? = null, @@ -56,14 +55,17 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } init { - session.flow().liveUserCryptoDevices(args.userId) + session.cryptoService().getLiveCryptoDeviceInfo(args.userId) + .asFlow() .execute { copy(cryptoDevices = it).also { refreshSelectedId() } } - session.flow().liveCrossSigningInfo(args.userId) + session.cryptoService().crossSigningService() + .getLiveCrossSigningKeys(args.userId) + .asFlow() .execute { copy(memberCrossSigningKey = it.invoke()?.getOrNull()) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index c4fc2bc7bb..d08d78e64b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roomprofile +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -27,6 +28,8 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.mapOptional +import im.vector.app.core.utils.unwrap import im.vector.app.features.home.ShortcutCreator import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers @@ -40,10 +43,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.state.isPublic -import org.matrix.android.sdk.flow.FlowRoom -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap class RoomProfileViewModel @AssistedInject constructor( @Assisted private val initialState: RoomProfileViewState, @@ -69,15 +68,14 @@ class RoomProfileViewModel @AssistedInject constructor( private val room = session.getRoom(initialState.roomId)!! init { - val flowRoom = room.flow() - observeRoomSummary(flowRoom) - observeRoomCreateContent(flowRoom) - observeBannedRoomMembers(flowRoom) + observeRoomSummary() + observeRoomCreateContent() + observeBannedRoomMembers() observePermissions() } - private fun observeRoomCreateContent(flowRoom: FlowRoom) { - flowRoom.liveStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.NoCondition) + private fun observeRoomCreateContent() { + room.getStateEventLive(EventType.STATE_ROOM_CREATE, QueryStringValue.NoCondition).asFlow() .mapOptional { it.content.toModel() } .unwrap() .execute { async -> @@ -92,16 +90,16 @@ class RoomProfileViewModel @AssistedInject constructor( } } - private fun observeRoomSummary(flowRoom: FlowRoom) { - flowRoom.liveRoomSummary() + private fun observeRoomSummary() { + room.getRoomSummaryLive().asFlow() .unwrap() .execute { copy(roomSummary = it) } } - private fun observeBannedRoomMembers(flowRoom: FlowRoom) { - flowRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) + private fun observeBannedRoomMembers() { + room.getRoomMembersLive(roomMemberQueryParams { memberships = listOf(Membership.BAN) }).asFlow() .execute { copy(bannedMembership = it) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 28a1804fab..086f279655 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile.alias +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -28,6 +29,8 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.mapOptional +import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -39,11 +42,6 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: RoomAliasViewState, private val session: Session) @@ -131,7 +129,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun observeRoomSummary() { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> copy( @@ -173,8 +172,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ private fun observeRoomCanonicalAlias() { - room.flow() - .liveStateEvent(EventType.STATE_ROOM_CANONICAL_ALIAS, QueryStringValue.NoCondition) + room.getStateEventLive(EventType.STATE_ROOM_CANONICAL_ALIAS, QueryStringValue.NoCondition) + .asFlow() .mapOptional { it.content.toModel() } .unwrap() .setOnEach { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 813d50c6bb..4e244b747b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile.banned +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -26,6 +27,7 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -38,10 +40,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, private val stringProvider: StringProvider, @@ -57,13 +55,15 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initia init { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> copy(roomSummary = async) } - room.flow().liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) + room.getRoomMembersLive(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) + .asFlow() .execute { copy( bannedMemberSummaries = it diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 2873b20400..c5ed085bff 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -27,6 +27,8 @@ import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.mapOptional +import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.Dispatchers @@ -51,9 +53,6 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, @@ -94,9 +93,9 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } combine( - room.flow().liveRoomMembers(roomMemberQueryParams), - room.flow() - .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + room.getRoomMembersLive(roomMemberQueryParams).asFlow(), + room.getStateEventLive(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + .asFlow() .mapOptional { it.content.toModel() } .unwrap() ) @@ -109,7 +108,8 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } if (room.isEncrypted()) { - room.flow().liveRoomMembers(roomMemberQueryParams) + room.getRoomMembersLive(roomMemberQueryParams) + .asFlow() .flowOn(Dispatchers.Main) .flatMapLatest { membersSummary -> session.cryptoService().getLiveCryptoDeviceInfo(membersSummary.map { it.userId }) @@ -153,7 +153,8 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } private fun observeRoomSummary() { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> copy(roomSummary = async) @@ -161,7 +162,8 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } private fun observeThirdPartyInvites() { - room.flow().liveStateEvents(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE)) + room.getStateEventsLive(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE)) + .asFlow() .execute { async -> copy(threePidInvites = async) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index d944b77f7d..498a70a1cb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.roomprofile.notifications -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success @@ -25,13 +25,10 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.unwrap import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomNotificationSettingsViewModel @AssistedInject constructor( @Assisted initialState: RoomNotificationSettingsViewState, @@ -66,7 +63,8 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( } private fun observeSummary() { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> copy(roomSummary = async) @@ -74,8 +72,8 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( } private fun observeNotificationState() { - room.rx() - .liveNotificationState() + room.getLiveRoomNotificationState() + .asFlow() .execute { copy(notificationState = it) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index 71e8a313b5..e3dd76f44c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile.permissions +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success @@ -25,6 +26,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -34,8 +36,6 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialState: RoomPermissionsViewState, private val session: Session) @@ -63,7 +63,8 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat } private fun observeRoomSummary() { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> copy( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 7b28ced130..f9d64dfb56 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roomprofile.settings import androidx.core.net.toFile +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory @@ -26,6 +27,8 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.mapOptional +import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.flow.launchIn @@ -43,9 +46,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState, private val vectorPreferences: VectorPreferences, @@ -125,7 +125,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeRoomSummary() { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> val roomSummary = async.invoke() @@ -161,8 +162,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeRoomHistoryVisibility() { - room.flow() - .liveStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.NoCondition) + room.getStateEventLive(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.NoCondition) + .asFlow() .mapOptional { it.content.toModel() } .unwrap() .mapNotNull { it.historyVisibility } @@ -172,8 +173,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeJoinRule() { - room.flow() - .liveStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition) + room.getStateEventLive(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition) + .asFlow() .mapOptional { it.content.toModel() } .unwrap() .mapNotNull { it.joinRules } @@ -183,8 +184,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeGuestAccess() { - room.flow() - .liveStateEvent(EventType.STATE_ROOM_GUEST_ACCESS, QueryStringValue.NoCondition) + room.getStateEventLive(EventType.STATE_ROOM_GUEST_ACCESS, QueryStringValue.NoCondition) + .asFlow() .mapOptional { it.content.toModel() } .unwrap() .mapNotNull { it.guestAccess } @@ -197,8 +198,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ private fun observeRoomAvatar() { - room.flow() - .liveStateEvent(EventType.STATE_ROOM_AVATAR, QueryStringValue.NoCondition) + room.getStateEventLive(EventType.STATE_ROOM_AVATAR, QueryStringValue.NoCondition) + .asFlow() .mapOptional { it.content.toModel() } .unwrap() .setOnEach { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index 4526024143..c9d9b6b23c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile.uploads +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -28,11 +29,10 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.unwrap import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.message.MessageType -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap class RoomUploadsViewModel @AssistedInject constructor( @Assisted initialState: RoomUploadsViewState, @@ -65,7 +65,8 @@ class RoomUploadsViewModel @AssistedInject constructor( } private fun observeRoomSummary() { - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .execute { async -> copy(roomSummary = async) diff --git a/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt b/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt index 5afcb77587..bb0330e6f6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt +++ b/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings +import androidx.lifecycle.asFlow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -25,7 +26,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.SecretsSynchronisationInfo data class SecretsSynchronisationInfo( @@ -39,11 +39,12 @@ data class SecretsSynchronisationInfo( ) fun Session.liveSecretSynchronisationInfo(): Flow { - val sessionFlow = flow() return combine( - sessionFlow.liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME)), - sessionFlow.liveCrossSigningInfo(myUserId), - sessionFlow.liveCrossSigningPrivateKeys() + accountDataService() + .getLiveUserAccountDataEvents(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME)) + .asFlow(), + cryptoService().crossSigningService().getLiveCrossSigningKeys(myUserId).asFlow(), + cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() ) { _, crossSigningInfo, pInfo -> // first check if 4S is already setup val is4SSetup = sharedSecretStorageService.isRecoverySetup() diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index 8eb9e2cdf3..f539f46b3a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -26,6 +26,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible +import androidx.lifecycle.asFlow import androidx.lifecycle.lifecycleScope import androidx.preference.EditTextPreference import androidx.preference.Preference @@ -47,6 +48,7 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.TextUtils import im.vector.app.core.utils.getSizeOfFiles import im.vector.app.core.utils.toast +import im.vector.app.core.utils.unwrap import im.vector.app.databinding.DialogChangePasswordBinding import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs @@ -62,8 +64,6 @@ import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.failure.isInvalidPassword import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap import java.io.File import java.util.UUID import javax.inject.Inject @@ -122,8 +122,8 @@ class VectorSettingsGeneralFragment @Inject constructor( } private fun observeUserAvatar() { - session.flow() - .liveUser(session.myUserId) + session.getUserLive(session.myUserId) + .asFlow() .unwrap() .distinctUntilChangedBy { user -> user.avatarUrl } .onEach { @@ -133,8 +133,8 @@ class VectorSettingsGeneralFragment @Inject constructor( } private fun observeUserDisplayName() { - session.flow() - .liveUser(session.myUserId) + session.getUserLive(session.myUserId) + .asFlow() .unwrap() .map { it.displayName ?: "" } .distinctUntilChanged() diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index a8fafb096a..47010f180a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.settings.crosssigning +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -37,7 +38,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth @@ -56,8 +56,8 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( init { combine( - session.flow().liveMyDevicesInfo(), - session.flow().liveCrossSigningInfo(session.myUserId) + session.cryptoService().getLiveMyDevicesInfo().asFlow(), + session.cryptoService().crossSigningService().getLiveCrossSigningKeys(session.myUserId).asFlow() ) { myDevicesInfo, mxCrossSigningInfo -> myDevicesInfo to mxCrossSigningInfo diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index 38342efc46..8c0c077472 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.settings.devices +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory @@ -27,9 +28,7 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.flow.map import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo -import org.matrix.android.sdk.rx.rx class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState, @Assisted val deviceId: String, @@ -50,7 +49,8 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As isRecoverySetup = session.sharedSecretStorageService.isRecoverySetup() ) } - session.flow().liveCrossSigningInfo(session.myUserId) + session.cryptoService().crossSigningService().getLiveCrossSigningKeys(session.myUserId) + .asFlow() .execute { copy( hasAccountCrossSigning = it.invoke()?.getOrNull() != null, @@ -58,7 +58,8 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As ) } - session.flow().liveUserCryptoDevices(session.myUserId) + session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId) + .asFlow() .map { list -> list.firstOrNull { it.deviceId == deviceId } } @@ -69,7 +70,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As ) } - session.flow().liveUserCryptoDevices(session.myUserId) + session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow() .map { it.size } .execute { copy( @@ -81,7 +82,8 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As copy(deviceInfo = Loading()) } - session.flow().liveMyDevicesInfo() + session.cryptoService().getLiveMyDevicesInfo() + .asFlow() .map { devices -> devices.firstOrNull { it.deviceId == deviceId } ?: DeviceInfo(deviceId = deviceId) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index b2b4c0c396..d58ad4a99a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -56,7 +57,6 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo @@ -123,8 +123,14 @@ class DevicesViewModel @AssistedInject constructor( } combine( - session.flow().liveUserCryptoDevices(session.myUserId), - session.flow().liveMyDevicesInfo() + session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow() + .onEach { + Timber.v("getLiveCryptoDeviceInfo") + }, + session.cryptoService().getLiveMyDevicesInfo().asFlow() + .onEach { + Timber.v("getLiveMyDevicesInfo") + } ) { cryptoList, infoList -> infoList @@ -141,7 +147,8 @@ class DevicesViewModel @AssistedInject constructor( ) } - session.flow().liveCrossSigningInfo(session.myUserId) + session.cryptoService().crossSigningService().getLiveCrossSigningKeys(session.myUserId) + .asFlow() .execute { copy( hasAccountCrossSigning = it.invoke()?.getOrNull() != null, @@ -157,7 +164,8 @@ class DevicesViewModel @AssistedInject constructor( // ) // } - session.flow().liveUserCryptoDevices(session.myUserId) + session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId) + .asFlow() .map { it.size } .distinctUntilChanged() .sample(5_000) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index e5739ec446..69ee9165a7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devtools +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState @@ -31,7 +32,6 @@ import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent -import org.matrix.android.sdk.flow.flow data class AccountDataViewState( val accountData: Async> = Uninitialized @@ -42,7 +42,9 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A : VectorViewModel(initialState) { init { - session.flow().liveUserAccountData(emptySet()) + session.accountDataService() + .getLiveUserAccountDataEvents(emptySet()) + .asFlow() .execute { copy(accountData = it) } diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index 7b7b5d0570..702a31f22c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.ignored +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -33,7 +34,6 @@ import im.vector.app.core.platform.VectorViewModelAction import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.flow.flow data class IgnoredUsersViewState( val ignoredUsers: List = emptyList(), @@ -67,8 +67,8 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeIgnoredUsers() { - session.flow() - .liveIgnoredUsers() + session.getIgnoredUsersLive() + .asFlow() .execute { async -> copy( ignoredUsers = async.invoke().orEmpty() diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index cd0d74a288..0d00c72062 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.threepids +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -38,7 +39,6 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth import timber.log.Timber @@ -101,8 +101,8 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } private fun observeThreePids() { - session.flow() - .liveThreePIds(true) + session.getThreePidsLive(true) + .asFlow() .execute { copy( threePids = it @@ -111,8 +111,8 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } private fun observePendingThreePids() { - session.flow() - .livePendingThreePIds() + session.getPendingThreePidsLive() + .asFlow() .execute { copy( pendingThreePids = it, diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index 44e5ca39f9..39dc3f2344 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.share +import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -37,7 +38,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow class IncomingShareViewModel @AssistedInject constructor( @Assisted initialState: IncomingShareViewState, @@ -69,8 +69,8 @@ class IncomingShareViewModel @AssistedInject constructor( val queryParams = roomSummaryQueryParams { memberships = listOf(Membership.JOIN) } - session - .flow().liveRoomSummaries(queryParams) + session.getRoomSummariesLive(queryParams) + .asFlow() .execute { copy(roomSummaries = it) } @@ -86,7 +86,7 @@ class IncomingShareViewModel @AssistedInject constructor( displayName = displayNameQuery memberships = listOf(Membership.JOIN) } - session.flow().liveRoomSummaries(filterQueryParams) + session.getRoomSummariesLive(filterQueryParams).asFlow() } .sample(300) .map { it.sortedWith(breadcrumbsRoomComparator) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index bb30670da9..a75f773a4c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.spaces +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -42,8 +43,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.rx import timber.log.Timber class SpaceMenuViewModel @AssistedInject constructor( @@ -78,17 +77,19 @@ class SpaceMenuViewModel @AssistedInject constructor( session.getRoom(initialState.spaceId)?.let { room -> - room.flow().liveRoomSummary().onEach { - it.getOrNull()?.let { - if (it.membership == Membership.LEAVE) { - setState { copy(leavingState = Success(Unit)) } - if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { - // switch to home? - appStateHandler.setCurrentSpace(null, session) + room.getRoomSummaryLive() + .asFlow() + .onEach { + it.getOrNull()?.let { + if (it.membership == Membership.LEAVE) { + setState { copy(leavingState = Success(Unit)) } + if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { + // switch to home? + appStateHandler.setCurrentSpace(null, session) + } + } } - } - } - }.launchIn(viewModelScope) + }.launchIn(viewModelScope) PowerLevelsFlowFactory(room) .createFlow() diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index 46293da209..e902240a2e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -53,7 +53,6 @@ import org.matrix.android.sdk.api.session.space.SpaceOrderUtils import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.asObservable import java.util.concurrent.TimeUnit @@ -82,14 +81,13 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp init { - session.getUserLive(session.myUserId).asObservable() - .subscribe { - setState { - copy( - myMxItem = it?.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() - ) - } - }.disposeOnClear() + session.getUserLive(session.myUserId) + .asFlow() + .setOnEach { + copy( + myMxItem = it.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() + ) + } observeSpaceSummaries() // observeSelectionState() @@ -284,16 +282,13 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp null) } - val flowSession = session.flow() - combine( - flowSession - .liveUser(session.myUserId) + session.getUserLive(session.myUserId) + .asFlow() .map { it.getOrNull() }, - flowSession - .liveSpaceSummaries(spaceSummaryQueryParams), + session.spaceService().getSpaceSummariesLive(spaceSummaryQueryParams).asFlow(), session .accountDataService() .getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)) @@ -319,7 +314,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp // clear local echos on update session.accountDataService() .getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)) - .asObservable().execute { + .asFlow() + .execute { copy( spaceOrderLocalEchos = emptyMap() ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index 5e2537f587..127ef5d6bf 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.spaces.explore +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -43,7 +44,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow import timber.log.Timber class SpaceDirectoryViewModel @AssistedInject constructor( @@ -147,9 +147,8 @@ class SpaceDirectoryViewModel @AssistedInject constructor( memberships = listOf(Membership.JOIN) excludeType = null } - session - .flow() - .liveRoomSummaries(queryParams) + session.getRoomSummariesLive(queryParams) + .asFlow() .map { it.map { it.roomId }.toSet() } @@ -159,8 +158,8 @@ class SpaceDirectoryViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.flow() - .liveRoomChangeMembershipState() + session.getChangeMembershipsLive() + .asFlow() .setOnEach { copy(changeMembershipStates = it) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index 3d24cf6225..5f4731e9f4 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.spaces.leave +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -30,6 +31,7 @@ import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.unwrap import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -39,8 +41,6 @@ import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class SpaceLeaveAdvancedViewModel @AssistedInject constructor( @@ -97,7 +97,8 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( val spaceSummary = session.getRoomSummary(initialState.spaceId) setState { copy(spaceSummary = spaceSummary) } session.getRoom(initialState.spaceId)?.let { room -> - room.flow().liveRoomSummary() + room.getRoomSummaryLive() + .asFlow() .unwrap() .onEach { if (it.membership == Membership.LEAVE) { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 69b98200c1..cae8540f8b 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -16,12 +16,12 @@ package im.vector.app.features.userdirectory +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext -import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -29,21 +29,23 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.sample import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.rx.rx -import java.util.concurrent.TimeUnit - -private typealias KnownUsersSearch = String -private typealias DirectoryUsersSearch = String data class ThreePidUser( val email: String, @@ -54,9 +56,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private val session: Session) : VectorViewModel(initialState) { - private val knownUsersSearch = BehaviorRelay.create() - private val directoryUsersSearch = BehaviorRelay.create() - private val identityServerUsersSearch = BehaviorRelay.create() + private val knownUsersSearch = MutableStateFlow("") + private val directoryUsersSearch = MutableStateFlow("") + private val identityServerUsersSearch = MutableStateFlow("") @AssistedFactory interface Factory { @@ -77,11 +79,10 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private val identityServerListener = object : IdentityServiceListener { override fun onIdentityServerChange() { withState { - identityServerUsersSearch.accept(it.searchTerm) + identityServerUsersSearch.tryEmit(it.searchTerm) + val identityServerURL = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) setState { - copy( - configuredIdentityServer = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) - ) + copy(configuredIdentityServer = identityServerURL) } } } @@ -120,7 +121,7 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private fun handleISUpdateConsent(action: UserListAction.UpdateUserConsent) { session.identityService().setUserConsent(action.consent) withState { - identityServerUsersSearch.accept(it.searchTerm) + identityServerUsersSearch.tryEmit(it.searchTerm) } } @@ -139,9 +140,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User ) } } - identityServerUsersSearch.accept(searchTerm) - knownUsersSearch.accept(searchTerm) - directoryUsersSearch.accept(searchTerm) + identityServerUsersSearch.tryEmit(searchTerm) + knownUsersSearch.tryEmit(searchTerm) + directoryUsersSearch.tryEmit(searchTerm) } private fun handleShareMyMatrixToLink() { @@ -151,9 +152,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User } private fun handleClearSearchUsers() { - knownUsersSearch.accept("") - directoryUsersSearch.accept("") - identityServerUsersSearch.accept("") + knownUsersSearch.tryEmit("") + directoryUsersSearch.tryEmit("") + identityServerUsersSearch.tryEmit("") setState { copy(searchTerm = "") } @@ -162,103 +163,82 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private fun observeUsers() = withState { state -> identityServerUsersSearch .filter { it.isEmail() } - .throttleLast(300, TimeUnit.MILLISECONDS) - .switchMapSingle { search -> - val flowSession = session.rx() - val stream = - flowSession.lookupThreePid(ThreePid.Email(search)).flatMap { - it.getOrNull()?.let { foundThreePid -> - flowSession.getProfileInfo(foundThreePid.matrixId) - .map { json -> - ThreePidUser( - email = search, - user = User( - userId = foundThreePid.matrixId, - displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, - avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String - ) - ) - } - .onErrorResumeNext { - Single.just(ThreePidUser(email = search, user = User(foundThreePid.matrixId))) - } - } ?: Single.just(ThreePidUser(email = search, user = null)) - } - stream.toAsync { - copy(matchingEmail = it) - } - } - .subscribe() - .disposeOnClear() + .sample(300) + .onEach { search -> + executeSearchEmail(search) + }.launchIn(viewModelScope) knownUsersSearch - .throttleLast(300, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .switchMap { - session.rx().livePagedUsers(it, state.excludedUserIds) - } - .execute { async -> - copy(knownUsers = async) + .sample(300) + .flowOn(Dispatchers.Main) + .flatMapLatest { search -> + session.getPagedUsersLive(search, state.excludedUserIds).asFlow() + }.execute { + copy(knownUsers = it) } directoryUsersSearch - .debounce(300, TimeUnit.MILLISECONDS) - .switchMapSingle { search -> - val stream = if (search.isBlank()) { - Single.just(emptyList()) - } else { - val searchObservable = session.rx() - .searchUsersDirectory(search, 50, state.excludedUserIds.orEmpty()) - .map { users -> - users.sortedBy { it.toMatrixItem().firstLetterOfDisplayName() } - } - // If it's a valid user id try to use Profile API - // because directory only returns users that are in public rooms or share a room with you, where as - // profile will work other federations - if (!MatrixPatterns.isUserId(search)) { - searchObservable - } else { - val profileObservable = session.rx().getProfileInfo(search) - .map { json -> - User( - userId = search, - displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, - avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String - ).toOptional() - } - .onErrorResumeNext { - // Profile API can be restricted and doesn't have to return result. - // In this case allow inviting valid user ids. - Single.just( - User( - userId = search, - displayName = null, - avatarUrl = null - ).toOptional() - ) - } + .debounce(300) + .onEach { search -> + executeSearchDirectory(state, search) + }.launchIn(viewModelScope) + } - Single.zip( - searchObservable, - profileObservable, - { searchResults, optionalProfile -> - val profile = optionalProfile.getOrNull() ?: return@zip searchResults - val searchContainsProfile = searchResults.any { it.userId == profile.userId } - if (searchContainsProfile) { - searchResults - } else { - listOf(profile) + searchResults - } - } + private suspend fun executeSearchEmail(search: String) { + suspend { + val params = listOf(ThreePid.Email(search)) + val foundThreePid = tryOrNull { + session.identityService().lookUp(params).firstOrNull() + } + if (foundThreePid == null) { + null + } else { + try { + val json = session.getProfile(foundThreePid.matrixId) + ThreePidUser( + email = search, + user = User( + userId = foundThreePid.matrixId, + displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, + avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String ) - } - } - stream.toAsync { - copy(directoryUsers = it) - } + ) + } catch (failure: Throwable) { + ThreePidUser(email = search, user = User(foundThreePid.matrixId)) } - .subscribe() - .disposeOnClear() + } + }.execute { + copy(matchingEmail = it) + } + } + + private suspend fun executeSearchDirectory(state: UserListViewState, search: String) { + suspend { + if (search.isBlank()) { + emptyList() + } else { + val searchResult = session + .searchUsersDirectory(search, 50, state.excludedUserIds.orEmpty()) + .sortedBy { it.toMatrixItem().firstLetterOfDisplayName() } + val userProfile = if (MatrixPatterns.isUserId(search)) { + val json = tryOrNull { session.getProfile(search) } + User( + userId = search, + displayName = json?.get(ProfileService.DISPLAY_NAME_KEY) as? String, + avatarUrl = json?.get(ProfileService.AVATAR_URL_KEY) as? String + ) + } else { + null + } + if (userProfile == null || searchResult.any { it.userId == userProfile.userId }) { + searchResult + } else { + listOf(userProfile) + searchResult + } + } + }.execute { + copy(directoryUsers = it) + } } private fun handleSelectUser(action: UserListAction.AddPendingSelection) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index c88750e6e1..05f6157d11 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.widgets import android.net.Uri +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -29,6 +30,8 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.mapOptional +import im.vector.app.core.utils.unwrap import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -42,9 +45,6 @@ import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerS import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap import timber.log.Timber import javax.net.ssl.HttpsURLConnection @@ -119,7 +119,8 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi if (room == null) { return } - room.flow().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + room.getStateEventLive(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + .asFlow() .mapOptional { it.content.toModel() } .unwrap() .map { @@ -135,8 +136,9 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi return } val widgetId = initialState.widgetId ?: return - session.flow() - .liveRoomWidgets(initialState.roomId, QueryStringValue.Equals(widgetId)) + session.widgetService() + .getRoomWidgetsLive(initialState.roomId, QueryStringValue.Equals(widgetId)) + .asFlow() .filter { it.isNotEmpty() } .map { it.first() } .execute { diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index bbfeea6a76..ee1ffd62c6 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.widgets.permissions +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory @@ -32,7 +33,6 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.widgets.model.WidgetType -import org.matrix.android.sdk.flow.flow import timber.log.Timber import java.net.URL @@ -49,8 +49,9 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in private fun observeWidget() { val widgetId = initialState.widgetId ?: return - session.flow() - .liveRoomWidgets(initialState.roomId, QueryStringValue.Equals(widgetId)) + session.widgetService() + .getRoomWidgetsLive(initialState.roomId, QueryStringValue.Equals(widgetId)) + .asFlow() .filter { it.isNotEmpty() } .map { val widget = it.first() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index c3719ffd8e..f0caac9b97 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.workers.signout import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -41,7 +42,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_S import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener -import org.matrix.android.sdk.flow.flow data class ServerBackupStatusViewState( val bannerState: Async = Uninitialized @@ -92,9 +92,19 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS init { session.cryptoService().keysBackupService().addListener(this) keysBackupState.value = session.cryptoService().keysBackupService().state - val liveUserAccountData = session.flow().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) - val liveCrossSigningInfo = session.flow().liveCrossSigningInfo(session.myUserId) - val liveCrossSigningPrivateKeys = session.flow().liveCrossSigningPrivateKeys() + val liveUserAccountData = session.accountDataService() + .getLiveUserAccountDataEvents(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) + .asFlow() + val liveCrossSigningInfo = session.cryptoService() + .crossSigningService() + .getLiveCrossSigningKeys(session.myUserId) + .asFlow() + val liveCrossSigningPrivateKeys = session.cryptoService() + .crossSigningService() + .getLiveCrossSigningPrivateKeys() + .asFlow() + + combine(liveUserAccountData, liveCrossSigningInfo, keyBackupFlow, liveCrossSigningPrivateKeys) { _, crossSigningInfo, keyBackupState, pInfo -> // first check if 4S is already setup if (session.sharedSecretStorageService.isRecoverySetup()) { diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt index 057d9e31f8..e6429b6263 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.workers.signout import android.net.Uri +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -43,7 +44,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_S import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener -import org.matrix.android.sdk.flow.flow import timber.log.Timber data class SignoutCheckViewState( @@ -98,7 +98,9 @@ class SignoutCheckViewModel @AssistedInject constructor( ) } - session.flow().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) + session.accountDataService() + .getLiveUserAccountDataEvents(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) + .asFlow() .map { session.sharedSecretStorageService.isRecoverySetup() } From 7e1e02973b87af7793444b58fec0aee8924c3ca0 Mon Sep 17 00:00:00 2001 From: Zet Date: Tue, 5 Oct 2021 05:32:48 +0000 Subject: [PATCH 031/144] Translated using Weblate (Arabic) Currently translated at 39.0% (1037 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/ar/ --- vector/src/main/res/values-ar/strings.xml | 97 ++++++++++++++++++++--- 1 file changed, 86 insertions(+), 11 deletions(-) diff --git a/vector/src/main/res/values-ar/strings.xml b/vector/src/main/res/values-ar/strings.xml index e3ef228a72..2ad5d7dde9 100644 --- a/vector/src/main/res/values-ar/strings.xml +++ b/vector/src/main/res/values-ar/strings.xml @@ -239,7 +239,8 @@ غيّر الاسم أبلِغ عن المحتوى مكالمة نشطة - مكالمة اجتماع جارية.\nانضم إليها %1$s أو %2$s. + مكالمة اجتماع جارية. +\nانضم إليها كـ %1$s أو %2$s. بالصوت بالصورة تعذّر بدء المكالمة، رجاءً أعِد المحاولة لاحقا @@ -672,12 +673,12 @@ JSON‏ معطوب قائمة المجموعات - لا تغييرات ملكية - تغيير واحد على الملكية - تغييران على الملكية - %d تغييرات على الملكية - %d تغييرا على الملكية - %d تغيير على الملكية + لا تغييرات على العضوية + تغيير واحد على العضوية + تغييران على العضوية + %d تغييرات على العضوية + %d تغييرا على العضوية + %d تغيير على العضوية "أرسِل كَ‍ " فشل اتصال الوسائط @@ -1109,8 +1110,8 @@ النسخ الاحتياطي المفاتيح ما زال جاريا. في حال خروجك الآن لن تتمكن لاحقا من قراءة الرسائل المشفرة. تأكد من تفعيل النسخ الاحتياطي للمفاتيح على كل أجهزتك كي لا تخسر رسائلك المشفرة ينسخ احتياطيا المفاتيح… - لا يوجد لديك أذن لبدء مكالمة إجتماع - لا يوجد لديك أذن لبدء إجتماع في هذه الغرفة + ليس لديك تصريح لبدء إجتماع + ليس لديك تصريح لبدء إجتماع في هذه الغرفة إبدأ بالمحادثة إعادة ضبط إصرف @@ -1126,7 +1127,7 @@ إستخدم النسخة الإحتياطية للمفتاح لا أريد رسائلي المشفرة نسخ إحتياطي للمفتاح - جاري إعداد الخدمة + يبدأ الخدمة… إفتراضي النظام غير %1$s عنوان الغرفة الى %2$s. أُرسلت الرسالة @@ -1230,7 +1231,7 @@ أنت أضفت %1$s كعناوين لهذه الغرفة. أنت أضفت %1$s كعناوين لهذه الغرفة. - لإكمال الاجراء امنح الصلاحيات الناقصة عبر إعدادات النظام. + لإكمال الاجراء امنح التصاريح الناقصة عبر إعدادات النظام. الفضاءات منعتَ الزوار من دخول الغرفة. منع %1$s الزوار من دخول الغرفة. @@ -1252,4 +1253,78 @@ أضفت الروابط البديلة %1$s للغرفة. أضفت الروابط البديلة %1$s للغرفة. + ينهي المكالمة… + لا رد + المتَصل به مشغول. + المستخدم مشغول + علّقتَ المكالمة + علق %s المكالمة + علِّق + استأنِف + عُد للمكالمة + مكالمة نشطة (%s) + مكالمة صوتية مع %s + مكالمة فيديو مع %s + + لا مكالمات فائة + مكالمة فائة واحدة + مكالمتان فائتتان + %d مكالمات فائة + %d مكالمةً فائة + %d مكالمة فائة + + يتصل… + اختر نغمة للمكالمات: + نغمة المكالمات الواردة + أكّد المكالمة قبل إجرائها + خطأ SSL. + هذا ليس عنوان صالح لخادم مايتركس + راجع واقبل سياسات هذا الخادم: + رقم الهاتف مستخدم بالفعل. + استخدمه كافتراضي ولا تسأل مجددًا + اسأل دائمًا + الخلفية + الأمامية + غيّر الكاميرا + السماعة + المكبر + الهاتف + اختر جهاز الصوت + لا تسألني مجددًا + جرب استخدام %s + فشل الاتصال بسبب سوء ضبط الخادم + الفضاءات + الدعوات + علّق + غيّر %1$s العنوان الرئيسي و العناوين البديلة للغرفة. + غيّر %1$s العناوين البديلة للغرفة. + دليل الغرف + لَم يُضبَط خادِم هُويَّة. + لا مزيد من النتائج + غرف مقترحة + الإخطارات + قيمة جديدة + نجح + ارجع + ألغ نشرها + بدّل + أضف + انسخ + علّمه كمقروء + أمتأكد من الخروج؟ + رَفض + قبُول + رَفض + مراجعة + تجاهل + إِجهَاض + قبُول + اتصل على أي حال + لا يمكن مكالمة نفسك + تَعَلَّم المَزِيد + ليس لديك تصريح لبدء مكالمة + ليس لديك تصريح لبدء مكالمة في هذه الغرفة + تصاريح ناقصة + لإرسال رسائل صوتية امنح تصريح الوصول للميكريفون. + لإكمال هذا الإجراء امنح تصريح الوصول للكميرا عبر إعدادات النظام. \ No newline at end of file From 0cc5b9fbd10cdd8d5a98be1fd591768bf1f474f9 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Tue, 5 Oct 2021 15:58:29 +0000 Subject: [PATCH 032/144] Translated using Weblate (Czech) Currently translated at 100.0% (2658 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/ --- vector/src/main/res/values-cs/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index d52e3eb828..ec0b783464 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -432,7 +432,7 @@ Uživatelské jméno již použito Domovský server: Server identity: - Ověřil/a jsem svoji e-mailovou adresu + Ověřil(a) jsem svoji e-mailovou adresu K resetování hesla zadejte e-mailovou adresu spojenou s vaším účtem: Musíte zadat e-mailovou adresu spojenou s vaším účtem. Musíte zadat nové heslo. @@ -1753,7 +1753,7 @@ Resetovat heslo na %1$s Ověřovací email bude odeslán do Vašeho mailboxu za účelem potvrzení nastavení nového heslo. Dále - Email + E-mail Nové heslo Varování! Změna hesla přenastaví všechny šifrovací klíče pro všechny Vaše relace, a tak učiní zašifrovanou historii chatů nečitelnou. Nastavte zálohu klíčů nebo exportujte své klíče místností z jiné relace, než se rozhodnete pokračovat. From 3a65b4c6942a1ff34030ed30d603ad0775e25eac Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Sun, 3 Oct 2021 20:56:19 +0000 Subject: [PATCH 033/144] Translated using Weblate (Ukrainian) Currently translated at 89.6% (2382 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- vector/src/main/res/values-uk/strings.xml | 103 ++++++++++++++++------ 1 file changed, 76 insertions(+), 27 deletions(-) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index 8c0c3927af..dfa0206a34 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -609,9 +609,9 @@ Інтерфейс користувача Мова Оберіть мову - Очікування Перевірки + Очікування перевірки Перевірте ваш email та перейдіть за посиланням у листі. Після цього клацніть продовжити. - Не вдалося перевірити поштову адресу. Будь ласка, перевірте ваш email та перейдіть за посиланням у листі. Після цього натисніть продовжити. + Не вдалося перевірити адресу е-пошти. Перевірте вкуазану адресу е-пошти та перейдіть за посиланням у листі. Після цього натисніть продовжити. Email адреса вже використовується. Цю email адресу не знайдено. Цей номер вже використовується. @@ -631,10 +631,10 @@ Будь ласка, оберіть країну Номер телефону Номер недійсний у обраній країні - Верифікація телефону - "Ми надіслали SMS з кодом активації. Будь ласка, введіть його нижче." + Перевірка телефону + Ми надіслали СМС з кодом активації. Введіть його внизу. Введіть код активації - При перевірці вашого номеру сталася помилка + Сталася помилка під час перевірки вашого номера Код 3 дні @@ -680,7 +680,7 @@ Наскрізне шифрування Наскрізне шифрування увімкнено Вийдіть з облікового запису, щоб отримати змогу увімкнути шифрування. - Шифрувати лише до звірених сеансів + Шифрувати лише для звірених сеансів Ніколи не надсилати зашифровані повідомлення з цього сеансу незвіреним сеансам у цій кімнаті. Кімната не має локальної адреси @@ -736,16 +736,14 @@ У чорному списку невідомий пристрій порожньо - Підтвердити + Звірити Скасувати Блокувати Дозволити - Перевірити пристрій - Для перевірки пристрою, будь ласка, зв’яжіться з його власником у інший спосіб (наприклад, особисто чи телефоном) та запитайте, чи збігається ключ, який він бачить у налаштуваннях користувача, з наведеним нижче: - Якщо вони збігаються, натисніть кнопку Підтвердити нижче. -Якщо ні, хтось втрутився в цей пристрій, і Вам імовірно слід його заблокувати. -У майбутньому цей процес верифікації стане більш складним. - Я підтверджую, що ключі співпадають + Звірити сеанс + Підтвердьте, порівнявши вказане за допомогою налаштувань користувача в іншому сеансі: + Якщо вони відрізняються, безпека вашого зв\'язку може бути під загрозою. + Я підтверджую, що ключі збігаються Кімната містить невідомі сеанси Кімната містить незвірені невідомі сеанси. @@ -800,7 +798,7 @@ Використовувати рідну камеру Щойно доданий вами пристрій \'%s\' править ключі шифрування. - Ваш незвірений пристрій \'%s\' вимагає ключі шифрування. + Ваш незвірений пристрій «%s» вимагає ключі шифрування. Почати перевірку Поділитись без перевірки Знехтувати запит @@ -1172,7 +1170,7 @@ Використати резервну копію ключів Резервне копіювання ключів… Якщо вийти зараз, ви втратите свої зашифровані повідомлення - Перевірка сеансу + Звірити сеанс Наліпка Галерея Файл @@ -1375,7 +1373,7 @@ Знехтувати Запит на розподіл ключів Поділитися - Перевірити + Звірити Незвірений сеанс запитує ключі шифрування. \nНазва сеансу: %1$s \nОстанні відвідини: %2$s @@ -1492,11 +1490,11 @@ Типове стиснення Медіа Додаткові відомості: %s - Під час підтвердження номера телефону сталася помилка. + Сталася помилка під час підтвердження номера телефону. Паролі не збігаються Пароль не дійсний Оновити пароль - Під час підтвердження вашої адреси електронної пошти сталася помилка. + Під час перевірки вашої адреси е-пошти сталася помилка. Увімкніть для цього параметр «Дозволити інтеграції» у налаштуваннях. Інтеграцію вимкнено Керування інтеграцією @@ -1703,8 +1701,8 @@ Увімкнути наскрізне шифрування… Повідомлення в цій кімнаті захищені наскрізним шифруванням. Звірити цей сеанс - Перевірте цей сеанс, підтвердивши, що на екрані партнера з’являються такі цифри - Перевірте цей сеанс, підтвердивши, що на екрані партнера з’являються ці емоджі + Звірте цей сеанс, підтвердивши, що на екрані партнера з’являються такі цифри + Звірте цей сеанс, підтвердивши, що на екрані партнера з’являються ці емоджі Звірте цей сеанс, щоб позначити його надійним. Довірені сеанси партнерів дають вам додаткову впевненість під час використання наскрізно зашифрованих повідомлень. Звірте цей сеанс, щоб позначити його надійним та надати йому доступ до зашифрованих повідомлень. Якщо ви не входили в цей сеанс, ваш обліковий запис може бути зламано: Назва або ID (#example:matrix.org) @@ -1801,11 +1799,11 @@ Ви не маєте доступу до цього повідомлення, бо відправник не довіряє вашому сеансу Позначити довіреним Зашифроване незвіреним пристроєм - Цей сеанс є довіреним для безпечного обміну повідомленням тому що його було звірено %1$s (%2$s): + Цей сеанс довірений для безпечного обміну повідомленням, оскільки його було звірено %1$s (%2$s): Звірено - Ваш новий сеанс тепер звірений. В нього є доступ до ваших зашифрованих повідомлень, а інші користувачі бачитимуть його, як довірений. + Ваш новий сеанс звірено. Він має доступ до ваших зашифрованих повідомлень, а інші користувачі бачитимуть його довіреним. Звірено %s - Захищені повідомлення з цим користувачем є наскрізно зашифрованими та є непрочитними для сторонніх осіб. + Захищені повідомлення з цим користувачем наскрізно зашифровані та непрочитні для сторонніх осіб. Ви успішно звірили цей сеанс. Звірено! Резервна копія має недійсний підпис з незвіреного сеансу %s @@ -1832,12 +1830,12 @@ Вдавайтесь до цього лише за умов відсутності жодного пристрою, з якого ви можете звірити поточний пристрій. Якщо ви скинете все Скинути все - Резервна копія не може бути дешифрована цією парольною фразою: переконайтесь, що відновлювальна парольна фраза зазначена правильно. + Не вдалося розшифрувати резервну копію цією парольною фразою: переконайтесь, що вказано правильну парольну фразу відновлення. Не знаєте вашої відновлювальної парольної фрази\? Ви можете %s. Відновлювальна парольна фраза Забули або втратили усі можливості для відновлення\? Скинути все Використати файл - Скористатись відновлювальними парольною фразою або ключем + Скористатись парольною фразою відновлення або ключем Скористатись відновлювальними парольною фразою або ключем Використовуйте найостаннішій ${app_name} на ваших інших пристроях, ${app_name} Web, ${app_name} для комп\'ютерів, ${app_name} iOS, ${app_name} для Android, або будь-який інший, здатний до перехресного підписування, Matrix-клієнт Використовуйте найостаннішій ${app_name} на ваших інших пристроях: @@ -2503,7 +2501,7 @@ Резервні копії всіх ключів створено Резервне копіювання ключів. Це може тривати кілька хвилин… Ваш обліковий запис може бути зламано - Перевірити, порівнявши емоджі натомість + Звірити, порівнявши емоджі натомість Не вдалося сканувати Сканувати за допомогою цього пристрою Якщо ви не можете сканувати код вгорі, перевірте, порівнявши короткий унікальний набір емоджі. @@ -2545,7 +2543,7 @@ Сеанс не може узгодити ключі, хеш, MAC або метод SAS Сеанс не розпізнає про цю транзакцію Час перевірки минув - Перевірте, порівнявши короткий текстовий рядок. + Звірити, порівнявши короткий текстовий рядок. Ви вийшли з облікового запису через хибні або застарілі облікові дані. Використати конфігурацію ${app_name} виявив користувацьку конфігурацію сервера для вашого userID домену «%1$s»: @@ -2751,4 +2749,55 @@ викидання користувачів прибере їх з цього простору. \n \nЩоб запобігти їх повторному приєднанню, замість цього слід заблокувати їх. + Звірити %s + Порівняйте унікальні емоджі, переконавшись, що їх показано в однаковому порядку. + Звірити порівнявши емоджі + Звірити + Створює просте опитування + Вибрана опція + + %d голос - Підсумок + %d голоси - Підсумок + %d голосів - Підсумок + %d голосів - Підсумок + + + %d голос + %d голоси + %d голосів + %d голосів + + Ініціалізувати перехресне підписування + %1$s (%2$s) входить за допомогою нового сеансу: + нестабільна + стабільна + Типова версія + Версії кімнати 👓 + Для безпеки зробіть це особисто або скористайтеся іншим способом зв\'язку. + Для власної безпеки, перевірте %s звіривши одноразовий код. + ${app_name} не обробляє події типу «%1$s» + Перевірка за допомогою емоджі + Перевірити власноруч + Запит перевірки надіслано + Результат перевірки + Якщо ви не бачите особу, порівняйте натомість емоджі + Ваш домен е-пошти не авторизовано для реєстрації на цьому сервері + Заборонити будь-кому, хто не є учасником %s, будь-коли приєднуватися до цієї кімнати + Показати корисні дані для зневадження застосунку + Показати дані зневадження на екрані + ${app_name} може частіше виходити з ладу, коли виникає несподівана помилка + Швидкий збій + Показано лише перші результати, введіть більше букв… + Ваше посилання matrix.to неправильне + Ви втратите доступ до захищених повідомлень, якщо ви не увійдете, щоб відновити ключі шифрування. + Не вдалося знайти чинний домашній сервер. Перевірте свій ідентифікатор + Це хибний ідентифікатор користувача. Очікуваний формат: «@user:homeserver.org» + Якщо ви не знаєте свій пароль, поверніться, щоб скинути його. + Цей домашній сервер працює давній версії. Попросіть адміністратора домашнього сервера оновити його. Ви можете продовжити, але деякі функції не працюватимуть. + Цей домашній сервер працює на надто давній версії для під\'єднання. Попросіть адміністратора домашнього сервера оновити його. + Ми щойно надіслали листа на %1$s +\nНатисніть на посилання, яке він містить, щоб продовжити створення облікового запису. + Ваш обліковий запис ще не створено. +\n +\nЗупинити процес реєстрації\? \ No newline at end of file From fe2bf80b84e31db47e4d4aba3ea3d40cd0e617c2 Mon Sep 17 00:00:00 2001 From: Glandos Date: Mon, 4 Oct 2021 20:17:50 +0000 Subject: [PATCH 034/144] Translated using Weblate (French) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/fr/ --- fastlane/metadata/android/fr-FR/changelogs/40102000.txt | 4 ++-- fastlane/metadata/android/fr-FR/changelogs/40102010.txt | 2 ++ fastlane/metadata/android/fr-FR/changelogs/40103000.txt | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/fr-FR/changelogs/40102010.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/40103000.txt diff --git a/fastlane/metadata/android/fr-FR/changelogs/40102000.txt b/fastlane/metadata/android/fr-FR/changelogs/40102000.txt index 504c3e24be..0bcf3551f6 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/40102000.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/40102000.txt @@ -1,2 +1,2 @@ -Principaux changements pour cette version : messages vocaux activés par défault. -Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.1.16 +Principaux changements pour cette version : messages vocaux activés par défaut. +Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/fr-FR/changelogs/40102010.txt b/fastlane/metadata/android/fr-FR/changelogs/40102010.txt new file mode 100644 index 0000000000..910d4bd9c0 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Principaux changements pour cette version : Beaucoup d’améliorations sur la VoIP et les Espaces (toujours en bêta). +Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/fr-FR/changelogs/40103000.txt b/fastlane/metadata/android/fr-FR/changelogs/40103000.txt new file mode 100644 index 0000000000..66c2c3db86 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Principaux changements pour cette version : Organisez vous salons à l’aide des Espaces ! +Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.0 From 16f6e358dcc318f0cd98579fae0a23ae5c43dbb8 Mon Sep 17 00:00:00 2001 From: Evrim Date: Wed, 6 Oct 2021 08:30:56 +0000 Subject: [PATCH 035/144] Translated using Weblate (Dutch) Currently translated at 62.6% (1664 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/nl/ --- vector/src/main/res/values-nl/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/res/values-nl/strings.xml b/vector/src/main/res/values-nl/strings.xml index 6c4038e56a..e7d6f84069 100644 --- a/vector/src/main/res/values-nl/strings.xml +++ b/vector/src/main/res/values-nl/strings.xml @@ -1585,4 +1585,5 @@ U heeft hier geüpgraded. %s heeft hier geüpgraded. U heeft toekomstige kamergeschiedenis zichtbaar gemaakt voor %1$s + %1$ds verliet de ruimte \ No newline at end of file From 0e498cc8385b8f172429a51971ad3793ec9dd778 Mon Sep 17 00:00:00 2001 From: Erik Huizinga Date: Wed, 6 Oct 2021 08:30:39 +0000 Subject: [PATCH 036/144] Translated using Weblate (Dutch) Currently translated at 62.6% (1664 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/nl/ --- vector/src/main/res/values-nl/strings.xml | 353 +++++++++++++++++++++- 1 file changed, 348 insertions(+), 5 deletions(-) diff --git a/vector/src/main/res/values-nl/strings.xml b/vector/src/main/res/values-nl/strings.xml index e7d6f84069..e133e4ac1d 100644 --- a/vector/src/main/res/values-nl/strings.xml +++ b/vector/src/main/res/values-nl/strings.xml @@ -112,10 +112,10 @@ %1$s heeft %2$s als gespreksadres toegevoegd en %3$s verwijderd. %1$s heeft het hoofdadres voor dit gesprek ingesteld op %2$s. %1$s heeft het hoofdadres voor dit gesprek verwijderd. - %1$s heeft gasten de toegang tot deze kamer verleend. + %1$s heeft gasten de toegang tot dit gesprek verleend. %1$s heeft gasten de toegang tot het gesprek verhinderd. - %1$s heeft eind-tot-eind-versleuteling ingeschakeld. - %1$s heeft eind-tot-eind-versleuteling ingeschakeld (onbekend algoritme %2$s). + %1$s heeft eind-tot-eindversleuteling ingeschakeld. + %1$s heeft eind-tot-eindversleuteling ingeschakeld (onbekend algoritme %2$s). Berichten Gesprek @@ -1552,7 +1552,7 @@ Kan stembericht niet afspelen Je hebt je profiel geüpdatet %1$s Je hebt een VoIP-vergadering aangevraagd - U heeft gasten de toegang tot deze kamer verleend. + U heeft gasten de toegang tot dit gesprek verleend. U heeft het hoofdadres voor dit gesprek ingesteld op %1$s. U heeft %1$s als gespreksadres toegevoegd en %2$s verwijderd. @@ -1584,6 +1584,349 @@ • Servers die overeenkomen met %s zijn verbannen. U heeft hier geüpgraded. %s heeft hier geüpgraded. - U heeft toekomstige kamergeschiedenis zichtbaar gemaakt voor %1$s + Je hebt toekomstige gespreksgeschiedenis zichtbaar gemaakt voor %1$s %1$ds verliet de ruimte + + Conclusie Bevestiging + Botknoppen + Gespreksinstellingen + Gespreksnaam + Integraties Beheren + + %d uitnodiging + %d uitnodigingen + + + %d seconde + %d seconden + + Weergave Melding + Test Push + %s verwijderen\? + Telefoonnummers + Emailadressen + Ophangen + Nieuwe PIN + PIN Herstellen + PIN vergeten\? + PIN Bevestigen + Uitnodiging intrekken + Contactpersonen + Telefoonboek + Kan Niet Ontsleutelen + Gespreksnaam + Beveiligingszin + Instellen + Beveiligde backup + Chat openen + Rol instellen + %1$s gebruiken + Huidige taal + Gebruikers Uitnodigen + Gebruikers uitnodigen + Leden toevoegen + Login bevestigen + %1$s (%2$s) + Wachtwoordzin herstellen + %s invoeren + Bestand Gebruiken + Configuratie meldingen + Versleuteling ingeschakeld + Klaar! + %s bevestigen + Accountwachtwoord + Berichtsleutel + Herstelwachtwoordzin + Bevestiging Geannuleerd + Sleutelverzoeken + Verwijderen Bevestigen + Gekozen Optie + + %d stem + %d stemmen + + Accountgegevens + Dev Tools + QR-code + Sleutels herstellen + Gekruist Ondertekenen Initialiseren + Niet Vertrouwd + Beveiliging Voltooien + Sessies Beheren + Actieve Sessies + Versleuteling inschakelen + Versleuteling inschakelen\? + Berichtverwerker + Andere gesprekken + Recente gesprekken + Gesprek Verlaten + + Eén persoon + %1$d personen + + Gespreksinstellingen + Administratoracties + Meer weten + %s geverifieerd + %s verifiëren + Kan niet scannen + Handmatig bevestigen + Bevestigingsverzoek + Bevestiging Verstuurd + %s heeft geannuleerd + Jij hebt geaccepteerd + %s heeft geaccepteerd + Je hebt geannuleerd + Niet beveiligd + Ze komen overeen + Versleuteling inschakelen + Andere sessies + Huidige sessie + Schudden gedetecteerd! + Gevoeligheidsdrempel + Ontwikkelaarsmodus + Geavanceerde instellingen + Initiële synchronisatie + Data wissen + Data wissen + Inloggen + Inloggen + Gezien door + Matrix-ID + Verouderde homeserver + Selecteer matrix.org + Opnieuw verzenden + Code invoeren + Telefoonnummer + Email (optioneel) + Nieuw wachtwoord + Inloggen + Aanmelden + Meer weten + Ongelezen berichten + Alleen vermeldingen + Beveiligde Backup + Verbanning gebruiker ongedaan maken + Gebruiker verbannen + Gebruiker verwijderen + Uitnodiging annuleren + Gebruiker niet meer negeren + Stem + onstabiel + stabiel + Alle berichten + Gebruiker negeren + Inhoud gerapporteerd + GEBRUIKER NEGEREN + Aangepaste rapportage + Dit is ongepast + Dit is spam + Gelezen door %s + Wachtwoord verbergen + Wachtwoord tonen + Bijlage verzenden + Identiteitsserver + Gelezen om + Voorwaarden Herzien + Gesprek binnengaan + Gesprek aanmaken + Gesprekken filteren + Berichtwijzigingen + Bestand versleutelen + Thumbnail versleutelen + Directe Berichten + Feedback + Token registreren + Pushregels + Snelle Reacties + Gespreksmap + Bericht verwijderd + Beveiligde Backup + Actieve widgets + %1$s: %2$s + Publiek + Privé + Steekwoorden + \@room + Overige + Geen + Gebruiker negeren + Jezelf degraderen\? + Uitnodiging annuleren + In de wacht zetten + SSL-fout. + Camera wisselen + Draadloze Koptelefoon + Spaces + Genodigden + Wisselen + Opwaarderen + Aanbevolen + Beschrijving + Willekeurig + Algemeen + Privé + Publiek + Mislukt + Verzonden + Verzenden + Type + Ongecontroleerd + Gecontroleerd + Geselecteerd + Video + Afbeelding + Schermafbeelding + Gebruikers + Overdragen + Verbinden + Onderwerp + Rol + "Onderwerp: " + Verlaten + Instellingen + Stemming + Of + Code + Suggesties + Contactpersonen + Recent + Onderwerp + Publiceren + Rechten + Doorgaan + Teruggaan + Depubliceren + Toevoegen + Afwijzen + Accepteren + Versturen + UITNODIGEN + Onversleuteld + Bericht + Problemen oplossen + Beëindigen + Verversen + Verwijderen + Waarschuwing: + Nee + Ja + Vertrouwd + Sessies + Waarschuwing + Geverifieerd + Verfiëren + Gekruist Ondertekenen + Tijdlijn + Negeren opheffen + Gebruikers + Genodigden + Aangepast + Moderatoren + Administrators + Uploads + Meldingen + Meer + Beveiliging + Jij + Wachten + Sticker + Bestand + Geluid + Afbeelding. + Video. + Snel falen + Instellingen + Schudden + Wachtwoord + Waarschuwing + Volgende + Wachtwoord + Gebruikersnaam + Volgende + Volgende + Volgende + Email + Waarschuwing + Succes! + Doorgaan + Waarschuwing! + Email + Volgende + Adres + Doorgaan + Ander + Spoiler + Instellingen + Dempen + RAPPORTEREN + BESTANDEN + MEDIA + Sticker + Galerij + Geluid + Camera + Contactpersoon + Bestand + In afwachting + (bewerkt) + Wachten… + Formaat: + Url: + session_name: + app_display_name: + push_key: + app_id: + Expert + Voorkeuren + Algemeen + BEKIJKEN + Beheren + Degraderen + Achterkant + Voorkant + Headset + Luidspreker + Telefoon + Meldingen + Succes + Kopiëren + Geef toestemming om de camera te gebruiken via de systeeminstellingen om deze actie uit te voeren. + Sommige rechten ontbreken om deze actie uit te voeren, geeft a.u.b. toestemming via de systeeminstellingen. + Spaces + Meer Weten + Begin met chatten + Herstellen + Afwijzen + Systeemstandaard + Je hebt eind-tot-eindversleuteling ingeschakeld (onbekend algoritme %1$s). + Je hebt eind-tot-eindversleuteling ingeschakeld. + Je hebt gasten de toegang tot het gesprek verhinderd. + %1$s heeft gasten de toegang tot het gesprek verhinderd. + Je hebt gasten de toegang tot het gesprek verhinderd. + Je hebt hier gasten toegelaten. + %1$s heeft hier gasten toegelaten. + Je hebt het gespreksadres gewijzigd. + %1$s heeft het gespreksadres gewijzigd. + Je hebt het hoofdadres en alternatieve gespreksadres gewijzigd. + %1$s heeft het hoofdadres en alternatieve gespreksadres gewijzigd. + Je hebt het alternatieve gespreksadres gewijzigd. + %1$s heeft het alternatieve gespreksadres gewijzigd. + + Je hebt alternatief gespreksadres %1$s verwijderd. + Je hebt alternatieve gespreksadressen %1$s verwijderd. + + + %1$s heeft %2$s als alternatief gespreksadres verwijderd. + %1$s heeft %2$s als alternatieve gespreksadressen verwijderd. + + + Je hebt %1$s als alternatief gespreksadres toegevoegd. + Je hebt %1$s als alternatieve gespreksadressen toegevoegd. + + + %1$s heeft %2$s als alternatief gespreksadres toegevoegd. + %1$s heeft %2$s als alternatieve gespreksadressen toegevoegd. + + Aan de slag \ No newline at end of file From a2f64177eba05a03a5c4b258cf8fa5517b1a9693 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 6 Oct 2021 15:20:33 +0100 Subject: [PATCH 037/144] adding the identity terms to the discovery page state --- .../discovery/DiscoverySettingsController.kt | 4 +- .../discovery/DiscoverySettingsFragment.kt | 4 +- .../discovery/DiscoverySettingsState.kt | 8 +++- .../discovery/DiscoverySettingsViewModel.kt | 45 +++++++++++++------ 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 440307ae51..8cd787d6ed 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -62,7 +62,7 @@ class DiscoverySettingsController @Inject constructor( } is Success -> { buildIdentityServerSection(data) - val hasIdentityServer = data.identityServer().isNullOrBlank().not() + val hasIdentityServer = data.identityServer()?.serverUrl.isNullOrBlank().not() if (hasIdentityServer && !data.termsNotSigned) { buildConsentSection(data) buildEmailsSection(data.emailList) @@ -106,7 +106,7 @@ class DiscoverySettingsController @Inject constructor( } private fun buildIdentityServerSection(data: DiscoverySettingsState) { - val identityServer = data.identityServer() ?: stringProvider.getString(R.string.none) + val identityServer = data.identityServer()?.serverUrl ?: stringProvider.getString(R.string.none) val host = this settingsSectionTitleItem { diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 0e67b03f7c..66ce2d0976 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -111,7 +111,7 @@ class DiscoverySettingsFragment @Inject constructor( requireContext(), termsActivityResultLauncher, TermsService.ServiceType.IdentityService, - state.identityServer()?.ensureProtocol() ?: "", + state.identityServer()?.serverUrl?.ensureProtocol() ?: "", null) } } @@ -179,7 +179,7 @@ class DiscoverySettingsFragment @Inject constructor( override fun onTapUpdateUserConsent(newValue: Boolean) { if (newValue) { withState(viewModel) { state -> - requireContext().showIdentityServerConsentDialog(state.identityServer.invoke()) { + requireContext().showIdentityServerConsentDialog(state.identityServer.invoke()?.serverUrl) { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index 21fbcf1ca7..bc56b770f5 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -19,12 +19,18 @@ package im.vector.app.features.discovery import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms data class DiscoverySettingsState( - val identityServer: Async = Uninitialized, + val identityServer: Async = Uninitialized, val emailList: Async> = Uninitialized, val phoneNumbersList: Async> = Uninitialized, // Can be true if terms are updated val termsNotSigned: Boolean = false, val userConsent: Boolean = false ) : MvRxState + +data class IdentityServerWithTerms( + val serverUrl: String?, + val terms: List +) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 9d730d29cf..d703385ca8 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -27,20 +27,25 @@ import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.ensureProtocol import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.rx.rx class DiscoverySettingsViewModel @AssistedInject constructor( @Assisted initialState: DiscoverySettingsState, - private val session: Session) : - VectorViewModel(initialState) { + private val session: Session, + private val stringProvider: StringProvider +) : VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -57,25 +62,37 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private val identityService = session.identityService() + private val termsService: TermsService = session private val identityServerManagerListener = object : IdentityServiceListener { override fun onIdentityServerChange() = withState { state -> - val identityServerUrl = identityService.getCurrentIdentityServerUrl() - val currentIS = state.identityServer() - setState { - copy( - identityServer = Success(identityServerUrl), - userConsent = identityService.getUserConsent() - ) + viewModelScope.launch { + val identityServer = fetchIdentityServerWithTerms() + val currentIS = state.identityServer() + setState { + copy( + identityServer = Success(identityServer), + userConsent = identityService.getUserConsent() + ) + } + if (currentIS != identityServer) retrieveBinding() } - if (currentIS != identityServerUrl) retrieveBinding() } } + private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms { + val identityServerUrl = identityService.getCurrentIdentityServerUrl() + val policies = if (identityServerUrl == null) emptyList() else { + val terms = termsService.getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) + terms.serverResponse.getLocalizedTerms(stringProvider.getString(R.string.resources_language)) + } + return IdentityServerWithTerms(identityServerUrl, policies) + } + init { setState { copy( - identityServer = Success(identityService.getCurrentIdentityServerUrl()), + identityServer = Success(IdentityServerWithTerms(identityService.getCurrentIdentityServerUrl(), emptyList())), userConsent = identityService.getUserConsent() ) } @@ -143,7 +160,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( val data = session.identityService().setNewIdentityServer(action.url) setState { copy( - identityServer = Success(data), + identityServer = Success(IdentityServerWithTerms(data, emptyList())), userConsent = false ) } @@ -287,7 +304,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private fun retrieveBinding(threePids: List) = withState { state -> - if (state.identityServer().isNullOrBlank()) return@withState + if (state.identityServer()?.serverUrl.isNullOrBlank()) return@withState val emails = threePids.filterIsInstance() val msisdns = threePids.filterIsInstance() @@ -335,7 +352,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private fun submitMsisdnToken(action: DiscoverySettingsAction.SubmitMsisdnToken) = withState { state -> - if (state.identityServer().isNullOrBlank()) return@withState + if (state.identityServer()?.serverUrl.isNullOrBlank()) return@withState changeThreePidSubmitState(action.threePid, Loading()) From 04d844b1a93041447cb517382ea1978beab67931 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 6 Oct 2021 16:10:17 +0100 Subject: [PATCH 038/144] allowing the expandable scrollbar to be hidden --- .../main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt index 218a22533a..a148c63865 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt @@ -40,11 +40,15 @@ abstract class ExpandableTextItem : VectorEpoxyModel( @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var movementMethod: MovementMethod? = null + @EpoxyAttribute + var enableScrollBar = true + private var isExpanded = false private var expandedLines = 0 override fun bind(holder: Holder) { super.bind(holder) + holder.content.isVerticalScrollBarEnabled = enableScrollBar holder.content.text = content holder.content.copyOnLongClick() holder.content.movementMethod = movementMethod From 7ce811c227ffd83c6c72c5ef09c7aa86da82a1f4 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 6 Oct 2021 16:23:33 +0100 Subject: [PATCH 039/144] binding the indentity server policy urls to the discovery page --- .../discovery/DiscoverySettingsController.kt | 39 ++++++++++---- .../discovery/DiscoverySettingsState.kt | 4 +- .../discovery/DiscoverySettingsViewModel.kt | 54 +++++++++++-------- vector/src/main/res/values/strings.xml | 1 + 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 8cd787d6ed..502b9c5337 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -26,11 +26,14 @@ import im.vector.app.R import im.vector.app.core.epoxy.attributes.ButtonStyle import im.vector.app.core.epoxy.attributes.ButtonType import im.vector.app.core.epoxy.attributes.IconMode +import im.vector.app.core.epoxy.expandableTextItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.getFormattedValue import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.EvenBetterLinkMovementMethod +import org.matrix.android.sdk.api.extensions.appendNl import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid @@ -105,8 +108,9 @@ class DiscoverySettingsController @Inject constructor( } } - private fun buildIdentityServerSection(data: DiscoverySettingsState) { - val identityServer = data.identityServer()?.serverUrl ?: stringProvider.getString(R.string.none) + private fun buildIdentityServerSection(data: DiscoverySettingsState, ) { + val identityServer = data.identityServer() + val identityServerUrl = identityServer?.serverUrl ?: stringProvider.getString(R.string.none) val host = this settingsSectionTitleItem { @@ -116,28 +120,43 @@ class DiscoverySettingsController @Inject constructor( settingsItem { id("idServer") - title(identityServer) + title(identityServerUrl) } - if (data.identityServer() != null && data.termsNotSigned) { + val policyUrls = identityServer?.policyUrls?.joinToString(separator = "\n") { it } + if (policyUrls != null) { + val title = stringProvider.getString(R.string.settings_discovery_identity_server_policies_title) + expandableTextItem { + id("policy-urls") + maxLines(1) + enableScrollBar(false) + content(buildString { + append(title) + appendNl(policyUrls) + }) + movementMethod(EvenBetterLinkMovementMethod()) + } + } + + if (identityServer != null && data.termsNotSigned) { settingsInfoItem { id("idServerFooter") - helperText(host.stringProvider.getString(R.string.settings_agree_to_terms, identityServer)) + helperText(host.stringProvider.getString(R.string.settings_agree_to_terms, identityServerUrl)) showCompoundDrawable(true) itemClickListener { host.listener?.openIdentityServerTerms() } } settingsButtonItem { id("seeTerms") colorProvider(host.colorProvider) - buttonTitle(host.stringProvider.getString(R.string.open_terms_of, identityServer)) + buttonTitle(host.stringProvider.getString(R.string.open_terms_of, identityServerUrl)) buttonClickListener { host.listener?.openIdentityServerTerms() } } } else { settingsInfoItem { id("idServerFooter") showCompoundDrawable(false) - if (data.identityServer() != null) { - helperText(host.stringProvider.getString(R.string.settings_discovery_identity_server_info, identityServer)) + if (identityServer != null) { + helperText(host.stringProvider.getString(R.string.settings_discovery_identity_server_info, identityServerUrl)) } else { helperTextResId(R.string.settings_discovery_identity_server_info_none) } @@ -147,7 +166,7 @@ class DiscoverySettingsController @Inject constructor( settingsButtonItem { id("change") colorProvider(host.colorProvider) - if (data.identityServer() == null) { + if (identityServer == null) { buttonTitleId(R.string.add_identity_server) } else { buttonTitleId(R.string.change_identity_server) @@ -155,7 +174,7 @@ class DiscoverySettingsController @Inject constructor( buttonClickListener { host.listener?.onTapChangeIdentityServer() } } - if (data.identityServer() != null) { + if (identityServer != null) { settingsInfoItem { id("removeInfo") helperTextResId(R.string.settings_discovery_disconnect_identity_server_info) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index bc56b770f5..360ff4b2b1 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -31,6 +31,6 @@ data class DiscoverySettingsState( ) : MvRxState data class IdentityServerWithTerms( - val serverUrl: String?, - val terms: List + val serverUrl: String, + val policyUrls: List ) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index d703385ca8..9025537c7e 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -67,32 +67,27 @@ class DiscoverySettingsViewModel @AssistedInject constructor( private val identityServerManagerListener = object : IdentityServiceListener { override fun onIdentityServerChange() = withState { state -> viewModelScope.launch { - val identityServer = fetchIdentityServerWithTerms() - val currentIS = state.identityServer() - setState { - copy( - identityServer = Success(identityServer), - userConsent = identityService.getUserConsent() - ) - } - if (currentIS != identityServer) retrieveBinding() + runCatching { fetchIdentityServerWithTerms() }.fold( + onSuccess = { + val currentIS = state.identityServer() + setState { + copy( + identityServer = Success(it), + userConsent = identityService.getUserConsent() + ) + } + if (currentIS != it) retrieveBinding() + }, + onFailure = { _viewEvents.post(DiscoverySettingsViewEvents.Failure(it)) } + ) } } } - private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms { - val identityServerUrl = identityService.getCurrentIdentityServerUrl() - val policies = if (identityServerUrl == null) emptyList() else { - val terms = termsService.getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) - terms.serverResponse.getLocalizedTerms(stringProvider.getString(R.string.resources_language)) - } - return IdentityServerWithTerms(identityServerUrl, policies) - } - init { setState { copy( - identityServer = Success(IdentityServerWithTerms(identityService.getCurrentIdentityServerUrl(), emptyList())), + identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { IdentityServerWithTerms(it, emptyList()) }), userConsent = identityService.getUserConsent() ) } @@ -116,7 +111,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( override fun handle(action: DiscoverySettingsAction) { when (action) { - DiscoverySettingsAction.Refresh -> refreshPendingEmailBindings() + DiscoverySettingsAction.Refresh -> fetchContent() DiscoverySettingsAction.RetrieveBinding -> retrieveBinding() DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer() is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action) @@ -395,12 +390,29 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } } - private fun refreshPendingEmailBindings() = withState { state -> + private fun fetchContent() = withState { state -> state.emailList()?.forEach { info -> when (info.isShared()) { SharedState.BINDING_IN_PROGRESS -> finalizeBind3pid(DiscoverySettingsAction.FinalizeBind3pid(info.threePid), false) else -> Unit } } + viewModelScope.launch { + runCatching { fetchIdentityServerWithTerms() }.fold( + onSuccess = { setState { copy(identityServer = Success(it)) } }, + onFailure = { _viewEvents.post(DiscoverySettingsViewEvents.Failure(it)) } + ) + } + } + + private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms? { + val identityServerUrl = identityService.getCurrentIdentityServerUrl() + return identityServerUrl?.let { + val terms = termsService.getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) + .serverResponse + .getLocalizedTerms(stringProvider.getString(R.string.resources_language)) + val policyUrls = terms.mapNotNull { it.localizedUrl } + IdentityServerWithTerms(identityServerUrl, policyUrls) + } } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index bb6f73812f..22cdd98e97 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2349,6 +2349,7 @@ Configure identity server Open Discovery Settings Change identity server + Identity server policy You are currently using %1$s to discover and be discoverable by existing contacts you know. You are not currently using an identity server. To discover and be discoverable by existing contacts you know, configure one below. Discoverable email addresses From 5a4f320bf984bece0d786dcb30855b876706139f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 6 Oct 2021 17:16:57 +0100 Subject: [PATCH 040/144] binding the expanded state as part of the view, allows us to manually control the expansion --- .../app/core/epoxy/ExpandableTextItem.kt | 33 +++++++++++++++---- .../discovery/DiscoverySettingsAction.kt | 1 + .../discovery/DiscoverySettingsController.kt | 8 ++++- .../discovery/DiscoverySettingsFragment.kt | 4 +++ .../discovery/DiscoverySettingsState.kt | 3 +- .../discovery/DiscoverySettingsViewModel.kt | 29 ++++++++++------ 6 files changed, 59 insertions(+), 19 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt index a148c63865..ee942ed885 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt @@ -43,7 +43,13 @@ abstract class ExpandableTextItem : VectorEpoxyModel( @EpoxyAttribute var enableScrollBar = true - private var isExpanded = false + @EpoxyAttribute + var expanded: Boolean? = null + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var onExpandClicked: () -> Unit? = {} + + private var internalIsExpanded = false private var expandedLines = 0 override fun bind(holder: Holder) { @@ -53,18 +59,31 @@ abstract class ExpandableTextItem : VectorEpoxyModel( holder.content.copyOnLongClick() holder.content.movementMethod = movementMethod + if (expanded == null) { + holder.view.setOnClickListener { + if (internalIsExpanded) { + collapse(holder) + } else { + expand(holder) + } + } + } else { + holder.view.setOnClickListener { onExpandClicked() } + } + holder.content.doOnPreDraw { if (holder.content.lineCount > maxLines) { expandedLines = holder.content.lineCount holder.content.maxLines = maxLines - holder.view.setOnClickListener { - if (isExpanded) { - collapse(holder) - } else { + expanded?.let { expanded -> + if (expanded) { expand(holder) + } else { + collapse(holder) } } + holder.arrow.isVisible = true } else { holder.arrow.isVisible = false @@ -81,7 +100,7 @@ abstract class ExpandableTextItem : VectorEpoxyModel( holder.content.ellipsize = null holder.arrow.setImageResource(R.drawable.ic_expand_less) holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_collapse) - isExpanded = true + internalIsExpanded = true } private fun collapse(holder: Holder) { @@ -93,7 +112,7 @@ abstract class ExpandableTextItem : VectorEpoxyModel( holder.content.ellipsize = TextUtils.TruncateAt.END holder.arrow.setImageResource(R.drawable.ic_expand_more) holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_expand) - isExpanded = false + internalIsExpanded = false } class Holder : VectorEpoxyHolder() { diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt index 426f1321e7..3d6038da3f 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt @@ -31,4 +31,5 @@ sealed class DiscoverySettingsAction : VectorViewModelAction { data class FinalizeBind3pid(val threePid: ThreePid) : DiscoverySettingsAction() data class SubmitMsisdnToken(val threePid: ThreePid.Msisdn, val code: String) : DiscoverySettingsAction() data class CancelBinding(val threePid: ThreePid) : DiscoverySettingsAction() + object PolicyUrlsExpandedStateToggled : DiscoverySettingsAction() } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 502b9c5337..1d8989adc1 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.discovery +import android.text.method.LinkMovementMethod import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail @@ -134,7 +135,11 @@ class DiscoverySettingsController @Inject constructor( append(title) appendNl(policyUrls) }) - movementMethod(EvenBetterLinkMovementMethod()) + movementMethod(LinkMovementMethod()) + expanded(data.isIdentityPolicyUrlsExpanded) + onExpandClicked { + host.listener?.onPolicyUrlsExpandedStateToggled() + } } } @@ -419,5 +424,6 @@ class DiscoverySettingsController @Inject constructor( fun onTapDisconnectIdentityServer() fun onTapUpdateUserConsent(newValue: Boolean) fun onTapRetryToRetrieveBindings() + fun onPolicyUrlsExpandedStateToggled() } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 66ce2d0976..b079157421 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -192,6 +192,10 @@ class DiscoverySettingsFragment @Inject constructor( viewModel.handle(DiscoverySettingsAction.RetrieveBinding) } + override fun onPolicyUrlsExpandedStateToggled() { + viewModel.handle(DiscoverySettingsAction.PolicyUrlsExpandedStateToggled) + } + private fun navigateToChangeIdentityServerFragment() { (vectorBaseActivity as? VectorSettingsActivity)?.navigateTo(SetIdentityServerFragment::class.java) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index 360ff4b2b1..4dbb5c495f 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -27,7 +27,8 @@ data class DiscoverySettingsState( val phoneNumbersList: Async> = Uninitialized, // Can be true if terms are updated val termsNotSigned: Boolean = false, - val userConsent: Boolean = false + val userConsent: Boolean = false, + val isIdentityPolicyUrlsExpanded: Boolean = false ) : MvRxState data class IdentityServerWithTerms( diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 9025537c7e..fc3eb7b990 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -111,16 +111,17 @@ class DiscoverySettingsViewModel @AssistedInject constructor( override fun handle(action: DiscoverySettingsAction) { when (action) { - DiscoverySettingsAction.Refresh -> fetchContent() - DiscoverySettingsAction.RetrieveBinding -> retrieveBinding() - DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer() - is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action) - is DiscoverySettingsAction.UpdateUserConsent -> handleUpdateUserConsent(action) - is DiscoverySettingsAction.RevokeThreePid -> revokeThreePid(action) - is DiscoverySettingsAction.ShareThreePid -> shareThreePid(action) - is DiscoverySettingsAction.FinalizeBind3pid -> finalizeBind3pid(action, true) - is DiscoverySettingsAction.SubmitMsisdnToken -> submitMsisdnToken(action) - is DiscoverySettingsAction.CancelBinding -> cancelBinding(action) + DiscoverySettingsAction.Refresh -> fetchContent() + DiscoverySettingsAction.RetrieveBinding -> retrieveBinding() + DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer() + DiscoverySettingsAction.PolicyUrlsExpandedStateToggled -> toggleExpandedPolicyUrlsState() + is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action) + is DiscoverySettingsAction.UpdateUserConsent -> handleUpdateUserConsent(action) + is DiscoverySettingsAction.RevokeThreePid -> revokeThreePid(action) + is DiscoverySettingsAction.ShareThreePid -> shareThreePid(action) + is DiscoverySettingsAction.FinalizeBind3pid -> finalizeBind3pid(action, true) + is DiscoverySettingsAction.SubmitMsisdnToken -> submitMsisdnToken(action) + is DiscoverySettingsAction.CancelBinding -> cancelBinding(action) }.exhaustive } @@ -147,6 +148,14 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } } + private fun toggleExpandedPolicyUrlsState() { + withState { + setState { + copy(isIdentityPolicyUrlsExpanded = !it.isIdentityPolicyUrlsExpanded) + } + } + } + private fun changeIdentityServer(action: DiscoverySettingsAction.ChangeIdentityServer) { setState { copy(identityServer = Loading()) } From 3111d0b46d7f662e50fc4663530e4f141b505d7c Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 6 Oct 2021 17:34:02 +0100 Subject: [PATCH 041/144] updating discovery consent dialog to include policy link which expands the policy urls view --- .../src/main/java/im/vector/app/core/utils/Dialogs.kt | 9 ++++++++- .../app/features/discovery/DiscoverySettingsAction.kt | 1 + .../features/discovery/DiscoverySettingsFragment.kt | 8 +++++--- .../features/discovery/DiscoverySettingsViewModel.kt | 11 ++++++----- vector/src/main/res/values/strings.xml | 1 + 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt index c73fa70388..99e8684229 100644 --- a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt +++ b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt @@ -40,13 +40,20 @@ fun Context.displayInWebView(url: String) { .show() } -fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, consentCallBack: (() -> Unit)) { +fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, policyLinkCallback: (() -> Unit)? = null, consentCallBack: (() -> Unit)) { MaterialAlertDialogBuilder(this) .setTitle(R.string.identity_server_consent_dialog_title) .setMessage(getString(R.string.identity_server_consent_dialog_content, configuredIdentityServer ?: "")) .setPositiveButton(R.string.yes) { _, _ -> consentCallBack.invoke() } + .apply { + if (policyLinkCallback != null) { + setNeutralButton(R.string.identity_server_consent_dialog_neutral_policy) { _, _ -> + policyLinkCallback.invoke() + } + } + } .setNegativeButton(R.string.no, null) .show() } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt index 3d6038da3f..74700f668f 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt @@ -32,4 +32,5 @@ sealed class DiscoverySettingsAction : VectorViewModelAction { data class SubmitMsisdnToken(val threePid: ThreePid.Msisdn, val code: String) : DiscoverySettingsAction() data class CancelBinding(val threePid: ThreePid) : DiscoverySettingsAction() object PolicyUrlsExpandedStateToggled : DiscoverySettingsAction() + object ExpandPolicyUrls : DiscoverySettingsAction() } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index b079157421..a1582d1a44 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -179,9 +179,11 @@ class DiscoverySettingsFragment @Inject constructor( override fun onTapUpdateUserConsent(newValue: Boolean) { if (newValue) { withState(viewModel) { state -> - requireContext().showIdentityServerConsentDialog(state.identityServer.invoke()?.serverUrl) { - viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) - } + requireContext().showIdentityServerConsentDialog( + state.identityServer.invoke()?.serverUrl, + policyLinkCallback = { viewModel.handle(DiscoverySettingsAction.ExpandPolicyUrls) }, + consentCallBack = { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) } + ) } } else { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(false)) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index fc3eb7b990..4c4511b089 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -115,6 +115,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( DiscoverySettingsAction.RetrieveBinding -> retrieveBinding() DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer() DiscoverySettingsAction.PolicyUrlsExpandedStateToggled -> toggleExpandedPolicyUrlsState() + DiscoverySettingsAction.ExpandPolicyUrls -> updatePolicyUrlsExpandedState(isExpanded = true) is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action) is DiscoverySettingsAction.UpdateUserConsent -> handleUpdateUserConsent(action) is DiscoverySettingsAction.RevokeThreePid -> revokeThreePid(action) @@ -149,11 +150,11 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private fun toggleExpandedPolicyUrlsState() { - withState { - setState { - copy(isIdentityPolicyUrlsExpanded = !it.isIdentityPolicyUrlsExpanded) - } - } + withState { state -> updatePolicyUrlsExpandedState(isExpanded = !state.isIdentityPolicyUrlsExpanded) } + } + + private fun updatePolicyUrlsExpandedState(isExpanded: Boolean) { + setState { copy(isIdentityPolicyUrlsExpanded = isExpanded) } } private fun changeIdentityServer(action: DiscoverySettingsAction.ChangeIdentityServer) { diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 22cdd98e97..065e618e57 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2368,6 +2368,7 @@ Send emails and phone numbers In order to discover existing contacts you know, do you accept to send your contact data (phone numbers and/or emails) to the configured identity server (%1$s)?\n\nFor more privacy, the sent data will be hashed before being sent. + Policy Enter an identity server URL Could not connect to identity server From 0125c7675d8575b7095bab22dbb91c67fbe4f9a7 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 6 Oct 2021 19:34:04 +0200 Subject: [PATCH 042/144] Fix SIP user to native user mapping is wrong #4176 (also clear dialpad entry when call is started) --- changelog.d/4176.bugfix | 1 + .../features/call/dialpad/DialPadFragment.kt | 6 +++-- .../features/call/dialpad/DialPadLookup.kt | 22 ++++++++++++------- .../app/features/home/HomeDetailFragment.kt | 16 ++++++++++---- 4 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 changelog.d/4176.bugfix diff --git a/changelog.d/4176.bugfix b/changelog.d/4176.bugfix new file mode 100644 index 0000000000..6134dfca7c --- /dev/null +++ b/changelog.d/4176.bugfix @@ -0,0 +1 @@ +SIP user to native user mapping is wrong \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt index 5e2a569188..16e7c01b5c 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt @@ -170,8 +170,10 @@ class DialPadFragment : Fragment(), TextWatcher { } } - private fun clear() { - digits.setText("") + fun clear() { + if (::digits.isInitialized) { + digits.setText("") + } } private fun formatNumber(dialString: String): String { diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt index d256156d15..eadccab4f6 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt @@ -39,15 +39,21 @@ class DialPadLookup @Inject constructor( suspend fun lookupPhoneNumber(phoneNumber: String): Result { session.vectorCallService.protocolChecker.awaitCheckProtocols() val thirdPartyUser = session.pstnLookup(phoneNumber, webRtcCallManager.supportedPSTNProtocol).firstOrNull() ?: throw Failure.NoResult - // check to see if this is a virtual user, in which case we should find the native user - val nativeUserId = if (webRtcCallManager.supportsVirtualRooms) { - val nativeLookupResults = session.sipNativeLookup(thirdPartyUser.userId) - nativeLookupResults.firstOrNull()?.userId ?: thirdPartyUser.userId + val sipUserId = thirdPartyUser.userId + val nativeLookupResults = session.sipNativeLookup(thirdPartyUser.userId) + // If I have a native user I check for an existing native room with him... + val roomId = if (nativeLookupResults.isNotEmpty()) { + val nativeUserId = nativeLookupResults.first().userId + if (nativeUserId == session.myUserId) { + throw Failure.NumberIsYours + } + session.getExistingDirectRoomWithUser(nativeUserId) + // if there is not, just create a DM with the sip user + ?: directRoomHelper.ensureDMExists(sipUserId) } else { - thirdPartyUser.userId + // do the same if there is no corresponding native user. + directRoomHelper.ensureDMExists(sipUserId) } - if (nativeUserId == session.myUserId) throw Failure.NumberIsYours - val roomId = directRoomHelper.ensureDMExists(nativeUserId) - return Result(userId = nativeUserId, roomId = roomId) + return Result(userId = sipUserId, roomId = roomId) } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 627f4b4581..24677cf940 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -155,7 +155,7 @@ class HomeDetailFragment @Inject constructor( viewModel.observeViewEvents { viewEvent -> when (viewEvent) { - HomeDetailViewEvents.CallStarted -> dismissLoadingDialog() + HomeDetailViewEvents.CallStarted -> handleCallStarted() is HomeDetailViewEvents.FailToCall -> showFailure(viewEvent.failure) HomeDetailViewEvents.Loading -> showLoadingDialog() } @@ -190,10 +190,16 @@ class HomeDetailFragment @Inject constructor( sharedCallActionViewModel .liveKnownCalls - .observe(viewLifecycleOwner, { + .observe(viewLifecycleOwner) { currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls()) invalidateOptionsMenu() - }) + } + } + + private fun handleCallStarted() { + dismissLoadingDialog() + val fragmentTag = HomeTab.DialPad.toFragmentTag() + (childFragmentManager.findFragmentByTag(fragmentTag) as? DialPadFragment)?.clear() } override fun onDestroyView() { @@ -370,8 +376,10 @@ class HomeDetailFragment @Inject constructor( invalidateOptionsMenu() } + private fun HomeTab.toFragmentTag() = "FRAGMENT_TAG_$this" + private fun updateSelectedFragment(tab: HomeTab) { - val fragmentTag = "FRAGMENT_TAG_$tab" + val fragmentTag = tab.toFragmentTag() val fragmentToShow = childFragmentManager.findFragmentByTag(fragmentTag) childFragmentManager.commitTransaction { childFragmentManager.fragments From c55598a099751c23c3e6f2411b9bed90e17c6240 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Oct 2021 23:07:32 +0000 Subject: [PATCH 043/144] Bump libphonenumber from 8.12.33 to 8.12.34 Bumps [libphonenumber](https://github.com/google/libphonenumber) from 8.12.33 to 8.12.34. - [Release notes](https://github.com/google/libphonenumber/releases) - [Changelog](https://github.com/google/libphonenumber/blob/master/making-metadata-changes.md) - [Commits](https://github.com/google/libphonenumber/compare/v8.12.33...v8.12.34) --- updated-dependencies: - dependency-name: com.googlecode.libphonenumber:libphonenumber dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 2cf218edf5..e3c8d7c4f1 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -154,7 +154,7 @@ dependencies { implementation 'com.otaliastudios:transcoder:0.10.4' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.33' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.34' testImplementation libs.tests.junit testImplementation 'org.robolectric:robolectric:4.6.1' diff --git a/vector/build.gradle b/vector/build.gradle index f2cb2831bd..b3cf745584 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -361,7 +361,7 @@ dependencies { implementation 'com.facebook.stetho:stetho:1.6.0' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.33' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.34' // rx implementation libs.rx.rxKotlin From 5fdaa45246fe74c91c13e00a38312a5900d5ffa0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Oct 2021 10:36:38 +0200 Subject: [PATCH 044/144] klint -> ktlint --- .github/workflows/quality.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 8c56edfa3a..5ccd00a02b 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -14,12 +14,12 @@ jobs: - name: Run code quality check suite run: ./tools/check/check_code_quality.sh - klint: + ktlint: name: Kotlin Linter runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Run klint + - name: Run ktlint run: | ./gradlew ktlintCheck --continue - name: Upload reports From 7e8ca29ca76e210d32bb0c3323738c23dc2b894c Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 10:09:03 +0100 Subject: [PATCH 045/144] creating dedicated discovery policy items and displaying within an expandable form item --- .../features/discovery/DiscoveryPolicyItem.kt | 51 +++++++++++++++++++ .../discovery/DiscoverySettingsController.kt | 35 ++++++------- .../discovery/DiscoverySettingsState.kt | 4 +- .../discovery/DiscoverySettingsViewModel.kt | 10 +++- .../main/res/layout/item_discovery_policy.xml | 51 +++++++++++++++++++ 5 files changed, 132 insertions(+), 19 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt create mode 100644 vector/src/main/res/layout/item_discovery_policy.xml diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt new file mode 100644 index 0000000000..c97a2286ae --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.discovery + +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.airbnb.epoxy.EpoxyModelWithHolder +import im.vector.app.R +import im.vector.app.core.epoxy.ClickListener +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.onClick + +@EpoxyModelClass(layout = R.layout.item_discovery_policy) +abstract class DiscoveryPolicyItem : EpoxyModelWithHolder() { + + @EpoxyAttribute + var name: String? = null + + @EpoxyAttribute + var url: String? = null + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var clickListener: ClickListener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.title.text = name + holder.url.text = url + holder.view.onClick(clickListener) + } + + class Holder : VectorEpoxyHolder() { + val title by bind(R.id.discovery_policy_name) + val url by bind(R.id.discovery_policy_url) + } +} diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 1d8989adc1..f2f9f8f0d9 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.discovery -import android.text.method.LinkMovementMethod import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail @@ -27,14 +26,13 @@ import im.vector.app.R import im.vector.app.core.epoxy.attributes.ButtonStyle import im.vector.app.core.epoxy.attributes.ButtonType import im.vector.app.core.epoxy.attributes.IconMode -import im.vector.app.core.epoxy.expandableTextItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.getFormattedValue import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.EvenBetterLinkMovementMethod -import org.matrix.android.sdk.api.extensions.appendNl +import im.vector.app.features.form.formAdvancedToggleItem +import im.vector.app.features.terms.termItem import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid @@ -124,21 +122,24 @@ class DiscoverySettingsController @Inject constructor( title(identityServerUrl) } - val policyUrls = identityServer?.policyUrls?.joinToString(separator = "\n") { it } - if (policyUrls != null) { - val title = stringProvider.getString(R.string.settings_discovery_identity_server_policies_title) - expandableTextItem { + val terms = identityServer?.terms + if (terms != null) { + formAdvancedToggleItem { id("policy-urls") - maxLines(1) - enableScrollBar(false) - content(buildString { - append(title) - appendNl(policyUrls) - }) - movementMethod(LinkMovementMethod()) + title(host.stringProvider.getString(R.string.settings_discovery_identity_server_policies_title)) expanded(data.isIdentityPolicyUrlsExpanded) - onExpandClicked { - host.listener?.onPolicyUrlsExpandedStateToggled() + listener { host.listener?.onPolicyUrlsExpandedStateToggled() } + } + if (data.isIdentityPolicyUrlsExpanded) { + terms.forEach { term -> + discoveryPolicyItem { + id(term.url) + name(term.name) + url(term.url) + clickListener { + // TODO + } + } } } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index 4dbb5c495f..a70b66941b 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -33,5 +33,7 @@ data class DiscoverySettingsState( data class IdentityServerWithTerms( val serverUrl: String, - val policyUrls: List + val terms: List ) + +data class IdentityServerTerms(val name: String, val url: String) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 4c4511b089..4240bdedad 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -421,7 +421,15 @@ class DiscoverySettingsViewModel @AssistedInject constructor( val terms = termsService.getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) .serverResponse .getLocalizedTerms(stringProvider.getString(R.string.resources_language)) - val policyUrls = terms.mapNotNull { it.localizedUrl } + val policyUrls = terms.mapNotNull { + val name = it.localizedName ?: it.policyName + val url = it.localizedUrl + if (name == null || url == null) { + null + } else { + IdentityServerTerms(name = name, url = url) + } + } IdentityServerWithTerms(identityServerUrl, policyUrls) } } diff --git a/vector/src/main/res/layout/item_discovery_policy.xml b/vector/src/main/res/layout/item_discovery_policy.xml new file mode 100644 index 0000000000..4630273904 --- /dev/null +++ b/vector/src/main/res/layout/item_discovery_policy.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + From 362ebcbe42cee1786184d739546e6161236875b2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 7 Oct 2021 11:11:44 +0200 Subject: [PATCH 046/144] Revert "Mavericks 2: remove matrix-sdk-android-flow as it will be easier when entirely migrating to flow" This reverts commit d9b02a20d84840a8549c48ee2dff8188c3ce5dcf. --- matrix-sdk-android-flow/.gitignore | 1 + matrix-sdk-android-flow/build.gradle | 49 +++++ matrix-sdk-android-flow/consumer-rules.pro | 0 matrix-sdk-android-flow/proguard-rules.pro | 21 ++ .../sdk/flow/ExampleInstrumentedTest.kt | 40 ++++ .../src/main/AndroidManifest.xml | 5 + .../org/matrix/android/sdk/flow/FlowRoom.kt | 83 +++++++ .../matrix/android/sdk/flow/FlowSession.kt | 138 ++++++++++++ .../matrix/android/sdk/flow}/OptionalFlow.kt | 2 +- .../android/sdk/flow/ExampleUnitTest.kt | 33 +++ settings.gradle | 1 + vector/build.gradle | 1 + .../app/core/platform/VectorViewModel.kt | 32 +++ .../quads/SharedSecureStorageViewModel.kt | 8 +- .../features/devtools/RoomDevToolViewModel.kt | 7 +- .../discovery/DiscoverySettingsViewModel.kt | 7 +- .../features/home/HomeActivityViewModel.kt | 9 +- .../app/features/home/HomeDetailViewModel.kt | 17 +- .../UnknownDeviceDetectorSharedViewModel.kt | 10 +- .../room/breadcrumbs/BreadcrumbsViewModel.kt | 8 +- .../home/room/detail/RoomDetailViewModel.kt | 26 ++- .../action/MessageActionsViewModel.kt | 12 +- .../home/room/list/RoomListViewModel.kt | 9 +- .../app/features/invite/InvitesAcceptor.kt | 7 +- .../login2/created/AccountCreatedViewModel.kt | 10 +- .../powerlevel/PowerLevelsFlowFactory.kt | 11 +- .../room/RequireActiveMembershipViewModel.kt | 8 +- .../roomdirectory/RoomDirectoryViewModel.kt | 10 +- .../roompreview/RoomPreviewViewModel.kt | 11 +- .../RoomMemberProfileViewModel.kt | 14 +- .../devices/DeviceListBottomSheetViewModel.kt | 10 +- .../roomprofile/RoomProfileViewModel.kt | 26 +-- .../roomprofile/alias/RoomAliasViewModel.kt | 15 +- .../banned/RoomBannedMemberListViewModel.kt | 12 +- .../members/RoomMemberListViewModel.kt | 20 +- .../RoomNotificationSettingsViewModel.kt | 14 +- .../permissions/RoomPermissionsViewModel.kt | 7 +- .../settings/RoomSettingsViewModel.kt | 25 ++- .../uploads/RoomUploadsViewModel.kt | 7 +- .../settings/SecretsSynchronisationInfo.kt | 11 +- .../settings/VectorSettingsGeneralFragment.kt | 12 +- .../CrossSigningSettingsViewModel.kt | 6 +- ...iceVerificationInfoBottomSheetViewModel.kt | 14 +- .../settings/devices/DevicesViewModel.kt | 18 +- .../settings/devtools/AccountDataViewModel.kt | 6 +- .../settings/ignored/IgnoredUsersViewModel.kt | 6 +- .../threepids/ThreePidsSettingsViewModel.kt | 10 +- .../features/share/IncomingShareViewModel.kt | 8 +- .../app/features/spaces/SpaceMenuViewModel.kt | 25 ++- .../features/spaces/SpacesListViewModel.kt | 28 +-- .../spaces/explore/SpaceDirectoryViewModel.kt | 11 +- .../leave/SpaceLeaveAdvancedViewModel.kt | 7 +- .../userdirectory/UserListViewModel.kt | 204 ++++++++++-------- .../app/features/widgets/WidgetViewModel.kt | 14 +- .../RoomWidgetPermissionViewModel.kt | 7 +- .../signout/ServerBackupStatusViewModel.kt | 18 +- .../workers/signout/SignoutCheckViewModel.kt | 6 +- 57 files changed, 767 insertions(+), 370 deletions(-) create mode 100644 matrix-sdk-android-flow/.gitignore create mode 100644 matrix-sdk-android-flow/build.gradle create mode 100644 matrix-sdk-android-flow/consumer-rules.pro create mode 100644 matrix-sdk-android-flow/proguard-rules.pro create mode 100644 matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt create mode 100644 matrix-sdk-android-flow/src/main/AndroidManifest.xml create mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt create mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt rename {vector/src/main/java/im/vector/app/core/utils => matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow}/OptionalFlow.kt (96%) create mode 100644 matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt diff --git a/matrix-sdk-android-flow/.gitignore b/matrix-sdk-android-flow/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/matrix-sdk-android-flow/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/matrix-sdk-android-flow/build.gradle b/matrix-sdk-android-flow/build.gradle new file mode 100644 index 0000000000..4aecec169b --- /dev/null +++ b/matrix-sdk-android-flow/build.gradle @@ -0,0 +1,49 @@ + +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 31 + + defaultConfig { + minSdk 21 + targetSdk 31 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation project(":matrix-sdk-android") + implementation libs.androidx.appCompat + + implementation libs.jetbrains.kotlinStdlibJdk7 + implementation libs.jetbrains.coroutinesCore + implementation libs.jetbrains.coroutinesAndroid + implementation libs.androidx.lifecycleLivedata + + // Paging + implementation libs.androidx.pagingRuntimeKtx + + // Logging + implementation libs.jakewharton.timber + +} \ No newline at end of file diff --git a/matrix-sdk-android-flow/consumer-rules.pro b/matrix-sdk-android-flow/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/matrix-sdk-android-flow/proguard-rules.pro b/matrix-sdk-android-flow/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/matrix-sdk-android-flow/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..41799955a4 --- /dev/null +++ b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.matrix.android.sdk.flow.test", appContext.packageName) + } +} diff --git a/matrix-sdk-android-flow/src/main/AndroidManifest.xml b/matrix-sdk-android-flow/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..2392c0bfcb --- /dev/null +++ b/matrix-sdk-android-flow/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt new file mode 100644 index 0000000000..a3e476ce08 --- /dev/null +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import androidx.lifecycle.asFlow +import kotlinx.coroutines.flow.Flow +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams +import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary +import org.matrix.android.sdk.api.session.room.model.ReadReceipt +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState +import org.matrix.android.sdk.api.session.room.send.UserDraft +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.util.Optional + +class FlowRoom(private val room: Room) { + + fun liveRoomSummary(): Flow> { + return room.getRoomSummaryLive().asFlow() + } + + fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow> { + return room.getRoomMembersLive(queryParams).asFlow() + } + + fun liveAnnotationSummary(eventId: String): Flow> { + return room.getEventAnnotationsSummaryLive(eventId).asFlow() + } + + fun liveTimelineEvent(eventId: String): Flow> { + return room.getTimeLineEventLive(eventId).asFlow() + } + + fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow> { + return room.getStateEventLive(eventType, stateKey).asFlow() + } + + fun liveStateEvents(eventTypes: Set): Flow> { + return room.getStateEventsLive(eventTypes).asFlow() + } + + fun liveReadMarker(): Flow> { + return room.getReadMarkerLive().asFlow() + } + + fun liveReadReceipt(): Flow> { + return room.getMyReadReceiptLive().asFlow() + } + + fun liveEventReadReceipts(eventId: String): Flow> { + return room.getEventReadReceiptsLive(eventId).asFlow() + } + + fun liveDraft(): Flow> { + return room.getDraftLive().asFlow() + } + + fun liveNotificationState(): Flow { + return room.getLiveRoomNotificationState().asFlow() + } +} + +fun Room.flow(): FlowRoom { + return FlowRoom(this) +} diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt new file mode 100644 index 0000000000..affcd4a65d --- /dev/null +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt @@ -0,0 +1,138 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import androidx.lifecycle.asFlow +import androidx.paging.PagedList +import kotlinx.coroutines.flow.Flow +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent +import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo +import org.matrix.android.sdk.api.session.group.GroupSummaryQueryParams +import org.matrix.android.sdk.api.session.group.model.GroupSummary +import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.api.session.pushers.Pusher +import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams +import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent +import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams +import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.session.widgets.model.Widget +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo +import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo + +class RxFlow(private val session: Session) { + + fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Flow> { + return session.getRoomSummariesLive(queryParams).asFlow() + } + + fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow> { + return session.getGroupSummariesLive(queryParams).asFlow() + } + + fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Flow> { + return session.spaceService().getSpaceSummariesLive(queryParams).asFlow() + } + + fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow> { + return session.getBreadcrumbsLive(queryParams).asFlow() + } + + fun liveMyDevicesInfo(): Flow> { + return session.cryptoService().getLiveMyDevicesInfo().asFlow() + } + + fun liveSyncState(): Flow { + return session.getSyncStateLive().asFlow() + } + + fun livePushers(): Flow> { + return session.getPushersLive().asFlow() + } + + fun liveUser(userId: String): Flow> { + return session.getUserLive(userId).asFlow() + } + + fun liveRoomMember(userId: String, roomId: String): Flow> { + return session.getRoomMemberLive(userId, roomId).asFlow() + } + + fun liveUsers(): Flow> { + return session.getUsersLive().asFlow() + } + + fun liveIgnoredUsers(): Flow> { + return session.getIgnoredUsersLive().asFlow() + } + + fun livePagedUsers(filter: String? = null, excludedUserIds: Set? = null): Flow> { + return session.getPagedUsersLive(filter, excludedUserIds).asFlow() + } + + fun liveThreePIds(refreshData: Boolean): Flow> { + return session.getThreePidsLive(refreshData).asFlow() + } + + fun livePendingThreePIds(): Flow> { + return session.getPendingThreePidsLive().asFlow() + } + + fun liveUserCryptoDevices(userId: String): Flow> { + return session.cryptoService().getLiveCryptoDeviceInfo(userId).asFlow() + } + + fun liveCrossSigningInfo(userId: String): Flow> { + return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asFlow() + } + + fun liveCrossSigningPrivateKeys(): Flow> { + return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() + } + + fun liveUserAccountData(types: Set): Flow> { + return session.accountDataService().getLiveUserAccountDataEvents(types).asFlow() + } + + fun liveRoomAccountData(types: Set): Flow> { + return session.accountDataService().getLiveRoomAccountDataEvents(types).asFlow() + } + + fun liveRoomWidgets( + roomId: String, + widgetId: QueryStringValue, + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): Flow> { + return session.widgetService().getRoomWidgetsLive(roomId, widgetId, widgetTypes, excludedTypes).asFlow() + } + + fun liveRoomChangeMembershipState(): Flow> { + return session.getChangeMembershipsLive().asFlow() + } +} + +fun Session.flow(): RxFlow { + return RxFlow(this) +} diff --git a/vector/src/main/java/im/vector/app/core/utils/OptionalFlow.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt similarity index 96% rename from vector/src/main/java/im/vector/app/core/utils/OptionalFlow.kt rename to matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt index 8e37ccfe8d..a9f062f379 100644 --- a/vector/src/main/java/im/vector/app/core/utils/OptionalFlow.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.core.utils +package org.matrix.android.sdk.flow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter diff --git a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt new file mode 100644 index 0000000000..bd627b2041 --- /dev/null +++ b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/settings.gradle b/settings.gradle index b88ea99b05..e3b84b4733 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,3 +5,4 @@ include ':diff-match-patch' include ':attachment-viewer' include ':multipicker' include ':library:ui-styles' +include ':matrix-sdk-android-flow' diff --git a/vector/build.gradle b/vector/build.gradle index a9c6a407d8..76bc71b2d4 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -323,6 +323,7 @@ dependencies { implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android-rx") + implementation project(":matrix-sdk-android-flow") implementation project(":diff-match-patch") implementation project(":multipicker") implementation project(":attachment-viewer") diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index 01700049c1..1a77a00fab 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -16,10 +16,16 @@ package im.vector.app.core.platform +import com.airbnb.mvrx.Async import com.airbnb.mvrx.BaseMvRxViewModel +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState +import com.airbnb.mvrx.Success import im.vector.app.core.utils.DataSource import im.vector.app.core.utils.PublishDataSource +import io.reactivex.Observable +import io.reactivex.Single abstract class VectorViewModel(initialState: S) : BaseMvRxViewModel(initialState) { @@ -32,5 +38,31 @@ abstract class VectorViewModel() val viewEvents: DataSource = _viewEvents + /** + * This method does the same thing as the execute function, but it doesn't subscribe to the stream + * so you can use this in a switchMap or a flatMap + */ + // False positive + @Suppress("USELESS_CAST", "NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER") + fun Single.toAsync(stateReducer: S.(Async) -> S): Single> { + setState { stateReducer(Loading()) } + return map { Success(it) as Async } + .onErrorReturn { Fail(it) } + .doOnSuccess { setState { stateReducer(it) } } + } + + /** + * This method does the same thing as the execute function, but it doesn't subscribe to the stream + * so you can use this in a switchMap or a flatMap + */ + // False positive + @Suppress("USELESS_CAST", "NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER") + fun Observable.toAsync(stateReducer: S.(Async) -> S): Observable> { + setState { stateReducer(Loading()) } + return map { Success(it) as Async } + .onErrorReturn { Fail(it) } + .doOnNext { setState { stateReducer(it) } } + } + abstract fun handle(action: VA) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 2a0ce1da2b..151b73ff32 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.crypto.quads -import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail @@ -44,7 +43,9 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.IntegrityResult import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding +import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.io.ByteArrayOutputStream @@ -115,9 +116,8 @@ class SharedSecureStorageViewModel @AssistedInject constructor( } } - session.cryptoService() - .getLiveCryptoDeviceInfo(session.myUserId) - .asFlow() + session.flow() + .liveUserCryptoDevices(session.myUserId) .distinctUntilChanged() .execute { copy( diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt index 0aed35fd55..4bab65fd5d 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.devtools -import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail @@ -41,7 +40,9 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.util.JsonDict +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.di.MoshiProvider +import org.matrix.android.sdk.rx.rx class RoomDevToolViewModel @AssistedInject constructor( @Assisted val initialState: RoomDevToolViewState, @@ -69,8 +70,8 @@ class RoomDevToolViewModel @AssistedInject constructor( init { session.getRoom(initialState.roomId) - ?.getStateEventsLive(emptySet()) - ?.asFlow() + ?.flow() + ?.liveStateEvents(emptySet()) ?.execute { async -> copy(stateEvents = async) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index ddbe2e151a..b248bcd065 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.discovery -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -37,6 +36,7 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.flow.flow class DiscoverySettingsViewModel @AssistedInject constructor( @Assisted initialState: DiscoverySettingsState, @@ -85,9 +85,8 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } private fun observeThreePids() { - session - .getThreePidsLive(true) - .asFlow() + session.flow() + .liveThreePIds(true) .onEach { retrieveBinding(it) } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 52ea898367..fa3df1ca97 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -47,9 +47,12 @@ import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.util.awaitCallback +import org.matrix.android.sdk.rx.asObservable +import org.matrix.android.sdk.rx.rx import timber.log.Timber import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -101,10 +104,8 @@ class HomeActivityViewModel @AssistedInject constructor( .crossSigningService().allPrivateKeysKnown() safeActiveSession - .cryptoService() - .crossSigningService() - .getLiveCrossSigningKeys(safeActiveSession.myUserId) - .asFlow() + .flow() + .liveCrossSigningInfo(safeActiveSession.myUserId) .onEach { val isVerified = it.getOrNull()?.isTrusted() ?: false if (!isVerified && onceTrusted) { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 85a0716a77..316c1791fe 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -48,6 +48,7 @@ import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.asObservable import timber.log.Timber import java.util.concurrent.TimeUnit @@ -95,13 +96,11 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho updateShowDialPadTab() observeDataStore() callManager.addProtocolsCheckerListener(this) - session.getUserLive(session.myUserId) - .asFlow() - .execute { - copy( - myMatrixItem = it.invoke()?.getOrNull()?.toMatrixItem() - ) - } + session.flow().liveUser(session.myUserId).execute { + copy( + myMatrixItem = it.invoke()?.getOrNull()?.toMatrixItem() + ) + } } private fun observeDataStore() { @@ -184,8 +183,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } private fun observeSyncState() { - session.getSyncStateLive() - .asFlow() + session.flow() + .liveSyncState() .setOnEach { syncState -> copy(syncState = syncState) } diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index ef22875eaa..143f843954 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -42,6 +41,7 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import timber.log.Timber @@ -99,9 +99,9 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted ) combine( - session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow(), - session.cryptoService().getLiveMyDevicesInfo().asFlow(), - session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() + session.flow().liveUserCryptoDevices(session.myUserId), + session.flow().liveMyDevicesInfo(), + session.flow().liveCrossSigningPrivateKeys() ) { cryptoList, infoList, pInfo -> // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") @@ -132,7 +132,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted ) } - session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow() + session.flow().liveUserCryptoDevices(session.myUserId) .distinctUntilChanged() .sample(5_000) .onEach { diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index 0e838db525..f32f132fe9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.breadcrumbs -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -26,10 +25,13 @@ import dagger.assisted.AssistedFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.rx.rx class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, private val session: Session) @@ -60,11 +62,11 @@ class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: B // PRIVATE METHODS ***************************************************************************** private fun observeBreadcrumbs() { - session.getBreadcrumbsLive(roomSummaryQueryParams { + session.flow() + .liveBreadcrumbs(roomSummaryQueryParams { displayName = QueryStringValue.NoCondition memberships = listOf(Membership.JOIN) }) - .asFlow() .execute { asyncBreadcrumbs -> copy(asyncBreadcrumbs = asyncBreadcrumbs) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index b1d18d7df4..ddb1c51b5b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -37,7 +37,6 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.unwrap import im.vector.app.features.attachments.toContentAttachmentData import im.vector.app.features.call.conference.ConferenceEvent import im.vector.app.features.call.conference.JitsiActiveConferenceHolder @@ -109,6 +108,8 @@ import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode import timber.log.Timber import java.util.concurrent.TimeUnit @@ -253,12 +254,11 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeActiveRoomWidgets() { - session.widgetService() - .getRoomWidgetsLive( + session.flow() + .liveRoomWidgets( roomId = initialState.roomId, widgetId = QueryStringValue.NoCondition ) - .asFlow() .map { widgets -> widgets.filter { it.isActive } } @@ -287,9 +287,8 @@ class RoomDetailViewModel @AssistedInject constructor( val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(session.myUserId, QueryStringValue.Case.SENSITIVE) } - room - .getRoomMembersLive(queryParams) - .asFlow() + room.flow() + .liveRoomMembers(queryParams) .map { it.firstOrNull().toOptional() } @@ -1506,8 +1505,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeSyncState() { - session.getSyncStateLive() - .asFlow() + session.flow() + .liveSyncState() .setOnEach { syncState -> copy(syncState = syncState) } @@ -1521,8 +1520,7 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeRoomSummary() { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy( @@ -1534,7 +1532,7 @@ class RoomDetailViewModel @AssistedInject constructor( private fun getUnreadState() { combine( timelineEvents, - room.getRoomSummaryLive().asFlow().unwrap() + room.flow().liveRoomSummary().unwrap() ) { timelineEvents, roomSummary -> computeUnreadState(timelineEvents, roomSummary) } @@ -1581,8 +1579,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.getChangeMembershipsLive() - .asFlow() + session.flow() + .liveRoomChangeMembershipState() .map { it[initialState.roomId] ?: ChangeMembershipState.Unknown } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index b04fa98ab1..f63366482b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.home.room.detail.timeline.action -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -29,7 +28,6 @@ import im.vector.app.core.extensions.canReact import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.unwrap import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillsPostProcessor @@ -60,6 +58,8 @@ import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap /** * Information related to an event and used to display preview in contextual bottom sheet. @@ -137,8 +137,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun observeEvent() { if (room == null) return - room.getTimeLineEventLive(initialState.eventId) - .asFlow() + room.flow() + .liveTimelineEvent(initialState.eventId) .unwrap() .execute { copy(timelineEvent = it) @@ -149,8 +149,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted if (room == null) return eventIdFlow .flatMapLatest { eventId -> - room.getEventAnnotationsSummaryLive(eventId) - .asFlow() + room.flow() + .liveAnnotationSummary(eventId) .map { annotations -> EmojiDataSource.quickEmojis.map { emoji -> ToggleState(emoji, annotations.getOrNull()?.reactionsSummary?.firstOrNull { it.key == emoji }?.addedByMe ?: false) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 59e73462bc..b54d2b1b4f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.home.room.list import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -43,6 +42,7 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.state.isPublic +import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject @@ -95,8 +95,7 @@ class RoomListViewModel @Inject constructor( ) } - session.getUserLive(session.myUserId) - .asFlow() + session.flow().liveUser(session.myUserId) .map { it.getOrNull()?.getBestName() } .distinctUntilChanged() .execute { @@ -107,8 +106,8 @@ class RoomListViewModel @Inject constructor( } private fun observeMembershipChanges() { - session.getChangeMembershipsLive() - .asFlow() + session.flow() + .liveRoomChangeMembershipState() .setOnEach { copy(roomMembershipChanges = it) } diff --git a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt index b22390c4d5..09eff756d5 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt @@ -16,7 +16,6 @@ package im.vector.app.features.invite -import androidx.lifecycle.asFlow import im.vector.app.ActiveSessionDataSource import im.vector.app.features.session.coroutineScope import io.reactivex.disposables.Disposable @@ -36,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -82,9 +82,10 @@ class InvitesAcceptor @Inject constructor( val roomQueryParams = roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } + val flowSession = session.flow() combine( - session.getRoomSummariesLive(roomQueryParams).asFlow(), - session.getChangeMembershipsLive().asFlow().debounce(1000) + flowSession.liveRoomSummaries(roomQueryParams), + flowSession.liveRoomChangeMembershipState().debounce(1000) ) { invitedRooms, _ -> invitedRooms.map { it.roomId } } .filter { it.isNotEmpty() } .onEach { invitedRoomIds -> diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt index 01ee3a7a23..c95434a548 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.login2.created -import androidx.lifecycle.asFlow +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -24,13 +24,13 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.unwrap -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.rx.unwrap import timber.log.Timber class AccountCreatedViewModel @AssistedInject constructor( @@ -62,8 +62,8 @@ class AccountCreatedViewModel @AssistedInject constructor( } private fun observeUser() { - session.getUserLive(session.myUserId) - .asFlow() + session.rx() + .liveUser(session.myUserId) .unwrap() .map { if (MatrixPatterns.isUserId(it.userId)) { diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt index e1992ec572..767d6f1ba7 100644 --- a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt +++ b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt @@ -16,9 +16,6 @@ package im.vector.app.features.powerlevel -import androidx.lifecycle.asFlow -import im.vector.app.core.utils.mapOptional -import im.vector.app.core.utils.unwrap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn @@ -27,13 +24,15 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap class PowerLevelsFlowFactory(private val room: Room) { fun createFlow(): Flow { - return room - .getStateEventLive(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) - .asFlow() + return room.flow() + .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .flowOn(Dispatchers.Default) .mapOptional { it.content.toModel() } .unwrap() diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index a24d2df72c..62519336f5 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.room -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory @@ -28,7 +27,6 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.unwrap import io.reactivex.Observable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -49,6 +47,8 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap /** * This ViewModel observe a room summary and notify when the room is left @@ -90,8 +90,8 @@ class RequireActiveMembershipViewModel @AssistedInject constructor( val emptyResult = Optional.empty() emit(emptyResult) } - room.getRoomSummaryLive() - .asFlow() + room.flow() + .liveRoomSummary() .unwrap() .flowOn(Dispatchers.Default) .map { mapToLeftViewEvent(room, it) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt index ff5e3ac3f5..a2089e6cd5 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomdirectory -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading @@ -39,6 +38,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFilter import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow import timber.log.Timber class RoomDirectoryViewModel @AssistedInject constructor( @@ -80,8 +80,8 @@ class RoomDirectoryViewModel @AssistedInject constructor( memberships = listOf(Membership.JOIN) } session - .getRoomSummariesLive(queryParams) - .asFlow() + .flow() + .liveRoomSummaries(queryParams) .map { roomSummaries -> roomSummaries .map { it.roomId } @@ -93,8 +93,8 @@ class RoomDirectoryViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.getChangeMembershipsLive() - .asFlow() + session.flow() + .liveRoomChangeMembershipState() .setOnEach { copy(changeMembershipStates = it) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 5fa7f34aca..2635307e95 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomdirectory.roompreview -import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -43,6 +42,8 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.rx.rx import timber.log.Timber class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val initialState: RoomPreviewViewState, @@ -167,8 +168,8 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini excludeType = null } session - .getRoomSummariesLive(queryParams) - .asFlow() + .flow() + .liveRoomSummaries(queryParams) .onEach { list -> val isRoomJoined = list.any { it.membership == Membership.JOIN @@ -186,8 +187,8 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini } private fun observeMembershipChanges() { - session.getChangeMembershipsLive() - .asFlow() + session.flow() + .liveRoomChangeMembershipState() .onEach { val changeMembership = it[initialState.roomId] ?: ChangeMembershipState.Unknown val joinState = when (changeMembership) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index b424d62b1c..14624b800e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.roommemberprofile -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -32,7 +31,6 @@ import im.vector.app.R import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine @@ -55,6 +53,8 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, private val stringProvider: StringProvider, @@ -107,7 +107,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } } - session.cryptoService().getLiveCryptoDeviceInfo(initialState.userId).asFlow() + session.flow().liveUserCryptoDevices(initialState.userId) .map { Pair( it.fold(true, { prev, dev -> prev && dev.isVerified }), @@ -121,14 +121,14 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v ) } - session.cryptoService().crossSigningService().getLiveCrossSigningKeys(initialState.userId).asFlow() + session.flow().liveCrossSigningInfo(initialState.userId) .execute { copy(userMXCrossSigningInfo = it.invoke()?.getOrNull()) } } private fun observeIgnoredState() { - session.getIgnoredUsersLive().asFlow() + session.flow().liveIgnoredUsers() .map { ignored -> ignored.find { it.userId == initialState.userId @@ -245,7 +245,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE) } - room.getRoomMembersLive(queryParams).asFlow() + room.flow().liveRoomMembers(queryParams) .map { it.firstOrNull().toOptional() } .unwrap() .execute { @@ -285,7 +285,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } private fun observeRoomSummaryAndPowerLevels(room: Room) { - val roomSummaryLive = room.getRoomSummaryLive().asFlow().unwrap() + val roomSummaryLive = room.flow().liveRoomSummary().unwrap() val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index 1de96a9ef4..b638d84181 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -16,7 +16,6 @@ */ package im.vector.app.features.roommemberprofile.devices -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -34,7 +33,9 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.rx.rx data class DeviceListViewState( val userItem: MatrixItem? = null, @@ -55,17 +56,14 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } init { - session.cryptoService().getLiveCryptoDeviceInfo(args.userId) - .asFlow() + session.flow().liveUserCryptoDevices(args.userId) .execute { copy(cryptoDevices = it).also { refreshSelectedId() } } - session.cryptoService().crossSigningService() - .getLiveCrossSigningKeys(args.userId) - .asFlow() + session.flow().liveCrossSigningInfo(args.userId) .execute { copy(memberCrossSigningKey = it.invoke()?.getOrNull()) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index d08d78e64b..c4fc2bc7bb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.roomprofile -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -28,8 +27,6 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.mapOptional -import im.vector.app.core.utils.unwrap import im.vector.app.features.home.ShortcutCreator import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers @@ -43,6 +40,10 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.state.isPublic +import org.matrix.android.sdk.flow.FlowRoom +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap class RoomProfileViewModel @AssistedInject constructor( @Assisted private val initialState: RoomProfileViewState, @@ -68,14 +69,15 @@ class RoomProfileViewModel @AssistedInject constructor( private val room = session.getRoom(initialState.roomId)!! init { - observeRoomSummary() - observeRoomCreateContent() - observeBannedRoomMembers() + val flowRoom = room.flow() + observeRoomSummary(flowRoom) + observeRoomCreateContent(flowRoom) + observeBannedRoomMembers(flowRoom) observePermissions() } - private fun observeRoomCreateContent() { - room.getStateEventLive(EventType.STATE_ROOM_CREATE, QueryStringValue.NoCondition).asFlow() + private fun observeRoomCreateContent(flowRoom: FlowRoom) { + flowRoom.liveStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .execute { async -> @@ -90,16 +92,16 @@ class RoomProfileViewModel @AssistedInject constructor( } } - private fun observeRoomSummary() { - room.getRoomSummaryLive().asFlow() + private fun observeRoomSummary(flowRoom: FlowRoom) { + flowRoom.liveRoomSummary() .unwrap() .execute { copy(roomSummary = it) } } - private fun observeBannedRoomMembers() { - room.getRoomMembersLive(roomMemberQueryParams { memberships = listOf(Membership.BAN) }).asFlow() + private fun observeBannedRoomMembers(flowRoom: FlowRoom) { + flowRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) .execute { copy(bannedMembership = it) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 086f279655..28a1804fab 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.alias -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading @@ -29,8 +28,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.mapOptional -import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -42,6 +39,11 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap +import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.rx.unwrap class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: RoomAliasViewState, private val session: Session) @@ -129,8 +131,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun observeRoomSummary() { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy( @@ -172,8 +173,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ private fun observeRoomCanonicalAlias() { - room.getStateEventLive(EventType.STATE_ROOM_CANONICAL_ALIAS, QueryStringValue.NoCondition) - .asFlow() + room.flow() + .liveStateEvent(EventType.STATE_ROOM_CANONICAL_ALIAS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .setOnEach { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 4e244b747b..813d50c6bb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.banned -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -27,7 +26,6 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -40,6 +38,10 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap +import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.rx.unwrap class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, private val stringProvider: StringProvider, @@ -55,15 +57,13 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initia init { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) } - room.getRoomMembersLive(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) - .asFlow() + room.flow().liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) .execute { copy( bannedMemberSummaries = it diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index c5ed085bff..2873b20400 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -27,8 +27,6 @@ import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.mapOptional -import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.Dispatchers @@ -53,6 +51,9 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, @@ -93,9 +94,9 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } combine( - room.getRoomMembersLive(roomMemberQueryParams).asFlow(), - room.getStateEventLive(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) - .asFlow() + room.flow().liveRoomMembers(roomMemberQueryParams), + room.flow() + .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() ) @@ -108,8 +109,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } if (room.isEncrypted()) { - room.getRoomMembersLive(roomMemberQueryParams) - .asFlow() + room.flow().liveRoomMembers(roomMemberQueryParams) .flowOn(Dispatchers.Main) .flatMapLatest { membersSummary -> session.cryptoService().getLiveCryptoDeviceInfo(membersSummary.map { it.userId }) @@ -153,8 +153,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } private fun observeRoomSummary() { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) @@ -162,8 +161,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } private fun observeThirdPartyInvites() { - room.getStateEventsLive(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE)) - .asFlow() + room.flow().liveStateEvents(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE)) .execute { async -> copy(threePidInvites = async) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index 498a70a1cb..d944b77f7d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -16,7 +16,7 @@ package im.vector.app.features.roomprofile.notifications -import androidx.lifecycle.asFlow +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success @@ -25,10 +25,13 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.unwrap import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap +import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.rx.unwrap class RoomNotificationSettingsViewModel @AssistedInject constructor( @Assisted initialState: RoomNotificationSettingsViewState, @@ -63,8 +66,7 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( } private fun observeSummary() { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) @@ -72,8 +74,8 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( } private fun observeNotificationState() { - room.getLiveRoomNotificationState() - .asFlow() + room.rx() + .liveNotificationState() .execute { copy(notificationState = it) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index e3dd76f44c..71e8a313b5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.permissions -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success @@ -26,7 +25,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -36,6 +34,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialState: RoomPermissionsViewState, private val session: Session) @@ -63,8 +63,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat } private fun observeRoomSummary() { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index f9d64dfb56..7b28ced130 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.roomprofile.settings import androidx.core.net.toFile -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory @@ -27,8 +26,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.mapOptional -import im.vector.app.core.utils.unwrap import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.flow.launchIn @@ -46,6 +43,9 @@ import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState, private val vectorPreferences: VectorPreferences, @@ -125,8 +125,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeRoomSummary() { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> val roomSummary = async.invoke() @@ -162,8 +161,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeRoomHistoryVisibility() { - room.getStateEventLive(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.NoCondition) - .asFlow() + room.flow() + .liveStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .mapNotNull { it.historyVisibility } @@ -173,8 +172,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeJoinRule() { - room.getStateEventLive(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition) - .asFlow() + room.flow() + .liveStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .mapNotNull { it.joinRules } @@ -184,8 +183,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeGuestAccess() { - room.getStateEventLive(EventType.STATE_ROOM_GUEST_ACCESS, QueryStringValue.NoCondition) - .asFlow() + room.flow() + .liveStateEvent(EventType.STATE_ROOM_GUEST_ACCESS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .mapNotNull { it.guestAccess } @@ -198,8 +197,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ private fun observeRoomAvatar() { - room.getStateEventLive(EventType.STATE_ROOM_AVATAR, QueryStringValue.NoCondition) - .asFlow() + room.flow() + .liveStateEvent(EventType.STATE_ROOM_AVATAR, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .setOnEach { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index c9d9b6b23c..4526024143 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.roomprofile.uploads -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -29,10 +28,11 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.unwrap import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.message.MessageType +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap class RoomUploadsViewModel @AssistedInject constructor( @Assisted initialState: RoomUploadsViewState, @@ -65,8 +65,7 @@ class RoomUploadsViewModel @AssistedInject constructor( } private fun observeRoomSummary() { - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .execute { async -> copy(roomSummary = async) diff --git a/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt b/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt index bb0330e6f6..5afcb77587 100644 --- a/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt +++ b/vector/src/main/java/im/vector/app/features/settings/SecretsSynchronisationInfo.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings -import androidx.lifecycle.asFlow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -26,6 +25,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.SecretsSynchronisationInfo data class SecretsSynchronisationInfo( @@ -39,12 +39,11 @@ data class SecretsSynchronisationInfo( ) fun Session.liveSecretSynchronisationInfo(): Flow { + val sessionFlow = flow() return combine( - accountDataService() - .getLiveUserAccountDataEvents(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME)) - .asFlow(), - cryptoService().crossSigningService().getLiveCrossSigningKeys(myUserId).asFlow(), - cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() + sessionFlow.liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME)), + sessionFlow.liveCrossSigningInfo(myUserId), + sessionFlow.liveCrossSigningPrivateKeys() ) { _, crossSigningInfo, pInfo -> // first check if 4S is already setup val is4SSetup = sharedSecretStorageService.isRecoverySetup() diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index f539f46b3a..8eb9e2cdf3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -26,7 +26,6 @@ import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible -import androidx.lifecycle.asFlow import androidx.lifecycle.lifecycleScope import androidx.preference.EditTextPreference import androidx.preference.Preference @@ -48,7 +47,6 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.TextUtils import im.vector.app.core.utils.getSizeOfFiles import im.vector.app.core.utils.toast -import im.vector.app.core.utils.unwrap import im.vector.app.databinding.DialogChangePasswordBinding import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs @@ -64,6 +62,8 @@ import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.failure.isInvalidPassword import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import java.io.File import java.util.UUID import javax.inject.Inject @@ -122,8 +122,8 @@ class VectorSettingsGeneralFragment @Inject constructor( } private fun observeUserAvatar() { - session.getUserLive(session.myUserId) - .asFlow() + session.flow() + .liveUser(session.myUserId) .unwrap() .distinctUntilChangedBy { user -> user.avatarUrl } .onEach { @@ -133,8 +133,8 @@ class VectorSettingsGeneralFragment @Inject constructor( } private fun observeUserDisplayName() { - session.getUserLive(session.myUserId) - .asFlow() + session.flow() + .liveUser(session.myUserId) .unwrap() .map { it.displayName ?: "" } .distinctUntilChanged() diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index 47010f180a..a8fafb096a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.settings.crosssigning -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -38,6 +37,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth @@ -56,8 +56,8 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( init { combine( - session.cryptoService().getLiveMyDevicesInfo().asFlow(), - session.cryptoService().crossSigningService().getLiveCrossSigningKeys(session.myUserId).asFlow() + session.flow().liveMyDevicesInfo(), + session.flow().liveCrossSigningInfo(session.myUserId) ) { myDevicesInfo, mxCrossSigningInfo -> myDevicesInfo to mxCrossSigningInfo diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index 8c0c077472..38342efc46 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.settings.devices -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory @@ -28,7 +27,9 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.flow.map import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo +import org.matrix.android.sdk.rx.rx class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState, @Assisted val deviceId: String, @@ -49,8 +50,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As isRecoverySetup = session.sharedSecretStorageService.isRecoverySetup() ) } - session.cryptoService().crossSigningService().getLiveCrossSigningKeys(session.myUserId) - .asFlow() + session.flow().liveCrossSigningInfo(session.myUserId) .execute { copy( hasAccountCrossSigning = it.invoke()?.getOrNull() != null, @@ -58,8 +58,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As ) } - session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId) - .asFlow() + session.flow().liveUserCryptoDevices(session.myUserId) .map { list -> list.firstOrNull { it.deviceId == deviceId } } @@ -70,7 +69,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As ) } - session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow() + session.flow().liveUserCryptoDevices(session.myUserId) .map { it.size } .execute { copy( @@ -82,8 +81,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As copy(deviceInfo = Loading()) } - session.cryptoService().getLiveMyDevicesInfo() - .asFlow() + session.flow().liveMyDevicesInfo() .map { devices -> devices.firstOrNull { it.deviceId == deviceId } ?: DeviceInfo(deviceId = deviceId) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index d58ad4a99a..b2b4c0c396 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.devices -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -57,6 +56,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo @@ -123,14 +123,8 @@ class DevicesViewModel @AssistedInject constructor( } combine( - session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId).asFlow() - .onEach { - Timber.v("getLiveCryptoDeviceInfo") - }, - session.cryptoService().getLiveMyDevicesInfo().asFlow() - .onEach { - Timber.v("getLiveMyDevicesInfo") - } + session.flow().liveUserCryptoDevices(session.myUserId), + session.flow().liveMyDevicesInfo() ) { cryptoList, infoList -> infoList @@ -147,8 +141,7 @@ class DevicesViewModel @AssistedInject constructor( ) } - session.cryptoService().crossSigningService().getLiveCrossSigningKeys(session.myUserId) - .asFlow() + session.flow().liveCrossSigningInfo(session.myUserId) .execute { copy( hasAccountCrossSigning = it.invoke()?.getOrNull() != null, @@ -164,8 +157,7 @@ class DevicesViewModel @AssistedInject constructor( // ) // } - session.cryptoService().getLiveCryptoDeviceInfo(session.myUserId) - .asFlow() + session.flow().liveUserCryptoDevices(session.myUserId) .map { it.size } .distinctUntilChanged() .sample(5_000) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index 69ee9165a7..e5739ec446 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.devtools -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState @@ -32,6 +31,7 @@ import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent +import org.matrix.android.sdk.flow.flow data class AccountDataViewState( val accountData: Async> = Uninitialized @@ -42,9 +42,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A : VectorViewModel(initialState) { init { - session.accountDataService() - .getLiveUserAccountDataEvents(emptySet()) - .asFlow() + session.flow().liveUserAccountData(emptySet()) .execute { copy(accountData = it) } diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index 702a31f22c..7b7b5d0570 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.ignored -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -34,6 +33,7 @@ import im.vector.app.core.platform.VectorViewModelAction import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.flow.flow data class IgnoredUsersViewState( val ignoredUsers: List = emptyList(), @@ -67,8 +67,8 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: } private fun observeIgnoredUsers() { - session.getIgnoredUsersLive() - .asFlow() + session.flow() + .liveIgnoredUsers() .execute { async -> copy( ignoredUsers = async.invoke().orEmpty() diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index 0d00c72062..cd0d74a288 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.threepids -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -39,6 +38,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth import timber.log.Timber @@ -101,8 +101,8 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } private fun observeThreePids() { - session.getThreePidsLive(true) - .asFlow() + session.flow() + .liveThreePIds(true) .execute { copy( threePids = it @@ -111,8 +111,8 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } private fun observePendingThreePids() { - session.getPendingThreePidsLive() - .asFlow() + session.flow() + .livePendingThreePIds() .execute { copy( pendingThreePids = it, diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index 39dc3f2344..44e5ca39f9 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.share -import androidx.lifecycle.asFlow import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -38,6 +37,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow class IncomingShareViewModel @AssistedInject constructor( @Assisted initialState: IncomingShareViewState, @@ -69,8 +69,8 @@ class IncomingShareViewModel @AssistedInject constructor( val queryParams = roomSummaryQueryParams { memberships = listOf(Membership.JOIN) } - session.getRoomSummariesLive(queryParams) - .asFlow() + session + .flow().liveRoomSummaries(queryParams) .execute { copy(roomSummaries = it) } @@ -86,7 +86,7 @@ class IncomingShareViewModel @AssistedInject constructor( displayName = displayNameQuery memberships = listOf(Membership.JOIN) } - session.getRoomSummariesLive(filterQueryParams).asFlow() + session.flow().liveRoomSummaries(filterQueryParams) } .sample(300) .map { it.sortedWith(breadcrumbsRoomComparator) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index a75f773a4c..bb30670da9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.spaces -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -43,6 +42,8 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.rx.rx import timber.log.Timber class SpaceMenuViewModel @AssistedInject constructor( @@ -77,19 +78,17 @@ class SpaceMenuViewModel @AssistedInject constructor( session.getRoom(initialState.spaceId)?.let { room -> - room.getRoomSummaryLive() - .asFlow() - .onEach { - it.getOrNull()?.let { - if (it.membership == Membership.LEAVE) { - setState { copy(leavingState = Success(Unit)) } - if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { - // switch to home? - appStateHandler.setCurrentSpace(null, session) - } - } + room.flow().liveRoomSummary().onEach { + it.getOrNull()?.let { + if (it.membership == Membership.LEAVE) { + setState { copy(leavingState = Success(Unit)) } + if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { + // switch to home? + appStateHandler.setCurrentSpace(null, session) } - }.launchIn(viewModelScope) + } + } + }.launchIn(viewModelScope) PowerLevelsFlowFactory(room) .createFlow() diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index e902240a2e..46293da209 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.session.space.SpaceOrderUtils import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.rx.asObservable import java.util.concurrent.TimeUnit @@ -81,13 +82,14 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp init { - session.getUserLive(session.myUserId) - .asFlow() - .setOnEach { - copy( - myMxItem = it.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() - ) - } + session.getUserLive(session.myUserId).asObservable() + .subscribe { + setState { + copy( + myMxItem = it?.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() + ) + } + }.disposeOnClear() observeSpaceSummaries() // observeSelectionState() @@ -282,13 +284,16 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp null) } + val flowSession = session.flow() + combine( - session.getUserLive(session.myUserId) - .asFlow() + flowSession + .liveUser(session.myUserId) .map { it.getOrNull() }, - session.spaceService().getSpaceSummariesLive(spaceSummaryQueryParams).asFlow(), + flowSession + .liveSpaceSummaries(spaceSummaryQueryParams), session .accountDataService() .getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)) @@ -314,8 +319,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp // clear local echos on update session.accountDataService() .getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)) - .asFlow() - .execute { + .asObservable().execute { copy( spaceOrderLocalEchos = emptyMap() ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index 127ef5d6bf..5e2537f587 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.spaces.explore -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -44,6 +43,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow import timber.log.Timber class SpaceDirectoryViewModel @AssistedInject constructor( @@ -147,8 +147,9 @@ class SpaceDirectoryViewModel @AssistedInject constructor( memberships = listOf(Membership.JOIN) excludeType = null } - session.getRoomSummariesLive(queryParams) - .asFlow() + session + .flow() + .liveRoomSummaries(queryParams) .map { it.map { it.roomId }.toSet() } @@ -158,8 +159,8 @@ class SpaceDirectoryViewModel @AssistedInject constructor( } private fun observeMembershipChanges() { - session.getChangeMembershipsLive() - .asFlow() + session.flow() + .liveRoomChangeMembershipState() .setOnEach { copy(changeMembershipStates = it) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index 5f4731e9f4..3d24cf6225 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.spaces.leave -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -31,7 +30,6 @@ import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.unwrap import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -41,6 +39,8 @@ import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class SpaceLeaveAdvancedViewModel @AssistedInject constructor( @@ -97,8 +97,7 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( val spaceSummary = session.getRoomSummary(initialState.spaceId) setState { copy(spaceSummary = spaceSummary) } session.getRoom(initialState.spaceId)?.let { room -> - room.getRoomSummaryLive() - .asFlow() + room.flow().liveRoomSummary() .unwrap() .onEach { if (it.membership == Membership.LEAVE) { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index cae8540f8b..69b98200c1 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -16,12 +16,12 @@ package im.vector.app.features.userdirectory -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext +import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -29,23 +29,21 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.sample +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers import org.matrix.android.sdk.api.MatrixPatterns -import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.rx.rx +import java.util.concurrent.TimeUnit + +private typealias KnownUsersSearch = String +private typealias DirectoryUsersSearch = String data class ThreePidUser( val email: String, @@ -56,9 +54,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private val session: Session) : VectorViewModel(initialState) { - private val knownUsersSearch = MutableStateFlow("") - private val directoryUsersSearch = MutableStateFlow("") - private val identityServerUsersSearch = MutableStateFlow("") + private val knownUsersSearch = BehaviorRelay.create() + private val directoryUsersSearch = BehaviorRelay.create() + private val identityServerUsersSearch = BehaviorRelay.create() @AssistedFactory interface Factory { @@ -79,10 +77,11 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private val identityServerListener = object : IdentityServiceListener { override fun onIdentityServerChange() { withState { - identityServerUsersSearch.tryEmit(it.searchTerm) - val identityServerURL = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) + identityServerUsersSearch.accept(it.searchTerm) setState { - copy(configuredIdentityServer = identityServerURL) + copy( + configuredIdentityServer = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) + ) } } } @@ -121,7 +120,7 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private fun handleISUpdateConsent(action: UserListAction.UpdateUserConsent) { session.identityService().setUserConsent(action.consent) withState { - identityServerUsersSearch.tryEmit(it.searchTerm) + identityServerUsersSearch.accept(it.searchTerm) } } @@ -140,9 +139,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User ) } } - identityServerUsersSearch.tryEmit(searchTerm) - knownUsersSearch.tryEmit(searchTerm) - directoryUsersSearch.tryEmit(searchTerm) + identityServerUsersSearch.accept(searchTerm) + knownUsersSearch.accept(searchTerm) + directoryUsersSearch.accept(searchTerm) } private fun handleShareMyMatrixToLink() { @@ -152,9 +151,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User } private fun handleClearSearchUsers() { - knownUsersSearch.tryEmit("") - directoryUsersSearch.tryEmit("") - identityServerUsersSearch.tryEmit("") + knownUsersSearch.accept("") + directoryUsersSearch.accept("") + identityServerUsersSearch.accept("") setState { copy(searchTerm = "") } @@ -163,82 +162,103 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private fun observeUsers() = withState { state -> identityServerUsersSearch .filter { it.isEmail() } - .sample(300) - .onEach { search -> - executeSearchEmail(search) - }.launchIn(viewModelScope) + .throttleLast(300, TimeUnit.MILLISECONDS) + .switchMapSingle { search -> + val flowSession = session.rx() + val stream = + flowSession.lookupThreePid(ThreePid.Email(search)).flatMap { + it.getOrNull()?.let { foundThreePid -> + flowSession.getProfileInfo(foundThreePid.matrixId) + .map { json -> + ThreePidUser( + email = search, + user = User( + userId = foundThreePid.matrixId, + displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, + avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String + ) + ) + } + .onErrorResumeNext { + Single.just(ThreePidUser(email = search, user = User(foundThreePid.matrixId))) + } + } ?: Single.just(ThreePidUser(email = search, user = null)) + } + stream.toAsync { + copy(matchingEmail = it) + } + } + .subscribe() + .disposeOnClear() knownUsersSearch - .sample(300) - .flowOn(Dispatchers.Main) - .flatMapLatest { search -> - session.getPagedUsersLive(search, state.excludedUserIds).asFlow() - }.execute { - copy(knownUsers = it) + .throttleLast(300, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .switchMap { + session.rx().livePagedUsers(it, state.excludedUserIds) + } + .execute { async -> + copy(knownUsers = async) } directoryUsersSearch - .debounce(300) - .onEach { search -> - executeSearchDirectory(state, search) - }.launchIn(viewModelScope) - } + .debounce(300, TimeUnit.MILLISECONDS) + .switchMapSingle { search -> + val stream = if (search.isBlank()) { + Single.just(emptyList()) + } else { + val searchObservable = session.rx() + .searchUsersDirectory(search, 50, state.excludedUserIds.orEmpty()) + .map { users -> + users.sortedBy { it.toMatrixItem().firstLetterOfDisplayName() } + } + // If it's a valid user id try to use Profile API + // because directory only returns users that are in public rooms or share a room with you, where as + // profile will work other federations + if (!MatrixPatterns.isUserId(search)) { + searchObservable + } else { + val profileObservable = session.rx().getProfileInfo(search) + .map { json -> + User( + userId = search, + displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, + avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String + ).toOptional() + } + .onErrorResumeNext { + // Profile API can be restricted and doesn't have to return result. + // In this case allow inviting valid user ids. + Single.just( + User( + userId = search, + displayName = null, + avatarUrl = null + ).toOptional() + ) + } - private suspend fun executeSearchEmail(search: String) { - suspend { - val params = listOf(ThreePid.Email(search)) - val foundThreePid = tryOrNull { - session.identityService().lookUp(params).firstOrNull() - } - if (foundThreePid == null) { - null - } else { - try { - val json = session.getProfile(foundThreePid.matrixId) - ThreePidUser( - email = search, - user = User( - userId = foundThreePid.matrixId, - displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, - avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String + Single.zip( + searchObservable, + profileObservable, + { searchResults, optionalProfile -> + val profile = optionalProfile.getOrNull() ?: return@zip searchResults + val searchContainsProfile = searchResults.any { it.userId == profile.userId } + if (searchContainsProfile) { + searchResults + } else { + listOf(profile) + searchResults + } + } ) - ) - } catch (failure: Throwable) { - ThreePidUser(email = search, user = User(foundThreePid.matrixId)) + } + } + stream.toAsync { + copy(directoryUsers = it) + } } - } - }.execute { - copy(matchingEmail = it) - } - } - - private suspend fun executeSearchDirectory(state: UserListViewState, search: String) { - suspend { - if (search.isBlank()) { - emptyList() - } else { - val searchResult = session - .searchUsersDirectory(search, 50, state.excludedUserIds.orEmpty()) - .sortedBy { it.toMatrixItem().firstLetterOfDisplayName() } - val userProfile = if (MatrixPatterns.isUserId(search)) { - val json = tryOrNull { session.getProfile(search) } - User( - userId = search, - displayName = json?.get(ProfileService.DISPLAY_NAME_KEY) as? String, - avatarUrl = json?.get(ProfileService.AVATAR_URL_KEY) as? String - ) - } else { - null - } - if (userProfile == null || searchResult.any { it.userId == userProfile.userId }) { - searchResult - } else { - listOf(userProfile) + searchResult - } - } - }.execute { - copy(directoryUsers = it) - } + .subscribe() + .disposeOnClear() } private fun handleSelectUser(action: UserListAction.AddPendingSelection) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index 05f6157d11..c88750e6e1 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.widgets import android.net.Uri -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -30,8 +29,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.core.utils.mapOptional -import im.vector.app.core.utils.unwrap import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -45,6 +42,9 @@ import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerS import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.mapOptional +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber import javax.net.ssl.HttpsURLConnection @@ -119,8 +119,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi if (room == null) { return } - room.getStateEventLive(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) - .asFlow() + room.flow().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() .map { @@ -136,9 +135,8 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi return } val widgetId = initialState.widgetId ?: return - session.widgetService() - .getRoomWidgetsLive(initialState.roomId, QueryStringValue.Equals(widgetId)) - .asFlow() + session.flow() + .liveRoomWidgets(initialState.roomId, QueryStringValue.Equals(widgetId)) .filter { it.isNotEmpty() } .map { it.first() } .execute { diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index ee1ffd62c6..bbfeea6a76 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.widgets.permissions -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory @@ -33,6 +32,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.widgets.model.WidgetType +import org.matrix.android.sdk.flow.flow import timber.log.Timber import java.net.URL @@ -49,9 +49,8 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in private fun observeWidget() { val widgetId = initialState.widgetId ?: return - session.widgetService() - .getRoomWidgetsLive(initialState.roomId, QueryStringValue.Equals(widgetId)) - .asFlow() + session.flow() + .liveRoomWidgets(initialState.roomId, QueryStringValue.Equals(widgetId)) .filter { it.isNotEmpty() } .map { val widget = it.first() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index f0caac9b97..c3719ffd8e 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.workers.signout import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -42,6 +41,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_S import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener +import org.matrix.android.sdk.flow.flow data class ServerBackupStatusViewState( val bannerState: Async = Uninitialized @@ -92,19 +92,9 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS init { session.cryptoService().keysBackupService().addListener(this) keysBackupState.value = session.cryptoService().keysBackupService().state - val liveUserAccountData = session.accountDataService() - .getLiveUserAccountDataEvents(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) - .asFlow() - val liveCrossSigningInfo = session.cryptoService() - .crossSigningService() - .getLiveCrossSigningKeys(session.myUserId) - .asFlow() - val liveCrossSigningPrivateKeys = session.cryptoService() - .crossSigningService() - .getLiveCrossSigningPrivateKeys() - .asFlow() - - + val liveUserAccountData = session.flow().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) + val liveCrossSigningInfo = session.flow().liveCrossSigningInfo(session.myUserId) + val liveCrossSigningPrivateKeys = session.flow().liveCrossSigningPrivateKeys() combine(liveUserAccountData, liveCrossSigningInfo, keyBackupFlow, liveCrossSigningPrivateKeys) { _, crossSigningInfo, keyBackupState, pInfo -> // first check if 4S is already setup if (session.sharedSecretStorageService.isRecoverySetup()) { diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt index e6429b6263..057d9e31f8 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.workers.signout import android.net.Uri -import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -44,6 +43,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_S import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener +import org.matrix.android.sdk.flow.flow import timber.log.Timber data class SignoutCheckViewState( @@ -98,9 +98,7 @@ class SignoutCheckViewModel @AssistedInject constructor( ) } - session.accountDataService() - .getLiveUserAccountDataEvents(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) - .asFlow() + session.flow().liveUserAccountData(setOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME)) .map { session.sharedSecretStorageService.isRecoverySetup() } From 2f732affa5d22a0714cafb02efc3de8da07e9c12 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 10:15:37 +0100 Subject: [PATCH 047/144] updating policy title to include show/hide --- .../features/discovery/DiscoverySettingsController.kt | 10 ++++++---- vector/src/main/res/values/strings.xml | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index f2f9f8f0d9..171a73c61d 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -32,7 +32,6 @@ import im.vector.app.core.extensions.getFormattedValue import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.form.formAdvancedToggleItem -import im.vector.app.features.terms.termItem import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid @@ -107,7 +106,7 @@ class DiscoverySettingsController @Inject constructor( } } - private fun buildIdentityServerSection(data: DiscoverySettingsState, ) { + private fun buildIdentityServerSection(data: DiscoverySettingsState) { val identityServer = data.identityServer() val identityServerUrl = identityServer?.serverUrl ?: stringProvider.getString(R.string.none) val host = this @@ -126,7 +125,10 @@ class DiscoverySettingsController @Inject constructor( if (terms != null) { formAdvancedToggleItem { id("policy-urls") - title(host.stringProvider.getString(R.string.settings_discovery_identity_server_policies_title)) + val titleRes = if (data.isIdentityPolicyUrlsExpanded) { + R.string.settings_discovery_hide_identity_server_policies_title + } else R.string.settings_discovery_show_identity_server_policies_title + title(host.stringProvider.getString(titleRes)) expanded(data.isIdentityPolicyUrlsExpanded) listener { host.listener?.onPolicyUrlsExpandedStateToggled() } } @@ -136,7 +138,7 @@ class DiscoverySettingsController @Inject constructor( id(term.url) name(term.name) url(term.url) - clickListener { + clickListener { // TODO } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 065e618e57..9cce711e29 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2349,7 +2349,8 @@ Configure identity server Open Discovery Settings Change identity server - Identity server policy + Show identity server policy + Hide identity server policy You are currently using %1$s to discover and be discoverable by existing contacts you know. You are not currently using an identity server. To discover and be discoverable by existing contacts you know, configure one below. Discoverable email addresses From 64fb94691b6a327eef45164e27cd52bb3d2b94d5 Mon Sep 17 00:00:00 2001 From: Erik Huizinga Date: Wed, 6 Oct 2021 08:59:13 +0000 Subject: [PATCH 048/144] Translated using Weblate (Dutch) Currently translated at 65.1% (1732 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/nl/ --- vector/src/main/res/values-nl/strings.xml | 83 ++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-nl/strings.xml b/vector/src/main/res/values-nl/strings.xml index e133e4ac1d..efa9228af5 100644 --- a/vector/src/main/res/values-nl/strings.xml +++ b/vector/src/main/res/values-nl/strings.xml @@ -1585,8 +1585,8 @@ U heeft hier geüpgraded. %s heeft hier geüpgraded. Je hebt toekomstige gespreksgeschiedenis zichtbaar gemaakt voor %1$s - %1$ds verliet de ruimte - + %1$ds over + %s is toegetreden. Conclusie Bevestiging Botknoppen Gespreksinstellingen @@ -1929,4 +1929,83 @@ %1$s heeft %2$s als alternatieve gespreksadressen toegevoegd. Aan de slag + Spacerechten + Gespreksrechten + Door deze gebruiker niet meer de verbannen kan hij/zij opnieuw toetreden tot de space. + Door deze gebruiker niet meer de verbannen kan hij/zij opnieuw toetreden tot het gesprek. + Door deze gebruiker te verbannen zal hij/zij verwijderd worden uit deze space en voorkomen dat hij/zij opnieuw toetreedt. + Reden voor verbanning + door deze gebruiker de verwijderen zal hij/zij niet meer in deze space zitten. +\n +\nOm te voorkomen dat hij/zij opnieuw toetreedt, kun je hem/haar ook verbannen. + door deze gebruiker te verwijderen zal hij/zij niet meer in dit gesprek zitten. +\n +\nOm te voorkomen dat hij/zij opnieuw toetreedt, kun je hem/haar ook verbannen. + Reden voor verwijdering + Weet je zeker dat je uitnodiging voor deze gebruiker wilt annuleren\? + Het niet meer negeren van deze gebruiker zal al zijn/haar berichten opnieuw doen weergeven. + Door deze gebruiker te negeren worden zijn/haar berichten verwijderd uit gesprekken die jullie delen. +\n +\nJe kunt deze actie op elk moment ongedaan maken in de algemene instellingen. + Je kunt deze wijziging niet ongedaan maken omdat je jezelf degradeert, als je de laatste gebruiker met rechten bent in het gesprek zal het onmogelijk zijn om opnieuw rechten te krijgen. + Dit gesprek is niet publiek. Je kunt niet opnieuw toetreden zonder uitnodiging. + Toch Doorgaan + Toegang verlenen tot je contactpersonen. + Om de QR-code te scannen moet je toegang verlenen tot de camera. + Oproep beëindigen + Geen antwoord + De gebruiker die je hebt gebeld is bezig. + Gebruiker bezig + Je hebt de oproep in de wacht gezet + %s heeft de oproep in de wacht gezet + Teruggaan naar oproep + Actieve Oproep (%s) + Bellen met + Videobellen met + + Gemiste video-oproep + %d gemiste video-oproepen + + + Gemiste stemoproep + gemiste stemoproepen + + Gaat over… + Bevestiging vragen voor het starten van een oproep + Onbedoelde oproep voorkomen + Niet geautoriseerd, geldige authenticatiegegevens ontbreken. + SSL-fout: de identiteit van de ander is niet bevestigd. + Dit telefoonnummer is al gedefinieerd. + Gebruik als standaard en niet opnieuw vragen + Altijd vragen + HD inschakelen + HD uitschakelen + Geluidsapparaat Selecteren + Live verbinding opzetten mislukt.Vraag de administrator van je homeserver om een TURN-server te configureren zodat oproepen betrouwbaar werken. + ${app_name} Oproep Mislukt + Homeserver API URL + Sleutel deelverzoekgeschiedenis verzenden + Alle gesprekken in de lijst tonen, waaronder gesprekken met expliciete inhoud. + Gesprekken tonen met expliciete inhoud + Gesprekslijst + Er zijn niet meer resultaten + Aanbevolen Gesprekken + Nieuwe waarde + Widget verwijderen mislukt + Widget toevoegen mislukt + Je kunt jezelf niet bellen, wacht totdat deelnemers de uitnodiging accepteren + Je kunt jezelf niet bellen + Vergaderingen gebruiken beveiligings- en toestemmingsbeleid van Jitsi. Alle huidige personen in het gesprek zullen een uitnodiging zien terwijl je vergadering bezig is. + Geluidsvergadering starten + Videoconferentie starten + Er is al een vergadering aan de gang! + Je mist het recht om een oproep te starten + Je mist het recht om een oproep in dit gesprek te starten + Je mist het recht om een vergadering te starten + Je mist het recht om een vergadering in dit gesprek te starten + Ontbrekende rechten + Geef toestemming om de microfoon te gebruiken om stemberichten te versturen. + Alles herstellen + Je bent toegetreden. + Stemberichten inschakelen \ No newline at end of file From f0cc56d95eb55585d9300ebba917362813cfaddf Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Wed, 6 Oct 2021 11:49:38 +0000 Subject: [PATCH 049/144] Translated using Weblate (Swedish) Currently translated at 100.0% (31 of 31 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sv/ --- fastlane/metadata/android/sv-SE/changelogs/40102010.txt | 2 ++ fastlane/metadata/android/sv-SE/changelogs/40103000.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40102010.txt create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40103000.txt diff --git a/fastlane/metadata/android/sv-SE/changelogs/40102010.txt b/fastlane/metadata/android/sv-SE/changelogs/40102010.txt new file mode 100644 index 0000000000..f29b95de79 --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: Många förbättringar för VoIP och utrymmen (fortfarande i beta). +Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/sv-SE/changelogs/40103000.txt b/fastlane/metadata/android/sv-SE/changelogs/40103000.txt new file mode 100644 index 0000000000..d9a2c34f1d --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: Organisera dina rum med utrymmen! +Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.0 From acf3b847819860c2e244c827a08217f59dc8daf5 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 7 Oct 2021 12:24:08 +0200 Subject: [PATCH 050/144] Mavericks 2: migrate UserListViewModel --- .../userdirectory/UserListViewModel.kt | 204 ++++++++---------- 1 file changed, 92 insertions(+), 112 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 69b98200c1..cae8540f8b 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -16,12 +16,12 @@ package im.vector.app.features.userdirectory +import androidx.lifecycle.asFlow import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext -import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -29,21 +29,23 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.sample import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.rx.rx -import java.util.concurrent.TimeUnit - -private typealias KnownUsersSearch = String -private typealias DirectoryUsersSearch = String data class ThreePidUser( val email: String, @@ -54,9 +56,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private val session: Session) : VectorViewModel(initialState) { - private val knownUsersSearch = BehaviorRelay.create() - private val directoryUsersSearch = BehaviorRelay.create() - private val identityServerUsersSearch = BehaviorRelay.create() + private val knownUsersSearch = MutableStateFlow("") + private val directoryUsersSearch = MutableStateFlow("") + private val identityServerUsersSearch = MutableStateFlow("") @AssistedFactory interface Factory { @@ -77,11 +79,10 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private val identityServerListener = object : IdentityServiceListener { override fun onIdentityServerChange() { withState { - identityServerUsersSearch.accept(it.searchTerm) + identityServerUsersSearch.tryEmit(it.searchTerm) + val identityServerURL = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) setState { - copy( - configuredIdentityServer = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) - ) + copy(configuredIdentityServer = identityServerURL) } } } @@ -120,7 +121,7 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private fun handleISUpdateConsent(action: UserListAction.UpdateUserConsent) { session.identityService().setUserConsent(action.consent) withState { - identityServerUsersSearch.accept(it.searchTerm) + identityServerUsersSearch.tryEmit(it.searchTerm) } } @@ -139,9 +140,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User ) } } - identityServerUsersSearch.accept(searchTerm) - knownUsersSearch.accept(searchTerm) - directoryUsersSearch.accept(searchTerm) + identityServerUsersSearch.tryEmit(searchTerm) + knownUsersSearch.tryEmit(searchTerm) + directoryUsersSearch.tryEmit(searchTerm) } private fun handleShareMyMatrixToLink() { @@ -151,9 +152,9 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User } private fun handleClearSearchUsers() { - knownUsersSearch.accept("") - directoryUsersSearch.accept("") - identityServerUsersSearch.accept("") + knownUsersSearch.tryEmit("") + directoryUsersSearch.tryEmit("") + identityServerUsersSearch.tryEmit("") setState { copy(searchTerm = "") } @@ -162,103 +163,82 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private fun observeUsers() = withState { state -> identityServerUsersSearch .filter { it.isEmail() } - .throttleLast(300, TimeUnit.MILLISECONDS) - .switchMapSingle { search -> - val flowSession = session.rx() - val stream = - flowSession.lookupThreePid(ThreePid.Email(search)).flatMap { - it.getOrNull()?.let { foundThreePid -> - flowSession.getProfileInfo(foundThreePid.matrixId) - .map { json -> - ThreePidUser( - email = search, - user = User( - userId = foundThreePid.matrixId, - displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, - avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String - ) - ) - } - .onErrorResumeNext { - Single.just(ThreePidUser(email = search, user = User(foundThreePid.matrixId))) - } - } ?: Single.just(ThreePidUser(email = search, user = null)) - } - stream.toAsync { - copy(matchingEmail = it) - } - } - .subscribe() - .disposeOnClear() + .sample(300) + .onEach { search -> + executeSearchEmail(search) + }.launchIn(viewModelScope) knownUsersSearch - .throttleLast(300, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .switchMap { - session.rx().livePagedUsers(it, state.excludedUserIds) - } - .execute { async -> - copy(knownUsers = async) + .sample(300) + .flowOn(Dispatchers.Main) + .flatMapLatest { search -> + session.getPagedUsersLive(search, state.excludedUserIds).asFlow() + }.execute { + copy(knownUsers = it) } directoryUsersSearch - .debounce(300, TimeUnit.MILLISECONDS) - .switchMapSingle { search -> - val stream = if (search.isBlank()) { - Single.just(emptyList()) - } else { - val searchObservable = session.rx() - .searchUsersDirectory(search, 50, state.excludedUserIds.orEmpty()) - .map { users -> - users.sortedBy { it.toMatrixItem().firstLetterOfDisplayName() } - } - // If it's a valid user id try to use Profile API - // because directory only returns users that are in public rooms or share a room with you, where as - // profile will work other federations - if (!MatrixPatterns.isUserId(search)) { - searchObservable - } else { - val profileObservable = session.rx().getProfileInfo(search) - .map { json -> - User( - userId = search, - displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, - avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String - ).toOptional() - } - .onErrorResumeNext { - // Profile API can be restricted and doesn't have to return result. - // In this case allow inviting valid user ids. - Single.just( - User( - userId = search, - displayName = null, - avatarUrl = null - ).toOptional() - ) - } + .debounce(300) + .onEach { search -> + executeSearchDirectory(state, search) + }.launchIn(viewModelScope) + } - Single.zip( - searchObservable, - profileObservable, - { searchResults, optionalProfile -> - val profile = optionalProfile.getOrNull() ?: return@zip searchResults - val searchContainsProfile = searchResults.any { it.userId == profile.userId } - if (searchContainsProfile) { - searchResults - } else { - listOf(profile) + searchResults - } - } + private suspend fun executeSearchEmail(search: String) { + suspend { + val params = listOf(ThreePid.Email(search)) + val foundThreePid = tryOrNull { + session.identityService().lookUp(params).firstOrNull() + } + if (foundThreePid == null) { + null + } else { + try { + val json = session.getProfile(foundThreePid.matrixId) + ThreePidUser( + email = search, + user = User( + userId = foundThreePid.matrixId, + displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, + avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String ) - } - } - stream.toAsync { - copy(directoryUsers = it) - } + ) + } catch (failure: Throwable) { + ThreePidUser(email = search, user = User(foundThreePid.matrixId)) } - .subscribe() - .disposeOnClear() + } + }.execute { + copy(matchingEmail = it) + } + } + + private suspend fun executeSearchDirectory(state: UserListViewState, search: String) { + suspend { + if (search.isBlank()) { + emptyList() + } else { + val searchResult = session + .searchUsersDirectory(search, 50, state.excludedUserIds.orEmpty()) + .sortedBy { it.toMatrixItem().firstLetterOfDisplayName() } + val userProfile = if (MatrixPatterns.isUserId(search)) { + val json = tryOrNull { session.getProfile(search) } + User( + userId = search, + displayName = json?.get(ProfileService.DISPLAY_NAME_KEY) as? String, + avatarUrl = json?.get(ProfileService.AVATAR_URL_KEY) as? String + ) + } else { + null + } + if (userProfile == null || searchResult.any { it.userId == userProfile.userId }) { + searchResult + } else { + listOf(userProfile) + searchResult + } + } + }.execute { + copy(directoryUsers = it) + } } private fun handleSelectUser(action: UserListAction.AddPendingSelection) = withState { state -> From 578358d8392df78ec68c699e2c4050c2ebe12d73 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 7 Oct 2021 12:24:53 +0200 Subject: [PATCH 051/144] Mavericks 2: introduce startWith (like startWithCallable from matrix-android-sdk-rx) --- .../org/matrix/android/sdk/flow/FlowExt.kt | 32 ++++++++++++++ .../org/matrix/android/sdk/flow/FlowRoom.kt | 22 ++++++++++ .../matrix/android/sdk/flow/FlowSession.kt | 42 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt new file mode 100644 index 0000000000..dd8a5d7750 --- /dev/null +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.flow + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext + +internal fun Flow.startWith(supplier: suspend () -> T): Flow { + return this + .onStart { + val value = withContext(Dispatchers.IO) { + supplier() + } + emit(value) + } +} diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt index a3e476ce08..1e0a1bc2e8 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt @@ -30,31 +30,50 @@ import org.matrix.android.sdk.api.session.room.notification.RoomNotificationStat import org.matrix.android.sdk.api.session.room.send.UserDraft import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.api.util.toOptional class FlowRoom(private val room: Room) { fun liveRoomSummary(): Flow> { return room.getRoomSummaryLive().asFlow() + .startWith { + room.roomSummary().toOptional() + } } fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow> { return room.getRoomMembersLive(queryParams).asFlow() + .startWith { + room.getRoomMembers(queryParams) + } } fun liveAnnotationSummary(eventId: String): Flow> { return room.getEventAnnotationsSummaryLive(eventId).asFlow() + .startWith { + room.getEventAnnotationsSummary(eventId).toOptional() + } } fun liveTimelineEvent(eventId: String): Flow> { return room.getTimeLineEventLive(eventId).asFlow() + .startWith { + room.getTimeLineEvent(eventId).toOptional() + } } fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow> { return room.getStateEventLive(eventType, stateKey).asFlow() + .startWith { + room.getStateEvent(eventType, stateKey).toOptional() + } } fun liveStateEvents(eventTypes: Set): Flow> { return room.getStateEventsLive(eventTypes).asFlow() + .startWith { + room.getStateEvents(eventTypes) + } } fun liveReadMarker(): Flow> { @@ -71,6 +90,9 @@ class FlowRoom(private val room: Room) { fun liveDraft(): Flow> { return room.getDraftLive().asFlow() + .startWith { + room.getDraft().toOptional() + } } fun liveNotificationState(): Flow { diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt index affcd4a65d..563ae30b45 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt @@ -37,6 +37,7 @@ import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo @@ -45,22 +46,37 @@ class RxFlow(private val session: Session) { fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Flow> { return session.getRoomSummariesLive(queryParams).asFlow() + .startWith { + session.getRoomSummaries(queryParams) + } } fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow> { return session.getGroupSummariesLive(queryParams).asFlow() + .startWith { + session.getGroupSummaries(queryParams) + } } fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Flow> { return session.spaceService().getSpaceSummariesLive(queryParams).asFlow() + .startWith { + session.spaceService().getSpaceSummaries(queryParams) + } } fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow> { return session.getBreadcrumbsLive(queryParams).asFlow() + .startWith { + session.getBreadcrumbs(queryParams) + } } fun liveMyDevicesInfo(): Flow> { return session.cryptoService().getLiveMyDevicesInfo().asFlow() + .startWith { + session.cryptoService().getMyDevicesInfo() + } } fun liveSyncState(): Flow { @@ -73,10 +89,16 @@ class RxFlow(private val session: Session) { fun liveUser(userId: String): Flow> { return session.getUserLive(userId).asFlow() + .startWith { + session.getUser(userId).toOptional() + } } fun liveRoomMember(userId: String, roomId: String): Flow> { return session.getRoomMemberLive(userId, roomId).asFlow() + .startWith { + session.getRoomMember(userId, roomId).toOptional() + } } fun liveUsers(): Flow> { @@ -93,30 +115,47 @@ class RxFlow(private val session: Session) { fun liveThreePIds(refreshData: Boolean): Flow> { return session.getThreePidsLive(refreshData).asFlow() + .startWith { session.getThreePids() } } fun livePendingThreePIds(): Flow> { return session.getPendingThreePidsLive().asFlow() + .startWith { session.getPendingThreePids() } } fun liveUserCryptoDevices(userId: String): Flow> { return session.cryptoService().getLiveCryptoDeviceInfo(userId).asFlow() + .startWith { + session.cryptoService().getCryptoDeviceInfo(userId) + } } fun liveCrossSigningInfo(userId: String): Flow> { return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asFlow() + .startWith { + session.cryptoService().crossSigningService().getUserCrossSigningKeys(userId).toOptional() + } } fun liveCrossSigningPrivateKeys(): Flow> { return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() + .startWith { + session.cryptoService().crossSigningService().getCrossSigningPrivateKeys().toOptional() + } } fun liveUserAccountData(types: Set): Flow> { return session.accountDataService().getLiveUserAccountDataEvents(types).asFlow() + .startWith { + session.accountDataService().getUserAccountDataEvents(types) + } } fun liveRoomAccountData(types: Set): Flow> { return session.accountDataService().getLiveRoomAccountDataEvents(types).asFlow() + .startWith { + session.accountDataService().getRoomAccountDataEvents(types) + } } fun liveRoomWidgets( @@ -126,6 +165,9 @@ class RxFlow(private val session: Session) { excludedTypes: Set? = null ): Flow> { return session.widgetService().getRoomWidgetsLive(roomId, widgetId, widgetTypes, excludedTypes).asFlow() + .startWith { + session.widgetService().getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes) + } } fun liveRoomChangeMembershipState(): Flow> { From c0fd266fc4ca3a68df68bb1120ec9501d55d93ab Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 10:23:41 +0100 Subject: [PATCH 052/144] using consistent naming for the policy and opening a chrome tab when policy is tapped --- .../discovery/DiscoverySettingsController.kt | 17 ++++++++--------- .../discovery/DiscoverySettingsFragment.kt | 6 ++++++ .../discovery/DiscoverySettingsState.kt | 5 ++--- .../discovery/DiscoverySettingsViewModel.kt | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 171a73c61d..2ed45c127b 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -121,8 +121,8 @@ class DiscoverySettingsController @Inject constructor( title(identityServerUrl) } - val terms = identityServer?.terms - if (terms != null) { + val policies = identityServer?.policies + if (policies != null) { formAdvancedToggleItem { id("policy-urls") val titleRes = if (data.isIdentityPolicyUrlsExpanded) { @@ -133,14 +133,12 @@ class DiscoverySettingsController @Inject constructor( listener { host.listener?.onPolicyUrlsExpandedStateToggled() } } if (data.isIdentityPolicyUrlsExpanded) { - terms.forEach { term -> + policies.forEach { policy -> discoveryPolicyItem { - id(term.url) - name(term.name) - url(term.url) - clickListener { - // TODO - } + id(policy.url) + name(policy.name) + url(policy.url) + clickListener { host.listener?.onPolicyTapped(policy) } } } } @@ -428,5 +426,6 @@ class DiscoverySettingsController @Inject constructor( fun onTapUpdateUserConsent(newValue: Boolean) fun onTapRetryToRetrieveBindings() fun onPolicyUrlsExpandedStateToggled() + fun onPolicyTapped(policy: IdentityServerPolicy) } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index a1582d1a44..1302df48ec 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -31,7 +31,9 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.displayInWebView import im.vector.app.core.utils.ensureProtocol +import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.discovery.change.SetIdentityServerFragment @@ -198,6 +200,10 @@ class DiscoverySettingsFragment @Inject constructor( viewModel.handle(DiscoverySettingsAction.PolicyUrlsExpandedStateToggled) } + override fun onPolicyTapped(policy: IdentityServerPolicy) { + openUrlInChromeCustomTab(requireContext(), null, policy.url) + } + private fun navigateToChangeIdentityServerFragment() { (vectorBaseActivity as? VectorSettingsActivity)?.navigateTo(SetIdentityServerFragment::class.java) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index a70b66941b..1d7346e463 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -19,7 +19,6 @@ package im.vector.app.features.discovery import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms data class DiscoverySettingsState( val identityServer: Async = Uninitialized, @@ -33,7 +32,7 @@ data class DiscoverySettingsState( data class IdentityServerWithTerms( val serverUrl: String, - val terms: List + val policies: List ) -data class IdentityServerTerms(val name: String, val url: String) +data class IdentityServerPolicy(val name: String, val url: String) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 4240bdedad..6d872e5043 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -427,7 +427,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( if (name == null || url == null) { null } else { - IdentityServerTerms(name = name, url = url) + IdentityServerPolicy(name = name, url = url) } } IdentityServerWithTerms(identityServerUrl, policyUrls) From 05166944d8e29aa200dd76598fe29182760893e9 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 12:45:59 +0100 Subject: [PATCH 053/144] navigating to the settings discovery page on policy link clicked from non discovery screens --- .../src/main/java/im/vector/app/core/utils/Dialogs.kt | 10 +++------- .../app/features/contactsbook/ContactsBookFragment.kt | 9 ++++++--- .../features/discovery/DiscoverySettingsFragment.kt | 1 - .../app/features/userdirectory/UserListFragment.kt | 8 +++++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt index 99e8684229..ed3ae00567 100644 --- a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt +++ b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt @@ -40,19 +40,15 @@ fun Context.displayInWebView(url: String) { .show() } -fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, policyLinkCallback: (() -> Unit)? = null, consentCallBack: (() -> Unit)) { +fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, policyLinkCallback: () -> Unit, consentCallBack: (() -> Unit)) { MaterialAlertDialogBuilder(this) .setTitle(R.string.identity_server_consent_dialog_title) .setMessage(getString(R.string.identity_server_consent_dialog_content, configuredIdentityServer ?: "")) .setPositiveButton(R.string.yes) { _, _ -> consentCallBack.invoke() } - .apply { - if (policyLinkCallback != null) { - setNeutralButton(R.string.identity_server_consent_dialog_neutral_policy) { _, _ -> - policyLinkCallback.invoke() - } - } + .setNeutralButton(R.string.identity_server_consent_dialog_neutral_policy) { _, _ -> + policyLinkCallback.invoke() } .setNegativeButton(R.string.no, null) .show() diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index fc913ff835..fc24aa3e87 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -31,6 +31,7 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.databinding.FragmentContactsBookBinding +import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.userdirectory.PendingSelection import im.vector.app.features.userdirectory.UserListAction import im.vector.app.features.userdirectory.UserListSharedAction @@ -74,9 +75,11 @@ class ContactsBookFragment @Inject constructor( private fun setupConsentView() { views.phoneBookSearchForMatrixContacts.setOnClickListener { withState(contactsBookViewModel) { state -> - requireContext().showIdentityServerConsentDialog(state.identityServerUrl) { - contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) - } + requireContext().showIdentityServerConsentDialog( + state.identityServerUrl, + policyLinkCallback = { navigator.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS) }, + consentCallBack = { contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) } + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 1302df48ec..0d31edd047 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -31,7 +31,6 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.core.utils.displayInWebView import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.core.utils.showIdentityServerConsentDialog diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 5fdc316851..009961ebbc 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -227,9 +227,11 @@ class UserListFragment @Inject constructor( override fun giveIdentityServerConsent() { withState(viewModel) { state -> - requireContext().showIdentityServerConsentDialog(state.configuredIdentityServer) { - viewModel.handle(UserListAction.UpdateUserConsent(true)) - } + requireContext().showIdentityServerConsentDialog( + state.configuredIdentityServer, + policyLinkCallback = { navigator.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS) }, + consentCallBack = { viewModel.handle(UserListAction.UpdateUserConsent(true)) } + ) } } From 728f34f53cb56a575c7c9ff262f6d1eba8fec6eb Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 13:07:30 +0100 Subject: [PATCH 054/144] converting the settings activity payload to a sealed class, this allows us to have custom arguments for the sub settings pages --- .../features/navigation/DefaultNavigator.kt | 5 ++ .../app/features/navigation/Navigator.kt | 15 ++++++ .../settings/VectorSettingsActivity.kt | 51 ++++++++++++++----- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 55b74e9b34..debdf3739c 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -353,6 +353,11 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } + override fun openSettings(context: Context, payload: SettingsActivityPayload) { + val intent = VectorSettingsActivity.getIntent(context, payload) + context.startActivity(intent) + } + override fun openDebug(context: Context) { context.startActivity(Intent(context, DebugMenuActivity::class.java)) } diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index c11b840522..4a021bdd34 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -19,6 +19,7 @@ package im.vector.app.features.navigation import android.app.Activity import android.content.Context import android.content.Intent +import android.os.Parcelable import android.view.View import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair @@ -31,6 +32,7 @@ import im.vector.app.features.roomdirectory.RoomDirectoryData import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData +import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.terms.TermsService @@ -83,6 +85,8 @@ interface Navigator { fun openSettings(context: Context, directAccess: Int = VectorSettingsActivity.EXTRA_DIRECT_ACCESS_ROOT) + fun openSettings(context: Context, payload: SettingsActivityPayload) + fun openDebug(context: Context) fun openKeysBackupSetup(context: Context, showManualExport: Boolean) @@ -139,3 +143,14 @@ interface Navigator { fun openCallTransfer(context: Context, callId: String) } + +sealed interface SettingsActivityPayload : Parcelable { + + @Parcelize object Root : SettingsActivityPayload + @Parcelize object AdvancedSettings : SettingsActivityPayload + @Parcelize object SecurityPrivacy : SettingsActivityPayload + @Parcelize object SecurityPrivacyManageSessions : SettingsActivityPayload + @Parcelize object General : SettingsActivityPayload + @Parcelize object Notifications : SettingsActivityPayload + @Parcelize object DiscoverySettings : SettingsActivityPayload +} diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index 298551af2a..c8b7c0a499 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -15,8 +15,10 @@ */ package im.vector.app.features.settings +import android.app.Activity import android.content.Context import android.content.Intent +import android.os.Parcelable import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.preference.Preference @@ -27,14 +29,18 @@ import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityVectorSettingsBinding import im.vector.app.features.discovery.DiscoverySettingsFragment +import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment +import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.session.Session import timber.log.Timber import javax.inject.Inject +private const val KEY_ACTIVITY_PAYLOAD = "settings-activity-payload" + /** * Displays the client settings. */ @@ -64,27 +70,27 @@ class VectorSettingsActivity : VectorBaseActivity if (isFirstCreation()) { // display the fragment - when (intent.getIntExtra(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOT)) { - EXTRA_DIRECT_ACCESS_GENERAL -> + + when (readPayload(SettingsActivityPayload.Root)) { + SettingsActivityPayload.General -> replaceFragment(R.id.vector_settings_page, VectorSettingsGeneralFragment::class.java, null, FRAGMENT_TAG) - EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS -> + SettingsActivityPayload.AdvancedSettings -> replaceFragment(R.id.vector_settings_page, VectorSettingsAdvancedSettingsFragment::class.java, null, FRAGMENT_TAG) - EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY -> + SettingsActivityPayload.SecurityPrivacy -> replaceFragment(R.id.vector_settings_page, VectorSettingsSecurityPrivacyFragment::class.java, null, FRAGMENT_TAG) - EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS -> + SettingsActivityPayload.SecurityPrivacyManageSessions -> replaceFragment(R.id.vector_settings_page, VectorSettingsDevicesFragment::class.java, null, FRAGMENT_TAG) - EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> { + SettingsActivityPayload.Notifications -> { requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) replaceFragment(R.id.vector_settings_page, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG) } - EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS -> { + SettingsActivityPayload.DiscoverySettings -> { replaceFragment(R.id.vector_settings_page, DiscoverySettingsFragment::class.java, null, FRAGMENT_TAG) } - - else -> + else -> replaceFragment(R.id.vector_settings_page, VectorSettingsRootFragment::class.java, null, FRAGMENT_TAG) } } @@ -157,10 +163,22 @@ class VectorSettingsActivity : VectorBaseActivity } companion object { - fun getIntent(context: Context, directAccess: Int) = Intent(context, VectorSettingsActivity::class.java) - .apply { putExtra(EXTRA_DIRECT_ACCESS, directAccess) } + fun getIntent(context: Context, directAccess: Int) = Companion.getIntent(context, when (directAccess) { + EXTRA_DIRECT_ACCESS_ROOT -> SettingsActivityPayload.Root + EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS -> SettingsActivityPayload.AdvancedSettings + EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY -> SettingsActivityPayload.SecurityPrivacy + EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS -> SettingsActivityPayload.SecurityPrivacyManageSessions + EXTRA_DIRECT_ACCESS_GENERAL -> SettingsActivityPayload.General + EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> SettingsActivityPayload.Notifications + EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS -> SettingsActivityPayload.DiscoverySettings + else -> { + Timber.w("Unknown directAccess: $directAccess defaulting to Root") + SettingsActivityPayload.Root + } + }) - private const val EXTRA_DIRECT_ACCESS = "EXTRA_DIRECT_ACCESS" + fun getIntent(context: Context, payload: SettingsActivityPayload) = Intent(context, VectorSettingsActivity::class.java) + .applyPayload(payload) const val EXTRA_DIRECT_ACCESS_ROOT = 0 const val EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS = 1 @@ -173,3 +191,12 @@ class VectorSettingsActivity : VectorBaseActivity private const val FRAGMENT_TAG = "VectorSettingsPreferencesFragment" } } + +private fun Activity.readPayload(default: T): T { + return intent.getParcelableExtra(KEY_ACTIVITY_PAYLOAD) ?: default +} + +private fun Intent.applyPayload(payload: T): Intent { + return putExtra(KEY_ACTIVITY_PAYLOAD, payload) +} + From 7b5972e3cf01ce0e0007ad277fa3e3e2c3cd1d19 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 13:50:43 +0100 Subject: [PATCH 055/144] expanding the discovery polices on recieving a custom payload - also meant updating the general link to discovery in order to manually pass the default arguments --- .../discovery/DiscoverySettingsFragment.kt | 6 ++++ .../app/features/navigation/Navigator.kt | 13 -------- .../navigation/SettingsActivityPayload.kt | 33 +++++++++++++++++++ .../features/settings/VectorPreferences.kt | 1 + .../settings/VectorSettingsActivity.kt | 15 +++++---- .../settings/VectorSettingsGeneralFragment.kt | 12 +++++++ .../main/res/xml/vector_settings_general.xml | 4 +-- 7 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/navigation/SettingsActivityPayload.kt diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 0d31edd047..f7f5452d84 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -21,6 +21,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -36,6 +37,7 @@ import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.discovery.change.SetIdentityServerFragment +import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.settings.VectorSettingsActivity import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid @@ -53,6 +55,7 @@ class DiscoverySettingsFragment @Inject constructor( } private val viewModel by fragmentViewModel(DiscoverySettingsViewModel::class) + private val discoveryArgs: SettingsActivityPayload.DiscoverySettings by args() lateinit var sharedViewModel: DiscoverySharedViewModel @@ -78,6 +81,9 @@ class DiscoverySettingsFragment @Inject constructor( } }.exhaustive } + if (discoveryArgs.expandIdentityPolicies) { + viewModel.handle(DiscoverySettingsAction.ExpandPolicyUrls) + } } override fun onDestroyView() { diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 4a021bdd34..612643c804 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -19,7 +19,6 @@ package im.vector.app.features.navigation import android.app.Activity import android.content.Context import android.content.Intent -import android.os.Parcelable import android.view.View import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair @@ -32,7 +31,6 @@ import im.vector.app.features.roomdirectory.RoomDirectoryData import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData -import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.terms.TermsService @@ -143,14 +141,3 @@ interface Navigator { fun openCallTransfer(context: Context, callId: String) } - -sealed interface SettingsActivityPayload : Parcelable { - - @Parcelize object Root : SettingsActivityPayload - @Parcelize object AdvancedSettings : SettingsActivityPayload - @Parcelize object SecurityPrivacy : SettingsActivityPayload - @Parcelize object SecurityPrivacyManageSessions : SettingsActivityPayload - @Parcelize object General : SettingsActivityPayload - @Parcelize object Notifications : SettingsActivityPayload - @Parcelize object DiscoverySettings : SettingsActivityPayload -} diff --git a/vector/src/main/java/im/vector/app/features/navigation/SettingsActivityPayload.kt b/vector/src/main/java/im/vector/app/features/navigation/SettingsActivityPayload.kt new file mode 100644 index 0000000000..0b128c51b1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/navigation/SettingsActivityPayload.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.navigation + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +sealed interface SettingsActivityPayload : Parcelable { + + @Parcelize object Root : SettingsActivityPayload + @Parcelize object AdvancedSettings : SettingsActivityPayload + @Parcelize object SecurityPrivacy : SettingsActivityPayload + @Parcelize object SecurityPrivacyManageSessions : SettingsActivityPayload + @Parcelize object General : SettingsActivityPayload + @Parcelize object Notifications : SettingsActivityPayload + + @Parcelize + data class DiscoverySettings(val expandIdentityPolicies: Boolean = false) : SettingsActivityPayload +} diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index a5df2ad1d4..07cd9d6dac 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -44,6 +44,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { const val SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY = "SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY" const val SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY = "SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY" const val SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY = "SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY" + const val SETTINGS_DISCOVERY_PREFERENCE_KEY = "SETTINGS_DISCOVERY_PREFERENCE_KEY" const val SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY" const val SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY" diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index c8b7c0a499..ee74a6855c 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -18,7 +18,9 @@ package im.vector.app.features.settings import android.app.Activity import android.content.Context import android.content.Intent +import android.os.Bundle import android.os.Parcelable +import android.util.Log import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.preference.Preference @@ -71,7 +73,7 @@ class VectorSettingsActivity : VectorBaseActivity if (isFirstCreation()) { // display the fragment - when (readPayload(SettingsActivityPayload.Root)) { + when (val payload = readPayload(SettingsActivityPayload.Root)) { SettingsActivityPayload.General -> replaceFragment(R.id.vector_settings_page, VectorSettingsGeneralFragment::class.java, null, FRAGMENT_TAG) SettingsActivityPayload.AdvancedSettings -> @@ -87,8 +89,9 @@ class VectorSettingsActivity : VectorBaseActivity requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) replaceFragment(R.id.vector_settings_page, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG) } - SettingsActivityPayload.DiscoverySettings -> { - replaceFragment(R.id.vector_settings_page, DiscoverySettingsFragment::class.java, null, FRAGMENT_TAG) + is SettingsActivityPayload.DiscoverySettings -> { + Log.e("!!!", "SettingsActivityPayload.DiscoverySettings : $payload") + replaceFragment(R.id.vector_settings_page, DiscoverySettingsFragment::class.java, payload, FRAGMENT_TAG) } else -> replaceFragment(R.id.vector_settings_page, VectorSettingsRootFragment::class.java, null, FRAGMENT_TAG) @@ -154,10 +157,10 @@ class VectorSettingsActivity : VectorBaseActivity } } - fun navigateTo(fragmentClass: Class) { + fun navigateTo(fragmentClass: Class, arguments: Bundle? = null) { supportFragmentManager.beginTransaction() .setCustomAnimations(R.anim.right_in, R.anim.fade_out, R.anim.fade_in, R.anim.right_out) - .replace(R.id.vector_settings_page, fragmentClass, null) + .replace(R.id.vector_settings_page, fragmentClass, arguments) .addToBackStack(null) .commit() } @@ -170,7 +173,7 @@ class VectorSettingsActivity : VectorBaseActivity EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS -> SettingsActivityPayload.SecurityPrivacyManageSessions EXTRA_DIRECT_ACCESS_GENERAL -> SettingsActivityPayload.General EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> SettingsActivityPayload.Notifications - EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS -> SettingsActivityPayload.DiscoverySettings + EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS -> SettingsActivityPayload.DiscoverySettings() else -> { Timber.w("Unknown directAccess: $directAccess defaulting to Root") SettingsActivityPayload.Root diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index f40079c615..51496d6eb0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -38,6 +38,7 @@ import im.vector.app.R import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hidePassword +import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.platform.SimpleTextWatcher import im.vector.app.core.preference.UserAvatarPreference @@ -50,6 +51,8 @@ import im.vector.app.core.utils.toast import im.vector.app.databinding.DialogChangePasswordBinding import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs +import im.vector.app.features.discovery.DiscoverySettingsFragment +import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.workers.signout.SignOutUiWorker import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.Dispatchers @@ -173,6 +176,15 @@ class VectorSettingsGeneralFragment @Inject constructor( mPasswordPreference.isVisible = false } + val discoveryPreference = findPreference(VectorPreferences.SETTINGS_DISCOVERY_PREFERENCE_KEY)!! + discoveryPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { + (requireActivity() as VectorSettingsActivity).navigateTo( + DiscoverySettingsFragment::class.java, + SettingsActivityPayload.DiscoverySettings().toMvRxBundle() + ) + true + } + // Advanced settings // user account diff --git a/vector/src/main/res/xml/vector_settings_general.xml b/vector/src/main/res/xml/vector_settings_general.xml index 73ce09eb22..e5929d8933 100644 --- a/vector/src/main/res/xml/vector_settings_general.xml +++ b/vector/src/main/res/xml/vector_settings_general.xml @@ -28,10 +28,10 @@ app:fragment="im.vector.app.features.settings.threepids.ThreePidsSettingsFragment" /> + android:title="@string/settings_discovery_category" /> From 791c92c99101d57419e519917687fdd8ef8675ac Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 13:54:36 +0100 Subject: [PATCH 056/144] launching the discovery page with policy expanded when tapping the policy link from the consent dialog --- .../app/features/contactsbook/ContactsBookFragment.kt | 6 ++++-- .../vector/app/features/settings/VectorSettingsActivity.kt | 4 +--- .../vector/app/features/userdirectory/UserListFragment.kt | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index fc24aa3e87..ea1841d870 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -31,7 +31,7 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.databinding.FragmentContactsBookBinding -import im.vector.app.features.settings.VectorSettingsActivity +import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.userdirectory.PendingSelection import im.vector.app.features.userdirectory.UserListAction import im.vector.app.features.userdirectory.UserListSharedAction @@ -77,7 +77,9 @@ class ContactsBookFragment @Inject constructor( withState(contactsBookViewModel) { state -> requireContext().showIdentityServerConsentDialog( state.identityServerUrl, - policyLinkCallback = { navigator.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS) }, + policyLinkCallback = { + navigator.openSettings(requireContext(), SettingsActivityPayload.DiscoverySettings(expandIdentityPolicies = true)) + }, consentCallBack = { contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) } ) } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index ee74a6855c..7cd4fc2d3d 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -35,7 +35,6 @@ import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment -import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.session.Session import timber.log.Timber @@ -89,7 +88,7 @@ class VectorSettingsActivity : VectorBaseActivity requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) replaceFragment(R.id.vector_settings_page, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG) } - is SettingsActivityPayload.DiscoverySettings -> { + is SettingsActivityPayload.DiscoverySettings -> { Log.e("!!!", "SettingsActivityPayload.DiscoverySettings : $payload") replaceFragment(R.id.vector_settings_page, DiscoverySettingsFragment::class.java, payload, FRAGMENT_TAG) } @@ -202,4 +201,3 @@ private fun Activity.readPayload(default: T): T { private fun Intent.applyPayload(payload: T): Intent { return putExtra(KEY_ACTIVITY_PAYLOAD, payload) } - diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 009961ebbc..c1fd878013 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -43,6 +43,7 @@ import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentUserListBinding import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel +import im.vector.app.features.navigation.SettingsActivityPayload import im.vector.app.features.settings.VectorSettingsActivity import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.user.model.User @@ -229,7 +230,9 @@ class UserListFragment @Inject constructor( withState(viewModel) { state -> requireContext().showIdentityServerConsentDialog( state.configuredIdentityServer, - policyLinkCallback = { navigator.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS) }, + policyLinkCallback = { + navigator.openSettings(requireContext(), SettingsActivityPayload.DiscoverySettings(expandIdentityPolicies = true)) + }, consentCallBack = { viewModel.handle(UserListAction.UpdateUserConsent(true)) } ) } From 496a531072cda8a28c8f54821fa01f01ba75bcfb Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 13:57:52 +0100 Subject: [PATCH 057/144] reverting uneedeed text item changes --- .../app/core/epoxy/ExpandableTextItem.kt | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt index ee942ed885..218a22533a 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt @@ -40,50 +40,27 @@ abstract class ExpandableTextItem : VectorEpoxyModel( @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var movementMethod: MovementMethod? = null - @EpoxyAttribute - var enableScrollBar = true - - @EpoxyAttribute - var expanded: Boolean? = null - - @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) - var onExpandClicked: () -> Unit? = {} - - private var internalIsExpanded = false + private var isExpanded = false private var expandedLines = 0 override fun bind(holder: Holder) { super.bind(holder) - holder.content.isVerticalScrollBarEnabled = enableScrollBar holder.content.text = content holder.content.copyOnLongClick() holder.content.movementMethod = movementMethod - if (expanded == null) { - holder.view.setOnClickListener { - if (internalIsExpanded) { - collapse(holder) - } else { - expand(holder) - } - } - } else { - holder.view.setOnClickListener { onExpandClicked() } - } - holder.content.doOnPreDraw { if (holder.content.lineCount > maxLines) { expandedLines = holder.content.lineCount holder.content.maxLines = maxLines - expanded?.let { expanded -> - if (expanded) { - expand(holder) - } else { + holder.view.setOnClickListener { + if (isExpanded) { collapse(holder) + } else { + expand(holder) } } - holder.arrow.isVisible = true } else { holder.arrow.isVisible = false @@ -100,7 +77,7 @@ abstract class ExpandableTextItem : VectorEpoxyModel( holder.content.ellipsize = null holder.arrow.setImageResource(R.drawable.ic_expand_less) holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_collapse) - internalIsExpanded = true + isExpanded = true } private fun collapse(holder: Holder) { @@ -112,7 +89,7 @@ abstract class ExpandableTextItem : VectorEpoxyModel( holder.content.ellipsize = TextUtils.TruncateAt.END holder.arrow.setImageResource(R.drawable.ic_expand_more) holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_expand) - internalIsExpanded = false + isExpanded = false } class Holder : VectorEpoxyHolder() { From 79ec0591d2817ce1c64e14129ebfa0425e101265 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 7 Oct 2021 15:32:57 +0200 Subject: [PATCH 058/144] Mavericks 2: continue removing rx --- .../call/conference/JitsiCallViewModel.kt | 15 ++++-- .../quads/SharedSecureStorageViewModel.kt | 1 - .../features/devtools/RoomDevToolViewModel.kt | 1 - .../features/home/HomeActivityViewModel.kt | 1 - .../room/breadcrumbs/BreadcrumbsViewModel.kt | 1 - .../invite/InviteUsersToRoomViewModel.kt | 1 - .../login2/created/AccountCreatedViewModel.kt | 7 +-- .../roompreview/RoomPreviewViewModel.kt | 1 - .../devices/DeviceListBottomSheetViewModel.kt | 1 - .../roomprofile/alias/RoomAliasViewModel.kt | 2 - .../banned/RoomBannedMemberListViewModel.kt | 2 - .../RoomNotificationSettingsViewModel.kt | 4 +- .../VectorSettingsSecurityPrivacyFragment.kt | 1 - ...iceVerificationInfoBottomSheetViewModel.kt | 1 - .../GossipingEventsPaperTrailViewModel.kt | 4 +- .../devtools/KeyRequestListViewModel.kt | 5 +- .../settings/push/PushGatewaysViewModel.kt | 5 +- .../app/features/spaces/SpaceMenuViewModel.kt | 1 - .../features/spaces/SpacesListViewModel.kt | 46 ++++++++++--------- 19 files changed, 47 insertions(+), 53 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt index b4bb01d374..d1d94cbf35 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.call.conference +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.MavericksViewModelFactory @@ -28,7 +29,11 @@ import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import io.reactivex.disposables.Disposable +import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -47,7 +52,7 @@ class JitsiCallViewModel @AssistedInject constructor( fun create(initialState: JitsiCallViewState): JitsiCallViewModel } - private var currentWidgetObserver: Disposable? = null + private var currentWidgetObserver: Job? = null private val widgetService = session.widgetService() private var confIsJoined = false @@ -59,11 +64,11 @@ class JitsiCallViewModel @AssistedInject constructor( private fun observeWidget(roomId: String, widgetId: String) { confIsJoined = false - currentWidgetObserver?.dispose() + currentWidgetObserver?.cancel() currentWidgetObserver = widgetService.getRoomWidgetsLive(roomId, QueryStringValue.Equals(widgetId), WidgetType.Jitsi.values()) - .asObservable() + .asFlow() .distinctUntilChanged() - .subscribe { + .onEach { val jitsiWidget = it.firstOrNull() if (jitsiWidget != null) { setState { @@ -81,7 +86,7 @@ class JitsiCallViewModel @AssistedInject constructor( } } } - .disposeOnClear() + .launchIn(viewModelScope) } private fun joinConference(jitsiWidget: Widget) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 151b73ff32..025fea1fb6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -45,7 +45,6 @@ import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding -import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.io.ByteArrayOutputStream diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt index 4bab65fd5d..023d1976c9 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -42,7 +42,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.di.MoshiProvider -import org.matrix.android.sdk.rx.rx class RoomDevToolViewModel @AssistedInject constructor( @Assisted val initialState: RoomDevToolViewState, diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index fa3df1ca97..0f70648d88 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -52,7 +52,6 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.asObservable -import org.matrix.android.sdk.rx.rx import timber.log.Timber import kotlin.coroutines.Continuation import kotlin.coroutines.resume diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index f32f132fe9..2fbdf7d0ec 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -31,7 +31,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.rx class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, private val session: Session) diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt index 1f723104cf..d6a6b42efa 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt @@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.rx.rx class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted initialState: InviteUsersToRoomViewState, diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt index c95434a548..34957dd47b 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt @@ -24,13 +24,14 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class AccountCreatedViewModel @AssistedInject constructor( @@ -62,7 +63,7 @@ class AccountCreatedViewModel @AssistedInject constructor( } private fun observeUser() { - session.rx() + session.flow() .liveUser(session.myUserId) .unwrap() .map { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 2635307e95..938be7c955 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -43,7 +43,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.rx import timber.log.Timber class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val initialState: RoomPreviewViewState, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index b638d84181..0b30b0487f 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -35,7 +35,6 @@ import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo -import org.matrix.android.sdk.rx.rx data class DeviceListViewState( val userItem: MatrixItem? = null, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 28a1804fab..7a4e4b44d0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -42,8 +42,6 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: RoomAliasViewState, private val session: Session) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 813d50c6bb..fd4871b682 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -40,8 +40,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, private val stringProvider: StringProvider, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index d944b77f7d..f91b482b13 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -30,8 +30,6 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap class RoomNotificationSettingsViewModel @AssistedInject constructor( @Assisted initialState: RoomNotificationSettingsViewState, @@ -74,7 +72,7 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( } private fun observeNotificationState() { - room.rx() + room.flow() .liveNotificationState() .execute { copy(notificationState = it) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 103c4ab06d..82d4e590c1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -72,7 +72,6 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse import org.matrix.android.sdk.rx.SecretsSynchronisationInfo -import org.matrix.android.sdk.rx.rx import javax.inject.Inject class VectorSettingsSecurityPrivacyFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index 38342efc46..3c8b6095e2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.map import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo -import org.matrix.android.sdk.rx.rx class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState, @Assisted val deviceId: String, diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt index e07da1065c..a00d3ebb13 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devtools +import androidx.lifecycle.asFlow import androidx.paging.PagedList import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext @@ -50,7 +51,8 @@ class GossipingEventsPaperTrailViewModel @AssistedInject constructor(@Assisted i setState { copy(events = Loading()) } - session.cryptoService().getGossipingEventsTrail().asObservable() + session.cryptoService().getGossipingEventsTrail() + .asFlow() .execute { copy(events = it) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt index 362f2768fc..9020d35081 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devtools +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import androidx.paging.PagedList import com.airbnb.mvrx.Async @@ -51,13 +52,13 @@ class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState fun refresh() { viewModelScope.launch { - session.cryptoService().getOutgoingRoomKeyRequestsPaged().asObservable() + session.cryptoService().getOutgoingRoomKeyRequestsPaged().asFlow() .execute { copy(outgoingRoomKeyRequests = it) } session.cryptoService().getIncomingRoomKeyRequestsPaged() - .asObservable() + .asFlow() .execute { copy(incomingRequests = it) } diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt index bc159e3fe0..7e6683007f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.push -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState @@ -31,7 +30,7 @@ import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.pushers.Pusher -import org.matrix.android.sdk.rx.RxSession +import org.matrix.android.sdk.flow.flow data class PushGatewayViewState( val pushGateways: Async> = Uninitialized @@ -62,7 +61,7 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: } private fun observePushers() { - RxSession(session) + session.flow() .livePushers() .execute { copy(pushGateways = it) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index bb30670da9..887c93afd4 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -43,7 +43,6 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.rx import timber.log.Timber class SpaceMenuViewModel @AssistedInject constructor( diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index 46293da209..ba789cbd4a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -34,8 +34,14 @@ import im.vector.app.features.settings.VectorPreferences import im.vector.app.group import im.vector.app.space import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.observeOn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.ActiveSpaceFilter @@ -82,14 +88,13 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp init { - session.getUserLive(session.myUserId).asObservable() - .subscribe { - setState { - copy( - myMxItem = it?.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() - ) - } - }.disposeOnClear() + session.getUserLive(session.myUserId) + .asFlow() + .setOnEach { + copy( + myMxItem = it?.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() + ) + } observeSpaceSummaries() // observeSelectionState() @@ -105,14 +110,10 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp .disposeOnClear() session.getGroupSummariesLive(groupSummaryQueryParams {}) - .asObservable() - .subscribe { - setState { - copy( - legacyGroups = it - ) - } - }.disposeOnClear() + .asFlow() + .setOnEach { + copy(legacyGroups = it) + } // XXX there should be a way to refactor this and share it session.getPagedRoomSummariesLive( @@ -122,10 +123,10 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp !vectorPreferences.prefSpacesShowAllRoomInHome() } ?: ActiveSpaceFilter.None }, sortOrder = RoomSortOrder.NONE - ).asObservable() - .throttleFirst(300, TimeUnit.MILLISECONDS) - .observeOn(Schedulers.computation()) - .subscribe { + ).asFlow() + .sample(300) + .flowOn(Dispatchers.Default) + .onEach { val inviteCount = if (autoAcceptInvites.hideInvites) { 0 } else { @@ -150,7 +151,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp homeAggregateCount = counts ) } - }.disposeOnClear() + }.launchIn(viewModelScope) } override fun handle(action: SpaceListAction) { @@ -319,7 +320,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp // clear local echos on update session.accountDataService() .getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)) - .asObservable().execute { + .asFlow() + .execute { copy( spaceOrderLocalEchos = emptyMap() ) From cccda9b699ce5493d1fc7641b923644f4ee47d7b Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 17:29:50 +0100 Subject: [PATCH 059/144] using single action for the updating the policy expanded toggling/state setting --- .../discovery/DiscoverySettingsAction.kt | 3 +-- .../discovery/DiscoverySettingsController.kt | 4 +-- .../discovery/DiscoverySettingsFragment.kt | 8 +++--- .../discovery/DiscoverySettingsViewModel.kt | 27 ++++++++----------- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt index 74700f668f..a2f855c499 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt @@ -31,6 +31,5 @@ sealed class DiscoverySettingsAction : VectorViewModelAction { data class FinalizeBind3pid(val threePid: ThreePid) : DiscoverySettingsAction() data class SubmitMsisdnToken(val threePid: ThreePid.Msisdn, val code: String) : DiscoverySettingsAction() data class CancelBinding(val threePid: ThreePid) : DiscoverySettingsAction() - object PolicyUrlsExpandedStateToggled : DiscoverySettingsAction() - object ExpandPolicyUrls : DiscoverySettingsAction() + data class SetPoliciesExpandState(val expanded: Boolean) : DiscoverySettingsAction() } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 2ed45c127b..56f345c13b 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -130,7 +130,7 @@ class DiscoverySettingsController @Inject constructor( } else R.string.settings_discovery_show_identity_server_policies_title title(host.stringProvider.getString(titleRes)) expanded(data.isIdentityPolicyUrlsExpanded) - listener { host.listener?.onPolicyUrlsExpandedStateToggled() } + listener { host.listener?.onPolicyUrlsExpandedStateToggled(!data.isIdentityPolicyUrlsExpanded) } } if (data.isIdentityPolicyUrlsExpanded) { policies.forEach { policy -> @@ -425,7 +425,7 @@ class DiscoverySettingsController @Inject constructor( fun onTapDisconnectIdentityServer() fun onTapUpdateUserConsent(newValue: Boolean) fun onTapRetryToRetrieveBindings() - fun onPolicyUrlsExpandedStateToggled() + fun onPolicyUrlsExpandedStateToggled(newExpandedState: Boolean) fun onPolicyTapped(policy: IdentityServerPolicy) } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index f7f5452d84..6de7c1fba5 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -82,7 +82,7 @@ class DiscoverySettingsFragment @Inject constructor( }.exhaustive } if (discoveryArgs.expandIdentityPolicies) { - viewModel.handle(DiscoverySettingsAction.ExpandPolicyUrls) + viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = true)) } } @@ -188,7 +188,7 @@ class DiscoverySettingsFragment @Inject constructor( withState(viewModel) { state -> requireContext().showIdentityServerConsentDialog( state.identityServer.invoke()?.serverUrl, - policyLinkCallback = { viewModel.handle(DiscoverySettingsAction.ExpandPolicyUrls) }, + policyLinkCallback = { viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = true)) }, consentCallBack = { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) } ) } @@ -201,8 +201,8 @@ class DiscoverySettingsFragment @Inject constructor( viewModel.handle(DiscoverySettingsAction.RetrieveBinding) } - override fun onPolicyUrlsExpandedStateToggled() { - viewModel.handle(DiscoverySettingsAction.PolicyUrlsExpandedStateToggled) + override fun onPolicyUrlsExpandedStateToggled(newExpandedState: Boolean) { + viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = newExpandedState)) } override fun onPolicyTapped(policy: IdentityServerPolicy) { diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 6d872e5043..8b454607d5 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -111,18 +111,17 @@ class DiscoverySettingsViewModel @AssistedInject constructor( override fun handle(action: DiscoverySettingsAction) { when (action) { - DiscoverySettingsAction.Refresh -> fetchContent() - DiscoverySettingsAction.RetrieveBinding -> retrieveBinding() - DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer() - DiscoverySettingsAction.PolicyUrlsExpandedStateToggled -> toggleExpandedPolicyUrlsState() - DiscoverySettingsAction.ExpandPolicyUrls -> updatePolicyUrlsExpandedState(isExpanded = true) - is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action) - is DiscoverySettingsAction.UpdateUserConsent -> handleUpdateUserConsent(action) - is DiscoverySettingsAction.RevokeThreePid -> revokeThreePid(action) - is DiscoverySettingsAction.ShareThreePid -> shareThreePid(action) - is DiscoverySettingsAction.FinalizeBind3pid -> finalizeBind3pid(action, true) - is DiscoverySettingsAction.SubmitMsisdnToken -> submitMsisdnToken(action) - is DiscoverySettingsAction.CancelBinding -> cancelBinding(action) + DiscoverySettingsAction.Refresh -> fetchContent() + DiscoverySettingsAction.RetrieveBinding -> retrieveBinding() + DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer() + is DiscoverySettingsAction.SetPoliciesExpandState -> updatePolicyUrlsExpandedState(action.expanded) + is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action) + is DiscoverySettingsAction.UpdateUserConsent -> handleUpdateUserConsent(action) + is DiscoverySettingsAction.RevokeThreePid -> revokeThreePid(action) + is DiscoverySettingsAction.ShareThreePid -> shareThreePid(action) + is DiscoverySettingsAction.FinalizeBind3pid -> finalizeBind3pid(action, true) + is DiscoverySettingsAction.SubmitMsisdnToken -> submitMsisdnToken(action) + is DiscoverySettingsAction.CancelBinding -> cancelBinding(action) }.exhaustive } @@ -149,10 +148,6 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } } - private fun toggleExpandedPolicyUrlsState() { - withState { state -> updatePolicyUrlsExpandedState(isExpanded = !state.isIdentityPolicyUrlsExpanded) } - } - private fun updatePolicyUrlsExpandedState(isExpanded: Boolean) { setState { copy(isIdentityPolicyUrlsExpanded = isExpanded) } } From d00858f83bb8d4a9dc52b2aec11c2968b88446d5 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 17:44:54 +0100 Subject: [PATCH 060/144] handling empty policy list by showing empty copy --- .../discovery/DiscoverySettingsController.kt | 19 +++++++++++++------ vector/src/main/res/values/strings.xml | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 56f345c13b..9b557ab675 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -133,12 +133,19 @@ class DiscoverySettingsController @Inject constructor( listener { host.listener?.onPolicyUrlsExpandedStateToggled(!data.isIdentityPolicyUrlsExpanded) } } if (data.isIdentityPolicyUrlsExpanded) { - policies.forEach { policy -> - discoveryPolicyItem { - id(policy.url) - name(policy.name) - url(policy.url) - clickListener { host.listener?.onPolicyTapped(policy) } + if (policies.isEmpty()) { + settingsInfoItem { + id("emptyPolicy") + helperText(host.stringProvider.getString(R.string.settings_discovery_no_policy_provided)) + } + } else { + policies.forEach { policy -> + discoveryPolicyItem { + id(policy.url) + name(policy.name) + url(policy.url) + clickListener { host.listener?.onPolicyTapped(policy) } + } } } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 9cce711e29..a4e079ee98 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2351,6 +2351,7 @@ Change identity server Show identity server policy Hide identity server policy + No policy provided by the identity server You are currently using %1$s to discover and be discoverable by existing contacts you know. You are not currently using an identity server. To discover and be discoverable by existing contacts you know, configure one below. Discoverable email addresses From 259b6d56d7e0b70f13c9c3365135bc2a32944fd2 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 17:45:30 +0100 Subject: [PATCH 061/144] using singular for the policy copy name to match content --- .../app/features/discovery/DiscoverySettingsController.kt | 4 ++-- vector/src/main/res/values/strings.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 9b557ab675..d8c67592f1 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -126,8 +126,8 @@ class DiscoverySettingsController @Inject constructor( formAdvancedToggleItem { id("policy-urls") val titleRes = if (data.isIdentityPolicyUrlsExpanded) { - R.string.settings_discovery_hide_identity_server_policies_title - } else R.string.settings_discovery_show_identity_server_policies_title + R.string.settings_discovery_hide_identity_server_policy_title + } else R.string.settings_discovery_show_identity_server_policy_title title(host.stringProvider.getString(titleRes)) expanded(data.isIdentityPolicyUrlsExpanded) listener { host.listener?.onPolicyUrlsExpandedStateToggled(!data.isIdentityPolicyUrlsExpanded) } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index a4e079ee98..50306f1528 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2349,8 +2349,8 @@ Configure identity server Open Discovery Settings Change identity server - Show identity server policy - Hide identity server policy + Show identity server policy + Hide identity server policy No policy provided by the identity server You are currently using %1$s to discover and be discoverable by existing contacts you know. You are not currently using an identity server. To discover and be discoverable by existing contacts you know, configure one below. From cb7260954aa1f1469ab6d4a72a89ab070a9e6ef9 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 7 Oct 2021 17:46:33 +0100 Subject: [PATCH 062/144] renaming xml view to avoid clashing with other file --- vector/src/main/res/layout/item_discovery_policy.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/layout/item_discovery_policy.xml b/vector/src/main/res/layout/item_discovery_policy.xml index 4630273904..cb2ac51142 100644 --- a/vector/src/main/res/layout/item_discovery_policy.xml +++ b/vector/src/main/res/layout/item_discovery_policy.xml @@ -16,7 +16,7 @@ android:paddingEnd="8dp" android:textColor="?vctr_content_primary" android:textStyle="bold" - app:layout_constraintEnd_toStartOf="@id/term_policy_arrow" + app:layout_constraintEnd_toStartOf="@id/discovery_policy_arrow" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="Integration manager" /> @@ -29,14 +29,14 @@ android:paddingStart="0dp" android:paddingEnd="8dp" android:textColor="?vctr_content_secondary" - app:layout_constraintEnd_toStartOf="@id/term_policy_arrow" + app:layout_constraintEnd_toStartOf="@id/discovery_policy_arrow" app:layout_constraintStart_toStartOf="@+id/discovery_policy_name" app:layout_constraintTop_toBottomOf="@+id/discovery_policy_name" tools:text="Use bots, bridges, widget and sticker packs." /> Date: Thu, 7 Oct 2021 17:56:54 +0100 Subject: [PATCH 063/144] reducing the discovery policy url text size by using caption style --- vector/src/main/res/layout/item_discovery_policy.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/layout/item_discovery_policy.xml b/vector/src/main/res/layout/item_discovery_policy.xml index cb2ac51142..1c03133111 100644 --- a/vector/src/main/res/layout/item_discovery_policy.xml +++ b/vector/src/main/res/layout/item_discovery_policy.xml @@ -23,7 +23,7 @@ Date: Thu, 7 Oct 2021 20:58:28 +0200 Subject: [PATCH 064/144] Add changelog for #4184 --- changelog.d/4184.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4184.feature diff --git a/changelog.d/4184.feature b/changelog.d/4184.feature new file mode 100644 index 0000000000..d25f7e258f --- /dev/null +++ b/changelog.d/4184.feature @@ -0,0 +1 @@ +Display identity server policies in the Discovery screen \ No newline at end of file From c66d6aab5ce3751714b1458afd8d0c75d80e5ab3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 8 Oct 2021 12:55:37 +0200 Subject: [PATCH 065/144] Timeline: dispatch update on a background thread --- .../timeline/TimelineEventController.kt | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index 5a37a343e6..d320f0b6e0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -244,20 +244,22 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec interceptorHelper.intercept(models, partialState.unreadState, timeline, callback) } - fun update(viewState: RoomDetailViewState) = synchronized(modelCache) { - val newPartialState = PartialState(viewState) - if (partialState.highlightedEventId != newPartialState.highlightedEventId) { - // Clear cache to force a refresh - for (i in 0 until modelCache.size) { - if (modelCache[i]?.eventId == viewState.highlightedEventId || - modelCache[i]?.eventId == partialState.highlightedEventId) { - modelCache[i] = null + fun update(viewState: RoomDetailViewState) = backgroundHandler.post { + synchronized(modelCache) { + val newPartialState = PartialState(viewState) + if (partialState.highlightedEventId != newPartialState.highlightedEventId) { + // Clear cache to force a refresh + for (i in 0 until modelCache.size) { + if (modelCache[i]?.eventId == viewState.highlightedEventId || + modelCache[i]?.eventId == partialState.highlightedEventId) { + modelCache[i] = null + } } } - } - if (newPartialState != partialState) { - partialState = newPartialState - requestModelBuild() + if (newPartialState != partialState) { + partialState = newPartialState + requestModelBuild() + } } } From 999a08c0f5c206f17b0ec5263098d2b2db2d8cc7 Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Fri, 8 Oct 2021 12:11:10 +0000 Subject: [PATCH 066/144] Translated using Weblate (Swedish) Currently translated at 98.4% (2617 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sv/ --- vector/src/main/res/values-sv/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vector/src/main/res/values-sv/strings.xml b/vector/src/main/res/values-sv/strings.xml index 0310c056eb..4add831fcf 100644 --- a/vector/src/main/res/values-sv/strings.xml +++ b/vector/src/main/res/values-sv/strings.xml @@ -2947,4 +2947,10 @@ Samtal ringer… Utrymmen Lär dig mer + Utrymmesbehörigheter + Om användaren avbannas kommer denna att få gå med i utrymmet igen. + Att banna användaren kommer att kicka denne från det här utrymmet och hindra hen från att gå med igen. + att kicka användaren kommer att ta bort den från det här utrymmet. +\n +\nFör att hindra hen från att gå med igen så bör de banna hen istället. \ No newline at end of file From bf219856a503ce44d821093733889c8f90ed6f7f Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 8 Oct 2021 02:01:14 +0000 Subject: [PATCH 067/144] Translated using Weblate (Ukrainian) Currently translated at 91.7% (2440 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- vector/src/main/res/values-uk/strings.xml | 64 ++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index dfa0206a34..b4cf230eea 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -1832,7 +1832,7 @@ Скинути все Не вдалося розшифрувати резервну копію цією парольною фразою: переконайтесь, що вказано правильну парольну фразу відновлення. Не знаєте вашої відновлювальної парольної фрази\? Ви можете %s. - Відновлювальна парольна фраза + Парольна фраза відновлення Забули або втратили усі можливості для відновлення\? Скинути все Використати файл Скористатись парольною фразою відновлення або ключем @@ -2800,4 +2800,66 @@ Ваш обліковий запис ще не створено. \n \nЗупинити процес реєстрації\? + Попередження: + Підтвердити вилучення + Запити ключів + ${app_name} Android + Увімкнути камеру + Вимкнути камеру + Додати людей + Додати учасників + Перевірте це посилання + Перевірте, де ви ввійшли + Інтерактивна перевірка за допомогою емоджі + Виберіть пароль. + Виберіть ім\'я користувача. + Не вдалося налаштувати перехресне підписування + Звірити власноруч за допомогою тексту + Не вдалося отримати доступ до безпечного сховища + ${app_name} iOS +\n${app_name} Android + ${app_name} для переглядача +\n${app_name} для ПК + Не вдалося зберегти медіафайл + Не вдалося додати медіафайл до галереї + Медіафайл додано до галереї + %1$s (%2$s) + Це не дійсний ключ відновлення + Парольна фраза відновлення + Введіть %s + Введіть %s, щоб продовжити + Перевірте себе та інших, щоб ваші бесіди були безпечними + Увімкнути перехресне підписування + Цей обліковий запис деактивовано. + Не вдалося імпортувати ключі + Ви створили та налаштували кімнату. + %s створює та налаштовує кімнату. + Шифрування, застосоване цією кімнатою не підтримується + Шифрування не увімкнено + Ви не можете зробити це з мобільного + Скопіювати у своє особисте хмарне сховище + Зберегти на USB-накопичувач або резервний диск + Надрукуйте його та зберігайте у безпечному місці + Ваші %2$s та %1$s налаштовано. +\n +\nТримайте їх у безпеці! Вони знадобляться вам, щоб розблокувати зашифровані повідомлення та захистити дані, якщо ви втратите всі активні сеанси. + Налаштування резервного копіювання ключів + Синхронізація самопідписаного ключа + Синхронізація користувацького ключа + Синхронізація головного ключа + Визначення типового ключа SSSS + Генерування ключів безпеки з парольної фрази + Публікування створених ключів ідентифікації + Використовуйте %1$s запаскою на випадок втрати %2$s. + Зберігайте його у надійному місці + Усе готово! + Ваш ключ відновлення + Налаштування відновлення. + Це може тривати кілька секунд, будь ласка, зачекайте. + Таємна фраза + Введіть таємну фразу, яку знаєте лише ви, яка використовується для захисту таємниць на вашому сервері. + Введіть таємну фразу, яку знаєте лише ви, яка використовується для захисту таємниць на вашому сервері. + Не застосовуйте пароль облікового запису повторно. + Введіть %s ще раз, щоб підтвердити. + Захистіть та розблокуйте зашифровані повідомлення та перевіряйте довіреність за допомогою %s. \ No newline at end of file From 5a5832dab718817a66128766fc69db209ae914ed Mon Sep 17 00:00:00 2001 From: Slimane Selyan AMIRI Date: Thu, 7 Oct 2021 16:44:18 +0000 Subject: [PATCH 068/144] Translated using Weblate (Kabyle) Currently translated at 81.0% (2154 of 2658 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/kab/ --- vector/src/main/res/values-kab/strings.xml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/vector/src/main/res/values-kab/strings.xml b/vector/src/main/res/values-kab/strings.xml index 53b4ee9214..53e883baf6 100644 --- a/vector/src/main/res/values-kab/strings.xml +++ b/vector/src/main/res/values-kab/strings.xml @@ -1244,8 +1244,8 @@ Seqdec tasarut n tɣellist Sirew tasarut n tɣellist ara tḥerzeḍ deg wadeg yettwaḍemnen, am yimsefrak n wawalen uufiren neɣ deg usenduq. Sbadu tafyirt taɣelsant - Kles tasarut-ik·im n tɣellist deg wadeg yettwaḍemnen, am umsefrak n wawalen uffiren neɣ neɣ usenduq. - Kles tasarut-ik·im n tɣellist deg wadeg yettwaḍemnen, am umsefrak n wawalen uffiren neɣ neɣ usenduq. + Kles tasarut n tɣellist deg wadeg yettwaḍemnen, am umsefrak n wawalen uffiren neɣ usenduq. + Kles tasarut n tɣellist deg wadeg yettwaḍemnen, am umsefrak n wawalen uffiren neɣ usenduq. Tbeddleḍ iɣewwaren n texxamt akken iwata Ur tezmireḍ ara ad tkecmeḍ ɣer izen-a Aṛaǧu i umazray n uwgelhen @@ -1327,7 +1327,6 @@ \n \nMa ulac aɣilif sireg anekcum ɣer isfuyla udhimen i d-iteddun i wakken tizmireḍ ad tessiwleḍ." ${app_name} yesra tasiregt n unekcum ɣer temkarḍit-inek·inem n tewlafin d tvidyut i tuzna d usekles n tceqqufin yeddan. -\n \n \nMa ulac aɣilif sireg anekcum deg yisfuyla udhimen i d-iteddun i wakken ad tizmireḍ ad tazneḍ ifuyla seg tiliɣri-inek·inem. ${app_name} yezmer ad issenqed adlis-inek·inem n tansiwin i wakken ad d-yaf iseqdacen-nniḍen n Matrix s usenned ɣer yimaylen d wuṭṭunen n tiliɣri nsen. @@ -1372,7 +1371,7 @@ Iznan deg usqerdec gar sin kan Iznan n yisqerdicen n ugraw Amtawi n ugilal - Askar n umatawi n ugilal (D armitan) + Askar n umatawi n ugilal Ulac amtawi n ugilal Bdu seg usenker Rmed amtawi n ugilal @@ -1401,7 +1400,8 @@ Awiǧit-a yettwarna sɣur: Aseqdec-is yezmer ad yesbadu inagan n tuqqna yerna ad yebḍu isefka d %s: Aseqdec-is yezmer ad yebḍu isefka d %s: - Ur yeddi ara usali n yiwiǧit%s + Ur yeddi ara usali n yiwiǧit. +\n%s Ales asali n yiwiǧit Ldi deg yiminig Sefesex anekcum i nekk @@ -1643,7 +1643,6 @@ \n%1$s Ilugan n ugilal ttwaremden i ${app_name}. \nAmahil i yettaɛraḍ usnas ad t-yeg yesɛa talast ma mazal-it yella ɣef ugilal, aya yezmer ad d-yawi ugur i yilɣa. -\n \n%1$s Sens ilugan Asefrer n tbatrit @@ -1836,7 +1835,7 @@ Kkes tisura-inek·inem n uwgelhen yettwaḥerzen ɣef uqeddac\? Ur tettuɣaleḍ ara ad tizmireḍ ad tesqedceḍ tasarut-ik·im n tririt i tɣuri n umazray n yiznan yettwawgelhen. Aḥraz n tsarut amaynut n yizen aɣelsan tettwaf-d. \n -\nMa yella ur tesbaduḍ ara tarrayt n tririt tamaynut, amker yezmer ad yeɛreḍ ad yekcem ɣer umiḍan-ik·im. Snifel awal uffir n umiḍan-ik·im syen sbadu tarrayt n tririt tamaynut din din deg yiɣewwaren. +\nMa yella ur tesbaduḍ ara tarrayt n tririt tamaynut, amker yezmer ad yeɛreḍ ad yekcem ɣer umiḍan-ik·im. Snifel awal uffir n umiḍan-ik·im syen sbadu tarrayt n tririt tamaynut din-din deg yiɣewwaren. Aḥraz aɣelsan Seḥbiber iman-ik·im mgal asruḥu n unekcum ɣer yiznann & yisefka yettwawgelhen Tisura timaynutin n yizen aɣelsan @@ -2151,7 +2150,7 @@ \n- Ibenk-a, neɣ ibenk-nniḍen \n- Tuqqna ɣer internet yettuseqdacen seg yal ibenk \n -\nAd ak·akem-nwelleh ad tesnifleḍ awal-ik·im uffir & tsarut n tririt deg yiɣewwaren tura tura. +\nAd ak·akem-nwelleh ad tesnifleḍ awal-ik·im uffir & tsarut n tririt deg yiɣewwaren tura. Senqed ibenkan-ik·im seg yiɣewwaren. Yettwasefsex usenqed Tefyirt tuffirt n tririt From 090273da14c2f8c79e85cc3d4b771d7b8cf2ae9c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Oct 2021 14:55:50 +0200 Subject: [PATCH 069/144] Fix lint error --- vector/src/main/res/values-nl/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-nl/strings.xml b/vector/src/main/res/values-nl/strings.xml index efa9228af5..98ce426c8c 100644 --- a/vector/src/main/res/values-nl/strings.xml +++ b/vector/src/main/res/values-nl/strings.xml @@ -1960,8 +1960,8 @@ %s heeft de oproep in de wacht gezet Teruggaan naar oproep Actieve Oproep (%s) - Bellen met - Videobellen met + Bellen met %s + Videobellen met %s Gemiste video-oproep %d gemiste video-oproepen From 66d4a48930db9f2036e65ded7462a9d378ba91a2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Oct 2021 15:07:52 +0200 Subject: [PATCH 070/144] Remove unused strings --- .../im/vector/app/features/userdirectory/UserListFragment.kt | 3 --- vector/src/main/res/layout/fragment_user_list.xml | 2 +- vector/src/main/res/values-ar/strings.xml | 2 -- vector/src/main/res/values-ca/strings.xml | 1 - vector/src/main/res/values-cs/strings.xml | 3 --- vector/src/main/res/values-de/strings.xml | 3 --- vector/src/main/res/values-eo/strings.xml | 3 --- vector/src/main/res/values-es/strings.xml | 3 --- vector/src/main/res/values-et/strings.xml | 3 --- vector/src/main/res/values-fa/strings.xml | 3 --- vector/src/main/res/values-fi/strings.xml | 1 - vector/src/main/res/values-fr-rCA/strings.xml | 3 --- vector/src/main/res/values-fr/strings.xml | 3 --- vector/src/main/res/values-hu/strings.xml | 3 --- vector/src/main/res/values-in/strings.xml | 2 -- vector/src/main/res/values-it/strings.xml | 3 --- vector/src/main/res/values-iw/strings.xml | 1 - vector/src/main/res/values-ja/strings.xml | 1 - vector/src/main/res/values-lv/strings.xml | 1 - vector/src/main/res/values-nb-rNO/strings.xml | 1 - vector/src/main/res/values-pl/strings.xml | 1 - vector/src/main/res/values-pt-rBR/strings.xml | 3 --- vector/src/main/res/values-ru/strings.xml | 3 --- vector/src/main/res/values-sq/strings.xml | 3 --- vector/src/main/res/values-sv/strings.xml | 3 --- vector/src/main/res/values-tr/strings.xml | 1 - vector/src/main/res/values-uk/strings.xml | 2 -- vector/src/main/res/values-vi/strings.xml | 1 - vector/src/main/res/values-zh-rCN/strings.xml | 3 --- vector/src/main/res/values-zh-rTW/strings.xml | 3 --- vector/src/main/res/values/strings.xml | 4 ---- 31 files changed, 1 insertion(+), 71 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index c1fd878013..e6d64c799f 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -132,9 +132,6 @@ class UserListFragment @Inject constructor( } private fun setupSearchView() { - withState(viewModel) { - views.userListSearch.hint = getString(R.string.user_directory_search_hint_2) - } views.userListSearch .textChanges() .startWith(views.userListSearch.text) diff --git a/vector/src/main/res/layout/fragment_user_list.xml b/vector/src/main/res/layout/fragment_user_list.xml index 96cad5b7e9..36e62d2b31 100644 --- a/vector/src/main/res/layout/fragment_user_list.xml +++ b/vector/src/main/res/layout/fragment_user_list.xml @@ -89,7 +89,7 @@ android:background="@null" android:drawablePadding="8dp" android:gravity="center_vertical" - android:hint="@string/user_directory_search_hint" + android:hint="@string/user_directory_search_hint_2" android:importantForAutofill="no" android:inputType="text" android:maxHeight="80dp" diff --git a/vector/src/main/res/values-ar/strings.xml b/vector/src/main/res/values-ar/strings.xml index 2ad5d7dde9..a37fad08d2 100644 --- a/vector/src/main/res/values-ar/strings.xml +++ b/vector/src/main/res/values-ar/strings.xml @@ -1166,8 +1166,6 @@ استكشِف الغُرف أضف غُرف غادر المساحة - - هل أنت متأكد أنك تريد مغادرة المساحة؟ انت الشخص الوحيد هنا إذا غادرت، فلن يتمكن أي شخص من الانضمام في المستقبل، بما في ذلك أنت. هذه المساحة ليست عامة. لن تتمكن من الانضمام مرة أخرى بدون دعوة. أنت المسؤول عن هذه المساحة، تأكد من أنك قمت بنقل حق المسؤول إلى عضو آخر قبل المغادرة. diff --git a/vector/src/main/res/values-ca/strings.xml b/vector/src/main/res/values-ca/strings.xml index 6a68166bb7..9f8f2a98b0 100644 --- a/vector/src/main/res/values-ca/strings.xml +++ b/vector/src/main/res/values-ca/strings.xml @@ -1700,7 +1700,6 @@ Cronologia Missatges no llegits Activa lliscar per respondre a la cronologia - Cerca a partir del nom o l\'ID Nom o ID (#exemple:matrix.org) Mostra el directori de la sales Crea una nova sala diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index ec0b783464..e13a903c5e 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -2543,7 +2543,6 @@ Poslední QR kód Přidat pomocí QR kódu - Hledat podle jména nebo ID Udělte právo přístupu ke kontaktům. Pro sken QR kódu je nutné povolit přístup k fotoaparátu. Poslat historii požadavků na sdílení klíčů @@ -2737,8 +2736,6 @@ Jste zváni Vítejte ve Spaces! Přidat existující místnosti a prostor - - Jste si jisti, že chcete opustit tento prostor\? Opustit prostor Přidat místnosti Prozkoumat místnosti diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml index 62db877318..73f0a9eb70 100644 --- a/vector/src/main/res/values-de/strings.xml +++ b/vector/src/main/res/values-de/strings.xml @@ -2523,7 +2523,6 @@ Kürzlich QR-Code Hinzufügen via QR-Code - Nach Name oder ID suchen Gib die Erlaubnis, um auf die Kamera zu zugreifen. Um den QR-Code zu scannen, muss der Zugriff auf die Kamera erlaubt werden. Öffentliche Adressen @@ -2770,8 +2769,6 @@ Verlasse den Raum mit der angegebenen ID (oder den aktuellen Raum, wenn keine ID angegeben wird) Name suchen Du wurdest eingeladen - - Bist du dir sicher, dass du den Space verlassen willst\? Space verlassen Räume hinzufügen Räume erkunden diff --git a/vector/src/main/res/values-eo/strings.xml b/vector/src/main/res/values-eo/strings.xml index 05fd62b90b..c8f0f5fd02 100644 --- a/vector/src/main/res/values-eo/strings.xml +++ b/vector/src/main/res/values-eo/strings.xml @@ -2596,7 +2596,6 @@ Rapidresponda kodo (QR) Aldoni per rapidresponda kodo (QR) Serĉi per nomo - Serĉi per nomo aŭ identigilo Densigante filmon %d%% Densigante bildon… Prikomenti @@ -2674,8 +2673,6 @@ Vi estas la sola administranto de ĉi tiu aro. Se vi foriros, neniu povos ĝin regi. Vi ne povos ree aliĝi sen invito. Vi estas la sola persono ĉi tie. Se vi foriros, neniu plu povos aliĝi, inkluzive vin mem. - - Ĉu vi certe volas foriri de la aro\? Foriri de aro Aldoni ĉambrojn Esplori ĉambrojn diff --git a/vector/src/main/res/values-es/strings.xml b/vector/src/main/res/values-es/strings.xml index 7af1373a53..c2f77e5afc 100644 --- a/vector/src/main/res/values-es/strings.xml +++ b/vector/src/main/res/values-es/strings.xml @@ -2551,7 +2551,6 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Mostrar teclado emoji Reciente Añadir mediante código QR - Buscar por nick o ID Código QR Sugerencias Contactos @@ -2619,8 +2618,6 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Espacio Experimental - Sala Restringida. Estas invitado Añadir salas - - Estas seguro de que quieres salir de este espacio\? Salir de este espacio Añadir salas Explorar salas diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml index a39ab26a2a..288e9246c2 100644 --- a/vector/src/main/res/values-et/strings.xml +++ b/vector/src/main/res/values-et/strings.xml @@ -2497,7 +2497,6 @@ Hiljutised QR-kood Lisa QR-koodi abil - Otsi nime või Matrix\'i tunnuse alusel Anna õigused oma kontakte lugeda. QR-koodi lugemiseks pead selleks kaamerale õigused andma. Alusta vestlust @@ -2679,8 +2678,6 @@ Kogukonnakeskused on uus võimalus siduda jututubasid ja inimesi. Tere tulemast kasutama kogukonnakeskuseid! Lisa olemasolevaid jututubasid ja kogukonnakeskuseid - - Kas oled kindel, et soovid lahkuda kogukonnakeskusest\? Lahku kogukonnakeskusest Lisa jututuba Uuri jututubasid diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index 9ce4769bcc..a2a3d856fc 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -2437,7 +2437,6 @@ اخیر رمز QR افزودن با رمز QR - جست‌وجو با نام یا شناسه دادن اجازهٔ دسترسی به آشنایانتان. برای پویش یک رمز QR نیاز است دسترسی به دوربین را مجاز کنید. آغاز به گپ @@ -2679,8 +2678,6 @@ فضاها شیوه‌ای جدید برای گروه‌بندی اتاق‌ها و افراد است. به فضاها خوش آمدید! افزودن فضا و اتاق‌های موجود - - مطمئنید که می‌خواهید فضا را ترک کنید؟ ترک فضا افزودن اتاق کاوش در اتاق‌ها diff --git a/vector/src/main/res/values-fi/strings.xml b/vector/src/main/res/values-fi/strings.xml index 28a576613e..634c4027b0 100644 --- a/vector/src/main/res/values-fi/strings.xml +++ b/vector/src/main/res/values-fi/strings.xml @@ -2078,7 +2078,6 @@ Aloita keskustelu Poista suosikeista Lisää suosikiksi - Hae nimellä tai käyttäjänimellä Ei aktiivisia sovelmia %1$s: %2$s %3$s %1$s: %2$s diff --git a/vector/src/main/res/values-fr-rCA/strings.xml b/vector/src/main/res/values-fr-rCA/strings.xml index 204580c842..cf9c07efb6 100644 --- a/vector/src/main/res/values-fr-rCA/strings.xml +++ b/vector/src/main/res/values-fr-rCA/strings.xml @@ -343,7 +343,6 @@ Lien copié dans le presse-papiers Ajouter un onglet dédié aux notifications non-lues sur l’écran principal. Activer le balayage pour répondre dans l’historique - Chercher par nom ou identifiant Nom ou identifiant (#exemple:matrix.org) Voir le répertoire des salons Envoyer un nouveau message privé @@ -2748,8 +2747,6 @@ Vous êtes admin de cet espace, assurez-vous d’avoir transféré les droits d’admin à un autre membre avant de partir. Cet espace n’est pas public. Vous ne pourrez pas le rejoindre sans invitation. Vous êtes la seule personne ici. Si vous partez, personne ne pourra entrer à l’avenir, même pas vous. - - Voulez-vous vraiment quitter l’espace\? Quitter l’espace Ajouter des salons Parcourir les salons diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml index bb8bca76cd..710145c63b 100644 --- a/vector/src/main/res/values-fr/strings.xml +++ b/vector/src/main/res/values-fr/strings.xml @@ -2452,7 +2452,6 @@ Récent code QR Ajouter avec un code QR - Chercher par nom ou identifiant Autoriser l’accès à vos contacts. Pour scanner un code QR, vous devez autoriser l’accès à votre appareil photo. Commencer une conversation @@ -2708,8 +2707,6 @@ Les espaces sont une nouvelle manière de regrouper les salons et les gens. Bienvenue dans les espaces ! Ajouter des salons et espaces existants - - Voulez-vous vraiment quitter l’espace \? Quitter l’espace Ajouter des salons Parcourir les salons diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml index 8635e8bf75..55b5dcb0c7 100644 --- a/vector/src/main/res/values-hu/strings.xml +++ b/vector/src/main/res/values-hu/strings.xml @@ -2306,7 +2306,6 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró Itt kezdődik ez a beszélgetés. Ez a %s szoba kezdete. Ismert felhasználók - Keresés névvel, vagy Matrix ID-vel Reakció: %s Hozzáadás Befejezés @@ -2490,8 +2489,6 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró Ön az egyetlen adminisztrátora a térnek. Ha kilép, senki nem tudja irányítani. Amíg nem hívnak meg újra nem tudsz újracsatlakozni. Csak te van itt. Ha kilépsz, akkor a jövőben senki nem tud majd ide belépni, beleértve téged is. - - Biztos el akarod hagyni a teret\? Tér elhagyása Szobák hozzáadása Szobák felderítése diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 295d7427a2..6875ba6782 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -1770,7 +1770,6 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Tautan disalin ke klipboard Aktifkan geser untuk balas di linimasa Cari Nama - Cari dengan nama atau ID Nama atau ID (#contoh:matrix.org) Tampilkan direktori ruangan Kirim pesan langsung baru @@ -2396,7 +2395,6 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Anda adalah admin satu-satunya di space ini. Meninggalkannya berarti siapa saja tidak akan mempunyai kontrol atas space-nya. Anda tidak akan bisa bergabung lagi kecuali jika Anda diundang lagi. Anda hanya salah satu orang di sini. Jika Anda tinggalkan, siapa saja tidak dapat bergabung di masa depan, termasuk Anda. - Apakah Anda yakin untuk meninggalkan space ini\? Apakah Anda yakin untuk meninggalkan %s\? Tinggalkan Space Tambahkan ruangan diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml index 67fc33c28e..2262774b1e 100644 --- a/vector/src/main/res/values-it/strings.xml +++ b/vector/src/main/res/values-it/strings.xml @@ -2550,7 +2550,6 @@ Recenti Codice QR Aggiungi da codice QR - Cerca per nome o ID Per scansionare un codice QR devi permettere l\'accesso alla fotocamera. Autorizza ad accedere ai tuoi contatti. Inizia a chattare @@ -2732,8 +2731,6 @@ Gli Spazi sono un nuovo modo per raggruppare stanze e contatti. Benvenuto negli Spazi! Aggiungi stanze e Spazi esistenti - - Vuoi veramente uscire dallo Spazio\? Esci dallo Spazio Aggiungi stanze Guarda le stanze diff --git a/vector/src/main/res/values-iw/strings.xml b/vector/src/main/res/values-iw/strings.xml index 41d6e147c5..f6c38a6a0f 100644 --- a/vector/src/main/res/values-iw/strings.xml +++ b/vector/src/main/res/values-iw/strings.xml @@ -1602,7 +1602,6 @@ הקישור הועתק ללוח הוסף כרטיסייה ייעודית להתראות שלא נקראו על המסך הראשי. אפשר החלקה כדי להשיב בציר הזמן - חפש לפי שם או תעודת זהות שם או מזהה (# לדוגמא: matrix.ahava528.com) צפו בספריית החדרים שלח הודעה ישירה חדשה diff --git a/vector/src/main/res/values-ja/strings.xml b/vector/src/main/res/values-ja/strings.xml index 96324c2058..135c0eb09b 100644 --- a/vector/src/main/res/values-ja/strings.xml +++ b/vector/src/main/res/values-ja/strings.xml @@ -1194,7 +1194,6 @@ ${app_name}からあなた個人の電話帳への検索要求を許可する場 コードを共有 ${app_name} で会話しましょう: %s フレンドを招待 - 名前または ID で検索 既知のユーザー 無効なQRコード (無効な URI)! パスワードが一致しません diff --git a/vector/src/main/res/values-lv/strings.xml b/vector/src/main/res/values-lv/strings.xml index 126942b645..bde0afd0eb 100644 --- a/vector/src/main/res/values-lv/strings.xml +++ b/vector/src/main/res/values-lv/strings.xml @@ -1074,7 +1074,6 @@ Nākotnē šī pārbaudes procedūra plānota sarežģītāka. Kamera Kods Zināmie lietotāji - Meklēt pēc nosaukuma vai ID Gaida… Tiešās ziņas Ieteikumu neizdevās nosūtīt (%s) diff --git a/vector/src/main/res/values-nb-rNO/strings.xml b/vector/src/main/res/values-nb-rNO/strings.xml index 822b37f2d7..454e946f5a 100644 --- a/vector/src/main/res/values-nb-rNO/strings.xml +++ b/vector/src/main/res/values-nb-rNO/strings.xml @@ -1205,7 +1205,6 @@ Ingen endringer funnet Finner du ikke det du leter etter\? Send en ny direkte melding - Søk etter navn eller ID Aktiver sveip for å svare på tidslinjen Link kopiert til utklippstavlen Legg til med matrix-ID diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 902c0df49e..75a6938bc0 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2125,7 +2125,6 @@ Spróbuj uruchomić ponownie aplikację. Kod QR Dodaj poprzez kod QR Dodaj dedykowaną kartę dla nieprzeczytanych powiadomień na ekranie głównym. - Szukaj poprzez imię bądź ID Pokazuj całą historię w zaszyfrowanych pokojach Ten pokój został utworzony ale niektóre zaproszenia nie zostały wysłane z następującego powodu: \n diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index 04cee3ef62..a9bc57047b 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -2567,7 +2567,6 @@ Usuárias(os) Conhecidas(os) QR code Adicionar por QR code - Pesquisar por nome ou ID Aceitar permissão para acessar seus contatos. Para scannear um QR code, você precisa permitir acesso a câmera. Começar a Conversar @@ -2815,8 +2814,6 @@ Você é a/o única(o) admin deste espaço. Sair dele vai significar que ninguém tem controle sobre ele. Você não vai ser capaz de se rejuntar a menos que você seja re-convidada(o). Você é a única pessoa aqui. Se você sair, ninguém vai ser capaz de se juntar no futuro, incluindo você. - - Você tem certeza que você quer sair do espaço\? Sair de Espaço Adicionar salas Explorar salas diff --git a/vector/src/main/res/values-ru/strings.xml b/vector/src/main/res/values-ru/strings.xml index 82cddb03cf..cf25de8f27 100644 --- a/vector/src/main/res/values-ru/strings.xml +++ b/vector/src/main/res/values-ru/strings.xml @@ -2586,7 +2586,6 @@ Название комнаты Вы дали свое согласие на отправку электронных писем и телефонных номеров на этот сервер идентификации для обнаружения других пользователей из ваших контактов. Добавить по QR-коду - Поиск по имени или идентификатору Разрешить доступ к вашим контактам. Чтобы отсканировать QR-код, вам нужно разрешить доступ к камере. Ссылка Matrix @@ -2868,8 +2867,6 @@ Вы являетесь администратором этого пространства, перед уходом убедитесь, что передали права администратора другому пользователю. Это пространство не является публичным. Вы не сможете присоединиться к нему без приглашения. Вы здесь единственный человек. Если вы уйдёте, никто не сможет присоединиться в будущем, включая вас. - - Вы уверены, что хотите покинуть пространство\? Покинуть пространство Добавить комнаты Список комнат diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index 182b3a1415..5e399ebe84 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -2483,7 +2483,6 @@ Përdorues të Ditur Kod QR Shtoni përmes kodi QR - Kërkoni sipas emri ose ID-je Që të skanoni një kod QR, lypset të lejoni përdorim kamere. Filloni të Llafoseni Jepni leje për hyrje te kontaktet tuaja. @@ -2668,8 +2667,6 @@ Jeni ftuar Mirë se vini te Hapësira! Shtoni dhoma ekzistuese dhe hapësira - - Jeni i sigurt se doni të dilni nga hapësira\? Braktiseni Hapësirën Shtoni dhoma Eksploroni dhoma diff --git a/vector/src/main/res/values-sv/strings.xml b/vector/src/main/res/values-sv/strings.xml index 4add831fcf..3fc05c4d40 100644 --- a/vector/src/main/res/values-sv/strings.xml +++ b/vector/src/main/res/values-sv/strings.xml @@ -2504,7 +2504,6 @@ Senaste QR-kod Lägg till med QR-kod - Sök med namn eller ID Det här rummet kan inte förhandsgranskas. Vill du gå med i det\? Det här rummet är inte åtkomligt just nu. \nFörsök igen senare, eller be en rumsadministratör för att kolla om du har åtkomst. @@ -2737,8 +2736,6 @@ Utrymmen är ett nytt sätt att gruppera rum och personer. Välkommen till utrymmen! Lägg till existerande rum och utrymme - - Är du säker på att du vill lämna utrymmet\? Lämna utrymme Lägg till rum Utforska rum diff --git a/vector/src/main/res/values-tr/strings.xml b/vector/src/main/res/values-tr/strings.xml index 1d62631111..6fc2a5a738 100644 --- a/vector/src/main/res/values-tr/strings.xml +++ b/vector/src/main/res/values-tr/strings.xml @@ -1616,7 +1616,6 @@ Kimlik sunucusunu yapılandırın Kimlik sunucusunun bağlantısını kesin Kimlik sunucusu - İsimle veya ID ile ara Gizli etkinlikleri zaman tünelinde göster Bu oda ön izlenemiyor. Katılmak ister misiniz\? Entegrasyonları Yönet diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index b4cf230eea..ee3dd722a1 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -1964,7 +1964,6 @@ QR-код Зображення QR-коду QR-код - Пошук за іменем або ID Додати за QR-кодом Очікування Зазначте адресу сервера ідентифікації @@ -2200,7 +2199,6 @@ Проведіть пальцем, щоб скасувати Розпочати голосове повідомлення Розпочато груповий виклик - Ви впевнені, що хочете вийти з простору\? Вийти з простору Керувати кімнатами %s запрошує вас diff --git a/vector/src/main/res/values-vi/strings.xml b/vector/src/main/res/values-vi/strings.xml index 5da3dbe059..ab164d62c7 100644 --- a/vector/src/main/res/values-vi/strings.xml +++ b/vector/src/main/res/values-vi/strings.xml @@ -1124,7 +1124,6 @@ Đường link được copy Bật chức năng quẹt để Trả lời Tìm Tên - Tìm theo tên hoặc ID Tên hoặc ID Xem danh mục phòng chat Gửi tin nhắn tới phòng 1-1 diff --git a/vector/src/main/res/values-zh-rCN/strings.xml b/vector/src/main/res/values-zh-rCN/strings.xml index 20df22ad79..685ffbb0cd 100644 --- a/vector/src/main/res/values-zh-rCN/strings.xml +++ b/vector/src/main/res/values-zh-rCN/strings.xml @@ -2419,7 +2419,6 @@ 最近 二维码 通过二维码添加 - 通过名称或 ID 搜索 聊天室设置 主题 聊天室话题(可选) @@ -2602,8 +2601,6 @@ 你是此空间唯一的管理员。离开就意味着没人能控制它。 除非你被重新邀请,否则你将无法重新加入。 你是这唯一的人。如果你离开,包括你在内的所有人都将无法加入此空间。 - - 你确定你想要离开此空间吗? 离开空间 添加聊天室 探索聊天室 diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml index 6f7a8b5dd3..8de57469ee 100644 --- a/vector/src/main/res/values-zh-rTW/strings.xml +++ b/vector/src/main/res/values-zh-rTW/strings.xml @@ -2449,7 +2449,6 @@ 最近 QR code 透過 QR code 新增 - 使用名稱或 ID 搜尋 允許存取您聯絡人的權限。 要掃描 QR code,您必須允許存取攝影機。 開始聊天 @@ -2628,8 +2627,6 @@ 空間是將聊天室與人們分組的新方式。 歡迎使用空間! 新增既有的聊天室與空間 - - 您確定您想要離開空間嗎? 離開空間 新增聊天室 探索聊天室 diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 50306f1528..a61ba52a79 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2308,8 +2308,6 @@ View the room directory Name or ID (#example:matrix.org) - - Search by name or ID Search by name, ID or mail Search Name @@ -3518,8 +3516,6 @@ Add rooms Leave Space Are you sure you want to leave %s? - - Are you sure you want to leave the space? You are the only person here. If you leave, no one will be able to join in the future, including you. You won\'t be able to rejoin unless you are re-invited. You\'re the only admin of this space. Leaving it will mean no one has control over it. From 9605841a846044d8c8e60e3206ce8fa7fed25fe1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Oct 2021 15:42:29 +0200 Subject: [PATCH 071/144] Run towncrier --- CHANGES.md | 46 ++++++++++++++++++++++++++++++++++++++++ changelog.d/1673.bugfix | 1 - changelog.d/240.feature | 1 - changelog.d/3048.bugfix | 1 - changelog.d/3347.bugfix | 1 - changelog.d/3732.feature | 1 - changelog.d/3898.bugfix | 2 -- changelog.d/3933.bugfix | 1 - changelog.d/3934.bugfix | 1 - changelog.d/3935.bugfix | 1 - changelog.d/3957.misc | 1 - changelog.d/4018.misc | 1 - changelog.d/4027.feature | 1 - changelog.d/4045.bugfix | 1 - changelog.d/4076.misc | 1 - changelog.d/4077.bugfix | 1 - changelog.d/4092.bugfix | 4 ---- changelog.d/4109.bugfix | 1 - changelog.d/4113.misc | 1 - changelog.d/4155.bugfix | 1 - changelog.d/4156.bugfix | 1 - changelog.d/4157.feature | 1 - changelog.d/4158.feature | 1 - changelog.d/4158.removal | 1 - changelog.d/4176.bugfix | 1 - changelog.d/4184.feature | 1 - changelog.d/983.bugfix | 1 - 27 files changed, 46 insertions(+), 30 deletions(-) delete mode 100644 changelog.d/1673.bugfix delete mode 100644 changelog.d/240.feature delete mode 100644 changelog.d/3048.bugfix delete mode 100644 changelog.d/3347.bugfix delete mode 100644 changelog.d/3732.feature delete mode 100644 changelog.d/3898.bugfix delete mode 100644 changelog.d/3933.bugfix delete mode 100644 changelog.d/3934.bugfix delete mode 100644 changelog.d/3935.bugfix delete mode 100644 changelog.d/3957.misc delete mode 100644 changelog.d/4018.misc delete mode 100644 changelog.d/4027.feature delete mode 100644 changelog.d/4045.bugfix delete mode 100644 changelog.d/4076.misc delete mode 100644 changelog.d/4077.bugfix delete mode 100644 changelog.d/4092.bugfix delete mode 100644 changelog.d/4109.bugfix delete mode 100644 changelog.d/4113.misc delete mode 100644 changelog.d/4155.bugfix delete mode 100644 changelog.d/4156.bugfix delete mode 100644 changelog.d/4157.feature delete mode 100644 changelog.d/4158.feature delete mode 100644 changelog.d/4158.removal delete mode 100644 changelog.d/4176.bugfix delete mode 100644 changelog.d/4184.feature delete mode 100644 changelog.d/983.bugfix diff --git a/CHANGES.md b/CHANGES.md index 8d4899e6fb..4b76ccce84 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,49 @@ +Changes in Element v1.3.2 (2021-10-08) +====================================== + +Features ✨ +---------- + - Android Auto notification support ([#240](https://github.com/vector-im/element-android/issues/240)) + - Add a fallback for user displayName when this one is null or empty ([#3732](https://github.com/vector-im/element-android/issues/3732)) + - Add client base url config to customize permalinks ([#4027](https://github.com/vector-im/element-android/issues/4027)) + - Check if DM exists before creating a new one ([#4157](https://github.com/vector-im/element-android/issues/4157)) + - Handle 8 new slash commands: `/ignore`, `/unignore`, `/roomname`, `/myroomnick`, `/roomavatar`, `/myroomavatar`, `/lenny`, `/whois`. ([#4158](https://github.com/vector-im/element-android/issues/4158)) + - Display identity server policies in the Discovery screen ([#4184](https://github.com/vector-im/element-android/issues/4184)) + +Bugfixes 🐛 +---------- + - Ensure initial sync progress dialog is hidden when the initial sync is over ([#983](https://github.com/vector-im/element-android/issues/983)) + - Avoid resending notifications that are already shown ([#1673](https://github.com/vector-im/element-android/issues/1673)) + - Room filter no results bad CTA in space mode when a space selected ([#3048](https://github.com/vector-im/element-android/issues/3048)) + - Fixes notifications not dismissing when reading messages on other devices ([#3347](https://github.com/vector-im/element-android/issues/3347)) + - Fixes the passphrase screen being incorrectly shown when pressing back on the key verification screen. + When the user doesn't have a passphrase set we don't show the passphrase screen. ([#3898](https://github.com/vector-im/element-android/issues/3898)) + - App doesn't take you to a Space after choosing to Join it ([#3933](https://github.com/vector-im/element-android/issues/3933)) + - Validate public space addresses and room aliases length ([#3934](https://github.com/vector-im/element-android/issues/3934)) + - Save button for adding rooms to a space is hidden when scrolling through list of rooms ([#3935](https://github.com/vector-im/element-android/issues/3935)) + - Align new room encryption default to Web ([#4045](https://github.com/vector-im/element-android/issues/4045)) + - Fix Reply/Edit mode animation is broken when sending ([#4077](https://github.com/vector-im/element-android/issues/4077)) + - Added changes that will make SearchView in search bar focused by default on opening reaction picker. + + When tapping close icon of SearchView, the SearchView did not collapse therefore added the on close listener + which will collapse the SearchView on close. ([#4092](https://github.com/vector-im/element-android/issues/4092)) + - Troubleshoot notification: Fix button not clickable ([#4109](https://github.com/vector-im/element-android/issues/4109)) + - Harmonize wording in the message bottom sheet and move up the View Reactions item ([#4155](https://github.com/vector-im/element-android/issues/4155)) + - Remove unused SendRelationWorker and related API call (3588) ([#4156](https://github.com/vector-im/element-android/issues/4156)) + - SIP user to native user mapping is wrong ([#4176](https://github.com/vector-im/element-android/issues/4176)) + +SDK API changes ⚠️ +------------------ + - Create extension `String.isMxcUrl()` ([#4158](https://github.com/vector-im/element-android/issues/4158)) + +Other changes +------------- + - Use ktlint plugin. See [the documentation](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#ktlint) for more detail. ([#3957](https://github.com/vector-im/element-android/issues/3957)) + - Minimize the use of exported="true" in android Manifest (link: https://github.com/matrix-org/matrix-dinsic/issues/618) ([#4018](https://github.com/vector-im/element-android/issues/4018)) + - Fix redundancy in heading in the bug report issue form ([#4076](https://github.com/vector-im/element-android/issues/4076)) + - Fix release label in the release issue template ([#4113](https://github.com/vector-im/element-android/issues/4113)) + + Changes in Element v1.3.1 (2021-09-29) ====================================== diff --git a/changelog.d/1673.bugfix b/changelog.d/1673.bugfix deleted file mode 100644 index b0459f34b8..0000000000 --- a/changelog.d/1673.bugfix +++ /dev/null @@ -1 +0,0 @@ -Avoid resending notifications that are already shown diff --git a/changelog.d/240.feature b/changelog.d/240.feature deleted file mode 100644 index ee4d07a975..0000000000 --- a/changelog.d/240.feature +++ /dev/null @@ -1 +0,0 @@ -Android Auto notification support diff --git a/changelog.d/3048.bugfix b/changelog.d/3048.bugfix deleted file mode 100644 index 81fbe7b65e..0000000000 --- a/changelog.d/3048.bugfix +++ /dev/null @@ -1 +0,0 @@ -Room filter no results bad CTA in space mode when a space selected \ No newline at end of file diff --git a/changelog.d/3347.bugfix b/changelog.d/3347.bugfix deleted file mode 100644 index 8ba1d7af88..0000000000 --- a/changelog.d/3347.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixes notifications not dismissing when reading messages on other devices \ No newline at end of file diff --git a/changelog.d/3732.feature b/changelog.d/3732.feature deleted file mode 100644 index 3352c30886..0000000000 --- a/changelog.d/3732.feature +++ /dev/null @@ -1 +0,0 @@ -Add a fallback for user displayName when this one is null or empty \ No newline at end of file diff --git a/changelog.d/3898.bugfix b/changelog.d/3898.bugfix deleted file mode 100644 index 49cab4adad..0000000000 --- a/changelog.d/3898.bugfix +++ /dev/null @@ -1,2 +0,0 @@ -Fixes the passphrase screen being incorrectly shown when pressing back on the key verification screen. -When the user doesn't have a passphrase set we don't show the passphrase screen. \ No newline at end of file diff --git a/changelog.d/3933.bugfix b/changelog.d/3933.bugfix deleted file mode 100644 index 3396bd75e7..0000000000 --- a/changelog.d/3933.bugfix +++ /dev/null @@ -1 +0,0 @@ -App doesn't take you to a Space after choosing to Join it \ No newline at end of file diff --git a/changelog.d/3934.bugfix b/changelog.d/3934.bugfix deleted file mode 100644 index 989f96d004..0000000000 --- a/changelog.d/3934.bugfix +++ /dev/null @@ -1 +0,0 @@ -Validate public space addresses and room aliases length \ No newline at end of file diff --git a/changelog.d/3935.bugfix b/changelog.d/3935.bugfix deleted file mode 100644 index f4b1d309b4..0000000000 --- a/changelog.d/3935.bugfix +++ /dev/null @@ -1 +0,0 @@ -Save button for adding rooms to a space is hidden when scrolling through list of rooms \ No newline at end of file diff --git a/changelog.d/3957.misc b/changelog.d/3957.misc deleted file mode 100644 index bc6e417a49..0000000000 --- a/changelog.d/3957.misc +++ /dev/null @@ -1 +0,0 @@ -Use ktlint plugin. See [the documentation](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#ktlint) for more detail. \ No newline at end of file diff --git a/changelog.d/4018.misc b/changelog.d/4018.misc deleted file mode 100644 index d0849d5842..0000000000 --- a/changelog.d/4018.misc +++ /dev/null @@ -1 +0,0 @@ -Minimize the use of exported="true" in android Manifest (link: https://github.com/matrix-org/matrix-dinsic/issues/618) diff --git a/changelog.d/4027.feature b/changelog.d/4027.feature deleted file mode 100644 index fa45d07ef9..0000000000 --- a/changelog.d/4027.feature +++ /dev/null @@ -1 +0,0 @@ -Add client base url config to customize permalinks \ No newline at end of file diff --git a/changelog.d/4045.bugfix b/changelog.d/4045.bugfix deleted file mode 100644 index c6798ae492..0000000000 --- a/changelog.d/4045.bugfix +++ /dev/null @@ -1 +0,0 @@ -Align new room encryption default to Web \ No newline at end of file diff --git a/changelog.d/4076.misc b/changelog.d/4076.misc deleted file mode 100644 index 97b50d8c54..0000000000 --- a/changelog.d/4076.misc +++ /dev/null @@ -1 +0,0 @@ -Fix redundancy in heading in the bug report issue form diff --git a/changelog.d/4077.bugfix b/changelog.d/4077.bugfix deleted file mode 100644 index a8ab6b3c54..0000000000 --- a/changelog.d/4077.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix Reply/Edit mode animation is broken when sending \ No newline at end of file diff --git a/changelog.d/4092.bugfix b/changelog.d/4092.bugfix deleted file mode 100644 index 68ce518060..0000000000 --- a/changelog.d/4092.bugfix +++ /dev/null @@ -1,4 +0,0 @@ -Added changes that will make SearchView in search bar focused by default on opening reaction picker. - -When tapping close icon of SearchView, the SearchView did not collapse therefore added the on close listener -which will collapse the SearchView on close. diff --git a/changelog.d/4109.bugfix b/changelog.d/4109.bugfix deleted file mode 100644 index 4f35dd7f55..0000000000 --- a/changelog.d/4109.bugfix +++ /dev/null @@ -1 +0,0 @@ -Troubleshoot notification: Fix button not clickable \ No newline at end of file diff --git a/changelog.d/4113.misc b/changelog.d/4113.misc deleted file mode 100644 index a5faa57b92..0000000000 --- a/changelog.d/4113.misc +++ /dev/null @@ -1 +0,0 @@ -Fix release label in the release issue template diff --git a/changelog.d/4155.bugfix b/changelog.d/4155.bugfix deleted file mode 100644 index 59f5c564aa..0000000000 --- a/changelog.d/4155.bugfix +++ /dev/null @@ -1 +0,0 @@ -Harmonize wording in the message bottom sheet and move up the View Reactions item \ No newline at end of file diff --git a/changelog.d/4156.bugfix b/changelog.d/4156.bugfix deleted file mode 100644 index a010398adc..0000000000 --- a/changelog.d/4156.bugfix +++ /dev/null @@ -1 +0,0 @@ -Remove unused SendRelationWorker and related API call (3588) \ No newline at end of file diff --git a/changelog.d/4157.feature b/changelog.d/4157.feature deleted file mode 100644 index 71cbe60b1f..0000000000 --- a/changelog.d/4157.feature +++ /dev/null @@ -1 +0,0 @@ -Check if DM exists before creating a new one \ No newline at end of file diff --git a/changelog.d/4158.feature b/changelog.d/4158.feature deleted file mode 100644 index 86d2c760c2..0000000000 --- a/changelog.d/4158.feature +++ /dev/null @@ -1 +0,0 @@ -Handle 8 new slash commands: `/ignore`, `/unignore`, `/roomname`, `/myroomnick`, `/roomavatar`, `/myroomavatar`, `/lenny`, `/whois`. \ No newline at end of file diff --git a/changelog.d/4158.removal b/changelog.d/4158.removal deleted file mode 100644 index 557a85262e..0000000000 --- a/changelog.d/4158.removal +++ /dev/null @@ -1 +0,0 @@ -Create extension `String.isMxcUrl()` \ No newline at end of file diff --git a/changelog.d/4176.bugfix b/changelog.d/4176.bugfix deleted file mode 100644 index 6134dfca7c..0000000000 --- a/changelog.d/4176.bugfix +++ /dev/null @@ -1 +0,0 @@ -SIP user to native user mapping is wrong \ No newline at end of file diff --git a/changelog.d/4184.feature b/changelog.d/4184.feature deleted file mode 100644 index d25f7e258f..0000000000 --- a/changelog.d/4184.feature +++ /dev/null @@ -1 +0,0 @@ -Display identity server policies in the Discovery screen \ No newline at end of file diff --git a/changelog.d/983.bugfix b/changelog.d/983.bugfix deleted file mode 100644 index 7318f7f4cd..0000000000 --- a/changelog.d/983.bugfix +++ /dev/null @@ -1 +0,0 @@ -Ensure initial sync progress dialog is hidden when the initial sync is over \ No newline at end of file From bcf6342312cba3e3124e26bc2ab022325b5d99f1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Oct 2021 15:49:58 +0200 Subject: [PATCH 072/144] Fastlane changelog --- fastlane/metadata/android/en-US/changelogs/40103020.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/40103020.txt diff --git a/fastlane/metadata/android/en-US/changelogs/40103020.txt b/fastlane/metadata/android/en-US/changelogs/40103020.txt new file mode 100644 index 0000000000..7ac48f4890 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Main changes in this version: Add support for Android Auto. Lot of bug fixes! +Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.2 \ No newline at end of file From 0a0dd4ee1b27391e58396f00d5fe0bbd803b0a29 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Oct 2021 16:16:35 +0200 Subject: [PATCH 073/144] Fix crash when opening Identity Server detail from preference --- .../features/settings/VectorSettingsGeneralFragment.kt | 8 ++++++-- vector/src/main/res/xml/vector_settings_general.xml | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index 51496d6eb0..1be0bb3f76 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -176,8 +176,7 @@ class VectorSettingsGeneralFragment @Inject constructor( mPasswordPreference.isVisible = false } - val discoveryPreference = findPreference(VectorPreferences.SETTINGS_DISCOVERY_PREFERENCE_KEY)!! - discoveryPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { + val openDiscoveryScreenPreferenceClickListener = Preference.OnPreferenceClickListener { (requireActivity() as VectorSettingsActivity).navigateTo( DiscoverySettingsFragment::class.java, SettingsActivityPayload.DiscoverySettings().toMvRxBundle() @@ -185,6 +184,11 @@ class VectorSettingsGeneralFragment @Inject constructor( true } + val discoveryPreference = findPreference(VectorPreferences.SETTINGS_DISCOVERY_PREFERENCE_KEY)!! + discoveryPreference.onPreferenceClickListener = openDiscoveryScreenPreferenceClickListener + + mIdentityServerPreference.onPreferenceClickListener = openDiscoveryScreenPreferenceClickListener + // Advanced settings // user account diff --git a/vector/src/main/res/xml/vector_settings_general.xml b/vector/src/main/res/xml/vector_settings_general.xml index e5929d8933..30ef4337dc 100644 --- a/vector/src/main/res/xml/vector_settings_general.xml +++ b/vector/src/main/res/xml/vector_settings_general.xml @@ -87,7 +87,6 @@ Date: Fri, 8 Oct 2021 16:26:36 +0200 Subject: [PATCH 074/144] Wait a bit more --- .../androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt index aef5d3fe49..a9cb5274ed 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt @@ -226,7 +226,7 @@ class UiAllScreensSanityTest { // Filter // TODO clickMenu(R.id.search) // Wait for emoji to load, it's async now - sleep(1_000) + sleep(2_000) clickListItem(R.id.emojiRecyclerView, 4) // Test Edit mode From 3a387c5e32ac22cda445082a421251fe3b006b61 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Oct 2021 16:36:48 +0200 Subject: [PATCH 075/144] version++ --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index e3c8d7c4f1..96bec7a2fb 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -31,7 +31,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.3.2\"" + buildConfigField "String", "SDK_VERSION", "\"1.3.3\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" diff --git a/vector/build.gradle b/vector/build.gradle index b3cf745584..4bcbd5d1fa 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -14,7 +14,7 @@ kapt { // Note: 2 digits max for each value ext.versionMajor = 1 ext.versionMinor = 3 -ext.versionPatch = 2 +ext.versionPatch = 3 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From 2a29243298d28a69b2866f0222910a4cd7c77433 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 8 Oct 2021 16:45:29 +0200 Subject: [PATCH 076/144] Mavericks 2: clean code, but have warnings --- build.gradle | 2 +- .../matrix/android/sdk/flow/ExampleInstrumentedTest.kt | 6 ++---- .../main/java/org/matrix/android/sdk/flow/FlowRoom.kt | 4 ++-- .../java/org/matrix/android/sdk/flow/ExampleUnitTest.kt | 3 +-- vector/build.gradle | 1 + .../core/platform/VectorBaseBottomSheetDialogFragment.kt | 3 +-- .../im/vector/app/features/call/VectorCallActivity.kt | 6 ++++-- .../app/features/call/conference/JitsiCallViewModel.kt | 2 -- .../app/features/discovery/DiscoverySettingsViewModel.kt | 8 ++++---- .../java/im/vector/app/features/home/HomeActivity.kt | 9 ++++----- .../im/vector/app/features/home/HomeActivityViewModel.kt | 1 - .../home/UnknownDeviceDetectorSharedViewModel.kt | 3 +-- .../home/room/breadcrumbs/BreadcrumbsViewModel.kt | 1 - .../app/features/home/room/detail/RoomDetailFragment.kt | 2 -- .../home/room/detail/composer/TextComposerViewModel.kt | 4 ++-- .../home/room/detail/composer/TextComposerViewState.kt | 3 ++- .../app/features/home/room/list/RoomListViewModel.kt | 2 +- .../app/features/invite/InviteUsersToRoomViewModel.kt | 3 --- .../im/vector/app/features/permalink/PermalinkHandler.kt | 2 -- .../main/java/im/vector/app/features/pin/PinActivity.kt | 2 +- .../features/room/RequireActiveMembershipViewModel.kt | 7 +------ .../roommemberprofile/RoomMemberProfileViewModel.kt | 2 +- .../roomprofile/members/RoomMemberListViewModel.kt | 6 +----- .../roomprofile/settings/RoomSettingsViewModel.kt | 2 +- .../settings/VectorSettingsSecurityPrivacyFragment.kt | 1 - .../crosssigning/CrossSigningSettingsViewModel.kt | 3 +-- .../app/features/settings/devices/DevicesViewModel.kt | 3 +-- .../devtools/GossipingEventsPaperTrailViewModel.kt | 1 - .../settings/devtools/KeyRequestListViewModel.kt | 1 - .../im/vector/app/features/spaces/SpacesListViewModel.kt | 6 +----- .../im/vector/app/features/usercode/UserCodeActivity.kt | 2 +- 31 files changed, 35 insertions(+), 66 deletions(-) diff --git a/build.gradle b/build.gradle index 6b611a79bb..93f3e17f34 100644 --- a/build.gradle +++ b/build.gradle @@ -80,7 +80,7 @@ allprojects { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { // Warnings are potential errors, so stop ignoring them // You can override by passing `-PallWarningsAsErrors=false` in the command line - kotlinOptions.allWarningsAsErrors = false //project.getProperties().getOrDefault("allWarningsAsErrors", "true").toBoolean() + kotlinOptions.allWarningsAsErrors = project.getProperties().getOrDefault("allWarningsAsErrors", "true").toBoolean() } // Fix "Java heap space" issue diff --git a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt index 41799955a4..ca3502a211 100644 --- a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt +++ b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt @@ -16,14 +16,12 @@ package org.matrix.android.sdk.flow -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt index 1e0a1bc2e8..a78597ee46 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt @@ -36,8 +36,8 @@ class FlowRoom(private val room: Room) { fun liveRoomSummary(): Flow> { return room.getRoomSummaryLive().asFlow() - .startWith { - room.roomSummary().toOptional() + .startWith { + room.roomSummary().toOptional() } } diff --git a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt index bd627b2041..59e0856576 100644 --- a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt +++ b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt @@ -16,9 +16,8 @@ package org.matrix.android.sdk.flow -import org.junit.Test - import org.junit.Assert.* +import org.junit.Test /** * Example local unit test, which will execute on the development machine (host). diff --git a/vector/build.gradle b/vector/build.gradle index fc83b81432..d766cb947f 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -378,6 +378,7 @@ dependencies { kapt libs.airbnb.epoxyProcessor implementation libs.airbnb.epoxyPaging implementation libs.airbnb.mavericks + //TODO: remove when entirely migrated to Flow implementation libs.airbnb.mavericksRx // Work diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt index d13e446ed4..68765d615e 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -27,9 +27,8 @@ import android.widget.FrameLayout import androidx.annotation.CallSuper import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding -import com.airbnb.mvrx.MavericksView import com.airbnb.mvrx.Mavericks -import com.airbnb.mvrx.MvRxView +import com.airbnb.mvrx.MavericksView import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 36aafc3a9a..0654942d4b 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -631,10 +631,11 @@ class VectorCallActivity : VectorBaseActivity(), CallContro const val INCOMING_ACCEPT = "INCOMING_ACCEPT" fun newIntent(context: Context, call: WebRtcCall, mode: String?): Intent { + val callArgs = CallArgs(call.nativeRoomId, call.callId, call.mxCall.opponentUserId, !call.mxCall.isOutgoing, call.mxCall.isVideoCall) return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? flags = Intent.FLAG_ACTIVITY_NEW_TASK - putExtra(Mavericks.KEY_ARG, CallArgs(call.nativeRoomId, call.callId, call.mxCall.opponentUserId, !call.mxCall.isOutgoing, call.mxCall.isVideoCall)) + putExtra(Mavericks.KEY_ARG, callArgs) putExtra(EXTRA_MODE, mode) } } @@ -646,10 +647,11 @@ class VectorCallActivity : VectorBaseActivity(), CallContro isIncomingCall: Boolean, isVideoCall: Boolean, mode: String?): Intent { + val callArgs = CallArgs(signalingRoomId, callId, otherUserId, isIncomingCall, isVideoCall) return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? flags = Intent.FLAG_ACTIVITY_NEW_TASK - putExtra(Mavericks.KEY_ARG, CallArgs(signalingRoomId, callId, otherUserId, isIncomingCall, isVideoCall)) + putExtra(Mavericks.KEY_ARG, callArgs) putExtra(EXTRA_MODE, mode) } } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt index 7f50e76e73..c46b4e459d 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt @@ -28,7 +28,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import io.reactivex.disposables.Disposable import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.distinctUntilChanged @@ -39,7 +38,6 @@ import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.WidgetType -import org.matrix.android.sdk.rx.asObservable class JitsiCallViewModel @AssistedInject constructor( @Assisted initialState: JitsiCallViewState, diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 9578e143eb..66f38928a7 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -26,21 +26,21 @@ import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureProtocol +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.api.session.terms.TermsService +import org.matrix.android.sdk.flow.flow class DiscoverySettingsViewModel @AssistedInject constructor( @Assisted initialState: DiscoverySettingsState, diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index a41f414011..ff1154acc3 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -27,10 +27,10 @@ import android.view.MenuItem import androidx.core.view.GravityCompat import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout -import androidx.lifecycle.lifecycleScope -import com.airbnb.mvrx.Mavericks import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import androidx.lifecycle.lifecycleScope +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -74,7 +74,6 @@ import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.push.fcm.FcmHelper -import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.initsync.SyncStatusService @@ -319,8 +318,8 @@ class HomeActivity : buildTask = true ) if (!isHandled) { - val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) - || deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) + val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) || + deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) MaterialAlertDialogBuilder(this@HomeActivity) .setTitle(R.string.dialog_title_error) .setMessage(if (isMatrixToLink) R.string.permalink_malformed else R.string.universal_link_malformed) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index c45c128c55..627ff4be12 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -51,7 +51,6 @@ import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.util.awaitCallback -import org.matrix.android.sdk.rx.asObservable import timber.log.Timber import kotlin.coroutines.Continuation import kotlin.coroutines.resume diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index b434c48373..36e31770a9 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -102,8 +102,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted session.flow().liveUserCryptoDevices(session.myUserId), session.flow().liveMyDevicesInfo(), session.flow().liveCrossSigningPrivateKeys() - ) - { cryptoList, infoList, pInfo -> + ) { cryptoList, infoList, pInfo -> // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") // Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}") infoList diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index 795899a7b0..8ed44053a1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -25,7 +25,6 @@ import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 6aebbb09e6..ed87f1b833 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -184,8 +184,6 @@ import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetArgs import im.vector.app.features.widgets.WidgetKind import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import nl.dionsegijn.konfetti.models.Shape diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt index 96b684afa6..742d2848a1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail.composer import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -707,7 +707,7 @@ class TextComposerViewModel @AssistedInject constructor( fun create(initialState: TextComposerViewState): TextComposerViewModel } - companion object : MvRxViewModelFactory { + companion object : MavericksViewModelFactory { @JvmStatic override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt index fd1dd2d0ad..ebcce425ea 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.composer +import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MvRxState import im.vector.app.features.home.room.detail.RoomDetailArgs import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent @@ -47,7 +48,7 @@ data class TextComposerViewState( val isVoiceRecording: Boolean = false, val isSendButtonVisible: Boolean = false, val sendMode: SendMode = SendMode.REGULAR("", false) -) : MvRxState { +) : MavericksState { val isComposerVisible: Boolean get() = canSendMessage && !isVoiceRecording diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index d3d4771692..345c33ec18 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -43,8 +43,8 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.state.isPublic -import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt index daa5d54dc0..fd06614950 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt @@ -30,10 +30,7 @@ import im.vector.app.features.userdirectory.PendingSelection import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index 598cf9b885..a02cfe7517 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -32,8 +32,6 @@ import org.matrix.android.sdk.api.session.permalinks.PermalinkParser import org.matrix.android.sdk.api.session.permalinks.PermalinkService import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomType -import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.api.util.toOptional import javax.inject.Inject class PermalinkHandler @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, diff --git a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt index a335f1a1f2..430be6bc1f 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt @@ -18,8 +18,8 @@ package im.vector.app.features.pin import android.content.Context import android.content.Intent -import com.google.android.material.appbar.MaterialToolbar import com.airbnb.mvrx.Mavericks +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index 7184b97d0a..44a6963a5d 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -27,19 +27,14 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import io.reactivex.Observable import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.subscribe -import kotlinx.coroutines.flow.switchMap import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -86,7 +81,7 @@ class RequireActiveMembershipViewModel @AssistedInject constructor( roomIdFlow .unwrap() .flatMapLatest { roomId -> - val room = session.getRoom(roomId) ?: return@flatMapLatest flow{ + val room = session.getRoom(roomId) ?: return@flatMapLatest flow { val emptyResult = Optional.empty() emit(emptyResult) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index be559f9859..e99c8dde64 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -31,8 +31,8 @@ import im.vector.app.R import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.displayname.getBestName +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 03c32e31e5..f16353353c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -28,7 +28,6 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory -import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine @@ -37,7 +36,6 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.switchMap import kotlinx.coroutines.launch import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse @@ -99,11 +97,9 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() - ) - { roomMembers, powerLevelsContent -> + ) { roomMembers, powerLevelsContent -> buildRoomMemberSummaries(powerLevelsContent, roomMembers) } - .execute { async -> copy(roomMemberSummaries = async) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 2b10d1c132..c3c8ca7e2f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -301,7 +301,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: _viewEvents.post(RoomSettingsViewEvents.Success) } catch (failure: Throwable) { _viewEvents.post(RoomSettingsViewEvents.Failure(failure)) - }finally { + } finally { updateLoadingState(isLoading = false) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 82d4e590c1..7e60e69379 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -58,7 +58,6 @@ import im.vector.app.features.pin.PinMode import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.isE2EByDefault import im.vector.app.features.themes.ThemeUtils -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flowOn diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index c6139f66f5..033d9cf716 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -58,8 +58,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( combine( session.flow().liveMyDevicesInfo(), session.flow().liveCrossSigningInfo(session.myUserId) - ) - { myDevicesInfo, mxCrossSigningInfo -> + ) { myDevicesInfo, mxCrossSigningInfo -> myDevicesInfo to mxCrossSigningInfo } .execute { data -> diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index b2b4c0c396..a8154c3e11 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -125,8 +125,7 @@ class DevicesViewModel @AssistedInject constructor( combine( session.flow().liveUserCryptoDevices(session.myUserId), session.flow().liveMyDevicesInfo() - ) - { cryptoList, infoList -> + ) { cryptoList, infoList -> infoList .sortedByDescending { it.lastSeenTs } .map { deviceInfo -> diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt index 920d1ab7d0..fd09b38919 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt @@ -33,7 +33,6 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.rx.asObservable data class GossipingEventsPaperTrailState( val events: Async> = Uninitialized diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt index 07b2addccf..37decc4a12 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt @@ -35,7 +35,6 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest import org.matrix.android.sdk.internal.crypto.OutgoingRoomKeyRequest -import org.matrix.android.sdk.rx.asObservable data class KeyRequestListViewState( val incomingRequests: Async> = Uninitialized, diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index ba789cbd4a..fbbaeafe72 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -33,13 +33,11 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import im.vector.app.group import im.vector.app.space -import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.observeOn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch @@ -60,8 +58,6 @@ import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.rx.asObservable -import java.util.concurrent.TimeUnit class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, private val appStateHandler: AppStateHandler, @@ -92,7 +88,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp .asFlow() .setOnEach { copy( - myMxItem = it?.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() + myMxItem = it.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() ) } diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt index 5c238040e2..db1bc3056a 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt @@ -24,8 +24,8 @@ import android.widget.Toast import androidx.core.app.ActivityCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment -import com.airbnb.mvrx.Mavericks import androidx.fragment.app.FragmentManager +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import im.vector.app.R From fdc3da979ef2f6d06f682bdc774061bcc07faebb Mon Sep 17 00:00:00 2001 From: discapacidad5 Date: Sun, 10 Oct 2021 13:25:20 +0000 Subject: [PATCH 077/144] Translated using Weblate (Spanish) Currently translated at 98.6% (2632 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/es/ --- vector/src/main/res/values-es/strings.xml | 137 ++++++++++++++++++++-- 1 file changed, 126 insertions(+), 11 deletions(-) diff --git a/vector/src/main/res/values-es/strings.xml b/vector/src/main/res/values-es/strings.xml index c2f77e5afc..ee01158f09 100644 --- a/vector/src/main/res/values-es/strings.xml +++ b/vector/src/main/res/values-es/strings.xml @@ -840,7 +840,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Selecciona un directorio de salas El servidor puede estar no disponible o sobrecargado Escribe un servidor doméstico desde donde listar las salas públicas - URL del Servidor Doméstico + Nombre del servidor Todas las salas en el servidor %s Todas las salas nativas de %s @@ -2263,7 +2263,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua \n- La conexión a Internet que está usando cualquiera de los dispositivos \n \nLe recomendamos que cambie su contraseña y clave de recuperación en Configuración de inmediato. - Verifique sus dispositivos desde Configuración. + Se canceló la verificación. Puede iniciar la verificación de nuevo. Establecer un %s Generar una clave de mensaje Confirmar %s @@ -2365,7 +2365,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Primero acepta los términos del servidor de identidad en la configuración. Para su privacidad, ${app_name} solo admite el envío de números de teléfono y correos electrónicos de usuario con hash. La asociación ha fallado. - No hay asociación actual con este identificador. + No existe una asociación actual con este identificador. Su servidor doméstico (%1$s) propone utilizar %2$s para su servidor de identidad Utilizar %1$s Alternativamente, puede ingresar cualquier otra URL del servidor de identidad @@ -2600,7 +2600,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua \nEsperando respuesta del servidor… El mensaje no se pudo enviar por un error Ver confirmaciones de recibido - Esta sala es publica + Sala publica Herramientas de Desarrollador Enviar Evento Personalizado Enviar Estado del Evento @@ -2682,7 +2682,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Pad de marcado Esta llamada ha terminado %1$s ha cortado esta llamada - Has cortado esta llamada %1$s + Rechazaste esta llamada %s Ahora estas en esta llamada %1$s empezó una llamada Has empezado una llamada @@ -2741,7 +2741,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Comprimiendo imagen… Feedback de espacios Versión de sala - Espacios + Solo miembros del espacio Cualquiera puede encontrar y unirse a esta sala Publico Privada @@ -2773,7 +2773,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Toca para editar espacios Selecciona espacios Decide qué espacios pueden acceder a esta sala. Si un espacio es seleccionado, sus miembros podrán encontrar y unirse al nombre de la Sala. - Espacios que pueden acceder + Espacios a los que puede acceder Permite a los miembros del espacio encontrar y acceder. Miembros del Espacio %s pueden encontrar, previsualizar y unirse. Cualquiera dentro de un espacio con esta sala puede encontrarla y unirse a ella. Únicamente los administradores de la sala pueden añadirla a un espacio. @@ -2829,7 +2829,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Tu servidor Otros espacios o habitaciones que puede que no conozcas Unirse a la sala de repuesto - Esta sala tiene borradores sin enviar + Tiene borradores sin enviar Puedes contactarme si tienes dudas de seguimiento Necesitas permiso para actualizar una sala Actualizar el espacio padre automáticamente @@ -2856,8 +2856,8 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua ¿Buscas a alguien que no está en %s\? Los avisos requieren soporte del servidor y una versión de sala experimental Añade salas y espacios existentes - Eres administrador de este espacio, asegúrate de haber transferido el derecho de administrador a otro miembro antes de salir. - Este espacio no es público. No podrás volver a unirte sin una invitación. + Eres el único administrador de este espacio. Dejarlo significará que nadie tiene control sobre él. + No podrás volver a unirte a menos que te vuelvan a invitar. Eres la única persona aquí. Si sales, nadie podrá unirse a aquí en el futuro, incluido tú. %d persona que conoces ya se ha unido @@ -2896,7 +2896,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Falta el tipo de mensaje Contenido del evento Explora el Estado de Sala - Este Espacio es público + Espacio público Nivel de confianza seguro Aviso de nivel de confianza Nivel de confianza por defecto @@ -2918,4 +2918,119 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Envía el mensaje con confeti Enviar contenido multimedia con su tamaño original inestable + Agregar salas existentes + Elige cosas para dar + Dejar salas y espacios específicos… + Dejarás todas las salas y espacios en %s. + No dejes salas y espacios + Deja todas las habitaciones y espacios + ¿Estás seguro de que quieres irte %s\? + Descubrimiento (%s) + Terminar la configuración + Invitar por correo electrónico, buscar contactos y más… + Terminé de configurar el descubrimiento. + Actualmente no está utilizando un servidor de identidad. Para invitar a compañeros de equipo y ser detectado por ellos, configure uno a continuación. + Invitar por nombre de usuario o correo + Asegúrate que las personas adecuadas tengan acceso a %s. Puede invitar a más persona más tarde. + ¿Quiénes son tus compañeros de equipo\? + Agregar al espacio dado + Clave de estado + Desliza para finalizar la llamada + %1$s tocar para volver + Llamada activa (%1$s) · + + Llamada activa · + %1$d llamadas activas· + + La conexión fallo + Sin respuesta + Video llamada perdida + Llamada de voz perdida + Video llamada rechazada + Llamada de voz rechazada + La videollamada finalizó • %1$s + La llamada de voz finalizó • %1$s + Videollamada activa + Llamada de voz activa + Video llamada entrante + Llamada de voz entrante + Rechazaste esta llamada + estable + Versión predeterminada + Versiones de salas 👓 + Su servidor doméstico acepta archivos adjuntos (archivos, medios, etc.) con un tamaño de hasta %s. + Límite de carga de archivo del servidor + Verificar comparando emoji en su lugar + Escanear con este dispositivo + Escanee el código con su otro dispositivo o cambié y escanee con este dispositivo + Voz + Creando espacio… + Algunos caracteres no están permitidos + Proporcione la dirección de la sala + Dirección del espacio + Puede habilitar esto si la sala solo se usa para colaborar con equipos internos en su servidor doméstico. Esto no se puede cambiar después. + Antepone ( ͡° ͜ʖ ͡°) a un mensaje de texto sin formato + Mostrar información útil para ayudar a depurar la aplicación + Mostrar información de depuración en la pantalla + No parece una dirección de correo electrónico válida + Inicio de sesión único + Política + Ninguna política proporcionada por el servidor de identidad + Ocultar la política del servidor de identidad + Mostrar la política del servidor de identidad + Abrir configuración de descubrimiento + Buscar por nombre, ID o correo + Estás usando una versión beta de espacios. Sus comentarios ayudarán a informar las próximas versiones. Se anotaran su plataforma y nombre de usuario para ayudarnos utilizar sus comentarios tanto como podamos. + Crear nuevo espacio + Muestra información sobre un usuario + Cambia tu avatar solo en esta sala + Cambiar el avatar de la sala actual + Cambiar su apodo para mostrar solo en la sala actual + Establecer el nombre de la sala + Deja de ignorar a un usuario, muestra sus mensajes en el futuro. + Ignorar a un usuario, ocultándole sus mensajes + Espacio que sabes que contiene esta sala + Cualquiera puede encontrar el espacio y unirse + ¿Publicar está sala para el público en el directorio de salas de %1$s\? + Acceso al espacio + ¿Quién puede acceder\? + Configuraciones de la cuenta + Puede administrar las notificaciones %1$s. + Tenga en cuenta que las menciones y las notificaciones de palabras clave no están disponibles en sala cifradas en dispositivos móviles. + Notificarme por + No recibirá notificaciones de menciones y palabras claves en salas encriptadas en dispositivos móviles. + Palabras clave + \@room + Las palabras claves no pueden contener \'%s\' + Las palabras claves no pueden empezar con \'.\' + Agregar nuevas palabra clave + Tus palabras clave + Habilitar notificación por correo electrónico para %s + Para recibir un correo electrónico con una notificación, asocie un correo electrónico a su cuenta de matrix + Notificación de correo electrónico + Ninguno + Solo menciones y palabras clave + Actualizar el espacio + Cambiar el nombre del espacio + Habilitar el cifrado de espacio + Cambiar la dirección principal del espacio + Cambiar avatar de espacio + No tienes permiso para actualizar los roles necesario para cambiar varias partes de este espacio + Seleccione los roles necesarios para cambiar varias partes de este espacio + Vea y actualice los roles necesarios para cambiar varias partes del espacio. + Permisos de espacio + Quitar la prohibición al usuario le permitirá unirse al espacio nuevamente. + La prohibición del usuario lo expulsara de este espacio y evitará que se una nuevamente. + Patear al usuario lo eliminará de este espacio. +\n +\nPara evitar que se unan nuevamente, debe prohibirlos. + Finalizando llamada… + Sin respuesta + El usuario al que llamó está ocupado. + Usuario ocupado + Llamada de audio con %s + Videollamada con %s + Llamada sonando… + Espacios + Aprende más \ No newline at end of file From fe4475c43043f741e08a2006308705309c6d753c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sat, 9 Oct 2021 09:57:17 +0000 Subject: [PATCH 078/144] Translated using Weblate (Estonian) Currently translated at 100.0% (2669 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/et/ --- vector/src/main/res/values-et/strings.xml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml index 288e9246c2..22e967b783 100644 --- a/vector/src/main/res/values-et/strings.xml +++ b/vector/src/main/res/values-et/strings.xml @@ -2857,13 +2857,13 @@ Luba kogukonna liikmetel leida ja vaadata. Privaatne (vaid kutse alusel) Häälsõnumite saatmiseks palun anna rakendusele õigus mikrofoni kasutada. - Alusta häälsõnumi salvestust + Salvesta häälsõnum Tühistamiseks viipa Häälsõnumi lukustus Peata häälsõnumi esitus Esita häälsõnumit Salvestan häälsõnumit - Kustuta salvestatud häälsõnum + Kustuta salvestus Salvestamiseks vajuta nuppu, saatmiseks lase nupp lahti Jäänud on %1$d s Salvestuse peatamiseks ja taasesituseks vajuta salvestuse vaadet @@ -2989,4 +2989,17 @@ kogukonnast välja müksamine eemaldab ta praeguseks sellest kogukonnast. \n \nKui soovid, et ta ei saaks uuesti liituda, siis peaksid seadma suhtluskeelu. + Lõpeta salvestamine + Lisa ( ͡° ͜ʖ ͡°) smaili vormindamata sõnumi algusesse + Reeglid + Isikutuvastusserveril pole reegleid kirjeldatud + Peida isikutuvastusserveri reeglid + Näita isikutuvastusserveri reegleid + Näitab teavet kasutaja kohta + Muudab sinu tunnuspilti vaid selles jututoas + Muudab selle jututoa tunnuspilti + Muudab sinu hüüdnime vaid selles jututoas + Määrab jututoa nime + Lõpeta kasutaja eiramine ja näita edaspidi tema sõnumeid + Eirab kasutajat peites kõik tema sõnumid sinu eest \ No newline at end of file From 51c83f8bf4098c58b1343830da7d68027226f9be Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Sat, 9 Oct 2021 12:23:22 +0000 Subject: [PATCH 079/144] Translated using Weblate (Persian) Currently translated at 100.0% (2669 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fa/ --- vector/src/main/res/values-fa/strings.xml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index a2a3d856fc..01d8185700 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -2446,7 +2446,7 @@ \n \nبرای محرمانگی بیش‌تر، داده‌ها پیش از ارسال، درهم ریخته می‌شوند. یک کلید امنیتی ایجاد کنید تا در مکانی امن مانند سامانه مدیریت رمز عبور یا گاوصندوق آن را ذخیره کنید. - در حال حاضر هیچ ارتباطی با این شناسه وجود ندارد. + ارتباطی با این شناسه وجود ندارد. هویت خود را تأیید کنید تا به پیام‌های رمز شده دسترسی پیدا کنید. هویت خود را با تأیید این ورود به سیستم از یکی دیگر از نشست‌های خود، تأیید کنید تا به پیام‌های رمز شده دسترسی پیدا کنید. @@ -2844,13 +2844,13 @@ برای توقّف یا شمیدن، روی ضبطتان بزنید %1$dث مانده برای ضبط نگه دارید. برای فرستادن رها کنید - حذف پیام صوتی ضبط‌شده + حذف ضبط ضبط کردن پیام صوتی مکث پیام صوتی پخش پیام صوتی قفل پیام صوتی برای لغو، بکشید - آغاز پیام صوتی + ضبط پیام صوتی نیازمند ارتقا صدا دیگر فضاها یا اتاق‌هایی که ممکن است نشناسید @@ -2989,4 +2989,17 @@ اخراج کاربر، از این فضا برش می‌دارد. \n \nبرای پیش‌گیری از پیوستن دوباره، باید تحریمش کنید. + پایان ضبط + ( ͡° ͜ʖ ͡°) را به ابتدای پیام‌های متنی خام می‌افزاید + سیاست + سیاستی از سوری کارساز هویت فراهم نشده + نهفتن سیاست کارساز هویت + نمایش سیاست کارساز هویت + اطّلاعات را دربارهٔ کاربر نشان می‌دهد + چهرکتان را فقط در این اتاق تغییر می‌دهد + چهرک اتاق کنونی را تفییر می‌دهد + نام نمایشیتان را فقط در اتاق کنونی تغییر می‌دهد + نام اتاق را تنظیم می‌کند + به کاربری چشم گشوده و پیام‌های بعدیش را نشان می‌دهد + از کاربری چشم‌پوشی کرده و پیام‌هایش را از شما پنهان می‌کند \ No newline at end of file From 16068d82145cbd067c99a4cc3309c1411fcca1d1 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sat, 9 Oct 2021 08:35:21 +0000 Subject: [PATCH 080/144] Translated using Weblate (Hungarian) Currently translated at 100.0% (2669 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/hu/ --- vector/src/main/res/values-hu/strings.xml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml index 55b5dcb0c7..64b9f8fee4 100644 --- a/vector/src/main/res/values-hu/strings.xml +++ b/vector/src/main/res/values-hu/strings.xml @@ -211,7 +211,7 @@ Visszafejtett forrás megtekintése Törlés Átnevezés - Tartalom bejelentése + Tartalom Bejelentése Aktív hívás Folyamatban lévő konferenciahívás. \nCsatlakozás: %1$s vagy %2$s @@ -2887,13 +2887,13 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró Megállításhoz és visszajátszáshoz koppints a felvételre vissza van: %1$d Felvételhez tartsd nyomva, a küldéshez engedd el - Felvett hang üzenet törlése + Felvétel törlése Hang üzenet felvétel Hang üzenet szüneteltetése Hang üzenet lejátszása Hang üzenet kitartó rögzítése Elhúzás a megszakításhoz - Hang üzenet indítása + Hang üzenet felvétele %s tér tagság megtalálhatja és hozzáférhet. Más tereket is beállíthatsz. %s a Beállításokba a közvetlen meghívások fogadásához az Elemenetben. Ehhez a térhez a meghívó ide lett elküldve: %s, ami nincs összefüggésben a fiókoddal @@ -2994,4 +2994,17 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró a felhasználó kirúgása eltávolítja a térből. \n \nHa meg akarja akadályozni, hogy újra csatlakozzon, akkor inkább tiltsa ki. + Felvétel megállítása + ( ͡° ͜ʖ ͡°) -t tesz a szöveg elejére + Felhasználási feltétel + Azonosítási szerver nem adott meg felhasználási feltételt + Azonosítási szerver felhasználási feltételek elrejtése + Azonosítási szerver felhasználási feltételek megjelenítése + A felhasználóról információ megjelenítése + Csak ebben a szobában változtatja meg a profilképedet + Megváltoztatja a profilképed a jelenlegi szobában + Csak ebben a szobában változtatja meg a becenevedet + Szobanév beállítása + A felhasználó újbóli figyelembe vétele, és az üzenetei megjelenítése a jövőben + Figyelmen kívül hagy egy felhasználót, elrejtve előled az üzeneteit \ No newline at end of file From 30cc056b61c039e278f9950c6d97dadf318ad6c9 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Sat, 9 Oct 2021 01:41:07 +0000 Subject: [PATCH 081/144] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2669 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/ --- vector/src/main/res/values-pt-rBR/strings.xml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index a9bc57047b..7e4d837573 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -296,7 +296,7 @@ Visualizar Fonte Decriptada Deletar Renomear - Reportar conteúdo + Reportar Conteúdo Chamada ativa Chamada de conferência em curso. \nJunte-se como %1$s ou %2$s @@ -2926,13 +2926,13 @@ Toque em sua gravação para parar ou escutar %1$ds restando Segure para gravar, solte para enviar - Deletar mensagem de voz gravada + Deletar gravação Gravando mensagem de voz Pausar Mensagem de Voz Tocar Mensagem de Voz Cadeado de Mensagem de Voz Deslize para cancelar - Começar Mensagem de Voz + Gravar Mensagem de Voz Permitir qualquer pessoa em %s a encontrar e acessar. Você pode selecionar outros espaços também. Upgrade Requerido Voz @@ -3059,4 +3059,17 @@ expulsar usuária(o) vai removê-la(o) deste espaço. \n \nPara preveni-la(o) de se juntar de novo, você devia bani-la(o) em vez disso. + Parar de Gravar + Prepende ( ͡° ͜ʖ ͡°) a uma mensagem de texto puro + Política + Nenhuma política provida pelo servidor de identidade + Esconder política de servidor de identidade + Mostrar política de servidor de identidade + Exibe informação sobre um/uma usuário(a) + Muda seu avatar nesta sala atual somente + Muda o avatar da sala atual + Muda seu apelido de exibição na sala atual somente + Define o nome da sala + Para de ignorar um/uma usuário(a), mostrando as mensagens dele/dela de agora em diante + Ignora um/uma usuário(a), escondendo as mensagens dele/dela de você \ No newline at end of file From 820eedc7a3f71f1cb7ada45463409c9bc76c54d2 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 8 Oct 2021 21:50:02 +0000 Subject: [PATCH 082/144] Translated using Weblate (Albanian) Currently translated at 99.4% (2653 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sq/ --- vector/src/main/res/values-sq/strings.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index 5e399ebe84..1426a0ea85 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -2978,4 +2978,17 @@ Përzënia e përdoruesit do ta heqë prej kësaj hapësire. \n \nQë të pengohet pjesëmarrja sërish e tij, duhet ta dëboni. + Ndale Regjistrimin + I shton ( ͡° ͜ʖ ͡°) një mesazhi tekst të thjeshtë përpara + Rregulla + S’ka rregulla të dhëna nga shërbyesi i identiteteve + Fshih rregulla shërbyesi identitetesh + Shfaq rregulla shërbyesi identitetesh + Shfaq hollësi mbi një përdorues + Ndryshon avatarin tuaj vetëm në këtë dhomë + Ndryshon avatarin e dhomës së tanishme + Ndryshon nofkën e shfaqur për ju vetëm në dhomën e tanishme + Cakton emrin e dhomës + E ndal shpërfilljen e një përdoruesi, duke i shfaqur mesazhet e tyre pas kësaj + Shpërfill një përdorues, duke fshehur mesazhet e tij prej jush \ No newline at end of file From fbec4c305e0e1698b11cdfa29e99b156606f2159 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 8 Oct 2021 20:35:35 +0000 Subject: [PATCH 083/144] Translated using Weblate (Ukrainian) Currently translated at 91.3% (2438 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- vector/src/main/res/values-uk/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index ee3dd722a1..7d4ef86094 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -2197,7 +2197,7 @@ Запросити за користувацьким іменем Запросити електронним листом Проведіть пальцем, щоб скасувати - Розпочати голосове повідомлення + Записати голосове повідомлення Розпочато груповий виклик Вийти з простору Керувати кімнатами From 4ffb30ec4ff9423e8e43afae4a62ed88a1509935 Mon Sep 17 00:00:00 2001 From: sr093906 Date: Sat, 9 Oct 2021 01:23:53 +0000 Subject: [PATCH 084/144] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (2669 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hans/ --- vector/src/main/res/values-zh-rCN/strings.xml | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/vector/src/main/res/values-zh-rCN/strings.xml b/vector/src/main/res/values-zh-rCN/strings.xml index 685ffbb0cd..92a5c757e3 100644 --- a/vector/src/main/res/values-zh-rCN/strings.xml +++ b/vector/src/main/res/values-zh-rCN/strings.xml @@ -543,7 +543,7 @@ 稍后再说 永久链接 重命名 - 举报消息 + 举报内容 当前通话 正在进行电话会议。 \n请以 %1$s 或 %2$s 的形式加入 @@ -2172,7 +2172,7 @@ 请先在设置中接受身份服务器的条款。 为了你的隐私,${app_name} 仅支持发送用户电子邮件和电话号码的哈希值。 关联失败。 - 目前与此标识符没有关联。 + 当前与此标识符没有关联。 你的主服务器(%1$s)建议使用 %2$s 作为你的身份服务器 使用 %1$s 或者,你可以输入任何其他身份服务器 URL @@ -2811,13 +2811,13 @@ 点按您的录音以停止或收听 剩余 %1$d秒 按住录音,松开发送 - 删除录制的语音消息 + 删除录音 录制语音消息 暂停语音消息 播放语音消息 语音消息锁 滑动取消 - 开始语音消息 + 录制语音消息 允许 %s 中的任何人查找和访问。 您也可以选择其他空间。 需要升级 语音 @@ -2943,4 +2943,17 @@ 把用户踢出也会从这个空间删除他们。 \n \n为了防止他们再次加入,你应该封禁他们。 + 停止录制 + 预置 ( ͡° ͜ʖ ͡°) 到纯文本消息 + 政策 + 身份服务器未提供政策 + 隐藏身份服务器政策 + 显示身份服务器策略 + 显示用户信息 + 仅更改您在当前聊天室的头像 + 更改当前聊天室的头像 + 仅在当前聊天室更改您的显示昵称 + 设置聊天室名称 + 停止忽略用户,继续显示他们的消息 + 忽略用户,隐藏他们的消息 \ No newline at end of file From aa1e4a9f445d3097c1355c0ae089d1a9f6562ada Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sat, 9 Oct 2021 08:31:43 +0000 Subject: [PATCH 085/144] Translated using Weblate (Hungarian) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/hu/ --- fastlane/metadata/android/hu-HU/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/hu-HU/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/hu-HU/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/hu-HU/changelogs/40103020.txt diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103010.txt b/fastlane/metadata/android/hu-HU/changelogs/40103010.txt new file mode 100644 index 0000000000..2cc0dab42d --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Rendezd a szobáidat terekbe! v1.3.1 a v1.3.0 összeomlásait javítja. +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103020.txt b/fastlane/metadata/android/hu-HU/changelogs/40103020.txt new file mode 100644 index 0000000000..ff8af250e2 --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Android Auto támogatás és sok hibajavítás! +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.2 From 25250cb508aff73f2aceb3a6bb2017f8b7d84680 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Sat, 9 Oct 2021 01:29:51 +0000 Subject: [PATCH 086/144] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/pt_BR/ --- fastlane/metadata/android/pt-BR/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/pt-BR/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40103020.txt diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103010.txt b/fastlane/metadata/android/pt-BR/changelogs/40103010.txt new file mode 100644 index 0000000000..25d497eaa9 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Organize suas salas usando Espaços! v1.3.1 está consertando um crash que pode ocorrer em v1.3.0. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103020.txt b/fastlane/metadata/android/pt-BR/changelogs/40103020.txt new file mode 100644 index 0000000000..e34e321494 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Adicionar suporte para Android Auto. Muitos consertos de bugs! +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.2 From c9d15c170864502a2b352526c512d13c0799b840 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 8 Oct 2021 20:38:33 +0000 Subject: [PATCH 087/144] Translated using Weblate (Ukrainian) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/uk/ --- fastlane/metadata/android/uk/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/uk/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/uk/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/uk/changelogs/40103020.txt diff --git a/fastlane/metadata/android/uk/changelogs/40103010.txt b/fastlane/metadata/android/uk/changelogs/40103010.txt new file mode 100644 index 0000000000..5940cdedb0 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Впорядковуйте кімнати у простори. v1.3.1 виправляє збої, які виникали у v1.3.0. +Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/uk/changelogs/40103020.txt b/fastlane/metadata/android/uk/changelogs/40103020.txt new file mode 100644 index 0000000000..dc80b0be10 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Додано підтримку Android Auto. Виправлення багато помилок! +Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.2 From eefff680b7b0fd673fa201fa44daf2375cb429d2 Mon Sep 17 00:00:00 2001 From: sr093906 Date: Sat, 9 Oct 2021 01:25:58 +0000 Subject: [PATCH 088/144] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hans/ --- fastlane/metadata/android/zh-CN/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/zh-CN/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/zh-CN/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/zh-CN/changelogs/40103020.txt diff --git a/fastlane/metadata/android/zh-CN/changelogs/40103010.txt b/fastlane/metadata/android/zh-CN/changelogs/40103010.txt new file mode 100644 index 0000000000..98b506fb6e --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/40103010.txt @@ -0,0 +1,2 @@ +此版本的主要变化:使用空间组织您的聊天室! v1.3.1 正在修复 v1.3.0 中可能发生的崩溃。 +完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/zh-CN/changelogs/40103020.txt b/fastlane/metadata/android/zh-CN/changelogs/40103020.txt new file mode 100644 index 0000000000..586ba8d892 --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/40103020.txt @@ -0,0 +1,2 @@ +此版本的主要变化: 添加对 Android Auto 的支持。 许多错误修复! +完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.2 From 0f1e5fcc5c55d27255dae87ee418f1c6a7cbccce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sat, 9 Oct 2021 09:50:36 +0000 Subject: [PATCH 089/144] Translated using Weblate (Estonian) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/et/ --- fastlane/metadata/android/et/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/et/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/et/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/et/changelogs/40103020.txt diff --git a/fastlane/metadata/android/et/changelogs/40103010.txt b/fastlane/metadata/android/et/changelogs/40103010.txt new file mode 100644 index 0000000000..e292f6db81 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: halda oma jututubasid koondades neid uut tüüpi kogukondadesse! Lisaks parandasime versioonis 1.3.0 tekkinud olulise vea. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/et/changelogs/40103020.txt b/fastlane/metadata/android/et/changelogs/40103020.txt new file mode 100644 index 0000000000..ca3c0d3ea5 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: Android Auto tugi ning palju veaparandusi! +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.2 From 1c20c096d89567324a564ffb5e296ce8622b4172 Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Sat, 9 Oct 2021 12:26:04 +0000 Subject: [PATCH 090/144] Translated using Weblate (Persian) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/fa/ --- fastlane/metadata/android/fa/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/fa/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/fa/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/fa/changelogs/40103020.txt diff --git a/fastlane/metadata/android/fa/changelogs/40103010.txt b/fastlane/metadata/android/fa/changelogs/40103010.txt new file mode 100644 index 0000000000..1a800ac505 --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40103010.txt @@ -0,0 +1,2 @@ +تغییرات اصلی در این نگارش: سازمان‌دهی اتاق‌هایتان با فضاها! نگارش ۱٫۳٫۱ فروپاشی‌ای را که می‌توانست در نگارش ۱٫۳٫۰ رخ دهد، رفع می‌کند. +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/fa/changelogs/40103020.txt b/fastlane/metadata/android/fa/changelogs/40103020.txt new file mode 100644 index 0000000000..be669d29a9 --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40103020.txt @@ -0,0 +1,2 @@ +تغییرات اصلی در این نگارش: افزودن پشتیبانی از اندروید خودرو. کلّی رفع اشکال! +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.2 From a8c8e1d6ff906e7adacd9b42eb92ad66f6c28e75 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 8 Oct 2021 21:45:11 +0000 Subject: [PATCH 091/144] Translated using Weblate (Albanian) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sq/ --- fastlane/metadata/android/sq/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/sq/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/sq/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/sq/changelogs/40103020.txt diff --git a/fastlane/metadata/android/sq/changelogs/40103010.txt b/fastlane/metadata/android/sq/changelogs/40103010.txt new file mode 100644 index 0000000000..1981135963 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Ndryshime kryesore në këtë version: Sistemoni dhomat tuaja duke përdorur Hapësira! v1.3.1 ndreq një vithisje që mund të ndodhë në v1.3.0. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/sq/changelogs/40103020.txt b/fastlane/metadata/android/sq/changelogs/40103020.txt new file mode 100644 index 0000000000..6c8bd02cf0 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Ndryshime kryesore në këtë version: Shtim mbulimi për Android Auto. Plot ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.2 From 06e1cfd6319d6e666b591fe4a261b18e0010d83d Mon Sep 17 00:00:00 2001 From: Linerly Date: Sun, 10 Oct 2021 14:36:37 +0000 Subject: [PATCH 092/144] Translated using Weblate (Indonesian) Currently translated at 100.0% (2669 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/id/ --- vector/src/main/res/values-in/strings.xml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 6875ba6782..6dbb2a764c 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -20,7 +20,7 @@ Bagikan Hapus Ubah Nama - Laporkan konten + Laporkan Konten Informasi Perangkat atau Undang @@ -2332,13 +2332,13 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Ketuk pada rekaman Anda untuk dihentikan atau didengarkan %1$dd lagi Tahan untuk merekam, lepaskan untuk mengirimnya - Hapus pesan suara yang telah direkam + Hapus rekaman Merekam pesan suara Jeda Pesan Suara Mainkan Pesan Suara Kunci Pesan Suara Geser untuk membatalkan - Mulai Pesan Suara + Rekam Pesan Suara Panggilan grup dimulai Maaf, sebuah kesalahan terjadi saat bergabung: %s Tingkatkan ke versi ruangan yang disarankan @@ -2937,4 +2937,17 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. mengeluarkan pengguna akan mengeluarkannya dari space ini. \n \nUntuk mencegah pengguna untuk bergabung lagi, Anda seharusnya cekal pengguna itu saja. + Berhenti Merekam + Menambahkan ( ͡° ͜ʖ ͡°) ke pesan teks biasa + Kebijakan + Tidak ada kebijakan yang disediakan oleh server identitasnya + Sembunyikan kebijakan server identitas + Tampilkan kebijakan server identitas + Mengabaikan sebuah pengguna, akan menyembunyikan pesan mereka + Mengabaikan sebuah pengguna, dan menampilkan pesan mereka + Mentetapkan nama ruangan + Mengubah nama tampilan Anda di ruangan saat ini saja + Mengubah avatar ruangan saat ini + Mengubah avatar Anda di ruangan saat ini saja + Menampilkan \ No newline at end of file From d309c71d83acc8d53c6df5a7392b204d17373d0c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 10:21:42 +0200 Subject: [PATCH 093/144] Disable Android Auto supports see https://github.com/vector-im/element-android/issues/4205 --- changelog.d/4205.bugfix | 1 + vector/src/main/AndroidManifest.xml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelog.d/4205.bugfix diff --git a/changelog.d/4205.bugfix b/changelog.d/4205.bugfix new file mode 100644 index 0000000000..e287de2f23 --- /dev/null +++ b/changelog.d/4205.bugfix @@ -0,0 +1 @@ +Disable Android Auto supports see https://github.com/vector-im/element-android/issues/4205 \ No newline at end of file diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 7492da37e8..d79d1bb969 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -83,9 +83,10 @@ android:name="android.max_aspect" android:value="9.9" /> - + Date: Sun, 10 Oct 2021 13:49:31 +0000 Subject: [PATCH 094/144] Translated using Weblate (Spanish) Currently translated at 100.0% (2669 of 2669 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/es/ --- vector/src/main/res/values-es/strings.xml | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/vector/src/main/res/values-es/strings.xml b/vector/src/main/res/values-es/strings.xml index ee01158f09..bf41e3c6d1 100644 --- a/vector/src/main/res/values-es/strings.xml +++ b/vector/src/main/res/values-es/strings.xml @@ -3033,4 +3033,40 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua Llamada sonando… Espacios Aprende más + %s en Configuración para recibir invitaciones directamente en Element. + Vincula este correo electrónico con tu cuenta + Esta invitación a este espacio se envió a %s que no está asociado con su cuenta + Esta invitación a esta sala se envió a %s que no está asociado con su cuenta + Para ayudar a los miembros del espacio a encontrar y unirse a una sala privada, vaya a la configuración de esa sala tocando el avatar. + Ayuda a los miembros del espacio a encontrar salas privadas + Esto facilita que las habitaciones se mantengan privadas de un espacio, al tiempo que permite que las personas en el espacio las encuentren y se unan. Todas las habitaciones nuevas de un espacio tendrán esta opción disponible. + Ayude a las personas en los espacios a encontrar y unirse a salas privadas por sí mismas, sin necesidad de invitar a todos manualmente. + Nuevo: Permita que las personas en los espacios encuentren y se unan a salas privadas + Tenga en cuenta que la mejora creará una nueva versión de la habitación. Todos los mensajes actuales permanecerán en esta sala archivada. + Cualquiera en un espacio para padres podrá encontrar y unirse a esta sala, sin necesidad de invitar a todos manualmente. Podrás cambiar esto en la configuración de la habitación en cualquier momento. + Cualquiera en %s podrá encontrar y unirse a esta sala, sin necesidad de invitar a todos manualmente. Podrás cambiar esto en la configuración de la habitación en cualquier momento. + Mensaje de voz (%1$s) + No se puede responder ni editar mientras el mensaje de voz está activo + No se puede grabar un mensaje de voz + No se puede reproducir este mensaje de voz + Habilitar mensaje de voz + Toca tu grabación para detenerla o escucharla + %1$ds dejado + Mantenga presionado para grabar, suelte para enviar + Eliminar grabación + Grabación de mensaje de voz + Para de grabar + Pausar mensaje de voz + Reproducir mensaje de voz + Bloqueo de mensajes de voz + Deslizar para cancelar + Grabar mensaje de voz + Llamada grupal iniciada + Lo sentimos, se produjo un error al intentar unirse: %s + Actualiza la versión de sala recomendada + Está sala está ejecutando la versión de sala %s, que este servidor doméstico a marcado como inestable. + Permitir que cualquiera en %s buscar y acceder. También puede seleccionar otros espacios. + Todas las salas en las que se encuentra se mostraran en inicio. + Mostrar todas las salas en inicio + Agregar un espacio a cualquier espacio que administre. \ No newline at end of file From d366fb1c0fad7499b26bafe643a9cea61741d4e2 Mon Sep 17 00:00:00 2001 From: discapacidad5 Date: Sun, 10 Oct 2021 14:04:52 +0000 Subject: [PATCH 095/144] Translated using Weblate (Spanish) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/es/ --- .../android/es-ES/changelogs/40100100.txt | 3 +- .../android/es-ES/changelogs/40100110.txt | 2 + .../android/es-ES/changelogs/40100120.txt | 2 + .../android/es-ES/changelogs/40100130.txt | 2 + .../android/es-ES/changelogs/40100140.txt | 2 + .../android/es-ES/changelogs/40100150.txt | 2 + .../android/es-ES/changelogs/40100160.txt | 2 + .../android/es-ES/changelogs/40100170.txt | 2 + .../android/es-ES/changelogs/40101000.txt | 2 + .../android/es-ES/changelogs/40101010.txt | 2 + .../android/es-ES/changelogs/40101020.txt | 2 + .../android/es-ES/changelogs/40101030.txt | 2 + .../android/es-ES/changelogs/40101040.txt | 2 + .../android/es-ES/changelogs/40101050.txt | 2 + .../android/es-ES/changelogs/40101060.txt | 2 + .../android/es-ES/changelogs/40101070.txt | 2 + .../android/es-ES/changelogs/40101080.txt | 2 + .../android/es-ES/changelogs/40101090.txt | 2 + .../android/es-ES/changelogs/40101100.txt | 2 + .../android/es-ES/changelogs/40101110.txt | 2 + .../android/es-ES/changelogs/40101120.txt | 2 + .../android/es-ES/changelogs/40101130.txt | 2 + .../android/es-ES/changelogs/40102000.txt | 2 + .../android/es-ES/changelogs/40102010.txt | 2 + .../android/es-ES/changelogs/40103000.txt | 2 + .../android/es-ES/changelogs/40103010.txt | 2 + .../android/es-ES/changelogs/40103020.txt | 2 + .../android/es-ES/full_description.txt | 47 +++++++++++-------- fastlane/metadata/android/es-ES/title.txt | 2 +- 29 files changed, 83 insertions(+), 21 deletions(-) create mode 100644 fastlane/metadata/android/es-ES/changelogs/40100110.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40100120.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40100130.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40100140.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40100150.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40100160.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40100170.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101000.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101010.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101020.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101030.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101040.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101050.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101060.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101070.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101080.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101090.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101100.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101110.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101120.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40101130.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40102000.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40102010.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40103000.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/es-ES/changelogs/40103020.txt diff --git a/fastlane/metadata/android/es-ES/changelogs/40100100.txt b/fastlane/metadata/android/es-ES/changelogs/40100100.txt index 70b786d12e..5cfcde2145 100644 --- a/fastlane/metadata/android/es-ES/changelogs/40100100.txt +++ b/fastlane/metadata/android/es-ES/changelogs/40100100.txt @@ -1 +1,2 @@ -// TODO +Esta nueva versión contiene principalmente correcciones de errores y mejoras. Enviar un mensaje ahora es mucho más rápido. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.10 diff --git a/fastlane/metadata/android/es-ES/changelogs/40100110.txt b/fastlane/metadata/android/es-ES/changelogs/40100110.txt new file mode 100644 index 0000000000..5444087750 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40100110.txt @@ -0,0 +1,2 @@ +Esta nueva versión contiene principalmente mejoras en la interfaz de usuario y la experiencia del usuario. Ahora puedes invitar amigos y crear mensajes directos muy rápido escaneando códigos QR. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.11 diff --git a/fastlane/metadata/android/es-ES/changelogs/40100120.txt b/fastlane/metadata/android/es-ES/changelogs/40100120.txt new file mode 100644 index 0000000000..3e17b0359b --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40100120.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: Vista previa de URL, nuevo teclado Emoji, nuevas capacidades de configuración de la habitación y ¡nieve para Navidad! +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.12 diff --git a/fastlane/metadata/android/es-ES/changelogs/40100130.txt b/fastlane/metadata/android/es-ES/changelogs/40100130.txt new file mode 100644 index 0000000000..c87cb0faf5 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40100130.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: Vista previa de URL, nuevo teclado Emoji, nuevas capacidades de configuración de la habitación y ¡nieve para Navidad! +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.13 diff --git a/fastlane/metadata/android/es-ES/changelogs/40100140.txt b/fastlane/metadata/android/es-ES/changelogs/40100140.txt new file mode 100644 index 0000000000..9bd36b13db --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40100140.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: Editar permisos de sala, tema automático de luz / oscuridad y un montón de correcciones de errores. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.14 diff --git a/fastlane/metadata/android/es-ES/changelogs/40100150.txt b/fastlane/metadata/android/es-ES/changelogs/40100150.txt new file mode 100644 index 0000000000..f1b7d303d1 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40100150.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: Soporte de inicio de sesión social. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.15 diff --git a/fastlane/metadata/android/es-ES/changelogs/40100160.txt b/fastlane/metadata/android/es-ES/changelogs/40100160.txt new file mode 100644 index 0000000000..707ec23519 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40100160.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: Soporte de inicio de sesión social. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.15 y https://github.com/vector-im/element-android/releases/tag/v1.0.16 diff --git a/fastlane/metadata/android/es-ES/changelogs/40100170.txt b/fastlane/metadata/android/es-ES/changelogs/40100170.txt new file mode 100644 index 0000000000..9c6d3d7f54 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: ¡Corrección de errores! +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101000.txt b/fastlane/metadata/android/es-ES/changelogs/40101000.txt new file mode 100644 index 0000000000..996f9fdde8 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101000.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: ¡Mejora de VoIP (audio y videollamadas en DM) y corrección de errores! +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.0 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101010.txt b/fastlane/metadata/android/es-ES/changelogs/40101010.txt new file mode 100644 index 0000000000..ea9662576c --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101010.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: mejora del rendimiento y corrección de errores. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.1 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101020.txt b/fastlane/metadata/android/es-ES/changelogs/40101020.txt new file mode 100644 index 0000000000..87a92a96cd --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101020.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: mejora del rendimiento y corrección de errores. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.2 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101030.txt b/fastlane/metadata/android/es-ES/changelogs/40101030.txt new file mode 100644 index 0000000000..ca82a2c59c --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101030.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: mejora del rendimiento y corrección de errores. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.3 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101040.txt b/fastlane/metadata/android/es-ES/changelogs/40101040.txt new file mode 100644 index 0000000000..59acee78de --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101040.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: mejora del rendimiento y corrección de errores. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.4 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101050.txt b/fastlane/metadata/android/es-ES/changelogs/40101050.txt new file mode 100644 index 0000000000..ccdd9fd8d6 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101050.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: correcciones urgentes para 1.1.4 +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.5 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101060.txt b/fastlane/metadata/android/es-ES/changelogs/40101060.txt new file mode 100644 index 0000000000..9da3a09866 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101060.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: correcciones urgentes para 1.1.5 +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.6 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101070.txt b/fastlane/metadata/android/es-ES/changelogs/40101070.txt new file mode 100644 index 0000000000..6abb774b93 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101070.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: soporte beta para Spaces. Comprima el video antes de enviarlo. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.7 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101080.txt b/fastlane/metadata/android/es-ES/changelogs/40101080.txt new file mode 100644 index 0000000000..776bc52a25 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101080.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: mejora de Spaces. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.8 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101090.txt b/fastlane/metadata/android/es-ES/changelogs/40101090.txt new file mode 100644 index 0000000000..eaeab1517a --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101090.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: agregar soporte para la red gitter.im. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.9 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101100.txt b/fastlane/metadata/android/es-ES/changelogs/40101100.txt new file mode 100644 index 0000000000..d82529cf22 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101100.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: actualización de tema y estilo y nuevas funcionalidades para espacios. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.10 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101110.txt b/fastlane/metadata/android/es-ES/changelogs/40101110.txt new file mode 100644 index 0000000000..6432d2052a --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101110.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: actualización de tema y estilo y nuevas funciones para espacios (corrección de errores para 1.1.10) +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.11 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101120.txt b/fastlane/metadata/android/es-ES/changelogs/40101120.txt new file mode 100644 index 0000000000..a657fff51c --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101120.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: actualización de tema y estilo y corrección de un bloqueo después de la videollamada +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.12 diff --git a/fastlane/metadata/android/es-ES/changelogs/40101130.txt b/fastlane/metadata/android/es-ES/changelogs/40101130.txt new file mode 100644 index 0000000000..c9fbf424ae --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40101130.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: principalmente actualización de estabilidad y corrección de errores. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.1.13 diff --git a/fastlane/metadata/android/es-ES/changelogs/40102000.txt b/fastlane/metadata/android/es-ES/changelogs/40102000.txt new file mode 100644 index 0000000000..907019b6d6 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: Mensaje de voz está habilitado por defecto. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/es-ES/changelogs/40102010.txt b/fastlane/metadata/android/es-ES/changelogs/40102010.txt new file mode 100644 index 0000000000..909921ffd4 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: Muchas mejoras en VoIP y Spaces (aún en beta). +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/es-ES/changelogs/40103000.txt b/fastlane/metadata/android/es-ES/changelogs/40103000.txt new file mode 100644 index 0000000000..054aa68541 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: ¡Organiza tus habitaciones usando Spaces! +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.3.0 diff --git a/fastlane/metadata/android/es-ES/changelogs/40103010.txt b/fastlane/metadata/android/es-ES/changelogs/40103010.txt new file mode 100644 index 0000000000..6ff7b0502e --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: ¡Organiza tus habitaciones usando Spaces! v1.3.1 está arreglando un bloqueo que puede ocurrir en v1.3.0. +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/es-ES/changelogs/40103020.txt b/fastlane/metadata/android/es-ES/changelogs/40103020.txt new file mode 100644 index 0000000000..22f7592aea --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Principales cambios en esta versión: agregar soporte para Android Auto. ¡Muchas correcciones de errores! +Registro de cambios completo: https://github.com/vector-im/element-android/releases/tag/v1.3.2 diff --git a/fastlane/metadata/android/es-ES/full_description.txt b/fastlane/metadata/android/es-ES/full_description.txt index 8c9915a735..fdba15e90e 100644 --- a/fastlane/metadata/android/es-ES/full_description.txt +++ b/fastlane/metadata/android/es-ES/full_description.txt @@ -1,30 +1,39 @@ -Element es un nuevo tipo de aplicación de mensajería y colaboración que: +Element es un mensajero seguro y una aplicación de colaboración en equipo de productividad que es ideal para chats grupales mientras se trabaja a distancia. Esta aplicación de chat utiliza encriptación de un extremo a otro para proporcionar poderosas videoconferencias, uso compartido de archivos y llamadas de voz. -1. Te da el control para preservar su privacidad -2. Te permite comunicarse con cualquier persona en la red Matrix e incluso más allá al integrarse con aplicaciones como Slack -3. Te protege de la publicidad, la minería de datos y los jardines vallados -4. Te protege a través de encriptación de Extremo-a-Extremo, con firma cruzada para verificar a otros +Las características de Element incluyen: +- Herramientas de comunicación online avanzadas +- Mensajes totalmente encriptados para permitir una comunicación corporativa más segura, incluso para trabajadores remotos +- Chat descentralizado basado en el marco de código abierto Matrix +- Uso compartido de archivos de forma segura con datos cifrados mientras gestiona proyectos +- Chats de video con voz sobre IP y pantalla compartida +- Fácil integración con sus herramientas de colaboración en línea favoritas, herramientas de gestión de proyectos, servicios VoIP y otras aplicaciones de mensajería para equipos -Element es completamente diferente de otras aplicaciones de mensajería y colaboración porque es descentralizado y de código abierto. +Element es completamente diferente de otras aplicaciones de mensajería y colaboración. Opera en Matrix, una red abierta para mensajería segura y comunicación descentralizada. Permite el autohospedaje para brindar a los usuarios la máxima propiedad y control de sus datos y mensajes. -Element te permite tener su propio servidor privado, o elegir uno público, para que tenga privacidad, posesión, y control de sus datos y conversaciones. Te da acceso a una red abierta; para que no se quede atrapado hablando solo con otros usuarios de Element. Y es muy seguro. +Privacidad y mensajería encriptada +Element lo protege de anuncios no deseados, minería de datos y jardines amurallados. También protege todos sus datos, video uno a uno y comunicación de voz a través del cifrado de extremo a extremo y la verificación de dispositivos con firma cruzada. -Element puede hacer todo esto porque opera en Matrix, el estándar para la comunicación abierta y descentralizada. +Element le brinda control sobre su privacidad al mismo tiempo que le permite comunicarse de manera segura con cualquier persona en la red Matrix u otras herramientas de colaboración empresarial al integrarse con aplicaciones como Slack. -Element te da el control permitiéndote elegir quién aloja tus conversaciones. Desde la aplicación Element, puedes elegir hospedar de diferentes maneras: +El elemento puede ser autohospedado +Para permitir un mayor control de sus conversaciones y datos confidenciales, Element puede ser autohospedado o puede elegir cualquier host basado en Matrix, el estándar para la comunicación descentralizada de código abierto. Element le brinda privacidad, cumplimiento de seguridad y flexibilidad de integración. -1. Obtén una cuenta gratuita en el servidor público de matrix.org alojado por los desarrolladores de Matrix, o elije entre miles de servidores públicos alojados por voluntarios -2. Autohospeda tu cuenta con un servidor en tu propio hardware -3. Regístrate para obtener una cuenta en un servidor personalizado simplemente suscribiéndote a la plataforma de alojamiento de Element Matrix Services +Sea dueño de sus datos +Tú decides dónde guardar tus datos y mensajes. Sin riesgo de minería de datos o acceso de terceros. -¿Por qué elegir Element? +Element te da el control de diferentes maneras: +1. Obtenga una cuenta gratuita en el servidor público de matrix.org alojado por los desarrolladores de Matrix, o elija entre miles de servidores públicos alojados por voluntarios +2. Autohospede su cuenta ejecutando un servidor en su propia infraestructura de TI +3. Regístrese para obtener una cuenta en un servidor personalizado simplemente suscribiéndose a la plataforma de alojamiento de Element Matrix Services -TOMA POSESIÓN DE TUS DATOS: Tú decides dónde guardar tus datos y mensajes. Tú eres el propietario y quien lo controla, no alguna MEGACORP que extrae tu datos o da acceso a terceros. +Colaboración y mensajería abierta +Puede chatear con cualquier persona en la red Matrix, ya sea que esté usando Element, otra aplicación Matrix o incluso si está usando una aplicación de mensajería diferente. -MENSAJERÍA ABIERTA Y COLABORACIÓN: Puede chatear con cualquier otra persona en la red de Matrix, tanto si usan Element u otra aplicación de Matrix, e incluso si están usando un sistema de mensajería diferente como Slack, IRC o XMPP. +Súper seguro +Cifrado real de extremo a extremo (solo aquellos en la conversación pueden descifrar mensajes) y verificación de dispositivos con firma cruzada. -SUPER SEGURO: Encriptación de Extremo-a-Extremo real (solo aquellos en la conversación pueden descifrar mensajes) y firma cruzada para verificar los dispositivos de los participantes de la conversación. +Completa comunicación e integración +Mensajería, llamadas de voz y video, uso compartido de archivos, uso compartido de pantalla y un montón de integraciones, bots y widgets. Construya salas, comunidades, manténgase en contacto y haga las cosas. -COMUNICACIÓN COMPLETA: Mensajería, llamadas de voz y video, uso compartido de archivos, uso compartido de pantalla y un montón de integraciones, bots y widgets. Crea salas, comunidades, mantente en contacto y organízate con eficacia. - -EN TODAS PARTES: Mantente en contacto donde quiera que estés con un historial de mensajes totalmente sincronizado en todos sus dispositivos y en la web en https://app.element.io. +Continúa donde lo dejaste +Manténgase en contacto donde quiera que esté con el historial de mensajes totalmente sincronizado en todos sus dispositivos y en la web en https://app.element.io diff --git a/fastlane/metadata/android/es-ES/title.txt b/fastlane/metadata/android/es-ES/title.txt index 971e5cf146..2e011d7ee7 100644 --- a/fastlane/metadata/android/es-ES/title.txt +++ b/fastlane/metadata/android/es-ES/title.txt @@ -1 +1 @@ -Element (previamente Riot.im) +Element - Mensajero seguro From 3c7e05b86e3f27b58b9af60793cba72d72a134e6 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sun, 10 Oct 2021 14:42:50 +0000 Subject: [PATCH 096/144] Translated using Weblate (Indonesian) Currently translated at 100.0% (33 of 33 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/id/ --- fastlane/metadata/android/id/changelogs/40103010.txt | 2 ++ fastlane/metadata/android/id/changelogs/40103020.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/id/changelogs/40103010.txt create mode 100644 fastlane/metadata/android/id/changelogs/40103020.txt diff --git a/fastlane/metadata/android/id/changelogs/40103010.txt b/fastlane/metadata/android/id/changelogs/40103010.txt new file mode 100644 index 0000000000..7823017895 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Organisir ruangan Anda dengan menggunakan sebuah Space! v1.3.1 memperbaiki crash yang dapat terjadi di v1.3.0. +Changelog lengkap: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/id/changelogs/40103020.txt b/fastlane/metadata/android/id/changelogs/40103020.txt new file mode 100644 index 0000000000..4f46881d68 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Penambahan dukungan untuk Android Auto. Banyak perbaikan bug! +Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.2 From 506dfe5fea8dbe6d2e84b365388c7a7e26d43b77 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 11 Oct 2021 11:46:37 +0300 Subject: [PATCH 097/144] Adding trailing space " " or ": " if the user started a sentence by mentioning someone, --- .../app/features/home/room/detail/AutoCompleter.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt index d4430730d7..1fcac59b00 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt @@ -220,8 +220,16 @@ class AutoCompleter @AssistedInject constructor( // Replace the word by its completion val displayName = matrixItem.getBestName() - // with a trailing space - editable.replace(startIndex, endIndex, "$displayName ") + + // Adding trailing space " " or ": " if the user started mention someone + val displayNameSuffix = + if (firstChar == "@" && editText.length() == 1) { + ": " + } else { + " " + } + + editable.replace(startIndex, endIndex, "$displayName$displayNameSuffix") // Add the span val span = PillImageSpan( From 3a6259fd299285b5ebb75bcd07f47285b5c5d86c Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 11 Oct 2021 11:53:43 +0300 Subject: [PATCH 098/144] Add changelog file --- changelog.d/908.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/908.bugfix diff --git a/changelog.d/908.bugfix b/changelog.d/908.bugfix new file mode 100644 index 0000000000..f43b03e892 --- /dev/null +++ b/changelog.d/908.bugfix @@ -0,0 +1 @@ +Issue #908 Adding trailing space " " or ": " if the user started a sentence by mentioning someone, From a2c790b4a168433f62ee77aa4a9c60cd8f314b74 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 11 Oct 2021 12:29:00 +0300 Subject: [PATCH 099/144] Update to support the whole typing name --- .../im/vector/app/features/home/room/detail/AutoCompleter.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt index 1fcac59b00..1d6530218d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt @@ -220,10 +220,9 @@ class AutoCompleter @AssistedInject constructor( // Replace the word by its completion val displayName = matrixItem.getBestName() - // Adding trailing space " " or ": " if the user started mention someone val displayNameSuffix = - if (firstChar == "@" && editText.length() == 1) { + if (firstChar == "@" && startIndex == 0) { ": " } else { " " From 713c93faa6f648a78ec2937cd5aa90fa59813f91 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 12:00:32 +0200 Subject: [PATCH 100/144] Towncrier --- CHANGES.md | 8 ++++++++ changelog.d/4205.bugfix | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) delete mode 100644 changelog.d/4205.bugfix diff --git a/CHANGES.md b/CHANGES.md index 4b76ccce84..c545a70671 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +Changes in Element v1.3.3 (2021-10-11) +====================================== + +Bugfixes 🐛 +---------- + - Disable Android Auto supports ([#4205](https://github.com/vector-im/element-android/issues/4205)) + + Changes in Element v1.3.2 (2021-10-08) ====================================== diff --git a/changelog.d/4205.bugfix b/changelog.d/4205.bugfix deleted file mode 100644 index e287de2f23..0000000000 --- a/changelog.d/4205.bugfix +++ /dev/null @@ -1 +0,0 @@ -Disable Android Auto supports see https://github.com/vector-im/element-android/issues/4205 \ No newline at end of file From 9acec43a48198540ac2632e742a186fcb0058c16 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 12:03:01 +0200 Subject: [PATCH 101/144] fastlane --- fastlane/metadata/android/en-US/changelogs/40103030.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/40103030.txt diff --git a/fastlane/metadata/android/en-US/changelogs/40103030.txt b/fastlane/metadata/android/en-US/changelogs/40103030.txt new file mode 100644 index 0000000000..2068aeed95 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103030.txt @@ -0,0 +1,2 @@ +Main changes in this version: Make identity server policy(ies) visible in the settings. Temporarily remove Android Auto support. +Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.3 \ No newline at end of file From 343783f8070ba7aaec25ea61832d4dfad64175fa Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 12:06:51 +0200 Subject: [PATCH 102/144] Version++ --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 96bec7a2fb..86a257c7d6 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -31,7 +31,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.3.3\"" + buildConfigField "String", "SDK_VERSION", "\"1.3.4\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" diff --git a/vector/build.gradle b/vector/build.gradle index 4bcbd5d1fa..c6350a2812 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -14,7 +14,7 @@ kapt { // Note: 2 digits max for each value ext.versionMajor = 1 ext.versionMinor = 3 -ext.versionPatch = 3 +ext.versionPatch = 4 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From 6dd0de612315bde5305d65e32c2e7d6db11d8e21 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 13:29:29 +0200 Subject: [PATCH 103/144] Mavericks 2.4.0 --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 9ab7bdc7b8..4d1fdb6a60 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -19,7 +19,7 @@ def moshi = "1.12.0" def lifecycle = "2.2.0" def rxBinding = "3.1.0" def epoxy = "4.6.2" -def mavericks = "2.3.0" +def mavericks = "2.4.0" def glide = "4.12.0" def bigImageViewer = "1.8.1" def jjwt = "0.11.2" From 6721669d1d6983d77ae1e96f6af8adfaa3853b83 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 14:13:42 +0200 Subject: [PATCH 104/144] Fixes false positive "This is an internal Mavericks API. It is not intended for external use." of MvRx `by viewModel()` calls. Maybe due to the inlining of code... This is a temporary fix... --- vector/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vector/build.gradle b/vector/build.gradle index d766cb947f..493dcb6bd8 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -298,6 +298,12 @@ android { kotlinOptions { jvmTarget = "11" + freeCompilerArgs += [ + "-Xopt-in=kotlin.RequiresOptIn", + // Fixes false positive "This is an internal Mavericks API. It is not intended for external use." + // of MvRx `by viewModel()` calls. Maybe due to the inlining of code... This is a temporary fix... + "-Xopt-in=com.airbnb.mvrx.InternalMavericksApi", + ] } sourceSets { From f89a32da1ff474ae9af240b125fa069bc9ed35e6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 14:27:55 +0200 Subject: [PATCH 105/144] Add opt-in for kotlinx.coroutines annotations --- vector/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vector/build.gradle b/vector/build.gradle index 493dcb6bd8..55e03de3f3 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -303,6 +303,10 @@ android { // Fixes false positive "This is an internal Mavericks API. It is not intended for external use." // of MvRx `by viewModel()` calls. Maybe due to the inlining of code... This is a temporary fix... "-Xopt-in=com.airbnb.mvrx.InternalMavericksApi", + // Opt in for kotlinx.coroutines.FlowPreview too + "-Xopt-in=kotlinx.coroutines.FlowPreview", + // Opt in for kotlinx.coroutines.ExperimentalCoroutinesApi too + "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", ] } From caf2c2c487fcf90fa9f183c854efdc140ea035b9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 14:29:58 +0200 Subject: [PATCH 106/144] Use same values for all modules --- matrix-sdk-android-flow/build.gradle | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android-flow/build.gradle b/matrix-sdk-android-flow/build.gradle index 4aecec169b..fd2e2e0824 100644 --- a/matrix-sdk-android-flow/build.gradle +++ b/matrix-sdk-android-flow/build.gradle @@ -5,11 +5,11 @@ plugins { } android { - compileSdk 31 + compileSdk versions.compileSdk defaultConfig { - minSdk 21 - targetSdk 31 + minSdk versions.minSdk + targetSdk versions.targetSdk testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" @@ -22,11 +22,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility versions.sourceCompat + targetCompatibility versions.targetCompat } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = "11" } } @@ -45,5 +45,4 @@ dependencies { // Logging implementation libs.jakewharton.timber - -} \ No newline at end of file +} From 6520729343461aef42fbf99a9973b0e68df8d14f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 14:41:40 +0200 Subject: [PATCH 107/144] ktlint --- .../features/home/room/detail/composer/TextComposerViewState.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt index ebcce425ea..3110aa8dc3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt @@ -17,7 +17,6 @@ package im.vector.app.features.home.room.detail.composer import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.MvRxState import im.vector.app.features.home.room.detail.RoomDetailArgs import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent From bde129ddced3599e915b19caa4a2d1cc22db8197 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 Oct 2021 14:42:06 +0200 Subject: [PATCH 108/144] Remove not compiling sample tests --- .../sdk/flow/ExampleInstrumentedTest.kt | 38 ------------------- .../android/sdk/flow/ExampleUnitTest.kt | 32 ---------------- 2 files changed, 70 deletions(-) delete mode 100644 matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt delete mode 100644 matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt diff --git a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt b/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt deleted file mode 100644 index ca3502a211..0000000000 --- a/matrix-sdk-android-flow/src/androidTest/java/org/matrix/android/sdk/flow/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.flow - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.* -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.matrix.android.sdk.flow.test", appContext.packageName) - } -} diff --git a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt b/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt deleted file mode 100644 index 59e0856576..0000000000 --- a/matrix-sdk-android-flow/src/test/java/org/matrix/android/sdk/flow/ExampleUnitTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.flow - -import org.junit.Assert.* -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} From ccc4a43737c85c8f8efccf8413a352e0a3f34109 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 11 Oct 2021 14:13:08 +0000 Subject: [PATCH 109/144] Sync Emojis --- .../emoji_picker_datasource_formatted.json | 1147 +++++++++++++---- .../main/res/raw/emoji_picker_datasource.json | 2 +- 2 files changed, 893 insertions(+), 256 deletions(-) diff --git a/tools/emojis/emoji_picker_datasource_formatted.json b/tools/emojis/emoji_picker_datasource_formatted.json index 33fc9be128..4f3038d53c 100644 --- a/tools/emojis/emoji_picker_datasource_formatted.json +++ b/tools/emojis/emoji_picker_datasource_formatted.json @@ -15,6 +15,7 @@ "face-with-tears-of-joy", "slightly-smiling-face", "upsidedown-face", + "melting-face", "winking-face", "smiling-face-with-smiling-eyes", "smiling-face-with-halo", @@ -33,15 +34,19 @@ "zany-face", "squinting-face-with-tongue", "moneymouth-face", - "hugging-face", + "smiling-face-with-open-hands", "face-with-hand-over-mouth", + "face-with-open-eyes-and-hand-over-mouth", + "face-with-peeking-eye", "shushing-face", "thinking-face", + "saluting-face", "zippermouth-face", "face-with-raised-eyebrow", "neutral-face", "expressionless-face", "face-without-mouth", + "dotted-line-face", "face-in-clouds", "smirking-face", "unamused-face", @@ -63,7 +68,7 @@ "hot-face", "cold-face", "woozy-face", - "knockedout-face", + "face-with-crossedout-eyes", "face-with-spiral-eyes", "exploding-head", "cowboy-hat-face", @@ -73,6 +78,7 @@ "nerd-face", "face-with-monocle", "confused-face", + "face-with-diagonal-mouth", "worried-face", "slightly-frowning-face", "frowning-face", @@ -81,6 +87,7 @@ "astonished-face", "flushed-face", "pleading-face", + "face-holding-back-tears", "frowning-face-with-open-mouth", "anguished-face", "fearful-face", @@ -172,11 +179,16 @@ "hand-with-fingers-splayed", "raised-hand", "vulcan-salute", + "rightwards-hand", + "leftwards-hand", + "palm-down-hand", + "palm-up-hand", "ok-hand", "pinched-fingers", "pinching-hand", "victory-hand", "crossed-fingers", + "hand-with-index-finger-and-thumb-crossed", "loveyou-gesture", "sign-of-the-horns", "call-me-hand", @@ -186,6 +198,7 @@ "middle-finger", "backhand-index-pointing-down", "index-pointing-up", + "index-pointing-at-the-viewer", "thumbs-up", "thumbs-down", "raised-fist", @@ -194,6 +207,7 @@ "rightfacing-fist", "clapping-hands", "raising-hands", + "heart-hands", "open-hands", "palms-up-together", "handshake", @@ -218,6 +232,7 @@ "eye", "tongue", "mouth", + "biting-lip", "baby", "child", "boy", @@ -337,6 +352,7 @@ "construction-worker", "man-construction-worker", "woman-construction-worker", + "person-with-crown", "prince", "princess", "person-wearing-turban", @@ -351,6 +367,8 @@ "man-with-veil", "woman-with-veil", "pregnant-woman", + "pregnant-man", + "pregnant-person", "breastfeeding", "woman-feeding-baby", "man-feeding-baby", @@ -386,6 +404,7 @@ "zombie", "man-zombie", "woman-zombie", + "troll", "person-getting-massage", "man-getting-massage", "woman-getting-massage", @@ -623,6 +642,7 @@ "shark", "octopus", "spiral-shell", + "coral", "snail", "butterfly", "bug", @@ -642,6 +662,7 @@ "bouquet", "cherry-blossom", "white-flower", + "lotus", "rosette", "rose", "wilted-flower", @@ -661,7 +682,9 @@ "four-leaf-clover", "maple-leaf", "fallen-leaf", - "leaf-fluttering-in-wind" + "leaf-fluttering-in-wind", + "empty-nest", + "nest-with-eggs" ] }, { @@ -701,6 +724,7 @@ "onion", "mushroom", "peanuts", + "beans", "chestnut", "bread", "croissant", @@ -786,6 +810,7 @@ "clinking-beer-mugs", "clinking-glasses", "tumbler-glass", + "pouring-liquid", "cup-with-straw", "bubble-tea", "beverage-box", @@ -796,6 +821,7 @@ "fork-and-knife", "spoon", "kitchen-knife", + "jar", "amphora" ] }, @@ -864,6 +890,7 @@ "bridge-at-night", "hot-springs", "carousel-horse", + "playground-slide", "ferris-wheel", "roller-coaster", "barber-pole", @@ -912,12 +939,14 @@ "railway-track", "oil-drum", "fuel-pump", + "wheel", "police-car-light", "horizontal-traffic-light", "vertical-traffic-light", "stop-sign", "construction", "anchor", + "ring-buoy", "sailboat", "canoe", "speedboat", @@ -1085,6 +1114,7 @@ "crystal-ball", "magic-wand", "nazar-amulet", + "hamsa", "video-game", "joystick", "slot-machine", @@ -1092,6 +1122,7 @@ "puzzle-piece", "teddy-bear", "piata", + "mirror-ball", "nesting-dolls", "spade-suit", "heart-suit", @@ -1193,6 +1224,7 @@ "pager", "fax-machine", "battery", + "low-battery", "electric-plug", "laptop", "desktop-computer", @@ -1333,7 +1365,9 @@ "drop-of-blood", "pill", "adhesive-bandage", + "crutch", "stethoscope", + "xray", "door", "elevator", "mirror", @@ -1354,6 +1388,7 @@ "roll-of-paper", "bucket", "soap", + "bubbles", "toothbrush", "sponge", "fire-extinguisher", @@ -1363,7 +1398,8 @@ "headstone", "funeral-urn", "moai", - "placard" + "placard", + "identification-card" ] }, { @@ -1473,6 +1509,7 @@ "plus", "minus", "divide", + "heavy-equals-sign", "infinity", "double-exclamation-mark", "exclamation-question-mark", @@ -2012,6 +2049,16 @@ "smile" ] }, + "melting-face": { + "a": "⊛ Melting Face", + "b": "1FAE0", + "j": [ + "disappear", + "dissolve", + "liquid", + "melt" + ] + }, "winking-face": { "a": "Winking Face", "b": "1F609", @@ -2271,13 +2318,16 @@ "dollar" ] }, - "hugging-face": { - "a": "Hugging Face", + "smiling-face-with-open-hands": { + "a": "Smiling Face with Open Hands", "b": "1F917", "j": [ "face", "hug", "hugging", + "open hands", + "smiling face", + "hugging_face", "smile" ] }, @@ -2292,6 +2342,27 @@ "face" ] }, + "face-with-open-eyes-and-hand-over-mouth": { + "a": "⊛ Face with Open Eyes and Hand over Mouth", + "b": "1FAE2", + "j": [ + "amazement", + "awe", + "disbelief", + "embarrass", + "scared", + "surprise" + ] + }, + "face-with-peeking-eye": { + "a": "⊛ Face with Peeking Eye", + "b": "1FAE3", + "j": [ + "captivated", + "peep", + "stare" + ] + }, "shushing-face": { "a": "Shushing Face", "b": "1F92B", @@ -2313,6 +2384,17 @@ "consider" ] }, + "saluting-face": { + "a": "⊛ Saluting Face", + "b": "1FAE1", + "j": [ + "ok", + "salute", + "sunny", + "troops", + "yes" + ] + }, "zippermouth-face": { "a": "Zipper-Mouth Face", "b": "1F910", @@ -2377,8 +2459,19 @@ "hellokitty" ] }, + "dotted-line-face": { + "a": "⊛ Dotted Line Face", + "b": "1FAE5", + "j": [ + "depressed", + "disappear", + "hide", + "introvert", + "invisible" + ] + }, "face-in-clouds": { - "a": "⊛ Face in Clouds", + "a": "Face in Clouds", "b": "1F636-200D-1F32B-FE0F", "j": [ "absentminded", @@ -2439,7 +2532,7 @@ ] }, "face-exhaling": { - "a": "⊛ Face Exhaling", + "a": "Face Exhaling", "b": "1F62E-200D-1F4A8", "j": [ "exhale", @@ -2631,14 +2724,15 @@ "wavy" ] }, - "knockedout-face": { - "a": "Knocked-out Face", + "face-with-crossedout-eyes": { + "a": "Face with Crossed-out Eyes", "b": "1F635", "j": [ + "crossed-out eyes", "dead", "face", + "face with crossed-out eyes", "knocked out", - "knocked-out face", "dizzy_face", "spent", "unconscious", @@ -2647,7 +2741,7 @@ ] }, "face-with-spiral-eyes": { - "a": "⊛ Face with Spiral Eyes", + "a": "Face with Spiral Eyes", "b": "1F635-200D-1F4AB", "j": [ "dizzy", @@ -2754,6 +2848,16 @@ ":/" ] }, + "face-with-diagonal-mouth": { + "a": "⊛ Face with Diagonal Mouth", + "b": "1FAE4", + "j": [ + "disappointed", + "meh", + "skeptical", + "unsure" + ] + }, "worried-face": { "a": "Worried Face", "b": "1F61F", @@ -2849,6 +2953,17 @@ "face" ] }, + "face-holding-back-tears": { + "a": "⊛ Face Holding Back Tears", + "b": "1F979", + "j": [ + "angry", + "cry", + "proud", + "resist", + "sad" + ] + }, "frowning-face-with-open-mouth": { "a": "Frowning Face with Open Mouth", "b": "1F626", @@ -3584,7 +3699,7 @@ ] }, "heart-on-fire": { - "a": "⊛ Heart on Fire", + "a": "Heart on Fire", "b": "2764-FE0F-200D-1F525", "j": [ "burn", @@ -3596,7 +3711,7 @@ ] }, "mending-heart": { - "a": "⊛ Mending Heart", + "a": "Mending Heart", "b": "2764-FE0F-200D-1FA79", "j": [ "healthier", @@ -3933,6 +4048,43 @@ "star trek" ] }, + "rightwards-hand": { + "a": "⊛ Rightwards Hand", + "b": "1FAF1", + "j": [ + "hand", + "right", + "rightward" + ] + }, + "leftwards-hand": { + "a": "⊛ Leftwards Hand", + "b": "1FAF2", + "j": [ + "hand", + "left", + "leftward" + ] + }, + "palm-down-hand": { + "a": "⊛ Palm Down Hand", + "b": "1FAF3", + "j": [ + "dismiss", + "drop", + "shoo" + ] + }, + "palm-up-hand": { + "a": "⊛ Palm Up Hand", + "b": "1FAF4", + "j": [ + "beckon", + "catch", + "come", + "offer" + ] + }, "ok-hand": { "a": "Ok Hand", "b": "1F44C", @@ -3995,6 +4147,17 @@ "lucky" ] }, + "hand-with-index-finger-and-thumb-crossed": { + "a": "⊛ Hand with Index Finger and Thumb Crossed", + "b": "1FAF0", + "j": [ + "expensive", + "heart", + "love", + "money", + "snap" + ] + }, "loveyou-gesture": { "a": "Love-You Gesture", "b": "1F91F", @@ -4110,6 +4273,14 @@ "direction" ] }, + "index-pointing-at-the-viewer": { + "a": "⊛ Index Pointing at the Viewer", + "b": "1FAF5", + "j": [ + "point", + "you" + ] + }, "thumbs-up": { "a": "Thumbs Up", "b": "1F44D", @@ -4217,6 +4388,13 @@ "hands" ] }, + "heart-hands": { + "a": "⊛ Heart Hands", + "b": "1FAF6", + "j": [ + "love" + ] + }, "open-hands": { "a": "Open Hands", "b": "1F450", @@ -4463,6 +4641,18 @@ "kiss" ] }, + "biting-lip": { + "a": "⊛ Biting Lip", + "b": "1FAE6", + "j": [ + "anxious", + "fear", + "flirting", + "nervous", + "uncomfortable", + "worried" + ] + }, "baby": { "a": "Baby", "b": "1F476", @@ -4552,7 +4742,7 @@ ] }, "man-beard": { - "a": "⊛ Man: Beard", + "a": "Man: Beard", "b": "1F9D4-200D-2642-FE0F", "j": [ "beard", @@ -4561,7 +4751,7 @@ ] }, "woman-beard": { - "a": "⊛ Woman: Beard", + "a": "Woman: Beard", "b": "1F9D4-200D-2640-FE0F", "j": [ "beard", @@ -5847,6 +6037,16 @@ "labor" ] }, + "person-with-crown": { + "a": "⊛ Person with Crown", + "b": "1FAC5", + "j": [ + "monarch", + "noble", + "regal", + "royalty" + ] + }, "prince": { "a": "Prince", "b": "1F934", @@ -6010,6 +6210,26 @@ "baby" ] }, + "pregnant-man": { + "a": "⊛ Pregnant Man", + "b": "1FAC3", + "j": [ + "belly", + "bloated", + "full", + "pregnant" + ] + }, + "pregnant-person": { + "a": "⊛ Pregnant Person", + "b": "1FAC4", + "j": [ + "belly", + "bloated", + "full", + "pregnant" + ] + }, "breastfeeding": { "a": "Breast-Feeding", "b": "1F931", @@ -6395,6 +6615,15 @@ "female" ] }, + "troll": { + "a": "⊛ Troll", + "b": "1F9CC", + "j": [ + "fairy tale", + "fantasy", + "monster" + ] + }, "person-getting-massage": { "a": "Person Getting Massage", "b": "1F486", @@ -8625,7 +8854,8 @@ "a": "Koala", "b": "1F428", "j": [ - "bear", + "face", + "marsupial", "animal", "nature" ] @@ -9125,6 +9355,14 @@ "beach" ] }, + "coral": { + "a": "⊛ Coral", + "b": "1FAB8", + "j": [ + "ocean", + "reef" + ] + }, "snail": { "a": "Snail", "b": "1F40C", @@ -9326,6 +9564,18 @@ "spring" ] }, + "lotus": { + "a": "⊛ Lotus", + "b": "1FAB7", + "j": [ + "Buddhism", + "flower", + "Hinduism", + "India", + "purity", + "Vietnam" + ] + }, "rosette": { "a": "Rosette", "b": "1F3F5", @@ -9564,6 +9814,20 @@ "spring" ] }, + "empty-nest": { + "a": "⊛ Empty Nest", + "b": "1FAB9", + "j": [ + "nesting" + ] + }, + "nest-with-eggs": { + "a": "⊛ Nest with Eggs", + "b": "1FABA", + "j": [ + "nesting" + ] + }, "grapes": { "a": "Grapes", "b": "1F347", @@ -9894,6 +10158,15 @@ "vegetable" ] }, + "beans": { + "a": "⊛ Beans", + "b": "1FAD8", + "j": [ + "food", + "kidney", + "legume" + ] + }, "chestnut": { "a": "Chestnut", "b": "1F330", @@ -10893,6 +11166,16 @@ "scotch" ] }, + "pouring-liquid": { + "a": "⊛ Pouring Liquid", + "b": "1FAD7", + "j": [ + "drink", + "empty", + "glass", + "spill" + ] + }, "cup-with-straw": { "a": "Cup with Straw", "b": "1F964", @@ -11010,6 +11293,17 @@ "kitchen" ] }, + "jar": { + "a": "⊛ Jar", + "b": "1FAD9", + "j": [ + "condiment", + "container", + "empty", + "sauce", + "store" + ] + }, "amphora": { "a": "Amphora", "b": "1F3FA", @@ -11690,6 +11984,14 @@ "carnival" ] }, + "playground-slide": { + "a": "⊛ Playground Slide", + "b": "1F6DD", + "j": [ + "amusement park", + "play" + ] + }, "ferris-wheel": { "a": "Ferris Wheel", "b": "1F3A1", @@ -12164,7 +12466,6 @@ "b": "1F68F", "j": [ "bus", - "busstop", "stop", "transportation", "wait" @@ -12212,6 +12513,15 @@ "petroleum" ] }, + "wheel": { + "a": "⊛ Wheel", + "b": "1F6DE", + "j": [ + "circle", + "tire", + "turn" + ] + }, "police-car-light": { "a": "Police Car Light", "b": "1F6A8", @@ -12283,6 +12593,17 @@ "boat" ] }, + "ring-buoy": { + "a": "⊛ Ring Buoy", + "b": "1F6DF", + "j": [ + "float", + "life preserver", + "life saver", + "rescue", + "safety" + ] + }, "sailboat": { "a": "Sailboat", "b": "26F5", @@ -14322,6 +14643,18 @@ "talisman" ] }, + "hamsa": { + "a": "⊛ Hamsa", + "b": "1FAAC", + "j": [ + "amulet", + "Fatima", + "hand", + "Mary", + "Miriam", + "protection" + ] + }, "video-game": { "a": "Video Game", "b": "1F3AE", @@ -14402,6 +14735,16 @@ "candy" ] }, + "mirror-ball": { + "a": "⊛ Mirror Ball", + "b": "1FAA9", + "j": [ + "dance", + "disco", + "glitter", + "party" + ] + }, "nesting-dolls": { "a": "Nesting Dolls", "b": "1FA86", @@ -15502,6 +15845,14 @@ "sustain" ] }, + "low-battery": { + "a": "⊛ Low Battery", + "b": "1FAAB", + "j": [ + "electronic", + "low energy" + ] + }, "electric-plug": { "a": "Electric Plug", "b": "1F50C", @@ -17135,6 +17486,17 @@ "heal" ] }, + "crutch": { + "a": "⊛ Crutch", + "b": "1FA7C", + "j": [ + "cane", + "disability", + "hurt", + "mobility aid", + "stick" + ] + }, "stethoscope": { "a": "Stethoscope", "b": "1FA7A", @@ -17145,6 +17507,16 @@ "health" ] }, + "xray": { + "a": "⊛ X-Ray", + "b": "1FA7B", + "j": [ + "bones", + "doctor", + "medical", + "skeleton" + ] + }, "door": { "a": "Door", "b": "1F6AA", @@ -17340,6 +17712,16 @@ "soapdish" ] }, + "bubbles": { + "a": "⊛ Bubbles", + "b": "1FAE7", + "j": [ + "burp", + "clean", + "soap", + "underwater" + ] + }, "toothbrush": { "a": "Toothbrush", "b": "1FAA5", @@ -17453,6 +17835,16 @@ "announcement" ] }, + "identification-card": { + "a": "⊛ Identification Card", + "b": "1FAAA", + "j": [ + "credentials", + "ID", + "license", + "security" + ] + }, "atm-sign": { "a": "Atm Sign", "b": "1F3E7", @@ -18715,6 +19107,14 @@ "calculation" ] }, + "heavy-equals-sign": { + "a": "⊛ Heavy Equals Sign", + "b": "1F7F0", + "j": [ + "equality", + "math" + ] + }, "infinity": { "a": "Infinity", "b": "267E", @@ -20247,7 +20647,8 @@ "ad", "nation", "country", - "banner" + "banner", + "andorra" ] }, "flag-united-arab-emirates": { @@ -20260,7 +20661,8 @@ "emirates", "nation", "country", - "banner" + "banner", + "united_arab_emirates" ] }, "flag-afghanistan": { @@ -20271,7 +20673,8 @@ "af", "nation", "country", - "banner" + "banner", + "afghanistan" ] }, "flag-antigua--barbuda": { @@ -20284,7 +20687,8 @@ "barbuda", "nation", "country", - "banner" + "banner", + "antigua_barbuda" ] }, "flag-anguilla": { @@ -20295,7 +20699,8 @@ "ai", "nation", "country", - "banner" + "banner", + "anguilla" ] }, "flag-albania": { @@ -20306,7 +20711,8 @@ "al", "nation", "country", - "banner" + "banner", + "albania" ] }, "flag-armenia": { @@ -20317,7 +20723,8 @@ "am", "nation", "country", - "banner" + "banner", + "armenia" ] }, "flag-angola": { @@ -20328,7 +20735,8 @@ "ao", "nation", "country", - "banner" + "banner", + "angola" ] }, "flag-antarctica": { @@ -20339,7 +20747,8 @@ "aq", "nation", "country", - "banner" + "banner", + "antarctica" ] }, "flag-argentina": { @@ -20350,7 +20759,8 @@ "ar", "nation", "country", - "banner" + "banner", + "argentina" ] }, "flag-american-samoa": { @@ -20362,7 +20772,8 @@ "ws", "nation", "country", - "banner" + "banner", + "american_samoa" ] }, "flag-austria": { @@ -20373,7 +20784,8 @@ "at", "nation", "country", - "banner" + "banner", + "austria" ] }, "flag-australia": { @@ -20384,7 +20796,8 @@ "au", "nation", "country", - "banner" + "banner", + "australia" ] }, "flag-aruba": { @@ -20395,7 +20808,8 @@ "aw", "nation", "country", - "banner" + "banner", + "aruba" ] }, "flag-land-islands": { @@ -20408,7 +20822,8 @@ "islands", "nation", "country", - "banner" + "banner", + "aland_islands" ] }, "flag-azerbaijan": { @@ -20419,7 +20834,8 @@ "az", "nation", "country", - "banner" + "banner", + "azerbaijan" ] }, "flag-bosnia--herzegovina": { @@ -20432,7 +20848,8 @@ "herzegovina", "nation", "country", - "banner" + "banner", + "bosnia_herzegovina" ] }, "flag-barbados": { @@ -20443,7 +20860,8 @@ "bb", "nation", "country", - "banner" + "banner", + "barbados" ] }, "flag-bangladesh": { @@ -20454,7 +20872,8 @@ "bd", "nation", "country", - "banner" + "banner", + "bangladesh" ] }, "flag-belgium": { @@ -20465,7 +20884,8 @@ "be", "nation", "country", - "banner" + "banner", + "belgium" ] }, "flag-burkina-faso": { @@ -20477,7 +20897,8 @@ "faso", "nation", "country", - "banner" + "banner", + "burkina_faso" ] }, "flag-bulgaria": { @@ -20488,7 +20909,8 @@ "bg", "nation", "country", - "banner" + "banner", + "bulgaria" ] }, "flag-bahrain": { @@ -20499,7 +20921,8 @@ "bh", "nation", "country", - "banner" + "banner", + "bahrain" ] }, "flag-burundi": { @@ -20510,7 +20933,8 @@ "bi", "nation", "country", - "banner" + "banner", + "burundi" ] }, "flag-benin": { @@ -20521,7 +20945,8 @@ "bj", "nation", "country", - "banner" + "banner", + "benin" ] }, "flag-st-barthlemy": { @@ -20534,7 +20959,8 @@ "barthélemy", "nation", "country", - "banner" + "banner", + "st_barthelemy" ] }, "flag-bermuda": { @@ -20545,7 +20971,8 @@ "bm", "nation", "country", - "banner" + "banner", + "bermuda" ] }, "flag-brunei": { @@ -20557,7 +20984,8 @@ "darussalam", "nation", "country", - "banner" + "banner", + "brunei" ] }, "flag-bolivia": { @@ -20568,7 +20996,8 @@ "bo", "nation", "country", - "banner" + "banner", + "bolivia" ] }, "flag-caribbean-netherlands": { @@ -20579,7 +21008,8 @@ "bonaire", "nation", "country", - "banner" + "banner", + "caribbean_netherlands" ] }, "flag-brazil": { @@ -20590,7 +21020,8 @@ "br", "nation", "country", - "banner" + "banner", + "brazil" ] }, "flag-bahamas": { @@ -20601,7 +21032,8 @@ "bs", "nation", "country", - "banner" + "banner", + "bahamas" ] }, "flag-bhutan": { @@ -20612,7 +21044,8 @@ "bt", "nation", "country", - "banner" + "banner", + "bhutan" ] }, "flag-bouvet-island": { @@ -20631,7 +21064,8 @@ "bw", "nation", "country", - "banner" + "banner", + "botswana" ] }, "flag-belarus": { @@ -20642,7 +21076,8 @@ "by", "nation", "country", - "banner" + "banner", + "belarus" ] }, "flag-belize": { @@ -20653,7 +21088,8 @@ "bz", "nation", "country", - "banner" + "banner", + "belize" ] }, "flag-canada": { @@ -20664,7 +21100,8 @@ "ca", "nation", "country", - "banner" + "banner", + "canada" ] }, "flag-cocos-keeling-islands": { @@ -20678,7 +21115,8 @@ "islands", "nation", "country", - "banner" + "banner", + "cocos_islands" ] }, "flag-congo--kinshasa": { @@ -20692,7 +21130,8 @@ "republic", "nation", "country", - "banner" + "banner", + "congo_kinshasa" ] }, "flag-central-african-republic": { @@ -20705,7 +21144,8 @@ "republic", "nation", "country", - "banner" + "banner", + "central_african_republic" ] }, "flag-congo--brazzaville": { @@ -20717,7 +21157,8 @@ "congo", "nation", "country", - "banner" + "banner", + "congo_brazzaville" ] }, "flag-switzerland": { @@ -20728,7 +21169,8 @@ "ch", "nation", "country", - "banner" + "banner", + "switzerland" ] }, "flag-cte-divoire": { @@ -20741,7 +21183,8 @@ "coast", "nation", "country", - "banner" + "banner", + "cote_d_ivoire" ] }, "flag-cook-islands": { @@ -20753,7 +21196,8 @@ "islands", "nation", "country", - "banner" + "banner", + "cook_islands" ] }, "flag-chile": { @@ -20763,7 +21207,8 @@ "flag", "nation", "country", - "banner" + "banner", + "chile" ] }, "flag-cameroon": { @@ -20774,7 +21219,8 @@ "cm", "nation", "country", - "banner" + "banner", + "cameroon" ] }, "flag-china": { @@ -20798,7 +21244,8 @@ "co", "nation", "country", - "banner" + "banner", + "colombia" ] }, "flag-clipperton-island": { @@ -20817,7 +21264,8 @@ "rica", "nation", "country", - "banner" + "banner", + "costa_rica" ] }, "flag-cuba": { @@ -20828,7 +21276,8 @@ "cu", "nation", "country", - "banner" + "banner", + "cuba" ] }, "flag-cape-verde": { @@ -20840,7 +21289,8 @@ "verde", "nation", "country", - "banner" + "banner", + "cape_verde" ] }, "flag-curaao": { @@ -20852,7 +21302,8 @@ "curaçao", "nation", "country", - "banner" + "banner", + "curacao" ] }, "flag-christmas-island": { @@ -20864,7 +21315,8 @@ "island", "nation", "country", - "banner" + "banner", + "christmas_island" ] }, "flag-cyprus": { @@ -20875,7 +21327,8 @@ "cy", "nation", "country", - "banner" + "banner", + "cyprus" ] }, "flag-czechia": { @@ -20886,7 +21339,8 @@ "cz", "nation", "country", - "banner" + "banner", + "czechia" ] }, "flag-germany": { @@ -20897,7 +21351,8 @@ "german", "nation", "country", - "banner" + "banner", + "germany" ] }, "flag-diego-garcia": { @@ -20915,7 +21370,8 @@ "dj", "nation", "country", - "banner" + "banner", + "djibouti" ] }, "flag-denmark": { @@ -20926,7 +21382,8 @@ "dk", "nation", "country", - "banner" + "banner", + "denmark" ] }, "flag-dominica": { @@ -20937,7 +21394,8 @@ "dm", "nation", "country", - "banner" + "banner", + "dominica" ] }, "flag-dominican-republic": { @@ -20949,7 +21407,8 @@ "republic", "nation", "country", - "banner" + "banner", + "dominican_republic" ] }, "flag-algeria": { @@ -20960,7 +21419,8 @@ "dz", "nation", "country", - "banner" + "banner", + "algeria" ] }, "flag-ceuta--melilla": { @@ -20979,7 +21439,8 @@ "ec", "nation", "country", - "banner" + "banner", + "ecuador" ] }, "flag-estonia": { @@ -20990,7 +21451,8 @@ "ee", "nation", "country", - "banner" + "banner", + "estonia" ] }, "flag-egypt": { @@ -21001,7 +21463,8 @@ "eg", "nation", "country", - "banner" + "banner", + "egypt" ] }, "flag-western-sahara": { @@ -21013,7 +21476,8 @@ "sahara", "nation", "country", - "banner" + "banner", + "western_sahara" ] }, "flag-eritrea": { @@ -21024,7 +21488,8 @@ "er", "nation", "country", - "banner" + "banner", + "eritrea" ] }, "flag-spain": { @@ -21046,7 +21511,8 @@ "et", "nation", "country", - "banner" + "banner", + "ethiopia" ] }, "flag-european-union": { @@ -21067,7 +21533,8 @@ "fi", "nation", "country", - "banner" + "banner", + "finland" ] }, "flag-fiji": { @@ -21078,7 +21545,8 @@ "fj", "nation", "country", - "banner" + "banner", + "fiji" ] }, "flag-falkland-islands": { @@ -21091,7 +21559,8 @@ "malvinas", "nation", "country", - "banner" + "banner", + "falkland_islands" ] }, "flag-micronesia": { @@ -21116,7 +21585,8 @@ "islands", "nation", "country", - "banner" + "banner", + "faroe_islands" ] }, "flag-france": { @@ -21139,7 +21609,8 @@ "ga", "nation", "country", - "banner" + "banner", + "gabon" ] }, "flag-united-kingdom": { @@ -21160,7 +21631,8 @@ "UK", "english", "england", - "union jack" + "union jack", + "united_kingdom" ] }, "flag-grenada": { @@ -21171,7 +21643,8 @@ "gd", "nation", "country", - "banner" + "banner", + "grenada" ] }, "flag-georgia": { @@ -21182,7 +21655,8 @@ "ge", "nation", "country", - "banner" + "banner", + "georgia" ] }, "flag-french-guiana": { @@ -21194,7 +21668,8 @@ "guiana", "nation", "country", - "banner" + "banner", + "french_guiana" ] }, "flag-guernsey": { @@ -21205,7 +21680,8 @@ "gg", "nation", "country", - "banner" + "banner", + "guernsey" ] }, "flag-ghana": { @@ -21216,7 +21692,8 @@ "gh", "nation", "country", - "banner" + "banner", + "ghana" ] }, "flag-gibraltar": { @@ -21227,7 +21704,8 @@ "gi", "nation", "country", - "banner" + "banner", + "gibraltar" ] }, "flag-greenland": { @@ -21238,7 +21716,8 @@ "gl", "nation", "country", - "banner" + "banner", + "greenland" ] }, "flag-gambia": { @@ -21249,7 +21728,8 @@ "gm", "nation", "country", - "banner" + "banner", + "gambia" ] }, "flag-guinea": { @@ -21260,7 +21740,8 @@ "gn", "nation", "country", - "banner" + "banner", + "guinea" ] }, "flag-guadeloupe": { @@ -21271,7 +21752,8 @@ "gp", "nation", "country", - "banner" + "banner", + "guadeloupe" ] }, "flag-equatorial-guinea": { @@ -21283,7 +21765,8 @@ "gn", "nation", "country", - "banner" + "banner", + "equatorial_guinea" ] }, "flag-greece": { @@ -21294,7 +21777,8 @@ "gr", "nation", "country", - "banner" + "banner", + "greece" ] }, "flag-south-georgia--south-sandwich-islands": { @@ -21309,7 +21793,8 @@ "islands", "nation", "country", - "banner" + "banner", + "south_georgia_south_sandwich_islands" ] }, "flag-guatemala": { @@ -21320,7 +21805,8 @@ "gt", "nation", "country", - "banner" + "banner", + "guatemala" ] }, "flag-guam": { @@ -21331,7 +21817,8 @@ "gu", "nation", "country", - "banner" + "banner", + "guam" ] }, "flag-guineabissau": { @@ -21344,7 +21831,8 @@ "bissau", "nation", "country", - "banner" + "banner", + "guinea_bissau" ] }, "flag-guyana": { @@ -21355,7 +21843,8 @@ "gy", "nation", "country", - "banner" + "banner", + "guyana" ] }, "flag-hong-kong-sar-china": { @@ -21367,7 +21856,8 @@ "kong", "nation", "country", - "banner" + "banner", + "hong_kong_sar_china" ] }, "flag-heard--mcdonald-islands": { @@ -21386,7 +21876,8 @@ "hn", "nation", "country", - "banner" + "banner", + "honduras" ] }, "flag-croatia": { @@ -21397,7 +21888,8 @@ "hr", "nation", "country", - "banner" + "banner", + "croatia" ] }, "flag-haiti": { @@ -21408,7 +21900,8 @@ "ht", "nation", "country", - "banner" + "banner", + "haiti" ] }, "flag-hungary": { @@ -21419,7 +21912,8 @@ "hu", "nation", "country", - "banner" + "banner", + "hungary" ] }, "flag-canary-islands": { @@ -21431,7 +21925,8 @@ "islands", "nation", "country", - "banner" + "banner", + "canary_islands" ] }, "flag-indonesia": { @@ -21441,7 +21936,8 @@ "flag", "nation", "country", - "banner" + "banner", + "indonesia" ] }, "flag-ireland": { @@ -21452,7 +21948,8 @@ "ie", "nation", "country", - "banner" + "banner", + "ireland" ] }, "flag-israel": { @@ -21463,7 +21960,8 @@ "il", "nation", "country", - "banner" + "banner", + "israel" ] }, "flag-isle-of-man": { @@ -21475,7 +21973,8 @@ "man", "nation", "country", - "banner" + "banner", + "isle_of_man" ] }, "flag-india": { @@ -21486,7 +21985,8 @@ "in", "nation", "country", - "banner" + "banner", + "india" ] }, "flag-british-indian-ocean-territory": { @@ -21500,7 +22000,8 @@ "territory", "nation", "country", - "banner" + "banner", + "british_indian_ocean_territory" ] }, "flag-iraq": { @@ -21511,7 +22012,8 @@ "iq", "nation", "country", - "banner" + "banner", + "iraq" ] }, "flag-iran": { @@ -21535,7 +22037,8 @@ "is", "nation", "country", - "banner" + "banner", + "iceland" ] }, "flag-italy": { @@ -21557,7 +22060,8 @@ "je", "nation", "country", - "banner" + "banner", + "jersey" ] }, "flag-jamaica": { @@ -21568,7 +22072,8 @@ "jm", "nation", "country", - "banner" + "banner", + "jamaica" ] }, "flag-jordan": { @@ -21579,7 +22084,8 @@ "jo", "nation", "country", - "banner" + "banner", + "jordan" ] }, "flag-japan": { @@ -21590,7 +22096,8 @@ "japanese", "nation", "country", - "banner" + "banner", + "japan" ] }, "flag-kenya": { @@ -21601,7 +22108,8 @@ "ke", "nation", "country", - "banner" + "banner", + "kenya" ] }, "flag-kyrgyzstan": { @@ -21612,7 +22120,8 @@ "kg", "nation", "country", - "banner" + "banner", + "kyrgyzstan" ] }, "flag-cambodia": { @@ -21623,7 +22132,8 @@ "kh", "nation", "country", - "banner" + "banner", + "cambodia" ] }, "flag-kiribati": { @@ -21634,7 +22144,8 @@ "ki", "nation", "country", - "banner" + "banner", + "kiribati" ] }, "flag-comoros": { @@ -21645,7 +22156,8 @@ "km", "nation", "country", - "banner" + "banner", + "comoros" ] }, "flag-st-kitts--nevis": { @@ -21659,7 +22171,8 @@ "nevis", "nation", "country", - "banner" + "banner", + "st_kitts_nevis" ] }, "flag-north-korea": { @@ -21671,7 +22184,8 @@ "korea", "nation", "country", - "banner" + "banner", + "north_korea" ] }, "flag-south-korea": { @@ -21683,7 +22197,8 @@ "korea", "nation", "country", - "banner" + "banner", + "south_korea" ] }, "flag-kuwait": { @@ -21694,7 +22209,8 @@ "kw", "nation", "country", - "banner" + "banner", + "kuwait" ] }, "flag-cayman-islands": { @@ -21706,7 +22222,8 @@ "islands", "nation", "country", - "banner" + "banner", + "cayman_islands" ] }, "flag-kazakhstan": { @@ -21717,7 +22234,8 @@ "kz", "nation", "country", - "banner" + "banner", + "kazakhstan" ] }, "flag-laos": { @@ -21730,7 +22248,8 @@ "republic", "nation", "country", - "banner" + "banner", + "laos" ] }, "flag-lebanon": { @@ -21741,7 +22260,8 @@ "lb", "nation", "country", - "banner" + "banner", + "lebanon" ] }, "flag-st-lucia": { @@ -21753,7 +22273,8 @@ "lucia", "nation", "country", - "banner" + "banner", + "st_lucia" ] }, "flag-liechtenstein": { @@ -21764,7 +22285,8 @@ "li", "nation", "country", - "banner" + "banner", + "liechtenstein" ] }, "flag-sri-lanka": { @@ -21776,7 +22298,8 @@ "lanka", "nation", "country", - "banner" + "banner", + "sri_lanka" ] }, "flag-liberia": { @@ -21787,7 +22310,8 @@ "lr", "nation", "country", - "banner" + "banner", + "liberia" ] }, "flag-lesotho": { @@ -21798,7 +22322,8 @@ "ls", "nation", "country", - "banner" + "banner", + "lesotho" ] }, "flag-lithuania": { @@ -21809,7 +22334,8 @@ "lt", "nation", "country", - "banner" + "banner", + "lithuania" ] }, "flag-luxembourg": { @@ -21820,7 +22346,8 @@ "lu", "nation", "country", - "banner" + "banner", + "luxembourg" ] }, "flag-latvia": { @@ -21831,7 +22358,8 @@ "lv", "nation", "country", - "banner" + "banner", + "latvia" ] }, "flag-libya": { @@ -21842,7 +22370,8 @@ "ly", "nation", "country", - "banner" + "banner", + "libya" ] }, "flag-morocco": { @@ -21853,7 +22382,8 @@ "ma", "nation", "country", - "banner" + "banner", + "morocco" ] }, "flag-monaco": { @@ -21864,7 +22394,8 @@ "mc", "nation", "country", - "banner" + "banner", + "monaco" ] }, "flag-moldova": { @@ -21887,7 +22418,8 @@ "me", "nation", "country", - "banner" + "banner", + "montenegro" ] }, "flag-st-martin": { @@ -21905,7 +22437,8 @@ "mg", "nation", "country", - "banner" + "banner", + "madagascar" ] }, "flag-marshall-islands": { @@ -21917,7 +22450,8 @@ "islands", "nation", "country", - "banner" + "banner", + "marshall_islands" ] }, "flag-north-macedonia": { @@ -21928,7 +22462,8 @@ "macedonia", "nation", "country", - "banner" + "banner", + "north_macedonia" ] }, "flag-mali": { @@ -21939,7 +22474,8 @@ "ml", "nation", "country", - "banner" + "banner", + "mali" ] }, "flag-myanmar-burma": { @@ -21951,7 +22487,8 @@ "mm", "nation", "country", - "banner" + "banner", + "myanmar" ] }, "flag-mongolia": { @@ -21962,7 +22499,8 @@ "mn", "nation", "country", - "banner" + "banner", + "mongolia" ] }, "flag-macao-sar-china": { @@ -21973,7 +22511,8 @@ "macao", "nation", "country", - "banner" + "banner", + "macao_sar_china" ] }, "flag-northern-mariana-islands": { @@ -21986,7 +22525,8 @@ "islands", "nation", "country", - "banner" + "banner", + "northern_mariana_islands" ] }, "flag-martinique": { @@ -21997,7 +22537,8 @@ "mq", "nation", "country", - "banner" + "banner", + "martinique" ] }, "flag-mauritania": { @@ -22008,7 +22549,8 @@ "mr", "nation", "country", - "banner" + "banner", + "mauritania" ] }, "flag-montserrat": { @@ -22019,7 +22561,8 @@ "ms", "nation", "country", - "banner" + "banner", + "montserrat" ] }, "flag-malta": { @@ -22030,7 +22573,8 @@ "mt", "nation", "country", - "banner" + "banner", + "malta" ] }, "flag-mauritius": { @@ -22041,7 +22585,8 @@ "mu", "nation", "country", - "banner" + "banner", + "mauritius" ] }, "flag-maldives": { @@ -22052,7 +22597,8 @@ "mv", "nation", "country", - "banner" + "banner", + "maldives" ] }, "flag-malawi": { @@ -22063,7 +22609,8 @@ "mw", "nation", "country", - "banner" + "banner", + "malawi" ] }, "flag-mexico": { @@ -22074,7 +22621,8 @@ "mx", "nation", "country", - "banner" + "banner", + "mexico" ] }, "flag-malaysia": { @@ -22085,7 +22633,8 @@ "my", "nation", "country", - "banner" + "banner", + "malaysia" ] }, "flag-mozambique": { @@ -22096,7 +22645,8 @@ "mz", "nation", "country", - "banner" + "banner", + "mozambique" ] }, "flag-namibia": { @@ -22107,7 +22657,8 @@ "na", "nation", "country", - "banner" + "banner", + "namibia" ] }, "flag-new-caledonia": { @@ -22119,7 +22670,8 @@ "caledonia", "nation", "country", - "banner" + "banner", + "new_caledonia" ] }, "flag-niger": { @@ -22130,7 +22682,8 @@ "ne", "nation", "country", - "banner" + "banner", + "niger" ] }, "flag-norfolk-island": { @@ -22142,7 +22695,8 @@ "island", "nation", "country", - "banner" + "banner", + "norfolk_island" ] }, "flag-nigeria": { @@ -22152,7 +22706,8 @@ "flag", "nation", "country", - "banner" + "banner", + "nigeria" ] }, "flag-nicaragua": { @@ -22163,7 +22718,8 @@ "ni", "nation", "country", - "banner" + "banner", + "nicaragua" ] }, "flag-netherlands": { @@ -22174,7 +22730,8 @@ "nl", "nation", "country", - "banner" + "banner", + "netherlands" ] }, "flag-norway": { @@ -22185,7 +22742,8 @@ "no", "nation", "country", - "banner" + "banner", + "norway" ] }, "flag-nepal": { @@ -22196,7 +22754,8 @@ "np", "nation", "country", - "banner" + "banner", + "nepal" ] }, "flag-nauru": { @@ -22207,7 +22766,8 @@ "nr", "nation", "country", - "banner" + "banner", + "nauru" ] }, "flag-niue": { @@ -22218,7 +22778,8 @@ "nu", "nation", "country", - "banner" + "banner", + "niue" ] }, "flag-new-zealand": { @@ -22230,7 +22791,8 @@ "zealand", "nation", "country", - "banner" + "banner", + "new_zealand" ] }, "flag-oman": { @@ -22241,7 +22803,8 @@ "om_symbol", "nation", "country", - "banner" + "banner", + "oman" ] }, "flag-panama": { @@ -22252,7 +22815,8 @@ "pa", "nation", "country", - "banner" + "banner", + "panama" ] }, "flag-peru": { @@ -22263,7 +22827,8 @@ "pe", "nation", "country", - "banner" + "banner", + "peru" ] }, "flag-french-polynesia": { @@ -22275,7 +22840,8 @@ "polynesia", "nation", "country", - "banner" + "banner", + "french_polynesia" ] }, "flag-papua-new-guinea": { @@ -22288,7 +22854,8 @@ "guinea", "nation", "country", - "banner" + "banner", + "papua_new_guinea" ] }, "flag-philippines": { @@ -22299,7 +22866,8 @@ "ph", "nation", "country", - "banner" + "banner", + "philippines" ] }, "flag-pakistan": { @@ -22310,7 +22878,8 @@ "pk", "nation", "country", - "banner" + "banner", + "pakistan" ] }, "flag-poland": { @@ -22321,7 +22890,8 @@ "pl", "nation", "country", - "banner" + "banner", + "poland" ] }, "flag-st-pierre--miquelon": { @@ -22335,7 +22905,8 @@ "miquelon", "nation", "country", - "banner" + "banner", + "st_pierre_miquelon" ] }, "flag-pitcairn-islands": { @@ -22346,7 +22917,8 @@ "pitcairn", "nation", "country", - "banner" + "banner", + "pitcairn_islands" ] }, "flag-puerto-rico": { @@ -22358,7 +22930,8 @@ "rico", "nation", "country", - "banner" + "banner", + "puerto_rico" ] }, "flag-palestinian-territories": { @@ -22371,7 +22944,8 @@ "territories", "nation", "country", - "banner" + "banner", + "palestinian_territories" ] }, "flag-portugal": { @@ -22382,7 +22956,8 @@ "pt", "nation", "country", - "banner" + "banner", + "portugal" ] }, "flag-palau": { @@ -22393,7 +22968,8 @@ "pw", "nation", "country", - "banner" + "banner", + "palau" ] }, "flag-paraguay": { @@ -22404,7 +22980,8 @@ "py", "nation", "country", - "banner" + "banner", + "paraguay" ] }, "flag-qatar": { @@ -22415,7 +22992,8 @@ "qa", "nation", "country", - "banner" + "banner", + "qatar" ] }, "flag-runion": { @@ -22427,7 +23005,8 @@ "réunion", "nation", "country", - "banner" + "banner", + "reunion" ] }, "flag-romania": { @@ -22438,7 +23017,8 @@ "ro", "nation", "country", - "banner" + "banner", + "romania" ] }, "flag-serbia": { @@ -22449,7 +23029,8 @@ "rs", "nation", "country", - "banner" + "banner", + "serbia" ] }, "flag-russia": { @@ -22461,7 +23042,8 @@ "federation", "nation", "country", - "banner" + "banner", + "russia" ] }, "flag-rwanda": { @@ -22472,7 +23054,8 @@ "rw", "nation", "country", - "banner" + "banner", + "rwanda" ] }, "flag-saudi-arabia": { @@ -22482,7 +23065,8 @@ "flag", "nation", "country", - "banner" + "banner", + "saudi_arabia" ] }, "flag-solomon-islands": { @@ -22494,7 +23078,8 @@ "islands", "nation", "country", - "banner" + "banner", + "solomon_islands" ] }, "flag-seychelles": { @@ -22505,7 +23090,8 @@ "sc", "nation", "country", - "banner" + "banner", + "seychelles" ] }, "flag-sudan": { @@ -22516,7 +23102,8 @@ "sd", "nation", "country", - "banner" + "banner", + "sudan" ] }, "flag-sweden": { @@ -22527,7 +23114,8 @@ "se", "nation", "country", - "banner" + "banner", + "sweden" ] }, "flag-singapore": { @@ -22538,7 +23126,8 @@ "sg", "nation", "country", - "banner" + "banner", + "singapore" ] }, "flag-st-helena": { @@ -22553,7 +23142,8 @@ "cunha", "nation", "country", - "banner" + "banner", + "st_helena" ] }, "flag-slovenia": { @@ -22564,7 +23154,8 @@ "si", "nation", "country", - "banner" + "banner", + "slovenia" ] }, "flag-svalbard--jan-mayen": { @@ -22583,7 +23174,8 @@ "sk", "nation", "country", - "banner" + "banner", + "slovakia" ] }, "flag-sierra-leone": { @@ -22595,7 +23187,8 @@ "leone", "nation", "country", - "banner" + "banner", + "sierra_leone" ] }, "flag-san-marino": { @@ -22607,7 +23200,8 @@ "marino", "nation", "country", - "banner" + "banner", + "san_marino" ] }, "flag-senegal": { @@ -22618,7 +23212,8 @@ "sn", "nation", "country", - "banner" + "banner", + "senegal" ] }, "flag-somalia": { @@ -22629,7 +23224,8 @@ "so", "nation", "country", - "banner" + "banner", + "somalia" ] }, "flag-suriname": { @@ -22640,7 +23236,8 @@ "sr", "nation", "country", - "banner" + "banner", + "suriname" ] }, "flag-south-sudan": { @@ -22652,7 +23249,8 @@ "sd", "nation", "country", - "banner" + "banner", + "south_sudan" ] }, "flag-so-tom--prncipe": { @@ -22666,7 +23264,8 @@ "principe", "nation", "country", - "banner" + "banner", + "sao_tome_principe" ] }, "flag-el-salvador": { @@ -22678,7 +23277,8 @@ "salvador", "nation", "country", - "banner" + "banner", + "el_salvador" ] }, "flag-sint-maarten": { @@ -22691,7 +23291,8 @@ "dutch", "nation", "country", - "banner" + "banner", + "sint_maarten" ] }, "flag-syria": { @@ -22704,7 +23305,8 @@ "republic", "nation", "country", - "banner" + "banner", + "syria" ] }, "flag-eswatini": { @@ -22715,7 +23317,8 @@ "sz", "nation", "country", - "banner" + "banner", + "eswatini" ] }, "flag-tristan-da-cunha": { @@ -22736,7 +23339,8 @@ "islands", "nation", "country", - "banner" + "banner", + "turks_caicos_islands" ] }, "flag-chad": { @@ -22747,7 +23351,8 @@ "td", "nation", "country", - "banner" + "banner", + "chad" ] }, "flag-french-southern-territories": { @@ -22760,7 +23365,8 @@ "territories", "nation", "country", - "banner" + "banner", + "french_southern_territories" ] }, "flag-togo": { @@ -22771,7 +23377,8 @@ "tg", "nation", "country", - "banner" + "banner", + "togo" ] }, "flag-thailand": { @@ -22782,7 +23389,8 @@ "th", "nation", "country", - "banner" + "banner", + "thailand" ] }, "flag-tajikistan": { @@ -22793,7 +23401,8 @@ "tj", "nation", "country", - "banner" + "banner", + "tajikistan" ] }, "flag-tokelau": { @@ -22804,7 +23413,8 @@ "tk", "nation", "country", - "banner" + "banner", + "tokelau" ] }, "flag-timorleste": { @@ -22817,7 +23427,8 @@ "leste", "nation", "country", - "banner" + "banner", + "timor_leste" ] }, "flag-turkmenistan": { @@ -22827,7 +23438,8 @@ "flag", "nation", "country", - "banner" + "banner", + "turkmenistan" ] }, "flag-tunisia": { @@ -22838,7 +23450,8 @@ "tn", "nation", "country", - "banner" + "banner", + "tunisia" ] }, "flag-tonga": { @@ -22849,7 +23462,8 @@ "to", "nation", "country", - "banner" + "banner", + "tonga" ] }, "flag-turkey": { @@ -22873,7 +23487,8 @@ "tobago", "nation", "country", - "banner" + "banner", + "trinidad_tobago" ] }, "flag-tuvalu": { @@ -22883,7 +23498,8 @@ "flag", "nation", "country", - "banner" + "banner", + "tuvalu" ] }, "flag-taiwan": { @@ -22894,7 +23510,8 @@ "tw", "nation", "country", - "banner" + "banner", + "taiwan" ] }, "flag-tanzania": { @@ -22918,7 +23535,8 @@ "ua", "nation", "country", - "banner" + "banner", + "ukraine" ] }, "flag-uganda": { @@ -22929,7 +23547,8 @@ "ug", "nation", "country", - "banner" + "banner", + "uganda" ] }, "flag-us-outlying-islands": { @@ -22959,7 +23578,8 @@ "america", "nation", "country", - "banner" + "banner", + "united_states" ] }, "flag-uruguay": { @@ -22970,7 +23590,8 @@ "uy", "nation", "country", - "banner" + "banner", + "uruguay" ] }, "flag-uzbekistan": { @@ -22981,7 +23602,8 @@ "uz", "nation", "country", - "banner" + "banner", + "uzbekistan" ] }, "flag-vatican-city": { @@ -22993,7 +23615,8 @@ "city", "nation", "country", - "banner" + "banner", + "vatican_city" ] }, "flag-st-vincent--grenadines": { @@ -23007,7 +23630,8 @@ "grenadines", "nation", "country", - "banner" + "banner", + "st_vincent_grenadines" ] }, "flag-venezuela": { @@ -23020,7 +23644,8 @@ "republic", "nation", "country", - "banner" + "banner", + "venezuela" ] }, "flag-british-virgin-islands": { @@ -23034,7 +23659,8 @@ "bvi", "nation", "country", - "banner" + "banner", + "british_virgin_islands" ] }, "flag-us-virgin-islands": { @@ -23048,7 +23674,8 @@ "us", "nation", "country", - "banner" + "banner", + "u_s_virgin_islands" ] }, "flag-vietnam": { @@ -23060,7 +23687,8 @@ "nam", "nation", "country", - "banner" + "banner", + "vietnam" ] }, "flag-vanuatu": { @@ -23071,7 +23699,8 @@ "vu", "nation", "country", - "banner" + "banner", + "vanuatu" ] }, "flag-wallis--futuna": { @@ -23084,7 +23713,8 @@ "futuna", "nation", "country", - "banner" + "banner", + "wallis_futuna" ] }, "flag-samoa": { @@ -23095,7 +23725,8 @@ "ws", "nation", "country", - "banner" + "banner", + "samoa" ] }, "flag-kosovo": { @@ -23106,7 +23737,8 @@ "xk", "nation", "country", - "banner" + "banner", + "kosovo" ] }, "flag-yemen": { @@ -23117,7 +23749,8 @@ "ye", "nation", "country", - "banner" + "banner", + "yemen" ] }, "flag-mayotte": { @@ -23128,7 +23761,8 @@ "yt", "nation", "country", - "banner" + "banner", + "mayotte" ] }, "flag-south-africa": { @@ -23140,7 +23774,8 @@ "africa", "nation", "country", - "banner" + "banner", + "south_africa" ] }, "flag-zambia": { @@ -23151,7 +23786,8 @@ "zm", "nation", "country", - "banner" + "banner", + "zambia" ] }, "flag-zimbabwe": { @@ -23162,7 +23798,8 @@ "zw", "nation", "country", - "banner" + "banner", + "zimbabwe" ] }, "flag-england": { diff --git a/vector/src/main/res/raw/emoji_picker_datasource.json b/vector/src/main/res/raw/emoji_picker_datasource.json index c2def98ebc..8843e7759a 100644 --- a/vector/src/main/res/raw/emoji_picker_datasource.json +++ b/vector/src/main/res/raw/emoji_picker_datasource.json @@ -1 +1 @@ -{"compressed":true,"categories":[{"id":"smileys_&_emotion","name":"Smileys & Emotion","emojis":["grinning-face","grinning-face-with-big-eyes","grinning-face-with-smiling-eyes","beaming-face-with-smiling-eyes","grinning-squinting-face","grinning-face-with-sweat","rolling-on-the-floor-laughing","face-with-tears-of-joy","slightly-smiling-face","upsidedown-face","winking-face","smiling-face-with-smiling-eyes","smiling-face-with-halo","smiling-face-with-hearts","smiling-face-with-hearteyes","starstruck","face-blowing-a-kiss","kissing-face","smiling-face","kissing-face-with-closed-eyes","kissing-face-with-smiling-eyes","smiling-face-with-tear","face-savoring-food","face-with-tongue","winking-face-with-tongue","zany-face","squinting-face-with-tongue","moneymouth-face","hugging-face","face-with-hand-over-mouth","shushing-face","thinking-face","zippermouth-face","face-with-raised-eyebrow","neutral-face","expressionless-face","face-without-mouth","face-in-clouds","smirking-face","unamused-face","face-with-rolling-eyes","grimacing-face","face-exhaling","lying-face","relieved-face","pensive-face","sleepy-face","drooling-face","sleeping-face","face-with-medical-mask","face-with-thermometer","face-with-headbandage","nauseated-face","face-vomiting","sneezing-face","hot-face","cold-face","woozy-face","knockedout-face","face-with-spiral-eyes","exploding-head","cowboy-hat-face","partying-face","disguised-face","smiling-face-with-sunglasses","nerd-face","face-with-monocle","confused-face","worried-face","slightly-frowning-face","frowning-face","face-with-open-mouth","hushed-face","astonished-face","flushed-face","pleading-face","frowning-face-with-open-mouth","anguished-face","fearful-face","anxious-face-with-sweat","sad-but-relieved-face","crying-face","loudly-crying-face","face-screaming-in-fear","confounded-face","persevering-face","disappointed-face","downcast-face-with-sweat","weary-face","tired-face","yawning-face","face-with-steam-from-nose","pouting-face","angry-face","face-with-symbols-on-mouth","smiling-face-with-horns","angry-face-with-horns","skull","skull-and-crossbones","pile-of-poo","clown-face","ogre","goblin","ghost","alien","alien-monster","robot","grinning-cat","grinning-cat-with-smiling-eyes","cat-with-tears-of-joy","smiling-cat-with-hearteyes","cat-with-wry-smile","kissing-cat","weary-cat","crying-cat","pouting-cat","seenoevil-monkey","hearnoevil-monkey","speaknoevil-monkey","kiss-mark","love-letter","heart-with-arrow","heart-with-ribbon","sparkling-heart","growing-heart","beating-heart","revolving-hearts","two-hearts","heart-decoration","heart-exclamation","broken-heart","heart-on-fire","mending-heart","red-heart","orange-heart","yellow-heart","green-heart","blue-heart","purple-heart","brown-heart","black-heart","white-heart","hundred-points","anger-symbol","collision","dizzy","sweat-droplets","dashing-away","hole","bomb","speech-balloon","eye-in-speech-bubble","left-speech-bubble","right-anger-bubble","thought-balloon","zzz"]},{"id":"people_&_body","name":"People & Body","emojis":["waving-hand","raised-back-of-hand","hand-with-fingers-splayed","raised-hand","vulcan-salute","ok-hand","pinched-fingers","pinching-hand","victory-hand","crossed-fingers","loveyou-gesture","sign-of-the-horns","call-me-hand","backhand-index-pointing-left","backhand-index-pointing-right","backhand-index-pointing-up","middle-finger","backhand-index-pointing-down","index-pointing-up","thumbs-up","thumbs-down","raised-fist","oncoming-fist","leftfacing-fist","rightfacing-fist","clapping-hands","raising-hands","open-hands","palms-up-together","handshake","folded-hands","writing-hand","nail-polish","selfie","flexed-biceps","mechanical-arm","mechanical-leg","leg","foot","ear","ear-with-hearing-aid","nose","brain","anatomical-heart","lungs","tooth","bone","eyes","eye","tongue","mouth","baby","child","boy","girl","person","person-blond-hair","man","person-beard","man-beard","woman-beard","man-red-hair","man-curly-hair","man-white-hair","man-bald","woman","woman-red-hair","person-red-hair","woman-curly-hair","person-curly-hair","woman-white-hair","person-white-hair","woman-bald","person-bald","woman-blond-hair","man-blond-hair","older-person","old-man","old-woman","person-frowning","man-frowning","woman-frowning","person-pouting","man-pouting","woman-pouting","person-gesturing-no","man-gesturing-no","woman-gesturing-no","person-gesturing-ok","man-gesturing-ok","woman-gesturing-ok","person-tipping-hand","man-tipping-hand","woman-tipping-hand","person-raising-hand","man-raising-hand","woman-raising-hand","deaf-person","deaf-man","deaf-woman","person-bowing","man-bowing","woman-bowing","person-facepalming","man-facepalming","woman-facepalming","person-shrugging","man-shrugging","woman-shrugging","health-worker","man-health-worker","woman-health-worker","student","man-student","woman-student","teacher","man-teacher","woman-teacher","judge","man-judge","woman-judge","farmer","man-farmer","woman-farmer","cook","man-cook","woman-cook","mechanic","man-mechanic","woman-mechanic","factory-worker","man-factory-worker","woman-factory-worker","office-worker","man-office-worker","woman-office-worker","scientist","man-scientist","woman-scientist","technologist","man-technologist","woman-technologist","singer","man-singer","woman-singer","artist","man-artist","woman-artist","pilot","man-pilot","woman-pilot","astronaut","man-astronaut","woman-astronaut","firefighter","man-firefighter","woman-firefighter","police-officer","man-police-officer","woman-police-officer","detective","man-detective","woman-detective","guard","man-guard","woman-guard","ninja","construction-worker","man-construction-worker","woman-construction-worker","prince","princess","person-wearing-turban","man-wearing-turban","woman-wearing-turban","person-with-skullcap","woman-with-headscarf","person-in-tuxedo","man-in-tuxedo","woman-in-tuxedo","person-with-veil","man-with-veil","woman-with-veil","pregnant-woman","breastfeeding","woman-feeding-baby","man-feeding-baby","person-feeding-baby","baby-angel","santa-claus","mrs-claus","mx-claus","superhero","man-superhero","woman-superhero","supervillain","man-supervillain","woman-supervillain","mage","man-mage","woman-mage","fairy","man-fairy","woman-fairy","vampire","man-vampire","woman-vampire","merperson","merman","mermaid","elf","man-elf","woman-elf","genie","man-genie","woman-genie","zombie","man-zombie","woman-zombie","person-getting-massage","man-getting-massage","woman-getting-massage","person-getting-haircut","man-getting-haircut","woman-getting-haircut","person-walking","man-walking","woman-walking","person-standing","man-standing","woman-standing","person-kneeling","man-kneeling","woman-kneeling","person-with-white-cane","man-with-white-cane","woman-with-white-cane","person-in-motorized-wheelchair","man-in-motorized-wheelchair","woman-in-motorized-wheelchair","person-in-manual-wheelchair","man-in-manual-wheelchair","woman-in-manual-wheelchair","person-running","man-running","woman-running","woman-dancing","man-dancing","person-in-suit-levitating","people-with-bunny-ears","men-with-bunny-ears","women-with-bunny-ears","person-in-steamy-room","man-in-steamy-room","woman-in-steamy-room","person-climbing","man-climbing","woman-climbing","person-fencing","horse-racing","skier","snowboarder","person-golfing","man-golfing","woman-golfing","person-surfing","man-surfing","woman-surfing","person-rowing-boat","man-rowing-boat","woman-rowing-boat","person-swimming","man-swimming","woman-swimming","person-bouncing-ball","man-bouncing-ball","woman-bouncing-ball","person-lifting-weights","man-lifting-weights","woman-lifting-weights","person-biking","man-biking","woman-biking","person-mountain-biking","man-mountain-biking","woman-mountain-biking","person-cartwheeling","man-cartwheeling","woman-cartwheeling","people-wrestling","men-wrestling","women-wrestling","person-playing-water-polo","man-playing-water-polo","woman-playing-water-polo","person-playing-handball","man-playing-handball","woman-playing-handball","person-juggling","man-juggling","woman-juggling","person-in-lotus-position","man-in-lotus-position","woman-in-lotus-position","person-taking-bath","person-in-bed","people-holding-hands","women-holding-hands","woman-and-man-holding-hands","men-holding-hands","kiss","kiss-woman-man","kiss-man-man","kiss-woman-woman","couple-with-heart","couple-with-heart-woman-man","couple-with-heart-man-man","couple-with-heart-woman-woman","family","family-man-woman-boy","family-man-woman-girl","family-man-woman-girl-boy","family-man-woman-boy-boy","family-man-woman-girl-girl","family-man-man-boy","family-man-man-girl","family-man-man-girl-boy","family-man-man-boy-boy","family-man-man-girl-girl","family-woman-woman-boy","family-woman-woman-girl","family-woman-woman-girl-boy","family-woman-woman-boy-boy","family-woman-woman-girl-girl","family-man-boy","family-man-boy-boy","family-man-girl","family-man-girl-boy","family-man-girl-girl","family-woman-boy","family-woman-boy-boy","family-woman-girl","family-woman-girl-boy","family-woman-girl-girl","speaking-head","bust-in-silhouette","busts-in-silhouette","people-hugging","footprints"]},{"id":"animals_&_nature","name":"Animals & Nature","emojis":["monkey-face","monkey","gorilla","orangutan","dog-face","dog","guide-dog","service-dog","poodle","wolf","fox","raccoon","cat-face","cat","black-cat","lion","tiger-face","tiger","leopard","horse-face","horse","unicorn","zebra","deer","bison","cow-face","ox","water-buffalo","cow","pig-face","pig","boar","pig-nose","ram","ewe","goat","camel","twohump-camel","llama","giraffe","elephant","mammoth","rhinoceros","hippopotamus","mouse-face","mouse","rat","hamster","rabbit-face","rabbit","chipmunk","beaver","hedgehog","bat","bear","polar-bear","koala","panda","sloth","otter","skunk","kangaroo","badger","paw-prints","turkey","chicken","rooster","hatching-chick","baby-chick","frontfacing-baby-chick","bird","penguin","dove","eagle","duck","swan","owl","dodo","feather","flamingo","peacock","parrot","frog","crocodile","turtle","lizard","snake","dragon-face","dragon","sauropod","trex","spouting-whale","whale","dolphin","seal","fish","tropical-fish","blowfish","shark","octopus","spiral-shell","snail","butterfly","bug","ant","honeybee","beetle","lady-beetle","cricket","cockroach","spider","spider-web","scorpion","mosquito","fly","worm","microbe","bouquet","cherry-blossom","white-flower","rosette","rose","wilted-flower","hibiscus","sunflower","blossom","tulip","seedling","potted-plant","evergreen-tree","deciduous-tree","palm-tree","cactus","sheaf-of-rice","herb","shamrock","four-leaf-clover","maple-leaf","fallen-leaf","leaf-fluttering-in-wind"]},{"id":"food_&_drink","name":"Food & Drink","emojis":["grapes","melon","watermelon","tangerine","lemon","banana","pineapple","mango","red-apple","green-apple","pear","peach","cherries","strawberry","blueberries","kiwi-fruit","tomato","olive","coconut","avocado","eggplant","potato","carrot","ear-of-corn","hot-pepper","bell-pepper","cucumber","leafy-green","broccoli","garlic","onion","mushroom","peanuts","chestnut","bread","croissant","baguette-bread","flatbread","pretzel","bagel","pancakes","waffle","cheese-wedge","meat-on-bone","poultry-leg","cut-of-meat","bacon","hamburger","french-fries","pizza","hot-dog","sandwich","taco","burrito","tamale","stuffed-flatbread","falafel","egg","cooking","shallow-pan-of-food","pot-of-food","fondue","bowl-with-spoon","green-salad","popcorn","butter","salt","canned-food","bento-box","rice-cracker","rice-ball","cooked-rice","curry-rice","steaming-bowl","spaghetti","roasted-sweet-potato","oden","sushi","fried-shrimp","fish-cake-with-swirl","moon-cake","dango","dumpling","fortune-cookie","takeout-box","crab","lobster","shrimp","squid","oyster","soft-ice-cream","shaved-ice","ice-cream","doughnut","cookie","birthday-cake","shortcake","cupcake","pie","chocolate-bar","candy","lollipop","custard","honey-pot","baby-bottle","glass-of-milk","hot-beverage","teapot","teacup-without-handle","sake","bottle-with-popping-cork","wine-glass","cocktail-glass","tropical-drink","beer-mug","clinking-beer-mugs","clinking-glasses","tumbler-glass","cup-with-straw","bubble-tea","beverage-box","mate","ice","chopsticks","fork-and-knife-with-plate","fork-and-knife","spoon","kitchen-knife","amphora"]},{"id":"travel_&_places","name":"Travel & Places","emojis":["globe-showing-europeafrica","globe-showing-americas","globe-showing-asiaaustralia","globe-with-meridians","world-map","map-of-japan","compass","snowcapped-mountain","mountain","volcano","mount-fuji","camping","beach-with-umbrella","desert","desert-island","national-park","stadium","classical-building","building-construction","brick","rock","wood","hut","houses","derelict-house","house","house-with-garden","office-building","japanese-post-office","post-office","hospital","bank","hotel","love-hotel","convenience-store","school","department-store","factory","japanese-castle","castle","wedding","tokyo-tower","statue-of-liberty","church","mosque","hindu-temple","synagogue","shinto-shrine","kaaba","fountain","tent","foggy","night-with-stars","cityscape","sunrise-over-mountains","sunrise","cityscape-at-dusk","sunset","bridge-at-night","hot-springs","carousel-horse","ferris-wheel","roller-coaster","barber-pole","circus-tent","locomotive","railway-car","highspeed-train","bullet-train","train","metro","light-rail","station","tram","monorail","mountain-railway","tram-car","bus","oncoming-bus","trolleybus","minibus","ambulance","fire-engine","police-car","oncoming-police-car","taxi","oncoming-taxi","automobile","oncoming-automobile","sport-utility-vehicle","pickup-truck","delivery-truck","articulated-lorry","tractor","racing-car","motorcycle","motor-scooter","manual-wheelchair","motorized-wheelchair","auto-rickshaw","bicycle","kick-scooter","skateboard","roller-skate","bus-stop","motorway","railway-track","oil-drum","fuel-pump","police-car-light","horizontal-traffic-light","vertical-traffic-light","stop-sign","construction","anchor","sailboat","canoe","speedboat","passenger-ship","ferry","motor-boat","ship","airplane","small-airplane","airplane-departure","airplane-arrival","parachute","seat","helicopter","suspension-railway","mountain-cableway","aerial-tramway","satellite","rocket","flying-saucer","bellhop-bell","luggage","hourglass-done","hourglass-not-done","watch","alarm-clock","stopwatch","timer-clock","mantelpiece-clock","twelve-oclock","twelvethirty","one-oclock","onethirty","two-oclock","twothirty","three-oclock","threethirty","four-oclock","fourthirty","five-oclock","fivethirty","six-oclock","sixthirty","seven-oclock","seventhirty","eight-oclock","eightthirty","nine-oclock","ninethirty","ten-oclock","tenthirty","eleven-oclock","eleventhirty","new-moon","waxing-crescent-moon","first-quarter-moon","waxing-gibbous-moon","full-moon","waning-gibbous-moon","last-quarter-moon","waning-crescent-moon","crescent-moon","new-moon-face","first-quarter-moon-face","last-quarter-moon-face","thermometer","sun","full-moon-face","sun-with-face","ringed-planet","star","glowing-star","shooting-star","milky-way","cloud","sun-behind-cloud","cloud-with-lightning-and-rain","sun-behind-small-cloud","sun-behind-large-cloud","sun-behind-rain-cloud","cloud-with-rain","cloud-with-snow","cloud-with-lightning","tornado","fog","wind-face","cyclone","rainbow","closed-umbrella","umbrella","umbrella-with-rain-drops","umbrella-on-ground","high-voltage","snowflake","snowman","snowman-without-snow","comet","fire","droplet","water-wave"]},{"id":"activities","name":"Activities","emojis":["jackolantern","christmas-tree","fireworks","sparkler","firecracker","sparkles","balloon","party-popper","confetti-ball","tanabata-tree","pine-decoration","japanese-dolls","carp-streamer","wind-chime","moon-viewing-ceremony","red-envelope","ribbon","wrapped-gift","reminder-ribbon","admission-tickets","ticket","military-medal","trophy","sports-medal","1st-place-medal","2nd-place-medal","3rd-place-medal","soccer-ball","baseball","softball","basketball","volleyball","american-football","rugby-football","tennis","flying-disc","bowling","cricket-game","field-hockey","ice-hockey","lacrosse","ping-pong","badminton","boxing-glove","martial-arts-uniform","goal-net","flag-in-hole","ice-skate","fishing-pole","diving-mask","running-shirt","skis","sled","curling-stone","bullseye","yoyo","kite","pool-8-ball","crystal-ball","magic-wand","nazar-amulet","video-game","joystick","slot-machine","game-die","puzzle-piece","teddy-bear","piata","nesting-dolls","spade-suit","heart-suit","diamond-suit","club-suit","chess-pawn","joker","mahjong-red-dragon","flower-playing-cards","performing-arts","framed-picture","artist-palette","thread","sewing-needle","yarn","knot"]},{"id":"objects","name":"Objects","emojis":["glasses","sunglasses","goggles","lab-coat","safety-vest","necktie","tshirt","jeans","scarf","gloves","coat","socks","dress","kimono","sari","onepiece-swimsuit","briefs","shorts","bikini","womans-clothes","purse","handbag","clutch-bag","shopping-bags","backpack","thong-sandal","mans-shoe","running-shoe","hiking-boot","flat-shoe","highheeled-shoe","womans-sandal","ballet-shoes","womans-boot","crown","womans-hat","top-hat","graduation-cap","billed-cap","military-helmet","rescue-workers-helmet","prayer-beads","lipstick","ring","gem-stone","muted-speaker","speaker-low-volume","speaker-medium-volume","speaker-high-volume","loudspeaker","megaphone","postal-horn","bell","bell-with-slash","musical-score","musical-note","musical-notes","studio-microphone","level-slider","control-knobs","microphone","headphone","radio","saxophone","accordion","guitar","musical-keyboard","trumpet","violin","banjo","drum","long-drum","mobile-phone","mobile-phone-with-arrow","telephone","telephone-receiver","pager","fax-machine","battery","electric-plug","laptop","desktop-computer","printer","keyboard","computer-mouse","trackball","computer-disk","floppy-disk","optical-disk","dvd","abacus","movie-camera","film-frames","film-projector","clapper-board","television","camera","camera-with-flash","video-camera","videocassette","magnifying-glass-tilted-left","magnifying-glass-tilted-right","candle","light-bulb","flashlight","red-paper-lantern","diya-lamp","notebook-with-decorative-cover","closed-book","open-book","green-book","blue-book","orange-book","books","notebook","ledger","page-with-curl","scroll","page-facing-up","newspaper","rolledup-newspaper","bookmark-tabs","bookmark","label","money-bag","coin","yen-banknote","dollar-banknote","euro-banknote","pound-banknote","money-with-wings","credit-card","receipt","chart-increasing-with-yen","envelope","email","incoming-envelope","envelope-with-arrow","outbox-tray","inbox-tray","package","closed-mailbox-with-raised-flag","closed-mailbox-with-lowered-flag","open-mailbox-with-raised-flag","open-mailbox-with-lowered-flag","postbox","ballot-box-with-ballot","pencil","black-nib","fountain-pen","pen","paintbrush","crayon","memo","briefcase","file-folder","open-file-folder","card-index-dividers","calendar","tearoff-calendar","spiral-notepad","spiral-calendar","card-index","chart-increasing","chart-decreasing","bar-chart","clipboard","pushpin","round-pushpin","paperclip","linked-paperclips","straight-ruler","triangular-ruler","scissors","card-file-box","file-cabinet","wastebasket","locked","unlocked","locked-with-pen","locked-with-key","key","old-key","hammer","axe","pick","hammer-and-pick","hammer-and-wrench","dagger","crossed-swords","water-pistol","boomerang","bow-and-arrow","shield","carpentry-saw","wrench","screwdriver","nut-and-bolt","gear","clamp","balance-scale","white-cane","link","chains","hook","toolbox","magnet","ladder","alembic","test-tube","petri-dish","dna","microscope","telescope","satellite-antenna","syringe","drop-of-blood","pill","adhesive-bandage","stethoscope","door","elevator","mirror","window","bed","couch-and-lamp","chair","toilet","plunger","shower","bathtub","mouse-trap","razor","lotion-bottle","safety-pin","broom","basket","roll-of-paper","bucket","soap","toothbrush","sponge","fire-extinguisher","shopping-cart","cigarette","coffin","headstone","funeral-urn","moai","placard"]},{"id":"symbols","name":"Symbols","emojis":["atm-sign","litter-in-bin-sign","potable-water","wheelchair-symbol","mens-room","womens-room","restroom","baby-symbol","water-closet","passport-control","customs","baggage-claim","left-luggage","warning","children-crossing","no-entry","prohibited","no-bicycles","no-smoking","no-littering","nonpotable-water","no-pedestrians","no-mobile-phones","no-one-under-eighteen","radioactive","biohazard","up-arrow","upright-arrow","right-arrow","downright-arrow","down-arrow","downleft-arrow","left-arrow","upleft-arrow","updown-arrow","leftright-arrow","right-arrow-curving-left","left-arrow-curving-right","right-arrow-curving-up","right-arrow-curving-down","clockwise-vertical-arrows","counterclockwise-arrows-button","back-arrow","end-arrow","on-arrow","soon-arrow","top-arrow","place-of-worship","atom-symbol","om","star-of-david","wheel-of-dharma","yin-yang","latin-cross","orthodox-cross","star-and-crescent","peace-symbol","menorah","dotted-sixpointed-star","aries","taurus","gemini","cancer","leo","virgo","libra","scorpio","sagittarius","capricorn","aquarius","pisces","ophiuchus","shuffle-tracks-button","repeat-button","repeat-single-button","play-button","fastforward-button","next-track-button","play-or-pause-button","reverse-button","fast-reverse-button","last-track-button","upwards-button","fast-up-button","downwards-button","fast-down-button","pause-button","stop-button","record-button","eject-button","cinema","dim-button","bright-button","antenna-bars","vibration-mode","mobile-phone-off","female-sign","male-sign","transgender-symbol","multiply","plus","minus","divide","infinity","double-exclamation-mark","exclamation-question-mark","red-question-mark","white-question-mark","white-exclamation-mark","red-exclamation-mark","wavy-dash","currency-exchange","heavy-dollar-sign","medical-symbol","recycling-symbol","fleurdelis","trident-emblem","name-badge","japanese-symbol-for-beginner","hollow-red-circle","check-mark-button","check-box-with-check","check-mark","cross-mark","cross-mark-button","curly-loop","double-curly-loop","part-alternation-mark","eightspoked-asterisk","eightpointed-star","sparkle","copyright","registered","trade-mark","keycap","keycap","keycap-0","keycap-1","keycap-2","keycap-3","keycap-4","keycap-5","keycap-6","keycap-7","keycap-8","keycap-9","keycap-10","input-latin-uppercase","input-latin-lowercase","input-numbers","input-symbols","input-latin-letters","a-button-blood-type","ab-button-blood-type","b-button-blood-type","cl-button","cool-button","free-button","information","id-button","circled-m","new-button","ng-button","o-button-blood-type","ok-button","p-button","sos-button","up-button","vs-button","japanese-here-button","japanese-service-charge-button","japanese-monthly-amount-button","japanese-not-free-of-charge-button","japanese-reserved-button","japanese-bargain-button","japanese-discount-button","japanese-free-of-charge-button","japanese-prohibited-button","japanese-acceptable-button","japanese-application-button","japanese-passing-grade-button","japanese-vacancy-button","japanese-congratulations-button","japanese-secret-button","japanese-open-for-business-button","japanese-no-vacancy-button","red-circle","orange-circle","yellow-circle","green-circle","blue-circle","purple-circle","brown-circle","black-circle","white-circle","red-square","orange-square","yellow-square","green-square","blue-square","purple-square","brown-square","black-large-square","white-large-square","black-medium-square","white-medium-square","black-mediumsmall-square","white-mediumsmall-square","black-small-square","white-small-square","large-orange-diamond","large-blue-diamond","small-orange-diamond","small-blue-diamond","red-triangle-pointed-up","red-triangle-pointed-down","diamond-with-a-dot","radio-button","white-square-button","black-square-button"]},{"id":"flags","name":"Flags","emojis":["chequered-flag","triangular-flag","crossed-flags","black-flag","white-flag","rainbow-flag","transgender-flag","pirate-flag","flag-ascension-island","flag-andorra","flag-united-arab-emirates","flag-afghanistan","flag-antigua--barbuda","flag-anguilla","flag-albania","flag-armenia","flag-angola","flag-antarctica","flag-argentina","flag-american-samoa","flag-austria","flag-australia","flag-aruba","flag-land-islands","flag-azerbaijan","flag-bosnia--herzegovina","flag-barbados","flag-bangladesh","flag-belgium","flag-burkina-faso","flag-bulgaria","flag-bahrain","flag-burundi","flag-benin","flag-st-barthlemy","flag-bermuda","flag-brunei","flag-bolivia","flag-caribbean-netherlands","flag-brazil","flag-bahamas","flag-bhutan","flag-bouvet-island","flag-botswana","flag-belarus","flag-belize","flag-canada","flag-cocos-keeling-islands","flag-congo--kinshasa","flag-central-african-republic","flag-congo--brazzaville","flag-switzerland","flag-cte-divoire","flag-cook-islands","flag-chile","flag-cameroon","flag-china","flag-colombia","flag-clipperton-island","flag-costa-rica","flag-cuba","flag-cape-verde","flag-curaao","flag-christmas-island","flag-cyprus","flag-czechia","flag-germany","flag-diego-garcia","flag-djibouti","flag-denmark","flag-dominica","flag-dominican-republic","flag-algeria","flag-ceuta--melilla","flag-ecuador","flag-estonia","flag-egypt","flag-western-sahara","flag-eritrea","flag-spain","flag-ethiopia","flag-european-union","flag-finland","flag-fiji","flag-falkland-islands","flag-micronesia","flag-faroe-islands","flag-france","flag-gabon","flag-united-kingdom","flag-grenada","flag-georgia","flag-french-guiana","flag-guernsey","flag-ghana","flag-gibraltar","flag-greenland","flag-gambia","flag-guinea","flag-guadeloupe","flag-equatorial-guinea","flag-greece","flag-south-georgia--south-sandwich-islands","flag-guatemala","flag-guam","flag-guineabissau","flag-guyana","flag-hong-kong-sar-china","flag-heard--mcdonald-islands","flag-honduras","flag-croatia","flag-haiti","flag-hungary","flag-canary-islands","flag-indonesia","flag-ireland","flag-israel","flag-isle-of-man","flag-india","flag-british-indian-ocean-territory","flag-iraq","flag-iran","flag-iceland","flag-italy","flag-jersey","flag-jamaica","flag-jordan","flag-japan","flag-kenya","flag-kyrgyzstan","flag-cambodia","flag-kiribati","flag-comoros","flag-st-kitts--nevis","flag-north-korea","flag-south-korea","flag-kuwait","flag-cayman-islands","flag-kazakhstan","flag-laos","flag-lebanon","flag-st-lucia","flag-liechtenstein","flag-sri-lanka","flag-liberia","flag-lesotho","flag-lithuania","flag-luxembourg","flag-latvia","flag-libya","flag-morocco","flag-monaco","flag-moldova","flag-montenegro","flag-st-martin","flag-madagascar","flag-marshall-islands","flag-north-macedonia","flag-mali","flag-myanmar-burma","flag-mongolia","flag-macao-sar-china","flag-northern-mariana-islands","flag-martinique","flag-mauritania","flag-montserrat","flag-malta","flag-mauritius","flag-maldives","flag-malawi","flag-mexico","flag-malaysia","flag-mozambique","flag-namibia","flag-new-caledonia","flag-niger","flag-norfolk-island","flag-nigeria","flag-nicaragua","flag-netherlands","flag-norway","flag-nepal","flag-nauru","flag-niue","flag-new-zealand","flag-oman","flag-panama","flag-peru","flag-french-polynesia","flag-papua-new-guinea","flag-philippines","flag-pakistan","flag-poland","flag-st-pierre--miquelon","flag-pitcairn-islands","flag-puerto-rico","flag-palestinian-territories","flag-portugal","flag-palau","flag-paraguay","flag-qatar","flag-runion","flag-romania","flag-serbia","flag-russia","flag-rwanda","flag-saudi-arabia","flag-solomon-islands","flag-seychelles","flag-sudan","flag-sweden","flag-singapore","flag-st-helena","flag-slovenia","flag-svalbard--jan-mayen","flag-slovakia","flag-sierra-leone","flag-san-marino","flag-senegal","flag-somalia","flag-suriname","flag-south-sudan","flag-so-tom--prncipe","flag-el-salvador","flag-sint-maarten","flag-syria","flag-eswatini","flag-tristan-da-cunha","flag-turks--caicos-islands","flag-chad","flag-french-southern-territories","flag-togo","flag-thailand","flag-tajikistan","flag-tokelau","flag-timorleste","flag-turkmenistan","flag-tunisia","flag-tonga","flag-turkey","flag-trinidad--tobago","flag-tuvalu","flag-taiwan","flag-tanzania","flag-ukraine","flag-uganda","flag-us-outlying-islands","flag-united-nations","flag-united-states","flag-uruguay","flag-uzbekistan","flag-vatican-city","flag-st-vincent--grenadines","flag-venezuela","flag-british-virgin-islands","flag-us-virgin-islands","flag-vietnam","flag-vanuatu","flag-wallis--futuna","flag-samoa","flag-kosovo","flag-yemen","flag-mayotte","flag-south-africa","flag-zambia","flag-zimbabwe","flag-england","flag-scotland","flag-wales"]}],"emojis":{"grinning-face":{"a":"Grinning Face","b":"1F600","j":["face","grin","smile","happy","joy",":D"]},"grinning-face-with-big-eyes":{"a":"Grinning Face with Big Eyes","b":"1F603","j":["face","mouth","open","smile","happy","joy","haha",":D",":)","funny"]},"grinning-face-with-smiling-eyes":{"a":"Grinning Face with Smiling Eyes","b":"1F604","j":["eye","face","mouth","open","smile","happy","joy","funny","haha","laugh","like",":D",":)"]},"beaming-face-with-smiling-eyes":{"a":"Beaming Face with Smiling Eyes","b":"1F601","j":["eye","face","grin","smile","happy","joy","kawaii"]},"grinning-squinting-face":{"a":"Grinning Squinting Face","b":"1F606","j":["face","laugh","mouth","satisfied","smile","happy","joy","lol","haha","glad","XD"]},"grinning-face-with-sweat":{"a":"Grinning Face with Sweat","b":"1F605","j":["cold","face","open","smile","sweat","hot","happy","laugh","relief"]},"rolling-on-the-floor-laughing":{"a":"Rolling on the Floor Laughing","b":"1F923","j":["face","floor","laugh","rofl","rolling","rotfl","laughing","lol","haha"]},"face-with-tears-of-joy":{"a":"Face with Tears of Joy","b":"1F602","j":["face","joy","laugh","tear","cry","tears","weep","happy","happytears","haha"]},"slightly-smiling-face":{"a":"Slightly Smiling Face","b":"1F642","j":["face","smile"]},"upsidedown-face":{"a":"Upside-Down Face","b":"1F643","j":["face","upside-down","upside_down_face","flipped","silly","smile"]},"winking-face":{"a":"Winking Face","b":"1F609","j":["face","wink","happy","mischievous","secret",";)","smile","eye"]},"smiling-face-with-smiling-eyes":{"a":"Smiling Face with Smiling Eyes","b":"1F60A","j":["blush","eye","face","smile","happy","flushed","crush","embarrassed","shy","joy"]},"smiling-face-with-halo":{"a":"Smiling Face with Halo","b":"1F607","j":["angel","face","fantasy","halo","innocent","heaven"]},"smiling-face-with-hearts":{"a":"Smiling Face with Hearts","b":"1F970","j":["adore","crush","hearts","in love","face","love","like","affection","valentines","infatuation"]},"smiling-face-with-hearteyes":{"a":"Smiling Face with Heart-Eyes","b":"1F60D","j":["eye","face","love","smile","smiling face with heart-eyes","smiling_face_with_heart_eyes","like","affection","valentines","infatuation","crush","heart"]},"starstruck":{"a":"Star-Struck","b":"1F929","j":["eyes","face","grinning","star","star-struck","starry-eyed","star_struck","smile","starry"]},"face-blowing-a-kiss":{"a":"Face Blowing a Kiss","b":"1F618","j":["face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face":{"a":"Kissing Face","b":"1F617","j":["face","kiss","love","like","3","valentines","infatuation"]},"smiling-face":{"a":"Smiling Face","b":"263A","j":["face","outlined","relaxed","smile","blush","massage","happiness"]},"kissing-face-with-closed-eyes":{"a":"Kissing Face with Closed Eyes","b":"1F61A","j":["closed","eye","face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face-with-smiling-eyes":{"a":"Kissing Face with Smiling Eyes","b":"1F619","j":["eye","face","kiss","smile","affection","valentines","infatuation"]},"smiling-face-with-tear":{"a":"Smiling Face with Tear","b":"1F972","j":["grateful","proud","relieved","smiling","tear","touched","sad","cry","pretend"]},"face-savoring-food":{"a":"Face Savoring Food","b":"1F60B","j":["delicious","face","savouring","smile","yum","happy","joy","tongue","silly","yummy","nom"]},"face-with-tongue":{"a":"Face with Tongue","b":"1F61B","j":["face","tongue","prank","childish","playful","mischievous","smile"]},"winking-face-with-tongue":{"a":"Winking Face with Tongue","b":"1F61C","j":["eye","face","joke","tongue","wink","prank","childish","playful","mischievous","smile"]},"zany-face":{"a":"Zany Face","b":"1F92A","j":["eye","goofy","large","small","face","crazy"]},"squinting-face-with-tongue":{"a":"Squinting Face with Tongue","b":"1F61D","j":["eye","face","horrible","taste","tongue","prank","playful","mischievous","smile"]},"moneymouth-face":{"a":"Money-Mouth Face","b":"1F911","j":["face","money","money-mouth face","mouth","money_mouth_face","rich","dollar"]},"hugging-face":{"a":"Hugging Face","b":"1F917","j":["face","hug","hugging","smile"]},"face-with-hand-over-mouth":{"a":"Face with Hand over Mouth","b":"1F92D","j":["whoops","shock","sudden realization","surprise","face"]},"shushing-face":{"a":"Shushing Face","b":"1F92B","j":["quiet","shush","face","shhh"]},"thinking-face":{"a":"Thinking Face","b":"1F914","j":["face","thinking","hmmm","think","consider"]},"zippermouth-face":{"a":"Zipper-Mouth Face","b":"1F910","j":["face","mouth","zipper","zipper-mouth face","zipper_mouth_face","sealed","secret"]},"face-with-raised-eyebrow":{"a":"Face with Raised Eyebrow","b":"1F928","j":["distrust","skeptic","disapproval","disbelief","mild surprise","scepticism","face","surprise"]},"neutral-face":{"a":"Neutral Face","b":"1F610","j":["deadpan","face","meh","neutral","indifference",":|"]},"expressionless-face":{"a":"Expressionless Face","b":"1F611","j":["expressionless","face","inexpressive","meh","unexpressive","indifferent","-_-","deadpan"]},"face-without-mouth":{"a":"Face Without Mouth","b":"1F636","j":["face","mouth","quiet","silent","hellokitty"]},"face-in-clouds":{"a":"⊛ Face in Clouds","b":"1F636-200D-1F32B-FE0F","j":["absentminded","face in clouds","face in the fog","head in clouds"]},"smirking-face":{"a":"Smirking Face","b":"1F60F","j":["face","smirk","smile","mean","prank","smug","sarcasm"]},"unamused-face":{"a":"Unamused Face","b":"1F612","j":["face","unamused","unhappy","indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"]},"face-with-rolling-eyes":{"a":"Face with Rolling Eyes","b":"1F644","j":["eyeroll","eyes","face","rolling","frustrated"]},"grimacing-face":{"a":"Grimacing Face","b":"1F62C","j":["face","grimace","teeth"]},"face-exhaling":{"a":"⊛ Face Exhaling","b":"1F62E-200D-1F4A8","j":["exhale","face exhaling","gasp","groan","relief","whisper","whistle"]},"lying-face":{"a":"Lying Face","b":"1F925","j":["face","lie","pinocchio"]},"relieved-face":{"a":"Relieved Face","b":"1F60C","j":["face","relieved","relaxed","phew","massage","happiness"]},"pensive-face":{"a":"Pensive Face","b":"1F614","j":["dejected","face","pensive","sad","depressed","upset"]},"sleepy-face":{"a":"Sleepy Face","b":"1F62A","j":["face","sleep","tired","rest","nap"]},"drooling-face":{"a":"Drooling Face","b":"1F924","j":["drooling","face"]},"sleeping-face":{"a":"Sleeping Face","b":"1F634","j":["face","sleep","zzz","tired","sleepy","night"]},"face-with-medical-mask":{"a":"Face with Medical Mask","b":"1F637","j":["cold","doctor","face","mask","sick","ill","disease"]},"face-with-thermometer":{"a":"Face with Thermometer","b":"1F912","j":["face","ill","sick","thermometer","temperature","cold","fever"]},"face-with-headbandage":{"a":"Face with Head-Bandage","b":"1F915","j":["bandage","face","face with head-bandage","hurt","injury","face_with_head_bandage","injured","clumsy"]},"nauseated-face":{"a":"Nauseated Face","b":"1F922","j":["face","nauseated","vomit","gross","green","sick","throw up","ill"]},"face-vomiting":{"a":"Face Vomiting","b":"1F92E","j":["puke","sick","vomit","face"]},"sneezing-face":{"a":"Sneezing Face","b":"1F927","j":["face","gesundheit","sneeze","sick","allergy"]},"hot-face":{"a":"Hot Face","b":"1F975","j":["feverish","heat stroke","hot","red-faced","sweating","face","heat","red"]},"cold-face":{"a":"Cold Face","b":"1F976","j":["blue-faced","cold","freezing","frostbite","icicles","face","blue","frozen"]},"woozy-face":{"a":"Woozy Face","b":"1F974","j":["dizzy","intoxicated","tipsy","uneven eyes","wavy mouth","face","wavy"]},"knockedout-face":{"a":"Knocked-out Face","b":"1F635","j":["dead","face","knocked out","knocked-out face","dizzy_face","spent","unconscious","xox","dizzy"]},"face-with-spiral-eyes":{"a":"⊛ Face with Spiral Eyes","b":"1F635-200D-1F4AB","j":["dizzy","face with spiral eyes","hypnotized","spiral","trouble","whoa"]},"exploding-head":{"a":"Exploding Head","b":"1F92F","j":["mind blown","shocked","face","mind","blown"]},"cowboy-hat-face":{"a":"Cowboy Hat Face","b":"1F920","j":["cowboy","cowgirl","face","hat"]},"partying-face":{"a":"Partying Face","b":"1F973","j":["celebration","hat","horn","party","face","woohoo"]},"disguised-face":{"a":"Disguised Face","b":"1F978","j":["disguise","face","glasses","incognito","nose","pretent","brows","moustache"]},"smiling-face-with-sunglasses":{"a":"Smiling Face with Sunglasses","b":"1F60E","j":["bright","cool","face","sun","sunglasses","smile","summer","beach","sunglass"]},"nerd-face":{"a":"Nerd Face","b":"1F913","j":["face","geek","nerd","nerdy","dork"]},"face-with-monocle":{"a":"Face with Monocle","b":"1F9D0","j":["stuffy","wealthy","face"]},"confused-face":{"a":"Confused Face","b":"1F615","j":["confused","face","meh","indifference","huh","weird","hmmm",":/"]},"worried-face":{"a":"Worried Face","b":"1F61F","j":["face","worried","concern","nervous",":("]},"slightly-frowning-face":{"a":"Slightly Frowning Face","b":"1F641","j":["face","frown","frowning","disappointed","sad","upset"]},"frowning-face":{"a":"Frowning Face","b":"2639","j":["face","frown","sad","upset"]},"face-with-open-mouth":{"a":"Face with Open Mouth","b":"1F62E","j":["face","mouth","open","sympathy","surprise","impressed","wow","whoa",":O"]},"hushed-face":{"a":"Hushed Face","b":"1F62F","j":["face","hushed","stunned","surprised","woo","shh"]},"astonished-face":{"a":"Astonished Face","b":"1F632","j":["astonished","face","shocked","totally","xox","surprised","poisoned"]},"flushed-face":{"a":"Flushed Face","b":"1F633","j":["dazed","face","flushed","blush","shy","flattered"]},"pleading-face":{"a":"Pleading Face","b":"1F97A","j":["begging","mercy","puppy eyes","face"]},"frowning-face-with-open-mouth":{"a":"Frowning Face with Open Mouth","b":"1F626","j":["face","frown","mouth","open","aw","what"]},"anguished-face":{"a":"Anguished Face","b":"1F627","j":["anguished","face","stunned","nervous"]},"fearful-face":{"a":"Fearful Face","b":"1F628","j":["face","fear","fearful","scared","terrified","nervous","oops","huh"]},"anxious-face-with-sweat":{"a":"Anxious Face with Sweat","b":"1F630","j":["blue","cold","face","rushed","sweat","nervous"]},"sad-but-relieved-face":{"a":"Sad but Relieved Face","b":"1F625","j":["disappointed","face","relieved","whew","phew","sweat","nervous"]},"crying-face":{"a":"Crying Face","b":"1F622","j":["cry","face","sad","tear","tears","depressed","upset",":'("]},"loudly-crying-face":{"a":"Loudly Crying Face","b":"1F62D","j":["cry","face","sad","sob","tear","tears","upset","depressed"]},"face-screaming-in-fear":{"a":"Face Screaming in Fear","b":"1F631","j":["face","fear","munch","scared","scream","omg"]},"confounded-face":{"a":"Confounded Face","b":"1F616","j":["confounded","face","confused","sick","unwell","oops",":S"]},"persevering-face":{"a":"Persevering Face","b":"1F623","j":["face","persevere","sick","no","upset","oops"]},"disappointed-face":{"a":"Disappointed Face","b":"1F61E","j":["disappointed","face","sad","upset","depressed",":("]},"downcast-face-with-sweat":{"a":"Downcast Face with Sweat","b":"1F613","j":["cold","face","sweat","hot","sad","tired","exercise"]},"weary-face":{"a":"Weary Face","b":"1F629","j":["face","tired","weary","sleepy","sad","frustrated","upset"]},"tired-face":{"a":"Tired Face","b":"1F62B","j":["face","tired","sick","whine","upset","frustrated"]},"yawning-face":{"a":"Yawning Face","b":"1F971","j":["bored","tired","yawn","sleepy"]},"face-with-steam-from-nose":{"a":"Face with Steam From Nose","b":"1F624","j":["face","triumph","won","gas","phew","proud","pride"]},"pouting-face":{"a":"Pouting Face","b":"1F621","j":["angry","face","mad","pouting","rage","red","hate","despise"]},"angry-face":{"a":"Angry Face","b":"1F620","j":["anger","angry","face","mad","annoyed","frustrated"]},"face-with-symbols-on-mouth":{"a":"Face with Symbols on Mouth","b":"1F92C","j":["swearing","cursing","face","cussing","profanity","expletive"]},"smiling-face-with-horns":{"a":"Smiling Face with Horns","b":"1F608","j":["face","fairy tale","fantasy","horns","smile","devil"]},"angry-face-with-horns":{"a":"Angry Face with Horns","b":"1F47F","j":["demon","devil","face","fantasy","imp","angry","horns"]},"skull":{"a":"Skull","b":"1F480","j":["death","face","fairy tale","monster","dead","skeleton","creepy"]},"skull-and-crossbones":{"a":"Skull and Crossbones","b":"2620","j":["crossbones","death","face","monster","skull","poison","danger","deadly","scary","pirate","evil"]},"pile-of-poo":{"a":"Pile of Poo","b":"1F4A9","j":["dung","face","monster","poo","poop","hankey","shitface","fail","turd","shit"]},"clown-face":{"a":"Clown Face","b":"1F921","j":["clown","face"]},"ogre":{"a":"Ogre","b":"1F479","j":["creature","face","fairy tale","fantasy","monster","troll","red","mask","halloween","scary","creepy","devil","demon","japanese"]},"goblin":{"a":"Goblin","b":"1F47A","j":["creature","face","fairy tale","fantasy","monster","red","evil","mask","scary","creepy","japanese"]},"ghost":{"a":"Ghost","b":"1F47B","j":["creature","face","fairy tale","fantasy","monster","halloween","spooky","scary"]},"alien":{"a":"Alien","b":"1F47D","j":["creature","extraterrestrial","face","fantasy","ufo","UFO","paul","weird","outer_space"]},"alien-monster":{"a":"Alien Monster","b":"1F47E","j":["alien","creature","extraterrestrial","face","monster","ufo","game","arcade","play"]},"robot":{"a":"Robot","b":"1F916","j":["face","monster","computer","machine","bot"]},"grinning-cat":{"a":"Grinning Cat","b":"1F63A","j":["cat","face","grinning","mouth","open","smile","animal","cats","happy"]},"grinning-cat-with-smiling-eyes":{"a":"Grinning Cat with Smiling Eyes","b":"1F638","j":["cat","eye","face","grin","smile","animal","cats"]},"cat-with-tears-of-joy":{"a":"Cat with Tears of Joy","b":"1F639","j":["cat","face","joy","tear","animal","cats","haha","happy","tears"]},"smiling-cat-with-hearteyes":{"a":"Smiling Cat with Heart-Eyes","b":"1F63B","j":["cat","eye","face","heart","love","smile","smiling cat with heart-eyes","smiling_cat_with_heart_eyes","animal","like","affection","cats","valentines"]},"cat-with-wry-smile":{"a":"Cat with Wry Smile","b":"1F63C","j":["cat","face","ironic","smile","wry","animal","cats","smirk"]},"kissing-cat":{"a":"Kissing Cat","b":"1F63D","j":["cat","eye","face","kiss","animal","cats"]},"weary-cat":{"a":"Weary Cat","b":"1F640","j":["cat","face","oh","surprised","weary","animal","cats","munch","scared","scream"]},"crying-cat":{"a":"Crying Cat","b":"1F63F","j":["cat","cry","face","sad","tear","animal","tears","weep","cats","upset"]},"pouting-cat":{"a":"Pouting Cat","b":"1F63E","j":["cat","face","pouting","animal","cats"]},"seenoevil-monkey":{"a":"See-No-Evil Monkey","b":"1F648","j":["evil","face","forbidden","monkey","see","see-no-evil monkey","see_no_evil_monkey","animal","nature","haha"]},"hearnoevil-monkey":{"a":"Hear-No-Evil Monkey","b":"1F649","j":["evil","face","forbidden","hear","hear-no-evil monkey","monkey","hear_no_evil_monkey","animal","nature"]},"speaknoevil-monkey":{"a":"Speak-No-Evil Monkey","b":"1F64A","j":["evil","face","forbidden","monkey","speak","speak-no-evil monkey","speak_no_evil_monkey","animal","nature","omg"]},"kiss-mark":{"a":"Kiss Mark","b":"1F48B","j":["kiss","lips","face","love","like","affection","valentines"]},"love-letter":{"a":"Love Letter","b":"1F48C","j":["heart","letter","love","mail","email","like","affection","envelope","valentines"]},"heart-with-arrow":{"a":"Heart with Arrow","b":"1F498","j":["arrow","cupid","love","like","heart","affection","valentines"]},"heart-with-ribbon":{"a":"Heart with Ribbon","b":"1F49D","j":["ribbon","valentine","love","valentines"]},"sparkling-heart":{"a":"Sparkling Heart","b":"1F496","j":["excited","sparkle","love","like","affection","valentines"]},"growing-heart":{"a":"Growing Heart","b":"1F497","j":["excited","growing","nervous","pulse","like","love","affection","valentines","pink"]},"beating-heart":{"a":"Beating Heart","b":"1F493","j":["beating","heartbeat","pulsating","love","like","affection","valentines","pink","heart"]},"revolving-hearts":{"a":"Revolving Hearts","b":"1F49E","j":["revolving","love","like","affection","valentines"]},"two-hearts":{"a":"Two Hearts","b":"1F495","j":["love","like","affection","valentines","heart"]},"heart-decoration":{"a":"Heart Decoration","b":"1F49F","j":["heart","purple-square","love","like"]},"heart-exclamation":{"a":"Heart Exclamation","b":"2763","j":["exclamation","mark","punctuation","decoration","love"]},"broken-heart":{"a":"Broken Heart","b":"1F494","j":["break","broken","sad","sorry","heart","heartbreak"]},"heart-on-fire":{"a":"⊛ Heart on Fire","b":"2764-FE0F-200D-1F525","j":["burn","heart","heart on fire","love","lust","sacred heart"]},"mending-heart":{"a":"⊛ Mending Heart","b":"2764-FE0F-200D-1FA79","j":["healthier","improving","mending","mending heart","recovering","recuperating","well"]},"red-heart":{"a":"Red Heart","b":"2764","j":["heart","love","like","valentines"]},"orange-heart":{"a":"Orange Heart","b":"1F9E1","j":["orange","love","like","affection","valentines"]},"yellow-heart":{"a":"Yellow Heart","b":"1F49B","j":["yellow","love","like","affection","valentines"]},"green-heart":{"a":"Green Heart","b":"1F49A","j":["green","love","like","affection","valentines"]},"blue-heart":{"a":"Blue Heart","b":"1F499","j":["blue","love","like","affection","valentines"]},"purple-heart":{"a":"Purple Heart","b":"1F49C","j":["purple","love","like","affection","valentines"]},"brown-heart":{"a":"Brown Heart","b":"1F90E","j":["brown","heart","coffee"]},"black-heart":{"a":"Black Heart","b":"1F5A4","j":["black","evil","wicked"]},"white-heart":{"a":"White Heart","b":"1F90D","j":["heart","white","pure"]},"hundred-points":{"a":"Hundred Points","b":"1F4AF","j":["100","full","hundred","score","perfect","numbers","century","exam","quiz","test","pass"]},"anger-symbol":{"a":"Anger Symbol","b":"1F4A2","j":["angry","comic","mad"]},"collision":{"a":"Collision","b":"1F4A5","j":["boom","comic","bomb","explode","explosion","blown"]},"dizzy":{"a":"Dizzy","b":"1F4AB","j":["comic","star","sparkle","shoot","magic"]},"sweat-droplets":{"a":"Sweat Droplets","b":"1F4A6","j":["comic","splashing","sweat","water","drip","oops"]},"dashing-away":{"a":"Dashing Away","b":"1F4A8","j":["comic","dash","running","wind","air","fast","shoo","fart","smoke","puff"]},"hole":{"a":"Hole","b":"1F573","j":["embarrassing"]},"bomb":{"a":"Bomb","b":"1F4A3","j":["comic","boom","explode","explosion","terrorism"]},"speech-balloon":{"a":"Speech Balloon","b":"1F4AC","j":["balloon","bubble","comic","dialog","speech","words","message","talk","chatting"]},"eye-in-speech-bubble":{"a":"Eye in Speech Bubble","b":"1F441-FE0F-200D-1F5E8-FE0F","j":["eye","speech bubble","witness","info"]},"left-speech-bubble":{"a":"Left Speech Bubble","b":"1F5E8","j":["dialog","speech","words","message","talk","chatting"]},"right-anger-bubble":{"a":"Right Anger Bubble","b":"1F5EF","j":["angry","balloon","bubble","mad","caption","speech","thinking"]},"thought-balloon":{"a":"Thought Balloon","b":"1F4AD","j":["balloon","bubble","comic","thought","cloud","speech","thinking","dream"]},"zzz":{"a":"Zzz","b":"1F4A4","j":["comic","sleep","sleepy","tired","dream"]},"waving-hand":{"a":"Waving Hand","b":"1F44B","j":["hand","wave","waving","hands","gesture","goodbye","solong","farewell","hello","hi","palm"]},"raised-back-of-hand":{"a":"Raised Back of Hand","b":"1F91A","j":["backhand","raised","fingers"]},"hand-with-fingers-splayed":{"a":"Hand with Fingers Splayed","b":"1F590","j":["finger","hand","splayed","fingers","palm"]},"raised-hand":{"a":"Raised Hand","b":"270B","j":["hand","high 5","high five","fingers","stop","highfive","palm","ban"]},"vulcan-salute":{"a":"Vulcan Salute","b":"1F596","j":["finger","hand","spock","vulcan","fingers","star trek"]},"ok-hand":{"a":"Ok Hand","b":"1F44C","j":["hand","OK","fingers","limbs","perfect","ok","okay"]},"pinched-fingers":{"a":"Pinched Fingers","b":"1F90C","j":["fingers","hand gesture","interrogation","pinched","sarcastic","size","tiny","small"]},"pinching-hand":{"a":"Pinching Hand","b":"1F90F","j":["small amount","tiny","small","size"]},"victory-hand":{"a":"Victory Hand","b":"270C","j":["hand","v","victory","fingers","ohyeah","peace","two"]},"crossed-fingers":{"a":"Crossed Fingers","b":"1F91E","j":["cross","finger","hand","luck","good","lucky"]},"loveyou-gesture":{"a":"Love-You Gesture","b":"1F91F","j":["hand","ILY","love-you gesture","love_you_gesture","fingers","gesture"]},"sign-of-the-horns":{"a":"Sign of the Horns","b":"1F918","j":["finger","hand","horns","rock-on","fingers","evil_eye","sign_of_horns","rock_on"]},"call-me-hand":{"a":"Call Me Hand","b":"1F919","j":["call","hand","hands","gesture"]},"backhand-index-pointing-left":{"a":"Backhand Index Pointing Left","b":"1F448","j":["backhand","finger","hand","index","point","direction","fingers","left"]},"backhand-index-pointing-right":{"a":"Backhand Index Pointing Right","b":"1F449","j":["backhand","finger","hand","index","point","fingers","direction","right"]},"backhand-index-pointing-up":{"a":"Backhand Index Pointing Up","b":"1F446","j":["backhand","finger","hand","point","up","fingers","direction"]},"middle-finger":{"a":"Middle Finger","b":"1F595","j":["finger","hand","fingers","rude","middle","flipping"]},"backhand-index-pointing-down":{"a":"Backhand Index Pointing Down","b":"1F447","j":["backhand","down","finger","hand","point","fingers","direction"]},"index-pointing-up":{"a":"Index Pointing Up","b":"261D","j":["finger","hand","index","point","up","fingers","direction"]},"thumbs-up":{"a":"Thumbs Up","b":"1F44D","j":["+1","hand","thumb","up","thumbsup","yes","awesome","good","agree","accept","cool","like"]},"thumbs-down":{"a":"Thumbs Down","b":"1F44E","j":["-1","down","hand","thumb","thumbsdown","no","dislike"]},"raised-fist":{"a":"Raised Fist","b":"270A","j":["clenched","fist","hand","punch","fingers","grasp"]},"oncoming-fist":{"a":"Oncoming Fist","b":"1F44A","j":["clenched","fist","hand","punch","angry","violence","hit","attack"]},"leftfacing-fist":{"a":"Left-Facing Fist","b":"1F91B","j":["fist","left-facing fist","leftwards","left_facing_fist","hand","fistbump"]},"rightfacing-fist":{"a":"Right-Facing Fist","b":"1F91C","j":["fist","right-facing fist","rightwards","right_facing_fist","hand","fistbump"]},"clapping-hands":{"a":"Clapping Hands","b":"1F44F","j":["clap","hand","hands","praise","applause","congrats","yay"]},"raising-hands":{"a":"Raising Hands","b":"1F64C","j":["celebration","gesture","hand","hooray","raised","yea","hands"]},"open-hands":{"a":"Open Hands","b":"1F450","j":["hand","open","fingers","butterfly","hands"]},"palms-up-together":{"a":"Palms Up Together","b":"1F932","j":["prayer","cupped hands","hands","gesture","cupped"]},"handshake":{"a":"Handshake","b":"1F91D","j":["agreement","hand","meeting","shake"]},"folded-hands":{"a":"Folded Hands","b":"1F64F","j":["ask","hand","high 5","high five","please","pray","thanks","hope","wish","namaste","highfive"]},"writing-hand":{"a":"Writing Hand","b":"270D","j":["hand","write","lower_left_ballpoint_pen","stationery","compose"]},"nail-polish":{"a":"Nail Polish","b":"1F485","j":["care","cosmetics","manicure","nail","polish","beauty","finger","fashion"]},"selfie":{"a":"Selfie","b":"1F933","j":["camera","phone"]},"flexed-biceps":{"a":"Flexed Biceps","b":"1F4AA","j":["biceps","comic","flex","muscle","arm","hand","summer","strong"]},"mechanical-arm":{"a":"Mechanical Arm","b":"1F9BE","j":["accessibility","prosthetic"]},"mechanical-leg":{"a":"Mechanical Leg","b":"1F9BF","j":["accessibility","prosthetic"]},"leg":{"a":"Leg","b":"1F9B5","j":["kick","limb"]},"foot":{"a":"Foot","b":"1F9B6","j":["kick","stomp"]},"ear":{"a":"Ear","b":"1F442","j":["body","face","hear","sound","listen"]},"ear-with-hearing-aid":{"a":"Ear with Hearing Aid","b":"1F9BB","j":["accessibility","hard of hearing"]},"nose":{"a":"Nose","b":"1F443","j":["body","smell","sniff"]},"brain":{"a":"Brain","b":"1F9E0","j":["intelligent","smart"]},"anatomical-heart":{"a":"Anatomical Heart","b":"1FAC0","j":["anatomical","cardiology","heart","organ","pulse","health","heartbeat"]},"lungs":{"a":"Lungs","b":"1FAC1","j":["breath","exhalation","inhalation","organ","respiration","breathe"]},"tooth":{"a":"Tooth","b":"1F9B7","j":["dentist","teeth"]},"bone":{"a":"Bone","b":"1F9B4","j":["skeleton"]},"eyes":{"a":"Eyes","b":"1F440","j":["eye","face","look","watch","stalk","peek","see"]},"eye":{"a":"Eye","b":"1F441","j":["body","face","look","see","watch","stare"]},"tongue":{"a":"Tongue","b":"1F445","j":["body","mouth","playful"]},"mouth":{"a":"Mouth","b":"1F444","j":["lips","kiss"]},"baby":{"a":"Baby","b":"1F476","j":["young","child","boy","girl","toddler"]},"child":{"a":"Child","b":"1F9D2","j":["gender-neutral","unspecified gender","young"]},"boy":{"a":"Boy","b":"1F466","j":["young","man","male","guy","teenager"]},"girl":{"a":"Girl","b":"1F467","j":["Virgo","young","zodiac","female","woman","teenager"]},"person":{"a":"Person","b":"1F9D1","j":["adult","gender-neutral","unspecified gender"]},"person-blond-hair":{"a":"Person: Blond Hair","b":"1F471","j":["blond","blond-haired person","hair","person: blond hair","hairstyle"]},"man":{"a":"Man","b":"1F468","j":["adult","mustache","father","dad","guy","classy","sir","moustache"]},"person-beard":{"a":"Person: Beard","b":"1F9D4","j":["beard","person","person: beard","bewhiskered","man_beard"]},"man-beard":{"a":"⊛ Man: Beard","b":"1F9D4-200D-2642-FE0F","j":["beard","man","man: beard"]},"woman-beard":{"a":"⊛ Woman: Beard","b":"1F9D4-200D-2640-FE0F","j":["beard","woman","woman: beard"]},"man-red-hair":{"a":"Man: Red Hair","b":"1F468-200D-1F9B0","j":["adult","man","red hair","hairstyle"]},"man-curly-hair":{"a":"Man: Curly Hair","b":"1F468-200D-1F9B1","j":["adult","curly hair","man","hairstyle"]},"man-white-hair":{"a":"Man: White Hair","b":"1F468-200D-1F9B3","j":["adult","man","white hair","old","elder"]},"man-bald":{"a":"Man: Bald","b":"1F468-200D-1F9B2","j":["adult","bald","man","hairless"]},"woman":{"a":"Woman","b":"1F469","j":["adult","female","girls","lady"]},"woman-red-hair":{"a":"Woman: Red Hair","b":"1F469-200D-1F9B0","j":["adult","red hair","woman","hairstyle"]},"person-red-hair":{"a":"Person: Red Hair","b":"1F9D1-200D-1F9B0","j":["adult","gender-neutral","person","red hair","unspecified gender","hairstyle"]},"woman-curly-hair":{"a":"Woman: Curly Hair","b":"1F469-200D-1F9B1","j":["adult","curly hair","woman","hairstyle"]},"person-curly-hair":{"a":"Person: Curly Hair","b":"1F9D1-200D-1F9B1","j":["adult","curly hair","gender-neutral","person","unspecified gender","hairstyle"]},"woman-white-hair":{"a":"Woman: White Hair","b":"1F469-200D-1F9B3","j":["adult","white hair","woman","old","elder"]},"person-white-hair":{"a":"Person: White Hair","b":"1F9D1-200D-1F9B3","j":["adult","gender-neutral","person","unspecified gender","white hair","elder","old"]},"woman-bald":{"a":"Woman: Bald","b":"1F469-200D-1F9B2","j":["adult","bald","woman","hairless"]},"person-bald":{"a":"Person: Bald","b":"1F9D1-200D-1F9B2","j":["adult","bald","gender-neutral","person","unspecified gender","hairless"]},"woman-blond-hair":{"a":"Woman: Blond Hair","b":"1F471-200D-2640-FE0F","j":["blond-haired woman","blonde","hair","woman","woman: blond hair","female","girl","person"]},"man-blond-hair":{"a":"Man: Blond Hair","b":"1F471-200D-2642-FE0F","j":["blond","blond-haired man","hair","man","man: blond hair","male","boy","blonde","guy","person"]},"older-person":{"a":"Older Person","b":"1F9D3","j":["adult","gender-neutral","old","unspecified gender","human","elder","senior"]},"old-man":{"a":"Old Man","b":"1F474","j":["adult","man","old","human","male","men","elder","senior"]},"old-woman":{"a":"Old Woman","b":"1F475","j":["adult","old","woman","human","female","women","lady","elder","senior"]},"person-frowning":{"a":"Person Frowning","b":"1F64D","j":["frown","gesture","worried"]},"man-frowning":{"a":"Man Frowning","b":"1F64D-200D-2642-FE0F","j":["frowning","gesture","man","male","boy","sad","depressed","discouraged","unhappy"]},"woman-frowning":{"a":"Woman Frowning","b":"1F64D-200D-2640-FE0F","j":["frowning","gesture","woman","female","girl","sad","depressed","discouraged","unhappy"]},"person-pouting":{"a":"Person Pouting","b":"1F64E","j":["gesture","pouting","upset"]},"man-pouting":{"a":"Man Pouting","b":"1F64E-200D-2642-FE0F","j":["gesture","man","pouting","male","boy"]},"woman-pouting":{"a":"Woman Pouting","b":"1F64E-200D-2640-FE0F","j":["gesture","pouting","woman","female","girl"]},"person-gesturing-no":{"a":"Person Gesturing No","b":"1F645","j":["forbidden","gesture","hand","person gesturing NO","prohibited","decline"]},"man-gesturing-no":{"a":"Man Gesturing No","b":"1F645-200D-2642-FE0F","j":["forbidden","gesture","hand","man","man gesturing NO","prohibited","male","boy","nope"]},"woman-gesturing-no":{"a":"Woman Gesturing No","b":"1F645-200D-2640-FE0F","j":["forbidden","gesture","hand","prohibited","woman","woman gesturing NO","female","girl","nope"]},"person-gesturing-ok":{"a":"Person Gesturing Ok","b":"1F646","j":["gesture","hand","OK","person gesturing OK","agree"]},"man-gesturing-ok":{"a":"Man Gesturing Ok","b":"1F646-200D-2642-FE0F","j":["gesture","hand","man","man gesturing OK","OK","men","boy","male","blue","human"]},"woman-gesturing-ok":{"a":"Woman Gesturing Ok","b":"1F646-200D-2640-FE0F","j":["gesture","hand","OK","woman","woman gesturing OK","women","girl","female","pink","human"]},"person-tipping-hand":{"a":"Person Tipping Hand","b":"1F481","j":["hand","help","information","sassy","tipping"]},"man-tipping-hand":{"a":"Man Tipping Hand","b":"1F481-200D-2642-FE0F","j":["man","sassy","tipping hand","male","boy","human","information"]},"woman-tipping-hand":{"a":"Woman Tipping Hand","b":"1F481-200D-2640-FE0F","j":["sassy","tipping hand","woman","female","girl","human","information"]},"person-raising-hand":{"a":"Person Raising Hand","b":"1F64B","j":["gesture","hand","happy","raised","question"]},"man-raising-hand":{"a":"Man Raising Hand","b":"1F64B-200D-2642-FE0F","j":["gesture","man","raising hand","male","boy"]},"woman-raising-hand":{"a":"Woman Raising Hand","b":"1F64B-200D-2640-FE0F","j":["gesture","raising hand","woman","female","girl"]},"deaf-person":{"a":"Deaf Person","b":"1F9CF","j":["accessibility","deaf","ear","hear"]},"deaf-man":{"a":"Deaf Man","b":"1F9CF-200D-2642-FE0F","j":["deaf","man","accessibility"]},"deaf-woman":{"a":"Deaf Woman","b":"1F9CF-200D-2640-FE0F","j":["deaf","woman","accessibility"]},"person-bowing":{"a":"Person Bowing","b":"1F647","j":["apology","bow","gesture","sorry","respectiful"]},"man-bowing":{"a":"Man Bowing","b":"1F647-200D-2642-FE0F","j":["apology","bowing","favor","gesture","man","sorry","male","boy"]},"woman-bowing":{"a":"Woman Bowing","b":"1F647-200D-2640-FE0F","j":["apology","bowing","favor","gesture","sorry","woman","female","girl"]},"person-facepalming":{"a":"Person Facepalming","b":"1F926","j":["disbelief","exasperation","face","palm","disappointed"]},"man-facepalming":{"a":"Man Facepalming","b":"1F926-200D-2642-FE0F","j":["disbelief","exasperation","facepalm","man","male","boy"]},"woman-facepalming":{"a":"Woman Facepalming","b":"1F926-200D-2640-FE0F","j":["disbelief","exasperation","facepalm","woman","female","girl"]},"person-shrugging":{"a":"Person Shrugging","b":"1F937","j":["doubt","ignorance","indifference","shrug","regardless"]},"man-shrugging":{"a":"Man Shrugging","b":"1F937-200D-2642-FE0F","j":["doubt","ignorance","indifference","man","shrug","male","boy","confused","indifferent"]},"woman-shrugging":{"a":"Woman Shrugging","b":"1F937-200D-2640-FE0F","j":["doubt","ignorance","indifference","shrug","woman","female","girl","confused","indifferent"]},"health-worker":{"a":"Health Worker","b":"1F9D1-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","hospital"]},"man-health-worker":{"a":"Man Health Worker","b":"1F468-200D-2695-FE0F","j":["doctor","healthcare","man","nurse","therapist","human"]},"woman-health-worker":{"a":"Woman Health Worker","b":"1F469-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","woman","human"]},"student":{"a":"Student","b":"1F9D1-200D-1F393","j":["graduate","learn"]},"man-student":{"a":"Man Student","b":"1F468-200D-1F393","j":["graduate","man","student","human"]},"woman-student":{"a":"Woman Student","b":"1F469-200D-1F393","j":["graduate","student","woman","human"]},"teacher":{"a":"Teacher","b":"1F9D1-200D-1F3EB","j":["instructor","professor"]},"man-teacher":{"a":"Man Teacher","b":"1F468-200D-1F3EB","j":["instructor","man","professor","teacher","human"]},"woman-teacher":{"a":"Woman Teacher","b":"1F469-200D-1F3EB","j":["instructor","professor","teacher","woman","human"]},"judge":{"a":"Judge","b":"1F9D1-200D-2696-FE0F","j":["justice","scales","law"]},"man-judge":{"a":"Man Judge","b":"1F468-200D-2696-FE0F","j":["judge","justice","man","scales","court","human"]},"woman-judge":{"a":"Woman Judge","b":"1F469-200D-2696-FE0F","j":["judge","justice","scales","woman","court","human"]},"farmer":{"a":"Farmer","b":"1F9D1-200D-1F33E","j":["gardener","rancher","crops"]},"man-farmer":{"a":"Man Farmer","b":"1F468-200D-1F33E","j":["farmer","gardener","man","rancher","human"]},"woman-farmer":{"a":"Woman Farmer","b":"1F469-200D-1F33E","j":["farmer","gardener","rancher","woman","human"]},"cook":{"a":"Cook","b":"1F9D1-200D-1F373","j":["chef","food","kitchen","culinary"]},"man-cook":{"a":"Man Cook","b":"1F468-200D-1F373","j":["chef","cook","man","human"]},"woman-cook":{"a":"Woman Cook","b":"1F469-200D-1F373","j":["chef","cook","woman","human"]},"mechanic":{"a":"Mechanic","b":"1F9D1-200D-1F527","j":["electrician","plumber","tradesperson","worker","technician"]},"man-mechanic":{"a":"Man Mechanic","b":"1F468-200D-1F527","j":["electrician","man","mechanic","plumber","tradesperson","human","wrench"]},"woman-mechanic":{"a":"Woman Mechanic","b":"1F469-200D-1F527","j":["electrician","mechanic","plumber","tradesperson","woman","human","wrench"]},"factory-worker":{"a":"Factory Worker","b":"1F9D1-200D-1F3ED","j":["assembly","factory","industrial","worker","labor"]},"man-factory-worker":{"a":"Man Factory Worker","b":"1F468-200D-1F3ED","j":["assembly","factory","industrial","man","worker","human"]},"woman-factory-worker":{"a":"Woman Factory Worker","b":"1F469-200D-1F3ED","j":["assembly","factory","industrial","woman","worker","human"]},"office-worker":{"a":"Office Worker","b":"1F9D1-200D-1F4BC","j":["architect","business","manager","white-collar"]},"man-office-worker":{"a":"Man Office Worker","b":"1F468-200D-1F4BC","j":["architect","business","man","manager","white-collar","human"]},"woman-office-worker":{"a":"Woman Office Worker","b":"1F469-200D-1F4BC","j":["architect","business","manager","white-collar","woman","human"]},"scientist":{"a":"Scientist","b":"1F9D1-200D-1F52C","j":["biologist","chemist","engineer","physicist","chemistry"]},"man-scientist":{"a":"Man Scientist","b":"1F468-200D-1F52C","j":["biologist","chemist","engineer","man","physicist","scientist","human"]},"woman-scientist":{"a":"Woman Scientist","b":"1F469-200D-1F52C","j":["biologist","chemist","engineer","physicist","scientist","woman","human"]},"technologist":{"a":"Technologist","b":"1F9D1-200D-1F4BB","j":["coder","developer","inventor","software","computer"]},"man-technologist":{"a":"Man Technologist","b":"1F468-200D-1F4BB","j":["coder","developer","inventor","man","software","technologist","engineer","programmer","human","laptop","computer"]},"woman-technologist":{"a":"Woman Technologist","b":"1F469-200D-1F4BB","j":["coder","developer","inventor","software","technologist","woman","engineer","programmer","human","laptop","computer"]},"singer":{"a":"Singer","b":"1F9D1-200D-1F3A4","j":["actor","entertainer","rock","star","song","artist","performer"]},"man-singer":{"a":"Man Singer","b":"1F468-200D-1F3A4","j":["actor","entertainer","man","rock","singer","star","rockstar","human"]},"woman-singer":{"a":"Woman Singer","b":"1F469-200D-1F3A4","j":["actor","entertainer","rock","singer","star","woman","rockstar","human"]},"artist":{"a":"Artist","b":"1F9D1-200D-1F3A8","j":["palette","painting","draw","creativity"]},"man-artist":{"a":"Man Artist","b":"1F468-200D-1F3A8","j":["artist","man","palette","painter","human"]},"woman-artist":{"a":"Woman Artist","b":"1F469-200D-1F3A8","j":["artist","palette","woman","painter","human"]},"pilot":{"a":"Pilot","b":"1F9D1-200D-2708-FE0F","j":["plane","fly","airplane"]},"man-pilot":{"a":"Man Pilot","b":"1F468-200D-2708-FE0F","j":["man","pilot","plane","aviator","human"]},"woman-pilot":{"a":"Woman Pilot","b":"1F469-200D-2708-FE0F","j":["pilot","plane","woman","aviator","human"]},"astronaut":{"a":"Astronaut","b":"1F9D1-200D-1F680","j":["rocket","outerspace"]},"man-astronaut":{"a":"Man Astronaut","b":"1F468-200D-1F680","j":["astronaut","man","rocket","space","human"]},"woman-astronaut":{"a":"Woman Astronaut","b":"1F469-200D-1F680","j":["astronaut","rocket","woman","space","human"]},"firefighter":{"a":"Firefighter","b":"1F9D1-200D-1F692","j":["firetruck","fire"]},"man-firefighter":{"a":"Man Firefighter","b":"1F468-200D-1F692","j":["firefighter","firetruck","man","fireman","human"]},"woman-firefighter":{"a":"Woman Firefighter","b":"1F469-200D-1F692","j":["firefighter","firetruck","woman","fireman","human"]},"police-officer":{"a":"Police Officer","b":"1F46E","j":["cop","officer","police"]},"man-police-officer":{"a":"Man Police Officer","b":"1F46E-200D-2642-FE0F","j":["cop","man","officer","police","law","legal","enforcement","arrest","911"]},"woman-police-officer":{"a":"Woman Police Officer","b":"1F46E-200D-2640-FE0F","j":["cop","officer","police","woman","law","legal","enforcement","arrest","911","female"]},"detective":{"a":"Detective","b":"1F575","j":["sleuth","spy","human"]},"man-detective":{"a":"Man Detective","b":"1F575-FE0F-200D-2642-FE0F","j":["detective","man","sleuth","spy","crime"]},"woman-detective":{"a":"Woman Detective","b":"1F575-FE0F-200D-2640-FE0F","j":["detective","sleuth","spy","woman","human","female"]},"guard":{"a":"Guard","b":"1F482","j":["protect"]},"man-guard":{"a":"Man Guard","b":"1F482-200D-2642-FE0F","j":["guard","man","uk","gb","british","male","guy","royal"]},"woman-guard":{"a":"Woman Guard","b":"1F482-200D-2640-FE0F","j":["guard","woman","uk","gb","british","female","royal"]},"ninja":{"a":"Ninja","b":"1F977","j":["fighter","hidden","stealth","ninjutsu","skills","japanese"]},"construction-worker":{"a":"Construction Worker","b":"1F477","j":["construction","hat","worker","labor","build"]},"man-construction-worker":{"a":"Man Construction Worker","b":"1F477-200D-2642-FE0F","j":["construction","man","worker","male","human","wip","guy","build","labor"]},"woman-construction-worker":{"a":"Woman Construction Worker","b":"1F477-200D-2640-FE0F","j":["construction","woman","worker","female","human","wip","build","labor"]},"prince":{"a":"Prince","b":"1F934","j":["boy","man","male","crown","royal","king"]},"princess":{"a":"Princess","b":"1F478","j":["fairy tale","fantasy","girl","woman","female","blond","crown","royal","queen"]},"person-wearing-turban":{"a":"Person Wearing Turban","b":"1F473","j":["turban","headdress"]},"man-wearing-turban":{"a":"Man Wearing Turban","b":"1F473-200D-2642-FE0F","j":["man","turban","male","indian","hinduism","arabs"]},"woman-wearing-turban":{"a":"Woman Wearing Turban","b":"1F473-200D-2640-FE0F","j":["turban","woman","female","indian","hinduism","arabs"]},"person-with-skullcap":{"a":"Person with Skullcap","b":"1F472","j":["cap","gua pi mao","hat","person","skullcap","man_with_skullcap","male","boy","chinese"]},"woman-with-headscarf":{"a":"Woman with Headscarf","b":"1F9D5","j":["headscarf","hijab","mantilla","tichel","bandana","head kerchief","female"]},"person-in-tuxedo":{"a":"Person in Tuxedo","b":"1F935","j":["groom","person","tuxedo","man_in_tuxedo","couple","marriage","wedding"]},"man-in-tuxedo":{"a":"Man in Tuxedo","b":"1F935-200D-2642-FE0F","j":["man","tuxedo","formal","fashion"]},"woman-in-tuxedo":{"a":"Woman in Tuxedo","b":"1F935-200D-2640-FE0F","j":["tuxedo","woman","formal","fashion"]},"person-with-veil":{"a":"Person with Veil","b":"1F470","j":["bride","person","veil","wedding","bride_with_veil","couple","marriage","woman"]},"man-with-veil":{"a":"Man with Veil","b":"1F470-200D-2642-FE0F","j":["man","veil","wedding","marriage"]},"woman-with-veil":{"a":"Woman with Veil","b":"1F470-200D-2640-FE0F","j":["veil","woman","wedding","marriage"]},"pregnant-woman":{"a":"Pregnant Woman","b":"1F930","j":["pregnant","woman","baby"]},"breastfeeding":{"a":"Breast-Feeding","b":"1F931","j":["baby","breast","breast-feeding","nursing","breast_feeding"]},"woman-feeding-baby":{"a":"Woman Feeding Baby","b":"1F469-200D-1F37C","j":["baby","feeding","nursing","woman","birth","food"]},"man-feeding-baby":{"a":"Man Feeding Baby","b":"1F468-200D-1F37C","j":["baby","feeding","man","nursing","birth","food"]},"person-feeding-baby":{"a":"Person Feeding Baby","b":"1F9D1-200D-1F37C","j":["baby","feeding","nursing","person","birth","food"]},"baby-angel":{"a":"Baby Angel","b":"1F47C","j":["angel","baby","face","fairy tale","fantasy","heaven","wings","halo"]},"santa-claus":{"a":"Santa Claus","b":"1F385","j":["celebration","Christmas","claus","father","santa","festival","man","male","xmas","father christmas"]},"mrs-claus":{"a":"Mrs. Claus","b":"1F936","j":["celebration","Christmas","claus","mother","Mrs.","woman","female","xmas","mother christmas"]},"mx-claus":{"a":"Mx Claus","b":"1F9D1-200D-1F384","j":["Claus, christmas","christmas"]},"superhero":{"a":"Superhero","b":"1F9B8","j":["good","hero","heroine","superpower","marvel"]},"man-superhero":{"a":"Man Superhero","b":"1F9B8-200D-2642-FE0F","j":["good","hero","man","superpower","male","superpowers"]},"woman-superhero":{"a":"Woman Superhero","b":"1F9B8-200D-2640-FE0F","j":["good","hero","heroine","superpower","woman","female","superpowers"]},"supervillain":{"a":"Supervillain","b":"1F9B9","j":["criminal","evil","superpower","villain","marvel"]},"man-supervillain":{"a":"Man Supervillain","b":"1F9B9-200D-2642-FE0F","j":["criminal","evil","man","superpower","villain","male","bad","hero","superpowers"]},"woman-supervillain":{"a":"Woman Supervillain","b":"1F9B9-200D-2640-FE0F","j":["criminal","evil","superpower","villain","woman","female","bad","heroine","superpowers"]},"mage":{"a":"Mage","b":"1F9D9","j":["sorcerer","sorceress","witch","wizard","magic"]},"man-mage":{"a":"Man Mage","b":"1F9D9-200D-2642-FE0F","j":["sorcerer","wizard","man","male","mage"]},"woman-mage":{"a":"Woman Mage","b":"1F9D9-200D-2640-FE0F","j":["sorceress","witch","woman","female","mage"]},"fairy":{"a":"Fairy","b":"1F9DA","j":["Oberon","Puck","Titania","wings","magical"]},"man-fairy":{"a":"Man Fairy","b":"1F9DA-200D-2642-FE0F","j":["Oberon","Puck","man","male"]},"woman-fairy":{"a":"Woman Fairy","b":"1F9DA-200D-2640-FE0F","j":["Titania","woman","female"]},"vampire":{"a":"Vampire","b":"1F9DB","j":["Dracula","undead","blood","twilight"]},"man-vampire":{"a":"Man Vampire","b":"1F9DB-200D-2642-FE0F","j":["Dracula","undead","man","male","dracula"]},"woman-vampire":{"a":"Woman Vampire","b":"1F9DB-200D-2640-FE0F","j":["undead","woman","female"]},"merperson":{"a":"Merperson","b":"1F9DC","j":["mermaid","merman","merwoman","sea"]},"merman":{"a":"Merman","b":"1F9DC-200D-2642-FE0F","j":["Triton","man","male","triton"]},"mermaid":{"a":"Mermaid","b":"1F9DC-200D-2640-FE0F","j":["merwoman","woman","female","ariel"]},"elf":{"a":"Elf","b":"1F9DD","j":["magical","LOTR style"]},"man-elf":{"a":"Man Elf","b":"1F9DD-200D-2642-FE0F","j":["magical","man","male"]},"woman-elf":{"a":"Woman Elf","b":"1F9DD-200D-2640-FE0F","j":["magical","woman","female"]},"genie":{"a":"Genie","b":"1F9DE","j":["djinn","(non-human color)","magical","wishes"]},"man-genie":{"a":"Man Genie","b":"1F9DE-200D-2642-FE0F","j":["djinn","man","male"]},"woman-genie":{"a":"Woman Genie","b":"1F9DE-200D-2640-FE0F","j":["djinn","woman","female"]},"zombie":{"a":"Zombie","b":"1F9DF","j":["undead","walking dead","(non-human color)","dead"]},"man-zombie":{"a":"Man Zombie","b":"1F9DF-200D-2642-FE0F","j":["undead","walking dead","man","male","dracula"]},"woman-zombie":{"a":"Woman Zombie","b":"1F9DF-200D-2640-FE0F","j":["undead","walking dead","woman","female"]},"person-getting-massage":{"a":"Person Getting Massage","b":"1F486","j":["face","massage","salon","relax"]},"man-getting-massage":{"a":"Man Getting Massage","b":"1F486-200D-2642-FE0F","j":["face","man","massage","male","boy","head"]},"woman-getting-massage":{"a":"Woman Getting Massage","b":"1F486-200D-2640-FE0F","j":["face","massage","woman","female","girl","head"]},"person-getting-haircut":{"a":"Person Getting Haircut","b":"1F487","j":["barber","beauty","haircut","parlor","hairstyle"]},"man-getting-haircut":{"a":"Man Getting Haircut","b":"1F487-200D-2642-FE0F","j":["haircut","man","male","boy"]},"woman-getting-haircut":{"a":"Woman Getting Haircut","b":"1F487-200D-2640-FE0F","j":["haircut","woman","female","girl"]},"person-walking":{"a":"Person Walking","b":"1F6B6","j":["hike","walk","walking","move"]},"man-walking":{"a":"Man Walking","b":"1F6B6-200D-2642-FE0F","j":["hike","man","walk","human","feet","steps"]},"woman-walking":{"a":"Woman Walking","b":"1F6B6-200D-2640-FE0F","j":["hike","walk","woman","human","feet","steps","female"]},"person-standing":{"a":"Person Standing","b":"1F9CD","j":["stand","standing","still"]},"man-standing":{"a":"Man Standing","b":"1F9CD-200D-2642-FE0F","j":["man","standing","still"]},"woman-standing":{"a":"Woman Standing","b":"1F9CD-200D-2640-FE0F","j":["standing","woman","still"]},"person-kneeling":{"a":"Person Kneeling","b":"1F9CE","j":["kneel","kneeling","pray","respectful"]},"man-kneeling":{"a":"Man Kneeling","b":"1F9CE-200D-2642-FE0F","j":["kneeling","man","pray","respectful"]},"woman-kneeling":{"a":"Woman Kneeling","b":"1F9CE-200D-2640-FE0F","j":["kneeling","woman","respectful","pray"]},"person-with-white-cane":{"a":"Person with White Cane","b":"1F9D1-200D-1F9AF","j":["accessibility","blind","person_with_probing_cane"]},"man-with-white-cane":{"a":"Man with White Cane","b":"1F468-200D-1F9AF","j":["accessibility","blind","man","man_with_probing_cane"]},"woman-with-white-cane":{"a":"Woman with White Cane","b":"1F469-200D-1F9AF","j":["accessibility","blind","woman","woman_with_probing_cane"]},"person-in-motorized-wheelchair":{"a":"Person in Motorized Wheelchair","b":"1F9D1-200D-1F9BC","j":["accessibility","wheelchair","disability"]},"man-in-motorized-wheelchair":{"a":"Man in Motorized Wheelchair","b":"1F468-200D-1F9BC","j":["accessibility","man","wheelchair","disability"]},"woman-in-motorized-wheelchair":{"a":"Woman in Motorized Wheelchair","b":"1F469-200D-1F9BC","j":["accessibility","wheelchair","woman","disability"]},"person-in-manual-wheelchair":{"a":"Person in Manual Wheelchair","b":"1F9D1-200D-1F9BD","j":["accessibility","wheelchair","disability"]},"man-in-manual-wheelchair":{"a":"Man in Manual Wheelchair","b":"1F468-200D-1F9BD","j":["accessibility","man","wheelchair","disability"]},"woman-in-manual-wheelchair":{"a":"Woman in Manual Wheelchair","b":"1F469-200D-1F9BD","j":["accessibility","wheelchair","woman","disability"]},"person-running":{"a":"Person Running","b":"1F3C3","j":["marathon","running","move"]},"man-running":{"a":"Man Running","b":"1F3C3-200D-2642-FE0F","j":["man","marathon","racing","running","walking","exercise","race"]},"woman-running":{"a":"Woman Running","b":"1F3C3-200D-2640-FE0F","j":["marathon","racing","running","woman","walking","exercise","race","female"]},"woman-dancing":{"a":"Woman Dancing","b":"1F483","j":["dance","dancing","woman","female","girl","fun"]},"man-dancing":{"a":"Man Dancing","b":"1F57A","j":["dance","dancing","man","male","boy","fun","dancer"]},"person-in-suit-levitating":{"a":"Person in Suit Levitating","b":"1F574","j":["business","person","suit","man_in_suit_levitating","levitate","hover","jump"]},"people-with-bunny-ears":{"a":"People with Bunny Ears","b":"1F46F","j":["bunny ear","dancer","partying","perform","costume"]},"men-with-bunny-ears":{"a":"Men with Bunny Ears","b":"1F46F-200D-2642-FE0F","j":["bunny ear","dancer","men","partying","male","bunny","boys"]},"women-with-bunny-ears":{"a":"Women with Bunny Ears","b":"1F46F-200D-2640-FE0F","j":["bunny ear","dancer","partying","women","female","bunny","girls"]},"person-in-steamy-room":{"a":"Person in Steamy Room","b":"1F9D6","j":["sauna","steam room","hamam","steambath","relax","spa"]},"man-in-steamy-room":{"a":"Man in Steamy Room","b":"1F9D6-200D-2642-FE0F","j":["sauna","steam room","male","man","spa","steamroom"]},"woman-in-steamy-room":{"a":"Woman in Steamy Room","b":"1F9D6-200D-2640-FE0F","j":["sauna","steam room","female","woman","spa","steamroom"]},"person-climbing":{"a":"Person Climbing","b":"1F9D7","j":["climber","sport"]},"man-climbing":{"a":"Man Climbing","b":"1F9D7-200D-2642-FE0F","j":["climber","sports","hobby","man","male","rock"]},"woman-climbing":{"a":"Woman Climbing","b":"1F9D7-200D-2640-FE0F","j":["climber","sports","hobby","woman","female","rock"]},"person-fencing":{"a":"Person Fencing","b":"1F93A","j":["fencer","fencing","sword","sports"]},"horse-racing":{"a":"Horse Racing","b":"1F3C7","j":["horse","jockey","racehorse","racing","animal","betting","competition","gambling","luck"]},"skier":{"a":"Skier","b":"26F7","j":["ski","snow","sports","winter"]},"snowboarder":{"a":"Snowboarder","b":"1F3C2","j":["ski","snow","snowboard","sports","winter"]},"person-golfing":{"a":"Person Golfing","b":"1F3CC","j":["ball","golf","sports","business"]},"man-golfing":{"a":"Man Golfing","b":"1F3CC-FE0F-200D-2642-FE0F","j":["golf","man","sport"]},"woman-golfing":{"a":"Woman Golfing","b":"1F3CC-FE0F-200D-2640-FE0F","j":["golf","woman","sports","business","female"]},"person-surfing":{"a":"Person Surfing","b":"1F3C4","j":["surfing","sport","sea"]},"man-surfing":{"a":"Man Surfing","b":"1F3C4-200D-2642-FE0F","j":["man","surfing","sports","ocean","sea","summer","beach"]},"woman-surfing":{"a":"Woman Surfing","b":"1F3C4-200D-2640-FE0F","j":["surfing","woman","sports","ocean","sea","summer","beach","female"]},"person-rowing-boat":{"a":"Person Rowing Boat","b":"1F6A3","j":["boat","rowboat","sport","move"]},"man-rowing-boat":{"a":"Man Rowing Boat","b":"1F6A3-200D-2642-FE0F","j":["boat","man","rowboat","sports","hobby","water","ship"]},"woman-rowing-boat":{"a":"Woman Rowing Boat","b":"1F6A3-200D-2640-FE0F","j":["boat","rowboat","woman","sports","hobby","water","ship","female"]},"person-swimming":{"a":"Person Swimming","b":"1F3CA","j":["swim","sport","pool"]},"man-swimming":{"a":"Man Swimming","b":"1F3CA-200D-2642-FE0F","j":["man","swim","sports","exercise","human","athlete","water","summer"]},"woman-swimming":{"a":"Woman Swimming","b":"1F3CA-200D-2640-FE0F","j":["swim","woman","sports","exercise","human","athlete","water","summer","female"]},"person-bouncing-ball":{"a":"Person Bouncing Ball","b":"26F9","j":["ball","sports","human"]},"man-bouncing-ball":{"a":"Man Bouncing Ball","b":"26F9-FE0F-200D-2642-FE0F","j":["ball","man","sport"]},"woman-bouncing-ball":{"a":"Woman Bouncing Ball","b":"26F9-FE0F-200D-2640-FE0F","j":["ball","woman","sports","human","female"]},"person-lifting-weights":{"a":"Person Lifting Weights","b":"1F3CB","j":["lifter","weight","sports","training","exercise"]},"man-lifting-weights":{"a":"Man Lifting Weights","b":"1F3CB-FE0F-200D-2642-FE0F","j":["man","weight lifter","sport"]},"woman-lifting-weights":{"a":"Woman Lifting Weights","b":"1F3CB-FE0F-200D-2640-FE0F","j":["weight lifter","woman","sports","training","exercise","female"]},"person-biking":{"a":"Person Biking","b":"1F6B4","j":["bicycle","biking","cyclist","sport","move"]},"man-biking":{"a":"Man Biking","b":"1F6B4-200D-2642-FE0F","j":["bicycle","biking","cyclist","man","sports","bike","exercise","hipster"]},"woman-biking":{"a":"Woman Biking","b":"1F6B4-200D-2640-FE0F","j":["bicycle","biking","cyclist","woman","sports","bike","exercise","hipster","female"]},"person-mountain-biking":{"a":"Person Mountain Biking","b":"1F6B5","j":["bicycle","bicyclist","bike","cyclist","mountain","sport","move"]},"man-mountain-biking":{"a":"Man Mountain Biking","b":"1F6B5-200D-2642-FE0F","j":["bicycle","bike","cyclist","man","mountain","transportation","sports","human","race"]},"woman-mountain-biking":{"a":"Woman Mountain Biking","b":"1F6B5-200D-2640-FE0F","j":["bicycle","bike","biking","cyclist","mountain","woman","transportation","sports","human","race","female"]},"person-cartwheeling":{"a":"Person Cartwheeling","b":"1F938","j":["cartwheel","gymnastics","sport","gymnastic"]},"man-cartwheeling":{"a":"Man Cartwheeling","b":"1F938-200D-2642-FE0F","j":["cartwheel","gymnastics","man"]},"woman-cartwheeling":{"a":"Woman Cartwheeling","b":"1F938-200D-2640-FE0F","j":["cartwheel","gymnastics","woman"]},"people-wrestling":{"a":"People Wrestling","b":"1F93C","j":["wrestle","wrestler","sport"]},"men-wrestling":{"a":"Men Wrestling","b":"1F93C-200D-2642-FE0F","j":["men","wrestle","sports","wrestlers"]},"women-wrestling":{"a":"Women Wrestling","b":"1F93C-200D-2640-FE0F","j":["women","wrestle","sports","wrestlers"]},"person-playing-water-polo":{"a":"Person Playing Water Polo","b":"1F93D","j":["polo","water","sport"]},"man-playing-water-polo":{"a":"Man Playing Water Polo","b":"1F93D-200D-2642-FE0F","j":["man","water polo","sports","pool"]},"woman-playing-water-polo":{"a":"Woman Playing Water Polo","b":"1F93D-200D-2640-FE0F","j":["water polo","woman","sports","pool"]},"person-playing-handball":{"a":"Person Playing Handball","b":"1F93E","j":["ball","handball","sport"]},"man-playing-handball":{"a":"Man Playing Handball","b":"1F93E-200D-2642-FE0F","j":["handball","man","sports"]},"woman-playing-handball":{"a":"Woman Playing Handball","b":"1F93E-200D-2640-FE0F","j":["handball","woman","sports"]},"person-juggling":{"a":"Person Juggling","b":"1F939","j":["balance","juggle","multitask","skill","performance"]},"man-juggling":{"a":"Man Juggling","b":"1F939-200D-2642-FE0F","j":["juggling","man","multitask","juggle","balance","skill"]},"woman-juggling":{"a":"Woman Juggling","b":"1F939-200D-2640-FE0F","j":["juggling","multitask","woman","juggle","balance","skill"]},"person-in-lotus-position":{"a":"Person in Lotus Position","b":"1F9D8","j":["meditation","yoga","serenity","meditate"]},"man-in-lotus-position":{"a":"Man in Lotus Position","b":"1F9D8-200D-2642-FE0F","j":["meditation","yoga","man","male","serenity","zen","mindfulness"]},"woman-in-lotus-position":{"a":"Woman in Lotus Position","b":"1F9D8-200D-2640-FE0F","j":["meditation","yoga","woman","female","serenity","zen","mindfulness"]},"person-taking-bath":{"a":"Person Taking Bath","b":"1F6C0","j":["bath","bathtub","clean","shower","bathroom"]},"person-in-bed":{"a":"Person in Bed","b":"1F6CC","j":["hotel","sleep","bed","rest"]},"people-holding-hands":{"a":"People Holding Hands","b":"1F9D1-200D-1F91D-200D-1F9D1","j":["couple","hand","hold","holding hands","person","friendship"]},"women-holding-hands":{"a":"Women Holding Hands","b":"1F46D","j":["couple","hand","holding hands","women","pair","friendship","love","like","female","people","human"]},"woman-and-man-holding-hands":{"a":"Woman and Man Holding Hands","b":"1F46B","j":["couple","hand","hold","holding hands","man","woman","pair","people","human","love","date","dating","like","affection","valentines","marriage"]},"men-holding-hands":{"a":"Men Holding Hands","b":"1F46C","j":["couple","Gemini","holding hands","man","men","twins","zodiac","pair","love","like","bromance","friendship","people","human"]},"kiss":{"a":"Kiss","b":"1F48F","j":["couple","pair","valentines","love","like","dating","marriage"]},"kiss-woman-man":{"a":"Kiss: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","woman","love"]},"kiss-man-man":{"a":"Kiss: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","pair","valentines","love","like","dating","marriage"]},"kiss-woman-woman":{"a":"Kiss: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F469","j":["couple","kiss","woman","pair","valentines","love","like","dating","marriage"]},"couple-with-heart":{"a":"Couple with Heart","b":"1F491","j":["couple","love","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-man":{"a":"Couple with Heart: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","woman"]},"couple-with-heart-man-man":{"a":"Couple with Heart: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-woman":{"a":"Couple with Heart: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F469","j":["couple","couple with heart","love","woman","pair","like","affection","human","dating","valentines","marriage"]},"family":{"a":"Family","b":"1F46A","j":["home","parents","child","mom","dad","father","mother","people","human"]},"family-man-woman-boy":{"a":"Family: Man, Woman, Boy","b":"1F468-200D-1F469-200D-1F466","j":["boy","family","man","woman","love"]},"family-man-woman-girl":{"a":"Family: Man, Woman, Girl","b":"1F468-200D-1F469-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","child"]},"family-man-woman-girl-boy":{"a":"Family: Man, Woman, Girl, Boy","b":"1F468-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","man","woman","home","parents","people","human","children"]},"family-man-woman-boy-boy":{"a":"Family: Man, Woman, Boy, Boy","b":"1F468-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","man","woman","home","parents","people","human","children"]},"family-man-woman-girl-girl":{"a":"Family: Man, Woman, Girl, Girl","b":"1F468-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","children"]},"family-man-man-boy":{"a":"Family: Man, Man, Boy","b":"1F468-200D-1F468-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl":{"a":"Family: Man, Man, Girl","b":"1F468-200D-1F468-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-man-man-girl-boy":{"a":"Family: Man, Man, Girl, Boy","b":"1F468-200D-1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parents","people","human","children"]},"family-man-man-boy-boy":{"a":"Family: Man, Man, Boy, Boy","b":"1F468-200D-1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl-girl":{"a":"Family: Man, Man, Girl, Girl","b":"1F468-200D-1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-woman-woman-boy":{"a":"Family: Woman, Woman, Boy","b":"1F469-200D-1F469-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl":{"a":"Family: Woman, Woman, Girl","b":"1F469-200D-1F469-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-girl-boy":{"a":"Family: Woman, Woman, Girl, Boy","b":"1F469-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-boy-boy":{"a":"Family: Woman, Woman, Boy, Boy","b":"1F469-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl-girl":{"a":"Family: Woman, Woman, Girl, Girl","b":"1F469-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-man-boy":{"a":"Family: Man, Boy","b":"1F468-200D-1F466","j":["boy","family","man","home","parent","people","human","child"]},"family-man-boy-boy":{"a":"Family: Man, Boy, Boy","b":"1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parent","people","human","children"]},"family-man-girl":{"a":"Family: Man, Girl","b":"1F468-200D-1F467","j":["family","girl","man","home","parent","people","human","child"]},"family-man-girl-boy":{"a":"Family: Man, Girl, Boy","b":"1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parent","people","human","children"]},"family-man-girl-girl":{"a":"Family: Man, Girl, Girl","b":"1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parent","people","human","children"]},"family-woman-boy":{"a":"Family: Woman, Boy","b":"1F469-200D-1F466","j":["boy","family","woman","home","parent","people","human","child"]},"family-woman-boy-boy":{"a":"Family: Woman, Boy, Boy","b":"1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parent","people","human","children"]},"family-woman-girl":{"a":"Family: Woman, Girl","b":"1F469-200D-1F467","j":["family","girl","woman","home","parent","people","human","child"]},"family-woman-girl-boy":{"a":"Family: Woman, Girl, Boy","b":"1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parent","people","human","children"]},"family-woman-girl-girl":{"a":"Family: Woman, Girl, Girl","b":"1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parent","people","human","children"]},"speaking-head":{"a":"Speaking Head","b":"1F5E3","j":["face","head","silhouette","speak","speaking","user","person","human","sing","say","talk"]},"bust-in-silhouette":{"a":"Bust in Silhouette","b":"1F464","j":["bust","silhouette","user","person","human"]},"busts-in-silhouette":{"a":"Busts in Silhouette","b":"1F465","j":["bust","silhouette","user","person","human","group","team"]},"people-hugging":{"a":"People Hugging","b":"1FAC2","j":["goodbye","hello","hug","thanks","care"]},"footprints":{"a":"Footprints","b":"1F463","j":["clothing","footprint","print","feet","tracking","walking","beach"]},"red-hair":{"a":"Red Hair","b":"1F9B0","j":["ginger","red hair","redhead"]},"curly-hair":{"a":"Curly Hair","b":"1F9B1","j":["afro","curly","curly hair","ringlets"]},"white-hair":{"a":"White Hair","b":"1F9B3","j":["gray","hair","old","white"]},"bald":{"a":"Bald","b":"1F9B2","j":["bald","chemotherapy","hairless","no hair","shaven"]},"monkey-face":{"a":"Monkey Face","b":"1F435","j":["face","monkey","animal","nature","circus"]},"monkey":{"a":"Monkey","b":"1F412","j":["animal","nature","banana","circus"]},"gorilla":{"a":"Gorilla","b":"1F98D","j":["animal","nature","circus"]},"orangutan":{"a":"Orangutan","b":"1F9A7","j":["ape","animal"]},"dog-face":{"a":"Dog Face","b":"1F436","j":["dog","face","pet","animal","friend","nature","woof","puppy","faithful"]},"dog":{"a":"Dog","b":"1F415","j":["pet","animal","nature","friend","doge","faithful"]},"guide-dog":{"a":"Guide Dog","b":"1F9AE","j":["accessibility","blind","guide","animal"]},"service-dog":{"a":"Service Dog","b":"1F415-200D-1F9BA","j":["accessibility","assistance","dog","service","blind","animal"]},"poodle":{"a":"Poodle","b":"1F429","j":["dog","animal","101","nature","pet"]},"wolf":{"a":"Wolf","b":"1F43A","j":["face","animal","nature","wild"]},"fox":{"a":"Fox","b":"1F98A","j":["face","animal","nature"]},"raccoon":{"a":"Raccoon","b":"1F99D","j":["curious","sly","animal","nature"]},"cat-face":{"a":"Cat Face","b":"1F431","j":["cat","face","pet","animal","meow","nature","kitten"]},"cat":{"a":"Cat","b":"1F408","j":["pet","animal","meow","cats"]},"black-cat":{"a":"Black Cat","b":"1F408-200D-2B1B","j":["black","cat","unlucky","superstition","luck"]},"lion":{"a":"Lion","b":"1F981","j":["face","Leo","zodiac","animal","nature"]},"tiger-face":{"a":"Tiger Face","b":"1F42F","j":["face","tiger","animal","cat","danger","wild","nature","roar"]},"tiger":{"a":"Tiger","b":"1F405","j":["animal","nature","roar"]},"leopard":{"a":"Leopard","b":"1F406","j":["animal","nature"]},"horse-face":{"a":"Horse Face","b":"1F434","j":["face","horse","animal","brown","nature"]},"horse":{"a":"Horse","b":"1F40E","j":["equestrian","racehorse","racing","animal","gamble","luck"]},"unicorn":{"a":"Unicorn","b":"1F984","j":["face","animal","nature","mystical"]},"zebra":{"a":"Zebra","b":"1F993","j":["stripe","animal","nature","stripes","safari"]},"deer":{"a":"Deer","b":"1F98C","j":["animal","nature","horns","venison"]},"bison":{"a":"Bison","b":"1F9AC","j":["buffalo","herd","wisent","ox"]},"cow-face":{"a":"Cow Face","b":"1F42E","j":["cow","face","beef","ox","animal","nature","moo","milk"]},"ox":{"a":"Ox","b":"1F402","j":["bull","Taurus","zodiac","animal","cow","beef"]},"water-buffalo":{"a":"Water Buffalo","b":"1F403","j":["buffalo","water","animal","nature","ox","cow"]},"cow":{"a":"Cow","b":"1F404","j":["beef","ox","animal","nature","moo","milk"]},"pig-face":{"a":"Pig Face","b":"1F437","j":["face","pig","animal","oink","nature"]},"pig":{"a":"Pig","b":"1F416","j":["sow","animal","nature"]},"boar":{"a":"Boar","b":"1F417","j":["pig","animal","nature"]},"pig-nose":{"a":"Pig Nose","b":"1F43D","j":["face","nose","pig","animal","oink"]},"ram":{"a":"Ram","b":"1F40F","j":["Aries","male","sheep","zodiac","animal","nature"]},"ewe":{"a":"Ewe","b":"1F411","j":["female","sheep","animal","nature","wool","shipit"]},"goat":{"a":"Goat","b":"1F410","j":["Capricorn","zodiac","animal","nature"]},"camel":{"a":"Camel","b":"1F42A","j":["dromedary","hump","animal","hot","desert"]},"twohump-camel":{"a":"Two-Hump Camel","b":"1F42B","j":["bactrian","camel","hump","two-hump camel","two_hump_camel","animal","nature","hot","desert"]},"llama":{"a":"Llama","b":"1F999","j":["alpaca","guanaco","vicuña","wool","animal","nature"]},"giraffe":{"a":"Giraffe","b":"1F992","j":["spots","animal","nature","safari"]},"elephant":{"a":"Elephant","b":"1F418","j":["animal","nature","nose","th","circus"]},"mammoth":{"a":"Mammoth","b":"1F9A3","j":["extinction","large","tusk","woolly","elephant","tusks"]},"rhinoceros":{"a":"Rhinoceros","b":"1F98F","j":["animal","nature","horn"]},"hippopotamus":{"a":"Hippopotamus","b":"1F99B","j":["hippo","animal","nature"]},"mouse-face":{"a":"Mouse Face","b":"1F42D","j":["face","mouse","animal","nature","cheese_wedge","rodent"]},"mouse":{"a":"Mouse","b":"1F401","j":["animal","nature","rodent"]},"rat":{"a":"Rat","b":"1F400","j":["animal","mouse","rodent"]},"hamster":{"a":"Hamster","b":"1F439","j":["face","pet","animal","nature"]},"rabbit-face":{"a":"Rabbit Face","b":"1F430","j":["bunny","face","pet","rabbit","animal","nature","spring","magic"]},"rabbit":{"a":"Rabbit","b":"1F407","j":["bunny","pet","animal","nature","magic","spring"]},"chipmunk":{"a":"Chipmunk","b":"1F43F","j":["squirrel","animal","nature","rodent"]},"beaver":{"a":"Beaver","b":"1F9AB","j":["dam","animal","rodent"]},"hedgehog":{"a":"Hedgehog","b":"1F994","j":["spiny","animal","nature"]},"bat":{"a":"Bat","b":"1F987","j":["vampire","animal","nature","blind"]},"bear":{"a":"Bear","b":"1F43B","j":["face","animal","nature","wild"]},"polar-bear":{"a":"Polar Bear","b":"1F43B-200D-2744-FE0F","j":["arctic","bear","white","animal"]},"koala":{"a":"Koala","b":"1F428","j":["bear","animal","nature"]},"panda":{"a":"Panda","b":"1F43C","j":["face","animal","nature"]},"sloth":{"a":"Sloth","b":"1F9A5","j":["lazy","slow","animal"]},"otter":{"a":"Otter","b":"1F9A6","j":["fishing","playful","animal"]},"skunk":{"a":"Skunk","b":"1F9A8","j":["stink","animal"]},"kangaroo":{"a":"Kangaroo","b":"1F998","j":["Australia","joey","jump","marsupial","animal","nature","australia","hop"]},"badger":{"a":"Badger","b":"1F9A1","j":["honey badger","pester","animal","nature","honey"]},"paw-prints":{"a":"Paw Prints","b":"1F43E","j":["feet","paw","print","animal","tracking","footprints","dog","cat","pet"]},"turkey":{"a":"Turkey","b":"1F983","j":["bird","animal"]},"chicken":{"a":"Chicken","b":"1F414","j":["bird","animal","cluck","nature"]},"rooster":{"a":"Rooster","b":"1F413","j":["bird","animal","nature","chicken"]},"hatching-chick":{"a":"Hatching Chick","b":"1F423","j":["baby","bird","chick","hatching","animal","chicken","egg","born"]},"baby-chick":{"a":"Baby Chick","b":"1F424","j":["baby","bird","chick","animal","chicken"]},"frontfacing-baby-chick":{"a":"Front-Facing Baby Chick","b":"1F425","j":["baby","bird","chick","front-facing baby chick","front_facing_baby_chick","animal","chicken"]},"bird":{"a":"Bird","b":"1F426","j":["animal","nature","fly","tweet","spring"]},"penguin":{"a":"Penguin","b":"1F427","j":["bird","animal","nature"]},"dove":{"a":"Dove","b":"1F54A","j":["bird","fly","peace","animal"]},"eagle":{"a":"Eagle","b":"1F985","j":["bird","animal","nature"]},"duck":{"a":"Duck","b":"1F986","j":["bird","animal","nature","mallard"]},"swan":{"a":"Swan","b":"1F9A2","j":["bird","cygnet","ugly duckling","animal","nature"]},"owl":{"a":"Owl","b":"1F989","j":["bird","wise","animal","nature","hoot"]},"dodo":{"a":"Dodo","b":"1F9A4","j":["extinction","large","Mauritius","animal","bird"]},"feather":{"a":"Feather","b":"1FAB6","j":["bird","flight","light","plumage","fly"]},"flamingo":{"a":"Flamingo","b":"1F9A9","j":["flamboyant","tropical","animal"]},"peacock":{"a":"Peacock","b":"1F99A","j":["bird","ostentatious","peahen","proud","animal","nature"]},"parrot":{"a":"Parrot","b":"1F99C","j":["bird","pirate","talk","animal","nature"]},"frog":{"a":"Frog","b":"1F438","j":["face","animal","nature","croak","toad"]},"crocodile":{"a":"Crocodile","b":"1F40A","j":["animal","nature","reptile","lizard","alligator"]},"turtle":{"a":"Turtle","b":"1F422","j":["terrapin","tortoise","animal","slow","nature"]},"lizard":{"a":"Lizard","b":"1F98E","j":["reptile","animal","nature"]},"snake":{"a":"Snake","b":"1F40D","j":["bearer","Ophiuchus","serpent","zodiac","animal","evil","nature","hiss","python"]},"dragon-face":{"a":"Dragon Face","b":"1F432","j":["dragon","face","fairy tale","animal","myth","nature","chinese","green"]},"dragon":{"a":"Dragon","b":"1F409","j":["fairy tale","animal","myth","nature","chinese","green"]},"sauropod":{"a":"Sauropod","b":"1F995","j":["brachiosaurus","brontosaurus","diplodocus","animal","nature","dinosaur","extinct"]},"trex":{"a":"T-Rex","b":"1F996","j":["Tyrannosaurus Rex","t_rex","animal","nature","dinosaur","tyrannosaurus","extinct"]},"spouting-whale":{"a":"Spouting Whale","b":"1F433","j":["face","spouting","whale","animal","nature","sea","ocean"]},"whale":{"a":"Whale","b":"1F40B","j":["animal","nature","sea","ocean"]},"dolphin":{"a":"Dolphin","b":"1F42C","j":["flipper","animal","nature","fish","sea","ocean","fins","beach"]},"seal":{"a":"Seal","b":"1F9AD","j":["sea lion","animal","creature","sea"]},"fish":{"a":"Fish","b":"1F41F","j":["Pisces","zodiac","animal","food","nature"]},"tropical-fish":{"a":"Tropical Fish","b":"1F420","j":["fish","tropical","animal","swim","ocean","beach","nemo"]},"blowfish":{"a":"Blowfish","b":"1F421","j":["fish","animal","nature","food","sea","ocean"]},"shark":{"a":"Shark","b":"1F988","j":["fish","animal","nature","sea","ocean","jaws","fins","beach"]},"octopus":{"a":"Octopus","b":"1F419","j":["animal","creature","ocean","sea","nature","beach"]},"spiral-shell":{"a":"Spiral Shell","b":"1F41A","j":["shell","spiral","nature","sea","beach"]},"snail":{"a":"Snail","b":"1F40C","j":["slow","animal","shell"]},"butterfly":{"a":"Butterfly","b":"1F98B","j":["insect","pretty","animal","nature","caterpillar"]},"bug":{"a":"Bug","b":"1F41B","j":["insect","animal","nature","worm"]},"ant":{"a":"Ant","b":"1F41C","j":["insect","animal","nature","bug"]},"honeybee":{"a":"Honeybee","b":"1F41D","j":["bee","insect","animal","nature","bug","spring","honey"]},"beetle":{"a":"Beetle","b":"1FAB2","j":["bug","insect"]},"lady-beetle":{"a":"Lady Beetle","b":"1F41E","j":["beetle","insect","ladybird","ladybug","animal","nature"]},"cricket":{"a":"Cricket","b":"1F997","j":["grasshopper","Orthoptera","animal","chirp"]},"cockroach":{"a":"Cockroach","b":"1FAB3","j":["insect","pest","roach","pests"]},"spider":{"a":"Spider","b":"1F577","j":["insect","animal","arachnid"]},"spider-web":{"a":"Spider Web","b":"1F578","j":["spider","web","animal","insect","arachnid","silk"]},"scorpion":{"a":"Scorpion","b":"1F982","j":["scorpio","Scorpio","zodiac","animal","arachnid"]},"mosquito":{"a":"Mosquito","b":"1F99F","j":["disease","fever","malaria","pest","virus","animal","nature","insect"]},"fly":{"a":"Fly","b":"1FAB0","j":["disease","maggot","pest","rotting","insect"]},"worm":{"a":"Worm","b":"1FAB1","j":["annelid","earthworm","parasite","animal"]},"microbe":{"a":"Microbe","b":"1F9A0","j":["amoeba","bacteria","virus","germs"]},"bouquet":{"a":"Bouquet","b":"1F490","j":["flower","flowers","nature","spring"]},"cherry-blossom":{"a":"Cherry Blossom","b":"1F338","j":["blossom","cherry","flower","nature","plant","spring"]},"white-flower":{"a":"White Flower","b":"1F4AE","j":["flower","japanese","spring"]},"rosette":{"a":"Rosette","b":"1F3F5","j":["plant","flower","decoration","military"]},"rose":{"a":"Rose","b":"1F339","j":["flower","flowers","valentines","love","spring"]},"wilted-flower":{"a":"Wilted Flower","b":"1F940","j":["flower","wilted","plant","nature"]},"hibiscus":{"a":"Hibiscus","b":"1F33A","j":["flower","plant","vegetable","flowers","beach"]},"sunflower":{"a":"Sunflower","b":"1F33B","j":["flower","sun","nature","plant","fall"]},"blossom":{"a":"Blossom","b":"1F33C","j":["flower","nature","flowers","yellow"]},"tulip":{"a":"Tulip","b":"1F337","j":["flower","flowers","plant","nature","summer","spring"]},"seedling":{"a":"Seedling","b":"1F331","j":["young","plant","nature","grass","lawn","spring"]},"potted-plant":{"a":"Potted Plant","b":"1FAB4","j":["boring","grow","house","nurturing","plant","useless","greenery"]},"evergreen-tree":{"a":"Evergreen Tree","b":"1F332","j":["tree","plant","nature"]},"deciduous-tree":{"a":"Deciduous Tree","b":"1F333","j":["deciduous","shedding","tree","plant","nature"]},"palm-tree":{"a":"Palm Tree","b":"1F334","j":["palm","tree","plant","vegetable","nature","summer","beach","mojito","tropical"]},"cactus":{"a":"Cactus","b":"1F335","j":["plant","vegetable","nature"]},"sheaf-of-rice":{"a":"Sheaf of Rice","b":"1F33E","j":["ear","grain","rice","nature","plant"]},"herb":{"a":"Herb","b":"1F33F","j":["leaf","vegetable","plant","medicine","weed","grass","lawn"]},"shamrock":{"a":"Shamrock","b":"2618","j":["plant","vegetable","nature","irish","clover"]},"four-leaf-clover":{"a":"Four Leaf Clover","b":"1F340","j":["4","clover","four","four-leaf clover","leaf","vegetable","plant","nature","lucky","irish"]},"maple-leaf":{"a":"Maple Leaf","b":"1F341","j":["falling","leaf","maple","nature","plant","vegetable","ca","fall"]},"fallen-leaf":{"a":"Fallen Leaf","b":"1F342","j":["falling","leaf","nature","plant","vegetable","leaves"]},"leaf-fluttering-in-wind":{"a":"Leaf Fluttering in Wind","b":"1F343","j":["blow","flutter","leaf","wind","nature","plant","tree","vegetable","grass","lawn","spring"]},"grapes":{"a":"Grapes","b":"1F347","j":["fruit","grape","food","wine"]},"melon":{"a":"Melon","b":"1F348","j":["fruit","nature","food"]},"watermelon":{"a":"Watermelon","b":"1F349","j":["fruit","food","picnic","summer"]},"tangerine":{"a":"Tangerine","b":"1F34A","j":["fruit","orange","food","nature"]},"lemon":{"a":"Lemon","b":"1F34B","j":["citrus","fruit","nature"]},"banana":{"a":"Banana","b":"1F34C","j":["fruit","food","monkey"]},"pineapple":{"a":"Pineapple","b":"1F34D","j":["fruit","nature","food"]},"mango":{"a":"Mango","b":"1F96D","j":["fruit","tropical","food"]},"red-apple":{"a":"Red Apple","b":"1F34E","j":["apple","fruit","red","mac","school"]},"green-apple":{"a":"Green Apple","b":"1F34F","j":["apple","fruit","green","nature"]},"pear":{"a":"Pear","b":"1F350","j":["fruit","nature","food"]},"peach":{"a":"Peach","b":"1F351","j":["fruit","nature","food"]},"cherries":{"a":"Cherries","b":"1F352","j":["berries","cherry","fruit","red","food"]},"strawberry":{"a":"Strawberry","b":"1F353","j":["berry","fruit","food","nature"]},"blueberries":{"a":"Blueberries","b":"1FAD0","j":["berry","bilberry","blue","blueberry","fruit"]},"kiwi-fruit":{"a":"Kiwi Fruit","b":"1F95D","j":["food","fruit","kiwi"]},"tomato":{"a":"Tomato","b":"1F345","j":["fruit","vegetable","nature","food"]},"olive":{"a":"Olive","b":"1FAD2","j":["food","fruit"]},"coconut":{"a":"Coconut","b":"1F965","j":["palm","piña colada","fruit","nature","food"]},"avocado":{"a":"Avocado","b":"1F951","j":["food","fruit"]},"eggplant":{"a":"Eggplant","b":"1F346","j":["aubergine","vegetable","nature","food"]},"potato":{"a":"Potato","b":"1F954","j":["food","vegetable","tuber","vegatable","starch"]},"carrot":{"a":"Carrot","b":"1F955","j":["food","vegetable","orange"]},"ear-of-corn":{"a":"Ear of Corn","b":"1F33D","j":["corn","ear","maize","maze","food","vegetable","plant"]},"hot-pepper":{"a":"Hot Pepper","b":"1F336","j":["hot","pepper","food","spicy","chilli","chili"]},"bell-pepper":{"a":"Bell Pepper","b":"1FAD1","j":["capsicum","pepper","vegetable","fruit","plant"]},"cucumber":{"a":"Cucumber","b":"1F952","j":["food","pickle","vegetable","fruit"]},"leafy-green":{"a":"Leafy Green","b":"1F96C","j":["bok choy","cabbage","kale","lettuce","food","vegetable","plant"]},"broccoli":{"a":"Broccoli","b":"1F966","j":["wild cabbage","fruit","food","vegetable"]},"garlic":{"a":"Garlic","b":"1F9C4","j":["flavoring","food","spice","cook"]},"onion":{"a":"Onion","b":"1F9C5","j":["flavoring","cook","food","spice"]},"mushroom":{"a":"Mushroom","b":"1F344","j":["toadstool","plant","vegetable"]},"peanuts":{"a":"Peanuts","b":"1F95C","j":["food","nut","peanut","vegetable"]},"chestnut":{"a":"Chestnut","b":"1F330","j":["plant","food","squirrel"]},"bread":{"a":"Bread","b":"1F35E","j":["loaf","food","wheat","breakfast","toast"]},"croissant":{"a":"Croissant","b":"1F950","j":["bread","breakfast","food","french","roll"]},"baguette-bread":{"a":"Baguette Bread","b":"1F956","j":["baguette","bread","food","french"]},"flatbread":{"a":"Flatbread","b":"1FAD3","j":["arepa","lavash","naan","pita","flour","food"]},"pretzel":{"a":"Pretzel","b":"1F968","j":["twisted","convoluted","food","bread"]},"bagel":{"a":"Bagel","b":"1F96F","j":["bakery","breakfast","schmear","food","bread"]},"pancakes":{"a":"Pancakes","b":"1F95E","j":["breakfast","crêpe","food","hotcake","pancake","flapjacks","hotcakes"]},"waffle":{"a":"Waffle","b":"1F9C7","j":["breakfast","indecisive","iron","food"]},"cheese-wedge":{"a":"Cheese Wedge","b":"1F9C0","j":["cheese","food","chadder"]},"meat-on-bone":{"a":"Meat on Bone","b":"1F356","j":["bone","meat","good","food","drumstick"]},"poultry-leg":{"a":"Poultry Leg","b":"1F357","j":["bone","chicken","drumstick","leg","poultry","food","meat","bird","turkey"]},"cut-of-meat":{"a":"Cut of Meat","b":"1F969","j":["chop","lambchop","porkchop","steak","food","cow","meat","cut"]},"bacon":{"a":"Bacon","b":"1F953","j":["breakfast","food","meat","pork","pig"]},"hamburger":{"a":"Hamburger","b":"1F354","j":["burger","meat","fast food","beef","cheeseburger","mcdonalds","burger king"]},"french-fries":{"a":"French Fries","b":"1F35F","j":["french","fries","chips","snack","fast food"]},"pizza":{"a":"Pizza","b":"1F355","j":["cheese","slice","food","party"]},"hot-dog":{"a":"Hot Dog","b":"1F32D","j":["frankfurter","hotdog","sausage","food"]},"sandwich":{"a":"Sandwich","b":"1F96A","j":["bread","food","lunch"]},"taco":{"a":"Taco","b":"1F32E","j":["mexican","food"]},"burrito":{"a":"Burrito","b":"1F32F","j":["mexican","wrap","food"]},"tamale":{"a":"Tamale","b":"1FAD4","j":["mexican","wrapped","food","masa"]},"stuffed-flatbread":{"a":"Stuffed Flatbread","b":"1F959","j":["falafel","flatbread","food","gyro","kebab","stuffed"]},"falafel":{"a":"Falafel","b":"1F9C6","j":["chickpea","meatball","food"]},"egg":{"a":"Egg","b":"1F95A","j":["breakfast","food","chicken"]},"cooking":{"a":"Cooking","b":"1F373","j":["breakfast","egg","frying","pan","food","kitchen"]},"shallow-pan-of-food":{"a":"Shallow Pan of Food","b":"1F958","j":["casserole","food","paella","pan","shallow","cooking"]},"pot-of-food":{"a":"Pot of Food","b":"1F372","j":["pot","stew","food","meat","soup"]},"fondue":{"a":"Fondue","b":"1FAD5","j":["cheese","chocolate","melted","pot","Swiss","food"]},"bowl-with-spoon":{"a":"Bowl with Spoon","b":"1F963","j":["breakfast","cereal","congee","oatmeal","porridge","food"]},"green-salad":{"a":"Green Salad","b":"1F957","j":["food","green","salad","healthy","lettuce"]},"popcorn":{"a":"Popcorn","b":"1F37F","j":["food","movie theater","films","snack"]},"butter":{"a":"Butter","b":"1F9C8","j":["dairy","food","cook"]},"salt":{"a":"Salt","b":"1F9C2","j":["condiment","shaker"]},"canned-food":{"a":"Canned Food","b":"1F96B","j":["can","food","soup"]},"bento-box":{"a":"Bento Box","b":"1F371","j":["bento","box","food","japanese"]},"rice-cracker":{"a":"Rice Cracker","b":"1F358","j":["cracker","rice","food","japanese"]},"rice-ball":{"a":"Rice Ball","b":"1F359","j":["ball","Japanese","rice","food","japanese"]},"cooked-rice":{"a":"Cooked Rice","b":"1F35A","j":["cooked","rice","food","china","asian"]},"curry-rice":{"a":"Curry Rice","b":"1F35B","j":["curry","rice","food","spicy","hot","indian"]},"steaming-bowl":{"a":"Steaming Bowl","b":"1F35C","j":["bowl","noodle","ramen","steaming","food","japanese","chopsticks"]},"spaghetti":{"a":"Spaghetti","b":"1F35D","j":["pasta","food","italian","noodle"]},"roasted-sweet-potato":{"a":"Roasted Sweet Potato","b":"1F360","j":["potato","roasted","sweet","food","nature"]},"oden":{"a":"Oden","b":"1F362","j":["kebab","seafood","skewer","stick","food","japanese"]},"sushi":{"a":"Sushi","b":"1F363","j":["food","fish","japanese","rice"]},"fried-shrimp":{"a":"Fried Shrimp","b":"1F364","j":["fried","prawn","shrimp","tempura","food","animal","appetizer","summer"]},"fish-cake-with-swirl":{"a":"Fish Cake with Swirl","b":"1F365","j":["cake","fish","pastry","swirl","food","japan","sea","beach","narutomaki","pink","kamaboko","surimi","ramen"]},"moon-cake":{"a":"Moon Cake","b":"1F96E","j":["autumn","festival","yuèbǐng","food"]},"dango":{"a":"Dango","b":"1F361","j":["dessert","Japanese","skewer","stick","sweet","food","japanese","barbecue","meat"]},"dumpling":{"a":"Dumpling","b":"1F95F","j":["empanada","gyōza","jiaozi","pierogi","potsticker","food"]},"fortune-cookie":{"a":"Fortune Cookie","b":"1F960","j":["prophecy","food"]},"takeout-box":{"a":"Takeout Box","b":"1F961","j":["oyster pail","food","leftovers"]},"crab":{"a":"Crab","b":"1F980","j":["Cancer","zodiac","animal","crustacean"]},"lobster":{"a":"Lobster","b":"1F99E","j":["bisque","claws","seafood","animal","nature"]},"shrimp":{"a":"Shrimp","b":"1F990","j":["food","shellfish","small","animal","ocean","nature","seafood"]},"squid":{"a":"Squid","b":"1F991","j":["food","molusc","animal","nature","ocean","sea"]},"oyster":{"a":"Oyster","b":"1F9AA","j":["diving","pearl","food"]},"soft-ice-cream":{"a":"Soft Ice Cream","b":"1F366","j":["cream","dessert","ice","icecream","soft","sweet","food","hot","summer"]},"shaved-ice":{"a":"Shaved Ice","b":"1F367","j":["dessert","ice","shaved","sweet","hot","summer"]},"ice-cream":{"a":"Ice Cream","b":"1F368","j":["cream","dessert","ice","sweet","food","hot"]},"doughnut":{"a":"Doughnut","b":"1F369","j":["breakfast","dessert","donut","sweet","food","snack"]},"cookie":{"a":"Cookie","b":"1F36A","j":["dessert","sweet","food","snack","oreo","chocolate"]},"birthday-cake":{"a":"Birthday Cake","b":"1F382","j":["birthday","cake","celebration","dessert","pastry","sweet","food"]},"shortcake":{"a":"Shortcake","b":"1F370","j":["cake","dessert","pastry","slice","sweet","food"]},"cupcake":{"a":"Cupcake","b":"1F9C1","j":["bakery","sweet","food","dessert"]},"pie":{"a":"Pie","b":"1F967","j":["filling","pastry","fruit","meat","food","dessert"]},"chocolate-bar":{"a":"Chocolate Bar","b":"1F36B","j":["bar","chocolate","dessert","sweet","food","snack"]},"candy":{"a":"Candy","b":"1F36C","j":["dessert","sweet","snack","lolly"]},"lollipop":{"a":"Lollipop","b":"1F36D","j":["candy","dessert","sweet","food","snack"]},"custard":{"a":"Custard","b":"1F36E","j":["dessert","pudding","sweet","food"]},"honey-pot":{"a":"Honey Pot","b":"1F36F","j":["honey","honeypot","pot","sweet","bees","kitchen"]},"baby-bottle":{"a":"Baby Bottle","b":"1F37C","j":["baby","bottle","drink","milk","food","container"]},"glass-of-milk":{"a":"Glass of Milk","b":"1F95B","j":["drink","glass","milk","beverage","cow"]},"hot-beverage":{"a":"Hot Beverage","b":"2615","j":["beverage","coffee","drink","hot","steaming","tea","caffeine","latte","espresso"]},"teapot":{"a":"Teapot","b":"1FAD6","j":["drink","pot","tea","hot"]},"teacup-without-handle":{"a":"Teacup Without Handle","b":"1F375","j":["beverage","cup","drink","tea","teacup","bowl","breakfast","green","british"]},"sake":{"a":"Sake","b":"1F376","j":["bar","beverage","bottle","cup","drink","wine","drunk","japanese","alcohol","booze"]},"bottle-with-popping-cork":{"a":"Bottle with Popping Cork","b":"1F37E","j":["bar","bottle","cork","drink","popping","wine","celebration"]},"wine-glass":{"a":"Wine Glass","b":"1F377","j":["bar","beverage","drink","glass","wine","drunk","alcohol","booze"]},"cocktail-glass":{"a":"Cocktail Glass","b":"1F378","j":["bar","cocktail","drink","glass","drunk","alcohol","beverage","booze","mojito"]},"tropical-drink":{"a":"Tropical Drink","b":"1F379","j":["bar","drink","tropical","beverage","cocktail","summer","beach","alcohol","booze","mojito"]},"beer-mug":{"a":"Beer Mug","b":"1F37A","j":["bar","beer","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-beer-mugs":{"a":"Clinking Beer Mugs","b":"1F37B","j":["bar","beer","clink","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-glasses":{"a":"Clinking Glasses","b":"1F942","j":["celebrate","clink","drink","glass","beverage","party","alcohol","cheers","wine","champagne","toast"]},"tumbler-glass":{"a":"Tumbler Glass","b":"1F943","j":["glass","liquor","shot","tumbler","whisky","drink","beverage","drunk","alcohol","booze","bourbon","scotch"]},"cup-with-straw":{"a":"Cup with Straw","b":"1F964","j":["juice","soda","malt","soft drink","water","drink"]},"bubble-tea":{"a":"Bubble Tea","b":"1F9CB","j":["bubble","milk","pearl","tea","taiwan","boba","milk tea","straw"]},"beverage-box":{"a":"Beverage Box","b":"1F9C3","j":["beverage","box","juice","straw","sweet","drink"]},"mate":{"a":"Mate","b":"1F9C9","j":["drink","tea","beverage"]},"ice":{"a":"Ice","b":"1F9CA","j":["cold","ice cube","iceberg","water"]},"chopsticks":{"a":"Chopsticks","b":"1F962","j":["hashi","jeotgarak","kuaizi","food"]},"fork-and-knife-with-plate":{"a":"Fork and Knife with Plate","b":"1F37D","j":["cooking","fork","knife","plate","food","eat","meal","lunch","dinner","restaurant"]},"fork-and-knife":{"a":"Fork and Knife","b":"1F374","j":["cooking","cutlery","fork","knife","kitchen"]},"spoon":{"a":"Spoon","b":"1F944","j":["tableware","cutlery","kitchen"]},"kitchen-knife":{"a":"Kitchen Knife","b":"1F52A","j":["cooking","hocho","knife","tool","weapon","blade","cutlery","kitchen"]},"amphora":{"a":"Amphora","b":"1F3FA","j":["Aquarius","cooking","drink","jug","zodiac","vase","jar"]},"globe-showing-europeafrica":{"a":"Globe Showing Europe-Africa","b":"1F30D","j":["Africa","earth","Europe","globe","globe showing Europe-Africa","world","globe_showing_europe_africa","international"]},"globe-showing-americas":{"a":"Globe Showing Americas","b":"1F30E","j":["Americas","earth","globe","globe showing Americas","world","USA","international"]},"globe-showing-asiaaustralia":{"a":"Globe Showing Asia-Australia","b":"1F30F","j":["Asia","Australia","earth","globe","globe showing Asia-Australia","world","globe_showing_asia_australia","east","international"]},"globe-with-meridians":{"a":"Globe with Meridians","b":"1F310","j":["earth","globe","meridians","world","international","internet","interweb","i18n"]},"world-map":{"a":"World Map","b":"1F5FA","j":["map","world","location","direction"]},"map-of-japan":{"a":"Map of Japan","b":"1F5FE","j":["Japan","map","map of Japan","nation","country","japanese","asia"]},"compass":{"a":"Compass","b":"1F9ED","j":["magnetic","navigation","orienteering"]},"snowcapped-mountain":{"a":"Snow-Capped Mountain","b":"1F3D4","j":["cold","mountain","snow","snow-capped mountain","snow_capped_mountain","photo","nature","environment","winter"]},"mountain":{"a":"Mountain","b":"26F0","j":["photo","nature","environment"]},"volcano":{"a":"Volcano","b":"1F30B","j":["eruption","mountain","photo","nature","disaster"]},"mount-fuji":{"a":"Mount Fuji","b":"1F5FB","j":["fuji","mountain","photo","nature","japanese"]},"camping":{"a":"Camping","b":"1F3D5","j":["photo","outdoors","tent"]},"beach-with-umbrella":{"a":"Beach with Umbrella","b":"1F3D6","j":["beach","umbrella","weather","summer","sunny","sand","mojito"]},"desert":{"a":"Desert","b":"1F3DC","j":["photo","warm","saharah"]},"desert-island":{"a":"Desert Island","b":"1F3DD","j":["desert","island","photo","tropical","mojito"]},"national-park":{"a":"National Park","b":"1F3DE","j":["park","photo","environment","nature"]},"stadium":{"a":"Stadium","b":"1F3DF","j":["photo","place","sports","concert","venue"]},"classical-building":{"a":"Classical Building","b":"1F3DB","j":["classical","art","culture","history"]},"building-construction":{"a":"Building Construction","b":"1F3D7","j":["construction","wip","working","progress"]},"brick":{"a":"Brick","b":"1F9F1","j":["bricks","clay","mortar","wall"]},"rock":{"a":"Rock","b":"1FAA8","j":["boulder","heavy","solid","stone"]},"wood":{"a":"Wood","b":"1FAB5","j":["log","lumber","timber","nature","trunk"]},"hut":{"a":"Hut","b":"1F6D6","j":["house","roundhouse","yurt","structure"]},"houses":{"a":"Houses","b":"1F3D8","j":["buildings","photo"]},"derelict-house":{"a":"Derelict House","b":"1F3DA","j":["derelict","house","abandon","evict","broken","building"]},"house":{"a":"House","b":"1F3E0","j":["home","building"]},"house-with-garden":{"a":"House with Garden","b":"1F3E1","j":["garden","home","house","plant","nature"]},"office-building":{"a":"Office Building","b":"1F3E2","j":["building","bureau","work"]},"japanese-post-office":{"a":"Japanese Post Office","b":"1F3E3","j":["Japanese","Japanese post office","post","building","envelope","communication"]},"post-office":{"a":"Post Office","b":"1F3E4","j":["European","post","building","email"]},"hospital":{"a":"Hospital","b":"1F3E5","j":["doctor","medicine","building","health","surgery"]},"bank":{"a":"Bank","b":"1F3E6","j":["building","money","sales","cash","business","enterprise"]},"hotel":{"a":"Hotel","b":"1F3E8","j":["building","accomodation","checkin"]},"love-hotel":{"a":"Love Hotel","b":"1F3E9","j":["hotel","love","like","affection","dating"]},"convenience-store":{"a":"Convenience Store","b":"1F3EA","j":["convenience","store","building","shopping","groceries"]},"school":{"a":"School","b":"1F3EB","j":["building","student","education","learn","teach"]},"department-store":{"a":"Department Store","b":"1F3EC","j":["department","store","building","shopping","mall"]},"factory":{"a":"Factory","b":"1F3ED","j":["building","industry","pollution","smoke"]},"japanese-castle":{"a":"Japanese Castle","b":"1F3EF","j":["castle","Japanese","photo","building"]},"castle":{"a":"Castle","b":"1F3F0","j":["European","building","royalty","history"]},"wedding":{"a":"Wedding","b":"1F492","j":["chapel","romance","love","like","affection","couple","marriage","bride","groom"]},"tokyo-tower":{"a":"Tokyo Tower","b":"1F5FC","j":["Tokyo","tower","photo","japanese"]},"statue-of-liberty":{"a":"Statue of Liberty","b":"1F5FD","j":["liberty","statue","american","newyork"]},"church":{"a":"Church","b":"26EA","j":["Christian","cross","religion","building","christ"]},"mosque":{"a":"Mosque","b":"1F54C","j":["islam","Muslim","religion","worship","minaret"]},"hindu-temple":{"a":"Hindu Temple","b":"1F6D5","j":["hindu","temple","religion"]},"synagogue":{"a":"Synagogue","b":"1F54D","j":["Jew","Jewish","religion","temple","judaism","worship","jewish"]},"shinto-shrine":{"a":"Shinto Shrine","b":"26E9","j":["religion","shinto","shrine","temple","japan","kyoto"]},"kaaba":{"a":"Kaaba","b":"1F54B","j":["islam","Muslim","religion","mecca","mosque"]},"fountain":{"a":"Fountain","b":"26F2","j":["photo","summer","water","fresh"]},"tent":{"a":"Tent","b":"26FA","j":["camping","photo","outdoors"]},"foggy":{"a":"Foggy","b":"1F301","j":["fog","photo","mountain"]},"night-with-stars":{"a":"Night with Stars","b":"1F303","j":["night","star","evening","city","downtown"]},"cityscape":{"a":"Cityscape","b":"1F3D9","j":["city","photo","night life","urban"]},"sunrise-over-mountains":{"a":"Sunrise over Mountains","b":"1F304","j":["morning","mountain","sun","sunrise","view","vacation","photo"]},"sunrise":{"a":"Sunrise","b":"1F305","j":["morning","sun","view","vacation","photo"]},"cityscape-at-dusk":{"a":"Cityscape at Dusk","b":"1F306","j":["city","dusk","evening","landscape","sunset","photo","sky","buildings"]},"sunset":{"a":"Sunset","b":"1F307","j":["dusk","sun","photo","good morning","dawn"]},"bridge-at-night":{"a":"Bridge at Night","b":"1F309","j":["bridge","night","photo","sanfrancisco"]},"hot-springs":{"a":"Hot Springs","b":"2668","j":["hot","hotsprings","springs","steaming","bath","warm","relax"]},"carousel-horse":{"a":"Carousel Horse","b":"1F3A0","j":["carousel","horse","photo","carnival"]},"ferris-wheel":{"a":"Ferris Wheel","b":"1F3A1","j":["amusement park","ferris","wheel","photo","carnival","londoneye"]},"roller-coaster":{"a":"Roller Coaster","b":"1F3A2","j":["amusement park","coaster","roller","carnival","playground","photo","fun"]},"barber-pole":{"a":"Barber Pole","b":"1F488","j":["barber","haircut","pole","hair","salon","style"]},"circus-tent":{"a":"Circus Tent","b":"1F3AA","j":["circus","tent","festival","carnival","party"]},"locomotive":{"a":"Locomotive","b":"1F682","j":["engine","railway","steam","train","transportation","vehicle"]},"railway-car":{"a":"Railway Car","b":"1F683","j":["car","electric","railway","train","tram","trolleybus","transportation","vehicle"]},"highspeed-train":{"a":"High-Speed Train","b":"1F684","j":["high-speed train","railway","shinkansen","speed","train","high_speed_train","transportation","vehicle"]},"bullet-train":{"a":"Bullet Train","b":"1F685","j":["bullet","railway","shinkansen","speed","train","transportation","vehicle","fast","public","travel"]},"train":{"a":"Train","b":"1F686","j":["railway","transportation","vehicle"]},"metro":{"a":"Metro","b":"1F687","j":["subway","transportation","blue-square","mrt","underground","tube"]},"light-rail":{"a":"Light Rail","b":"1F688","j":["railway","transportation","vehicle"]},"station":{"a":"Station","b":"1F689","j":["railway","train","transportation","vehicle","public"]},"tram":{"a":"Tram","b":"1F68A","j":["trolleybus","transportation","vehicle"]},"monorail":{"a":"Monorail","b":"1F69D","j":["vehicle","transportation"]},"mountain-railway":{"a":"Mountain Railway","b":"1F69E","j":["car","mountain","railway","transportation","vehicle"]},"tram-car":{"a":"Tram Car","b":"1F68B","j":["car","tram","trolleybus","transportation","vehicle","carriage","public","travel"]},"bus":{"a":"Bus","b":"1F68C","j":["vehicle","car","transportation"]},"oncoming-bus":{"a":"Oncoming Bus","b":"1F68D","j":["bus","oncoming","vehicle","transportation"]},"trolleybus":{"a":"Trolleybus","b":"1F68E","j":["bus","tram","trolley","bart","transportation","vehicle"]},"minibus":{"a":"Minibus","b":"1F690","j":["bus","vehicle","car","transportation"]},"ambulance":{"a":"Ambulance","b":"1F691","j":["vehicle","health","911","hospital"]},"fire-engine":{"a":"Fire Engine","b":"1F692","j":["engine","fire","truck","transportation","cars","vehicle"]},"police-car":{"a":"Police Car","b":"1F693","j":["car","patrol","police","vehicle","cars","transportation","law","legal","enforcement"]},"oncoming-police-car":{"a":"Oncoming Police Car","b":"1F694","j":["car","oncoming","police","vehicle","law","legal","enforcement","911"]},"taxi":{"a":"Taxi","b":"1F695","j":["vehicle","uber","cars","transportation"]},"oncoming-taxi":{"a":"Oncoming Taxi","b":"1F696","j":["oncoming","taxi","vehicle","cars","uber"]},"automobile":{"a":"Automobile","b":"1F697","j":["car","red","transportation","vehicle"]},"oncoming-automobile":{"a":"Oncoming Automobile","b":"1F698","j":["automobile","car","oncoming","vehicle","transportation"]},"sport-utility-vehicle":{"a":"Sport Utility Vehicle","b":"1F699","j":["recreational","sport utility","transportation","vehicle"]},"pickup-truck":{"a":"Pickup Truck","b":"1F6FB","j":["pick-up","pickup","truck","car","transportation"]},"delivery-truck":{"a":"Delivery Truck","b":"1F69A","j":["delivery","truck","cars","transportation"]},"articulated-lorry":{"a":"Articulated Lorry","b":"1F69B","j":["lorry","semi","truck","vehicle","cars","transportation","express"]},"tractor":{"a":"Tractor","b":"1F69C","j":["vehicle","car","farming","agriculture"]},"racing-car":{"a":"Racing Car","b":"1F3CE","j":["car","racing","sports","race","fast","formula","f1"]},"motorcycle":{"a":"Motorcycle","b":"1F3CD","j":["racing","race","sports","fast"]},"motor-scooter":{"a":"Motor Scooter","b":"1F6F5","j":["motor","scooter","vehicle","vespa","sasha"]},"manual-wheelchair":{"a":"Manual Wheelchair","b":"1F9BD","j":["accessibility"]},"motorized-wheelchair":{"a":"Motorized Wheelchair","b":"1F9BC","j":["accessibility"]},"auto-rickshaw":{"a":"Auto Rickshaw","b":"1F6FA","j":["tuk tuk","move","transportation"]},"bicycle":{"a":"Bicycle","b":"1F6B2","j":["bike","sports","exercise","hipster"]},"kick-scooter":{"a":"Kick Scooter","b":"1F6F4","j":["kick","scooter","vehicle","razor"]},"skateboard":{"a":"Skateboard","b":"1F6F9","j":["board"]},"roller-skate":{"a":"Roller Skate","b":"1F6FC","j":["roller","skate","footwear","sports"]},"bus-stop":{"a":"Bus Stop","b":"1F68F","j":["bus","busstop","stop","transportation","wait"]},"motorway":{"a":"Motorway","b":"1F6E3","j":["highway","road","cupertino","interstate"]},"railway-track":{"a":"Railway Track","b":"1F6E4","j":["railway","train","transportation"]},"oil-drum":{"a":"Oil Drum","b":"1F6E2","j":["drum","oil","barrell"]},"fuel-pump":{"a":"Fuel Pump","b":"26FD","j":["diesel","fuel","fuelpump","gas","pump","station","gas station","petroleum"]},"police-car-light":{"a":"Police Car Light","b":"1F6A8","j":["beacon","car","light","police","revolving","ambulance","911","emergency","alert","error","pinged","law","legal"]},"horizontal-traffic-light":{"a":"Horizontal Traffic Light","b":"1F6A5","j":["light","signal","traffic","transportation"]},"vertical-traffic-light":{"a":"Vertical Traffic Light","b":"1F6A6","j":["light","signal","traffic","transportation","driving"]},"stop-sign":{"a":"Stop Sign","b":"1F6D1","j":["octagonal","sign","stop"]},"construction":{"a":"Construction","b":"1F6A7","j":["barrier","wip","progress","caution","warning"]},"anchor":{"a":"Anchor","b":"2693","j":["ship","tool","ferry","sea","boat"]},"sailboat":{"a":"Sailboat","b":"26F5","j":["boat","resort","sea","yacht","ship","summer","transportation","water","sailing"]},"canoe":{"a":"Canoe","b":"1F6F6","j":["boat","paddle","water","ship"]},"speedboat":{"a":"Speedboat","b":"1F6A4","j":["boat","ship","transportation","vehicle","summer"]},"passenger-ship":{"a":"Passenger Ship","b":"1F6F3","j":["passenger","ship","yacht","cruise","ferry"]},"ferry":{"a":"Ferry","b":"26F4","j":["boat","passenger","ship","yacht"]},"motor-boat":{"a":"Motor Boat","b":"1F6E5","j":["boat","motorboat","ship"]},"ship":{"a":"Ship","b":"1F6A2","j":["boat","passenger","transportation","titanic","deploy"]},"airplane":{"a":"Airplane","b":"2708","j":["aeroplane","vehicle","transportation","flight","fly"]},"small-airplane":{"a":"Small Airplane","b":"1F6E9","j":["aeroplane","airplane","flight","transportation","fly","vehicle"]},"airplane-departure":{"a":"Airplane Departure","b":"1F6EB","j":["aeroplane","airplane","check-in","departure","departures","airport","flight","landing"]},"airplane-arrival":{"a":"Airplane Arrival","b":"1F6EC","j":["aeroplane","airplane","arrivals","arriving","landing","airport","flight","boarding"]},"parachute":{"a":"Parachute","b":"1FA82","j":["hang-glide","parasail","skydive","fly","glide"]},"seat":{"a":"Seat","b":"1F4BA","j":["chair","sit","airplane","transport","bus","flight","fly"]},"helicopter":{"a":"Helicopter","b":"1F681","j":["vehicle","transportation","fly"]},"suspension-railway":{"a":"Suspension Railway","b":"1F69F","j":["railway","suspension","vehicle","transportation"]},"mountain-cableway":{"a":"Mountain Cableway","b":"1F6A0","j":["cable","gondola","mountain","transportation","vehicle","ski"]},"aerial-tramway":{"a":"Aerial Tramway","b":"1F6A1","j":["aerial","cable","car","gondola","tramway","transportation","vehicle","ski"]},"satellite":{"a":"Satellite","b":"1F6F0","j":["space","communication","gps","orbit","spaceflight","NASA","ISS"]},"rocket":{"a":"Rocket","b":"1F680","j":["space","launch","ship","staffmode","NASA","outer space","outer_space","fly"]},"flying-saucer":{"a":"Flying Saucer","b":"1F6F8","j":["UFO","transportation","vehicle","ufo"]},"bellhop-bell":{"a":"Bellhop Bell","b":"1F6CE","j":["bell","bellhop","hotel","service"]},"luggage":{"a":"Luggage","b":"1F9F3","j":["packing","travel"]},"hourglass-done":{"a":"Hourglass Done","b":"231B","j":["sand","timer","time","clock","oldschool","limit","exam","quiz","test"]},"hourglass-not-done":{"a":"Hourglass Not Done","b":"23F3","j":["hourglass","sand","timer","oldschool","time","countdown"]},"watch":{"a":"Watch","b":"231A","j":["clock","time","accessories"]},"alarm-clock":{"a":"Alarm Clock","b":"23F0","j":["alarm","clock","time","wake"]},"stopwatch":{"a":"Stopwatch","b":"23F1","j":["clock","time","deadline"]},"timer-clock":{"a":"Timer Clock","b":"23F2","j":["clock","timer","alarm"]},"mantelpiece-clock":{"a":"Mantelpiece Clock","b":"1F570","j":["clock","time"]},"twelve-oclock":{"a":"Twelve O’Clock","b":"1F55B","j":["00","12","12:00","clock","o’clock","twelve","twelve_o_clock","time","noon","midnight","midday","late","early","schedule"]},"twelvethirty":{"a":"Twelve-Thirty","b":"1F567","j":["12","12:30","clock","thirty","twelve","twelve-thirty","twelve_thirty","time","late","early","schedule"]},"one-oclock":{"a":"One O’Clock","b":"1F550","j":["00","1","1:00","clock","o’clock","one","one_o_clock","time","late","early","schedule"]},"onethirty":{"a":"One-Thirty","b":"1F55C","j":["1","1:30","clock","one","one-thirty","thirty","one_thirty","time","late","early","schedule"]},"two-oclock":{"a":"Two O’Clock","b":"1F551","j":["00","2","2:00","clock","o’clock","two","two_o_clock","time","late","early","schedule"]},"twothirty":{"a":"Two-Thirty","b":"1F55D","j":["2","2:30","clock","thirty","two","two-thirty","two_thirty","time","late","early","schedule"]},"three-oclock":{"a":"Three O’Clock","b":"1F552","j":["00","3","3:00","clock","o’clock","three","three_o_clock","time","late","early","schedule"]},"threethirty":{"a":"Three-Thirty","b":"1F55E","j":["3","3:30","clock","thirty","three","three-thirty","three_thirty","time","late","early","schedule"]},"four-oclock":{"a":"Four O’Clock","b":"1F553","j":["00","4","4:00","clock","four","o’clock","four_o_clock","time","late","early","schedule"]},"fourthirty":{"a":"Four-Thirty","b":"1F55F","j":["4","4:30","clock","four","four-thirty","thirty","four_thirty","time","late","early","schedule"]},"five-oclock":{"a":"Five O’Clock","b":"1F554","j":["00","5","5:00","clock","five","o’clock","five_o_clock","time","late","early","schedule"]},"fivethirty":{"a":"Five-Thirty","b":"1F560","j":["5","5:30","clock","five","five-thirty","thirty","five_thirty","time","late","early","schedule"]},"six-oclock":{"a":"Six O’Clock","b":"1F555","j":["00","6","6:00","clock","o’clock","six","six_o_clock","time","late","early","schedule","dawn","dusk"]},"sixthirty":{"a":"Six-Thirty","b":"1F561","j":["6","6:30","clock","six","six-thirty","thirty","six_thirty","time","late","early","schedule"]},"seven-oclock":{"a":"Seven O’Clock","b":"1F556","j":["00","7","7:00","clock","o’clock","seven","seven_o_clock","time","late","early","schedule"]},"seventhirty":{"a":"Seven-Thirty","b":"1F562","j":["7","7:30","clock","seven","seven-thirty","thirty","seven_thirty","time","late","early","schedule"]},"eight-oclock":{"a":"Eight O’Clock","b":"1F557","j":["00","8","8:00","clock","eight","o’clock","eight_o_clock","time","late","early","schedule"]},"eightthirty":{"a":"Eight-Thirty","b":"1F563","j":["8","8:30","clock","eight","eight-thirty","thirty","eight_thirty","time","late","early","schedule"]},"nine-oclock":{"a":"Nine O’Clock","b":"1F558","j":["00","9","9:00","clock","nine","o’clock","nine_o_clock","time","late","early","schedule"]},"ninethirty":{"a":"Nine-Thirty","b":"1F564","j":["9","9:30","clock","nine","nine-thirty","thirty","nine_thirty","time","late","early","schedule"]},"ten-oclock":{"a":"Ten O’Clock","b":"1F559","j":["00","10","10:00","clock","o’clock","ten","ten_o_clock","time","late","early","schedule"]},"tenthirty":{"a":"Ten-Thirty","b":"1F565","j":["10","10:30","clock","ten","ten-thirty","thirty","ten_thirty","time","late","early","schedule"]},"eleven-oclock":{"a":"Eleven O’Clock","b":"1F55A","j":["00","11","11:00","clock","eleven","o’clock","eleven_o_clock","time","late","early","schedule"]},"eleventhirty":{"a":"Eleven-Thirty","b":"1F566","j":["11","11:30","clock","eleven","eleven-thirty","thirty","eleven_thirty","time","late","early","schedule"]},"new-moon":{"a":"New Moon","b":"1F311","j":["dark","moon","nature","twilight","planet","space","night","evening","sleep"]},"waxing-crescent-moon":{"a":"Waxing Crescent Moon","b":"1F312","j":["crescent","moon","waxing","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon":{"a":"First Quarter Moon","b":"1F313","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waxing-gibbous-moon":{"a":"Waxing Gibbous Moon","b":"1F314","j":["gibbous","moon","waxing","nature","night","sky","gray","twilight","planet","space","evening","sleep"]},"full-moon":{"a":"Full Moon","b":"1F315","j":["full","moon","nature","yellow","twilight","planet","space","night","evening","sleep"]},"waning-gibbous-moon":{"a":"Waning Gibbous Moon","b":"1F316","j":["gibbous","moon","waning","nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"]},"last-quarter-moon":{"a":"Last Quarter Moon","b":"1F317","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waning-crescent-moon":{"a":"Waning Crescent Moon","b":"1F318","j":["crescent","moon","waning","nature","twilight","planet","space","night","evening","sleep"]},"crescent-moon":{"a":"Crescent Moon","b":"1F319","j":["crescent","moon","night","sleep","sky","evening","magic"]},"new-moon-face":{"a":"New Moon Face","b":"1F31A","j":["face","moon","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon-face":{"a":"First Quarter Moon Face","b":"1F31B","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"last-quarter-moon-face":{"a":"Last Quarter Moon Face","b":"1F31C","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"thermometer":{"a":"Thermometer","b":"1F321","j":["weather","temperature","hot","cold"]},"sun":{"a":"Sun","b":"2600","j":["bright","rays","sunny","weather","nature","brightness","summer","beach","spring"]},"full-moon-face":{"a":"Full Moon Face","b":"1F31D","j":["bright","face","full","moon","nature","twilight","planet","space","night","evening","sleep"]},"sun-with-face":{"a":"Sun with Face","b":"1F31E","j":["bright","face","sun","nature","morning","sky"]},"ringed-planet":{"a":"Ringed Planet","b":"1FA90","j":["saturn","saturnine","outerspace"]},"star":{"a":"Star","b":"2B50","j":["night","yellow"]},"glowing-star":{"a":"Glowing Star","b":"1F31F","j":["glittery","glow","shining","sparkle","star","night","awesome","good","magic"]},"shooting-star":{"a":"Shooting Star","b":"1F320","j":["falling","shooting","star","night","photo"]},"milky-way":{"a":"Milky Way","b":"1F30C","j":["space","photo","stars"]},"cloud":{"a":"Cloud","b":"2601","j":["weather","sky"]},"sun-behind-cloud":{"a":"Sun Behind Cloud","b":"26C5","j":["cloud","sun","weather","nature","cloudy","morning","fall","spring"]},"cloud-with-lightning-and-rain":{"a":"Cloud with Lightning and Rain","b":"26C8","j":["cloud","rain","thunder","weather","lightning"]},"sun-behind-small-cloud":{"a":"Sun Behind Small Cloud","b":"1F324","j":["cloud","sun","weather"]},"sun-behind-large-cloud":{"a":"Sun Behind Large Cloud","b":"1F325","j":["cloud","sun","weather"]},"sun-behind-rain-cloud":{"a":"Sun Behind Rain Cloud","b":"1F326","j":["cloud","rain","sun","weather"]},"cloud-with-rain":{"a":"Cloud with Rain","b":"1F327","j":["cloud","rain","weather"]},"cloud-with-snow":{"a":"Cloud with Snow","b":"1F328","j":["cloud","cold","snow","weather"]},"cloud-with-lightning":{"a":"Cloud with Lightning","b":"1F329","j":["cloud","lightning","weather","thunder"]},"tornado":{"a":"Tornado","b":"1F32A","j":["cloud","whirlwind","weather","cyclone","twister"]},"fog":{"a":"Fog","b":"1F32B","j":["cloud","weather"]},"wind-face":{"a":"Wind Face","b":"1F32C","j":["blow","cloud","face","wind","gust","air"]},"cyclone":{"a":"Cyclone","b":"1F300","j":["dizzy","hurricane","twister","typhoon","weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado"]},"rainbow":{"a":"Rainbow","b":"1F308","j":["rain","nature","happy","unicorn_face","photo","sky","spring"]},"closed-umbrella":{"a":"Closed Umbrella","b":"1F302","j":["clothing","rain","umbrella","weather","drizzle"]},"umbrella":{"a":"Umbrella","b":"2602","j":["clothing","rain","weather","spring"]},"umbrella-with-rain-drops":{"a":"Umbrella with Rain Drops","b":"2614","j":["clothing","drop","rain","umbrella","rainy","weather","spring"]},"umbrella-on-ground":{"a":"Umbrella on Ground","b":"26F1","j":["rain","sun","umbrella","weather","summer"]},"high-voltage":{"a":"High Voltage","b":"26A1","j":["danger","electric","lightning","voltage","zap","thunder","weather","lightning bolt","fast"]},"snowflake":{"a":"Snowflake","b":"2744","j":["cold","snow","winter","season","weather","christmas","xmas"]},"snowman":{"a":"Snowman","b":"2603","j":["cold","snow","winter","season","weather","christmas","xmas","frozen"]},"snowman-without-snow":{"a":"Snowman Without Snow","b":"26C4","j":["cold","snow","snowman","winter","season","weather","christmas","xmas","frozen","without_snow"]},"comet":{"a":"Comet","b":"2604","j":["space"]},"fire":{"a":"Fire","b":"1F525","j":["flame","tool","hot","cook"]},"droplet":{"a":"Droplet","b":"1F4A7","j":["cold","comic","drop","sweat","water","drip","faucet","spring"]},"water-wave":{"a":"Water Wave","b":"1F30A","j":["ocean","water","wave","sea","nature","tsunami","disaster"]},"jackolantern":{"a":"Jack-O-Lantern","b":"1F383","j":["celebration","halloween","jack","jack-o-lantern","lantern","jack_o_lantern","light","pumpkin","creepy","fall"]},"christmas-tree":{"a":"Christmas Tree","b":"1F384","j":["celebration","Christmas","tree","festival","vacation","december","xmas"]},"fireworks":{"a":"Fireworks","b":"1F386","j":["celebration","photo","festival","carnival","congratulations"]},"sparkler":{"a":"Sparkler","b":"1F387","j":["celebration","fireworks","sparkle","stars","night","shine"]},"firecracker":{"a":"Firecracker","b":"1F9E8","j":["dynamite","explosive","fireworks","boom","explode","explosion"]},"sparkles":{"a":"Sparkles","b":"2728","j":["*","sparkle","star","stars","shine","shiny","cool","awesome","good","magic"]},"balloon":{"a":"Balloon","b":"1F388","j":["celebration","party","birthday","circus"]},"party-popper":{"a":"Party Popper","b":"1F389","j":["celebration","party","popper","tada","congratulations","birthday","magic","circus"]},"confetti-ball":{"a":"Confetti Ball","b":"1F38A","j":["ball","celebration","confetti","festival","party","birthday","circus"]},"tanabata-tree":{"a":"Tanabata Tree","b":"1F38B","j":["banner","celebration","Japanese","tree","plant","nature","branch","summer"]},"pine-decoration":{"a":"Pine Decoration","b":"1F38D","j":["bamboo","celebration","Japanese","pine","plant","nature","vegetable","panda"]},"japanese-dolls":{"a":"Japanese Dolls","b":"1F38E","j":["celebration","doll","festival","Japanese","Japanese dolls","japanese","toy","kimono"]},"carp-streamer":{"a":"Carp Streamer","b":"1F38F","j":["carp","celebration","streamer","fish","japanese","koinobori","banner"]},"wind-chime":{"a":"Wind Chime","b":"1F390","j":["bell","celebration","chime","wind","nature","ding","spring"]},"moon-viewing-ceremony":{"a":"Moon Viewing Ceremony","b":"1F391","j":["celebration","ceremony","moon","photo","japan","asia","tsukimi"]},"red-envelope":{"a":"Red Envelope","b":"1F9E7","j":["gift","good luck","hóngbāo","lai see","money"]},"ribbon":{"a":"Ribbon","b":"1F380","j":["celebration","decoration","pink","girl","bowtie"]},"wrapped-gift":{"a":"Wrapped Gift","b":"1F381","j":["box","celebration","gift","present","wrapped","birthday","christmas","xmas"]},"reminder-ribbon":{"a":"Reminder Ribbon","b":"1F397","j":["celebration","reminder","ribbon","sports","cause","support","awareness"]},"admission-tickets":{"a":"Admission Tickets","b":"1F39F","j":["admission","ticket","sports","concert","entrance"]},"ticket":{"a":"Ticket","b":"1F3AB","j":["admission","event","concert","pass"]},"military-medal":{"a":"Military Medal","b":"1F396","j":["celebration","medal","military","award","winning","army"]},"trophy":{"a":"Trophy","b":"1F3C6","j":["prize","win","award","contest","place","ftw","ceremony"]},"sports-medal":{"a":"Sports Medal","b":"1F3C5","j":["medal","award","winning"]},"1st-place-medal":{"a":"1st Place Medal","b":"1F947","j":["first","gold","medal","award","winning"]},"2nd-place-medal":{"a":"2nd Place Medal","b":"1F948","j":["medal","second","silver","award"]},"3rd-place-medal":{"a":"3rd Place Medal","b":"1F949","j":["bronze","medal","third","award"]},"soccer-ball":{"a":"Soccer Ball","b":"26BD","j":["ball","football","soccer","sports"]},"baseball":{"a":"Baseball","b":"26BE","j":["ball","sports","balls"]},"softball":{"a":"Softball","b":"1F94E","j":["ball","glove","underarm","sports","balls"]},"basketball":{"a":"Basketball","b":"1F3C0","j":["ball","hoop","sports","balls","NBA"]},"volleyball":{"a":"Volleyball","b":"1F3D0","j":["ball","game","sports","balls"]},"american-football":{"a":"American Football","b":"1F3C8","j":["american","ball","football","sports","balls","NFL"]},"rugby-football":{"a":"Rugby Football","b":"1F3C9","j":["ball","football","rugby","sports","team"]},"tennis":{"a":"Tennis","b":"1F3BE","j":["ball","racquet","sports","balls","green"]},"flying-disc":{"a":"Flying Disc","b":"1F94F","j":["ultimate","sports","frisbee"]},"bowling":{"a":"Bowling","b":"1F3B3","j":["ball","game","sports","fun","play"]},"cricket-game":{"a":"Cricket Game","b":"1F3CF","j":["ball","bat","game","sports"]},"field-hockey":{"a":"Field Hockey","b":"1F3D1","j":["ball","field","game","hockey","stick","sports"]},"ice-hockey":{"a":"Ice Hockey","b":"1F3D2","j":["game","hockey","ice","puck","stick","sports"]},"lacrosse":{"a":"Lacrosse","b":"1F94D","j":["ball","goal","stick","sports"]},"ping-pong":{"a":"Ping Pong","b":"1F3D3","j":["ball","bat","game","paddle","table tennis","sports","pingpong"]},"badminton":{"a":"Badminton","b":"1F3F8","j":["birdie","game","racquet","shuttlecock","sports"]},"boxing-glove":{"a":"Boxing Glove","b":"1F94A","j":["boxing","glove","sports","fighting"]},"martial-arts-uniform":{"a":"Martial Arts Uniform","b":"1F94B","j":["judo","karate","martial arts","taekwondo","uniform"]},"goal-net":{"a":"Goal Net","b":"1F945","j":["goal","net","sports"]},"flag-in-hole":{"a":"Flag in Hole","b":"26F3","j":["golf","hole","sports","business","flag","summer"]},"ice-skate":{"a":"Ice Skate","b":"26F8","j":["ice","skate","sports"]},"fishing-pole":{"a":"Fishing Pole","b":"1F3A3","j":["fish","pole","food","hobby","summer"]},"diving-mask":{"a":"Diving Mask","b":"1F93F","j":["diving","scuba","snorkeling","sport","ocean"]},"running-shirt":{"a":"Running Shirt","b":"1F3BD","j":["athletics","running","sash","shirt","play","pageant"]},"skis":{"a":"Skis","b":"1F3BF","j":["ski","snow","sports","winter","cold"]},"sled":{"a":"Sled","b":"1F6F7","j":["sledge","sleigh","luge","toboggan"]},"curling-stone":{"a":"Curling Stone","b":"1F94C","j":["game","rock","sports"]},"bullseye":{"a":"Bullseye","b":"1F3AF","j":["dart","direct hit","game","hit","target","direct_hit","play","bar"]},"yoyo":{"a":"Yo-Yo","b":"1FA80","j":["fluctuate","toy","yo-yo","yo_yo"]},"kite":{"a":"Kite","b":"1FA81","j":["fly","soar","wind"]},"pool-8-ball":{"a":"Pool 8 Ball","b":"1F3B1","j":["8","ball","billiard","eight","game","pool","hobby","luck","magic"]},"crystal-ball":{"a":"Crystal Ball","b":"1F52E","j":["ball","crystal","fairy tale","fantasy","fortune","tool","disco","party","magic","circus","fortune_teller"]},"magic-wand":{"a":"Magic Wand","b":"1FA84","j":["magic","witch","wizard","supernature","power"]},"nazar-amulet":{"a":"Nazar Amulet","b":"1F9FF","j":["bead","charm","evil-eye","nazar","talisman"]},"video-game":{"a":"Video Game","b":"1F3AE","j":["controller","game","play","console","PS4"]},"joystick":{"a":"Joystick","b":"1F579","j":["game","video game","play"]},"slot-machine":{"a":"Slot Machine","b":"1F3B0","j":["game","slot","bet","gamble","vegas","fruit machine","luck","casino"]},"game-die":{"a":"Game Die","b":"1F3B2","j":["dice","die","game","random","tabletop","play","luck"]},"puzzle-piece":{"a":"Puzzle Piece","b":"1F9E9","j":["clue","interlocking","jigsaw","piece","puzzle"]},"teddy-bear":{"a":"Teddy Bear","b":"1F9F8","j":["plaything","plush","stuffed","toy"]},"piata":{"a":"Piñata","b":"1FA85","j":["celebration","party","piñata","pinata","mexico","candy"]},"nesting-dolls":{"a":"Nesting Dolls","b":"1FA86","j":["doll","nesting","russia","matryoshka","toy"]},"spade-suit":{"a":"Spade Suit","b":"2660","j":["card","game","poker","cards","suits","magic"]},"heart-suit":{"a":"Heart Suit","b":"2665","j":["card","game","poker","cards","magic","suits"]},"diamond-suit":{"a":"Diamond Suit","b":"2666","j":["card","game","poker","cards","magic","suits"]},"club-suit":{"a":"Club Suit","b":"2663","j":["card","game","poker","cards","magic","suits"]},"chess-pawn":{"a":"Chess Pawn","b":"265F","j":["chess","dupe","expendable"]},"joker":{"a":"Joker","b":"1F0CF","j":["card","game","wildcard","poker","cards","play","magic"]},"mahjong-red-dragon":{"a":"Mahjong Red Dragon","b":"1F004","j":["game","mahjong","red","play","chinese","kanji"]},"flower-playing-cards":{"a":"Flower Playing Cards","b":"1F3B4","j":["card","flower","game","Japanese","playing","sunset","red"]},"performing-arts":{"a":"Performing Arts","b":"1F3AD","j":["art","mask","performing","theater","theatre","acting","drama"]},"framed-picture":{"a":"Framed Picture","b":"1F5BC","j":["art","frame","museum","painting","picture","photography"]},"artist-palette":{"a":"Artist Palette","b":"1F3A8","j":["art","museum","painting","palette","design","paint","draw","colors"]},"thread":{"a":"Thread","b":"1F9F5","j":["needle","sewing","spool","string"]},"sewing-needle":{"a":"Sewing Needle","b":"1FAA1","j":["embroidery","needle","sewing","stitches","sutures","tailoring"]},"yarn":{"a":"Yarn","b":"1F9F6","j":["ball","crochet","knit"]},"knot":{"a":"Knot","b":"1FAA2","j":["rope","tangled","tie","twine","twist","scout"]},"glasses":{"a":"Glasses","b":"1F453","j":["clothing","eye","eyeglasses","eyewear","fashion","accessories","eyesight","nerdy","dork","geek"]},"sunglasses":{"a":"Sunglasses","b":"1F576","j":["dark","eye","eyewear","glasses","face","cool","accessories"]},"goggles":{"a":"Goggles","b":"1F97D","j":["eye protection","swimming","welding","eyes","protection","safety"]},"lab-coat":{"a":"Lab Coat","b":"1F97C","j":["doctor","experiment","scientist","chemist"]},"safety-vest":{"a":"Safety Vest","b":"1F9BA","j":["emergency","safety","vest","protection"]},"necktie":{"a":"Necktie","b":"1F454","j":["clothing","tie","shirt","suitup","formal","fashion","cloth","business"]},"tshirt":{"a":"T-Shirt","b":"1F455","j":["clothing","shirt","t-shirt","t_shirt","fashion","cloth","casual","tee"]},"jeans":{"a":"Jeans","b":"1F456","j":["clothing","pants","trousers","fashion","shopping"]},"scarf":{"a":"Scarf","b":"1F9E3","j":["neck","winter","clothes"]},"gloves":{"a":"Gloves","b":"1F9E4","j":["hand","hands","winter","clothes"]},"coat":{"a":"Coat","b":"1F9E5","j":["jacket"]},"socks":{"a":"Socks","b":"1F9E6","j":["stocking","stockings","clothes"]},"dress":{"a":"Dress","b":"1F457","j":["clothing","clothes","fashion","shopping"]},"kimono":{"a":"Kimono","b":"1F458","j":["clothing","dress","fashion","women","female","japanese"]},"sari":{"a":"Sari","b":"1F97B","j":["clothing","dress"]},"onepiece-swimsuit":{"a":"One-Piece Swimsuit","b":"1FA71","j":["bathing suit","one-piece swimsuit","one_piece_swimsuit","fashion"]},"briefs":{"a":"Briefs","b":"1FA72","j":["bathing suit","one-piece","swimsuit","underwear","clothing"]},"shorts":{"a":"Shorts","b":"1FA73","j":["bathing suit","pants","underwear","clothing"]},"bikini":{"a":"Bikini","b":"1F459","j":["clothing","swim","swimming","female","woman","girl","fashion","beach","summer"]},"womans-clothes":{"a":"Woman’S Clothes","b":"1F45A","j":["clothing","woman","woman’s clothes","woman_s_clothes","fashion","shopping_bags","female"]},"purse":{"a":"Purse","b":"1F45B","j":["clothing","coin","fashion","accessories","money","sales","shopping"]},"handbag":{"a":"Handbag","b":"1F45C","j":["bag","clothing","purse","fashion","accessory","accessories","shopping"]},"clutch-bag":{"a":"Clutch Bag","b":"1F45D","j":["bag","clothing","pouch","accessories","shopping"]},"shopping-bags":{"a":"Shopping Bags","b":"1F6CD","j":["bag","hotel","shopping","mall","buy","purchase"]},"backpack":{"a":"Backpack","b":"1F392","j":["bag","rucksack","satchel","school","student","education"]},"thong-sandal":{"a":"Thong Sandal","b":"1FA74","j":["beach sandals","sandals","thong sandals","thongs","zōri","footwear","summer"]},"mans-shoe":{"a":"Man’S Shoe","b":"1F45E","j":["clothing","man","man’s shoe","shoe","man_s_shoe","fashion","male"]},"running-shoe":{"a":"Running Shoe","b":"1F45F","j":["athletic","clothing","shoe","sneaker","shoes","sports","sneakers"]},"hiking-boot":{"a":"Hiking Boot","b":"1F97E","j":["backpacking","boot","camping","hiking"]},"flat-shoe":{"a":"Flat Shoe","b":"1F97F","j":["ballet flat","slip-on","slipper","ballet"]},"highheeled-shoe":{"a":"High-Heeled Shoe","b":"1F460","j":["clothing","heel","high-heeled shoe","shoe","woman","high_heeled_shoe","fashion","shoes","female","pumps","stiletto"]},"womans-sandal":{"a":"Woman’S Sandal","b":"1F461","j":["clothing","sandal","shoe","woman","woman’s sandal","woman_s_sandal","shoes","fashion","flip flops"]},"ballet-shoes":{"a":"Ballet Shoes","b":"1FA70","j":["ballet","dance"]},"womans-boot":{"a":"Woman’S Boot","b":"1F462","j":["boot","clothing","shoe","woman","woman’s boot","woman_s_boot","shoes","fashion"]},"crown":{"a":"Crown","b":"1F451","j":["clothing","king","queen","kod","leader","royalty","lord"]},"womans-hat":{"a":"Woman’S Hat","b":"1F452","j":["clothing","hat","woman","woman’s hat","woman_s_hat","fashion","accessories","female","lady","spring"]},"top-hat":{"a":"Top Hat","b":"1F3A9","j":["clothing","hat","top","tophat","magic","gentleman","classy","circus"]},"graduation-cap":{"a":"Graduation Cap","b":"1F393","j":["cap","celebration","clothing","graduation","hat","school","college","degree","university","legal","learn","education"]},"billed-cap":{"a":"Billed Cap","b":"1F9E2","j":["baseball cap","cap","baseball"]},"military-helmet":{"a":"Military Helmet","b":"1FA96","j":["army","helmet","military","soldier","warrior","protection"]},"rescue-workers-helmet":{"a":"Rescue Worker’S Helmet","b":"26D1","j":["aid","cross","face","hat","helmet","rescue worker’s helmet","rescue_worker_s_helmet","construction","build"]},"prayer-beads":{"a":"Prayer Beads","b":"1F4FF","j":["beads","clothing","necklace","prayer","religion","dhikr","religious"]},"lipstick":{"a":"Lipstick","b":"1F484","j":["cosmetics","makeup","female","girl","fashion","woman"]},"ring":{"a":"Ring","b":"1F48D","j":["diamond","wedding","propose","marriage","valentines","fashion","jewelry","gem","engagement"]},"gem-stone":{"a":"Gem Stone","b":"1F48E","j":["diamond","gem","jewel","blue","ruby","jewelry"]},"muted-speaker":{"a":"Muted Speaker","b":"1F507","j":["mute","quiet","silent","speaker","sound","volume","silence"]},"speaker-low-volume":{"a":"Speaker Low Volume","b":"1F508","j":["soft","sound","volume","silence","broadcast"]},"speaker-medium-volume":{"a":"Speaker Medium Volume","b":"1F509","j":["medium","volume","speaker","broadcast"]},"speaker-high-volume":{"a":"Speaker High Volume","b":"1F50A","j":["loud","volume","noise","noisy","speaker","broadcast"]},"loudspeaker":{"a":"Loudspeaker","b":"1F4E2","j":["loud","public address","volume","sound"]},"megaphone":{"a":"Megaphone","b":"1F4E3","j":["cheering","sound","speaker","volume"]},"postal-horn":{"a":"Postal Horn","b":"1F4EF","j":["horn","post","postal","instrument","music"]},"bell":{"a":"Bell","b":"1F514","j":["sound","notification","christmas","xmas","chime"]},"bell-with-slash":{"a":"Bell with Slash","b":"1F515","j":["bell","forbidden","mute","quiet","silent","sound","volume"]},"musical-score":{"a":"Musical Score","b":"1F3BC","j":["music","score","treble","clef","compose"]},"musical-note":{"a":"Musical Note","b":"1F3B5","j":["music","note","score","tone","sound"]},"musical-notes":{"a":"Musical Notes","b":"1F3B6","j":["music","note","notes","score"]},"studio-microphone":{"a":"Studio Microphone","b":"1F399","j":["mic","microphone","music","studio","sing","recording","artist","talkshow"]},"level-slider":{"a":"Level Slider","b":"1F39A","j":["level","music","slider","scale"]},"control-knobs":{"a":"Control Knobs","b":"1F39B","j":["control","knobs","music","dial"]},"microphone":{"a":"Microphone","b":"1F3A4","j":["karaoke","mic","sound","music","PA","sing","talkshow"]},"headphone":{"a":"Headphone","b":"1F3A7","j":["earbud","music","score","gadgets"]},"radio":{"a":"Radio","b":"1F4FB","j":["video","communication","music","podcast","program"]},"saxophone":{"a":"Saxophone","b":"1F3B7","j":["instrument","music","sax","jazz","blues"]},"accordion":{"a":"Accordion","b":"1FA97","j":["concertina","squeeze box","music"]},"guitar":{"a":"Guitar","b":"1F3B8","j":["instrument","music"]},"musical-keyboard":{"a":"Musical Keyboard","b":"1F3B9","j":["instrument","keyboard","music","piano","compose"]},"trumpet":{"a":"Trumpet","b":"1F3BA","j":["instrument","music","brass"]},"violin":{"a":"Violin","b":"1F3BB","j":["instrument","music","orchestra","symphony"]},"banjo":{"a":"Banjo","b":"1FA95","j":["music","stringed","instructment"]},"drum":{"a":"Drum","b":"1F941","j":["drumsticks","music","instrument","snare"]},"long-drum":{"a":"Long Drum","b":"1FA98","j":["beat","conga","drum","rhythm","music"]},"mobile-phone":{"a":"Mobile Phone","b":"1F4F1","j":["cell","mobile","phone","telephone","technology","apple","gadgets","dial"]},"mobile-phone-with-arrow":{"a":"Mobile Phone with Arrow","b":"1F4F2","j":["arrow","cell","mobile","phone","receive","iphone","incoming"]},"telephone":{"a":"Telephone","b":"260E","j":["phone","technology","communication","dial"]},"telephone-receiver":{"a":"Telephone Receiver","b":"1F4DE","j":["phone","receiver","telephone","technology","communication","dial"]},"pager":{"a":"Pager","b":"1F4DF","j":["bbcall","oldschool","90s"]},"fax-machine":{"a":"Fax Machine","b":"1F4E0","j":["fax","communication","technology"]},"battery":{"a":"Battery","b":"1F50B","j":["power","energy","sustain"]},"electric-plug":{"a":"Electric Plug","b":"1F50C","j":["electric","electricity","plug","charger","power"]},"laptop":{"a":"Laptop","b":"1F4BB","j":["computer","pc","personal","technology","screen","display","monitor"]},"desktop-computer":{"a":"Desktop Computer","b":"1F5A5","j":["computer","desktop","technology","computing","screen"]},"printer":{"a":"Printer","b":"1F5A8","j":["computer","paper","ink"]},"keyboard":{"a":"Keyboard","b":"2328","j":["computer","technology","type","input","text"]},"computer-mouse":{"a":"Computer Mouse","b":"1F5B1","j":["computer","click"]},"trackball":{"a":"Trackball","b":"1F5B2","j":["computer","technology","trackpad"]},"computer-disk":{"a":"Computer Disk","b":"1F4BD","j":["computer","disk","minidisk","optical","technology","record","data","90s"]},"floppy-disk":{"a":"Floppy Disk","b":"1F4BE","j":["computer","disk","floppy","oldschool","technology","save","90s","80s"]},"optical-disk":{"a":"Optical Disk","b":"1F4BF","j":["cd","computer","disk","optical","technology","dvd","disc","90s"]},"dvd":{"a":"Dvd","b":"1F4C0","j":["blu-ray","computer","disk","optical","cd","disc"]},"abacus":{"a":"Abacus","b":"1F9EE","j":["calculation"]},"movie-camera":{"a":"Movie Camera","b":"1F3A5","j":["camera","cinema","movie","film","record"]},"film-frames":{"a":"Film Frames","b":"1F39E","j":["cinema","film","frames","movie"]},"film-projector":{"a":"Film Projector","b":"1F4FD","j":["cinema","film","movie","projector","video","tape","record"]},"clapper-board":{"a":"Clapper Board","b":"1F3AC","j":["clapper","movie","film","record"]},"television":{"a":"Television","b":"1F4FA","j":["tv","video","technology","program","oldschool","show"]},"camera":{"a":"Camera","b":"1F4F7","j":["video","gadgets","photography"]},"camera-with-flash":{"a":"Camera with Flash","b":"1F4F8","j":["camera","flash","video","photography","gadgets"]},"video-camera":{"a":"Video Camera","b":"1F4F9","j":["camera","video","film","record"]},"videocassette":{"a":"Videocassette","b":"1F4FC","j":["tape","vhs","video","record","oldschool","90s","80s"]},"magnifying-glass-tilted-left":{"a":"Magnifying Glass Tilted Left","b":"1F50D","j":["glass","magnifying","search","tool","zoom","find","detective"]},"magnifying-glass-tilted-right":{"a":"Magnifying Glass Tilted Right","b":"1F50E","j":["glass","magnifying","search","tool","zoom","find","detective"]},"candle":{"a":"Candle","b":"1F56F","j":["light","fire","wax"]},"light-bulb":{"a":"Light Bulb","b":"1F4A1","j":["bulb","comic","electric","idea","light","electricity"]},"flashlight":{"a":"Flashlight","b":"1F526","j":["electric","light","tool","torch","dark","camping","sight","night"]},"red-paper-lantern":{"a":"Red Paper Lantern","b":"1F3EE","j":["bar","lantern","light","red","paper","halloween","spooky"]},"diya-lamp":{"a":"Diya Lamp","b":"1FA94","j":["diya","lamp","oil","lighting"]},"notebook-with-decorative-cover":{"a":"Notebook with Decorative Cover","b":"1F4D4","j":["book","cover","decorated","notebook","classroom","notes","record","paper","study"]},"closed-book":{"a":"Closed Book","b":"1F4D5","j":["book","closed","read","library","knowledge","textbook","learn"]},"open-book":{"a":"Open Book","b":"1F4D6","j":["book","open","read","library","knowledge","literature","learn","study"]},"green-book":{"a":"Green Book","b":"1F4D7","j":["book","green","read","library","knowledge","study"]},"blue-book":{"a":"Blue Book","b":"1F4D8","j":["blue","book","read","library","knowledge","learn","study"]},"orange-book":{"a":"Orange Book","b":"1F4D9","j":["book","orange","read","library","knowledge","textbook","study"]},"books":{"a":"Books","b":"1F4DA","j":["book","literature","library","study"]},"notebook":{"a":"Notebook","b":"1F4D3","j":["stationery","record","notes","paper","study"]},"ledger":{"a":"Ledger","b":"1F4D2","j":["notebook","notes","paper"]},"page-with-curl":{"a":"Page with Curl","b":"1F4C3","j":["curl","document","page","documents","office","paper"]},"scroll":{"a":"Scroll","b":"1F4DC","j":["paper","documents","ancient","history"]},"page-facing-up":{"a":"Page Facing Up","b":"1F4C4","j":["document","page","documents","office","paper","information"]},"newspaper":{"a":"Newspaper","b":"1F4F0","j":["news","paper","press","headline"]},"rolledup-newspaper":{"a":"Rolled-Up Newspaper","b":"1F5DE","j":["news","newspaper","paper","rolled","rolled-up newspaper","rolled_up_newspaper","press","headline"]},"bookmark-tabs":{"a":"Bookmark Tabs","b":"1F4D1","j":["bookmark","mark","marker","tabs","favorite","save","order","tidy"]},"bookmark":{"a":"Bookmark","b":"1F516","j":["mark","favorite","label","save"]},"label":{"a":"Label","b":"1F3F7","j":["sale","tag"]},"money-bag":{"a":"Money Bag","b":"1F4B0","j":["bag","dollar","money","moneybag","payment","coins","sale"]},"coin":{"a":"Coin","b":"1FA99","j":["gold","metal","money","silver","treasure","currency"]},"yen-banknote":{"a":"Yen Banknote","b":"1F4B4","j":["banknote","bill","currency","money","note","yen","sales","japanese","dollar"]},"dollar-banknote":{"a":"Dollar Banknote","b":"1F4B5","j":["banknote","bill","currency","dollar","money","note","sales"]},"euro-banknote":{"a":"Euro Banknote","b":"1F4B6","j":["banknote","bill","currency","euro","money","note","sales","dollar"]},"pound-banknote":{"a":"Pound Banknote","b":"1F4B7","j":["banknote","bill","currency","money","note","pound","british","sterling","sales","bills","uk","england"]},"money-with-wings":{"a":"Money with Wings","b":"1F4B8","j":["banknote","bill","fly","money","wings","dollar","bills","payment","sale"]},"credit-card":{"a":"Credit Card","b":"1F4B3","j":["card","credit","money","sales","dollar","bill","payment","shopping"]},"receipt":{"a":"Receipt","b":"1F9FE","j":["accounting","bookkeeping","evidence","proof","expenses"]},"chart-increasing-with-yen":{"a":"Chart Increasing with Yen","b":"1F4B9","j":["chart","graph","growth","money","yen","green-square","presentation","stats"]},"envelope":{"a":"Envelope","b":"2709","j":["email","letter","postal","inbox","communication"]},"email":{"a":"E-Mail","b":"1F4E7","j":["e-mail","letter","mail","e_mail","communication","inbox"]},"incoming-envelope":{"a":"Incoming Envelope","b":"1F4E8","j":["e-mail","email","envelope","incoming","letter","receive","inbox"]},"envelope-with-arrow":{"a":"Envelope with Arrow","b":"1F4E9","j":["arrow","e-mail","email","envelope","outgoing","communication"]},"outbox-tray":{"a":"Outbox Tray","b":"1F4E4","j":["box","letter","mail","outbox","sent","tray","inbox","email"]},"inbox-tray":{"a":"Inbox Tray","b":"1F4E5","j":["box","inbox","letter","mail","receive","tray","email","documents"]},"package":{"a":"Package","b":"1F4E6","j":["box","parcel","mail","gift","cardboard","moving"]},"closed-mailbox-with-raised-flag":{"a":"Closed Mailbox with Raised Flag","b":"1F4EB","j":["closed","mail","mailbox","postbox","email","inbox","communication"]},"closed-mailbox-with-lowered-flag":{"a":"Closed Mailbox with Lowered Flag","b":"1F4EA","j":["closed","lowered","mail","mailbox","postbox","email","communication","inbox"]},"open-mailbox-with-raised-flag":{"a":"Open Mailbox with Raised Flag","b":"1F4EC","j":["mail","mailbox","open","postbox","email","inbox","communication"]},"open-mailbox-with-lowered-flag":{"a":"Open Mailbox with Lowered Flag","b":"1F4ED","j":["lowered","mail","mailbox","open","postbox","email","inbox"]},"postbox":{"a":"Postbox","b":"1F4EE","j":["mail","mailbox","email","letter","envelope"]},"ballot-box-with-ballot":{"a":"Ballot Box with Ballot","b":"1F5F3","j":["ballot","box","election","vote"]},"pencil":{"a":"Pencil","b":"270F","j":["stationery","write","paper","writing","school","study"]},"black-nib":{"a":"Black Nib","b":"2712","j":["nib","pen","stationery","writing","write"]},"fountain-pen":{"a":"Fountain Pen","b":"1F58B","j":["fountain","pen","stationery","writing","write"]},"pen":{"a":"Pen","b":"1F58A","j":["ballpoint","stationery","writing","write"]},"paintbrush":{"a":"Paintbrush","b":"1F58C","j":["painting","drawing","creativity","art"]},"crayon":{"a":"Crayon","b":"1F58D","j":["drawing","creativity"]},"memo":{"a":"Memo","b":"1F4DD","j":["pencil","write","documents","stationery","paper","writing","legal","exam","quiz","test","study","compose"]},"briefcase":{"a":"Briefcase","b":"1F4BC","j":["business","documents","work","law","legal","job","career"]},"file-folder":{"a":"File Folder","b":"1F4C1","j":["file","folder","documents","business","office"]},"open-file-folder":{"a":"Open File Folder","b":"1F4C2","j":["file","folder","open","documents","load"]},"card-index-dividers":{"a":"Card Index Dividers","b":"1F5C2","j":["card","dividers","index","organizing","business","stationery"]},"calendar":{"a":"Calendar","b":"1F4C5","j":["date","schedule"]},"tearoff-calendar":{"a":"Tear-off Calendar","b":"1F4C6","j":["calendar","tear-off calendar","tear_off_calendar","schedule","date","planning"]},"spiral-notepad":{"a":"Spiral Notepad","b":"1F5D2","j":["note","pad","spiral","memo","stationery"]},"spiral-calendar":{"a":"Spiral Calendar","b":"1F5D3","j":["calendar","pad","spiral","date","schedule","planning"]},"card-index":{"a":"Card Index","b":"1F4C7","j":["card","index","rolodex","business","stationery"]},"chart-increasing":{"a":"Chart Increasing","b":"1F4C8","j":["chart","graph","growth","trend","upward","presentation","stats","recovery","business","economics","money","sales","good","success"]},"chart-decreasing":{"a":"Chart Decreasing","b":"1F4C9","j":["chart","down","graph","trend","presentation","stats","recession","business","economics","money","sales","bad","failure"]},"bar-chart":{"a":"Bar Chart","b":"1F4CA","j":["bar","chart","graph","presentation","stats"]},"clipboard":{"a":"Clipboard","b":"1F4CB","j":["stationery","documents"]},"pushpin":{"a":"Pushpin","b":"1F4CC","j":["pin","stationery","mark","here"]},"round-pushpin":{"a":"Round Pushpin","b":"1F4CD","j":["pin","pushpin","stationery","location","map","here"]},"paperclip":{"a":"Paperclip","b":"1F4CE","j":["documents","stationery"]},"linked-paperclips":{"a":"Linked Paperclips","b":"1F587","j":["link","paperclip","documents","stationery"]},"straight-ruler":{"a":"Straight Ruler","b":"1F4CF","j":["ruler","straight edge","stationery","calculate","length","math","school","drawing","architect","sketch"]},"triangular-ruler":{"a":"Triangular Ruler","b":"1F4D0","j":["ruler","set","triangle","stationery","math","architect","sketch"]},"scissors":{"a":"Scissors","b":"2702","j":["cutting","tool","stationery","cut"]},"card-file-box":{"a":"Card File Box","b":"1F5C3","j":["box","card","file","business","stationery"]},"file-cabinet":{"a":"File Cabinet","b":"1F5C4","j":["cabinet","file","filing","organizing"]},"wastebasket":{"a":"Wastebasket","b":"1F5D1","j":["bin","trash","rubbish","garbage","toss"]},"locked":{"a":"Locked","b":"1F512","j":["closed","security","password","padlock"]},"unlocked":{"a":"Unlocked","b":"1F513","j":["lock","open","unlock","privacy","security"]},"locked-with-pen":{"a":"Locked with Pen","b":"1F50F","j":["ink","lock","nib","pen","privacy","security","secret"]},"locked-with-key":{"a":"Locked with Key","b":"1F510","j":["closed","key","lock","secure","security","privacy"]},"key":{"a":"Key","b":"1F511","j":["lock","password","door"]},"old-key":{"a":"Old Key","b":"1F5DD","j":["clue","key","lock","old","door","password"]},"hammer":{"a":"Hammer","b":"1F528","j":["tool","tools","build","create"]},"axe":{"a":"Axe","b":"1FA93","j":["chop","hatchet","split","wood","tool","cut"]},"pick":{"a":"Pick","b":"26CF","j":["mining","tool","tools","dig"]},"hammer-and-pick":{"a":"Hammer and Pick","b":"2692","j":["hammer","pick","tool","tools","build","create"]},"hammer-and-wrench":{"a":"Hammer and Wrench","b":"1F6E0","j":["hammer","spanner","tool","wrench","tools","build","create"]},"dagger":{"a":"Dagger","b":"1F5E1","j":["knife","weapon"]},"crossed-swords":{"a":"Crossed Swords","b":"2694","j":["crossed","swords","weapon"]},"water-pistol":{"a":"Water Pistol","b":"1F52B","j":["gun","handgun","pistol","revolver","tool","water","weapon","violence"]},"boomerang":{"a":"Boomerang","b":"1FA83","j":["australia","rebound","repercussion","weapon"]},"bow-and-arrow":{"a":"Bow and Arrow","b":"1F3F9","j":["archer","arrow","bow","Sagittarius","zodiac","sports"]},"shield":{"a":"Shield","b":"1F6E1","j":["weapon","protection","security"]},"carpentry-saw":{"a":"Carpentry Saw","b":"1FA9A","j":["carpenter","lumber","saw","tool","cut","chop"]},"wrench":{"a":"Wrench","b":"1F527","j":["spanner","tool","tools","diy","ikea","fix","maintainer"]},"screwdriver":{"a":"Screwdriver","b":"1FA9B","j":["screw","tool","tools"]},"nut-and-bolt":{"a":"Nut and Bolt","b":"1F529","j":["bolt","nut","tool","handy","tools","fix"]},"gear":{"a":"Gear","b":"2699","j":["cog","cogwheel","tool"]},"clamp":{"a":"Clamp","b":"1F5DC","j":["compress","tool","vice"]},"balance-scale":{"a":"Balance Scale","b":"2696","j":["balance","justice","Libra","scale","zodiac","law","fairness","weight"]},"white-cane":{"a":"White Cane","b":"1F9AF","j":["accessibility","blind","probing_cane"]},"link":{"a":"Link","b":"1F517","j":["rings","url"]},"chains":{"a":"Chains","b":"26D3","j":["chain","lock","arrest"]},"hook":{"a":"Hook","b":"1FA9D","j":["catch","crook","curve","ensnare","selling point","tools"]},"toolbox":{"a":"Toolbox","b":"1F9F0","j":["chest","mechanic","tool","tools","diy","fix","maintainer"]},"magnet":{"a":"Magnet","b":"1F9F2","j":["attraction","horseshoe","magnetic"]},"ladder":{"a":"Ladder","b":"1FA9C","j":["climb","rung","step","tools"]},"alembic":{"a":"Alembic","b":"2697","j":["chemistry","tool","distilling","science","experiment"]},"test-tube":{"a":"Test Tube","b":"1F9EA","j":["chemist","chemistry","experiment","lab","science"]},"petri-dish":{"a":"Petri Dish","b":"1F9EB","j":["bacteria","biologist","biology","culture","lab"]},"dna":{"a":"Dna","b":"1F9EC","j":["biologist","evolution","gene","genetics","life"]},"microscope":{"a":"Microscope","b":"1F52C","j":["science","tool","laboratory","experiment","zoomin","study"]},"telescope":{"a":"Telescope","b":"1F52D","j":["science","tool","stars","space","zoom","astronomy"]},"satellite-antenna":{"a":"Satellite Antenna","b":"1F4E1","j":["antenna","dish","satellite","communication","future","radio","space"]},"syringe":{"a":"Syringe","b":"1F489","j":["medicine","needle","shot","sick","health","hospital","drugs","blood","doctor","nurse"]},"drop-of-blood":{"a":"Drop of Blood","b":"1FA78","j":["bleed","blood donation","injury","medicine","menstruation","period","hurt","harm","wound"]},"pill":{"a":"Pill","b":"1F48A","j":["doctor","medicine","sick","health","pharmacy","drug"]},"adhesive-bandage":{"a":"Adhesive Bandage","b":"1FA79","j":["bandage","heal"]},"stethoscope":{"a":"Stethoscope","b":"1FA7A","j":["doctor","heart","medicine","health"]},"door":{"a":"Door","b":"1F6AA","j":["house","entry","exit"]},"elevator":{"a":"Elevator","b":"1F6D7","j":["accessibility","hoist","lift"]},"mirror":{"a":"Mirror","b":"1FA9E","j":["reflection","reflector","speculum"]},"window":{"a":"Window","b":"1FA9F","j":["frame","fresh air","opening","transparent","view","scenery"]},"bed":{"a":"Bed","b":"1F6CF","j":["hotel","sleep","rest"]},"couch-and-lamp":{"a":"Couch and Lamp","b":"1F6CB","j":["couch","hotel","lamp","read","chill"]},"chair":{"a":"Chair","b":"1FA91","j":["seat","sit","furniture"]},"toilet":{"a":"Toilet","b":"1F6BD","j":["restroom","wc","washroom","bathroom","potty"]},"plunger":{"a":"Plunger","b":"1FAA0","j":["force cup","plumber","suction","toilet"]},"shower":{"a":"Shower","b":"1F6BF","j":["water","clean","bathroom"]},"bathtub":{"a":"Bathtub","b":"1F6C1","j":["bath","clean","shower","bathroom"]},"mouse-trap":{"a":"Mouse Trap","b":"1FAA4","j":["bait","mousetrap","snare","trap","cheese"]},"razor":{"a":"Razor","b":"1FA92","j":["sharp","shave","cut"]},"lotion-bottle":{"a":"Lotion Bottle","b":"1F9F4","j":["lotion","moisturizer","shampoo","sunscreen"]},"safety-pin":{"a":"Safety Pin","b":"1F9F7","j":["diaper","punk rock"]},"broom":{"a":"Broom","b":"1F9F9","j":["cleaning","sweeping","witch"]},"basket":{"a":"Basket","b":"1F9FA","j":["farming","laundry","picnic"]},"roll-of-paper":{"a":"Roll of Paper","b":"1F9FB","j":["paper towels","toilet paper","roll"]},"bucket":{"a":"Bucket","b":"1FAA3","j":["cask","pail","vat","water","container"]},"soap":{"a":"Soap","b":"1F9FC","j":["bar","bathing","cleaning","lather","soapdish"]},"toothbrush":{"a":"Toothbrush","b":"1FAA5","j":["bathroom","brush","clean","dental","hygiene","teeth"]},"sponge":{"a":"Sponge","b":"1F9FD","j":["absorbing","cleaning","porous"]},"fire-extinguisher":{"a":"Fire Extinguisher","b":"1F9EF","j":["extinguish","fire","quench"]},"shopping-cart":{"a":"Shopping Cart","b":"1F6D2","j":["cart","shopping","trolley"]},"cigarette":{"a":"Cigarette","b":"1F6AC","j":["smoking","kills","tobacco","joint","smoke"]},"coffin":{"a":"Coffin","b":"26B0","j":["death","vampire","dead","die","rip","graveyard","cemetery","casket","funeral","box"]},"headstone":{"a":"Headstone","b":"1FAA6","j":["cemetery","grave","graveyard","tombstone","death","rip"]},"funeral-urn":{"a":"Funeral Urn","b":"26B1","j":["ashes","death","funeral","urn","dead","die","rip"]},"moai":{"a":"Moai","b":"1F5FF","j":["face","moyai","statue","rock","easter island"]},"placard":{"a":"Placard","b":"1FAA7","j":["demonstration","picket","protest","sign","announcement"]},"atm-sign":{"a":"Atm Sign","b":"1F3E7","j":["atm","ATM sign","automated","bank","teller","money","sales","cash","blue-square","payment"]},"litter-in-bin-sign":{"a":"Litter in Bin Sign","b":"1F6AE","j":["litter","litter bin","blue-square","sign","human","info"]},"potable-water":{"a":"Potable Water","b":"1F6B0","j":["drinking","potable","water","blue-square","liquid","restroom","cleaning","faucet"]},"wheelchair-symbol":{"a":"Wheelchair Symbol","b":"267F","j":["access","blue-square","disabled","accessibility"]},"mens-room":{"a":"Men’S Room","b":"1F6B9","j":["lavatory","man","men’s room","restroom","wc","men_s_room","toilet","blue-square","gender","male"]},"womens-room":{"a":"Women’S Room","b":"1F6BA","j":["lavatory","restroom","wc","woman","women’s room","women_s_room","purple-square","female","toilet","loo","gender"]},"restroom":{"a":"Restroom","b":"1F6BB","j":["lavatory","WC","blue-square","toilet","refresh","wc","gender"]},"baby-symbol":{"a":"Baby Symbol","b":"1F6BC","j":["baby","changing","orange-square","child"]},"water-closet":{"a":"Water Closet","b":"1F6BE","j":["closet","lavatory","restroom","water","wc","toilet","blue-square"]},"passport-control":{"a":"Passport Control","b":"1F6C2","j":["control","passport","custom","blue-square"]},"customs":{"a":"Customs","b":"1F6C3","j":["passport","border","blue-square"]},"baggage-claim":{"a":"Baggage Claim","b":"1F6C4","j":["baggage","claim","blue-square","airport","transport"]},"left-luggage":{"a":"Left Luggage","b":"1F6C5","j":["baggage","locker","luggage","blue-square","travel"]},"warning":{"a":"Warning","b":"26A0","j":["exclamation","wip","alert","error","problem","issue"]},"children-crossing":{"a":"Children Crossing","b":"1F6B8","j":["child","crossing","pedestrian","traffic","school","warning","danger","sign","driving","yellow-diamond"]},"no-entry":{"a":"No Entry","b":"26D4","j":["entry","forbidden","no","not","prohibited","traffic","limit","security","privacy","bad","denied","stop","circle"]},"prohibited":{"a":"Prohibited","b":"1F6AB","j":["entry","forbidden","no","not","forbid","stop","limit","denied","disallow","circle"]},"no-bicycles":{"a":"No Bicycles","b":"1F6B3","j":["bicycle","bike","forbidden","no","prohibited","cyclist","circle"]},"no-smoking":{"a":"No Smoking","b":"1F6AD","j":["forbidden","no","not","prohibited","smoking","cigarette","blue-square","smell","smoke"]},"no-littering":{"a":"No Littering","b":"1F6AF","j":["forbidden","litter","no","not","prohibited","trash","bin","garbage","circle"]},"nonpotable-water":{"a":"Non-Potable Water","b":"1F6B1","j":["non-drinking","non-potable","water","non_potable_water","drink","faucet","tap","circle"]},"no-pedestrians":{"a":"No Pedestrians","b":"1F6B7","j":["forbidden","no","not","pedestrian","prohibited","rules","crossing","walking","circle"]},"no-mobile-phones":{"a":"No Mobile Phones","b":"1F4F5","j":["cell","forbidden","mobile","no","phone","iphone","mute","circle"]},"no-one-under-eighteen":{"a":"No One Under Eighteen","b":"1F51E","j":["18","age restriction","eighteen","prohibited","underage","drink","pub","night","minor","circle"]},"radioactive":{"a":"Radioactive","b":"2622","j":["sign","nuclear","danger"]},"biohazard":{"a":"Biohazard","b":"2623","j":["sign","danger"]},"up-arrow":{"a":"Up Arrow","b":"2B06","j":["arrow","cardinal","direction","north","blue-square","continue","top"]},"upright-arrow":{"a":"Up-Right Arrow","b":"2197","j":["arrow","direction","intercardinal","northeast","up-right arrow","up_right_arrow","blue-square","point","diagonal"]},"right-arrow":{"a":"Right Arrow","b":"27A1","j":["arrow","cardinal","direction","east","blue-square","next"]},"downright-arrow":{"a":"Down-Right Arrow","b":"2198","j":["arrow","direction","down-right arrow","intercardinal","southeast","down_right_arrow","blue-square","diagonal"]},"down-arrow":{"a":"Down Arrow","b":"2B07","j":["arrow","cardinal","direction","down","south","blue-square","bottom"]},"downleft-arrow":{"a":"Down-Left Arrow","b":"2199","j":["arrow","direction","down-left arrow","intercardinal","southwest","down_left_arrow","blue-square","diagonal"]},"left-arrow":{"a":"Left Arrow","b":"2B05","j":["arrow","cardinal","direction","west","blue-square","previous","back"]},"upleft-arrow":{"a":"Up-Left Arrow","b":"2196","j":["arrow","direction","intercardinal","northwest","up-left arrow","up_left_arrow","blue-square","point","diagonal"]},"updown-arrow":{"a":"Up-Down Arrow","b":"2195","j":["arrow","up-down arrow","up_down_arrow","blue-square","direction","way","vertical"]},"leftright-arrow":{"a":"Left-Right Arrow","b":"2194","j":["arrow","left-right arrow","left_right_arrow","shape","direction","horizontal","sideways"]},"right-arrow-curving-left":{"a":"Right Arrow Curving Left","b":"21A9","j":["arrow","back","return","blue-square","undo","enter"]},"left-arrow-curving-right":{"a":"Left Arrow Curving Right","b":"21AA","j":["arrow","blue-square","return","rotate","direction"]},"right-arrow-curving-up":{"a":"Right Arrow Curving Up","b":"2934","j":["arrow","blue-square","direction","top"]},"right-arrow-curving-down":{"a":"Right Arrow Curving Down","b":"2935","j":["arrow","down","blue-square","direction","bottom"]},"clockwise-vertical-arrows":{"a":"Clockwise Vertical Arrows","b":"1F503","j":["arrow","clockwise","reload","sync","cycle","round","repeat"]},"counterclockwise-arrows-button":{"a":"Counterclockwise Arrows Button","b":"1F504","j":["anticlockwise","arrow","counterclockwise","withershins","blue-square","sync","cycle"]},"back-arrow":{"a":"Back Arrow","b":"1F519","j":["arrow","back","BACK arrow","words","return"]},"end-arrow":{"a":"End Arrow","b":"1F51A","j":["arrow","end","END arrow","words"]},"on-arrow":{"a":"On! Arrow","b":"1F51B","j":["arrow","mark","on","ON! arrow","words"]},"soon-arrow":{"a":"Soon Arrow","b":"1F51C","j":["arrow","soon","SOON arrow","words"]},"top-arrow":{"a":"Top Arrow","b":"1F51D","j":["arrow","top","TOP arrow","up","words","blue-square"]},"place-of-worship":{"a":"Place of Worship","b":"1F6D0","j":["religion","worship","church","temple","prayer"]},"atom-symbol":{"a":"Atom Symbol","b":"269B","j":["atheist","atom","science","physics","chemistry"]},"om":{"a":"Om","b":"1F549","j":["Hindu","religion","hinduism","buddhism","sikhism","jainism"]},"star-of-david":{"a":"Star of David","b":"2721","j":["David","Jew","Jewish","religion","star","star of David","judaism"]},"wheel-of-dharma":{"a":"Wheel of Dharma","b":"2638","j":["Buddhist","dharma","religion","wheel","hinduism","buddhism","sikhism","jainism"]},"yin-yang":{"a":"Yin Yang","b":"262F","j":["religion","tao","taoist","yang","yin","balance"]},"latin-cross":{"a":"Latin Cross","b":"271D","j":["Christian","cross","religion","christianity"]},"orthodox-cross":{"a":"Orthodox Cross","b":"2626","j":["Christian","cross","religion","suppedaneum"]},"star-and-crescent":{"a":"Star and Crescent","b":"262A","j":["islam","Muslim","religion"]},"peace-symbol":{"a":"Peace Symbol","b":"262E","j":["peace","hippie"]},"menorah":{"a":"Menorah","b":"1F54E","j":["candelabrum","candlestick","religion","hanukkah","candles","jewish"]},"dotted-sixpointed-star":{"a":"Dotted Six-Pointed Star","b":"1F52F","j":["dotted six-pointed star","fortune","star","dotted_six_pointed_star","purple-square","religion","jewish","hexagram"]},"aries":{"a":"Aries","b":"2648","j":["ram","zodiac","sign","purple-square","astrology"]},"taurus":{"a":"Taurus","b":"2649","j":["bull","ox","zodiac","purple-square","sign","astrology"]},"gemini":{"a":"Gemini","b":"264A","j":["twins","zodiac","sign","purple-square","astrology"]},"cancer":{"a":"Cancer","b":"264B","j":["crab","zodiac","sign","purple-square","astrology"]},"leo":{"a":"Leo","b":"264C","j":["lion","zodiac","sign","purple-square","astrology"]},"virgo":{"a":"Virgo","b":"264D","j":["zodiac","sign","purple-square","astrology"]},"libra":{"a":"Libra","b":"264E","j":["balance","justice","scales","zodiac","sign","purple-square","astrology"]},"scorpio":{"a":"Scorpio","b":"264F","j":["scorpion","scorpius","zodiac","sign","purple-square","astrology"]},"sagittarius":{"a":"Sagittarius","b":"2650","j":["archer","zodiac","sign","purple-square","astrology"]},"capricorn":{"a":"Capricorn","b":"2651","j":["goat","zodiac","sign","purple-square","astrology"]},"aquarius":{"a":"Aquarius","b":"2652","j":["bearer","water","zodiac","sign","purple-square","astrology"]},"pisces":{"a":"Pisces","b":"2653","j":["fish","zodiac","purple-square","sign","astrology"]},"ophiuchus":{"a":"Ophiuchus","b":"26CE","j":["bearer","serpent","snake","zodiac","sign","purple-square","constellation","astrology"]},"shuffle-tracks-button":{"a":"Shuffle Tracks Button","b":"1F500","j":["arrow","crossed","blue-square","shuffle","music","random"]},"repeat-button":{"a":"Repeat Button","b":"1F501","j":["arrow","clockwise","repeat","loop","record"]},"repeat-single-button":{"a":"Repeat Single Button","b":"1F502","j":["arrow","clockwise","once","blue-square","loop"]},"play-button":{"a":"Play Button","b":"25B6","j":["arrow","play","right","triangle","blue-square","direction"]},"fastforward-button":{"a":"Fast-Forward Button","b":"23E9","j":["arrow","double","fast","fast-forward button","forward","fast_forward_button","blue-square","play","speed","continue"]},"next-track-button":{"a":"Next Track Button","b":"23ED","j":["arrow","next scene","next track","triangle","forward","next","blue-square"]},"play-or-pause-button":{"a":"Play or Pause Button","b":"23EF","j":["arrow","pause","play","right","triangle","blue-square"]},"reverse-button":{"a":"Reverse Button","b":"25C0","j":["arrow","left","reverse","triangle","blue-square","direction"]},"fast-reverse-button":{"a":"Fast Reverse Button","b":"23EA","j":["arrow","double","rewind","play","blue-square"]},"last-track-button":{"a":"Last Track Button","b":"23EE","j":["arrow","previous scene","previous track","triangle","backward"]},"upwards-button":{"a":"Upwards Button","b":"1F53C","j":["arrow","button","red","blue-square","triangle","direction","point","forward","top"]},"fast-up-button":{"a":"Fast Up Button","b":"23EB","j":["arrow","double","blue-square","direction","top"]},"downwards-button":{"a":"Downwards Button","b":"1F53D","j":["arrow","button","down","red","blue-square","direction","bottom"]},"fast-down-button":{"a":"Fast Down Button","b":"23EC","j":["arrow","double","down","blue-square","direction","bottom"]},"pause-button":{"a":"Pause Button","b":"23F8","j":["bar","double","pause","vertical","blue-square"]},"stop-button":{"a":"Stop Button","b":"23F9","j":["square","stop","blue-square"]},"record-button":{"a":"Record Button","b":"23FA","j":["circle","record","blue-square"]},"eject-button":{"a":"Eject Button","b":"23CF","j":["eject","blue-square"]},"cinema":{"a":"Cinema","b":"1F3A6","j":["camera","film","movie","blue-square","record","curtain","stage","theater"]},"dim-button":{"a":"Dim Button","b":"1F505","j":["brightness","dim","low","sun","afternoon","warm","summer"]},"bright-button":{"a":"Bright Button","b":"1F506","j":["bright","brightness","sun","light"]},"antenna-bars":{"a":"Antenna Bars","b":"1F4F6","j":["antenna","bar","cell","mobile","phone","blue-square","reception","internet","connection","wifi","bluetooth","bars"]},"vibration-mode":{"a":"Vibration Mode","b":"1F4F3","j":["cell","mobile","mode","phone","telephone","vibration","orange-square"]},"mobile-phone-off":{"a":"Mobile Phone off","b":"1F4F4","j":["cell","mobile","off","phone","telephone","mute","orange-square","silence","quiet"]},"female-sign":{"a":"Female Sign","b":"2640","j":["woman","women","lady","girl"]},"male-sign":{"a":"Male Sign","b":"2642","j":["man","boy","men"]},"transgender-symbol":{"a":"Transgender Symbol","b":"26A7","j":["transgender","lgbtq"]},"multiply":{"a":"Multiply","b":"2716","j":["×","cancel","multiplication","sign","x","multiplication_sign","math","calculation"]},"plus":{"a":"Plus","b":"2795","j":["+","math","sign","plus_sign","calculation","addition","more","increase"]},"minus":{"a":"Minus","b":"2796","j":["-","−","math","sign","minus_sign","calculation","subtract","less"]},"divide":{"a":"Divide","b":"2797","j":["÷","division","math","sign","division_sign","calculation"]},"infinity":{"a":"Infinity","b":"267E","j":["forever","unbounded","universal"]},"double-exclamation-mark":{"a":"Double Exclamation Mark","b":"203C","j":["!","!!","bangbang","exclamation","mark","surprise"]},"exclamation-question-mark":{"a":"Exclamation Question Mark","b":"2049","j":["!","!?","?","exclamation","interrobang","mark","punctuation","question","wat","surprise"]},"red-question-mark":{"a":"Red Question Mark","b":"2753","j":["?","mark","punctuation","question","question_mark","doubt","confused"]},"white-question-mark":{"a":"White Question Mark","b":"2754","j":["?","mark","outlined","punctuation","question","doubts","gray","huh","confused"]},"white-exclamation-mark":{"a":"White Exclamation Mark","b":"2755","j":["!","exclamation","mark","outlined","punctuation","surprise","gray","wow","warning"]},"red-exclamation-mark":{"a":"Red Exclamation Mark","b":"2757","j":["!","exclamation","mark","punctuation","exclamation_mark","heavy_exclamation_mark","danger","surprise","wow","warning"]},"wavy-dash":{"a":"Wavy Dash","b":"3030","j":["dash","punctuation","wavy","draw","line","moustache","mustache","squiggle","scribble"]},"currency-exchange":{"a":"Currency Exchange","b":"1F4B1","j":["bank","currency","exchange","money","sales","dollar","travel"]},"heavy-dollar-sign":{"a":"Heavy Dollar Sign","b":"1F4B2","j":["currency","dollar","money","sales","payment","buck"]},"medical-symbol":{"a":"Medical Symbol","b":"2695","j":["aesculapius","medicine","staff","health","hospital"]},"recycling-symbol":{"a":"Recycling Symbol","b":"267B","j":["recycle","arrow","environment","garbage","trash"]},"fleurdelis":{"a":"Fleur-De-Lis","b":"269C","j":["fleur-de-lis","fleur_de_lis","decorative","scout"]},"trident-emblem":{"a":"Trident Emblem","b":"1F531","j":["anchor","emblem","ship","tool","trident","weapon","spear"]},"name-badge":{"a":"Name Badge","b":"1F4DB","j":["badge","name","fire","forbid"]},"japanese-symbol-for-beginner":{"a":"Japanese Symbol for Beginner","b":"1F530","j":["beginner","chevron","Japanese","Japanese symbol for beginner","leaf","badge","shield"]},"hollow-red-circle":{"a":"Hollow Red Circle","b":"2B55","j":["circle","large","o","red","round"]},"check-mark-button":{"a":"Check Mark Button","b":"2705","j":["✓","button","check","mark","green-square","ok","agree","vote","election","answer","tick"]},"check-box-with-check":{"a":"Check Box with Check","b":"2611","j":["✓","box","check","ok","agree","confirm","black-square","vote","election","yes","tick"]},"check-mark":{"a":"Check Mark","b":"2714","j":["✓","check","mark","ok","nike","answer","yes","tick"]},"cross-mark":{"a":"Cross Mark","b":"274C","j":["×","cancel","cross","mark","multiplication","multiply","x","no","delete","remove","red"]},"cross-mark-button":{"a":"Cross Mark Button","b":"274E","j":["×","mark","square","x","green-square","no","deny"]},"curly-loop":{"a":"Curly Loop","b":"27B0","j":["curl","loop","scribble","draw","shape","squiggle"]},"double-curly-loop":{"a":"Double Curly Loop","b":"27BF","j":["curl","double","loop","tape","cassette"]},"part-alternation-mark":{"a":"Part Alternation Mark","b":"303D","j":["mark","part","graph","presentation","stats","business","economics","bad"]},"eightspoked-asterisk":{"a":"Eight-Spoked Asterisk","b":"2733","j":["*","asterisk","eight-spoked asterisk","eight_spoked_asterisk","star","sparkle","green-square"]},"eightpointed-star":{"a":"Eight-Pointed Star","b":"2734","j":["*","eight-pointed star","star","eight_pointed_star","orange-square","shape","polygon"]},"sparkle":{"a":"Sparkle","b":"2747","j":["*","stars","green-square","awesome","good","fireworks"]},"copyright":{"a":"Copyright","b":"00A9","j":["c","ip","license","circle","law","legal"]},"registered":{"a":"Registered","b":"00AE","j":["r","alphabet","circle"]},"trade-mark":{"a":"Trade Mark","b":"2122","j":["mark","tm","trademark","brand","law","legal"]},"keycap":{"a":"Keycap: *","b":"002A-FE0F-20E3","j":["keycap_","star"]},"keycap-0":{"a":"Keycap: 0","b":"0030-FE0F-20E3","j":["keycap","0","numbers","blue-square","null"]},"keycap-1":{"a":"Keycap: 1","b":"0031-FE0F-20E3","j":["keycap","blue-square","numbers","1"]},"keycap-2":{"a":"Keycap: 2","b":"0032-FE0F-20E3","j":["keycap","numbers","2","prime","blue-square"]},"keycap-3":{"a":"Keycap: 3","b":"0033-FE0F-20E3","j":["keycap","3","numbers","prime","blue-square"]},"keycap-4":{"a":"Keycap: 4","b":"0034-FE0F-20E3","j":["keycap","4","numbers","blue-square"]},"keycap-5":{"a":"Keycap: 5","b":"0035-FE0F-20E3","j":["keycap","5","numbers","blue-square","prime"]},"keycap-6":{"a":"Keycap: 6","b":"0036-FE0F-20E3","j":["keycap","6","numbers","blue-square"]},"keycap-7":{"a":"Keycap: 7","b":"0037-FE0F-20E3","j":["keycap","7","numbers","blue-square","prime"]},"keycap-8":{"a":"Keycap: 8","b":"0038-FE0F-20E3","j":["keycap","8","blue-square","numbers"]},"keycap-9":{"a":"Keycap: 9","b":"0039-FE0F-20E3","j":["keycap","blue-square","numbers","9"]},"keycap-10":{"a":"Keycap: 10","b":"1F51F","j":["keycap","numbers","10","blue-square"]},"input-latin-uppercase":{"a":"Input Latin Uppercase","b":"1F520","j":["ABCD","input","latin","letters","uppercase","alphabet","words","blue-square"]},"input-latin-lowercase":{"a":"Input Latin Lowercase","b":"1F521","j":["abcd","input","latin","letters","lowercase","blue-square","alphabet"]},"input-numbers":{"a":"Input Numbers","b":"1F522","j":["1234","input","numbers","blue-square"]},"input-symbols":{"a":"Input Symbols","b":"1F523","j":["〒♪&%","input","blue-square","music","note","ampersand","percent","glyphs","characters"]},"input-latin-letters":{"a":"Input Latin Letters","b":"1F524","j":["abc","alphabet","input","latin","letters","blue-square"]},"a-button-blood-type":{"a":"A Button (Blood Type)","b":"1F170","j":["a","A button (blood type)","blood type","a_button","red-square","alphabet","letter"]},"ab-button-blood-type":{"a":"Ab Button (Blood Type)","b":"1F18E","j":["ab","AB button (blood type)","blood type","ab_button","red-square","alphabet"]},"b-button-blood-type":{"a":"B Button (Blood Type)","b":"1F171","j":["b","B button (blood type)","blood type","b_button","red-square","alphabet","letter"]},"cl-button":{"a":"Cl Button","b":"1F191","j":["cl","CL button","alphabet","words","red-square"]},"cool-button":{"a":"Cool Button","b":"1F192","j":["cool","COOL button","words","blue-square"]},"free-button":{"a":"Free Button","b":"1F193","j":["free","FREE button","blue-square","words"]},"information":{"a":"Information","b":"2139","j":["i","blue-square","alphabet","letter"]},"id-button":{"a":"Id Button","b":"1F194","j":["id","ID button","identity","purple-square","words"]},"circled-m":{"a":"Circled M","b":"24C2","j":["circle","circled M","m","alphabet","blue-circle","letter"]},"new-button":{"a":"New Button","b":"1F195","j":["new","NEW button","blue-square","words","start"]},"ng-button":{"a":"Ng Button","b":"1F196","j":["ng","NG button","blue-square","words","shape","icon"]},"o-button-blood-type":{"a":"O Button (Blood Type)","b":"1F17E","j":["blood type","o","O button (blood type)","o_button","alphabet","red-square","letter"]},"ok-button":{"a":"Ok Button","b":"1F197","j":["OK","OK button","good","agree","yes","blue-square"]},"p-button":{"a":"P Button","b":"1F17F","j":["P button","parking","cars","blue-square","alphabet","letter"]},"sos-button":{"a":"Sos Button","b":"1F198","j":["help","sos","SOS button","red-square","words","emergency","911"]},"up-button":{"a":"Up! Button","b":"1F199","j":["mark","up","UP! button","blue-square","above","high"]},"vs-button":{"a":"Vs Button","b":"1F19A","j":["versus","vs","VS button","words","orange-square"]},"japanese-here-button":{"a":"Japanese “Here” Button","b":"1F201","j":["“here”","Japanese","Japanese “here” button","katakana","ココ","blue-square","here","japanese","destination"]},"japanese-service-charge-button":{"a":"Japanese “Service Charge” Button","b":"1F202","j":["“service charge”","Japanese","Japanese “service charge” button","katakana","サ","japanese","blue-square"]},"japanese-monthly-amount-button":{"a":"Japanese “Monthly Amount” Button","b":"1F237","j":["“monthly amount”","ideograph","Japanese","Japanese “monthly amount” button","月","chinese","month","moon","japanese","orange-square","kanji"]},"japanese-not-free-of-charge-button":{"a":"Japanese “Not Free of Charge” Button","b":"1F236","j":["“not free of charge”","ideograph","Japanese","Japanese “not free of charge” button","有","orange-square","chinese","have","kanji"]},"japanese-reserved-button":{"a":"Japanese “Reserved” Button","b":"1F22F","j":["“reserved”","ideograph","Japanese","Japanese “reserved” button","指","chinese","point","green-square","kanji"]},"japanese-bargain-button":{"a":"Japanese “Bargain” Button","b":"1F250","j":["“bargain”","ideograph","Japanese","Japanese “bargain” button","得","chinese","kanji","obtain","get","circle"]},"japanese-discount-button":{"a":"Japanese “Discount” Button","b":"1F239","j":["“discount”","ideograph","Japanese","Japanese “discount” button","割","cut","divide","chinese","kanji","pink-square"]},"japanese-free-of-charge-button":{"a":"Japanese “Free of Charge” Button","b":"1F21A","j":["“free of charge”","ideograph","Japanese","Japanese “free of charge” button","無","nothing","chinese","kanji","japanese","orange-square"]},"japanese-prohibited-button":{"a":"Japanese “Prohibited” Button","b":"1F232","j":["“prohibited”","ideograph","Japanese","Japanese “prohibited” button","禁","kanji","japanese","chinese","forbidden","limit","restricted","red-square"]},"japanese-acceptable-button":{"a":"Japanese “Acceptable” Button","b":"1F251","j":["“acceptable”","ideograph","Japanese","Japanese “acceptable” button","可","ok","good","chinese","kanji","agree","yes","orange-circle"]},"japanese-application-button":{"a":"Japanese “Application” Button","b":"1F238","j":["“application”","ideograph","Japanese","Japanese “application” button","申","chinese","japanese","kanji","orange-square"]},"japanese-passing-grade-button":{"a":"Japanese “Passing Grade” Button","b":"1F234","j":["“passing grade”","ideograph","Japanese","Japanese “passing grade” button","合","japanese","chinese","join","kanji","red-square"]},"japanese-vacancy-button":{"a":"Japanese “Vacancy” Button","b":"1F233","j":["“vacancy”","ideograph","Japanese","Japanese “vacancy” button","空","kanji","japanese","chinese","empty","sky","blue-square"]},"japanese-congratulations-button":{"a":"Japanese “Congratulations” Button","b":"3297","j":["“congratulations”","ideograph","Japanese","Japanese “congratulations” button","祝","chinese","kanji","japanese","red-circle"]},"japanese-secret-button":{"a":"Japanese “Secret” Button","b":"3299","j":["“secret”","ideograph","Japanese","Japanese “secret” button","秘","privacy","chinese","sshh","kanji","red-circle"]},"japanese-open-for-business-button":{"a":"Japanese “Open for Business” Button","b":"1F23A","j":["“open for business”","ideograph","Japanese","Japanese “open for business” button","営","japanese","opening hours","orange-square"]},"japanese-no-vacancy-button":{"a":"Japanese “No Vacancy” Button","b":"1F235","j":["“no vacancy”","ideograph","Japanese","Japanese “no vacancy” button","満","full","chinese","japanese","red-square","kanji"]},"red-circle":{"a":"Red Circle","b":"1F534","j":["circle","geometric","red","shape","error","danger"]},"orange-circle":{"a":"Orange Circle","b":"1F7E0","j":["circle","orange","round"]},"yellow-circle":{"a":"Yellow Circle","b":"1F7E1","j":["circle","yellow","round"]},"green-circle":{"a":"Green Circle","b":"1F7E2","j":["circle","green","round"]},"blue-circle":{"a":"Blue Circle","b":"1F535","j":["blue","circle","geometric","shape","icon","button"]},"purple-circle":{"a":"Purple Circle","b":"1F7E3","j":["circle","purple","round"]},"brown-circle":{"a":"Brown Circle","b":"1F7E4","j":["brown","circle","round"]},"black-circle":{"a":"Black Circle","b":"26AB","j":["circle","geometric","shape","button","round"]},"white-circle":{"a":"White Circle","b":"26AA","j":["circle","geometric","shape","round"]},"red-square":{"a":"Red Square","b":"1F7E5","j":["red","square"]},"orange-square":{"a":"Orange Square","b":"1F7E7","j":["orange","square"]},"yellow-square":{"a":"Yellow Square","b":"1F7E8","j":["square","yellow"]},"green-square":{"a":"Green Square","b":"1F7E9","j":["green","square"]},"blue-square":{"a":"Blue Square","b":"1F7E6","j":["blue","square"]},"purple-square":{"a":"Purple Square","b":"1F7EA","j":["purple","square"]},"brown-square":{"a":"Brown Square","b":"1F7EB","j":["brown","square"]},"black-large-square":{"a":"Black Large Square","b":"2B1B","j":["geometric","square","shape","icon","button"]},"white-large-square":{"a":"White Large Square","b":"2B1C","j":["geometric","square","shape","icon","stone","button"]},"black-medium-square":{"a":"Black Medium Square","b":"25FC","j":["geometric","square","shape","button","icon"]},"white-medium-square":{"a":"White Medium Square","b":"25FB","j":["geometric","square","shape","stone","icon"]},"black-mediumsmall-square":{"a":"Black Medium-Small Square","b":"25FE","j":["black medium-small square","geometric","square","black_medium_small_square","icon","shape","button"]},"white-mediumsmall-square":{"a":"White Medium-Small Square","b":"25FD","j":["geometric","square","white medium-small square","white_medium_small_square","shape","stone","icon","button"]},"black-small-square":{"a":"Black Small Square","b":"25AA","j":["geometric","square","shape","icon"]},"white-small-square":{"a":"White Small Square","b":"25AB","j":["geometric","square","shape","icon"]},"large-orange-diamond":{"a":"Large Orange Diamond","b":"1F536","j":["diamond","geometric","orange","shape","jewel","gem"]},"large-blue-diamond":{"a":"Large Blue Diamond","b":"1F537","j":["blue","diamond","geometric","shape","jewel","gem"]},"small-orange-diamond":{"a":"Small Orange Diamond","b":"1F538","j":["diamond","geometric","orange","shape","jewel","gem"]},"small-blue-diamond":{"a":"Small Blue Diamond","b":"1F539","j":["blue","diamond","geometric","shape","jewel","gem"]},"red-triangle-pointed-up":{"a":"Red Triangle Pointed Up","b":"1F53A","j":["geometric","red","shape","direction","up","top"]},"red-triangle-pointed-down":{"a":"Red Triangle Pointed Down","b":"1F53B","j":["down","geometric","red","shape","direction","bottom"]},"diamond-with-a-dot":{"a":"Diamond with a Dot","b":"1F4A0","j":["comic","diamond","geometric","inside","jewel","blue","gem","crystal","fancy"]},"radio-button":{"a":"Radio Button","b":"1F518","j":["button","geometric","radio","input","old","music","circle"]},"white-square-button":{"a":"White Square Button","b":"1F533","j":["button","geometric","outlined","square","shape","input"]},"black-square-button":{"a":"Black Square Button","b":"1F532","j":["button","geometric","square","shape","input","frame"]},"chequered-flag":{"a":"Chequered Flag","b":"1F3C1","j":["checkered","chequered","racing","contest","finishline","race","gokart"]},"triangular-flag":{"a":"Triangular Flag","b":"1F6A9","j":["post","mark","milestone","place"]},"crossed-flags":{"a":"Crossed Flags","b":"1F38C","j":["celebration","cross","crossed","Japanese","japanese","nation","country","border"]},"black-flag":{"a":"Black Flag","b":"1F3F4","j":["waving","pirate"]},"white-flag":{"a":"White Flag","b":"1F3F3","j":["waving","losing","loser","lost","surrender","give up","fail"]},"rainbow-flag":{"a":"Rainbow Flag","b":"1F3F3-FE0F-200D-1F308","j":["pride","rainbow","flag","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"]},"transgender-flag":{"a":"Transgender Flag","b":"1F3F3-FE0F-200D-26A7-FE0F","j":["flag","light blue","pink","transgender","white","lgbtq"]},"pirate-flag":{"a":"Pirate Flag","b":"1F3F4-200D-2620-FE0F","j":["Jolly Roger","pirate","plunder","treasure","skull","crossbones","flag","banner"]},"flag-ascension-island":{"a":"Flag: Ascension Island","b":"1F1E6-1F1E8","j":["flag"]},"flag-andorra":{"a":"Flag: Andorra","b":"1F1E6-1F1E9","j":["flag","ad","nation","country","banner"]},"flag-united-arab-emirates":{"a":"Flag: United Arab Emirates","b":"1F1E6-1F1EA","j":["flag","united","arab","emirates","nation","country","banner"]},"flag-afghanistan":{"a":"Flag: Afghanistan","b":"1F1E6-1F1EB","j":["flag","af","nation","country","banner"]},"flag-antigua--barbuda":{"a":"Flag: Antigua & Barbuda","b":"1F1E6-1F1EC","j":["flag","flag_antigua_barbuda","antigua","barbuda","nation","country","banner"]},"flag-anguilla":{"a":"Flag: Anguilla","b":"1F1E6-1F1EE","j":["flag","ai","nation","country","banner"]},"flag-albania":{"a":"Flag: Albania","b":"1F1E6-1F1F1","j":["flag","al","nation","country","banner"]},"flag-armenia":{"a":"Flag: Armenia","b":"1F1E6-1F1F2","j":["flag","am","nation","country","banner"]},"flag-angola":{"a":"Flag: Angola","b":"1F1E6-1F1F4","j":["flag","ao","nation","country","banner"]},"flag-antarctica":{"a":"Flag: Antarctica","b":"1F1E6-1F1F6","j":["flag","aq","nation","country","banner"]},"flag-argentina":{"a":"Flag: Argentina","b":"1F1E6-1F1F7","j":["flag","ar","nation","country","banner"]},"flag-american-samoa":{"a":"Flag: American Samoa","b":"1F1E6-1F1F8","j":["flag","american","ws","nation","country","banner"]},"flag-austria":{"a":"Flag: Austria","b":"1F1E6-1F1F9","j":["flag","at","nation","country","banner"]},"flag-australia":{"a":"Flag: Australia","b":"1F1E6-1F1FA","j":["flag","au","nation","country","banner"]},"flag-aruba":{"a":"Flag: Aruba","b":"1F1E6-1F1FC","j":["flag","aw","nation","country","banner"]},"flag-land-islands":{"a":"Flag: Åland Islands","b":"1F1E6-1F1FD","j":["flag","flag_aland_islands","Åland","islands","nation","country","banner"]},"flag-azerbaijan":{"a":"Flag: Azerbaijan","b":"1F1E6-1F1FF","j":["flag","az","nation","country","banner"]},"flag-bosnia--herzegovina":{"a":"Flag: Bosnia & Herzegovina","b":"1F1E7-1F1E6","j":["flag","flag_bosnia_herzegovina","bosnia","herzegovina","nation","country","banner"]},"flag-barbados":{"a":"Flag: Barbados","b":"1F1E7-1F1E7","j":["flag","bb","nation","country","banner"]},"flag-bangladesh":{"a":"Flag: Bangladesh","b":"1F1E7-1F1E9","j":["flag","bd","nation","country","banner"]},"flag-belgium":{"a":"Flag: Belgium","b":"1F1E7-1F1EA","j":["flag","be","nation","country","banner"]},"flag-burkina-faso":{"a":"Flag: Burkina Faso","b":"1F1E7-1F1EB","j":["flag","burkina","faso","nation","country","banner"]},"flag-bulgaria":{"a":"Flag: Bulgaria","b":"1F1E7-1F1EC","j":["flag","bg","nation","country","banner"]},"flag-bahrain":{"a":"Flag: Bahrain","b":"1F1E7-1F1ED","j":["flag","bh","nation","country","banner"]},"flag-burundi":{"a":"Flag: Burundi","b":"1F1E7-1F1EE","j":["flag","bi","nation","country","banner"]},"flag-benin":{"a":"Flag: Benin","b":"1F1E7-1F1EF","j":["flag","bj","nation","country","banner"]},"flag-st-barthlemy":{"a":"Flag: St. Barthélemy","b":"1F1E7-1F1F1","j":["flag","flag_st_barthelemy","saint","barthélemy","nation","country","banner"]},"flag-bermuda":{"a":"Flag: Bermuda","b":"1F1E7-1F1F2","j":["flag","bm","nation","country","banner"]},"flag-brunei":{"a":"Flag: Brunei","b":"1F1E7-1F1F3","j":["flag","bn","darussalam","nation","country","banner"]},"flag-bolivia":{"a":"Flag: Bolivia","b":"1F1E7-1F1F4","j":["flag","bo","nation","country","banner"]},"flag-caribbean-netherlands":{"a":"Flag: Caribbean Netherlands","b":"1F1E7-1F1F6","j":["flag","bonaire","nation","country","banner"]},"flag-brazil":{"a":"Flag: Brazil","b":"1F1E7-1F1F7","j":["flag","br","nation","country","banner"]},"flag-bahamas":{"a":"Flag: Bahamas","b":"1F1E7-1F1F8","j":["flag","bs","nation","country","banner"]},"flag-bhutan":{"a":"Flag: Bhutan","b":"1F1E7-1F1F9","j":["flag","bt","nation","country","banner"]},"flag-bouvet-island":{"a":"Flag: Bouvet Island","b":"1F1E7-1F1FB","j":["flag","norway"]},"flag-botswana":{"a":"Flag: Botswana","b":"1F1E7-1F1FC","j":["flag","bw","nation","country","banner"]},"flag-belarus":{"a":"Flag: Belarus","b":"1F1E7-1F1FE","j":["flag","by","nation","country","banner"]},"flag-belize":{"a":"Flag: Belize","b":"1F1E7-1F1FF","j":["flag","bz","nation","country","banner"]},"flag-canada":{"a":"Flag: Canada","b":"1F1E8-1F1E6","j":["flag","ca","nation","country","banner"]},"flag-cocos-keeling-islands":{"a":"Flag: Cocos (Keeling) Islands","b":"1F1E8-1F1E8","j":["flag","flag_cocos_islands","cocos","keeling","islands","nation","country","banner"]},"flag-congo--kinshasa":{"a":"Flag: Congo - Kinshasa","b":"1F1E8-1F1E9","j":["flag","flag_congo_kinshasa","congo","democratic","republic","nation","country","banner"]},"flag-central-african-republic":{"a":"Flag: Central African Republic","b":"1F1E8-1F1EB","j":["flag","central","african","republic","nation","country","banner"]},"flag-congo--brazzaville":{"a":"Flag: Congo - Brazzaville","b":"1F1E8-1F1EC","j":["flag","flag_congo_brazzaville","congo","nation","country","banner"]},"flag-switzerland":{"a":"Flag: Switzerland","b":"1F1E8-1F1ED","j":["flag","ch","nation","country","banner"]},"flag-cte-divoire":{"a":"Flag: Côte D’Ivoire","b":"1F1E8-1F1EE","j":["flag","flag_cote_d_ivoire","ivory","coast","nation","country","banner"]},"flag-cook-islands":{"a":"Flag: Cook Islands","b":"1F1E8-1F1F0","j":["flag","cook","islands","nation","country","banner"]},"flag-chile":{"a":"Flag: Chile","b":"1F1E8-1F1F1","j":["flag","nation","country","banner"]},"flag-cameroon":{"a":"Flag: Cameroon","b":"1F1E8-1F1F2","j":["flag","cm","nation","country","banner"]},"flag-china":{"a":"Flag: China","b":"1F1E8-1F1F3","j":["flag","china","chinese","prc","country","nation","banner"]},"flag-colombia":{"a":"Flag: Colombia","b":"1F1E8-1F1F4","j":["flag","co","nation","country","banner"]},"flag-clipperton-island":{"a":"Flag: Clipperton Island","b":"1F1E8-1F1F5","j":["flag"]},"flag-costa-rica":{"a":"Flag: Costa Rica","b":"1F1E8-1F1F7","j":["flag","costa","rica","nation","country","banner"]},"flag-cuba":{"a":"Flag: Cuba","b":"1F1E8-1F1FA","j":["flag","cu","nation","country","banner"]},"flag-cape-verde":{"a":"Flag: Cape Verde","b":"1F1E8-1F1FB","j":["flag","cabo","verde","nation","country","banner"]},"flag-curaao":{"a":"Flag: Curaçao","b":"1F1E8-1F1FC","j":["flag","flag_curacao","curaçao","nation","country","banner"]},"flag-christmas-island":{"a":"Flag: Christmas Island","b":"1F1E8-1F1FD","j":["flag","christmas","island","nation","country","banner"]},"flag-cyprus":{"a":"Flag: Cyprus","b":"1F1E8-1F1FE","j":["flag","cy","nation","country","banner"]},"flag-czechia":{"a":"Flag: Czechia","b":"1F1E8-1F1FF","j":["flag","cz","nation","country","banner"]},"flag-germany":{"a":"Flag: Germany","b":"1F1E9-1F1EA","j":["flag","german","nation","country","banner"]},"flag-diego-garcia":{"a":"Flag: Diego Garcia","b":"1F1E9-1F1EC","j":["flag"]},"flag-djibouti":{"a":"Flag: Djibouti","b":"1F1E9-1F1EF","j":["flag","dj","nation","country","banner"]},"flag-denmark":{"a":"Flag: Denmark","b":"1F1E9-1F1F0","j":["flag","dk","nation","country","banner"]},"flag-dominica":{"a":"Flag: Dominica","b":"1F1E9-1F1F2","j":["flag","dm","nation","country","banner"]},"flag-dominican-republic":{"a":"Flag: Dominican Republic","b":"1F1E9-1F1F4","j":["flag","dominican","republic","nation","country","banner"]},"flag-algeria":{"a":"Flag: Algeria","b":"1F1E9-1F1FF","j":["flag","dz","nation","country","banner"]},"flag-ceuta--melilla":{"a":"Flag: Ceuta & Melilla","b":"1F1EA-1F1E6","j":["flag","flag_ceuta_melilla"]},"flag-ecuador":{"a":"Flag: Ecuador","b":"1F1EA-1F1E8","j":["flag","ec","nation","country","banner"]},"flag-estonia":{"a":"Flag: Estonia","b":"1F1EA-1F1EA","j":["flag","ee","nation","country","banner"]},"flag-egypt":{"a":"Flag: Egypt","b":"1F1EA-1F1EC","j":["flag","eg","nation","country","banner"]},"flag-western-sahara":{"a":"Flag: Western Sahara","b":"1F1EA-1F1ED","j":["flag","western","sahara","nation","country","banner"]},"flag-eritrea":{"a":"Flag: Eritrea","b":"1F1EA-1F1F7","j":["flag","er","nation","country","banner"]},"flag-spain":{"a":"Flag: Spain","b":"1F1EA-1F1F8","j":["flag","spain","nation","country","banner"]},"flag-ethiopia":{"a":"Flag: Ethiopia","b":"1F1EA-1F1F9","j":["flag","et","nation","country","banner"]},"flag-european-union":{"a":"Flag: European Union","b":"1F1EA-1F1FA","j":["flag","european","union","banner"]},"flag-finland":{"a":"Flag: Finland","b":"1F1EB-1F1EE","j":["flag","fi","nation","country","banner"]},"flag-fiji":{"a":"Flag: Fiji","b":"1F1EB-1F1EF","j":["flag","fj","nation","country","banner"]},"flag-falkland-islands":{"a":"Flag: Falkland Islands","b":"1F1EB-1F1F0","j":["flag","falkland","islands","malvinas","nation","country","banner"]},"flag-micronesia":{"a":"Flag: Micronesia","b":"1F1EB-1F1F2","j":["flag","micronesia","federated","states","nation","country","banner"]},"flag-faroe-islands":{"a":"Flag: Faroe Islands","b":"1F1EB-1F1F4","j":["flag","faroe","islands","nation","country","banner"]},"flag-france":{"a":"Flag: France","b":"1F1EB-1F1F7","j":["flag","banner","nation","france","french","country"]},"flag-gabon":{"a":"Flag: Gabon","b":"1F1EC-1F1E6","j":["flag","ga","nation","country","banner"]},"flag-united-kingdom":{"a":"Flag: United Kingdom","b":"1F1EC-1F1E7","j":["flag","united","kingdom","great","britain","northern","ireland","nation","country","banner","british","UK","english","england","union jack"]},"flag-grenada":{"a":"Flag: Grenada","b":"1F1EC-1F1E9","j":["flag","gd","nation","country","banner"]},"flag-georgia":{"a":"Flag: Georgia","b":"1F1EC-1F1EA","j":["flag","ge","nation","country","banner"]},"flag-french-guiana":{"a":"Flag: French Guiana","b":"1F1EC-1F1EB","j":["flag","french","guiana","nation","country","banner"]},"flag-guernsey":{"a":"Flag: Guernsey","b":"1F1EC-1F1EC","j":["flag","gg","nation","country","banner"]},"flag-ghana":{"a":"Flag: Ghana","b":"1F1EC-1F1ED","j":["flag","gh","nation","country","banner"]},"flag-gibraltar":{"a":"Flag: Gibraltar","b":"1F1EC-1F1EE","j":["flag","gi","nation","country","banner"]},"flag-greenland":{"a":"Flag: Greenland","b":"1F1EC-1F1F1","j":["flag","gl","nation","country","banner"]},"flag-gambia":{"a":"Flag: Gambia","b":"1F1EC-1F1F2","j":["flag","gm","nation","country","banner"]},"flag-guinea":{"a":"Flag: Guinea","b":"1F1EC-1F1F3","j":["flag","gn","nation","country","banner"]},"flag-guadeloupe":{"a":"Flag: Guadeloupe","b":"1F1EC-1F1F5","j":["flag","gp","nation","country","banner"]},"flag-equatorial-guinea":{"a":"Flag: Equatorial Guinea","b":"1F1EC-1F1F6","j":["flag","equatorial","gn","nation","country","banner"]},"flag-greece":{"a":"Flag: Greece","b":"1F1EC-1F1F7","j":["flag","gr","nation","country","banner"]},"flag-south-georgia--south-sandwich-islands":{"a":"Flag: South Georgia & South Sandwich Islands","b":"1F1EC-1F1F8","j":["flag","flag_south_georgia_south_sandwich_islands","south","georgia","sandwich","islands","nation","country","banner"]},"flag-guatemala":{"a":"Flag: Guatemala","b":"1F1EC-1F1F9","j":["flag","gt","nation","country","banner"]},"flag-guam":{"a":"Flag: Guam","b":"1F1EC-1F1FA","j":["flag","gu","nation","country","banner"]},"flag-guineabissau":{"a":"Flag: Guinea-Bissau","b":"1F1EC-1F1FC","j":["flag","flag_guinea_bissau","gw","bissau","nation","country","banner"]},"flag-guyana":{"a":"Flag: Guyana","b":"1F1EC-1F1FE","j":["flag","gy","nation","country","banner"]},"flag-hong-kong-sar-china":{"a":"Flag: Hong Kong Sar China","b":"1F1ED-1F1F0","j":["flag","hong","kong","nation","country","banner"]},"flag-heard--mcdonald-islands":{"a":"Flag: Heard & Mcdonald Islands","b":"1F1ED-1F1F2","j":["flag","flag_heard_mcdonald_islands"]},"flag-honduras":{"a":"Flag: Honduras","b":"1F1ED-1F1F3","j":["flag","hn","nation","country","banner"]},"flag-croatia":{"a":"Flag: Croatia","b":"1F1ED-1F1F7","j":["flag","hr","nation","country","banner"]},"flag-haiti":{"a":"Flag: Haiti","b":"1F1ED-1F1F9","j":["flag","ht","nation","country","banner"]},"flag-hungary":{"a":"Flag: Hungary","b":"1F1ED-1F1FA","j":["flag","hu","nation","country","banner"]},"flag-canary-islands":{"a":"Flag: Canary Islands","b":"1F1EE-1F1E8","j":["flag","canary","islands","nation","country","banner"]},"flag-indonesia":{"a":"Flag: Indonesia","b":"1F1EE-1F1E9","j":["flag","nation","country","banner"]},"flag-ireland":{"a":"Flag: Ireland","b":"1F1EE-1F1EA","j":["flag","ie","nation","country","banner"]},"flag-israel":{"a":"Flag: Israel","b":"1F1EE-1F1F1","j":["flag","il","nation","country","banner"]},"flag-isle-of-man":{"a":"Flag: Isle of Man","b":"1F1EE-1F1F2","j":["flag","isle","man","nation","country","banner"]},"flag-india":{"a":"Flag: India","b":"1F1EE-1F1F3","j":["flag","in","nation","country","banner"]},"flag-british-indian-ocean-territory":{"a":"Flag: British Indian Ocean Territory","b":"1F1EE-1F1F4","j":["flag","british","indian","ocean","territory","nation","country","banner"]},"flag-iraq":{"a":"Flag: Iraq","b":"1F1EE-1F1F6","j":["flag","iq","nation","country","banner"]},"flag-iran":{"a":"Flag: Iran","b":"1F1EE-1F1F7","j":["flag","iran","islamic","republic","nation","country","banner"]},"flag-iceland":{"a":"Flag: Iceland","b":"1F1EE-1F1F8","j":["flag","is","nation","country","banner"]},"flag-italy":{"a":"Flag: Italy","b":"1F1EE-1F1F9","j":["flag","italy","nation","country","banner"]},"flag-jersey":{"a":"Flag: Jersey","b":"1F1EF-1F1EA","j":["flag","je","nation","country","banner"]},"flag-jamaica":{"a":"Flag: Jamaica","b":"1F1EF-1F1F2","j":["flag","jm","nation","country","banner"]},"flag-jordan":{"a":"Flag: Jordan","b":"1F1EF-1F1F4","j":["flag","jo","nation","country","banner"]},"flag-japan":{"a":"Flag: Japan","b":"1F1EF-1F1F5","j":["flag","japanese","nation","country","banner"]},"flag-kenya":{"a":"Flag: Kenya","b":"1F1F0-1F1EA","j":["flag","ke","nation","country","banner"]},"flag-kyrgyzstan":{"a":"Flag: Kyrgyzstan","b":"1F1F0-1F1EC","j":["flag","kg","nation","country","banner"]},"flag-cambodia":{"a":"Flag: Cambodia","b":"1F1F0-1F1ED","j":["flag","kh","nation","country","banner"]},"flag-kiribati":{"a":"Flag: Kiribati","b":"1F1F0-1F1EE","j":["flag","ki","nation","country","banner"]},"flag-comoros":{"a":"Flag: Comoros","b":"1F1F0-1F1F2","j":["flag","km","nation","country","banner"]},"flag-st-kitts--nevis":{"a":"Flag: St. Kitts & Nevis","b":"1F1F0-1F1F3","j":["flag","flag_st_kitts_nevis","saint","kitts","nevis","nation","country","banner"]},"flag-north-korea":{"a":"Flag: North Korea","b":"1F1F0-1F1F5","j":["flag","north","korea","nation","country","banner"]},"flag-south-korea":{"a":"Flag: South Korea","b":"1F1F0-1F1F7","j":["flag","south","korea","nation","country","banner"]},"flag-kuwait":{"a":"Flag: Kuwait","b":"1F1F0-1F1FC","j":["flag","kw","nation","country","banner"]},"flag-cayman-islands":{"a":"Flag: Cayman Islands","b":"1F1F0-1F1FE","j":["flag","cayman","islands","nation","country","banner"]},"flag-kazakhstan":{"a":"Flag: Kazakhstan","b":"1F1F0-1F1FF","j":["flag","kz","nation","country","banner"]},"flag-laos":{"a":"Flag: Laos","b":"1F1F1-1F1E6","j":["flag","lao","democratic","republic","nation","country","banner"]},"flag-lebanon":{"a":"Flag: Lebanon","b":"1F1F1-1F1E7","j":["flag","lb","nation","country","banner"]},"flag-st-lucia":{"a":"Flag: St. Lucia","b":"1F1F1-1F1E8","j":["flag","saint","lucia","nation","country","banner"]},"flag-liechtenstein":{"a":"Flag: Liechtenstein","b":"1F1F1-1F1EE","j":["flag","li","nation","country","banner"]},"flag-sri-lanka":{"a":"Flag: Sri Lanka","b":"1F1F1-1F1F0","j":["flag","sri","lanka","nation","country","banner"]},"flag-liberia":{"a":"Flag: Liberia","b":"1F1F1-1F1F7","j":["flag","lr","nation","country","banner"]},"flag-lesotho":{"a":"Flag: Lesotho","b":"1F1F1-1F1F8","j":["flag","ls","nation","country","banner"]},"flag-lithuania":{"a":"Flag: Lithuania","b":"1F1F1-1F1F9","j":["flag","lt","nation","country","banner"]},"flag-luxembourg":{"a":"Flag: Luxembourg","b":"1F1F1-1F1FA","j":["flag","lu","nation","country","banner"]},"flag-latvia":{"a":"Flag: Latvia","b":"1F1F1-1F1FB","j":["flag","lv","nation","country","banner"]},"flag-libya":{"a":"Flag: Libya","b":"1F1F1-1F1FE","j":["flag","ly","nation","country","banner"]},"flag-morocco":{"a":"Flag: Morocco","b":"1F1F2-1F1E6","j":["flag","ma","nation","country","banner"]},"flag-monaco":{"a":"Flag: Monaco","b":"1F1F2-1F1E8","j":["flag","mc","nation","country","banner"]},"flag-moldova":{"a":"Flag: Moldova","b":"1F1F2-1F1E9","j":["flag","moldova","republic","nation","country","banner"]},"flag-montenegro":{"a":"Flag: Montenegro","b":"1F1F2-1F1EA","j":["flag","me","nation","country","banner"]},"flag-st-martin":{"a":"Flag: St. Martin","b":"1F1F2-1F1EB","j":["flag"]},"flag-madagascar":{"a":"Flag: Madagascar","b":"1F1F2-1F1EC","j":["flag","mg","nation","country","banner"]},"flag-marshall-islands":{"a":"Flag: Marshall Islands","b":"1F1F2-1F1ED","j":["flag","marshall","islands","nation","country","banner"]},"flag-north-macedonia":{"a":"Flag: North Macedonia","b":"1F1F2-1F1F0","j":["flag","macedonia","nation","country","banner"]},"flag-mali":{"a":"Flag: Mali","b":"1F1F2-1F1F1","j":["flag","ml","nation","country","banner"]},"flag-myanmar-burma":{"a":"Flag: Myanmar (Burma)","b":"1F1F2-1F1F2","j":["flag","flag_myanmar","mm","nation","country","banner"]},"flag-mongolia":{"a":"Flag: Mongolia","b":"1F1F2-1F1F3","j":["flag","mn","nation","country","banner"]},"flag-macao-sar-china":{"a":"Flag: Macao Sar China","b":"1F1F2-1F1F4","j":["flag","macao","nation","country","banner"]},"flag-northern-mariana-islands":{"a":"Flag: Northern Mariana Islands","b":"1F1F2-1F1F5","j":["flag","northern","mariana","islands","nation","country","banner"]},"flag-martinique":{"a":"Flag: Martinique","b":"1F1F2-1F1F6","j":["flag","mq","nation","country","banner"]},"flag-mauritania":{"a":"Flag: Mauritania","b":"1F1F2-1F1F7","j":["flag","mr","nation","country","banner"]},"flag-montserrat":{"a":"Flag: Montserrat","b":"1F1F2-1F1F8","j":["flag","ms","nation","country","banner"]},"flag-malta":{"a":"Flag: Malta","b":"1F1F2-1F1F9","j":["flag","mt","nation","country","banner"]},"flag-mauritius":{"a":"Flag: Mauritius","b":"1F1F2-1F1FA","j":["flag","mu","nation","country","banner"]},"flag-maldives":{"a":"Flag: Maldives","b":"1F1F2-1F1FB","j":["flag","mv","nation","country","banner"]},"flag-malawi":{"a":"Flag: Malawi","b":"1F1F2-1F1FC","j":["flag","mw","nation","country","banner"]},"flag-mexico":{"a":"Flag: Mexico","b":"1F1F2-1F1FD","j":["flag","mx","nation","country","banner"]},"flag-malaysia":{"a":"Flag: Malaysia","b":"1F1F2-1F1FE","j":["flag","my","nation","country","banner"]},"flag-mozambique":{"a":"Flag: Mozambique","b":"1F1F2-1F1FF","j":["flag","mz","nation","country","banner"]},"flag-namibia":{"a":"Flag: Namibia","b":"1F1F3-1F1E6","j":["flag","na","nation","country","banner"]},"flag-new-caledonia":{"a":"Flag: New Caledonia","b":"1F1F3-1F1E8","j":["flag","new","caledonia","nation","country","banner"]},"flag-niger":{"a":"Flag: Niger","b":"1F1F3-1F1EA","j":["flag","ne","nation","country","banner"]},"flag-norfolk-island":{"a":"Flag: Norfolk Island","b":"1F1F3-1F1EB","j":["flag","norfolk","island","nation","country","banner"]},"flag-nigeria":{"a":"Flag: Nigeria","b":"1F1F3-1F1EC","j":["flag","nation","country","banner"]},"flag-nicaragua":{"a":"Flag: Nicaragua","b":"1F1F3-1F1EE","j":["flag","ni","nation","country","banner"]},"flag-netherlands":{"a":"Flag: Netherlands","b":"1F1F3-1F1F1","j":["flag","nl","nation","country","banner"]},"flag-norway":{"a":"Flag: Norway","b":"1F1F3-1F1F4","j":["flag","no","nation","country","banner"]},"flag-nepal":{"a":"Flag: Nepal","b":"1F1F3-1F1F5","j":["flag","np","nation","country","banner"]},"flag-nauru":{"a":"Flag: Nauru","b":"1F1F3-1F1F7","j":["flag","nr","nation","country","banner"]},"flag-niue":{"a":"Flag: Niue","b":"1F1F3-1F1FA","j":["flag","nu","nation","country","banner"]},"flag-new-zealand":{"a":"Flag: New Zealand","b":"1F1F3-1F1FF","j":["flag","new","zealand","nation","country","banner"]},"flag-oman":{"a":"Flag: Oman","b":"1F1F4-1F1F2","j":["flag","om_symbol","nation","country","banner"]},"flag-panama":{"a":"Flag: Panama","b":"1F1F5-1F1E6","j":["flag","pa","nation","country","banner"]},"flag-peru":{"a":"Flag: Peru","b":"1F1F5-1F1EA","j":["flag","pe","nation","country","banner"]},"flag-french-polynesia":{"a":"Flag: French Polynesia","b":"1F1F5-1F1EB","j":["flag","french","polynesia","nation","country","banner"]},"flag-papua-new-guinea":{"a":"Flag: Papua New Guinea","b":"1F1F5-1F1EC","j":["flag","papua","new","guinea","nation","country","banner"]},"flag-philippines":{"a":"Flag: Philippines","b":"1F1F5-1F1ED","j":["flag","ph","nation","country","banner"]},"flag-pakistan":{"a":"Flag: Pakistan","b":"1F1F5-1F1F0","j":["flag","pk","nation","country","banner"]},"flag-poland":{"a":"Flag: Poland","b":"1F1F5-1F1F1","j":["flag","pl","nation","country","banner"]},"flag-st-pierre--miquelon":{"a":"Flag: St. Pierre & Miquelon","b":"1F1F5-1F1F2","j":["flag","flag_st_pierre_miquelon","saint","pierre","miquelon","nation","country","banner"]},"flag-pitcairn-islands":{"a":"Flag: Pitcairn Islands","b":"1F1F5-1F1F3","j":["flag","pitcairn","nation","country","banner"]},"flag-puerto-rico":{"a":"Flag: Puerto Rico","b":"1F1F5-1F1F7","j":["flag","puerto","rico","nation","country","banner"]},"flag-palestinian-territories":{"a":"Flag: Palestinian Territories","b":"1F1F5-1F1F8","j":["flag","palestine","palestinian","territories","nation","country","banner"]},"flag-portugal":{"a":"Flag: Portugal","b":"1F1F5-1F1F9","j":["flag","pt","nation","country","banner"]},"flag-palau":{"a":"Flag: Palau","b":"1F1F5-1F1FC","j":["flag","pw","nation","country","banner"]},"flag-paraguay":{"a":"Flag: Paraguay","b":"1F1F5-1F1FE","j":["flag","py","nation","country","banner"]},"flag-qatar":{"a":"Flag: Qatar","b":"1F1F6-1F1E6","j":["flag","qa","nation","country","banner"]},"flag-runion":{"a":"Flag: Réunion","b":"1F1F7-1F1EA","j":["flag","flag_reunion","réunion","nation","country","banner"]},"flag-romania":{"a":"Flag: Romania","b":"1F1F7-1F1F4","j":["flag","ro","nation","country","banner"]},"flag-serbia":{"a":"Flag: Serbia","b":"1F1F7-1F1F8","j":["flag","rs","nation","country","banner"]},"flag-russia":{"a":"Flag: Russia","b":"1F1F7-1F1FA","j":["flag","russian","federation","nation","country","banner"]},"flag-rwanda":{"a":"Flag: Rwanda","b":"1F1F7-1F1FC","j":["flag","rw","nation","country","banner"]},"flag-saudi-arabia":{"a":"Flag: Saudi Arabia","b":"1F1F8-1F1E6","j":["flag","nation","country","banner"]},"flag-solomon-islands":{"a":"Flag: Solomon Islands","b":"1F1F8-1F1E7","j":["flag","solomon","islands","nation","country","banner"]},"flag-seychelles":{"a":"Flag: Seychelles","b":"1F1F8-1F1E8","j":["flag","sc","nation","country","banner"]},"flag-sudan":{"a":"Flag: Sudan","b":"1F1F8-1F1E9","j":["flag","sd","nation","country","banner"]},"flag-sweden":{"a":"Flag: Sweden","b":"1F1F8-1F1EA","j":["flag","se","nation","country","banner"]},"flag-singapore":{"a":"Flag: Singapore","b":"1F1F8-1F1EC","j":["flag","sg","nation","country","banner"]},"flag-st-helena":{"a":"Flag: St. Helena","b":"1F1F8-1F1ED","j":["flag","saint","helena","ascension","tristan","cunha","nation","country","banner"]},"flag-slovenia":{"a":"Flag: Slovenia","b":"1F1F8-1F1EE","j":["flag","si","nation","country","banner"]},"flag-svalbard--jan-mayen":{"a":"Flag: Svalbard & Jan Mayen","b":"1F1F8-1F1EF","j":["flag","flag_svalbard_jan_mayen"]},"flag-slovakia":{"a":"Flag: Slovakia","b":"1F1F8-1F1F0","j":["flag","sk","nation","country","banner"]},"flag-sierra-leone":{"a":"Flag: Sierra Leone","b":"1F1F8-1F1F1","j":["flag","sierra","leone","nation","country","banner"]},"flag-san-marino":{"a":"Flag: San Marino","b":"1F1F8-1F1F2","j":["flag","san","marino","nation","country","banner"]},"flag-senegal":{"a":"Flag: Senegal","b":"1F1F8-1F1F3","j":["flag","sn","nation","country","banner"]},"flag-somalia":{"a":"Flag: Somalia","b":"1F1F8-1F1F4","j":["flag","so","nation","country","banner"]},"flag-suriname":{"a":"Flag: Suriname","b":"1F1F8-1F1F7","j":["flag","sr","nation","country","banner"]},"flag-south-sudan":{"a":"Flag: South Sudan","b":"1F1F8-1F1F8","j":["flag","south","sd","nation","country","banner"]},"flag-so-tom--prncipe":{"a":"Flag: São Tomé & Príncipe","b":"1F1F8-1F1F9","j":["flag","flag_sao_tome_principe","sao","tome","principe","nation","country","banner"]},"flag-el-salvador":{"a":"Flag: El Salvador","b":"1F1F8-1F1FB","j":["flag","el","salvador","nation","country","banner"]},"flag-sint-maarten":{"a":"Flag: Sint Maarten","b":"1F1F8-1F1FD","j":["flag","sint","maarten","dutch","nation","country","banner"]},"flag-syria":{"a":"Flag: Syria","b":"1F1F8-1F1FE","j":["flag","syrian","arab","republic","nation","country","banner"]},"flag-eswatini":{"a":"Flag: Eswatini","b":"1F1F8-1F1FF","j":["flag","sz","nation","country","banner"]},"flag-tristan-da-cunha":{"a":"Flag: Tristan Da Cunha","b":"1F1F9-1F1E6","j":["flag"]},"flag-turks--caicos-islands":{"a":"Flag: Turks & Caicos Islands","b":"1F1F9-1F1E8","j":["flag","flag_turks_caicos_islands","turks","caicos","islands","nation","country","banner"]},"flag-chad":{"a":"Flag: Chad","b":"1F1F9-1F1E9","j":["flag","td","nation","country","banner"]},"flag-french-southern-territories":{"a":"Flag: French Southern Territories","b":"1F1F9-1F1EB","j":["flag","french","southern","territories","nation","country","banner"]},"flag-togo":{"a":"Flag: Togo","b":"1F1F9-1F1EC","j":["flag","tg","nation","country","banner"]},"flag-thailand":{"a":"Flag: Thailand","b":"1F1F9-1F1ED","j":["flag","th","nation","country","banner"]},"flag-tajikistan":{"a":"Flag: Tajikistan","b":"1F1F9-1F1EF","j":["flag","tj","nation","country","banner"]},"flag-tokelau":{"a":"Flag: Tokelau","b":"1F1F9-1F1F0","j":["flag","tk","nation","country","banner"]},"flag-timorleste":{"a":"Flag: Timor-Leste","b":"1F1F9-1F1F1","j":["flag","flag_timor_leste","timor","leste","nation","country","banner"]},"flag-turkmenistan":{"a":"Flag: Turkmenistan","b":"1F1F9-1F1F2","j":["flag","nation","country","banner"]},"flag-tunisia":{"a":"Flag: Tunisia","b":"1F1F9-1F1F3","j":["flag","tn","nation","country","banner"]},"flag-tonga":{"a":"Flag: Tonga","b":"1F1F9-1F1F4","j":["flag","to","nation","country","banner"]},"flag-turkey":{"a":"Flag: Turkey","b":"1F1F9-1F1F7","j":["flag","turkey","nation","country","banner"]},"flag-trinidad--tobago":{"a":"Flag: Trinidad & Tobago","b":"1F1F9-1F1F9","j":["flag","flag_trinidad_tobago","trinidad","tobago","nation","country","banner"]},"flag-tuvalu":{"a":"Flag: Tuvalu","b":"1F1F9-1F1FB","j":["flag","nation","country","banner"]},"flag-taiwan":{"a":"Flag: Taiwan","b":"1F1F9-1F1FC","j":["flag","tw","nation","country","banner"]},"flag-tanzania":{"a":"Flag: Tanzania","b":"1F1F9-1F1FF","j":["flag","tanzania","united","republic","nation","country","banner"]},"flag-ukraine":{"a":"Flag: Ukraine","b":"1F1FA-1F1E6","j":["flag","ua","nation","country","banner"]},"flag-uganda":{"a":"Flag: Uganda","b":"1F1FA-1F1EC","j":["flag","ug","nation","country","banner"]},"flag-us-outlying-islands":{"a":"Flag: U.S. Outlying Islands","b":"1F1FA-1F1F2","j":["flag","flag_u_s_outlying_islands"]},"flag-united-nations":{"a":"Flag: United Nations","b":"1F1FA-1F1F3","j":["flag","un","banner"]},"flag-united-states":{"a":"Flag: United States","b":"1F1FA-1F1F8","j":["flag","united","states","america","nation","country","banner"]},"flag-uruguay":{"a":"Flag: Uruguay","b":"1F1FA-1F1FE","j":["flag","uy","nation","country","banner"]},"flag-uzbekistan":{"a":"Flag: Uzbekistan","b":"1F1FA-1F1FF","j":["flag","uz","nation","country","banner"]},"flag-vatican-city":{"a":"Flag: Vatican City","b":"1F1FB-1F1E6","j":["flag","vatican","city","nation","country","banner"]},"flag-st-vincent--grenadines":{"a":"Flag: St. Vincent & Grenadines","b":"1F1FB-1F1E8","j":["flag","flag_st_vincent_grenadines","saint","vincent","grenadines","nation","country","banner"]},"flag-venezuela":{"a":"Flag: Venezuela","b":"1F1FB-1F1EA","j":["flag","ve","bolivarian","republic","nation","country","banner"]},"flag-british-virgin-islands":{"a":"Flag: British Virgin Islands","b":"1F1FB-1F1EC","j":["flag","british","virgin","islands","bvi","nation","country","banner"]},"flag-us-virgin-islands":{"a":"Flag: U.S. Virgin Islands","b":"1F1FB-1F1EE","j":["flag","flag_u_s_virgin_islands","virgin","islands","us","nation","country","banner"]},"flag-vietnam":{"a":"Flag: Vietnam","b":"1F1FB-1F1F3","j":["flag","viet","nam","nation","country","banner"]},"flag-vanuatu":{"a":"Flag: Vanuatu","b":"1F1FB-1F1FA","j":["flag","vu","nation","country","banner"]},"flag-wallis--futuna":{"a":"Flag: Wallis & Futuna","b":"1F1FC-1F1EB","j":["flag","flag_wallis_futuna","wallis","futuna","nation","country","banner"]},"flag-samoa":{"a":"Flag: Samoa","b":"1F1FC-1F1F8","j":["flag","ws","nation","country","banner"]},"flag-kosovo":{"a":"Flag: Kosovo","b":"1F1FD-1F1F0","j":["flag","xk","nation","country","banner"]},"flag-yemen":{"a":"Flag: Yemen","b":"1F1FE-1F1EA","j":["flag","ye","nation","country","banner"]},"flag-mayotte":{"a":"Flag: Mayotte","b":"1F1FE-1F1F9","j":["flag","yt","nation","country","banner"]},"flag-south-africa":{"a":"Flag: South Africa","b":"1F1FF-1F1E6","j":["flag","south","africa","nation","country","banner"]},"flag-zambia":{"a":"Flag: Zambia","b":"1F1FF-1F1F2","j":["flag","zm","nation","country","banner"]},"flag-zimbabwe":{"a":"Flag: Zimbabwe","b":"1F1FF-1F1FC","j":["flag","zw","nation","country","banner"]},"flag-england":{"a":"Flag: England","b":"1F3F4-E0067-E0062-E0065-E006E-E0067-E007F","j":["flag","english"]},"flag-scotland":{"a":"Flag: Scotland","b":"1F3F4-E0067-E0062-E0073-E0063-E0074-E007F","j":["flag","scottish"]},"flag-wales":{"a":"Flag: Wales","b":"1F3F4-E0067-E0062-E0077-E006C-E0073-E007F","j":["flag","welsh"]}},"aliases":{}} \ No newline at end of file +{"compressed":true,"categories":[{"id":"smileys_&_emotion","name":"Smileys & Emotion","emojis":["grinning-face","grinning-face-with-big-eyes","grinning-face-with-smiling-eyes","beaming-face-with-smiling-eyes","grinning-squinting-face","grinning-face-with-sweat","rolling-on-the-floor-laughing","face-with-tears-of-joy","slightly-smiling-face","upsidedown-face","melting-face","winking-face","smiling-face-with-smiling-eyes","smiling-face-with-halo","smiling-face-with-hearts","smiling-face-with-hearteyes","starstruck","face-blowing-a-kiss","kissing-face","smiling-face","kissing-face-with-closed-eyes","kissing-face-with-smiling-eyes","smiling-face-with-tear","face-savoring-food","face-with-tongue","winking-face-with-tongue","zany-face","squinting-face-with-tongue","moneymouth-face","smiling-face-with-open-hands","face-with-hand-over-mouth","face-with-open-eyes-and-hand-over-mouth","face-with-peeking-eye","shushing-face","thinking-face","saluting-face","zippermouth-face","face-with-raised-eyebrow","neutral-face","expressionless-face","face-without-mouth","dotted-line-face","face-in-clouds","smirking-face","unamused-face","face-with-rolling-eyes","grimacing-face","face-exhaling","lying-face","relieved-face","pensive-face","sleepy-face","drooling-face","sleeping-face","face-with-medical-mask","face-with-thermometer","face-with-headbandage","nauseated-face","face-vomiting","sneezing-face","hot-face","cold-face","woozy-face","face-with-crossedout-eyes","face-with-spiral-eyes","exploding-head","cowboy-hat-face","partying-face","disguised-face","smiling-face-with-sunglasses","nerd-face","face-with-monocle","confused-face","face-with-diagonal-mouth","worried-face","slightly-frowning-face","frowning-face","face-with-open-mouth","hushed-face","astonished-face","flushed-face","pleading-face","face-holding-back-tears","frowning-face-with-open-mouth","anguished-face","fearful-face","anxious-face-with-sweat","sad-but-relieved-face","crying-face","loudly-crying-face","face-screaming-in-fear","confounded-face","persevering-face","disappointed-face","downcast-face-with-sweat","weary-face","tired-face","yawning-face","face-with-steam-from-nose","pouting-face","angry-face","face-with-symbols-on-mouth","smiling-face-with-horns","angry-face-with-horns","skull","skull-and-crossbones","pile-of-poo","clown-face","ogre","goblin","ghost","alien","alien-monster","robot","grinning-cat","grinning-cat-with-smiling-eyes","cat-with-tears-of-joy","smiling-cat-with-hearteyes","cat-with-wry-smile","kissing-cat","weary-cat","crying-cat","pouting-cat","seenoevil-monkey","hearnoevil-monkey","speaknoevil-monkey","kiss-mark","love-letter","heart-with-arrow","heart-with-ribbon","sparkling-heart","growing-heart","beating-heart","revolving-hearts","two-hearts","heart-decoration","heart-exclamation","broken-heart","heart-on-fire","mending-heart","red-heart","orange-heart","yellow-heart","green-heart","blue-heart","purple-heart","brown-heart","black-heart","white-heart","hundred-points","anger-symbol","collision","dizzy","sweat-droplets","dashing-away","hole","bomb","speech-balloon","eye-in-speech-bubble","left-speech-bubble","right-anger-bubble","thought-balloon","zzz"]},{"id":"people_&_body","name":"People & Body","emojis":["waving-hand","raised-back-of-hand","hand-with-fingers-splayed","raised-hand","vulcan-salute","rightwards-hand","leftwards-hand","palm-down-hand","palm-up-hand","ok-hand","pinched-fingers","pinching-hand","victory-hand","crossed-fingers","hand-with-index-finger-and-thumb-crossed","loveyou-gesture","sign-of-the-horns","call-me-hand","backhand-index-pointing-left","backhand-index-pointing-right","backhand-index-pointing-up","middle-finger","backhand-index-pointing-down","index-pointing-up","index-pointing-at-the-viewer","thumbs-up","thumbs-down","raised-fist","oncoming-fist","leftfacing-fist","rightfacing-fist","clapping-hands","raising-hands","heart-hands","open-hands","palms-up-together","handshake","folded-hands","writing-hand","nail-polish","selfie","flexed-biceps","mechanical-arm","mechanical-leg","leg","foot","ear","ear-with-hearing-aid","nose","brain","anatomical-heart","lungs","tooth","bone","eyes","eye","tongue","mouth","biting-lip","baby","child","boy","girl","person","person-blond-hair","man","person-beard","man-beard","woman-beard","man-red-hair","man-curly-hair","man-white-hair","man-bald","woman","woman-red-hair","person-red-hair","woman-curly-hair","person-curly-hair","woman-white-hair","person-white-hair","woman-bald","person-bald","woman-blond-hair","man-blond-hair","older-person","old-man","old-woman","person-frowning","man-frowning","woman-frowning","person-pouting","man-pouting","woman-pouting","person-gesturing-no","man-gesturing-no","woman-gesturing-no","person-gesturing-ok","man-gesturing-ok","woman-gesturing-ok","person-tipping-hand","man-tipping-hand","woman-tipping-hand","person-raising-hand","man-raising-hand","woman-raising-hand","deaf-person","deaf-man","deaf-woman","person-bowing","man-bowing","woman-bowing","person-facepalming","man-facepalming","woman-facepalming","person-shrugging","man-shrugging","woman-shrugging","health-worker","man-health-worker","woman-health-worker","student","man-student","woman-student","teacher","man-teacher","woman-teacher","judge","man-judge","woman-judge","farmer","man-farmer","woman-farmer","cook","man-cook","woman-cook","mechanic","man-mechanic","woman-mechanic","factory-worker","man-factory-worker","woman-factory-worker","office-worker","man-office-worker","woman-office-worker","scientist","man-scientist","woman-scientist","technologist","man-technologist","woman-technologist","singer","man-singer","woman-singer","artist","man-artist","woman-artist","pilot","man-pilot","woman-pilot","astronaut","man-astronaut","woman-astronaut","firefighter","man-firefighter","woman-firefighter","police-officer","man-police-officer","woman-police-officer","detective","man-detective","woman-detective","guard","man-guard","woman-guard","ninja","construction-worker","man-construction-worker","woman-construction-worker","person-with-crown","prince","princess","person-wearing-turban","man-wearing-turban","woman-wearing-turban","person-with-skullcap","woman-with-headscarf","person-in-tuxedo","man-in-tuxedo","woman-in-tuxedo","person-with-veil","man-with-veil","woman-with-veil","pregnant-woman","pregnant-man","pregnant-person","breastfeeding","woman-feeding-baby","man-feeding-baby","person-feeding-baby","baby-angel","santa-claus","mrs-claus","mx-claus","superhero","man-superhero","woman-superhero","supervillain","man-supervillain","woman-supervillain","mage","man-mage","woman-mage","fairy","man-fairy","woman-fairy","vampire","man-vampire","woman-vampire","merperson","merman","mermaid","elf","man-elf","woman-elf","genie","man-genie","woman-genie","zombie","man-zombie","woman-zombie","troll","person-getting-massage","man-getting-massage","woman-getting-massage","person-getting-haircut","man-getting-haircut","woman-getting-haircut","person-walking","man-walking","woman-walking","person-standing","man-standing","woman-standing","person-kneeling","man-kneeling","woman-kneeling","person-with-white-cane","man-with-white-cane","woman-with-white-cane","person-in-motorized-wheelchair","man-in-motorized-wheelchair","woman-in-motorized-wheelchair","person-in-manual-wheelchair","man-in-manual-wheelchair","woman-in-manual-wheelchair","person-running","man-running","woman-running","woman-dancing","man-dancing","person-in-suit-levitating","people-with-bunny-ears","men-with-bunny-ears","women-with-bunny-ears","person-in-steamy-room","man-in-steamy-room","woman-in-steamy-room","person-climbing","man-climbing","woman-climbing","person-fencing","horse-racing","skier","snowboarder","person-golfing","man-golfing","woman-golfing","person-surfing","man-surfing","woman-surfing","person-rowing-boat","man-rowing-boat","woman-rowing-boat","person-swimming","man-swimming","woman-swimming","person-bouncing-ball","man-bouncing-ball","woman-bouncing-ball","person-lifting-weights","man-lifting-weights","woman-lifting-weights","person-biking","man-biking","woman-biking","person-mountain-biking","man-mountain-biking","woman-mountain-biking","person-cartwheeling","man-cartwheeling","woman-cartwheeling","people-wrestling","men-wrestling","women-wrestling","person-playing-water-polo","man-playing-water-polo","woman-playing-water-polo","person-playing-handball","man-playing-handball","woman-playing-handball","person-juggling","man-juggling","woman-juggling","person-in-lotus-position","man-in-lotus-position","woman-in-lotus-position","person-taking-bath","person-in-bed","people-holding-hands","women-holding-hands","woman-and-man-holding-hands","men-holding-hands","kiss","kiss-woman-man","kiss-man-man","kiss-woman-woman","couple-with-heart","couple-with-heart-woman-man","couple-with-heart-man-man","couple-with-heart-woman-woman","family","family-man-woman-boy","family-man-woman-girl","family-man-woman-girl-boy","family-man-woman-boy-boy","family-man-woman-girl-girl","family-man-man-boy","family-man-man-girl","family-man-man-girl-boy","family-man-man-boy-boy","family-man-man-girl-girl","family-woman-woman-boy","family-woman-woman-girl","family-woman-woman-girl-boy","family-woman-woman-boy-boy","family-woman-woman-girl-girl","family-man-boy","family-man-boy-boy","family-man-girl","family-man-girl-boy","family-man-girl-girl","family-woman-boy","family-woman-boy-boy","family-woman-girl","family-woman-girl-boy","family-woman-girl-girl","speaking-head","bust-in-silhouette","busts-in-silhouette","people-hugging","footprints"]},{"id":"animals_&_nature","name":"Animals & Nature","emojis":["monkey-face","monkey","gorilla","orangutan","dog-face","dog","guide-dog","service-dog","poodle","wolf","fox","raccoon","cat-face","cat","black-cat","lion","tiger-face","tiger","leopard","horse-face","horse","unicorn","zebra","deer","bison","cow-face","ox","water-buffalo","cow","pig-face","pig","boar","pig-nose","ram","ewe","goat","camel","twohump-camel","llama","giraffe","elephant","mammoth","rhinoceros","hippopotamus","mouse-face","mouse","rat","hamster","rabbit-face","rabbit","chipmunk","beaver","hedgehog","bat","bear","polar-bear","koala","panda","sloth","otter","skunk","kangaroo","badger","paw-prints","turkey","chicken","rooster","hatching-chick","baby-chick","frontfacing-baby-chick","bird","penguin","dove","eagle","duck","swan","owl","dodo","feather","flamingo","peacock","parrot","frog","crocodile","turtle","lizard","snake","dragon-face","dragon","sauropod","trex","spouting-whale","whale","dolphin","seal","fish","tropical-fish","blowfish","shark","octopus","spiral-shell","coral","snail","butterfly","bug","ant","honeybee","beetle","lady-beetle","cricket","cockroach","spider","spider-web","scorpion","mosquito","fly","worm","microbe","bouquet","cherry-blossom","white-flower","lotus","rosette","rose","wilted-flower","hibiscus","sunflower","blossom","tulip","seedling","potted-plant","evergreen-tree","deciduous-tree","palm-tree","cactus","sheaf-of-rice","herb","shamrock","four-leaf-clover","maple-leaf","fallen-leaf","leaf-fluttering-in-wind","empty-nest","nest-with-eggs"]},{"id":"food_&_drink","name":"Food & Drink","emojis":["grapes","melon","watermelon","tangerine","lemon","banana","pineapple","mango","red-apple","green-apple","pear","peach","cherries","strawberry","blueberries","kiwi-fruit","tomato","olive","coconut","avocado","eggplant","potato","carrot","ear-of-corn","hot-pepper","bell-pepper","cucumber","leafy-green","broccoli","garlic","onion","mushroom","peanuts","beans","chestnut","bread","croissant","baguette-bread","flatbread","pretzel","bagel","pancakes","waffle","cheese-wedge","meat-on-bone","poultry-leg","cut-of-meat","bacon","hamburger","french-fries","pizza","hot-dog","sandwich","taco","burrito","tamale","stuffed-flatbread","falafel","egg","cooking","shallow-pan-of-food","pot-of-food","fondue","bowl-with-spoon","green-salad","popcorn","butter","salt","canned-food","bento-box","rice-cracker","rice-ball","cooked-rice","curry-rice","steaming-bowl","spaghetti","roasted-sweet-potato","oden","sushi","fried-shrimp","fish-cake-with-swirl","moon-cake","dango","dumpling","fortune-cookie","takeout-box","crab","lobster","shrimp","squid","oyster","soft-ice-cream","shaved-ice","ice-cream","doughnut","cookie","birthday-cake","shortcake","cupcake","pie","chocolate-bar","candy","lollipop","custard","honey-pot","baby-bottle","glass-of-milk","hot-beverage","teapot","teacup-without-handle","sake","bottle-with-popping-cork","wine-glass","cocktail-glass","tropical-drink","beer-mug","clinking-beer-mugs","clinking-glasses","tumbler-glass","pouring-liquid","cup-with-straw","bubble-tea","beverage-box","mate","ice","chopsticks","fork-and-knife-with-plate","fork-and-knife","spoon","kitchen-knife","jar","amphora"]},{"id":"travel_&_places","name":"Travel & Places","emojis":["globe-showing-europeafrica","globe-showing-americas","globe-showing-asiaaustralia","globe-with-meridians","world-map","map-of-japan","compass","snowcapped-mountain","mountain","volcano","mount-fuji","camping","beach-with-umbrella","desert","desert-island","national-park","stadium","classical-building","building-construction","brick","rock","wood","hut","houses","derelict-house","house","house-with-garden","office-building","japanese-post-office","post-office","hospital","bank","hotel","love-hotel","convenience-store","school","department-store","factory","japanese-castle","castle","wedding","tokyo-tower","statue-of-liberty","church","mosque","hindu-temple","synagogue","shinto-shrine","kaaba","fountain","tent","foggy","night-with-stars","cityscape","sunrise-over-mountains","sunrise","cityscape-at-dusk","sunset","bridge-at-night","hot-springs","carousel-horse","playground-slide","ferris-wheel","roller-coaster","barber-pole","circus-tent","locomotive","railway-car","highspeed-train","bullet-train","train","metro","light-rail","station","tram","monorail","mountain-railway","tram-car","bus","oncoming-bus","trolleybus","minibus","ambulance","fire-engine","police-car","oncoming-police-car","taxi","oncoming-taxi","automobile","oncoming-automobile","sport-utility-vehicle","pickup-truck","delivery-truck","articulated-lorry","tractor","racing-car","motorcycle","motor-scooter","manual-wheelchair","motorized-wheelchair","auto-rickshaw","bicycle","kick-scooter","skateboard","roller-skate","bus-stop","motorway","railway-track","oil-drum","fuel-pump","wheel","police-car-light","horizontal-traffic-light","vertical-traffic-light","stop-sign","construction","anchor","ring-buoy","sailboat","canoe","speedboat","passenger-ship","ferry","motor-boat","ship","airplane","small-airplane","airplane-departure","airplane-arrival","parachute","seat","helicopter","suspension-railway","mountain-cableway","aerial-tramway","satellite","rocket","flying-saucer","bellhop-bell","luggage","hourglass-done","hourglass-not-done","watch","alarm-clock","stopwatch","timer-clock","mantelpiece-clock","twelve-oclock","twelvethirty","one-oclock","onethirty","two-oclock","twothirty","three-oclock","threethirty","four-oclock","fourthirty","five-oclock","fivethirty","six-oclock","sixthirty","seven-oclock","seventhirty","eight-oclock","eightthirty","nine-oclock","ninethirty","ten-oclock","tenthirty","eleven-oclock","eleventhirty","new-moon","waxing-crescent-moon","first-quarter-moon","waxing-gibbous-moon","full-moon","waning-gibbous-moon","last-quarter-moon","waning-crescent-moon","crescent-moon","new-moon-face","first-quarter-moon-face","last-quarter-moon-face","thermometer","sun","full-moon-face","sun-with-face","ringed-planet","star","glowing-star","shooting-star","milky-way","cloud","sun-behind-cloud","cloud-with-lightning-and-rain","sun-behind-small-cloud","sun-behind-large-cloud","sun-behind-rain-cloud","cloud-with-rain","cloud-with-snow","cloud-with-lightning","tornado","fog","wind-face","cyclone","rainbow","closed-umbrella","umbrella","umbrella-with-rain-drops","umbrella-on-ground","high-voltage","snowflake","snowman","snowman-without-snow","comet","fire","droplet","water-wave"]},{"id":"activities","name":"Activities","emojis":["jackolantern","christmas-tree","fireworks","sparkler","firecracker","sparkles","balloon","party-popper","confetti-ball","tanabata-tree","pine-decoration","japanese-dolls","carp-streamer","wind-chime","moon-viewing-ceremony","red-envelope","ribbon","wrapped-gift","reminder-ribbon","admission-tickets","ticket","military-medal","trophy","sports-medal","1st-place-medal","2nd-place-medal","3rd-place-medal","soccer-ball","baseball","softball","basketball","volleyball","american-football","rugby-football","tennis","flying-disc","bowling","cricket-game","field-hockey","ice-hockey","lacrosse","ping-pong","badminton","boxing-glove","martial-arts-uniform","goal-net","flag-in-hole","ice-skate","fishing-pole","diving-mask","running-shirt","skis","sled","curling-stone","bullseye","yoyo","kite","pool-8-ball","crystal-ball","magic-wand","nazar-amulet","hamsa","video-game","joystick","slot-machine","game-die","puzzle-piece","teddy-bear","piata","mirror-ball","nesting-dolls","spade-suit","heart-suit","diamond-suit","club-suit","chess-pawn","joker","mahjong-red-dragon","flower-playing-cards","performing-arts","framed-picture","artist-palette","thread","sewing-needle","yarn","knot"]},{"id":"objects","name":"Objects","emojis":["glasses","sunglasses","goggles","lab-coat","safety-vest","necktie","tshirt","jeans","scarf","gloves","coat","socks","dress","kimono","sari","onepiece-swimsuit","briefs","shorts","bikini","womans-clothes","purse","handbag","clutch-bag","shopping-bags","backpack","thong-sandal","mans-shoe","running-shoe","hiking-boot","flat-shoe","highheeled-shoe","womans-sandal","ballet-shoes","womans-boot","crown","womans-hat","top-hat","graduation-cap","billed-cap","military-helmet","rescue-workers-helmet","prayer-beads","lipstick","ring","gem-stone","muted-speaker","speaker-low-volume","speaker-medium-volume","speaker-high-volume","loudspeaker","megaphone","postal-horn","bell","bell-with-slash","musical-score","musical-note","musical-notes","studio-microphone","level-slider","control-knobs","microphone","headphone","radio","saxophone","accordion","guitar","musical-keyboard","trumpet","violin","banjo","drum","long-drum","mobile-phone","mobile-phone-with-arrow","telephone","telephone-receiver","pager","fax-machine","battery","low-battery","electric-plug","laptop","desktop-computer","printer","keyboard","computer-mouse","trackball","computer-disk","floppy-disk","optical-disk","dvd","abacus","movie-camera","film-frames","film-projector","clapper-board","television","camera","camera-with-flash","video-camera","videocassette","magnifying-glass-tilted-left","magnifying-glass-tilted-right","candle","light-bulb","flashlight","red-paper-lantern","diya-lamp","notebook-with-decorative-cover","closed-book","open-book","green-book","blue-book","orange-book","books","notebook","ledger","page-with-curl","scroll","page-facing-up","newspaper","rolledup-newspaper","bookmark-tabs","bookmark","label","money-bag","coin","yen-banknote","dollar-banknote","euro-banknote","pound-banknote","money-with-wings","credit-card","receipt","chart-increasing-with-yen","envelope","email","incoming-envelope","envelope-with-arrow","outbox-tray","inbox-tray","package","closed-mailbox-with-raised-flag","closed-mailbox-with-lowered-flag","open-mailbox-with-raised-flag","open-mailbox-with-lowered-flag","postbox","ballot-box-with-ballot","pencil","black-nib","fountain-pen","pen","paintbrush","crayon","memo","briefcase","file-folder","open-file-folder","card-index-dividers","calendar","tearoff-calendar","spiral-notepad","spiral-calendar","card-index","chart-increasing","chart-decreasing","bar-chart","clipboard","pushpin","round-pushpin","paperclip","linked-paperclips","straight-ruler","triangular-ruler","scissors","card-file-box","file-cabinet","wastebasket","locked","unlocked","locked-with-pen","locked-with-key","key","old-key","hammer","axe","pick","hammer-and-pick","hammer-and-wrench","dagger","crossed-swords","water-pistol","boomerang","bow-and-arrow","shield","carpentry-saw","wrench","screwdriver","nut-and-bolt","gear","clamp","balance-scale","white-cane","link","chains","hook","toolbox","magnet","ladder","alembic","test-tube","petri-dish","dna","microscope","telescope","satellite-antenna","syringe","drop-of-blood","pill","adhesive-bandage","crutch","stethoscope","xray","door","elevator","mirror","window","bed","couch-and-lamp","chair","toilet","plunger","shower","bathtub","mouse-trap","razor","lotion-bottle","safety-pin","broom","basket","roll-of-paper","bucket","soap","bubbles","toothbrush","sponge","fire-extinguisher","shopping-cart","cigarette","coffin","headstone","funeral-urn","moai","placard","identification-card"]},{"id":"symbols","name":"Symbols","emojis":["atm-sign","litter-in-bin-sign","potable-water","wheelchair-symbol","mens-room","womens-room","restroom","baby-symbol","water-closet","passport-control","customs","baggage-claim","left-luggage","warning","children-crossing","no-entry","prohibited","no-bicycles","no-smoking","no-littering","nonpotable-water","no-pedestrians","no-mobile-phones","no-one-under-eighteen","radioactive","biohazard","up-arrow","upright-arrow","right-arrow","downright-arrow","down-arrow","downleft-arrow","left-arrow","upleft-arrow","updown-arrow","leftright-arrow","right-arrow-curving-left","left-arrow-curving-right","right-arrow-curving-up","right-arrow-curving-down","clockwise-vertical-arrows","counterclockwise-arrows-button","back-arrow","end-arrow","on-arrow","soon-arrow","top-arrow","place-of-worship","atom-symbol","om","star-of-david","wheel-of-dharma","yin-yang","latin-cross","orthodox-cross","star-and-crescent","peace-symbol","menorah","dotted-sixpointed-star","aries","taurus","gemini","cancer","leo","virgo","libra","scorpio","sagittarius","capricorn","aquarius","pisces","ophiuchus","shuffle-tracks-button","repeat-button","repeat-single-button","play-button","fastforward-button","next-track-button","play-or-pause-button","reverse-button","fast-reverse-button","last-track-button","upwards-button","fast-up-button","downwards-button","fast-down-button","pause-button","stop-button","record-button","eject-button","cinema","dim-button","bright-button","antenna-bars","vibration-mode","mobile-phone-off","female-sign","male-sign","transgender-symbol","multiply","plus","minus","divide","heavy-equals-sign","infinity","double-exclamation-mark","exclamation-question-mark","red-question-mark","white-question-mark","white-exclamation-mark","red-exclamation-mark","wavy-dash","currency-exchange","heavy-dollar-sign","medical-symbol","recycling-symbol","fleurdelis","trident-emblem","name-badge","japanese-symbol-for-beginner","hollow-red-circle","check-mark-button","check-box-with-check","check-mark","cross-mark","cross-mark-button","curly-loop","double-curly-loop","part-alternation-mark","eightspoked-asterisk","eightpointed-star","sparkle","copyright","registered","trade-mark","keycap","keycap","keycap-0","keycap-1","keycap-2","keycap-3","keycap-4","keycap-5","keycap-6","keycap-7","keycap-8","keycap-9","keycap-10","input-latin-uppercase","input-latin-lowercase","input-numbers","input-symbols","input-latin-letters","a-button-blood-type","ab-button-blood-type","b-button-blood-type","cl-button","cool-button","free-button","information","id-button","circled-m","new-button","ng-button","o-button-blood-type","ok-button","p-button","sos-button","up-button","vs-button","japanese-here-button","japanese-service-charge-button","japanese-monthly-amount-button","japanese-not-free-of-charge-button","japanese-reserved-button","japanese-bargain-button","japanese-discount-button","japanese-free-of-charge-button","japanese-prohibited-button","japanese-acceptable-button","japanese-application-button","japanese-passing-grade-button","japanese-vacancy-button","japanese-congratulations-button","japanese-secret-button","japanese-open-for-business-button","japanese-no-vacancy-button","red-circle","orange-circle","yellow-circle","green-circle","blue-circle","purple-circle","brown-circle","black-circle","white-circle","red-square","orange-square","yellow-square","green-square","blue-square","purple-square","brown-square","black-large-square","white-large-square","black-medium-square","white-medium-square","black-mediumsmall-square","white-mediumsmall-square","black-small-square","white-small-square","large-orange-diamond","large-blue-diamond","small-orange-diamond","small-blue-diamond","red-triangle-pointed-up","red-triangle-pointed-down","diamond-with-a-dot","radio-button","white-square-button","black-square-button"]},{"id":"flags","name":"Flags","emojis":["chequered-flag","triangular-flag","crossed-flags","black-flag","white-flag","rainbow-flag","transgender-flag","pirate-flag","flag-ascension-island","flag-andorra","flag-united-arab-emirates","flag-afghanistan","flag-antigua--barbuda","flag-anguilla","flag-albania","flag-armenia","flag-angola","flag-antarctica","flag-argentina","flag-american-samoa","flag-austria","flag-australia","flag-aruba","flag-land-islands","flag-azerbaijan","flag-bosnia--herzegovina","flag-barbados","flag-bangladesh","flag-belgium","flag-burkina-faso","flag-bulgaria","flag-bahrain","flag-burundi","flag-benin","flag-st-barthlemy","flag-bermuda","flag-brunei","flag-bolivia","flag-caribbean-netherlands","flag-brazil","flag-bahamas","flag-bhutan","flag-bouvet-island","flag-botswana","flag-belarus","flag-belize","flag-canada","flag-cocos-keeling-islands","flag-congo--kinshasa","flag-central-african-republic","flag-congo--brazzaville","flag-switzerland","flag-cte-divoire","flag-cook-islands","flag-chile","flag-cameroon","flag-china","flag-colombia","flag-clipperton-island","flag-costa-rica","flag-cuba","flag-cape-verde","flag-curaao","flag-christmas-island","flag-cyprus","flag-czechia","flag-germany","flag-diego-garcia","flag-djibouti","flag-denmark","flag-dominica","flag-dominican-republic","flag-algeria","flag-ceuta--melilla","flag-ecuador","flag-estonia","flag-egypt","flag-western-sahara","flag-eritrea","flag-spain","flag-ethiopia","flag-european-union","flag-finland","flag-fiji","flag-falkland-islands","flag-micronesia","flag-faroe-islands","flag-france","flag-gabon","flag-united-kingdom","flag-grenada","flag-georgia","flag-french-guiana","flag-guernsey","flag-ghana","flag-gibraltar","flag-greenland","flag-gambia","flag-guinea","flag-guadeloupe","flag-equatorial-guinea","flag-greece","flag-south-georgia--south-sandwich-islands","flag-guatemala","flag-guam","flag-guineabissau","flag-guyana","flag-hong-kong-sar-china","flag-heard--mcdonald-islands","flag-honduras","flag-croatia","flag-haiti","flag-hungary","flag-canary-islands","flag-indonesia","flag-ireland","flag-israel","flag-isle-of-man","flag-india","flag-british-indian-ocean-territory","flag-iraq","flag-iran","flag-iceland","flag-italy","flag-jersey","flag-jamaica","flag-jordan","flag-japan","flag-kenya","flag-kyrgyzstan","flag-cambodia","flag-kiribati","flag-comoros","flag-st-kitts--nevis","flag-north-korea","flag-south-korea","flag-kuwait","flag-cayman-islands","flag-kazakhstan","flag-laos","flag-lebanon","flag-st-lucia","flag-liechtenstein","flag-sri-lanka","flag-liberia","flag-lesotho","flag-lithuania","flag-luxembourg","flag-latvia","flag-libya","flag-morocco","flag-monaco","flag-moldova","flag-montenegro","flag-st-martin","flag-madagascar","flag-marshall-islands","flag-north-macedonia","flag-mali","flag-myanmar-burma","flag-mongolia","flag-macao-sar-china","flag-northern-mariana-islands","flag-martinique","flag-mauritania","flag-montserrat","flag-malta","flag-mauritius","flag-maldives","flag-malawi","flag-mexico","flag-malaysia","flag-mozambique","flag-namibia","flag-new-caledonia","flag-niger","flag-norfolk-island","flag-nigeria","flag-nicaragua","flag-netherlands","flag-norway","flag-nepal","flag-nauru","flag-niue","flag-new-zealand","flag-oman","flag-panama","flag-peru","flag-french-polynesia","flag-papua-new-guinea","flag-philippines","flag-pakistan","flag-poland","flag-st-pierre--miquelon","flag-pitcairn-islands","flag-puerto-rico","flag-palestinian-territories","flag-portugal","flag-palau","flag-paraguay","flag-qatar","flag-runion","flag-romania","flag-serbia","flag-russia","flag-rwanda","flag-saudi-arabia","flag-solomon-islands","flag-seychelles","flag-sudan","flag-sweden","flag-singapore","flag-st-helena","flag-slovenia","flag-svalbard--jan-mayen","flag-slovakia","flag-sierra-leone","flag-san-marino","flag-senegal","flag-somalia","flag-suriname","flag-south-sudan","flag-so-tom--prncipe","flag-el-salvador","flag-sint-maarten","flag-syria","flag-eswatini","flag-tristan-da-cunha","flag-turks--caicos-islands","flag-chad","flag-french-southern-territories","flag-togo","flag-thailand","flag-tajikistan","flag-tokelau","flag-timorleste","flag-turkmenistan","flag-tunisia","flag-tonga","flag-turkey","flag-trinidad--tobago","flag-tuvalu","flag-taiwan","flag-tanzania","flag-ukraine","flag-uganda","flag-us-outlying-islands","flag-united-nations","flag-united-states","flag-uruguay","flag-uzbekistan","flag-vatican-city","flag-st-vincent--grenadines","flag-venezuela","flag-british-virgin-islands","flag-us-virgin-islands","flag-vietnam","flag-vanuatu","flag-wallis--futuna","flag-samoa","flag-kosovo","flag-yemen","flag-mayotte","flag-south-africa","flag-zambia","flag-zimbabwe","flag-england","flag-scotland","flag-wales"]}],"emojis":{"grinning-face":{"a":"Grinning Face","b":"1F600","j":["face","grin","smile","happy","joy",":D"]},"grinning-face-with-big-eyes":{"a":"Grinning Face with Big Eyes","b":"1F603","j":["face","mouth","open","smile","happy","joy","haha",":D",":)","funny"]},"grinning-face-with-smiling-eyes":{"a":"Grinning Face with Smiling Eyes","b":"1F604","j":["eye","face","mouth","open","smile","happy","joy","funny","haha","laugh","like",":D",":)"]},"beaming-face-with-smiling-eyes":{"a":"Beaming Face with Smiling Eyes","b":"1F601","j":["eye","face","grin","smile","happy","joy","kawaii"]},"grinning-squinting-face":{"a":"Grinning Squinting Face","b":"1F606","j":["face","laugh","mouth","satisfied","smile","happy","joy","lol","haha","glad","XD"]},"grinning-face-with-sweat":{"a":"Grinning Face with Sweat","b":"1F605","j":["cold","face","open","smile","sweat","hot","happy","laugh","relief"]},"rolling-on-the-floor-laughing":{"a":"Rolling on the Floor Laughing","b":"1F923","j":["face","floor","laugh","rofl","rolling","rotfl","laughing","lol","haha"]},"face-with-tears-of-joy":{"a":"Face with Tears of Joy","b":"1F602","j":["face","joy","laugh","tear","cry","tears","weep","happy","happytears","haha"]},"slightly-smiling-face":{"a":"Slightly Smiling Face","b":"1F642","j":["face","smile"]},"upsidedown-face":{"a":"Upside-Down Face","b":"1F643","j":["face","upside-down","upside_down_face","flipped","silly","smile"]},"melting-face":{"a":"⊛ Melting Face","b":"1FAE0","j":["disappear","dissolve","liquid","melt"]},"winking-face":{"a":"Winking Face","b":"1F609","j":["face","wink","happy","mischievous","secret",";)","smile","eye"]},"smiling-face-with-smiling-eyes":{"a":"Smiling Face with Smiling Eyes","b":"1F60A","j":["blush","eye","face","smile","happy","flushed","crush","embarrassed","shy","joy"]},"smiling-face-with-halo":{"a":"Smiling Face with Halo","b":"1F607","j":["angel","face","fantasy","halo","innocent","heaven"]},"smiling-face-with-hearts":{"a":"Smiling Face with Hearts","b":"1F970","j":["adore","crush","hearts","in love","face","love","like","affection","valentines","infatuation"]},"smiling-face-with-hearteyes":{"a":"Smiling Face with Heart-Eyes","b":"1F60D","j":["eye","face","love","smile","smiling face with heart-eyes","smiling_face_with_heart_eyes","like","affection","valentines","infatuation","crush","heart"]},"starstruck":{"a":"Star-Struck","b":"1F929","j":["eyes","face","grinning","star","star-struck","starry-eyed","star_struck","smile","starry"]},"face-blowing-a-kiss":{"a":"Face Blowing a Kiss","b":"1F618","j":["face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face":{"a":"Kissing Face","b":"1F617","j":["face","kiss","love","like","3","valentines","infatuation"]},"smiling-face":{"a":"Smiling Face","b":"263A","j":["face","outlined","relaxed","smile","blush","massage","happiness"]},"kissing-face-with-closed-eyes":{"a":"Kissing Face with Closed Eyes","b":"1F61A","j":["closed","eye","face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face-with-smiling-eyes":{"a":"Kissing Face with Smiling Eyes","b":"1F619","j":["eye","face","kiss","smile","affection","valentines","infatuation"]},"smiling-face-with-tear":{"a":"Smiling Face with Tear","b":"1F972","j":["grateful","proud","relieved","smiling","tear","touched","sad","cry","pretend"]},"face-savoring-food":{"a":"Face Savoring Food","b":"1F60B","j":["delicious","face","savouring","smile","yum","happy","joy","tongue","silly","yummy","nom"]},"face-with-tongue":{"a":"Face with Tongue","b":"1F61B","j":["face","tongue","prank","childish","playful","mischievous","smile"]},"winking-face-with-tongue":{"a":"Winking Face with Tongue","b":"1F61C","j":["eye","face","joke","tongue","wink","prank","childish","playful","mischievous","smile"]},"zany-face":{"a":"Zany Face","b":"1F92A","j":["eye","goofy","large","small","face","crazy"]},"squinting-face-with-tongue":{"a":"Squinting Face with Tongue","b":"1F61D","j":["eye","face","horrible","taste","tongue","prank","playful","mischievous","smile"]},"moneymouth-face":{"a":"Money-Mouth Face","b":"1F911","j":["face","money","money-mouth face","mouth","money_mouth_face","rich","dollar"]},"smiling-face-with-open-hands":{"a":"Smiling Face with Open Hands","b":"1F917","j":["face","hug","hugging","open hands","smiling face","hugging_face","smile"]},"face-with-hand-over-mouth":{"a":"Face with Hand over Mouth","b":"1F92D","j":["whoops","shock","sudden realization","surprise","face"]},"face-with-open-eyes-and-hand-over-mouth":{"a":"⊛ Face with Open Eyes and Hand over Mouth","b":"1FAE2","j":["amazement","awe","disbelief","embarrass","scared","surprise"]},"face-with-peeking-eye":{"a":"⊛ Face with Peeking Eye","b":"1FAE3","j":["captivated","peep","stare"]},"shushing-face":{"a":"Shushing Face","b":"1F92B","j":["quiet","shush","face","shhh"]},"thinking-face":{"a":"Thinking Face","b":"1F914","j":["face","thinking","hmmm","think","consider"]},"saluting-face":{"a":"⊛ Saluting Face","b":"1FAE1","j":["ok","salute","sunny","troops","yes"]},"zippermouth-face":{"a":"Zipper-Mouth Face","b":"1F910","j":["face","mouth","zipper","zipper-mouth face","zipper_mouth_face","sealed","secret"]},"face-with-raised-eyebrow":{"a":"Face with Raised Eyebrow","b":"1F928","j":["distrust","skeptic","disapproval","disbelief","mild surprise","scepticism","face","surprise"]},"neutral-face":{"a":"Neutral Face","b":"1F610","j":["deadpan","face","meh","neutral","indifference",":|"]},"expressionless-face":{"a":"Expressionless Face","b":"1F611","j":["expressionless","face","inexpressive","meh","unexpressive","indifferent","-_-","deadpan"]},"face-without-mouth":{"a":"Face Without Mouth","b":"1F636","j":["face","mouth","quiet","silent","hellokitty"]},"dotted-line-face":{"a":"⊛ Dotted Line Face","b":"1FAE5","j":["depressed","disappear","hide","introvert","invisible"]},"face-in-clouds":{"a":"Face in Clouds","b":"1F636-200D-1F32B-FE0F","j":["absentminded","face in clouds","face in the fog","head in clouds"]},"smirking-face":{"a":"Smirking Face","b":"1F60F","j":["face","smirk","smile","mean","prank","smug","sarcasm"]},"unamused-face":{"a":"Unamused Face","b":"1F612","j":["face","unamused","unhappy","indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"]},"face-with-rolling-eyes":{"a":"Face with Rolling Eyes","b":"1F644","j":["eyeroll","eyes","face","rolling","frustrated"]},"grimacing-face":{"a":"Grimacing Face","b":"1F62C","j":["face","grimace","teeth"]},"face-exhaling":{"a":"Face Exhaling","b":"1F62E-200D-1F4A8","j":["exhale","face exhaling","gasp","groan","relief","whisper","whistle"]},"lying-face":{"a":"Lying Face","b":"1F925","j":["face","lie","pinocchio"]},"relieved-face":{"a":"Relieved Face","b":"1F60C","j":["face","relieved","relaxed","phew","massage","happiness"]},"pensive-face":{"a":"Pensive Face","b":"1F614","j":["dejected","face","pensive","sad","depressed","upset"]},"sleepy-face":{"a":"Sleepy Face","b":"1F62A","j":["face","sleep","tired","rest","nap"]},"drooling-face":{"a":"Drooling Face","b":"1F924","j":["drooling","face"]},"sleeping-face":{"a":"Sleeping Face","b":"1F634","j":["face","sleep","zzz","tired","sleepy","night"]},"face-with-medical-mask":{"a":"Face with Medical Mask","b":"1F637","j":["cold","doctor","face","mask","sick","ill","disease"]},"face-with-thermometer":{"a":"Face with Thermometer","b":"1F912","j":["face","ill","sick","thermometer","temperature","cold","fever"]},"face-with-headbandage":{"a":"Face with Head-Bandage","b":"1F915","j":["bandage","face","face with head-bandage","hurt","injury","face_with_head_bandage","injured","clumsy"]},"nauseated-face":{"a":"Nauseated Face","b":"1F922","j":["face","nauseated","vomit","gross","green","sick","throw up","ill"]},"face-vomiting":{"a":"Face Vomiting","b":"1F92E","j":["puke","sick","vomit","face"]},"sneezing-face":{"a":"Sneezing Face","b":"1F927","j":["face","gesundheit","sneeze","sick","allergy"]},"hot-face":{"a":"Hot Face","b":"1F975","j":["feverish","heat stroke","hot","red-faced","sweating","face","heat","red"]},"cold-face":{"a":"Cold Face","b":"1F976","j":["blue-faced","cold","freezing","frostbite","icicles","face","blue","frozen"]},"woozy-face":{"a":"Woozy Face","b":"1F974","j":["dizzy","intoxicated","tipsy","uneven eyes","wavy mouth","face","wavy"]},"face-with-crossedout-eyes":{"a":"Face with Crossed-out Eyes","b":"1F635","j":["crossed-out eyes","dead","face","face with crossed-out eyes","knocked out","dizzy_face","spent","unconscious","xox","dizzy"]},"face-with-spiral-eyes":{"a":"Face with Spiral Eyes","b":"1F635-200D-1F4AB","j":["dizzy","face with spiral eyes","hypnotized","spiral","trouble","whoa"]},"exploding-head":{"a":"Exploding Head","b":"1F92F","j":["mind blown","shocked","face","mind","blown"]},"cowboy-hat-face":{"a":"Cowboy Hat Face","b":"1F920","j":["cowboy","cowgirl","face","hat"]},"partying-face":{"a":"Partying Face","b":"1F973","j":["celebration","hat","horn","party","face","woohoo"]},"disguised-face":{"a":"Disguised Face","b":"1F978","j":["disguise","face","glasses","incognito","nose","pretent","brows","moustache"]},"smiling-face-with-sunglasses":{"a":"Smiling Face with Sunglasses","b":"1F60E","j":["bright","cool","face","sun","sunglasses","smile","summer","beach","sunglass"]},"nerd-face":{"a":"Nerd Face","b":"1F913","j":["face","geek","nerd","nerdy","dork"]},"face-with-monocle":{"a":"Face with Monocle","b":"1F9D0","j":["stuffy","wealthy","face"]},"confused-face":{"a":"Confused Face","b":"1F615","j":["confused","face","meh","indifference","huh","weird","hmmm",":/"]},"face-with-diagonal-mouth":{"a":"⊛ Face with Diagonal Mouth","b":"1FAE4","j":["disappointed","meh","skeptical","unsure"]},"worried-face":{"a":"Worried Face","b":"1F61F","j":["face","worried","concern","nervous",":("]},"slightly-frowning-face":{"a":"Slightly Frowning Face","b":"1F641","j":["face","frown","frowning","disappointed","sad","upset"]},"frowning-face":{"a":"Frowning Face","b":"2639","j":["face","frown","sad","upset"]},"face-with-open-mouth":{"a":"Face with Open Mouth","b":"1F62E","j":["face","mouth","open","sympathy","surprise","impressed","wow","whoa",":O"]},"hushed-face":{"a":"Hushed Face","b":"1F62F","j":["face","hushed","stunned","surprised","woo","shh"]},"astonished-face":{"a":"Astonished Face","b":"1F632","j":["astonished","face","shocked","totally","xox","surprised","poisoned"]},"flushed-face":{"a":"Flushed Face","b":"1F633","j":["dazed","face","flushed","blush","shy","flattered"]},"pleading-face":{"a":"Pleading Face","b":"1F97A","j":["begging","mercy","puppy eyes","face"]},"face-holding-back-tears":{"a":"⊛ Face Holding Back Tears","b":"1F979","j":["angry","cry","proud","resist","sad"]},"frowning-face-with-open-mouth":{"a":"Frowning Face with Open Mouth","b":"1F626","j":["face","frown","mouth","open","aw","what"]},"anguished-face":{"a":"Anguished Face","b":"1F627","j":["anguished","face","stunned","nervous"]},"fearful-face":{"a":"Fearful Face","b":"1F628","j":["face","fear","fearful","scared","terrified","nervous","oops","huh"]},"anxious-face-with-sweat":{"a":"Anxious Face with Sweat","b":"1F630","j":["blue","cold","face","rushed","sweat","nervous"]},"sad-but-relieved-face":{"a":"Sad but Relieved Face","b":"1F625","j":["disappointed","face","relieved","whew","phew","sweat","nervous"]},"crying-face":{"a":"Crying Face","b":"1F622","j":["cry","face","sad","tear","tears","depressed","upset",":'("]},"loudly-crying-face":{"a":"Loudly Crying Face","b":"1F62D","j":["cry","face","sad","sob","tear","tears","upset","depressed"]},"face-screaming-in-fear":{"a":"Face Screaming in Fear","b":"1F631","j":["face","fear","munch","scared","scream","omg"]},"confounded-face":{"a":"Confounded Face","b":"1F616","j":["confounded","face","confused","sick","unwell","oops",":S"]},"persevering-face":{"a":"Persevering Face","b":"1F623","j":["face","persevere","sick","no","upset","oops"]},"disappointed-face":{"a":"Disappointed Face","b":"1F61E","j":["disappointed","face","sad","upset","depressed",":("]},"downcast-face-with-sweat":{"a":"Downcast Face with Sweat","b":"1F613","j":["cold","face","sweat","hot","sad","tired","exercise"]},"weary-face":{"a":"Weary Face","b":"1F629","j":["face","tired","weary","sleepy","sad","frustrated","upset"]},"tired-face":{"a":"Tired Face","b":"1F62B","j":["face","tired","sick","whine","upset","frustrated"]},"yawning-face":{"a":"Yawning Face","b":"1F971","j":["bored","tired","yawn","sleepy"]},"face-with-steam-from-nose":{"a":"Face with Steam From Nose","b":"1F624","j":["face","triumph","won","gas","phew","proud","pride"]},"pouting-face":{"a":"Pouting Face","b":"1F621","j":["angry","face","mad","pouting","rage","red","hate","despise"]},"angry-face":{"a":"Angry Face","b":"1F620","j":["anger","angry","face","mad","annoyed","frustrated"]},"face-with-symbols-on-mouth":{"a":"Face with Symbols on Mouth","b":"1F92C","j":["swearing","cursing","face","cussing","profanity","expletive"]},"smiling-face-with-horns":{"a":"Smiling Face with Horns","b":"1F608","j":["face","fairy tale","fantasy","horns","smile","devil"]},"angry-face-with-horns":{"a":"Angry Face with Horns","b":"1F47F","j":["demon","devil","face","fantasy","imp","angry","horns"]},"skull":{"a":"Skull","b":"1F480","j":["death","face","fairy tale","monster","dead","skeleton","creepy"]},"skull-and-crossbones":{"a":"Skull and Crossbones","b":"2620","j":["crossbones","death","face","monster","skull","poison","danger","deadly","scary","pirate","evil"]},"pile-of-poo":{"a":"Pile of Poo","b":"1F4A9","j":["dung","face","monster","poo","poop","hankey","shitface","fail","turd","shit"]},"clown-face":{"a":"Clown Face","b":"1F921","j":["clown","face"]},"ogre":{"a":"Ogre","b":"1F479","j":["creature","face","fairy tale","fantasy","monster","troll","red","mask","halloween","scary","creepy","devil","demon","japanese"]},"goblin":{"a":"Goblin","b":"1F47A","j":["creature","face","fairy tale","fantasy","monster","red","evil","mask","scary","creepy","japanese"]},"ghost":{"a":"Ghost","b":"1F47B","j":["creature","face","fairy tale","fantasy","monster","halloween","spooky","scary"]},"alien":{"a":"Alien","b":"1F47D","j":["creature","extraterrestrial","face","fantasy","ufo","UFO","paul","weird","outer_space"]},"alien-monster":{"a":"Alien Monster","b":"1F47E","j":["alien","creature","extraterrestrial","face","monster","ufo","game","arcade","play"]},"robot":{"a":"Robot","b":"1F916","j":["face","monster","computer","machine","bot"]},"grinning-cat":{"a":"Grinning Cat","b":"1F63A","j":["cat","face","grinning","mouth","open","smile","animal","cats","happy"]},"grinning-cat-with-smiling-eyes":{"a":"Grinning Cat with Smiling Eyes","b":"1F638","j":["cat","eye","face","grin","smile","animal","cats"]},"cat-with-tears-of-joy":{"a":"Cat with Tears of Joy","b":"1F639","j":["cat","face","joy","tear","animal","cats","haha","happy","tears"]},"smiling-cat-with-hearteyes":{"a":"Smiling Cat with Heart-Eyes","b":"1F63B","j":["cat","eye","face","heart","love","smile","smiling cat with heart-eyes","smiling_cat_with_heart_eyes","animal","like","affection","cats","valentines"]},"cat-with-wry-smile":{"a":"Cat with Wry Smile","b":"1F63C","j":["cat","face","ironic","smile","wry","animal","cats","smirk"]},"kissing-cat":{"a":"Kissing Cat","b":"1F63D","j":["cat","eye","face","kiss","animal","cats"]},"weary-cat":{"a":"Weary Cat","b":"1F640","j":["cat","face","oh","surprised","weary","animal","cats","munch","scared","scream"]},"crying-cat":{"a":"Crying Cat","b":"1F63F","j":["cat","cry","face","sad","tear","animal","tears","weep","cats","upset"]},"pouting-cat":{"a":"Pouting Cat","b":"1F63E","j":["cat","face","pouting","animal","cats"]},"seenoevil-monkey":{"a":"See-No-Evil Monkey","b":"1F648","j":["evil","face","forbidden","monkey","see","see-no-evil monkey","see_no_evil_monkey","animal","nature","haha"]},"hearnoevil-monkey":{"a":"Hear-No-Evil Monkey","b":"1F649","j":["evil","face","forbidden","hear","hear-no-evil monkey","monkey","hear_no_evil_monkey","animal","nature"]},"speaknoevil-monkey":{"a":"Speak-No-Evil Monkey","b":"1F64A","j":["evil","face","forbidden","monkey","speak","speak-no-evil monkey","speak_no_evil_monkey","animal","nature","omg"]},"kiss-mark":{"a":"Kiss Mark","b":"1F48B","j":["kiss","lips","face","love","like","affection","valentines"]},"love-letter":{"a":"Love Letter","b":"1F48C","j":["heart","letter","love","mail","email","like","affection","envelope","valentines"]},"heart-with-arrow":{"a":"Heart with Arrow","b":"1F498","j":["arrow","cupid","love","like","heart","affection","valentines"]},"heart-with-ribbon":{"a":"Heart with Ribbon","b":"1F49D","j":["ribbon","valentine","love","valentines"]},"sparkling-heart":{"a":"Sparkling Heart","b":"1F496","j":["excited","sparkle","love","like","affection","valentines"]},"growing-heart":{"a":"Growing Heart","b":"1F497","j":["excited","growing","nervous","pulse","like","love","affection","valentines","pink"]},"beating-heart":{"a":"Beating Heart","b":"1F493","j":["beating","heartbeat","pulsating","love","like","affection","valentines","pink","heart"]},"revolving-hearts":{"a":"Revolving Hearts","b":"1F49E","j":["revolving","love","like","affection","valentines"]},"two-hearts":{"a":"Two Hearts","b":"1F495","j":["love","like","affection","valentines","heart"]},"heart-decoration":{"a":"Heart Decoration","b":"1F49F","j":["heart","purple-square","love","like"]},"heart-exclamation":{"a":"Heart Exclamation","b":"2763","j":["exclamation","mark","punctuation","decoration","love"]},"broken-heart":{"a":"Broken Heart","b":"1F494","j":["break","broken","sad","sorry","heart","heartbreak"]},"heart-on-fire":{"a":"Heart on Fire","b":"2764-FE0F-200D-1F525","j":["burn","heart","heart on fire","love","lust","sacred heart"]},"mending-heart":{"a":"Mending Heart","b":"2764-FE0F-200D-1FA79","j":["healthier","improving","mending","mending heart","recovering","recuperating","well"]},"red-heart":{"a":"Red Heart","b":"2764","j":["heart","love","like","valentines"]},"orange-heart":{"a":"Orange Heart","b":"1F9E1","j":["orange","love","like","affection","valentines"]},"yellow-heart":{"a":"Yellow Heart","b":"1F49B","j":["yellow","love","like","affection","valentines"]},"green-heart":{"a":"Green Heart","b":"1F49A","j":["green","love","like","affection","valentines"]},"blue-heart":{"a":"Blue Heart","b":"1F499","j":["blue","love","like","affection","valentines"]},"purple-heart":{"a":"Purple Heart","b":"1F49C","j":["purple","love","like","affection","valentines"]},"brown-heart":{"a":"Brown Heart","b":"1F90E","j":["brown","heart","coffee"]},"black-heart":{"a":"Black Heart","b":"1F5A4","j":["black","evil","wicked"]},"white-heart":{"a":"White Heart","b":"1F90D","j":["heart","white","pure"]},"hundred-points":{"a":"Hundred Points","b":"1F4AF","j":["100","full","hundred","score","perfect","numbers","century","exam","quiz","test","pass"]},"anger-symbol":{"a":"Anger Symbol","b":"1F4A2","j":["angry","comic","mad"]},"collision":{"a":"Collision","b":"1F4A5","j":["boom","comic","bomb","explode","explosion","blown"]},"dizzy":{"a":"Dizzy","b":"1F4AB","j":["comic","star","sparkle","shoot","magic"]},"sweat-droplets":{"a":"Sweat Droplets","b":"1F4A6","j":["comic","splashing","sweat","water","drip","oops"]},"dashing-away":{"a":"Dashing Away","b":"1F4A8","j":["comic","dash","running","wind","air","fast","shoo","fart","smoke","puff"]},"hole":{"a":"Hole","b":"1F573","j":["embarrassing"]},"bomb":{"a":"Bomb","b":"1F4A3","j":["comic","boom","explode","explosion","terrorism"]},"speech-balloon":{"a":"Speech Balloon","b":"1F4AC","j":["balloon","bubble","comic","dialog","speech","words","message","talk","chatting"]},"eye-in-speech-bubble":{"a":"Eye in Speech Bubble","b":"1F441-FE0F-200D-1F5E8-FE0F","j":["eye","speech bubble","witness","info"]},"left-speech-bubble":{"a":"Left Speech Bubble","b":"1F5E8","j":["dialog","speech","words","message","talk","chatting"]},"right-anger-bubble":{"a":"Right Anger Bubble","b":"1F5EF","j":["angry","balloon","bubble","mad","caption","speech","thinking"]},"thought-balloon":{"a":"Thought Balloon","b":"1F4AD","j":["balloon","bubble","comic","thought","cloud","speech","thinking","dream"]},"zzz":{"a":"Zzz","b":"1F4A4","j":["comic","sleep","sleepy","tired","dream"]},"waving-hand":{"a":"Waving Hand","b":"1F44B","j":["hand","wave","waving","hands","gesture","goodbye","solong","farewell","hello","hi","palm"]},"raised-back-of-hand":{"a":"Raised Back of Hand","b":"1F91A","j":["backhand","raised","fingers"]},"hand-with-fingers-splayed":{"a":"Hand with Fingers Splayed","b":"1F590","j":["finger","hand","splayed","fingers","palm"]},"raised-hand":{"a":"Raised Hand","b":"270B","j":["hand","high 5","high five","fingers","stop","highfive","palm","ban"]},"vulcan-salute":{"a":"Vulcan Salute","b":"1F596","j":["finger","hand","spock","vulcan","fingers","star trek"]},"rightwards-hand":{"a":"⊛ Rightwards Hand","b":"1FAF1","j":["hand","right","rightward"]},"leftwards-hand":{"a":"⊛ Leftwards Hand","b":"1FAF2","j":["hand","left","leftward"]},"palm-down-hand":{"a":"⊛ Palm Down Hand","b":"1FAF3","j":["dismiss","drop","shoo"]},"palm-up-hand":{"a":"⊛ Palm Up Hand","b":"1FAF4","j":["beckon","catch","come","offer"]},"ok-hand":{"a":"Ok Hand","b":"1F44C","j":["hand","OK","fingers","limbs","perfect","ok","okay"]},"pinched-fingers":{"a":"Pinched Fingers","b":"1F90C","j":["fingers","hand gesture","interrogation","pinched","sarcastic","size","tiny","small"]},"pinching-hand":{"a":"Pinching Hand","b":"1F90F","j":["small amount","tiny","small","size"]},"victory-hand":{"a":"Victory Hand","b":"270C","j":["hand","v","victory","fingers","ohyeah","peace","two"]},"crossed-fingers":{"a":"Crossed Fingers","b":"1F91E","j":["cross","finger","hand","luck","good","lucky"]},"hand-with-index-finger-and-thumb-crossed":{"a":"⊛ Hand with Index Finger and Thumb Crossed","b":"1FAF0","j":["expensive","heart","love","money","snap"]},"loveyou-gesture":{"a":"Love-You Gesture","b":"1F91F","j":["hand","ILY","love-you gesture","love_you_gesture","fingers","gesture"]},"sign-of-the-horns":{"a":"Sign of the Horns","b":"1F918","j":["finger","hand","horns","rock-on","fingers","evil_eye","sign_of_horns","rock_on"]},"call-me-hand":{"a":"Call Me Hand","b":"1F919","j":["call","hand","hands","gesture"]},"backhand-index-pointing-left":{"a":"Backhand Index Pointing Left","b":"1F448","j":["backhand","finger","hand","index","point","direction","fingers","left"]},"backhand-index-pointing-right":{"a":"Backhand Index Pointing Right","b":"1F449","j":["backhand","finger","hand","index","point","fingers","direction","right"]},"backhand-index-pointing-up":{"a":"Backhand Index Pointing Up","b":"1F446","j":["backhand","finger","hand","point","up","fingers","direction"]},"middle-finger":{"a":"Middle Finger","b":"1F595","j":["finger","hand","fingers","rude","middle","flipping"]},"backhand-index-pointing-down":{"a":"Backhand Index Pointing Down","b":"1F447","j":["backhand","down","finger","hand","point","fingers","direction"]},"index-pointing-up":{"a":"Index Pointing Up","b":"261D","j":["finger","hand","index","point","up","fingers","direction"]},"index-pointing-at-the-viewer":{"a":"⊛ Index Pointing at the Viewer","b":"1FAF5","j":["point","you"]},"thumbs-up":{"a":"Thumbs Up","b":"1F44D","j":["+1","hand","thumb","up","thumbsup","yes","awesome","good","agree","accept","cool","like"]},"thumbs-down":{"a":"Thumbs Down","b":"1F44E","j":["-1","down","hand","thumb","thumbsdown","no","dislike"]},"raised-fist":{"a":"Raised Fist","b":"270A","j":["clenched","fist","hand","punch","fingers","grasp"]},"oncoming-fist":{"a":"Oncoming Fist","b":"1F44A","j":["clenched","fist","hand","punch","angry","violence","hit","attack"]},"leftfacing-fist":{"a":"Left-Facing Fist","b":"1F91B","j":["fist","left-facing fist","leftwards","left_facing_fist","hand","fistbump"]},"rightfacing-fist":{"a":"Right-Facing Fist","b":"1F91C","j":["fist","right-facing fist","rightwards","right_facing_fist","hand","fistbump"]},"clapping-hands":{"a":"Clapping Hands","b":"1F44F","j":["clap","hand","hands","praise","applause","congrats","yay"]},"raising-hands":{"a":"Raising Hands","b":"1F64C","j":["celebration","gesture","hand","hooray","raised","yea","hands"]},"heart-hands":{"a":"⊛ Heart Hands","b":"1FAF6","j":["love"]},"open-hands":{"a":"Open Hands","b":"1F450","j":["hand","open","fingers","butterfly","hands"]},"palms-up-together":{"a":"Palms Up Together","b":"1F932","j":["prayer","cupped hands","hands","gesture","cupped"]},"handshake":{"a":"Handshake","b":"1F91D","j":["agreement","hand","meeting","shake"]},"folded-hands":{"a":"Folded Hands","b":"1F64F","j":["ask","hand","high 5","high five","please","pray","thanks","hope","wish","namaste","highfive"]},"writing-hand":{"a":"Writing Hand","b":"270D","j":["hand","write","lower_left_ballpoint_pen","stationery","compose"]},"nail-polish":{"a":"Nail Polish","b":"1F485","j":["care","cosmetics","manicure","nail","polish","beauty","finger","fashion"]},"selfie":{"a":"Selfie","b":"1F933","j":["camera","phone"]},"flexed-biceps":{"a":"Flexed Biceps","b":"1F4AA","j":["biceps","comic","flex","muscle","arm","hand","summer","strong"]},"mechanical-arm":{"a":"Mechanical Arm","b":"1F9BE","j":["accessibility","prosthetic"]},"mechanical-leg":{"a":"Mechanical Leg","b":"1F9BF","j":["accessibility","prosthetic"]},"leg":{"a":"Leg","b":"1F9B5","j":["kick","limb"]},"foot":{"a":"Foot","b":"1F9B6","j":["kick","stomp"]},"ear":{"a":"Ear","b":"1F442","j":["body","face","hear","sound","listen"]},"ear-with-hearing-aid":{"a":"Ear with Hearing Aid","b":"1F9BB","j":["accessibility","hard of hearing"]},"nose":{"a":"Nose","b":"1F443","j":["body","smell","sniff"]},"brain":{"a":"Brain","b":"1F9E0","j":["intelligent","smart"]},"anatomical-heart":{"a":"Anatomical Heart","b":"1FAC0","j":["anatomical","cardiology","heart","organ","pulse","health","heartbeat"]},"lungs":{"a":"Lungs","b":"1FAC1","j":["breath","exhalation","inhalation","organ","respiration","breathe"]},"tooth":{"a":"Tooth","b":"1F9B7","j":["dentist","teeth"]},"bone":{"a":"Bone","b":"1F9B4","j":["skeleton"]},"eyes":{"a":"Eyes","b":"1F440","j":["eye","face","look","watch","stalk","peek","see"]},"eye":{"a":"Eye","b":"1F441","j":["body","face","look","see","watch","stare"]},"tongue":{"a":"Tongue","b":"1F445","j":["body","mouth","playful"]},"mouth":{"a":"Mouth","b":"1F444","j":["lips","kiss"]},"biting-lip":{"a":"⊛ Biting Lip","b":"1FAE6","j":["anxious","fear","flirting","nervous","uncomfortable","worried"]},"baby":{"a":"Baby","b":"1F476","j":["young","child","boy","girl","toddler"]},"child":{"a":"Child","b":"1F9D2","j":["gender-neutral","unspecified gender","young"]},"boy":{"a":"Boy","b":"1F466","j":["young","man","male","guy","teenager"]},"girl":{"a":"Girl","b":"1F467","j":["Virgo","young","zodiac","female","woman","teenager"]},"person":{"a":"Person","b":"1F9D1","j":["adult","gender-neutral","unspecified gender"]},"person-blond-hair":{"a":"Person: Blond Hair","b":"1F471","j":["blond","blond-haired person","hair","person: blond hair","hairstyle"]},"man":{"a":"Man","b":"1F468","j":["adult","mustache","father","dad","guy","classy","sir","moustache"]},"person-beard":{"a":"Person: Beard","b":"1F9D4","j":["beard","person","person: beard","bewhiskered","man_beard"]},"man-beard":{"a":"Man: Beard","b":"1F9D4-200D-2642-FE0F","j":["beard","man","man: beard"]},"woman-beard":{"a":"Woman: Beard","b":"1F9D4-200D-2640-FE0F","j":["beard","woman","woman: beard"]},"man-red-hair":{"a":"Man: Red Hair","b":"1F468-200D-1F9B0","j":["adult","man","red hair","hairstyle"]},"man-curly-hair":{"a":"Man: Curly Hair","b":"1F468-200D-1F9B1","j":["adult","curly hair","man","hairstyle"]},"man-white-hair":{"a":"Man: White Hair","b":"1F468-200D-1F9B3","j":["adult","man","white hair","old","elder"]},"man-bald":{"a":"Man: Bald","b":"1F468-200D-1F9B2","j":["adult","bald","man","hairless"]},"woman":{"a":"Woman","b":"1F469","j":["adult","female","girls","lady"]},"woman-red-hair":{"a":"Woman: Red Hair","b":"1F469-200D-1F9B0","j":["adult","red hair","woman","hairstyle"]},"person-red-hair":{"a":"Person: Red Hair","b":"1F9D1-200D-1F9B0","j":["adult","gender-neutral","person","red hair","unspecified gender","hairstyle"]},"woman-curly-hair":{"a":"Woman: Curly Hair","b":"1F469-200D-1F9B1","j":["adult","curly hair","woman","hairstyle"]},"person-curly-hair":{"a":"Person: Curly Hair","b":"1F9D1-200D-1F9B1","j":["adult","curly hair","gender-neutral","person","unspecified gender","hairstyle"]},"woman-white-hair":{"a":"Woman: White Hair","b":"1F469-200D-1F9B3","j":["adult","white hair","woman","old","elder"]},"person-white-hair":{"a":"Person: White Hair","b":"1F9D1-200D-1F9B3","j":["adult","gender-neutral","person","unspecified gender","white hair","elder","old"]},"woman-bald":{"a":"Woman: Bald","b":"1F469-200D-1F9B2","j":["adult","bald","woman","hairless"]},"person-bald":{"a":"Person: Bald","b":"1F9D1-200D-1F9B2","j":["adult","bald","gender-neutral","person","unspecified gender","hairless"]},"woman-blond-hair":{"a":"Woman: Blond Hair","b":"1F471-200D-2640-FE0F","j":["blond-haired woman","blonde","hair","woman","woman: blond hair","female","girl","person"]},"man-blond-hair":{"a":"Man: Blond Hair","b":"1F471-200D-2642-FE0F","j":["blond","blond-haired man","hair","man","man: blond hair","male","boy","blonde","guy","person"]},"older-person":{"a":"Older Person","b":"1F9D3","j":["adult","gender-neutral","old","unspecified gender","human","elder","senior"]},"old-man":{"a":"Old Man","b":"1F474","j":["adult","man","old","human","male","men","elder","senior"]},"old-woman":{"a":"Old Woman","b":"1F475","j":["adult","old","woman","human","female","women","lady","elder","senior"]},"person-frowning":{"a":"Person Frowning","b":"1F64D","j":["frown","gesture","worried"]},"man-frowning":{"a":"Man Frowning","b":"1F64D-200D-2642-FE0F","j":["frowning","gesture","man","male","boy","sad","depressed","discouraged","unhappy"]},"woman-frowning":{"a":"Woman Frowning","b":"1F64D-200D-2640-FE0F","j":["frowning","gesture","woman","female","girl","sad","depressed","discouraged","unhappy"]},"person-pouting":{"a":"Person Pouting","b":"1F64E","j":["gesture","pouting","upset"]},"man-pouting":{"a":"Man Pouting","b":"1F64E-200D-2642-FE0F","j":["gesture","man","pouting","male","boy"]},"woman-pouting":{"a":"Woman Pouting","b":"1F64E-200D-2640-FE0F","j":["gesture","pouting","woman","female","girl"]},"person-gesturing-no":{"a":"Person Gesturing No","b":"1F645","j":["forbidden","gesture","hand","person gesturing NO","prohibited","decline"]},"man-gesturing-no":{"a":"Man Gesturing No","b":"1F645-200D-2642-FE0F","j":["forbidden","gesture","hand","man","man gesturing NO","prohibited","male","boy","nope"]},"woman-gesturing-no":{"a":"Woman Gesturing No","b":"1F645-200D-2640-FE0F","j":["forbidden","gesture","hand","prohibited","woman","woman gesturing NO","female","girl","nope"]},"person-gesturing-ok":{"a":"Person Gesturing Ok","b":"1F646","j":["gesture","hand","OK","person gesturing OK","agree"]},"man-gesturing-ok":{"a":"Man Gesturing Ok","b":"1F646-200D-2642-FE0F","j":["gesture","hand","man","man gesturing OK","OK","men","boy","male","blue","human"]},"woman-gesturing-ok":{"a":"Woman Gesturing Ok","b":"1F646-200D-2640-FE0F","j":["gesture","hand","OK","woman","woman gesturing OK","women","girl","female","pink","human"]},"person-tipping-hand":{"a":"Person Tipping Hand","b":"1F481","j":["hand","help","information","sassy","tipping"]},"man-tipping-hand":{"a":"Man Tipping Hand","b":"1F481-200D-2642-FE0F","j":["man","sassy","tipping hand","male","boy","human","information"]},"woman-tipping-hand":{"a":"Woman Tipping Hand","b":"1F481-200D-2640-FE0F","j":["sassy","tipping hand","woman","female","girl","human","information"]},"person-raising-hand":{"a":"Person Raising Hand","b":"1F64B","j":["gesture","hand","happy","raised","question"]},"man-raising-hand":{"a":"Man Raising Hand","b":"1F64B-200D-2642-FE0F","j":["gesture","man","raising hand","male","boy"]},"woman-raising-hand":{"a":"Woman Raising Hand","b":"1F64B-200D-2640-FE0F","j":["gesture","raising hand","woman","female","girl"]},"deaf-person":{"a":"Deaf Person","b":"1F9CF","j":["accessibility","deaf","ear","hear"]},"deaf-man":{"a":"Deaf Man","b":"1F9CF-200D-2642-FE0F","j":["deaf","man","accessibility"]},"deaf-woman":{"a":"Deaf Woman","b":"1F9CF-200D-2640-FE0F","j":["deaf","woman","accessibility"]},"person-bowing":{"a":"Person Bowing","b":"1F647","j":["apology","bow","gesture","sorry","respectiful"]},"man-bowing":{"a":"Man Bowing","b":"1F647-200D-2642-FE0F","j":["apology","bowing","favor","gesture","man","sorry","male","boy"]},"woman-bowing":{"a":"Woman Bowing","b":"1F647-200D-2640-FE0F","j":["apology","bowing","favor","gesture","sorry","woman","female","girl"]},"person-facepalming":{"a":"Person Facepalming","b":"1F926","j":["disbelief","exasperation","face","palm","disappointed"]},"man-facepalming":{"a":"Man Facepalming","b":"1F926-200D-2642-FE0F","j":["disbelief","exasperation","facepalm","man","male","boy"]},"woman-facepalming":{"a":"Woman Facepalming","b":"1F926-200D-2640-FE0F","j":["disbelief","exasperation","facepalm","woman","female","girl"]},"person-shrugging":{"a":"Person Shrugging","b":"1F937","j":["doubt","ignorance","indifference","shrug","regardless"]},"man-shrugging":{"a":"Man Shrugging","b":"1F937-200D-2642-FE0F","j":["doubt","ignorance","indifference","man","shrug","male","boy","confused","indifferent"]},"woman-shrugging":{"a":"Woman Shrugging","b":"1F937-200D-2640-FE0F","j":["doubt","ignorance","indifference","shrug","woman","female","girl","confused","indifferent"]},"health-worker":{"a":"Health Worker","b":"1F9D1-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","hospital"]},"man-health-worker":{"a":"Man Health Worker","b":"1F468-200D-2695-FE0F","j":["doctor","healthcare","man","nurse","therapist","human"]},"woman-health-worker":{"a":"Woman Health Worker","b":"1F469-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","woman","human"]},"student":{"a":"Student","b":"1F9D1-200D-1F393","j":["graduate","learn"]},"man-student":{"a":"Man Student","b":"1F468-200D-1F393","j":["graduate","man","student","human"]},"woman-student":{"a":"Woman Student","b":"1F469-200D-1F393","j":["graduate","student","woman","human"]},"teacher":{"a":"Teacher","b":"1F9D1-200D-1F3EB","j":["instructor","professor"]},"man-teacher":{"a":"Man Teacher","b":"1F468-200D-1F3EB","j":["instructor","man","professor","teacher","human"]},"woman-teacher":{"a":"Woman Teacher","b":"1F469-200D-1F3EB","j":["instructor","professor","teacher","woman","human"]},"judge":{"a":"Judge","b":"1F9D1-200D-2696-FE0F","j":["justice","scales","law"]},"man-judge":{"a":"Man Judge","b":"1F468-200D-2696-FE0F","j":["judge","justice","man","scales","court","human"]},"woman-judge":{"a":"Woman Judge","b":"1F469-200D-2696-FE0F","j":["judge","justice","scales","woman","court","human"]},"farmer":{"a":"Farmer","b":"1F9D1-200D-1F33E","j":["gardener","rancher","crops"]},"man-farmer":{"a":"Man Farmer","b":"1F468-200D-1F33E","j":["farmer","gardener","man","rancher","human"]},"woman-farmer":{"a":"Woman Farmer","b":"1F469-200D-1F33E","j":["farmer","gardener","rancher","woman","human"]},"cook":{"a":"Cook","b":"1F9D1-200D-1F373","j":["chef","food","kitchen","culinary"]},"man-cook":{"a":"Man Cook","b":"1F468-200D-1F373","j":["chef","cook","man","human"]},"woman-cook":{"a":"Woman Cook","b":"1F469-200D-1F373","j":["chef","cook","woman","human"]},"mechanic":{"a":"Mechanic","b":"1F9D1-200D-1F527","j":["electrician","plumber","tradesperson","worker","technician"]},"man-mechanic":{"a":"Man Mechanic","b":"1F468-200D-1F527","j":["electrician","man","mechanic","plumber","tradesperson","human","wrench"]},"woman-mechanic":{"a":"Woman Mechanic","b":"1F469-200D-1F527","j":["electrician","mechanic","plumber","tradesperson","woman","human","wrench"]},"factory-worker":{"a":"Factory Worker","b":"1F9D1-200D-1F3ED","j":["assembly","factory","industrial","worker","labor"]},"man-factory-worker":{"a":"Man Factory Worker","b":"1F468-200D-1F3ED","j":["assembly","factory","industrial","man","worker","human"]},"woman-factory-worker":{"a":"Woman Factory Worker","b":"1F469-200D-1F3ED","j":["assembly","factory","industrial","woman","worker","human"]},"office-worker":{"a":"Office Worker","b":"1F9D1-200D-1F4BC","j":["architect","business","manager","white-collar"]},"man-office-worker":{"a":"Man Office Worker","b":"1F468-200D-1F4BC","j":["architect","business","man","manager","white-collar","human"]},"woman-office-worker":{"a":"Woman Office Worker","b":"1F469-200D-1F4BC","j":["architect","business","manager","white-collar","woman","human"]},"scientist":{"a":"Scientist","b":"1F9D1-200D-1F52C","j":["biologist","chemist","engineer","physicist","chemistry"]},"man-scientist":{"a":"Man Scientist","b":"1F468-200D-1F52C","j":["biologist","chemist","engineer","man","physicist","scientist","human"]},"woman-scientist":{"a":"Woman Scientist","b":"1F469-200D-1F52C","j":["biologist","chemist","engineer","physicist","scientist","woman","human"]},"technologist":{"a":"Technologist","b":"1F9D1-200D-1F4BB","j":["coder","developer","inventor","software","computer"]},"man-technologist":{"a":"Man Technologist","b":"1F468-200D-1F4BB","j":["coder","developer","inventor","man","software","technologist","engineer","programmer","human","laptop","computer"]},"woman-technologist":{"a":"Woman Technologist","b":"1F469-200D-1F4BB","j":["coder","developer","inventor","software","technologist","woman","engineer","programmer","human","laptop","computer"]},"singer":{"a":"Singer","b":"1F9D1-200D-1F3A4","j":["actor","entertainer","rock","star","song","artist","performer"]},"man-singer":{"a":"Man Singer","b":"1F468-200D-1F3A4","j":["actor","entertainer","man","rock","singer","star","rockstar","human"]},"woman-singer":{"a":"Woman Singer","b":"1F469-200D-1F3A4","j":["actor","entertainer","rock","singer","star","woman","rockstar","human"]},"artist":{"a":"Artist","b":"1F9D1-200D-1F3A8","j":["palette","painting","draw","creativity"]},"man-artist":{"a":"Man Artist","b":"1F468-200D-1F3A8","j":["artist","man","palette","painter","human"]},"woman-artist":{"a":"Woman Artist","b":"1F469-200D-1F3A8","j":["artist","palette","woman","painter","human"]},"pilot":{"a":"Pilot","b":"1F9D1-200D-2708-FE0F","j":["plane","fly","airplane"]},"man-pilot":{"a":"Man Pilot","b":"1F468-200D-2708-FE0F","j":["man","pilot","plane","aviator","human"]},"woman-pilot":{"a":"Woman Pilot","b":"1F469-200D-2708-FE0F","j":["pilot","plane","woman","aviator","human"]},"astronaut":{"a":"Astronaut","b":"1F9D1-200D-1F680","j":["rocket","outerspace"]},"man-astronaut":{"a":"Man Astronaut","b":"1F468-200D-1F680","j":["astronaut","man","rocket","space","human"]},"woman-astronaut":{"a":"Woman Astronaut","b":"1F469-200D-1F680","j":["astronaut","rocket","woman","space","human"]},"firefighter":{"a":"Firefighter","b":"1F9D1-200D-1F692","j":["firetruck","fire"]},"man-firefighter":{"a":"Man Firefighter","b":"1F468-200D-1F692","j":["firefighter","firetruck","man","fireman","human"]},"woman-firefighter":{"a":"Woman Firefighter","b":"1F469-200D-1F692","j":["firefighter","firetruck","woman","fireman","human"]},"police-officer":{"a":"Police Officer","b":"1F46E","j":["cop","officer","police"]},"man-police-officer":{"a":"Man Police Officer","b":"1F46E-200D-2642-FE0F","j":["cop","man","officer","police","law","legal","enforcement","arrest","911"]},"woman-police-officer":{"a":"Woman Police Officer","b":"1F46E-200D-2640-FE0F","j":["cop","officer","police","woman","law","legal","enforcement","arrest","911","female"]},"detective":{"a":"Detective","b":"1F575","j":["sleuth","spy","human"]},"man-detective":{"a":"Man Detective","b":"1F575-FE0F-200D-2642-FE0F","j":["detective","man","sleuth","spy","crime"]},"woman-detective":{"a":"Woman Detective","b":"1F575-FE0F-200D-2640-FE0F","j":["detective","sleuth","spy","woman","human","female"]},"guard":{"a":"Guard","b":"1F482","j":["protect"]},"man-guard":{"a":"Man Guard","b":"1F482-200D-2642-FE0F","j":["guard","man","uk","gb","british","male","guy","royal"]},"woman-guard":{"a":"Woman Guard","b":"1F482-200D-2640-FE0F","j":["guard","woman","uk","gb","british","female","royal"]},"ninja":{"a":"Ninja","b":"1F977","j":["fighter","hidden","stealth","ninjutsu","skills","japanese"]},"construction-worker":{"a":"Construction Worker","b":"1F477","j":["construction","hat","worker","labor","build"]},"man-construction-worker":{"a":"Man Construction Worker","b":"1F477-200D-2642-FE0F","j":["construction","man","worker","male","human","wip","guy","build","labor"]},"woman-construction-worker":{"a":"Woman Construction Worker","b":"1F477-200D-2640-FE0F","j":["construction","woman","worker","female","human","wip","build","labor"]},"person-with-crown":{"a":"⊛ Person with Crown","b":"1FAC5","j":["monarch","noble","regal","royalty"]},"prince":{"a":"Prince","b":"1F934","j":["boy","man","male","crown","royal","king"]},"princess":{"a":"Princess","b":"1F478","j":["fairy tale","fantasy","girl","woman","female","blond","crown","royal","queen"]},"person-wearing-turban":{"a":"Person Wearing Turban","b":"1F473","j":["turban","headdress"]},"man-wearing-turban":{"a":"Man Wearing Turban","b":"1F473-200D-2642-FE0F","j":["man","turban","male","indian","hinduism","arabs"]},"woman-wearing-turban":{"a":"Woman Wearing Turban","b":"1F473-200D-2640-FE0F","j":["turban","woman","female","indian","hinduism","arabs"]},"person-with-skullcap":{"a":"Person with Skullcap","b":"1F472","j":["cap","gua pi mao","hat","person","skullcap","man_with_skullcap","male","boy","chinese"]},"woman-with-headscarf":{"a":"Woman with Headscarf","b":"1F9D5","j":["headscarf","hijab","mantilla","tichel","bandana","head kerchief","female"]},"person-in-tuxedo":{"a":"Person in Tuxedo","b":"1F935","j":["groom","person","tuxedo","man_in_tuxedo","couple","marriage","wedding"]},"man-in-tuxedo":{"a":"Man in Tuxedo","b":"1F935-200D-2642-FE0F","j":["man","tuxedo","formal","fashion"]},"woman-in-tuxedo":{"a":"Woman in Tuxedo","b":"1F935-200D-2640-FE0F","j":["tuxedo","woman","formal","fashion"]},"person-with-veil":{"a":"Person with Veil","b":"1F470","j":["bride","person","veil","wedding","bride_with_veil","couple","marriage","woman"]},"man-with-veil":{"a":"Man with Veil","b":"1F470-200D-2642-FE0F","j":["man","veil","wedding","marriage"]},"woman-with-veil":{"a":"Woman with Veil","b":"1F470-200D-2640-FE0F","j":["veil","woman","wedding","marriage"]},"pregnant-woman":{"a":"Pregnant Woman","b":"1F930","j":["pregnant","woman","baby"]},"pregnant-man":{"a":"⊛ Pregnant Man","b":"1FAC3","j":["belly","bloated","full","pregnant"]},"pregnant-person":{"a":"⊛ Pregnant Person","b":"1FAC4","j":["belly","bloated","full","pregnant"]},"breastfeeding":{"a":"Breast-Feeding","b":"1F931","j":["baby","breast","breast-feeding","nursing","breast_feeding"]},"woman-feeding-baby":{"a":"Woman Feeding Baby","b":"1F469-200D-1F37C","j":["baby","feeding","nursing","woman","birth","food"]},"man-feeding-baby":{"a":"Man Feeding Baby","b":"1F468-200D-1F37C","j":["baby","feeding","man","nursing","birth","food"]},"person-feeding-baby":{"a":"Person Feeding Baby","b":"1F9D1-200D-1F37C","j":["baby","feeding","nursing","person","birth","food"]},"baby-angel":{"a":"Baby Angel","b":"1F47C","j":["angel","baby","face","fairy tale","fantasy","heaven","wings","halo"]},"santa-claus":{"a":"Santa Claus","b":"1F385","j":["celebration","Christmas","claus","father","santa","festival","man","male","xmas","father christmas"]},"mrs-claus":{"a":"Mrs. Claus","b":"1F936","j":["celebration","Christmas","claus","mother","Mrs.","woman","female","xmas","mother christmas"]},"mx-claus":{"a":"Mx Claus","b":"1F9D1-200D-1F384","j":["Claus, christmas","christmas"]},"superhero":{"a":"Superhero","b":"1F9B8","j":["good","hero","heroine","superpower","marvel"]},"man-superhero":{"a":"Man Superhero","b":"1F9B8-200D-2642-FE0F","j":["good","hero","man","superpower","male","superpowers"]},"woman-superhero":{"a":"Woman Superhero","b":"1F9B8-200D-2640-FE0F","j":["good","hero","heroine","superpower","woman","female","superpowers"]},"supervillain":{"a":"Supervillain","b":"1F9B9","j":["criminal","evil","superpower","villain","marvel"]},"man-supervillain":{"a":"Man Supervillain","b":"1F9B9-200D-2642-FE0F","j":["criminal","evil","man","superpower","villain","male","bad","hero","superpowers"]},"woman-supervillain":{"a":"Woman Supervillain","b":"1F9B9-200D-2640-FE0F","j":["criminal","evil","superpower","villain","woman","female","bad","heroine","superpowers"]},"mage":{"a":"Mage","b":"1F9D9","j":["sorcerer","sorceress","witch","wizard","magic"]},"man-mage":{"a":"Man Mage","b":"1F9D9-200D-2642-FE0F","j":["sorcerer","wizard","man","male","mage"]},"woman-mage":{"a":"Woman Mage","b":"1F9D9-200D-2640-FE0F","j":["sorceress","witch","woman","female","mage"]},"fairy":{"a":"Fairy","b":"1F9DA","j":["Oberon","Puck","Titania","wings","magical"]},"man-fairy":{"a":"Man Fairy","b":"1F9DA-200D-2642-FE0F","j":["Oberon","Puck","man","male"]},"woman-fairy":{"a":"Woman Fairy","b":"1F9DA-200D-2640-FE0F","j":["Titania","woman","female"]},"vampire":{"a":"Vampire","b":"1F9DB","j":["Dracula","undead","blood","twilight"]},"man-vampire":{"a":"Man Vampire","b":"1F9DB-200D-2642-FE0F","j":["Dracula","undead","man","male","dracula"]},"woman-vampire":{"a":"Woman Vampire","b":"1F9DB-200D-2640-FE0F","j":["undead","woman","female"]},"merperson":{"a":"Merperson","b":"1F9DC","j":["mermaid","merman","merwoman","sea"]},"merman":{"a":"Merman","b":"1F9DC-200D-2642-FE0F","j":["Triton","man","male","triton"]},"mermaid":{"a":"Mermaid","b":"1F9DC-200D-2640-FE0F","j":["merwoman","woman","female","ariel"]},"elf":{"a":"Elf","b":"1F9DD","j":["magical","LOTR style"]},"man-elf":{"a":"Man Elf","b":"1F9DD-200D-2642-FE0F","j":["magical","man","male"]},"woman-elf":{"a":"Woman Elf","b":"1F9DD-200D-2640-FE0F","j":["magical","woman","female"]},"genie":{"a":"Genie","b":"1F9DE","j":["djinn","(non-human color)","magical","wishes"]},"man-genie":{"a":"Man Genie","b":"1F9DE-200D-2642-FE0F","j":["djinn","man","male"]},"woman-genie":{"a":"Woman Genie","b":"1F9DE-200D-2640-FE0F","j":["djinn","woman","female"]},"zombie":{"a":"Zombie","b":"1F9DF","j":["undead","walking dead","(non-human color)","dead"]},"man-zombie":{"a":"Man Zombie","b":"1F9DF-200D-2642-FE0F","j":["undead","walking dead","man","male","dracula"]},"woman-zombie":{"a":"Woman Zombie","b":"1F9DF-200D-2640-FE0F","j":["undead","walking dead","woman","female"]},"troll":{"a":"⊛ Troll","b":"1F9CC","j":["fairy tale","fantasy","monster"]},"person-getting-massage":{"a":"Person Getting Massage","b":"1F486","j":["face","massage","salon","relax"]},"man-getting-massage":{"a":"Man Getting Massage","b":"1F486-200D-2642-FE0F","j":["face","man","massage","male","boy","head"]},"woman-getting-massage":{"a":"Woman Getting Massage","b":"1F486-200D-2640-FE0F","j":["face","massage","woman","female","girl","head"]},"person-getting-haircut":{"a":"Person Getting Haircut","b":"1F487","j":["barber","beauty","haircut","parlor","hairstyle"]},"man-getting-haircut":{"a":"Man Getting Haircut","b":"1F487-200D-2642-FE0F","j":["haircut","man","male","boy"]},"woman-getting-haircut":{"a":"Woman Getting Haircut","b":"1F487-200D-2640-FE0F","j":["haircut","woman","female","girl"]},"person-walking":{"a":"Person Walking","b":"1F6B6","j":["hike","walk","walking","move"]},"man-walking":{"a":"Man Walking","b":"1F6B6-200D-2642-FE0F","j":["hike","man","walk","human","feet","steps"]},"woman-walking":{"a":"Woman Walking","b":"1F6B6-200D-2640-FE0F","j":["hike","walk","woman","human","feet","steps","female"]},"person-standing":{"a":"Person Standing","b":"1F9CD","j":["stand","standing","still"]},"man-standing":{"a":"Man Standing","b":"1F9CD-200D-2642-FE0F","j":["man","standing","still"]},"woman-standing":{"a":"Woman Standing","b":"1F9CD-200D-2640-FE0F","j":["standing","woman","still"]},"person-kneeling":{"a":"Person Kneeling","b":"1F9CE","j":["kneel","kneeling","pray","respectful"]},"man-kneeling":{"a":"Man Kneeling","b":"1F9CE-200D-2642-FE0F","j":["kneeling","man","pray","respectful"]},"woman-kneeling":{"a":"Woman Kneeling","b":"1F9CE-200D-2640-FE0F","j":["kneeling","woman","respectful","pray"]},"person-with-white-cane":{"a":"Person with White Cane","b":"1F9D1-200D-1F9AF","j":["accessibility","blind","person_with_probing_cane"]},"man-with-white-cane":{"a":"Man with White Cane","b":"1F468-200D-1F9AF","j":["accessibility","blind","man","man_with_probing_cane"]},"woman-with-white-cane":{"a":"Woman with White Cane","b":"1F469-200D-1F9AF","j":["accessibility","blind","woman","woman_with_probing_cane"]},"person-in-motorized-wheelchair":{"a":"Person in Motorized Wheelchair","b":"1F9D1-200D-1F9BC","j":["accessibility","wheelchair","disability"]},"man-in-motorized-wheelchair":{"a":"Man in Motorized Wheelchair","b":"1F468-200D-1F9BC","j":["accessibility","man","wheelchair","disability"]},"woman-in-motorized-wheelchair":{"a":"Woman in Motorized Wheelchair","b":"1F469-200D-1F9BC","j":["accessibility","wheelchair","woman","disability"]},"person-in-manual-wheelchair":{"a":"Person in Manual Wheelchair","b":"1F9D1-200D-1F9BD","j":["accessibility","wheelchair","disability"]},"man-in-manual-wheelchair":{"a":"Man in Manual Wheelchair","b":"1F468-200D-1F9BD","j":["accessibility","man","wheelchair","disability"]},"woman-in-manual-wheelchair":{"a":"Woman in Manual Wheelchair","b":"1F469-200D-1F9BD","j":["accessibility","wheelchair","woman","disability"]},"person-running":{"a":"Person Running","b":"1F3C3","j":["marathon","running","move"]},"man-running":{"a":"Man Running","b":"1F3C3-200D-2642-FE0F","j":["man","marathon","racing","running","walking","exercise","race"]},"woman-running":{"a":"Woman Running","b":"1F3C3-200D-2640-FE0F","j":["marathon","racing","running","woman","walking","exercise","race","female"]},"woman-dancing":{"a":"Woman Dancing","b":"1F483","j":["dance","dancing","woman","female","girl","fun"]},"man-dancing":{"a":"Man Dancing","b":"1F57A","j":["dance","dancing","man","male","boy","fun","dancer"]},"person-in-suit-levitating":{"a":"Person in Suit Levitating","b":"1F574","j":["business","person","suit","man_in_suit_levitating","levitate","hover","jump"]},"people-with-bunny-ears":{"a":"People with Bunny Ears","b":"1F46F","j":["bunny ear","dancer","partying","perform","costume"]},"men-with-bunny-ears":{"a":"Men with Bunny Ears","b":"1F46F-200D-2642-FE0F","j":["bunny ear","dancer","men","partying","male","bunny","boys"]},"women-with-bunny-ears":{"a":"Women with Bunny Ears","b":"1F46F-200D-2640-FE0F","j":["bunny ear","dancer","partying","women","female","bunny","girls"]},"person-in-steamy-room":{"a":"Person in Steamy Room","b":"1F9D6","j":["sauna","steam room","hamam","steambath","relax","spa"]},"man-in-steamy-room":{"a":"Man in Steamy Room","b":"1F9D6-200D-2642-FE0F","j":["sauna","steam room","male","man","spa","steamroom"]},"woman-in-steamy-room":{"a":"Woman in Steamy Room","b":"1F9D6-200D-2640-FE0F","j":["sauna","steam room","female","woman","spa","steamroom"]},"person-climbing":{"a":"Person Climbing","b":"1F9D7","j":["climber","sport"]},"man-climbing":{"a":"Man Climbing","b":"1F9D7-200D-2642-FE0F","j":["climber","sports","hobby","man","male","rock"]},"woman-climbing":{"a":"Woman Climbing","b":"1F9D7-200D-2640-FE0F","j":["climber","sports","hobby","woman","female","rock"]},"person-fencing":{"a":"Person Fencing","b":"1F93A","j":["fencer","fencing","sword","sports"]},"horse-racing":{"a":"Horse Racing","b":"1F3C7","j":["horse","jockey","racehorse","racing","animal","betting","competition","gambling","luck"]},"skier":{"a":"Skier","b":"26F7","j":["ski","snow","sports","winter"]},"snowboarder":{"a":"Snowboarder","b":"1F3C2","j":["ski","snow","snowboard","sports","winter"]},"person-golfing":{"a":"Person Golfing","b":"1F3CC","j":["ball","golf","sports","business"]},"man-golfing":{"a":"Man Golfing","b":"1F3CC-FE0F-200D-2642-FE0F","j":["golf","man","sport"]},"woman-golfing":{"a":"Woman Golfing","b":"1F3CC-FE0F-200D-2640-FE0F","j":["golf","woman","sports","business","female"]},"person-surfing":{"a":"Person Surfing","b":"1F3C4","j":["surfing","sport","sea"]},"man-surfing":{"a":"Man Surfing","b":"1F3C4-200D-2642-FE0F","j":["man","surfing","sports","ocean","sea","summer","beach"]},"woman-surfing":{"a":"Woman Surfing","b":"1F3C4-200D-2640-FE0F","j":["surfing","woman","sports","ocean","sea","summer","beach","female"]},"person-rowing-boat":{"a":"Person Rowing Boat","b":"1F6A3","j":["boat","rowboat","sport","move"]},"man-rowing-boat":{"a":"Man Rowing Boat","b":"1F6A3-200D-2642-FE0F","j":["boat","man","rowboat","sports","hobby","water","ship"]},"woman-rowing-boat":{"a":"Woman Rowing Boat","b":"1F6A3-200D-2640-FE0F","j":["boat","rowboat","woman","sports","hobby","water","ship","female"]},"person-swimming":{"a":"Person Swimming","b":"1F3CA","j":["swim","sport","pool"]},"man-swimming":{"a":"Man Swimming","b":"1F3CA-200D-2642-FE0F","j":["man","swim","sports","exercise","human","athlete","water","summer"]},"woman-swimming":{"a":"Woman Swimming","b":"1F3CA-200D-2640-FE0F","j":["swim","woman","sports","exercise","human","athlete","water","summer","female"]},"person-bouncing-ball":{"a":"Person Bouncing Ball","b":"26F9","j":["ball","sports","human"]},"man-bouncing-ball":{"a":"Man Bouncing Ball","b":"26F9-FE0F-200D-2642-FE0F","j":["ball","man","sport"]},"woman-bouncing-ball":{"a":"Woman Bouncing Ball","b":"26F9-FE0F-200D-2640-FE0F","j":["ball","woman","sports","human","female"]},"person-lifting-weights":{"a":"Person Lifting Weights","b":"1F3CB","j":["lifter","weight","sports","training","exercise"]},"man-lifting-weights":{"a":"Man Lifting Weights","b":"1F3CB-FE0F-200D-2642-FE0F","j":["man","weight lifter","sport"]},"woman-lifting-weights":{"a":"Woman Lifting Weights","b":"1F3CB-FE0F-200D-2640-FE0F","j":["weight lifter","woman","sports","training","exercise","female"]},"person-biking":{"a":"Person Biking","b":"1F6B4","j":["bicycle","biking","cyclist","sport","move"]},"man-biking":{"a":"Man Biking","b":"1F6B4-200D-2642-FE0F","j":["bicycle","biking","cyclist","man","sports","bike","exercise","hipster"]},"woman-biking":{"a":"Woman Biking","b":"1F6B4-200D-2640-FE0F","j":["bicycle","biking","cyclist","woman","sports","bike","exercise","hipster","female"]},"person-mountain-biking":{"a":"Person Mountain Biking","b":"1F6B5","j":["bicycle","bicyclist","bike","cyclist","mountain","sport","move"]},"man-mountain-biking":{"a":"Man Mountain Biking","b":"1F6B5-200D-2642-FE0F","j":["bicycle","bike","cyclist","man","mountain","transportation","sports","human","race"]},"woman-mountain-biking":{"a":"Woman Mountain Biking","b":"1F6B5-200D-2640-FE0F","j":["bicycle","bike","biking","cyclist","mountain","woman","transportation","sports","human","race","female"]},"person-cartwheeling":{"a":"Person Cartwheeling","b":"1F938","j":["cartwheel","gymnastics","sport","gymnastic"]},"man-cartwheeling":{"a":"Man Cartwheeling","b":"1F938-200D-2642-FE0F","j":["cartwheel","gymnastics","man"]},"woman-cartwheeling":{"a":"Woman Cartwheeling","b":"1F938-200D-2640-FE0F","j":["cartwheel","gymnastics","woman"]},"people-wrestling":{"a":"People Wrestling","b":"1F93C","j":["wrestle","wrestler","sport"]},"men-wrestling":{"a":"Men Wrestling","b":"1F93C-200D-2642-FE0F","j":["men","wrestle","sports","wrestlers"]},"women-wrestling":{"a":"Women Wrestling","b":"1F93C-200D-2640-FE0F","j":["women","wrestle","sports","wrestlers"]},"person-playing-water-polo":{"a":"Person Playing Water Polo","b":"1F93D","j":["polo","water","sport"]},"man-playing-water-polo":{"a":"Man Playing Water Polo","b":"1F93D-200D-2642-FE0F","j":["man","water polo","sports","pool"]},"woman-playing-water-polo":{"a":"Woman Playing Water Polo","b":"1F93D-200D-2640-FE0F","j":["water polo","woman","sports","pool"]},"person-playing-handball":{"a":"Person Playing Handball","b":"1F93E","j":["ball","handball","sport"]},"man-playing-handball":{"a":"Man Playing Handball","b":"1F93E-200D-2642-FE0F","j":["handball","man","sports"]},"woman-playing-handball":{"a":"Woman Playing Handball","b":"1F93E-200D-2640-FE0F","j":["handball","woman","sports"]},"person-juggling":{"a":"Person Juggling","b":"1F939","j":["balance","juggle","multitask","skill","performance"]},"man-juggling":{"a":"Man Juggling","b":"1F939-200D-2642-FE0F","j":["juggling","man","multitask","juggle","balance","skill"]},"woman-juggling":{"a":"Woman Juggling","b":"1F939-200D-2640-FE0F","j":["juggling","multitask","woman","juggle","balance","skill"]},"person-in-lotus-position":{"a":"Person in Lotus Position","b":"1F9D8","j":["meditation","yoga","serenity","meditate"]},"man-in-lotus-position":{"a":"Man in Lotus Position","b":"1F9D8-200D-2642-FE0F","j":["meditation","yoga","man","male","serenity","zen","mindfulness"]},"woman-in-lotus-position":{"a":"Woman in Lotus Position","b":"1F9D8-200D-2640-FE0F","j":["meditation","yoga","woman","female","serenity","zen","mindfulness"]},"person-taking-bath":{"a":"Person Taking Bath","b":"1F6C0","j":["bath","bathtub","clean","shower","bathroom"]},"person-in-bed":{"a":"Person in Bed","b":"1F6CC","j":["hotel","sleep","bed","rest"]},"people-holding-hands":{"a":"People Holding Hands","b":"1F9D1-200D-1F91D-200D-1F9D1","j":["couple","hand","hold","holding hands","person","friendship"]},"women-holding-hands":{"a":"Women Holding Hands","b":"1F46D","j":["couple","hand","holding hands","women","pair","friendship","love","like","female","people","human"]},"woman-and-man-holding-hands":{"a":"Woman and Man Holding Hands","b":"1F46B","j":["couple","hand","hold","holding hands","man","woman","pair","people","human","love","date","dating","like","affection","valentines","marriage"]},"men-holding-hands":{"a":"Men Holding Hands","b":"1F46C","j":["couple","Gemini","holding hands","man","men","twins","zodiac","pair","love","like","bromance","friendship","people","human"]},"kiss":{"a":"Kiss","b":"1F48F","j":["couple","pair","valentines","love","like","dating","marriage"]},"kiss-woman-man":{"a":"Kiss: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","woman","love"]},"kiss-man-man":{"a":"Kiss: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","pair","valentines","love","like","dating","marriage"]},"kiss-woman-woman":{"a":"Kiss: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F469","j":["couple","kiss","woman","pair","valentines","love","like","dating","marriage"]},"couple-with-heart":{"a":"Couple with Heart","b":"1F491","j":["couple","love","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-man":{"a":"Couple with Heart: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","woman"]},"couple-with-heart-man-man":{"a":"Couple with Heart: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-woman":{"a":"Couple with Heart: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F469","j":["couple","couple with heart","love","woman","pair","like","affection","human","dating","valentines","marriage"]},"family":{"a":"Family","b":"1F46A","j":["home","parents","child","mom","dad","father","mother","people","human"]},"family-man-woman-boy":{"a":"Family: Man, Woman, Boy","b":"1F468-200D-1F469-200D-1F466","j":["boy","family","man","woman","love"]},"family-man-woman-girl":{"a":"Family: Man, Woman, Girl","b":"1F468-200D-1F469-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","child"]},"family-man-woman-girl-boy":{"a":"Family: Man, Woman, Girl, Boy","b":"1F468-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","man","woman","home","parents","people","human","children"]},"family-man-woman-boy-boy":{"a":"Family: Man, Woman, Boy, Boy","b":"1F468-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","man","woman","home","parents","people","human","children"]},"family-man-woman-girl-girl":{"a":"Family: Man, Woman, Girl, Girl","b":"1F468-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","children"]},"family-man-man-boy":{"a":"Family: Man, Man, Boy","b":"1F468-200D-1F468-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl":{"a":"Family: Man, Man, Girl","b":"1F468-200D-1F468-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-man-man-girl-boy":{"a":"Family: Man, Man, Girl, Boy","b":"1F468-200D-1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parents","people","human","children"]},"family-man-man-boy-boy":{"a":"Family: Man, Man, Boy, Boy","b":"1F468-200D-1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl-girl":{"a":"Family: Man, Man, Girl, Girl","b":"1F468-200D-1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-woman-woman-boy":{"a":"Family: Woman, Woman, Boy","b":"1F469-200D-1F469-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl":{"a":"Family: Woman, Woman, Girl","b":"1F469-200D-1F469-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-girl-boy":{"a":"Family: Woman, Woman, Girl, Boy","b":"1F469-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-boy-boy":{"a":"Family: Woman, Woman, Boy, Boy","b":"1F469-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl-girl":{"a":"Family: Woman, Woman, Girl, Girl","b":"1F469-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-man-boy":{"a":"Family: Man, Boy","b":"1F468-200D-1F466","j":["boy","family","man","home","parent","people","human","child"]},"family-man-boy-boy":{"a":"Family: Man, Boy, Boy","b":"1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parent","people","human","children"]},"family-man-girl":{"a":"Family: Man, Girl","b":"1F468-200D-1F467","j":["family","girl","man","home","parent","people","human","child"]},"family-man-girl-boy":{"a":"Family: Man, Girl, Boy","b":"1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parent","people","human","children"]},"family-man-girl-girl":{"a":"Family: Man, Girl, Girl","b":"1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parent","people","human","children"]},"family-woman-boy":{"a":"Family: Woman, Boy","b":"1F469-200D-1F466","j":["boy","family","woman","home","parent","people","human","child"]},"family-woman-boy-boy":{"a":"Family: Woman, Boy, Boy","b":"1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parent","people","human","children"]},"family-woman-girl":{"a":"Family: Woman, Girl","b":"1F469-200D-1F467","j":["family","girl","woman","home","parent","people","human","child"]},"family-woman-girl-boy":{"a":"Family: Woman, Girl, Boy","b":"1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parent","people","human","children"]},"family-woman-girl-girl":{"a":"Family: Woman, Girl, Girl","b":"1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parent","people","human","children"]},"speaking-head":{"a":"Speaking Head","b":"1F5E3","j":["face","head","silhouette","speak","speaking","user","person","human","sing","say","talk"]},"bust-in-silhouette":{"a":"Bust in Silhouette","b":"1F464","j":["bust","silhouette","user","person","human"]},"busts-in-silhouette":{"a":"Busts in Silhouette","b":"1F465","j":["bust","silhouette","user","person","human","group","team"]},"people-hugging":{"a":"People Hugging","b":"1FAC2","j":["goodbye","hello","hug","thanks","care"]},"footprints":{"a":"Footprints","b":"1F463","j":["clothing","footprint","print","feet","tracking","walking","beach"]},"red-hair":{"a":"Red Hair","b":"1F9B0","j":["ginger","red hair","redhead"]},"curly-hair":{"a":"Curly Hair","b":"1F9B1","j":["afro","curly","curly hair","ringlets"]},"white-hair":{"a":"White Hair","b":"1F9B3","j":["gray","hair","old","white"]},"bald":{"a":"Bald","b":"1F9B2","j":["bald","chemotherapy","hairless","no hair","shaven"]},"monkey-face":{"a":"Monkey Face","b":"1F435","j":["face","monkey","animal","nature","circus"]},"monkey":{"a":"Monkey","b":"1F412","j":["animal","nature","banana","circus"]},"gorilla":{"a":"Gorilla","b":"1F98D","j":["animal","nature","circus"]},"orangutan":{"a":"Orangutan","b":"1F9A7","j":["ape","animal"]},"dog-face":{"a":"Dog Face","b":"1F436","j":["dog","face","pet","animal","friend","nature","woof","puppy","faithful"]},"dog":{"a":"Dog","b":"1F415","j":["pet","animal","nature","friend","doge","faithful"]},"guide-dog":{"a":"Guide Dog","b":"1F9AE","j":["accessibility","blind","guide","animal"]},"service-dog":{"a":"Service Dog","b":"1F415-200D-1F9BA","j":["accessibility","assistance","dog","service","blind","animal"]},"poodle":{"a":"Poodle","b":"1F429","j":["dog","animal","101","nature","pet"]},"wolf":{"a":"Wolf","b":"1F43A","j":["face","animal","nature","wild"]},"fox":{"a":"Fox","b":"1F98A","j":["face","animal","nature"]},"raccoon":{"a":"Raccoon","b":"1F99D","j":["curious","sly","animal","nature"]},"cat-face":{"a":"Cat Face","b":"1F431","j":["cat","face","pet","animal","meow","nature","kitten"]},"cat":{"a":"Cat","b":"1F408","j":["pet","animal","meow","cats"]},"black-cat":{"a":"Black Cat","b":"1F408-200D-2B1B","j":["black","cat","unlucky","superstition","luck"]},"lion":{"a":"Lion","b":"1F981","j":["face","Leo","zodiac","animal","nature"]},"tiger-face":{"a":"Tiger Face","b":"1F42F","j":["face","tiger","animal","cat","danger","wild","nature","roar"]},"tiger":{"a":"Tiger","b":"1F405","j":["animal","nature","roar"]},"leopard":{"a":"Leopard","b":"1F406","j":["animal","nature"]},"horse-face":{"a":"Horse Face","b":"1F434","j":["face","horse","animal","brown","nature"]},"horse":{"a":"Horse","b":"1F40E","j":["equestrian","racehorse","racing","animal","gamble","luck"]},"unicorn":{"a":"Unicorn","b":"1F984","j":["face","animal","nature","mystical"]},"zebra":{"a":"Zebra","b":"1F993","j":["stripe","animal","nature","stripes","safari"]},"deer":{"a":"Deer","b":"1F98C","j":["animal","nature","horns","venison"]},"bison":{"a":"Bison","b":"1F9AC","j":["buffalo","herd","wisent","ox"]},"cow-face":{"a":"Cow Face","b":"1F42E","j":["cow","face","beef","ox","animal","nature","moo","milk"]},"ox":{"a":"Ox","b":"1F402","j":["bull","Taurus","zodiac","animal","cow","beef"]},"water-buffalo":{"a":"Water Buffalo","b":"1F403","j":["buffalo","water","animal","nature","ox","cow"]},"cow":{"a":"Cow","b":"1F404","j":["beef","ox","animal","nature","moo","milk"]},"pig-face":{"a":"Pig Face","b":"1F437","j":["face","pig","animal","oink","nature"]},"pig":{"a":"Pig","b":"1F416","j":["sow","animal","nature"]},"boar":{"a":"Boar","b":"1F417","j":["pig","animal","nature"]},"pig-nose":{"a":"Pig Nose","b":"1F43D","j":["face","nose","pig","animal","oink"]},"ram":{"a":"Ram","b":"1F40F","j":["Aries","male","sheep","zodiac","animal","nature"]},"ewe":{"a":"Ewe","b":"1F411","j":["female","sheep","animal","nature","wool","shipit"]},"goat":{"a":"Goat","b":"1F410","j":["Capricorn","zodiac","animal","nature"]},"camel":{"a":"Camel","b":"1F42A","j":["dromedary","hump","animal","hot","desert"]},"twohump-camel":{"a":"Two-Hump Camel","b":"1F42B","j":["bactrian","camel","hump","two-hump camel","two_hump_camel","animal","nature","hot","desert"]},"llama":{"a":"Llama","b":"1F999","j":["alpaca","guanaco","vicuña","wool","animal","nature"]},"giraffe":{"a":"Giraffe","b":"1F992","j":["spots","animal","nature","safari"]},"elephant":{"a":"Elephant","b":"1F418","j":["animal","nature","nose","th","circus"]},"mammoth":{"a":"Mammoth","b":"1F9A3","j":["extinction","large","tusk","woolly","elephant","tusks"]},"rhinoceros":{"a":"Rhinoceros","b":"1F98F","j":["animal","nature","horn"]},"hippopotamus":{"a":"Hippopotamus","b":"1F99B","j":["hippo","animal","nature"]},"mouse-face":{"a":"Mouse Face","b":"1F42D","j":["face","mouse","animal","nature","cheese_wedge","rodent"]},"mouse":{"a":"Mouse","b":"1F401","j":["animal","nature","rodent"]},"rat":{"a":"Rat","b":"1F400","j":["animal","mouse","rodent"]},"hamster":{"a":"Hamster","b":"1F439","j":["face","pet","animal","nature"]},"rabbit-face":{"a":"Rabbit Face","b":"1F430","j":["bunny","face","pet","rabbit","animal","nature","spring","magic"]},"rabbit":{"a":"Rabbit","b":"1F407","j":["bunny","pet","animal","nature","magic","spring"]},"chipmunk":{"a":"Chipmunk","b":"1F43F","j":["squirrel","animal","nature","rodent"]},"beaver":{"a":"Beaver","b":"1F9AB","j":["dam","animal","rodent"]},"hedgehog":{"a":"Hedgehog","b":"1F994","j":["spiny","animal","nature"]},"bat":{"a":"Bat","b":"1F987","j":["vampire","animal","nature","blind"]},"bear":{"a":"Bear","b":"1F43B","j":["face","animal","nature","wild"]},"polar-bear":{"a":"Polar Bear","b":"1F43B-200D-2744-FE0F","j":["arctic","bear","white","animal"]},"koala":{"a":"Koala","b":"1F428","j":["face","marsupial","animal","nature"]},"panda":{"a":"Panda","b":"1F43C","j":["face","animal","nature"]},"sloth":{"a":"Sloth","b":"1F9A5","j":["lazy","slow","animal"]},"otter":{"a":"Otter","b":"1F9A6","j":["fishing","playful","animal"]},"skunk":{"a":"Skunk","b":"1F9A8","j":["stink","animal"]},"kangaroo":{"a":"Kangaroo","b":"1F998","j":["Australia","joey","jump","marsupial","animal","nature","australia","hop"]},"badger":{"a":"Badger","b":"1F9A1","j":["honey badger","pester","animal","nature","honey"]},"paw-prints":{"a":"Paw Prints","b":"1F43E","j":["feet","paw","print","animal","tracking","footprints","dog","cat","pet"]},"turkey":{"a":"Turkey","b":"1F983","j":["bird","animal"]},"chicken":{"a":"Chicken","b":"1F414","j":["bird","animal","cluck","nature"]},"rooster":{"a":"Rooster","b":"1F413","j":["bird","animal","nature","chicken"]},"hatching-chick":{"a":"Hatching Chick","b":"1F423","j":["baby","bird","chick","hatching","animal","chicken","egg","born"]},"baby-chick":{"a":"Baby Chick","b":"1F424","j":["baby","bird","chick","animal","chicken"]},"frontfacing-baby-chick":{"a":"Front-Facing Baby Chick","b":"1F425","j":["baby","bird","chick","front-facing baby chick","front_facing_baby_chick","animal","chicken"]},"bird":{"a":"Bird","b":"1F426","j":["animal","nature","fly","tweet","spring"]},"penguin":{"a":"Penguin","b":"1F427","j":["bird","animal","nature"]},"dove":{"a":"Dove","b":"1F54A","j":["bird","fly","peace","animal"]},"eagle":{"a":"Eagle","b":"1F985","j":["bird","animal","nature"]},"duck":{"a":"Duck","b":"1F986","j":["bird","animal","nature","mallard"]},"swan":{"a":"Swan","b":"1F9A2","j":["bird","cygnet","ugly duckling","animal","nature"]},"owl":{"a":"Owl","b":"1F989","j":["bird","wise","animal","nature","hoot"]},"dodo":{"a":"Dodo","b":"1F9A4","j":["extinction","large","Mauritius","animal","bird"]},"feather":{"a":"Feather","b":"1FAB6","j":["bird","flight","light","plumage","fly"]},"flamingo":{"a":"Flamingo","b":"1F9A9","j":["flamboyant","tropical","animal"]},"peacock":{"a":"Peacock","b":"1F99A","j":["bird","ostentatious","peahen","proud","animal","nature"]},"parrot":{"a":"Parrot","b":"1F99C","j":["bird","pirate","talk","animal","nature"]},"frog":{"a":"Frog","b":"1F438","j":["face","animal","nature","croak","toad"]},"crocodile":{"a":"Crocodile","b":"1F40A","j":["animal","nature","reptile","lizard","alligator"]},"turtle":{"a":"Turtle","b":"1F422","j":["terrapin","tortoise","animal","slow","nature"]},"lizard":{"a":"Lizard","b":"1F98E","j":["reptile","animal","nature"]},"snake":{"a":"Snake","b":"1F40D","j":["bearer","Ophiuchus","serpent","zodiac","animal","evil","nature","hiss","python"]},"dragon-face":{"a":"Dragon Face","b":"1F432","j":["dragon","face","fairy tale","animal","myth","nature","chinese","green"]},"dragon":{"a":"Dragon","b":"1F409","j":["fairy tale","animal","myth","nature","chinese","green"]},"sauropod":{"a":"Sauropod","b":"1F995","j":["brachiosaurus","brontosaurus","diplodocus","animal","nature","dinosaur","extinct"]},"trex":{"a":"T-Rex","b":"1F996","j":["Tyrannosaurus Rex","t_rex","animal","nature","dinosaur","tyrannosaurus","extinct"]},"spouting-whale":{"a":"Spouting Whale","b":"1F433","j":["face","spouting","whale","animal","nature","sea","ocean"]},"whale":{"a":"Whale","b":"1F40B","j":["animal","nature","sea","ocean"]},"dolphin":{"a":"Dolphin","b":"1F42C","j":["flipper","animal","nature","fish","sea","ocean","fins","beach"]},"seal":{"a":"Seal","b":"1F9AD","j":["sea lion","animal","creature","sea"]},"fish":{"a":"Fish","b":"1F41F","j":["Pisces","zodiac","animal","food","nature"]},"tropical-fish":{"a":"Tropical Fish","b":"1F420","j":["fish","tropical","animal","swim","ocean","beach","nemo"]},"blowfish":{"a":"Blowfish","b":"1F421","j":["fish","animal","nature","food","sea","ocean"]},"shark":{"a":"Shark","b":"1F988","j":["fish","animal","nature","sea","ocean","jaws","fins","beach"]},"octopus":{"a":"Octopus","b":"1F419","j":["animal","creature","ocean","sea","nature","beach"]},"spiral-shell":{"a":"Spiral Shell","b":"1F41A","j":["shell","spiral","nature","sea","beach"]},"coral":{"a":"⊛ Coral","b":"1FAB8","j":["ocean","reef"]},"snail":{"a":"Snail","b":"1F40C","j":["slow","animal","shell"]},"butterfly":{"a":"Butterfly","b":"1F98B","j":["insect","pretty","animal","nature","caterpillar"]},"bug":{"a":"Bug","b":"1F41B","j":["insect","animal","nature","worm"]},"ant":{"a":"Ant","b":"1F41C","j":["insect","animal","nature","bug"]},"honeybee":{"a":"Honeybee","b":"1F41D","j":["bee","insect","animal","nature","bug","spring","honey"]},"beetle":{"a":"Beetle","b":"1FAB2","j":["bug","insect"]},"lady-beetle":{"a":"Lady Beetle","b":"1F41E","j":["beetle","insect","ladybird","ladybug","animal","nature"]},"cricket":{"a":"Cricket","b":"1F997","j":["grasshopper","Orthoptera","animal","chirp"]},"cockroach":{"a":"Cockroach","b":"1FAB3","j":["insect","pest","roach","pests"]},"spider":{"a":"Spider","b":"1F577","j":["insect","animal","arachnid"]},"spider-web":{"a":"Spider Web","b":"1F578","j":["spider","web","animal","insect","arachnid","silk"]},"scorpion":{"a":"Scorpion","b":"1F982","j":["scorpio","Scorpio","zodiac","animal","arachnid"]},"mosquito":{"a":"Mosquito","b":"1F99F","j":["disease","fever","malaria","pest","virus","animal","nature","insect"]},"fly":{"a":"Fly","b":"1FAB0","j":["disease","maggot","pest","rotting","insect"]},"worm":{"a":"Worm","b":"1FAB1","j":["annelid","earthworm","parasite","animal"]},"microbe":{"a":"Microbe","b":"1F9A0","j":["amoeba","bacteria","virus","germs"]},"bouquet":{"a":"Bouquet","b":"1F490","j":["flower","flowers","nature","spring"]},"cherry-blossom":{"a":"Cherry Blossom","b":"1F338","j":["blossom","cherry","flower","nature","plant","spring"]},"white-flower":{"a":"White Flower","b":"1F4AE","j":["flower","japanese","spring"]},"lotus":{"a":"⊛ Lotus","b":"1FAB7","j":["Buddhism","flower","Hinduism","India","purity","Vietnam"]},"rosette":{"a":"Rosette","b":"1F3F5","j":["plant","flower","decoration","military"]},"rose":{"a":"Rose","b":"1F339","j":["flower","flowers","valentines","love","spring"]},"wilted-flower":{"a":"Wilted Flower","b":"1F940","j":["flower","wilted","plant","nature"]},"hibiscus":{"a":"Hibiscus","b":"1F33A","j":["flower","plant","vegetable","flowers","beach"]},"sunflower":{"a":"Sunflower","b":"1F33B","j":["flower","sun","nature","plant","fall"]},"blossom":{"a":"Blossom","b":"1F33C","j":["flower","nature","flowers","yellow"]},"tulip":{"a":"Tulip","b":"1F337","j":["flower","flowers","plant","nature","summer","spring"]},"seedling":{"a":"Seedling","b":"1F331","j":["young","plant","nature","grass","lawn","spring"]},"potted-plant":{"a":"Potted Plant","b":"1FAB4","j":["boring","grow","house","nurturing","plant","useless","greenery"]},"evergreen-tree":{"a":"Evergreen Tree","b":"1F332","j":["tree","plant","nature"]},"deciduous-tree":{"a":"Deciduous Tree","b":"1F333","j":["deciduous","shedding","tree","plant","nature"]},"palm-tree":{"a":"Palm Tree","b":"1F334","j":["palm","tree","plant","vegetable","nature","summer","beach","mojito","tropical"]},"cactus":{"a":"Cactus","b":"1F335","j":["plant","vegetable","nature"]},"sheaf-of-rice":{"a":"Sheaf of Rice","b":"1F33E","j":["ear","grain","rice","nature","plant"]},"herb":{"a":"Herb","b":"1F33F","j":["leaf","vegetable","plant","medicine","weed","grass","lawn"]},"shamrock":{"a":"Shamrock","b":"2618","j":["plant","vegetable","nature","irish","clover"]},"four-leaf-clover":{"a":"Four Leaf Clover","b":"1F340","j":["4","clover","four","four-leaf clover","leaf","vegetable","plant","nature","lucky","irish"]},"maple-leaf":{"a":"Maple Leaf","b":"1F341","j":["falling","leaf","maple","nature","plant","vegetable","ca","fall"]},"fallen-leaf":{"a":"Fallen Leaf","b":"1F342","j":["falling","leaf","nature","plant","vegetable","leaves"]},"leaf-fluttering-in-wind":{"a":"Leaf Fluttering in Wind","b":"1F343","j":["blow","flutter","leaf","wind","nature","plant","tree","vegetable","grass","lawn","spring"]},"empty-nest":{"a":"⊛ Empty Nest","b":"1FAB9","j":["nesting"]},"nest-with-eggs":{"a":"⊛ Nest with Eggs","b":"1FABA","j":["nesting"]},"grapes":{"a":"Grapes","b":"1F347","j":["fruit","grape","food","wine"]},"melon":{"a":"Melon","b":"1F348","j":["fruit","nature","food"]},"watermelon":{"a":"Watermelon","b":"1F349","j":["fruit","food","picnic","summer"]},"tangerine":{"a":"Tangerine","b":"1F34A","j":["fruit","orange","food","nature"]},"lemon":{"a":"Lemon","b":"1F34B","j":["citrus","fruit","nature"]},"banana":{"a":"Banana","b":"1F34C","j":["fruit","food","monkey"]},"pineapple":{"a":"Pineapple","b":"1F34D","j":["fruit","nature","food"]},"mango":{"a":"Mango","b":"1F96D","j":["fruit","tropical","food"]},"red-apple":{"a":"Red Apple","b":"1F34E","j":["apple","fruit","red","mac","school"]},"green-apple":{"a":"Green Apple","b":"1F34F","j":["apple","fruit","green","nature"]},"pear":{"a":"Pear","b":"1F350","j":["fruit","nature","food"]},"peach":{"a":"Peach","b":"1F351","j":["fruit","nature","food"]},"cherries":{"a":"Cherries","b":"1F352","j":["berries","cherry","fruit","red","food"]},"strawberry":{"a":"Strawberry","b":"1F353","j":["berry","fruit","food","nature"]},"blueberries":{"a":"Blueberries","b":"1FAD0","j":["berry","bilberry","blue","blueberry","fruit"]},"kiwi-fruit":{"a":"Kiwi Fruit","b":"1F95D","j":["food","fruit","kiwi"]},"tomato":{"a":"Tomato","b":"1F345","j":["fruit","vegetable","nature","food"]},"olive":{"a":"Olive","b":"1FAD2","j":["food","fruit"]},"coconut":{"a":"Coconut","b":"1F965","j":["palm","piña colada","fruit","nature","food"]},"avocado":{"a":"Avocado","b":"1F951","j":["food","fruit"]},"eggplant":{"a":"Eggplant","b":"1F346","j":["aubergine","vegetable","nature","food"]},"potato":{"a":"Potato","b":"1F954","j":["food","vegetable","tuber","vegatable","starch"]},"carrot":{"a":"Carrot","b":"1F955","j":["food","vegetable","orange"]},"ear-of-corn":{"a":"Ear of Corn","b":"1F33D","j":["corn","ear","maize","maze","food","vegetable","plant"]},"hot-pepper":{"a":"Hot Pepper","b":"1F336","j":["hot","pepper","food","spicy","chilli","chili"]},"bell-pepper":{"a":"Bell Pepper","b":"1FAD1","j":["capsicum","pepper","vegetable","fruit","plant"]},"cucumber":{"a":"Cucumber","b":"1F952","j":["food","pickle","vegetable","fruit"]},"leafy-green":{"a":"Leafy Green","b":"1F96C","j":["bok choy","cabbage","kale","lettuce","food","vegetable","plant"]},"broccoli":{"a":"Broccoli","b":"1F966","j":["wild cabbage","fruit","food","vegetable"]},"garlic":{"a":"Garlic","b":"1F9C4","j":["flavoring","food","spice","cook"]},"onion":{"a":"Onion","b":"1F9C5","j":["flavoring","cook","food","spice"]},"mushroom":{"a":"Mushroom","b":"1F344","j":["toadstool","plant","vegetable"]},"peanuts":{"a":"Peanuts","b":"1F95C","j":["food","nut","peanut","vegetable"]},"beans":{"a":"⊛ Beans","b":"1FAD8","j":["food","kidney","legume"]},"chestnut":{"a":"Chestnut","b":"1F330","j":["plant","food","squirrel"]},"bread":{"a":"Bread","b":"1F35E","j":["loaf","food","wheat","breakfast","toast"]},"croissant":{"a":"Croissant","b":"1F950","j":["bread","breakfast","food","french","roll"]},"baguette-bread":{"a":"Baguette Bread","b":"1F956","j":["baguette","bread","food","french"]},"flatbread":{"a":"Flatbread","b":"1FAD3","j":["arepa","lavash","naan","pita","flour","food"]},"pretzel":{"a":"Pretzel","b":"1F968","j":["twisted","convoluted","food","bread"]},"bagel":{"a":"Bagel","b":"1F96F","j":["bakery","breakfast","schmear","food","bread"]},"pancakes":{"a":"Pancakes","b":"1F95E","j":["breakfast","crêpe","food","hotcake","pancake","flapjacks","hotcakes"]},"waffle":{"a":"Waffle","b":"1F9C7","j":["breakfast","indecisive","iron","food"]},"cheese-wedge":{"a":"Cheese Wedge","b":"1F9C0","j":["cheese","food","chadder"]},"meat-on-bone":{"a":"Meat on Bone","b":"1F356","j":["bone","meat","good","food","drumstick"]},"poultry-leg":{"a":"Poultry Leg","b":"1F357","j":["bone","chicken","drumstick","leg","poultry","food","meat","bird","turkey"]},"cut-of-meat":{"a":"Cut of Meat","b":"1F969","j":["chop","lambchop","porkchop","steak","food","cow","meat","cut"]},"bacon":{"a":"Bacon","b":"1F953","j":["breakfast","food","meat","pork","pig"]},"hamburger":{"a":"Hamburger","b":"1F354","j":["burger","meat","fast food","beef","cheeseburger","mcdonalds","burger king"]},"french-fries":{"a":"French Fries","b":"1F35F","j":["french","fries","chips","snack","fast food"]},"pizza":{"a":"Pizza","b":"1F355","j":["cheese","slice","food","party"]},"hot-dog":{"a":"Hot Dog","b":"1F32D","j":["frankfurter","hotdog","sausage","food"]},"sandwich":{"a":"Sandwich","b":"1F96A","j":["bread","food","lunch"]},"taco":{"a":"Taco","b":"1F32E","j":["mexican","food"]},"burrito":{"a":"Burrito","b":"1F32F","j":["mexican","wrap","food"]},"tamale":{"a":"Tamale","b":"1FAD4","j":["mexican","wrapped","food","masa"]},"stuffed-flatbread":{"a":"Stuffed Flatbread","b":"1F959","j":["falafel","flatbread","food","gyro","kebab","stuffed"]},"falafel":{"a":"Falafel","b":"1F9C6","j":["chickpea","meatball","food"]},"egg":{"a":"Egg","b":"1F95A","j":["breakfast","food","chicken"]},"cooking":{"a":"Cooking","b":"1F373","j":["breakfast","egg","frying","pan","food","kitchen"]},"shallow-pan-of-food":{"a":"Shallow Pan of Food","b":"1F958","j":["casserole","food","paella","pan","shallow","cooking"]},"pot-of-food":{"a":"Pot of Food","b":"1F372","j":["pot","stew","food","meat","soup"]},"fondue":{"a":"Fondue","b":"1FAD5","j":["cheese","chocolate","melted","pot","Swiss","food"]},"bowl-with-spoon":{"a":"Bowl with Spoon","b":"1F963","j":["breakfast","cereal","congee","oatmeal","porridge","food"]},"green-salad":{"a":"Green Salad","b":"1F957","j":["food","green","salad","healthy","lettuce"]},"popcorn":{"a":"Popcorn","b":"1F37F","j":["food","movie theater","films","snack"]},"butter":{"a":"Butter","b":"1F9C8","j":["dairy","food","cook"]},"salt":{"a":"Salt","b":"1F9C2","j":["condiment","shaker"]},"canned-food":{"a":"Canned Food","b":"1F96B","j":["can","food","soup"]},"bento-box":{"a":"Bento Box","b":"1F371","j":["bento","box","food","japanese"]},"rice-cracker":{"a":"Rice Cracker","b":"1F358","j":["cracker","rice","food","japanese"]},"rice-ball":{"a":"Rice Ball","b":"1F359","j":["ball","Japanese","rice","food","japanese"]},"cooked-rice":{"a":"Cooked Rice","b":"1F35A","j":["cooked","rice","food","china","asian"]},"curry-rice":{"a":"Curry Rice","b":"1F35B","j":["curry","rice","food","spicy","hot","indian"]},"steaming-bowl":{"a":"Steaming Bowl","b":"1F35C","j":["bowl","noodle","ramen","steaming","food","japanese","chopsticks"]},"spaghetti":{"a":"Spaghetti","b":"1F35D","j":["pasta","food","italian","noodle"]},"roasted-sweet-potato":{"a":"Roasted Sweet Potato","b":"1F360","j":["potato","roasted","sweet","food","nature"]},"oden":{"a":"Oden","b":"1F362","j":["kebab","seafood","skewer","stick","food","japanese"]},"sushi":{"a":"Sushi","b":"1F363","j":["food","fish","japanese","rice"]},"fried-shrimp":{"a":"Fried Shrimp","b":"1F364","j":["fried","prawn","shrimp","tempura","food","animal","appetizer","summer"]},"fish-cake-with-swirl":{"a":"Fish Cake with Swirl","b":"1F365","j":["cake","fish","pastry","swirl","food","japan","sea","beach","narutomaki","pink","kamaboko","surimi","ramen"]},"moon-cake":{"a":"Moon Cake","b":"1F96E","j":["autumn","festival","yuèbǐng","food"]},"dango":{"a":"Dango","b":"1F361","j":["dessert","Japanese","skewer","stick","sweet","food","japanese","barbecue","meat"]},"dumpling":{"a":"Dumpling","b":"1F95F","j":["empanada","gyōza","jiaozi","pierogi","potsticker","food"]},"fortune-cookie":{"a":"Fortune Cookie","b":"1F960","j":["prophecy","food"]},"takeout-box":{"a":"Takeout Box","b":"1F961","j":["oyster pail","food","leftovers"]},"crab":{"a":"Crab","b":"1F980","j":["Cancer","zodiac","animal","crustacean"]},"lobster":{"a":"Lobster","b":"1F99E","j":["bisque","claws","seafood","animal","nature"]},"shrimp":{"a":"Shrimp","b":"1F990","j":["food","shellfish","small","animal","ocean","nature","seafood"]},"squid":{"a":"Squid","b":"1F991","j":["food","molusc","animal","nature","ocean","sea"]},"oyster":{"a":"Oyster","b":"1F9AA","j":["diving","pearl","food"]},"soft-ice-cream":{"a":"Soft Ice Cream","b":"1F366","j":["cream","dessert","ice","icecream","soft","sweet","food","hot","summer"]},"shaved-ice":{"a":"Shaved Ice","b":"1F367","j":["dessert","ice","shaved","sweet","hot","summer"]},"ice-cream":{"a":"Ice Cream","b":"1F368","j":["cream","dessert","ice","sweet","food","hot"]},"doughnut":{"a":"Doughnut","b":"1F369","j":["breakfast","dessert","donut","sweet","food","snack"]},"cookie":{"a":"Cookie","b":"1F36A","j":["dessert","sweet","food","snack","oreo","chocolate"]},"birthday-cake":{"a":"Birthday Cake","b":"1F382","j":["birthday","cake","celebration","dessert","pastry","sweet","food"]},"shortcake":{"a":"Shortcake","b":"1F370","j":["cake","dessert","pastry","slice","sweet","food"]},"cupcake":{"a":"Cupcake","b":"1F9C1","j":["bakery","sweet","food","dessert"]},"pie":{"a":"Pie","b":"1F967","j":["filling","pastry","fruit","meat","food","dessert"]},"chocolate-bar":{"a":"Chocolate Bar","b":"1F36B","j":["bar","chocolate","dessert","sweet","food","snack"]},"candy":{"a":"Candy","b":"1F36C","j":["dessert","sweet","snack","lolly"]},"lollipop":{"a":"Lollipop","b":"1F36D","j":["candy","dessert","sweet","food","snack"]},"custard":{"a":"Custard","b":"1F36E","j":["dessert","pudding","sweet","food"]},"honey-pot":{"a":"Honey Pot","b":"1F36F","j":["honey","honeypot","pot","sweet","bees","kitchen"]},"baby-bottle":{"a":"Baby Bottle","b":"1F37C","j":["baby","bottle","drink","milk","food","container"]},"glass-of-milk":{"a":"Glass of Milk","b":"1F95B","j":["drink","glass","milk","beverage","cow"]},"hot-beverage":{"a":"Hot Beverage","b":"2615","j":["beverage","coffee","drink","hot","steaming","tea","caffeine","latte","espresso"]},"teapot":{"a":"Teapot","b":"1FAD6","j":["drink","pot","tea","hot"]},"teacup-without-handle":{"a":"Teacup Without Handle","b":"1F375","j":["beverage","cup","drink","tea","teacup","bowl","breakfast","green","british"]},"sake":{"a":"Sake","b":"1F376","j":["bar","beverage","bottle","cup","drink","wine","drunk","japanese","alcohol","booze"]},"bottle-with-popping-cork":{"a":"Bottle with Popping Cork","b":"1F37E","j":["bar","bottle","cork","drink","popping","wine","celebration"]},"wine-glass":{"a":"Wine Glass","b":"1F377","j":["bar","beverage","drink","glass","wine","drunk","alcohol","booze"]},"cocktail-glass":{"a":"Cocktail Glass","b":"1F378","j":["bar","cocktail","drink","glass","drunk","alcohol","beverage","booze","mojito"]},"tropical-drink":{"a":"Tropical Drink","b":"1F379","j":["bar","drink","tropical","beverage","cocktail","summer","beach","alcohol","booze","mojito"]},"beer-mug":{"a":"Beer Mug","b":"1F37A","j":["bar","beer","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-beer-mugs":{"a":"Clinking Beer Mugs","b":"1F37B","j":["bar","beer","clink","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-glasses":{"a":"Clinking Glasses","b":"1F942","j":["celebrate","clink","drink","glass","beverage","party","alcohol","cheers","wine","champagne","toast"]},"tumbler-glass":{"a":"Tumbler Glass","b":"1F943","j":["glass","liquor","shot","tumbler","whisky","drink","beverage","drunk","alcohol","booze","bourbon","scotch"]},"pouring-liquid":{"a":"⊛ Pouring Liquid","b":"1FAD7","j":["drink","empty","glass","spill"]},"cup-with-straw":{"a":"Cup with Straw","b":"1F964","j":["juice","soda","malt","soft drink","water","drink"]},"bubble-tea":{"a":"Bubble Tea","b":"1F9CB","j":["bubble","milk","pearl","tea","taiwan","boba","milk tea","straw"]},"beverage-box":{"a":"Beverage Box","b":"1F9C3","j":["beverage","box","juice","straw","sweet","drink"]},"mate":{"a":"Mate","b":"1F9C9","j":["drink","tea","beverage"]},"ice":{"a":"Ice","b":"1F9CA","j":["cold","ice cube","iceberg","water"]},"chopsticks":{"a":"Chopsticks","b":"1F962","j":["hashi","jeotgarak","kuaizi","food"]},"fork-and-knife-with-plate":{"a":"Fork and Knife with Plate","b":"1F37D","j":["cooking","fork","knife","plate","food","eat","meal","lunch","dinner","restaurant"]},"fork-and-knife":{"a":"Fork and Knife","b":"1F374","j":["cooking","cutlery","fork","knife","kitchen"]},"spoon":{"a":"Spoon","b":"1F944","j":["tableware","cutlery","kitchen"]},"kitchen-knife":{"a":"Kitchen Knife","b":"1F52A","j":["cooking","hocho","knife","tool","weapon","blade","cutlery","kitchen"]},"jar":{"a":"⊛ Jar","b":"1FAD9","j":["condiment","container","empty","sauce","store"]},"amphora":{"a":"Amphora","b":"1F3FA","j":["Aquarius","cooking","drink","jug","zodiac","vase","jar"]},"globe-showing-europeafrica":{"a":"Globe Showing Europe-Africa","b":"1F30D","j":["Africa","earth","Europe","globe","globe showing Europe-Africa","world","globe_showing_europe_africa","international"]},"globe-showing-americas":{"a":"Globe Showing Americas","b":"1F30E","j":["Americas","earth","globe","globe showing Americas","world","USA","international"]},"globe-showing-asiaaustralia":{"a":"Globe Showing Asia-Australia","b":"1F30F","j":["Asia","Australia","earth","globe","globe showing Asia-Australia","world","globe_showing_asia_australia","east","international"]},"globe-with-meridians":{"a":"Globe with Meridians","b":"1F310","j":["earth","globe","meridians","world","international","internet","interweb","i18n"]},"world-map":{"a":"World Map","b":"1F5FA","j":["map","world","location","direction"]},"map-of-japan":{"a":"Map of Japan","b":"1F5FE","j":["Japan","map","map of Japan","nation","country","japanese","asia"]},"compass":{"a":"Compass","b":"1F9ED","j":["magnetic","navigation","orienteering"]},"snowcapped-mountain":{"a":"Snow-Capped Mountain","b":"1F3D4","j":["cold","mountain","snow","snow-capped mountain","snow_capped_mountain","photo","nature","environment","winter"]},"mountain":{"a":"Mountain","b":"26F0","j":["photo","nature","environment"]},"volcano":{"a":"Volcano","b":"1F30B","j":["eruption","mountain","photo","nature","disaster"]},"mount-fuji":{"a":"Mount Fuji","b":"1F5FB","j":["fuji","mountain","photo","nature","japanese"]},"camping":{"a":"Camping","b":"1F3D5","j":["photo","outdoors","tent"]},"beach-with-umbrella":{"a":"Beach with Umbrella","b":"1F3D6","j":["beach","umbrella","weather","summer","sunny","sand","mojito"]},"desert":{"a":"Desert","b":"1F3DC","j":["photo","warm","saharah"]},"desert-island":{"a":"Desert Island","b":"1F3DD","j":["desert","island","photo","tropical","mojito"]},"national-park":{"a":"National Park","b":"1F3DE","j":["park","photo","environment","nature"]},"stadium":{"a":"Stadium","b":"1F3DF","j":["photo","place","sports","concert","venue"]},"classical-building":{"a":"Classical Building","b":"1F3DB","j":["classical","art","culture","history"]},"building-construction":{"a":"Building Construction","b":"1F3D7","j":["construction","wip","working","progress"]},"brick":{"a":"Brick","b":"1F9F1","j":["bricks","clay","mortar","wall"]},"rock":{"a":"Rock","b":"1FAA8","j":["boulder","heavy","solid","stone"]},"wood":{"a":"Wood","b":"1FAB5","j":["log","lumber","timber","nature","trunk"]},"hut":{"a":"Hut","b":"1F6D6","j":["house","roundhouse","yurt","structure"]},"houses":{"a":"Houses","b":"1F3D8","j":["buildings","photo"]},"derelict-house":{"a":"Derelict House","b":"1F3DA","j":["derelict","house","abandon","evict","broken","building"]},"house":{"a":"House","b":"1F3E0","j":["home","building"]},"house-with-garden":{"a":"House with Garden","b":"1F3E1","j":["garden","home","house","plant","nature"]},"office-building":{"a":"Office Building","b":"1F3E2","j":["building","bureau","work"]},"japanese-post-office":{"a":"Japanese Post Office","b":"1F3E3","j":["Japanese","Japanese post office","post","building","envelope","communication"]},"post-office":{"a":"Post Office","b":"1F3E4","j":["European","post","building","email"]},"hospital":{"a":"Hospital","b":"1F3E5","j":["doctor","medicine","building","health","surgery"]},"bank":{"a":"Bank","b":"1F3E6","j":["building","money","sales","cash","business","enterprise"]},"hotel":{"a":"Hotel","b":"1F3E8","j":["building","accomodation","checkin"]},"love-hotel":{"a":"Love Hotel","b":"1F3E9","j":["hotel","love","like","affection","dating"]},"convenience-store":{"a":"Convenience Store","b":"1F3EA","j":["convenience","store","building","shopping","groceries"]},"school":{"a":"School","b":"1F3EB","j":["building","student","education","learn","teach"]},"department-store":{"a":"Department Store","b":"1F3EC","j":["department","store","building","shopping","mall"]},"factory":{"a":"Factory","b":"1F3ED","j":["building","industry","pollution","smoke"]},"japanese-castle":{"a":"Japanese Castle","b":"1F3EF","j":["castle","Japanese","photo","building"]},"castle":{"a":"Castle","b":"1F3F0","j":["European","building","royalty","history"]},"wedding":{"a":"Wedding","b":"1F492","j":["chapel","romance","love","like","affection","couple","marriage","bride","groom"]},"tokyo-tower":{"a":"Tokyo Tower","b":"1F5FC","j":["Tokyo","tower","photo","japanese"]},"statue-of-liberty":{"a":"Statue of Liberty","b":"1F5FD","j":["liberty","statue","american","newyork"]},"church":{"a":"Church","b":"26EA","j":["Christian","cross","religion","building","christ"]},"mosque":{"a":"Mosque","b":"1F54C","j":["islam","Muslim","religion","worship","minaret"]},"hindu-temple":{"a":"Hindu Temple","b":"1F6D5","j":["hindu","temple","religion"]},"synagogue":{"a":"Synagogue","b":"1F54D","j":["Jew","Jewish","religion","temple","judaism","worship","jewish"]},"shinto-shrine":{"a":"Shinto Shrine","b":"26E9","j":["religion","shinto","shrine","temple","japan","kyoto"]},"kaaba":{"a":"Kaaba","b":"1F54B","j":["islam","Muslim","religion","mecca","mosque"]},"fountain":{"a":"Fountain","b":"26F2","j":["photo","summer","water","fresh"]},"tent":{"a":"Tent","b":"26FA","j":["camping","photo","outdoors"]},"foggy":{"a":"Foggy","b":"1F301","j":["fog","photo","mountain"]},"night-with-stars":{"a":"Night with Stars","b":"1F303","j":["night","star","evening","city","downtown"]},"cityscape":{"a":"Cityscape","b":"1F3D9","j":["city","photo","night life","urban"]},"sunrise-over-mountains":{"a":"Sunrise over Mountains","b":"1F304","j":["morning","mountain","sun","sunrise","view","vacation","photo"]},"sunrise":{"a":"Sunrise","b":"1F305","j":["morning","sun","view","vacation","photo"]},"cityscape-at-dusk":{"a":"Cityscape at Dusk","b":"1F306","j":["city","dusk","evening","landscape","sunset","photo","sky","buildings"]},"sunset":{"a":"Sunset","b":"1F307","j":["dusk","sun","photo","good morning","dawn"]},"bridge-at-night":{"a":"Bridge at Night","b":"1F309","j":["bridge","night","photo","sanfrancisco"]},"hot-springs":{"a":"Hot Springs","b":"2668","j":["hot","hotsprings","springs","steaming","bath","warm","relax"]},"carousel-horse":{"a":"Carousel Horse","b":"1F3A0","j":["carousel","horse","photo","carnival"]},"playground-slide":{"a":"⊛ Playground Slide","b":"1F6DD","j":["amusement park","play"]},"ferris-wheel":{"a":"Ferris Wheel","b":"1F3A1","j":["amusement park","ferris","wheel","photo","carnival","londoneye"]},"roller-coaster":{"a":"Roller Coaster","b":"1F3A2","j":["amusement park","coaster","roller","carnival","playground","photo","fun"]},"barber-pole":{"a":"Barber Pole","b":"1F488","j":["barber","haircut","pole","hair","salon","style"]},"circus-tent":{"a":"Circus Tent","b":"1F3AA","j":["circus","tent","festival","carnival","party"]},"locomotive":{"a":"Locomotive","b":"1F682","j":["engine","railway","steam","train","transportation","vehicle"]},"railway-car":{"a":"Railway Car","b":"1F683","j":["car","electric","railway","train","tram","trolleybus","transportation","vehicle"]},"highspeed-train":{"a":"High-Speed Train","b":"1F684","j":["high-speed train","railway","shinkansen","speed","train","high_speed_train","transportation","vehicle"]},"bullet-train":{"a":"Bullet Train","b":"1F685","j":["bullet","railway","shinkansen","speed","train","transportation","vehicle","fast","public","travel"]},"train":{"a":"Train","b":"1F686","j":["railway","transportation","vehicle"]},"metro":{"a":"Metro","b":"1F687","j":["subway","transportation","blue-square","mrt","underground","tube"]},"light-rail":{"a":"Light Rail","b":"1F688","j":["railway","transportation","vehicle"]},"station":{"a":"Station","b":"1F689","j":["railway","train","transportation","vehicle","public"]},"tram":{"a":"Tram","b":"1F68A","j":["trolleybus","transportation","vehicle"]},"monorail":{"a":"Monorail","b":"1F69D","j":["vehicle","transportation"]},"mountain-railway":{"a":"Mountain Railway","b":"1F69E","j":["car","mountain","railway","transportation","vehicle"]},"tram-car":{"a":"Tram Car","b":"1F68B","j":["car","tram","trolleybus","transportation","vehicle","carriage","public","travel"]},"bus":{"a":"Bus","b":"1F68C","j":["vehicle","car","transportation"]},"oncoming-bus":{"a":"Oncoming Bus","b":"1F68D","j":["bus","oncoming","vehicle","transportation"]},"trolleybus":{"a":"Trolleybus","b":"1F68E","j":["bus","tram","trolley","bart","transportation","vehicle"]},"minibus":{"a":"Minibus","b":"1F690","j":["bus","vehicle","car","transportation"]},"ambulance":{"a":"Ambulance","b":"1F691","j":["vehicle","health","911","hospital"]},"fire-engine":{"a":"Fire Engine","b":"1F692","j":["engine","fire","truck","transportation","cars","vehicle"]},"police-car":{"a":"Police Car","b":"1F693","j":["car","patrol","police","vehicle","cars","transportation","law","legal","enforcement"]},"oncoming-police-car":{"a":"Oncoming Police Car","b":"1F694","j":["car","oncoming","police","vehicle","law","legal","enforcement","911"]},"taxi":{"a":"Taxi","b":"1F695","j":["vehicle","uber","cars","transportation"]},"oncoming-taxi":{"a":"Oncoming Taxi","b":"1F696","j":["oncoming","taxi","vehicle","cars","uber"]},"automobile":{"a":"Automobile","b":"1F697","j":["car","red","transportation","vehicle"]},"oncoming-automobile":{"a":"Oncoming Automobile","b":"1F698","j":["automobile","car","oncoming","vehicle","transportation"]},"sport-utility-vehicle":{"a":"Sport Utility Vehicle","b":"1F699","j":["recreational","sport utility","transportation","vehicle"]},"pickup-truck":{"a":"Pickup Truck","b":"1F6FB","j":["pick-up","pickup","truck","car","transportation"]},"delivery-truck":{"a":"Delivery Truck","b":"1F69A","j":["delivery","truck","cars","transportation"]},"articulated-lorry":{"a":"Articulated Lorry","b":"1F69B","j":["lorry","semi","truck","vehicle","cars","transportation","express"]},"tractor":{"a":"Tractor","b":"1F69C","j":["vehicle","car","farming","agriculture"]},"racing-car":{"a":"Racing Car","b":"1F3CE","j":["car","racing","sports","race","fast","formula","f1"]},"motorcycle":{"a":"Motorcycle","b":"1F3CD","j":["racing","race","sports","fast"]},"motor-scooter":{"a":"Motor Scooter","b":"1F6F5","j":["motor","scooter","vehicle","vespa","sasha"]},"manual-wheelchair":{"a":"Manual Wheelchair","b":"1F9BD","j":["accessibility"]},"motorized-wheelchair":{"a":"Motorized Wheelchair","b":"1F9BC","j":["accessibility"]},"auto-rickshaw":{"a":"Auto Rickshaw","b":"1F6FA","j":["tuk tuk","move","transportation"]},"bicycle":{"a":"Bicycle","b":"1F6B2","j":["bike","sports","exercise","hipster"]},"kick-scooter":{"a":"Kick Scooter","b":"1F6F4","j":["kick","scooter","vehicle","razor"]},"skateboard":{"a":"Skateboard","b":"1F6F9","j":["board"]},"roller-skate":{"a":"Roller Skate","b":"1F6FC","j":["roller","skate","footwear","sports"]},"bus-stop":{"a":"Bus Stop","b":"1F68F","j":["bus","stop","transportation","wait"]},"motorway":{"a":"Motorway","b":"1F6E3","j":["highway","road","cupertino","interstate"]},"railway-track":{"a":"Railway Track","b":"1F6E4","j":["railway","train","transportation"]},"oil-drum":{"a":"Oil Drum","b":"1F6E2","j":["drum","oil","barrell"]},"fuel-pump":{"a":"Fuel Pump","b":"26FD","j":["diesel","fuel","fuelpump","gas","pump","station","gas station","petroleum"]},"wheel":{"a":"⊛ Wheel","b":"1F6DE","j":["circle","tire","turn"]},"police-car-light":{"a":"Police Car Light","b":"1F6A8","j":["beacon","car","light","police","revolving","ambulance","911","emergency","alert","error","pinged","law","legal"]},"horizontal-traffic-light":{"a":"Horizontal Traffic Light","b":"1F6A5","j":["light","signal","traffic","transportation"]},"vertical-traffic-light":{"a":"Vertical Traffic Light","b":"1F6A6","j":["light","signal","traffic","transportation","driving"]},"stop-sign":{"a":"Stop Sign","b":"1F6D1","j":["octagonal","sign","stop"]},"construction":{"a":"Construction","b":"1F6A7","j":["barrier","wip","progress","caution","warning"]},"anchor":{"a":"Anchor","b":"2693","j":["ship","tool","ferry","sea","boat"]},"ring-buoy":{"a":"⊛ Ring Buoy","b":"1F6DF","j":["float","life preserver","life saver","rescue","safety"]},"sailboat":{"a":"Sailboat","b":"26F5","j":["boat","resort","sea","yacht","ship","summer","transportation","water","sailing"]},"canoe":{"a":"Canoe","b":"1F6F6","j":["boat","paddle","water","ship"]},"speedboat":{"a":"Speedboat","b":"1F6A4","j":["boat","ship","transportation","vehicle","summer"]},"passenger-ship":{"a":"Passenger Ship","b":"1F6F3","j":["passenger","ship","yacht","cruise","ferry"]},"ferry":{"a":"Ferry","b":"26F4","j":["boat","passenger","ship","yacht"]},"motor-boat":{"a":"Motor Boat","b":"1F6E5","j":["boat","motorboat","ship"]},"ship":{"a":"Ship","b":"1F6A2","j":["boat","passenger","transportation","titanic","deploy"]},"airplane":{"a":"Airplane","b":"2708","j":["aeroplane","vehicle","transportation","flight","fly"]},"small-airplane":{"a":"Small Airplane","b":"1F6E9","j":["aeroplane","airplane","flight","transportation","fly","vehicle"]},"airplane-departure":{"a":"Airplane Departure","b":"1F6EB","j":["aeroplane","airplane","check-in","departure","departures","airport","flight","landing"]},"airplane-arrival":{"a":"Airplane Arrival","b":"1F6EC","j":["aeroplane","airplane","arrivals","arriving","landing","airport","flight","boarding"]},"parachute":{"a":"Parachute","b":"1FA82","j":["hang-glide","parasail","skydive","fly","glide"]},"seat":{"a":"Seat","b":"1F4BA","j":["chair","sit","airplane","transport","bus","flight","fly"]},"helicopter":{"a":"Helicopter","b":"1F681","j":["vehicle","transportation","fly"]},"suspension-railway":{"a":"Suspension Railway","b":"1F69F","j":["railway","suspension","vehicle","transportation"]},"mountain-cableway":{"a":"Mountain Cableway","b":"1F6A0","j":["cable","gondola","mountain","transportation","vehicle","ski"]},"aerial-tramway":{"a":"Aerial Tramway","b":"1F6A1","j":["aerial","cable","car","gondola","tramway","transportation","vehicle","ski"]},"satellite":{"a":"Satellite","b":"1F6F0","j":["space","communication","gps","orbit","spaceflight","NASA","ISS"]},"rocket":{"a":"Rocket","b":"1F680","j":["space","launch","ship","staffmode","NASA","outer space","outer_space","fly"]},"flying-saucer":{"a":"Flying Saucer","b":"1F6F8","j":["UFO","transportation","vehicle","ufo"]},"bellhop-bell":{"a":"Bellhop Bell","b":"1F6CE","j":["bell","bellhop","hotel","service"]},"luggage":{"a":"Luggage","b":"1F9F3","j":["packing","travel"]},"hourglass-done":{"a":"Hourglass Done","b":"231B","j":["sand","timer","time","clock","oldschool","limit","exam","quiz","test"]},"hourglass-not-done":{"a":"Hourglass Not Done","b":"23F3","j":["hourglass","sand","timer","oldschool","time","countdown"]},"watch":{"a":"Watch","b":"231A","j":["clock","time","accessories"]},"alarm-clock":{"a":"Alarm Clock","b":"23F0","j":["alarm","clock","time","wake"]},"stopwatch":{"a":"Stopwatch","b":"23F1","j":["clock","time","deadline"]},"timer-clock":{"a":"Timer Clock","b":"23F2","j":["clock","timer","alarm"]},"mantelpiece-clock":{"a":"Mantelpiece Clock","b":"1F570","j":["clock","time"]},"twelve-oclock":{"a":"Twelve O’Clock","b":"1F55B","j":["00","12","12:00","clock","o’clock","twelve","twelve_o_clock","time","noon","midnight","midday","late","early","schedule"]},"twelvethirty":{"a":"Twelve-Thirty","b":"1F567","j":["12","12:30","clock","thirty","twelve","twelve-thirty","twelve_thirty","time","late","early","schedule"]},"one-oclock":{"a":"One O’Clock","b":"1F550","j":["00","1","1:00","clock","o’clock","one","one_o_clock","time","late","early","schedule"]},"onethirty":{"a":"One-Thirty","b":"1F55C","j":["1","1:30","clock","one","one-thirty","thirty","one_thirty","time","late","early","schedule"]},"two-oclock":{"a":"Two O’Clock","b":"1F551","j":["00","2","2:00","clock","o’clock","two","two_o_clock","time","late","early","schedule"]},"twothirty":{"a":"Two-Thirty","b":"1F55D","j":["2","2:30","clock","thirty","two","two-thirty","two_thirty","time","late","early","schedule"]},"three-oclock":{"a":"Three O’Clock","b":"1F552","j":["00","3","3:00","clock","o’clock","three","three_o_clock","time","late","early","schedule"]},"threethirty":{"a":"Three-Thirty","b":"1F55E","j":["3","3:30","clock","thirty","three","three-thirty","three_thirty","time","late","early","schedule"]},"four-oclock":{"a":"Four O’Clock","b":"1F553","j":["00","4","4:00","clock","four","o’clock","four_o_clock","time","late","early","schedule"]},"fourthirty":{"a":"Four-Thirty","b":"1F55F","j":["4","4:30","clock","four","four-thirty","thirty","four_thirty","time","late","early","schedule"]},"five-oclock":{"a":"Five O’Clock","b":"1F554","j":["00","5","5:00","clock","five","o’clock","five_o_clock","time","late","early","schedule"]},"fivethirty":{"a":"Five-Thirty","b":"1F560","j":["5","5:30","clock","five","five-thirty","thirty","five_thirty","time","late","early","schedule"]},"six-oclock":{"a":"Six O’Clock","b":"1F555","j":["00","6","6:00","clock","o’clock","six","six_o_clock","time","late","early","schedule","dawn","dusk"]},"sixthirty":{"a":"Six-Thirty","b":"1F561","j":["6","6:30","clock","six","six-thirty","thirty","six_thirty","time","late","early","schedule"]},"seven-oclock":{"a":"Seven O’Clock","b":"1F556","j":["00","7","7:00","clock","o’clock","seven","seven_o_clock","time","late","early","schedule"]},"seventhirty":{"a":"Seven-Thirty","b":"1F562","j":["7","7:30","clock","seven","seven-thirty","thirty","seven_thirty","time","late","early","schedule"]},"eight-oclock":{"a":"Eight O’Clock","b":"1F557","j":["00","8","8:00","clock","eight","o’clock","eight_o_clock","time","late","early","schedule"]},"eightthirty":{"a":"Eight-Thirty","b":"1F563","j":["8","8:30","clock","eight","eight-thirty","thirty","eight_thirty","time","late","early","schedule"]},"nine-oclock":{"a":"Nine O’Clock","b":"1F558","j":["00","9","9:00","clock","nine","o’clock","nine_o_clock","time","late","early","schedule"]},"ninethirty":{"a":"Nine-Thirty","b":"1F564","j":["9","9:30","clock","nine","nine-thirty","thirty","nine_thirty","time","late","early","schedule"]},"ten-oclock":{"a":"Ten O’Clock","b":"1F559","j":["00","10","10:00","clock","o’clock","ten","ten_o_clock","time","late","early","schedule"]},"tenthirty":{"a":"Ten-Thirty","b":"1F565","j":["10","10:30","clock","ten","ten-thirty","thirty","ten_thirty","time","late","early","schedule"]},"eleven-oclock":{"a":"Eleven O’Clock","b":"1F55A","j":["00","11","11:00","clock","eleven","o’clock","eleven_o_clock","time","late","early","schedule"]},"eleventhirty":{"a":"Eleven-Thirty","b":"1F566","j":["11","11:30","clock","eleven","eleven-thirty","thirty","eleven_thirty","time","late","early","schedule"]},"new-moon":{"a":"New Moon","b":"1F311","j":["dark","moon","nature","twilight","planet","space","night","evening","sleep"]},"waxing-crescent-moon":{"a":"Waxing Crescent Moon","b":"1F312","j":["crescent","moon","waxing","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon":{"a":"First Quarter Moon","b":"1F313","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waxing-gibbous-moon":{"a":"Waxing Gibbous Moon","b":"1F314","j":["gibbous","moon","waxing","nature","night","sky","gray","twilight","planet","space","evening","sleep"]},"full-moon":{"a":"Full Moon","b":"1F315","j":["full","moon","nature","yellow","twilight","planet","space","night","evening","sleep"]},"waning-gibbous-moon":{"a":"Waning Gibbous Moon","b":"1F316","j":["gibbous","moon","waning","nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"]},"last-quarter-moon":{"a":"Last Quarter Moon","b":"1F317","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waning-crescent-moon":{"a":"Waning Crescent Moon","b":"1F318","j":["crescent","moon","waning","nature","twilight","planet","space","night","evening","sleep"]},"crescent-moon":{"a":"Crescent Moon","b":"1F319","j":["crescent","moon","night","sleep","sky","evening","magic"]},"new-moon-face":{"a":"New Moon Face","b":"1F31A","j":["face","moon","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon-face":{"a":"First Quarter Moon Face","b":"1F31B","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"last-quarter-moon-face":{"a":"Last Quarter Moon Face","b":"1F31C","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"thermometer":{"a":"Thermometer","b":"1F321","j":["weather","temperature","hot","cold"]},"sun":{"a":"Sun","b":"2600","j":["bright","rays","sunny","weather","nature","brightness","summer","beach","spring"]},"full-moon-face":{"a":"Full Moon Face","b":"1F31D","j":["bright","face","full","moon","nature","twilight","planet","space","night","evening","sleep"]},"sun-with-face":{"a":"Sun with Face","b":"1F31E","j":["bright","face","sun","nature","morning","sky"]},"ringed-planet":{"a":"Ringed Planet","b":"1FA90","j":["saturn","saturnine","outerspace"]},"star":{"a":"Star","b":"2B50","j":["night","yellow"]},"glowing-star":{"a":"Glowing Star","b":"1F31F","j":["glittery","glow","shining","sparkle","star","night","awesome","good","magic"]},"shooting-star":{"a":"Shooting Star","b":"1F320","j":["falling","shooting","star","night","photo"]},"milky-way":{"a":"Milky Way","b":"1F30C","j":["space","photo","stars"]},"cloud":{"a":"Cloud","b":"2601","j":["weather","sky"]},"sun-behind-cloud":{"a":"Sun Behind Cloud","b":"26C5","j":["cloud","sun","weather","nature","cloudy","morning","fall","spring"]},"cloud-with-lightning-and-rain":{"a":"Cloud with Lightning and Rain","b":"26C8","j":["cloud","rain","thunder","weather","lightning"]},"sun-behind-small-cloud":{"a":"Sun Behind Small Cloud","b":"1F324","j":["cloud","sun","weather"]},"sun-behind-large-cloud":{"a":"Sun Behind Large Cloud","b":"1F325","j":["cloud","sun","weather"]},"sun-behind-rain-cloud":{"a":"Sun Behind Rain Cloud","b":"1F326","j":["cloud","rain","sun","weather"]},"cloud-with-rain":{"a":"Cloud with Rain","b":"1F327","j":["cloud","rain","weather"]},"cloud-with-snow":{"a":"Cloud with Snow","b":"1F328","j":["cloud","cold","snow","weather"]},"cloud-with-lightning":{"a":"Cloud with Lightning","b":"1F329","j":["cloud","lightning","weather","thunder"]},"tornado":{"a":"Tornado","b":"1F32A","j":["cloud","whirlwind","weather","cyclone","twister"]},"fog":{"a":"Fog","b":"1F32B","j":["cloud","weather"]},"wind-face":{"a":"Wind Face","b":"1F32C","j":["blow","cloud","face","wind","gust","air"]},"cyclone":{"a":"Cyclone","b":"1F300","j":["dizzy","hurricane","twister","typhoon","weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado"]},"rainbow":{"a":"Rainbow","b":"1F308","j":["rain","nature","happy","unicorn_face","photo","sky","spring"]},"closed-umbrella":{"a":"Closed Umbrella","b":"1F302","j":["clothing","rain","umbrella","weather","drizzle"]},"umbrella":{"a":"Umbrella","b":"2602","j":["clothing","rain","weather","spring"]},"umbrella-with-rain-drops":{"a":"Umbrella with Rain Drops","b":"2614","j":["clothing","drop","rain","umbrella","rainy","weather","spring"]},"umbrella-on-ground":{"a":"Umbrella on Ground","b":"26F1","j":["rain","sun","umbrella","weather","summer"]},"high-voltage":{"a":"High Voltage","b":"26A1","j":["danger","electric","lightning","voltage","zap","thunder","weather","lightning bolt","fast"]},"snowflake":{"a":"Snowflake","b":"2744","j":["cold","snow","winter","season","weather","christmas","xmas"]},"snowman":{"a":"Snowman","b":"2603","j":["cold","snow","winter","season","weather","christmas","xmas","frozen"]},"snowman-without-snow":{"a":"Snowman Without Snow","b":"26C4","j":["cold","snow","snowman","winter","season","weather","christmas","xmas","frozen","without_snow"]},"comet":{"a":"Comet","b":"2604","j":["space"]},"fire":{"a":"Fire","b":"1F525","j":["flame","tool","hot","cook"]},"droplet":{"a":"Droplet","b":"1F4A7","j":["cold","comic","drop","sweat","water","drip","faucet","spring"]},"water-wave":{"a":"Water Wave","b":"1F30A","j":["ocean","water","wave","sea","nature","tsunami","disaster"]},"jackolantern":{"a":"Jack-O-Lantern","b":"1F383","j":["celebration","halloween","jack","jack-o-lantern","lantern","jack_o_lantern","light","pumpkin","creepy","fall"]},"christmas-tree":{"a":"Christmas Tree","b":"1F384","j":["celebration","Christmas","tree","festival","vacation","december","xmas"]},"fireworks":{"a":"Fireworks","b":"1F386","j":["celebration","photo","festival","carnival","congratulations"]},"sparkler":{"a":"Sparkler","b":"1F387","j":["celebration","fireworks","sparkle","stars","night","shine"]},"firecracker":{"a":"Firecracker","b":"1F9E8","j":["dynamite","explosive","fireworks","boom","explode","explosion"]},"sparkles":{"a":"Sparkles","b":"2728","j":["*","sparkle","star","stars","shine","shiny","cool","awesome","good","magic"]},"balloon":{"a":"Balloon","b":"1F388","j":["celebration","party","birthday","circus"]},"party-popper":{"a":"Party Popper","b":"1F389","j":["celebration","party","popper","tada","congratulations","birthday","magic","circus"]},"confetti-ball":{"a":"Confetti Ball","b":"1F38A","j":["ball","celebration","confetti","festival","party","birthday","circus"]},"tanabata-tree":{"a":"Tanabata Tree","b":"1F38B","j":["banner","celebration","Japanese","tree","plant","nature","branch","summer"]},"pine-decoration":{"a":"Pine Decoration","b":"1F38D","j":["bamboo","celebration","Japanese","pine","plant","nature","vegetable","panda"]},"japanese-dolls":{"a":"Japanese Dolls","b":"1F38E","j":["celebration","doll","festival","Japanese","Japanese dolls","japanese","toy","kimono"]},"carp-streamer":{"a":"Carp Streamer","b":"1F38F","j":["carp","celebration","streamer","fish","japanese","koinobori","banner"]},"wind-chime":{"a":"Wind Chime","b":"1F390","j":["bell","celebration","chime","wind","nature","ding","spring"]},"moon-viewing-ceremony":{"a":"Moon Viewing Ceremony","b":"1F391","j":["celebration","ceremony","moon","photo","japan","asia","tsukimi"]},"red-envelope":{"a":"Red Envelope","b":"1F9E7","j":["gift","good luck","hóngbāo","lai see","money"]},"ribbon":{"a":"Ribbon","b":"1F380","j":["celebration","decoration","pink","girl","bowtie"]},"wrapped-gift":{"a":"Wrapped Gift","b":"1F381","j":["box","celebration","gift","present","wrapped","birthday","christmas","xmas"]},"reminder-ribbon":{"a":"Reminder Ribbon","b":"1F397","j":["celebration","reminder","ribbon","sports","cause","support","awareness"]},"admission-tickets":{"a":"Admission Tickets","b":"1F39F","j":["admission","ticket","sports","concert","entrance"]},"ticket":{"a":"Ticket","b":"1F3AB","j":["admission","event","concert","pass"]},"military-medal":{"a":"Military Medal","b":"1F396","j":["celebration","medal","military","award","winning","army"]},"trophy":{"a":"Trophy","b":"1F3C6","j":["prize","win","award","contest","place","ftw","ceremony"]},"sports-medal":{"a":"Sports Medal","b":"1F3C5","j":["medal","award","winning"]},"1st-place-medal":{"a":"1st Place Medal","b":"1F947","j":["first","gold","medal","award","winning"]},"2nd-place-medal":{"a":"2nd Place Medal","b":"1F948","j":["medal","second","silver","award"]},"3rd-place-medal":{"a":"3rd Place Medal","b":"1F949","j":["bronze","medal","third","award"]},"soccer-ball":{"a":"Soccer Ball","b":"26BD","j":["ball","football","soccer","sports"]},"baseball":{"a":"Baseball","b":"26BE","j":["ball","sports","balls"]},"softball":{"a":"Softball","b":"1F94E","j":["ball","glove","underarm","sports","balls"]},"basketball":{"a":"Basketball","b":"1F3C0","j":["ball","hoop","sports","balls","NBA"]},"volleyball":{"a":"Volleyball","b":"1F3D0","j":["ball","game","sports","balls"]},"american-football":{"a":"American Football","b":"1F3C8","j":["american","ball","football","sports","balls","NFL"]},"rugby-football":{"a":"Rugby Football","b":"1F3C9","j":["ball","football","rugby","sports","team"]},"tennis":{"a":"Tennis","b":"1F3BE","j":["ball","racquet","sports","balls","green"]},"flying-disc":{"a":"Flying Disc","b":"1F94F","j":["ultimate","sports","frisbee"]},"bowling":{"a":"Bowling","b":"1F3B3","j":["ball","game","sports","fun","play"]},"cricket-game":{"a":"Cricket Game","b":"1F3CF","j":["ball","bat","game","sports"]},"field-hockey":{"a":"Field Hockey","b":"1F3D1","j":["ball","field","game","hockey","stick","sports"]},"ice-hockey":{"a":"Ice Hockey","b":"1F3D2","j":["game","hockey","ice","puck","stick","sports"]},"lacrosse":{"a":"Lacrosse","b":"1F94D","j":["ball","goal","stick","sports"]},"ping-pong":{"a":"Ping Pong","b":"1F3D3","j":["ball","bat","game","paddle","table tennis","sports","pingpong"]},"badminton":{"a":"Badminton","b":"1F3F8","j":["birdie","game","racquet","shuttlecock","sports"]},"boxing-glove":{"a":"Boxing Glove","b":"1F94A","j":["boxing","glove","sports","fighting"]},"martial-arts-uniform":{"a":"Martial Arts Uniform","b":"1F94B","j":["judo","karate","martial arts","taekwondo","uniform"]},"goal-net":{"a":"Goal Net","b":"1F945","j":["goal","net","sports"]},"flag-in-hole":{"a":"Flag in Hole","b":"26F3","j":["golf","hole","sports","business","flag","summer"]},"ice-skate":{"a":"Ice Skate","b":"26F8","j":["ice","skate","sports"]},"fishing-pole":{"a":"Fishing Pole","b":"1F3A3","j":["fish","pole","food","hobby","summer"]},"diving-mask":{"a":"Diving Mask","b":"1F93F","j":["diving","scuba","snorkeling","sport","ocean"]},"running-shirt":{"a":"Running Shirt","b":"1F3BD","j":["athletics","running","sash","shirt","play","pageant"]},"skis":{"a":"Skis","b":"1F3BF","j":["ski","snow","sports","winter","cold"]},"sled":{"a":"Sled","b":"1F6F7","j":["sledge","sleigh","luge","toboggan"]},"curling-stone":{"a":"Curling Stone","b":"1F94C","j":["game","rock","sports"]},"bullseye":{"a":"Bullseye","b":"1F3AF","j":["dart","direct hit","game","hit","target","direct_hit","play","bar"]},"yoyo":{"a":"Yo-Yo","b":"1FA80","j":["fluctuate","toy","yo-yo","yo_yo"]},"kite":{"a":"Kite","b":"1FA81","j":["fly","soar","wind"]},"pool-8-ball":{"a":"Pool 8 Ball","b":"1F3B1","j":["8","ball","billiard","eight","game","pool","hobby","luck","magic"]},"crystal-ball":{"a":"Crystal Ball","b":"1F52E","j":["ball","crystal","fairy tale","fantasy","fortune","tool","disco","party","magic","circus","fortune_teller"]},"magic-wand":{"a":"Magic Wand","b":"1FA84","j":["magic","witch","wizard","supernature","power"]},"nazar-amulet":{"a":"Nazar Amulet","b":"1F9FF","j":["bead","charm","evil-eye","nazar","talisman"]},"hamsa":{"a":"⊛ Hamsa","b":"1FAAC","j":["amulet","Fatima","hand","Mary","Miriam","protection"]},"video-game":{"a":"Video Game","b":"1F3AE","j":["controller","game","play","console","PS4"]},"joystick":{"a":"Joystick","b":"1F579","j":["game","video game","play"]},"slot-machine":{"a":"Slot Machine","b":"1F3B0","j":["game","slot","bet","gamble","vegas","fruit machine","luck","casino"]},"game-die":{"a":"Game Die","b":"1F3B2","j":["dice","die","game","random","tabletop","play","luck"]},"puzzle-piece":{"a":"Puzzle Piece","b":"1F9E9","j":["clue","interlocking","jigsaw","piece","puzzle"]},"teddy-bear":{"a":"Teddy Bear","b":"1F9F8","j":["plaything","plush","stuffed","toy"]},"piata":{"a":"Piñata","b":"1FA85","j":["celebration","party","piñata","pinata","mexico","candy"]},"mirror-ball":{"a":"⊛ Mirror Ball","b":"1FAA9","j":["dance","disco","glitter","party"]},"nesting-dolls":{"a":"Nesting Dolls","b":"1FA86","j":["doll","nesting","russia","matryoshka","toy"]},"spade-suit":{"a":"Spade Suit","b":"2660","j":["card","game","poker","cards","suits","magic"]},"heart-suit":{"a":"Heart Suit","b":"2665","j":["card","game","poker","cards","magic","suits"]},"diamond-suit":{"a":"Diamond Suit","b":"2666","j":["card","game","poker","cards","magic","suits"]},"club-suit":{"a":"Club Suit","b":"2663","j":["card","game","poker","cards","magic","suits"]},"chess-pawn":{"a":"Chess Pawn","b":"265F","j":["chess","dupe","expendable"]},"joker":{"a":"Joker","b":"1F0CF","j":["card","game","wildcard","poker","cards","play","magic"]},"mahjong-red-dragon":{"a":"Mahjong Red Dragon","b":"1F004","j":["game","mahjong","red","play","chinese","kanji"]},"flower-playing-cards":{"a":"Flower Playing Cards","b":"1F3B4","j":["card","flower","game","Japanese","playing","sunset","red"]},"performing-arts":{"a":"Performing Arts","b":"1F3AD","j":["art","mask","performing","theater","theatre","acting","drama"]},"framed-picture":{"a":"Framed Picture","b":"1F5BC","j":["art","frame","museum","painting","picture","photography"]},"artist-palette":{"a":"Artist Palette","b":"1F3A8","j":["art","museum","painting","palette","design","paint","draw","colors"]},"thread":{"a":"Thread","b":"1F9F5","j":["needle","sewing","spool","string"]},"sewing-needle":{"a":"Sewing Needle","b":"1FAA1","j":["embroidery","needle","sewing","stitches","sutures","tailoring"]},"yarn":{"a":"Yarn","b":"1F9F6","j":["ball","crochet","knit"]},"knot":{"a":"Knot","b":"1FAA2","j":["rope","tangled","tie","twine","twist","scout"]},"glasses":{"a":"Glasses","b":"1F453","j":["clothing","eye","eyeglasses","eyewear","fashion","accessories","eyesight","nerdy","dork","geek"]},"sunglasses":{"a":"Sunglasses","b":"1F576","j":["dark","eye","eyewear","glasses","face","cool","accessories"]},"goggles":{"a":"Goggles","b":"1F97D","j":["eye protection","swimming","welding","eyes","protection","safety"]},"lab-coat":{"a":"Lab Coat","b":"1F97C","j":["doctor","experiment","scientist","chemist"]},"safety-vest":{"a":"Safety Vest","b":"1F9BA","j":["emergency","safety","vest","protection"]},"necktie":{"a":"Necktie","b":"1F454","j":["clothing","tie","shirt","suitup","formal","fashion","cloth","business"]},"tshirt":{"a":"T-Shirt","b":"1F455","j":["clothing","shirt","t-shirt","t_shirt","fashion","cloth","casual","tee"]},"jeans":{"a":"Jeans","b":"1F456","j":["clothing","pants","trousers","fashion","shopping"]},"scarf":{"a":"Scarf","b":"1F9E3","j":["neck","winter","clothes"]},"gloves":{"a":"Gloves","b":"1F9E4","j":["hand","hands","winter","clothes"]},"coat":{"a":"Coat","b":"1F9E5","j":["jacket"]},"socks":{"a":"Socks","b":"1F9E6","j":["stocking","stockings","clothes"]},"dress":{"a":"Dress","b":"1F457","j":["clothing","clothes","fashion","shopping"]},"kimono":{"a":"Kimono","b":"1F458","j":["clothing","dress","fashion","women","female","japanese"]},"sari":{"a":"Sari","b":"1F97B","j":["clothing","dress"]},"onepiece-swimsuit":{"a":"One-Piece Swimsuit","b":"1FA71","j":["bathing suit","one-piece swimsuit","one_piece_swimsuit","fashion"]},"briefs":{"a":"Briefs","b":"1FA72","j":["bathing suit","one-piece","swimsuit","underwear","clothing"]},"shorts":{"a":"Shorts","b":"1FA73","j":["bathing suit","pants","underwear","clothing"]},"bikini":{"a":"Bikini","b":"1F459","j":["clothing","swim","swimming","female","woman","girl","fashion","beach","summer"]},"womans-clothes":{"a":"Woman’S Clothes","b":"1F45A","j":["clothing","woman","woman’s clothes","woman_s_clothes","fashion","shopping_bags","female"]},"purse":{"a":"Purse","b":"1F45B","j":["clothing","coin","fashion","accessories","money","sales","shopping"]},"handbag":{"a":"Handbag","b":"1F45C","j":["bag","clothing","purse","fashion","accessory","accessories","shopping"]},"clutch-bag":{"a":"Clutch Bag","b":"1F45D","j":["bag","clothing","pouch","accessories","shopping"]},"shopping-bags":{"a":"Shopping Bags","b":"1F6CD","j":["bag","hotel","shopping","mall","buy","purchase"]},"backpack":{"a":"Backpack","b":"1F392","j":["bag","rucksack","satchel","school","student","education"]},"thong-sandal":{"a":"Thong Sandal","b":"1FA74","j":["beach sandals","sandals","thong sandals","thongs","zōri","footwear","summer"]},"mans-shoe":{"a":"Man’S Shoe","b":"1F45E","j":["clothing","man","man’s shoe","shoe","man_s_shoe","fashion","male"]},"running-shoe":{"a":"Running Shoe","b":"1F45F","j":["athletic","clothing","shoe","sneaker","shoes","sports","sneakers"]},"hiking-boot":{"a":"Hiking Boot","b":"1F97E","j":["backpacking","boot","camping","hiking"]},"flat-shoe":{"a":"Flat Shoe","b":"1F97F","j":["ballet flat","slip-on","slipper","ballet"]},"highheeled-shoe":{"a":"High-Heeled Shoe","b":"1F460","j":["clothing","heel","high-heeled shoe","shoe","woman","high_heeled_shoe","fashion","shoes","female","pumps","stiletto"]},"womans-sandal":{"a":"Woman’S Sandal","b":"1F461","j":["clothing","sandal","shoe","woman","woman’s sandal","woman_s_sandal","shoes","fashion","flip flops"]},"ballet-shoes":{"a":"Ballet Shoes","b":"1FA70","j":["ballet","dance"]},"womans-boot":{"a":"Woman’S Boot","b":"1F462","j":["boot","clothing","shoe","woman","woman’s boot","woman_s_boot","shoes","fashion"]},"crown":{"a":"Crown","b":"1F451","j":["clothing","king","queen","kod","leader","royalty","lord"]},"womans-hat":{"a":"Woman’S Hat","b":"1F452","j":["clothing","hat","woman","woman’s hat","woman_s_hat","fashion","accessories","female","lady","spring"]},"top-hat":{"a":"Top Hat","b":"1F3A9","j":["clothing","hat","top","tophat","magic","gentleman","classy","circus"]},"graduation-cap":{"a":"Graduation Cap","b":"1F393","j":["cap","celebration","clothing","graduation","hat","school","college","degree","university","legal","learn","education"]},"billed-cap":{"a":"Billed Cap","b":"1F9E2","j":["baseball cap","cap","baseball"]},"military-helmet":{"a":"Military Helmet","b":"1FA96","j":["army","helmet","military","soldier","warrior","protection"]},"rescue-workers-helmet":{"a":"Rescue Worker’S Helmet","b":"26D1","j":["aid","cross","face","hat","helmet","rescue worker’s helmet","rescue_worker_s_helmet","construction","build"]},"prayer-beads":{"a":"Prayer Beads","b":"1F4FF","j":["beads","clothing","necklace","prayer","religion","dhikr","religious"]},"lipstick":{"a":"Lipstick","b":"1F484","j":["cosmetics","makeup","female","girl","fashion","woman"]},"ring":{"a":"Ring","b":"1F48D","j":["diamond","wedding","propose","marriage","valentines","fashion","jewelry","gem","engagement"]},"gem-stone":{"a":"Gem Stone","b":"1F48E","j":["diamond","gem","jewel","blue","ruby","jewelry"]},"muted-speaker":{"a":"Muted Speaker","b":"1F507","j":["mute","quiet","silent","speaker","sound","volume","silence"]},"speaker-low-volume":{"a":"Speaker Low Volume","b":"1F508","j":["soft","sound","volume","silence","broadcast"]},"speaker-medium-volume":{"a":"Speaker Medium Volume","b":"1F509","j":["medium","volume","speaker","broadcast"]},"speaker-high-volume":{"a":"Speaker High Volume","b":"1F50A","j":["loud","volume","noise","noisy","speaker","broadcast"]},"loudspeaker":{"a":"Loudspeaker","b":"1F4E2","j":["loud","public address","volume","sound"]},"megaphone":{"a":"Megaphone","b":"1F4E3","j":["cheering","sound","speaker","volume"]},"postal-horn":{"a":"Postal Horn","b":"1F4EF","j":["horn","post","postal","instrument","music"]},"bell":{"a":"Bell","b":"1F514","j":["sound","notification","christmas","xmas","chime"]},"bell-with-slash":{"a":"Bell with Slash","b":"1F515","j":["bell","forbidden","mute","quiet","silent","sound","volume"]},"musical-score":{"a":"Musical Score","b":"1F3BC","j":["music","score","treble","clef","compose"]},"musical-note":{"a":"Musical Note","b":"1F3B5","j":["music","note","score","tone","sound"]},"musical-notes":{"a":"Musical Notes","b":"1F3B6","j":["music","note","notes","score"]},"studio-microphone":{"a":"Studio Microphone","b":"1F399","j":["mic","microphone","music","studio","sing","recording","artist","talkshow"]},"level-slider":{"a":"Level Slider","b":"1F39A","j":["level","music","slider","scale"]},"control-knobs":{"a":"Control Knobs","b":"1F39B","j":["control","knobs","music","dial"]},"microphone":{"a":"Microphone","b":"1F3A4","j":["karaoke","mic","sound","music","PA","sing","talkshow"]},"headphone":{"a":"Headphone","b":"1F3A7","j":["earbud","music","score","gadgets"]},"radio":{"a":"Radio","b":"1F4FB","j":["video","communication","music","podcast","program"]},"saxophone":{"a":"Saxophone","b":"1F3B7","j":["instrument","music","sax","jazz","blues"]},"accordion":{"a":"Accordion","b":"1FA97","j":["concertina","squeeze box","music"]},"guitar":{"a":"Guitar","b":"1F3B8","j":["instrument","music"]},"musical-keyboard":{"a":"Musical Keyboard","b":"1F3B9","j":["instrument","keyboard","music","piano","compose"]},"trumpet":{"a":"Trumpet","b":"1F3BA","j":["instrument","music","brass"]},"violin":{"a":"Violin","b":"1F3BB","j":["instrument","music","orchestra","symphony"]},"banjo":{"a":"Banjo","b":"1FA95","j":["music","stringed","instructment"]},"drum":{"a":"Drum","b":"1F941","j":["drumsticks","music","instrument","snare"]},"long-drum":{"a":"Long Drum","b":"1FA98","j":["beat","conga","drum","rhythm","music"]},"mobile-phone":{"a":"Mobile Phone","b":"1F4F1","j":["cell","mobile","phone","telephone","technology","apple","gadgets","dial"]},"mobile-phone-with-arrow":{"a":"Mobile Phone with Arrow","b":"1F4F2","j":["arrow","cell","mobile","phone","receive","iphone","incoming"]},"telephone":{"a":"Telephone","b":"260E","j":["phone","technology","communication","dial"]},"telephone-receiver":{"a":"Telephone Receiver","b":"1F4DE","j":["phone","receiver","telephone","technology","communication","dial"]},"pager":{"a":"Pager","b":"1F4DF","j":["bbcall","oldschool","90s"]},"fax-machine":{"a":"Fax Machine","b":"1F4E0","j":["fax","communication","technology"]},"battery":{"a":"Battery","b":"1F50B","j":["power","energy","sustain"]},"low-battery":{"a":"⊛ Low Battery","b":"1FAAB","j":["electronic","low energy"]},"electric-plug":{"a":"Electric Plug","b":"1F50C","j":["electric","electricity","plug","charger","power"]},"laptop":{"a":"Laptop","b":"1F4BB","j":["computer","pc","personal","technology","screen","display","monitor"]},"desktop-computer":{"a":"Desktop Computer","b":"1F5A5","j":["computer","desktop","technology","computing","screen"]},"printer":{"a":"Printer","b":"1F5A8","j":["computer","paper","ink"]},"keyboard":{"a":"Keyboard","b":"2328","j":["computer","technology","type","input","text"]},"computer-mouse":{"a":"Computer Mouse","b":"1F5B1","j":["computer","click"]},"trackball":{"a":"Trackball","b":"1F5B2","j":["computer","technology","trackpad"]},"computer-disk":{"a":"Computer Disk","b":"1F4BD","j":["computer","disk","minidisk","optical","technology","record","data","90s"]},"floppy-disk":{"a":"Floppy Disk","b":"1F4BE","j":["computer","disk","floppy","oldschool","technology","save","90s","80s"]},"optical-disk":{"a":"Optical Disk","b":"1F4BF","j":["cd","computer","disk","optical","technology","dvd","disc","90s"]},"dvd":{"a":"Dvd","b":"1F4C0","j":["blu-ray","computer","disk","optical","cd","disc"]},"abacus":{"a":"Abacus","b":"1F9EE","j":["calculation"]},"movie-camera":{"a":"Movie Camera","b":"1F3A5","j":["camera","cinema","movie","film","record"]},"film-frames":{"a":"Film Frames","b":"1F39E","j":["cinema","film","frames","movie"]},"film-projector":{"a":"Film Projector","b":"1F4FD","j":["cinema","film","movie","projector","video","tape","record"]},"clapper-board":{"a":"Clapper Board","b":"1F3AC","j":["clapper","movie","film","record"]},"television":{"a":"Television","b":"1F4FA","j":["tv","video","technology","program","oldschool","show"]},"camera":{"a":"Camera","b":"1F4F7","j":["video","gadgets","photography"]},"camera-with-flash":{"a":"Camera with Flash","b":"1F4F8","j":["camera","flash","video","photography","gadgets"]},"video-camera":{"a":"Video Camera","b":"1F4F9","j":["camera","video","film","record"]},"videocassette":{"a":"Videocassette","b":"1F4FC","j":["tape","vhs","video","record","oldschool","90s","80s"]},"magnifying-glass-tilted-left":{"a":"Magnifying Glass Tilted Left","b":"1F50D","j":["glass","magnifying","search","tool","zoom","find","detective"]},"magnifying-glass-tilted-right":{"a":"Magnifying Glass Tilted Right","b":"1F50E","j":["glass","magnifying","search","tool","zoom","find","detective"]},"candle":{"a":"Candle","b":"1F56F","j":["light","fire","wax"]},"light-bulb":{"a":"Light Bulb","b":"1F4A1","j":["bulb","comic","electric","idea","light","electricity"]},"flashlight":{"a":"Flashlight","b":"1F526","j":["electric","light","tool","torch","dark","camping","sight","night"]},"red-paper-lantern":{"a":"Red Paper Lantern","b":"1F3EE","j":["bar","lantern","light","red","paper","halloween","spooky"]},"diya-lamp":{"a":"Diya Lamp","b":"1FA94","j":["diya","lamp","oil","lighting"]},"notebook-with-decorative-cover":{"a":"Notebook with Decorative Cover","b":"1F4D4","j":["book","cover","decorated","notebook","classroom","notes","record","paper","study"]},"closed-book":{"a":"Closed Book","b":"1F4D5","j":["book","closed","read","library","knowledge","textbook","learn"]},"open-book":{"a":"Open Book","b":"1F4D6","j":["book","open","read","library","knowledge","literature","learn","study"]},"green-book":{"a":"Green Book","b":"1F4D7","j":["book","green","read","library","knowledge","study"]},"blue-book":{"a":"Blue Book","b":"1F4D8","j":["blue","book","read","library","knowledge","learn","study"]},"orange-book":{"a":"Orange Book","b":"1F4D9","j":["book","orange","read","library","knowledge","textbook","study"]},"books":{"a":"Books","b":"1F4DA","j":["book","literature","library","study"]},"notebook":{"a":"Notebook","b":"1F4D3","j":["stationery","record","notes","paper","study"]},"ledger":{"a":"Ledger","b":"1F4D2","j":["notebook","notes","paper"]},"page-with-curl":{"a":"Page with Curl","b":"1F4C3","j":["curl","document","page","documents","office","paper"]},"scroll":{"a":"Scroll","b":"1F4DC","j":["paper","documents","ancient","history"]},"page-facing-up":{"a":"Page Facing Up","b":"1F4C4","j":["document","page","documents","office","paper","information"]},"newspaper":{"a":"Newspaper","b":"1F4F0","j":["news","paper","press","headline"]},"rolledup-newspaper":{"a":"Rolled-Up Newspaper","b":"1F5DE","j":["news","newspaper","paper","rolled","rolled-up newspaper","rolled_up_newspaper","press","headline"]},"bookmark-tabs":{"a":"Bookmark Tabs","b":"1F4D1","j":["bookmark","mark","marker","tabs","favorite","save","order","tidy"]},"bookmark":{"a":"Bookmark","b":"1F516","j":["mark","favorite","label","save"]},"label":{"a":"Label","b":"1F3F7","j":["sale","tag"]},"money-bag":{"a":"Money Bag","b":"1F4B0","j":["bag","dollar","money","moneybag","payment","coins","sale"]},"coin":{"a":"Coin","b":"1FA99","j":["gold","metal","money","silver","treasure","currency"]},"yen-banknote":{"a":"Yen Banknote","b":"1F4B4","j":["banknote","bill","currency","money","note","yen","sales","japanese","dollar"]},"dollar-banknote":{"a":"Dollar Banknote","b":"1F4B5","j":["banknote","bill","currency","dollar","money","note","sales"]},"euro-banknote":{"a":"Euro Banknote","b":"1F4B6","j":["banknote","bill","currency","euro","money","note","sales","dollar"]},"pound-banknote":{"a":"Pound Banknote","b":"1F4B7","j":["banknote","bill","currency","money","note","pound","british","sterling","sales","bills","uk","england"]},"money-with-wings":{"a":"Money with Wings","b":"1F4B8","j":["banknote","bill","fly","money","wings","dollar","bills","payment","sale"]},"credit-card":{"a":"Credit Card","b":"1F4B3","j":["card","credit","money","sales","dollar","bill","payment","shopping"]},"receipt":{"a":"Receipt","b":"1F9FE","j":["accounting","bookkeeping","evidence","proof","expenses"]},"chart-increasing-with-yen":{"a":"Chart Increasing with Yen","b":"1F4B9","j":["chart","graph","growth","money","yen","green-square","presentation","stats"]},"envelope":{"a":"Envelope","b":"2709","j":["email","letter","postal","inbox","communication"]},"email":{"a":"E-Mail","b":"1F4E7","j":["e-mail","letter","mail","e_mail","communication","inbox"]},"incoming-envelope":{"a":"Incoming Envelope","b":"1F4E8","j":["e-mail","email","envelope","incoming","letter","receive","inbox"]},"envelope-with-arrow":{"a":"Envelope with Arrow","b":"1F4E9","j":["arrow","e-mail","email","envelope","outgoing","communication"]},"outbox-tray":{"a":"Outbox Tray","b":"1F4E4","j":["box","letter","mail","outbox","sent","tray","inbox","email"]},"inbox-tray":{"a":"Inbox Tray","b":"1F4E5","j":["box","inbox","letter","mail","receive","tray","email","documents"]},"package":{"a":"Package","b":"1F4E6","j":["box","parcel","mail","gift","cardboard","moving"]},"closed-mailbox-with-raised-flag":{"a":"Closed Mailbox with Raised Flag","b":"1F4EB","j":["closed","mail","mailbox","postbox","email","inbox","communication"]},"closed-mailbox-with-lowered-flag":{"a":"Closed Mailbox with Lowered Flag","b":"1F4EA","j":["closed","lowered","mail","mailbox","postbox","email","communication","inbox"]},"open-mailbox-with-raised-flag":{"a":"Open Mailbox with Raised Flag","b":"1F4EC","j":["mail","mailbox","open","postbox","email","inbox","communication"]},"open-mailbox-with-lowered-flag":{"a":"Open Mailbox with Lowered Flag","b":"1F4ED","j":["lowered","mail","mailbox","open","postbox","email","inbox"]},"postbox":{"a":"Postbox","b":"1F4EE","j":["mail","mailbox","email","letter","envelope"]},"ballot-box-with-ballot":{"a":"Ballot Box with Ballot","b":"1F5F3","j":["ballot","box","election","vote"]},"pencil":{"a":"Pencil","b":"270F","j":["stationery","write","paper","writing","school","study"]},"black-nib":{"a":"Black Nib","b":"2712","j":["nib","pen","stationery","writing","write"]},"fountain-pen":{"a":"Fountain Pen","b":"1F58B","j":["fountain","pen","stationery","writing","write"]},"pen":{"a":"Pen","b":"1F58A","j":["ballpoint","stationery","writing","write"]},"paintbrush":{"a":"Paintbrush","b":"1F58C","j":["painting","drawing","creativity","art"]},"crayon":{"a":"Crayon","b":"1F58D","j":["drawing","creativity"]},"memo":{"a":"Memo","b":"1F4DD","j":["pencil","write","documents","stationery","paper","writing","legal","exam","quiz","test","study","compose"]},"briefcase":{"a":"Briefcase","b":"1F4BC","j":["business","documents","work","law","legal","job","career"]},"file-folder":{"a":"File Folder","b":"1F4C1","j":["file","folder","documents","business","office"]},"open-file-folder":{"a":"Open File Folder","b":"1F4C2","j":["file","folder","open","documents","load"]},"card-index-dividers":{"a":"Card Index Dividers","b":"1F5C2","j":["card","dividers","index","organizing","business","stationery"]},"calendar":{"a":"Calendar","b":"1F4C5","j":["date","schedule"]},"tearoff-calendar":{"a":"Tear-off Calendar","b":"1F4C6","j":["calendar","tear-off calendar","tear_off_calendar","schedule","date","planning"]},"spiral-notepad":{"a":"Spiral Notepad","b":"1F5D2","j":["note","pad","spiral","memo","stationery"]},"spiral-calendar":{"a":"Spiral Calendar","b":"1F5D3","j":["calendar","pad","spiral","date","schedule","planning"]},"card-index":{"a":"Card Index","b":"1F4C7","j":["card","index","rolodex","business","stationery"]},"chart-increasing":{"a":"Chart Increasing","b":"1F4C8","j":["chart","graph","growth","trend","upward","presentation","stats","recovery","business","economics","money","sales","good","success"]},"chart-decreasing":{"a":"Chart Decreasing","b":"1F4C9","j":["chart","down","graph","trend","presentation","stats","recession","business","economics","money","sales","bad","failure"]},"bar-chart":{"a":"Bar Chart","b":"1F4CA","j":["bar","chart","graph","presentation","stats"]},"clipboard":{"a":"Clipboard","b":"1F4CB","j":["stationery","documents"]},"pushpin":{"a":"Pushpin","b":"1F4CC","j":["pin","stationery","mark","here"]},"round-pushpin":{"a":"Round Pushpin","b":"1F4CD","j":["pin","pushpin","stationery","location","map","here"]},"paperclip":{"a":"Paperclip","b":"1F4CE","j":["documents","stationery"]},"linked-paperclips":{"a":"Linked Paperclips","b":"1F587","j":["link","paperclip","documents","stationery"]},"straight-ruler":{"a":"Straight Ruler","b":"1F4CF","j":["ruler","straight edge","stationery","calculate","length","math","school","drawing","architect","sketch"]},"triangular-ruler":{"a":"Triangular Ruler","b":"1F4D0","j":["ruler","set","triangle","stationery","math","architect","sketch"]},"scissors":{"a":"Scissors","b":"2702","j":["cutting","tool","stationery","cut"]},"card-file-box":{"a":"Card File Box","b":"1F5C3","j":["box","card","file","business","stationery"]},"file-cabinet":{"a":"File Cabinet","b":"1F5C4","j":["cabinet","file","filing","organizing"]},"wastebasket":{"a":"Wastebasket","b":"1F5D1","j":["bin","trash","rubbish","garbage","toss"]},"locked":{"a":"Locked","b":"1F512","j":["closed","security","password","padlock"]},"unlocked":{"a":"Unlocked","b":"1F513","j":["lock","open","unlock","privacy","security"]},"locked-with-pen":{"a":"Locked with Pen","b":"1F50F","j":["ink","lock","nib","pen","privacy","security","secret"]},"locked-with-key":{"a":"Locked with Key","b":"1F510","j":["closed","key","lock","secure","security","privacy"]},"key":{"a":"Key","b":"1F511","j":["lock","password","door"]},"old-key":{"a":"Old Key","b":"1F5DD","j":["clue","key","lock","old","door","password"]},"hammer":{"a":"Hammer","b":"1F528","j":["tool","tools","build","create"]},"axe":{"a":"Axe","b":"1FA93","j":["chop","hatchet","split","wood","tool","cut"]},"pick":{"a":"Pick","b":"26CF","j":["mining","tool","tools","dig"]},"hammer-and-pick":{"a":"Hammer and Pick","b":"2692","j":["hammer","pick","tool","tools","build","create"]},"hammer-and-wrench":{"a":"Hammer and Wrench","b":"1F6E0","j":["hammer","spanner","tool","wrench","tools","build","create"]},"dagger":{"a":"Dagger","b":"1F5E1","j":["knife","weapon"]},"crossed-swords":{"a":"Crossed Swords","b":"2694","j":["crossed","swords","weapon"]},"water-pistol":{"a":"Water Pistol","b":"1F52B","j":["gun","handgun","pistol","revolver","tool","water","weapon","violence"]},"boomerang":{"a":"Boomerang","b":"1FA83","j":["australia","rebound","repercussion","weapon"]},"bow-and-arrow":{"a":"Bow and Arrow","b":"1F3F9","j":["archer","arrow","bow","Sagittarius","zodiac","sports"]},"shield":{"a":"Shield","b":"1F6E1","j":["weapon","protection","security"]},"carpentry-saw":{"a":"Carpentry Saw","b":"1FA9A","j":["carpenter","lumber","saw","tool","cut","chop"]},"wrench":{"a":"Wrench","b":"1F527","j":["spanner","tool","tools","diy","ikea","fix","maintainer"]},"screwdriver":{"a":"Screwdriver","b":"1FA9B","j":["screw","tool","tools"]},"nut-and-bolt":{"a":"Nut and Bolt","b":"1F529","j":["bolt","nut","tool","handy","tools","fix"]},"gear":{"a":"Gear","b":"2699","j":["cog","cogwheel","tool"]},"clamp":{"a":"Clamp","b":"1F5DC","j":["compress","tool","vice"]},"balance-scale":{"a":"Balance Scale","b":"2696","j":["balance","justice","Libra","scale","zodiac","law","fairness","weight"]},"white-cane":{"a":"White Cane","b":"1F9AF","j":["accessibility","blind","probing_cane"]},"link":{"a":"Link","b":"1F517","j":["rings","url"]},"chains":{"a":"Chains","b":"26D3","j":["chain","lock","arrest"]},"hook":{"a":"Hook","b":"1FA9D","j":["catch","crook","curve","ensnare","selling point","tools"]},"toolbox":{"a":"Toolbox","b":"1F9F0","j":["chest","mechanic","tool","tools","diy","fix","maintainer"]},"magnet":{"a":"Magnet","b":"1F9F2","j":["attraction","horseshoe","magnetic"]},"ladder":{"a":"Ladder","b":"1FA9C","j":["climb","rung","step","tools"]},"alembic":{"a":"Alembic","b":"2697","j":["chemistry","tool","distilling","science","experiment"]},"test-tube":{"a":"Test Tube","b":"1F9EA","j":["chemist","chemistry","experiment","lab","science"]},"petri-dish":{"a":"Petri Dish","b":"1F9EB","j":["bacteria","biologist","biology","culture","lab"]},"dna":{"a":"Dna","b":"1F9EC","j":["biologist","evolution","gene","genetics","life"]},"microscope":{"a":"Microscope","b":"1F52C","j":["science","tool","laboratory","experiment","zoomin","study"]},"telescope":{"a":"Telescope","b":"1F52D","j":["science","tool","stars","space","zoom","astronomy"]},"satellite-antenna":{"a":"Satellite Antenna","b":"1F4E1","j":["antenna","dish","satellite","communication","future","radio","space"]},"syringe":{"a":"Syringe","b":"1F489","j":["medicine","needle","shot","sick","health","hospital","drugs","blood","doctor","nurse"]},"drop-of-blood":{"a":"Drop of Blood","b":"1FA78","j":["bleed","blood donation","injury","medicine","menstruation","period","hurt","harm","wound"]},"pill":{"a":"Pill","b":"1F48A","j":["doctor","medicine","sick","health","pharmacy","drug"]},"adhesive-bandage":{"a":"Adhesive Bandage","b":"1FA79","j":["bandage","heal"]},"crutch":{"a":"⊛ Crutch","b":"1FA7C","j":["cane","disability","hurt","mobility aid","stick"]},"stethoscope":{"a":"Stethoscope","b":"1FA7A","j":["doctor","heart","medicine","health"]},"xray":{"a":"⊛ X-Ray","b":"1FA7B","j":["bones","doctor","medical","skeleton"]},"door":{"a":"Door","b":"1F6AA","j":["house","entry","exit"]},"elevator":{"a":"Elevator","b":"1F6D7","j":["accessibility","hoist","lift"]},"mirror":{"a":"Mirror","b":"1FA9E","j":["reflection","reflector","speculum"]},"window":{"a":"Window","b":"1FA9F","j":["frame","fresh air","opening","transparent","view","scenery"]},"bed":{"a":"Bed","b":"1F6CF","j":["hotel","sleep","rest"]},"couch-and-lamp":{"a":"Couch and Lamp","b":"1F6CB","j":["couch","hotel","lamp","read","chill"]},"chair":{"a":"Chair","b":"1FA91","j":["seat","sit","furniture"]},"toilet":{"a":"Toilet","b":"1F6BD","j":["restroom","wc","washroom","bathroom","potty"]},"plunger":{"a":"Plunger","b":"1FAA0","j":["force cup","plumber","suction","toilet"]},"shower":{"a":"Shower","b":"1F6BF","j":["water","clean","bathroom"]},"bathtub":{"a":"Bathtub","b":"1F6C1","j":["bath","clean","shower","bathroom"]},"mouse-trap":{"a":"Mouse Trap","b":"1FAA4","j":["bait","mousetrap","snare","trap","cheese"]},"razor":{"a":"Razor","b":"1FA92","j":["sharp","shave","cut"]},"lotion-bottle":{"a":"Lotion Bottle","b":"1F9F4","j":["lotion","moisturizer","shampoo","sunscreen"]},"safety-pin":{"a":"Safety Pin","b":"1F9F7","j":["diaper","punk rock"]},"broom":{"a":"Broom","b":"1F9F9","j":["cleaning","sweeping","witch"]},"basket":{"a":"Basket","b":"1F9FA","j":["farming","laundry","picnic"]},"roll-of-paper":{"a":"Roll of Paper","b":"1F9FB","j":["paper towels","toilet paper","roll"]},"bucket":{"a":"Bucket","b":"1FAA3","j":["cask","pail","vat","water","container"]},"soap":{"a":"Soap","b":"1F9FC","j":["bar","bathing","cleaning","lather","soapdish"]},"bubbles":{"a":"⊛ Bubbles","b":"1FAE7","j":["burp","clean","soap","underwater"]},"toothbrush":{"a":"Toothbrush","b":"1FAA5","j":["bathroom","brush","clean","dental","hygiene","teeth"]},"sponge":{"a":"Sponge","b":"1F9FD","j":["absorbing","cleaning","porous"]},"fire-extinguisher":{"a":"Fire Extinguisher","b":"1F9EF","j":["extinguish","fire","quench"]},"shopping-cart":{"a":"Shopping Cart","b":"1F6D2","j":["cart","shopping","trolley"]},"cigarette":{"a":"Cigarette","b":"1F6AC","j":["smoking","kills","tobacco","joint","smoke"]},"coffin":{"a":"Coffin","b":"26B0","j":["death","vampire","dead","die","rip","graveyard","cemetery","casket","funeral","box"]},"headstone":{"a":"Headstone","b":"1FAA6","j":["cemetery","grave","graveyard","tombstone","death","rip"]},"funeral-urn":{"a":"Funeral Urn","b":"26B1","j":["ashes","death","funeral","urn","dead","die","rip"]},"moai":{"a":"Moai","b":"1F5FF","j":["face","moyai","statue","rock","easter island"]},"placard":{"a":"Placard","b":"1FAA7","j":["demonstration","picket","protest","sign","announcement"]},"identification-card":{"a":"⊛ Identification Card","b":"1FAAA","j":["credentials","ID","license","security"]},"atm-sign":{"a":"Atm Sign","b":"1F3E7","j":["atm","ATM sign","automated","bank","teller","money","sales","cash","blue-square","payment"]},"litter-in-bin-sign":{"a":"Litter in Bin Sign","b":"1F6AE","j":["litter","litter bin","blue-square","sign","human","info"]},"potable-water":{"a":"Potable Water","b":"1F6B0","j":["drinking","potable","water","blue-square","liquid","restroom","cleaning","faucet"]},"wheelchair-symbol":{"a":"Wheelchair Symbol","b":"267F","j":["access","blue-square","disabled","accessibility"]},"mens-room":{"a":"Men’S Room","b":"1F6B9","j":["lavatory","man","men’s room","restroom","wc","men_s_room","toilet","blue-square","gender","male"]},"womens-room":{"a":"Women’S Room","b":"1F6BA","j":["lavatory","restroom","wc","woman","women’s room","women_s_room","purple-square","female","toilet","loo","gender"]},"restroom":{"a":"Restroom","b":"1F6BB","j":["lavatory","WC","blue-square","toilet","refresh","wc","gender"]},"baby-symbol":{"a":"Baby Symbol","b":"1F6BC","j":["baby","changing","orange-square","child"]},"water-closet":{"a":"Water Closet","b":"1F6BE","j":["closet","lavatory","restroom","water","wc","toilet","blue-square"]},"passport-control":{"a":"Passport Control","b":"1F6C2","j":["control","passport","custom","blue-square"]},"customs":{"a":"Customs","b":"1F6C3","j":["passport","border","blue-square"]},"baggage-claim":{"a":"Baggage Claim","b":"1F6C4","j":["baggage","claim","blue-square","airport","transport"]},"left-luggage":{"a":"Left Luggage","b":"1F6C5","j":["baggage","locker","luggage","blue-square","travel"]},"warning":{"a":"Warning","b":"26A0","j":["exclamation","wip","alert","error","problem","issue"]},"children-crossing":{"a":"Children Crossing","b":"1F6B8","j":["child","crossing","pedestrian","traffic","school","warning","danger","sign","driving","yellow-diamond"]},"no-entry":{"a":"No Entry","b":"26D4","j":["entry","forbidden","no","not","prohibited","traffic","limit","security","privacy","bad","denied","stop","circle"]},"prohibited":{"a":"Prohibited","b":"1F6AB","j":["entry","forbidden","no","not","forbid","stop","limit","denied","disallow","circle"]},"no-bicycles":{"a":"No Bicycles","b":"1F6B3","j":["bicycle","bike","forbidden","no","prohibited","cyclist","circle"]},"no-smoking":{"a":"No Smoking","b":"1F6AD","j":["forbidden","no","not","prohibited","smoking","cigarette","blue-square","smell","smoke"]},"no-littering":{"a":"No Littering","b":"1F6AF","j":["forbidden","litter","no","not","prohibited","trash","bin","garbage","circle"]},"nonpotable-water":{"a":"Non-Potable Water","b":"1F6B1","j":["non-drinking","non-potable","water","non_potable_water","drink","faucet","tap","circle"]},"no-pedestrians":{"a":"No Pedestrians","b":"1F6B7","j":["forbidden","no","not","pedestrian","prohibited","rules","crossing","walking","circle"]},"no-mobile-phones":{"a":"No Mobile Phones","b":"1F4F5","j":["cell","forbidden","mobile","no","phone","iphone","mute","circle"]},"no-one-under-eighteen":{"a":"No One Under Eighteen","b":"1F51E","j":["18","age restriction","eighteen","prohibited","underage","drink","pub","night","minor","circle"]},"radioactive":{"a":"Radioactive","b":"2622","j":["sign","nuclear","danger"]},"biohazard":{"a":"Biohazard","b":"2623","j":["sign","danger"]},"up-arrow":{"a":"Up Arrow","b":"2B06","j":["arrow","cardinal","direction","north","blue-square","continue","top"]},"upright-arrow":{"a":"Up-Right Arrow","b":"2197","j":["arrow","direction","intercardinal","northeast","up-right arrow","up_right_arrow","blue-square","point","diagonal"]},"right-arrow":{"a":"Right Arrow","b":"27A1","j":["arrow","cardinal","direction","east","blue-square","next"]},"downright-arrow":{"a":"Down-Right Arrow","b":"2198","j":["arrow","direction","down-right arrow","intercardinal","southeast","down_right_arrow","blue-square","diagonal"]},"down-arrow":{"a":"Down Arrow","b":"2B07","j":["arrow","cardinal","direction","down","south","blue-square","bottom"]},"downleft-arrow":{"a":"Down-Left Arrow","b":"2199","j":["arrow","direction","down-left arrow","intercardinal","southwest","down_left_arrow","blue-square","diagonal"]},"left-arrow":{"a":"Left Arrow","b":"2B05","j":["arrow","cardinal","direction","west","blue-square","previous","back"]},"upleft-arrow":{"a":"Up-Left Arrow","b":"2196","j":["arrow","direction","intercardinal","northwest","up-left arrow","up_left_arrow","blue-square","point","diagonal"]},"updown-arrow":{"a":"Up-Down Arrow","b":"2195","j":["arrow","up-down arrow","up_down_arrow","blue-square","direction","way","vertical"]},"leftright-arrow":{"a":"Left-Right Arrow","b":"2194","j":["arrow","left-right arrow","left_right_arrow","shape","direction","horizontal","sideways"]},"right-arrow-curving-left":{"a":"Right Arrow Curving Left","b":"21A9","j":["arrow","back","return","blue-square","undo","enter"]},"left-arrow-curving-right":{"a":"Left Arrow Curving Right","b":"21AA","j":["arrow","blue-square","return","rotate","direction"]},"right-arrow-curving-up":{"a":"Right Arrow Curving Up","b":"2934","j":["arrow","blue-square","direction","top"]},"right-arrow-curving-down":{"a":"Right Arrow Curving Down","b":"2935","j":["arrow","down","blue-square","direction","bottom"]},"clockwise-vertical-arrows":{"a":"Clockwise Vertical Arrows","b":"1F503","j":["arrow","clockwise","reload","sync","cycle","round","repeat"]},"counterclockwise-arrows-button":{"a":"Counterclockwise Arrows Button","b":"1F504","j":["anticlockwise","arrow","counterclockwise","withershins","blue-square","sync","cycle"]},"back-arrow":{"a":"Back Arrow","b":"1F519","j":["arrow","back","BACK arrow","words","return"]},"end-arrow":{"a":"End Arrow","b":"1F51A","j":["arrow","end","END arrow","words"]},"on-arrow":{"a":"On! Arrow","b":"1F51B","j":["arrow","mark","on","ON! arrow","words"]},"soon-arrow":{"a":"Soon Arrow","b":"1F51C","j":["arrow","soon","SOON arrow","words"]},"top-arrow":{"a":"Top Arrow","b":"1F51D","j":["arrow","top","TOP arrow","up","words","blue-square"]},"place-of-worship":{"a":"Place of Worship","b":"1F6D0","j":["religion","worship","church","temple","prayer"]},"atom-symbol":{"a":"Atom Symbol","b":"269B","j":["atheist","atom","science","physics","chemistry"]},"om":{"a":"Om","b":"1F549","j":["Hindu","religion","hinduism","buddhism","sikhism","jainism"]},"star-of-david":{"a":"Star of David","b":"2721","j":["David","Jew","Jewish","religion","star","star of David","judaism"]},"wheel-of-dharma":{"a":"Wheel of Dharma","b":"2638","j":["Buddhist","dharma","religion","wheel","hinduism","buddhism","sikhism","jainism"]},"yin-yang":{"a":"Yin Yang","b":"262F","j":["religion","tao","taoist","yang","yin","balance"]},"latin-cross":{"a":"Latin Cross","b":"271D","j":["Christian","cross","religion","christianity"]},"orthodox-cross":{"a":"Orthodox Cross","b":"2626","j":["Christian","cross","religion","suppedaneum"]},"star-and-crescent":{"a":"Star and Crescent","b":"262A","j":["islam","Muslim","religion"]},"peace-symbol":{"a":"Peace Symbol","b":"262E","j":["peace","hippie"]},"menorah":{"a":"Menorah","b":"1F54E","j":["candelabrum","candlestick","religion","hanukkah","candles","jewish"]},"dotted-sixpointed-star":{"a":"Dotted Six-Pointed Star","b":"1F52F","j":["dotted six-pointed star","fortune","star","dotted_six_pointed_star","purple-square","religion","jewish","hexagram"]},"aries":{"a":"Aries","b":"2648","j":["ram","zodiac","sign","purple-square","astrology"]},"taurus":{"a":"Taurus","b":"2649","j":["bull","ox","zodiac","purple-square","sign","astrology"]},"gemini":{"a":"Gemini","b":"264A","j":["twins","zodiac","sign","purple-square","astrology"]},"cancer":{"a":"Cancer","b":"264B","j":["crab","zodiac","sign","purple-square","astrology"]},"leo":{"a":"Leo","b":"264C","j":["lion","zodiac","sign","purple-square","astrology"]},"virgo":{"a":"Virgo","b":"264D","j":["zodiac","sign","purple-square","astrology"]},"libra":{"a":"Libra","b":"264E","j":["balance","justice","scales","zodiac","sign","purple-square","astrology"]},"scorpio":{"a":"Scorpio","b":"264F","j":["scorpion","scorpius","zodiac","sign","purple-square","astrology"]},"sagittarius":{"a":"Sagittarius","b":"2650","j":["archer","zodiac","sign","purple-square","astrology"]},"capricorn":{"a":"Capricorn","b":"2651","j":["goat","zodiac","sign","purple-square","astrology"]},"aquarius":{"a":"Aquarius","b":"2652","j":["bearer","water","zodiac","sign","purple-square","astrology"]},"pisces":{"a":"Pisces","b":"2653","j":["fish","zodiac","purple-square","sign","astrology"]},"ophiuchus":{"a":"Ophiuchus","b":"26CE","j":["bearer","serpent","snake","zodiac","sign","purple-square","constellation","astrology"]},"shuffle-tracks-button":{"a":"Shuffle Tracks Button","b":"1F500","j":["arrow","crossed","blue-square","shuffle","music","random"]},"repeat-button":{"a":"Repeat Button","b":"1F501","j":["arrow","clockwise","repeat","loop","record"]},"repeat-single-button":{"a":"Repeat Single Button","b":"1F502","j":["arrow","clockwise","once","blue-square","loop"]},"play-button":{"a":"Play Button","b":"25B6","j":["arrow","play","right","triangle","blue-square","direction"]},"fastforward-button":{"a":"Fast-Forward Button","b":"23E9","j":["arrow","double","fast","fast-forward button","forward","fast_forward_button","blue-square","play","speed","continue"]},"next-track-button":{"a":"Next Track Button","b":"23ED","j":["arrow","next scene","next track","triangle","forward","next","blue-square"]},"play-or-pause-button":{"a":"Play or Pause Button","b":"23EF","j":["arrow","pause","play","right","triangle","blue-square"]},"reverse-button":{"a":"Reverse Button","b":"25C0","j":["arrow","left","reverse","triangle","blue-square","direction"]},"fast-reverse-button":{"a":"Fast Reverse Button","b":"23EA","j":["arrow","double","rewind","play","blue-square"]},"last-track-button":{"a":"Last Track Button","b":"23EE","j":["arrow","previous scene","previous track","triangle","backward"]},"upwards-button":{"a":"Upwards Button","b":"1F53C","j":["arrow","button","red","blue-square","triangle","direction","point","forward","top"]},"fast-up-button":{"a":"Fast Up Button","b":"23EB","j":["arrow","double","blue-square","direction","top"]},"downwards-button":{"a":"Downwards Button","b":"1F53D","j":["arrow","button","down","red","blue-square","direction","bottom"]},"fast-down-button":{"a":"Fast Down Button","b":"23EC","j":["arrow","double","down","blue-square","direction","bottom"]},"pause-button":{"a":"Pause Button","b":"23F8","j":["bar","double","pause","vertical","blue-square"]},"stop-button":{"a":"Stop Button","b":"23F9","j":["square","stop","blue-square"]},"record-button":{"a":"Record Button","b":"23FA","j":["circle","record","blue-square"]},"eject-button":{"a":"Eject Button","b":"23CF","j":["eject","blue-square"]},"cinema":{"a":"Cinema","b":"1F3A6","j":["camera","film","movie","blue-square","record","curtain","stage","theater"]},"dim-button":{"a":"Dim Button","b":"1F505","j":["brightness","dim","low","sun","afternoon","warm","summer"]},"bright-button":{"a":"Bright Button","b":"1F506","j":["bright","brightness","sun","light"]},"antenna-bars":{"a":"Antenna Bars","b":"1F4F6","j":["antenna","bar","cell","mobile","phone","blue-square","reception","internet","connection","wifi","bluetooth","bars"]},"vibration-mode":{"a":"Vibration Mode","b":"1F4F3","j":["cell","mobile","mode","phone","telephone","vibration","orange-square"]},"mobile-phone-off":{"a":"Mobile Phone off","b":"1F4F4","j":["cell","mobile","off","phone","telephone","mute","orange-square","silence","quiet"]},"female-sign":{"a":"Female Sign","b":"2640","j":["woman","women","lady","girl"]},"male-sign":{"a":"Male Sign","b":"2642","j":["man","boy","men"]},"transgender-symbol":{"a":"Transgender Symbol","b":"26A7","j":["transgender","lgbtq"]},"multiply":{"a":"Multiply","b":"2716","j":["×","cancel","multiplication","sign","x","multiplication_sign","math","calculation"]},"plus":{"a":"Plus","b":"2795","j":["+","math","sign","plus_sign","calculation","addition","more","increase"]},"minus":{"a":"Minus","b":"2796","j":["-","−","math","sign","minus_sign","calculation","subtract","less"]},"divide":{"a":"Divide","b":"2797","j":["÷","division","math","sign","division_sign","calculation"]},"heavy-equals-sign":{"a":"⊛ Heavy Equals Sign","b":"1F7F0","j":["equality","math"]},"infinity":{"a":"Infinity","b":"267E","j":["forever","unbounded","universal"]},"double-exclamation-mark":{"a":"Double Exclamation Mark","b":"203C","j":["!","!!","bangbang","exclamation","mark","surprise"]},"exclamation-question-mark":{"a":"Exclamation Question Mark","b":"2049","j":["!","!?","?","exclamation","interrobang","mark","punctuation","question","wat","surprise"]},"red-question-mark":{"a":"Red Question Mark","b":"2753","j":["?","mark","punctuation","question","question_mark","doubt","confused"]},"white-question-mark":{"a":"White Question Mark","b":"2754","j":["?","mark","outlined","punctuation","question","doubts","gray","huh","confused"]},"white-exclamation-mark":{"a":"White Exclamation Mark","b":"2755","j":["!","exclamation","mark","outlined","punctuation","surprise","gray","wow","warning"]},"red-exclamation-mark":{"a":"Red Exclamation Mark","b":"2757","j":["!","exclamation","mark","punctuation","exclamation_mark","heavy_exclamation_mark","danger","surprise","wow","warning"]},"wavy-dash":{"a":"Wavy Dash","b":"3030","j":["dash","punctuation","wavy","draw","line","moustache","mustache","squiggle","scribble"]},"currency-exchange":{"a":"Currency Exchange","b":"1F4B1","j":["bank","currency","exchange","money","sales","dollar","travel"]},"heavy-dollar-sign":{"a":"Heavy Dollar Sign","b":"1F4B2","j":["currency","dollar","money","sales","payment","buck"]},"medical-symbol":{"a":"Medical Symbol","b":"2695","j":["aesculapius","medicine","staff","health","hospital"]},"recycling-symbol":{"a":"Recycling Symbol","b":"267B","j":["recycle","arrow","environment","garbage","trash"]},"fleurdelis":{"a":"Fleur-De-Lis","b":"269C","j":["fleur-de-lis","fleur_de_lis","decorative","scout"]},"trident-emblem":{"a":"Trident Emblem","b":"1F531","j":["anchor","emblem","ship","tool","trident","weapon","spear"]},"name-badge":{"a":"Name Badge","b":"1F4DB","j":["badge","name","fire","forbid"]},"japanese-symbol-for-beginner":{"a":"Japanese Symbol for Beginner","b":"1F530","j":["beginner","chevron","Japanese","Japanese symbol for beginner","leaf","badge","shield"]},"hollow-red-circle":{"a":"Hollow Red Circle","b":"2B55","j":["circle","large","o","red","round"]},"check-mark-button":{"a":"Check Mark Button","b":"2705","j":["✓","button","check","mark","green-square","ok","agree","vote","election","answer","tick"]},"check-box-with-check":{"a":"Check Box with Check","b":"2611","j":["✓","box","check","ok","agree","confirm","black-square","vote","election","yes","tick"]},"check-mark":{"a":"Check Mark","b":"2714","j":["✓","check","mark","ok","nike","answer","yes","tick"]},"cross-mark":{"a":"Cross Mark","b":"274C","j":["×","cancel","cross","mark","multiplication","multiply","x","no","delete","remove","red"]},"cross-mark-button":{"a":"Cross Mark Button","b":"274E","j":["×","mark","square","x","green-square","no","deny"]},"curly-loop":{"a":"Curly Loop","b":"27B0","j":["curl","loop","scribble","draw","shape","squiggle"]},"double-curly-loop":{"a":"Double Curly Loop","b":"27BF","j":["curl","double","loop","tape","cassette"]},"part-alternation-mark":{"a":"Part Alternation Mark","b":"303D","j":["mark","part","graph","presentation","stats","business","economics","bad"]},"eightspoked-asterisk":{"a":"Eight-Spoked Asterisk","b":"2733","j":["*","asterisk","eight-spoked asterisk","eight_spoked_asterisk","star","sparkle","green-square"]},"eightpointed-star":{"a":"Eight-Pointed Star","b":"2734","j":["*","eight-pointed star","star","eight_pointed_star","orange-square","shape","polygon"]},"sparkle":{"a":"Sparkle","b":"2747","j":["*","stars","green-square","awesome","good","fireworks"]},"copyright":{"a":"Copyright","b":"00A9","j":["c","ip","license","circle","law","legal"]},"registered":{"a":"Registered","b":"00AE","j":["r","alphabet","circle"]},"trade-mark":{"a":"Trade Mark","b":"2122","j":["mark","tm","trademark","brand","law","legal"]},"keycap":{"a":"Keycap: *","b":"002A-FE0F-20E3","j":["keycap_","star"]},"keycap-0":{"a":"Keycap: 0","b":"0030-FE0F-20E3","j":["keycap","0","numbers","blue-square","null"]},"keycap-1":{"a":"Keycap: 1","b":"0031-FE0F-20E3","j":["keycap","blue-square","numbers","1"]},"keycap-2":{"a":"Keycap: 2","b":"0032-FE0F-20E3","j":["keycap","numbers","2","prime","blue-square"]},"keycap-3":{"a":"Keycap: 3","b":"0033-FE0F-20E3","j":["keycap","3","numbers","prime","blue-square"]},"keycap-4":{"a":"Keycap: 4","b":"0034-FE0F-20E3","j":["keycap","4","numbers","blue-square"]},"keycap-5":{"a":"Keycap: 5","b":"0035-FE0F-20E3","j":["keycap","5","numbers","blue-square","prime"]},"keycap-6":{"a":"Keycap: 6","b":"0036-FE0F-20E3","j":["keycap","6","numbers","blue-square"]},"keycap-7":{"a":"Keycap: 7","b":"0037-FE0F-20E3","j":["keycap","7","numbers","blue-square","prime"]},"keycap-8":{"a":"Keycap: 8","b":"0038-FE0F-20E3","j":["keycap","8","blue-square","numbers"]},"keycap-9":{"a":"Keycap: 9","b":"0039-FE0F-20E3","j":["keycap","blue-square","numbers","9"]},"keycap-10":{"a":"Keycap: 10","b":"1F51F","j":["keycap","numbers","10","blue-square"]},"input-latin-uppercase":{"a":"Input Latin Uppercase","b":"1F520","j":["ABCD","input","latin","letters","uppercase","alphabet","words","blue-square"]},"input-latin-lowercase":{"a":"Input Latin Lowercase","b":"1F521","j":["abcd","input","latin","letters","lowercase","blue-square","alphabet"]},"input-numbers":{"a":"Input Numbers","b":"1F522","j":["1234","input","numbers","blue-square"]},"input-symbols":{"a":"Input Symbols","b":"1F523","j":["〒♪&%","input","blue-square","music","note","ampersand","percent","glyphs","characters"]},"input-latin-letters":{"a":"Input Latin Letters","b":"1F524","j":["abc","alphabet","input","latin","letters","blue-square"]},"a-button-blood-type":{"a":"A Button (Blood Type)","b":"1F170","j":["a","A button (blood type)","blood type","a_button","red-square","alphabet","letter"]},"ab-button-blood-type":{"a":"Ab Button (Blood Type)","b":"1F18E","j":["ab","AB button (blood type)","blood type","ab_button","red-square","alphabet"]},"b-button-blood-type":{"a":"B Button (Blood Type)","b":"1F171","j":["b","B button (blood type)","blood type","b_button","red-square","alphabet","letter"]},"cl-button":{"a":"Cl Button","b":"1F191","j":["cl","CL button","alphabet","words","red-square"]},"cool-button":{"a":"Cool Button","b":"1F192","j":["cool","COOL button","words","blue-square"]},"free-button":{"a":"Free Button","b":"1F193","j":["free","FREE button","blue-square","words"]},"information":{"a":"Information","b":"2139","j":["i","blue-square","alphabet","letter"]},"id-button":{"a":"Id Button","b":"1F194","j":["id","ID button","identity","purple-square","words"]},"circled-m":{"a":"Circled M","b":"24C2","j":["circle","circled M","m","alphabet","blue-circle","letter"]},"new-button":{"a":"New Button","b":"1F195","j":["new","NEW button","blue-square","words","start"]},"ng-button":{"a":"Ng Button","b":"1F196","j":["ng","NG button","blue-square","words","shape","icon"]},"o-button-blood-type":{"a":"O Button (Blood Type)","b":"1F17E","j":["blood type","o","O button (blood type)","o_button","alphabet","red-square","letter"]},"ok-button":{"a":"Ok Button","b":"1F197","j":["OK","OK button","good","agree","yes","blue-square"]},"p-button":{"a":"P Button","b":"1F17F","j":["P button","parking","cars","blue-square","alphabet","letter"]},"sos-button":{"a":"Sos Button","b":"1F198","j":["help","sos","SOS button","red-square","words","emergency","911"]},"up-button":{"a":"Up! Button","b":"1F199","j":["mark","up","UP! button","blue-square","above","high"]},"vs-button":{"a":"Vs Button","b":"1F19A","j":["versus","vs","VS button","words","orange-square"]},"japanese-here-button":{"a":"Japanese “Here” Button","b":"1F201","j":["“here”","Japanese","Japanese “here” button","katakana","ココ","blue-square","here","japanese","destination"]},"japanese-service-charge-button":{"a":"Japanese “Service Charge” Button","b":"1F202","j":["“service charge”","Japanese","Japanese “service charge” button","katakana","サ","japanese","blue-square"]},"japanese-monthly-amount-button":{"a":"Japanese “Monthly Amount” Button","b":"1F237","j":["“monthly amount”","ideograph","Japanese","Japanese “monthly amount” button","月","chinese","month","moon","japanese","orange-square","kanji"]},"japanese-not-free-of-charge-button":{"a":"Japanese “Not Free of Charge” Button","b":"1F236","j":["“not free of charge”","ideograph","Japanese","Japanese “not free of charge” button","有","orange-square","chinese","have","kanji"]},"japanese-reserved-button":{"a":"Japanese “Reserved” Button","b":"1F22F","j":["“reserved”","ideograph","Japanese","Japanese “reserved” button","指","chinese","point","green-square","kanji"]},"japanese-bargain-button":{"a":"Japanese “Bargain” Button","b":"1F250","j":["“bargain”","ideograph","Japanese","Japanese “bargain” button","得","chinese","kanji","obtain","get","circle"]},"japanese-discount-button":{"a":"Japanese “Discount” Button","b":"1F239","j":["“discount”","ideograph","Japanese","Japanese “discount” button","割","cut","divide","chinese","kanji","pink-square"]},"japanese-free-of-charge-button":{"a":"Japanese “Free of Charge” Button","b":"1F21A","j":["“free of charge”","ideograph","Japanese","Japanese “free of charge” button","無","nothing","chinese","kanji","japanese","orange-square"]},"japanese-prohibited-button":{"a":"Japanese “Prohibited” Button","b":"1F232","j":["“prohibited”","ideograph","Japanese","Japanese “prohibited” button","禁","kanji","japanese","chinese","forbidden","limit","restricted","red-square"]},"japanese-acceptable-button":{"a":"Japanese “Acceptable” Button","b":"1F251","j":["“acceptable”","ideograph","Japanese","Japanese “acceptable” button","可","ok","good","chinese","kanji","agree","yes","orange-circle"]},"japanese-application-button":{"a":"Japanese “Application” Button","b":"1F238","j":["“application”","ideograph","Japanese","Japanese “application” button","申","chinese","japanese","kanji","orange-square"]},"japanese-passing-grade-button":{"a":"Japanese “Passing Grade” Button","b":"1F234","j":["“passing grade”","ideograph","Japanese","Japanese “passing grade” button","合","japanese","chinese","join","kanji","red-square"]},"japanese-vacancy-button":{"a":"Japanese “Vacancy” Button","b":"1F233","j":["“vacancy”","ideograph","Japanese","Japanese “vacancy” button","空","kanji","japanese","chinese","empty","sky","blue-square"]},"japanese-congratulations-button":{"a":"Japanese “Congratulations” Button","b":"3297","j":["“congratulations”","ideograph","Japanese","Japanese “congratulations” button","祝","chinese","kanji","japanese","red-circle"]},"japanese-secret-button":{"a":"Japanese “Secret” Button","b":"3299","j":["“secret”","ideograph","Japanese","Japanese “secret” button","秘","privacy","chinese","sshh","kanji","red-circle"]},"japanese-open-for-business-button":{"a":"Japanese “Open for Business” Button","b":"1F23A","j":["“open for business”","ideograph","Japanese","Japanese “open for business” button","営","japanese","opening hours","orange-square"]},"japanese-no-vacancy-button":{"a":"Japanese “No Vacancy” Button","b":"1F235","j":["“no vacancy”","ideograph","Japanese","Japanese “no vacancy” button","満","full","chinese","japanese","red-square","kanji"]},"red-circle":{"a":"Red Circle","b":"1F534","j":["circle","geometric","red","shape","error","danger"]},"orange-circle":{"a":"Orange Circle","b":"1F7E0","j":["circle","orange","round"]},"yellow-circle":{"a":"Yellow Circle","b":"1F7E1","j":["circle","yellow","round"]},"green-circle":{"a":"Green Circle","b":"1F7E2","j":["circle","green","round"]},"blue-circle":{"a":"Blue Circle","b":"1F535","j":["blue","circle","geometric","shape","icon","button"]},"purple-circle":{"a":"Purple Circle","b":"1F7E3","j":["circle","purple","round"]},"brown-circle":{"a":"Brown Circle","b":"1F7E4","j":["brown","circle","round"]},"black-circle":{"a":"Black Circle","b":"26AB","j":["circle","geometric","shape","button","round"]},"white-circle":{"a":"White Circle","b":"26AA","j":["circle","geometric","shape","round"]},"red-square":{"a":"Red Square","b":"1F7E5","j":["red","square"]},"orange-square":{"a":"Orange Square","b":"1F7E7","j":["orange","square"]},"yellow-square":{"a":"Yellow Square","b":"1F7E8","j":["square","yellow"]},"green-square":{"a":"Green Square","b":"1F7E9","j":["green","square"]},"blue-square":{"a":"Blue Square","b":"1F7E6","j":["blue","square"]},"purple-square":{"a":"Purple Square","b":"1F7EA","j":["purple","square"]},"brown-square":{"a":"Brown Square","b":"1F7EB","j":["brown","square"]},"black-large-square":{"a":"Black Large Square","b":"2B1B","j":["geometric","square","shape","icon","button"]},"white-large-square":{"a":"White Large Square","b":"2B1C","j":["geometric","square","shape","icon","stone","button"]},"black-medium-square":{"a":"Black Medium Square","b":"25FC","j":["geometric","square","shape","button","icon"]},"white-medium-square":{"a":"White Medium Square","b":"25FB","j":["geometric","square","shape","stone","icon"]},"black-mediumsmall-square":{"a":"Black Medium-Small Square","b":"25FE","j":["black medium-small square","geometric","square","black_medium_small_square","icon","shape","button"]},"white-mediumsmall-square":{"a":"White Medium-Small Square","b":"25FD","j":["geometric","square","white medium-small square","white_medium_small_square","shape","stone","icon","button"]},"black-small-square":{"a":"Black Small Square","b":"25AA","j":["geometric","square","shape","icon"]},"white-small-square":{"a":"White Small Square","b":"25AB","j":["geometric","square","shape","icon"]},"large-orange-diamond":{"a":"Large Orange Diamond","b":"1F536","j":["diamond","geometric","orange","shape","jewel","gem"]},"large-blue-diamond":{"a":"Large Blue Diamond","b":"1F537","j":["blue","diamond","geometric","shape","jewel","gem"]},"small-orange-diamond":{"a":"Small Orange Diamond","b":"1F538","j":["diamond","geometric","orange","shape","jewel","gem"]},"small-blue-diamond":{"a":"Small Blue Diamond","b":"1F539","j":["blue","diamond","geometric","shape","jewel","gem"]},"red-triangle-pointed-up":{"a":"Red Triangle Pointed Up","b":"1F53A","j":["geometric","red","shape","direction","up","top"]},"red-triangle-pointed-down":{"a":"Red Triangle Pointed Down","b":"1F53B","j":["down","geometric","red","shape","direction","bottom"]},"diamond-with-a-dot":{"a":"Diamond with a Dot","b":"1F4A0","j":["comic","diamond","geometric","inside","jewel","blue","gem","crystal","fancy"]},"radio-button":{"a":"Radio Button","b":"1F518","j":["button","geometric","radio","input","old","music","circle"]},"white-square-button":{"a":"White Square Button","b":"1F533","j":["button","geometric","outlined","square","shape","input"]},"black-square-button":{"a":"Black Square Button","b":"1F532","j":["button","geometric","square","shape","input","frame"]},"chequered-flag":{"a":"Chequered Flag","b":"1F3C1","j":["checkered","chequered","racing","contest","finishline","race","gokart"]},"triangular-flag":{"a":"Triangular Flag","b":"1F6A9","j":["post","mark","milestone","place"]},"crossed-flags":{"a":"Crossed Flags","b":"1F38C","j":["celebration","cross","crossed","Japanese","japanese","nation","country","border"]},"black-flag":{"a":"Black Flag","b":"1F3F4","j":["waving","pirate"]},"white-flag":{"a":"White Flag","b":"1F3F3","j":["waving","losing","loser","lost","surrender","give up","fail"]},"rainbow-flag":{"a":"Rainbow Flag","b":"1F3F3-FE0F-200D-1F308","j":["pride","rainbow","flag","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"]},"transgender-flag":{"a":"Transgender Flag","b":"1F3F3-FE0F-200D-26A7-FE0F","j":["flag","light blue","pink","transgender","white","lgbtq"]},"pirate-flag":{"a":"Pirate Flag","b":"1F3F4-200D-2620-FE0F","j":["Jolly Roger","pirate","plunder","treasure","skull","crossbones","flag","banner"]},"flag-ascension-island":{"a":"Flag: Ascension Island","b":"1F1E6-1F1E8","j":["flag"]},"flag-andorra":{"a":"Flag: Andorra","b":"1F1E6-1F1E9","j":["flag","ad","nation","country","banner","andorra"]},"flag-united-arab-emirates":{"a":"Flag: United Arab Emirates","b":"1F1E6-1F1EA","j":["flag","united","arab","emirates","nation","country","banner","united_arab_emirates"]},"flag-afghanistan":{"a":"Flag: Afghanistan","b":"1F1E6-1F1EB","j":["flag","af","nation","country","banner","afghanistan"]},"flag-antigua--barbuda":{"a":"Flag: Antigua & Barbuda","b":"1F1E6-1F1EC","j":["flag","flag_antigua_barbuda","antigua","barbuda","nation","country","banner","antigua_barbuda"]},"flag-anguilla":{"a":"Flag: Anguilla","b":"1F1E6-1F1EE","j":["flag","ai","nation","country","banner","anguilla"]},"flag-albania":{"a":"Flag: Albania","b":"1F1E6-1F1F1","j":["flag","al","nation","country","banner","albania"]},"flag-armenia":{"a":"Flag: Armenia","b":"1F1E6-1F1F2","j":["flag","am","nation","country","banner","armenia"]},"flag-angola":{"a":"Flag: Angola","b":"1F1E6-1F1F4","j":["flag","ao","nation","country","banner","angola"]},"flag-antarctica":{"a":"Flag: Antarctica","b":"1F1E6-1F1F6","j":["flag","aq","nation","country","banner","antarctica"]},"flag-argentina":{"a":"Flag: Argentina","b":"1F1E6-1F1F7","j":["flag","ar","nation","country","banner","argentina"]},"flag-american-samoa":{"a":"Flag: American Samoa","b":"1F1E6-1F1F8","j":["flag","american","ws","nation","country","banner","american_samoa"]},"flag-austria":{"a":"Flag: Austria","b":"1F1E6-1F1F9","j":["flag","at","nation","country","banner","austria"]},"flag-australia":{"a":"Flag: Australia","b":"1F1E6-1F1FA","j":["flag","au","nation","country","banner","australia"]},"flag-aruba":{"a":"Flag: Aruba","b":"1F1E6-1F1FC","j":["flag","aw","nation","country","banner","aruba"]},"flag-land-islands":{"a":"Flag: Åland Islands","b":"1F1E6-1F1FD","j":["flag","flag_aland_islands","Åland","islands","nation","country","banner","aland_islands"]},"flag-azerbaijan":{"a":"Flag: Azerbaijan","b":"1F1E6-1F1FF","j":["flag","az","nation","country","banner","azerbaijan"]},"flag-bosnia--herzegovina":{"a":"Flag: Bosnia & Herzegovina","b":"1F1E7-1F1E6","j":["flag","flag_bosnia_herzegovina","bosnia","herzegovina","nation","country","banner","bosnia_herzegovina"]},"flag-barbados":{"a":"Flag: Barbados","b":"1F1E7-1F1E7","j":["flag","bb","nation","country","banner","barbados"]},"flag-bangladesh":{"a":"Flag: Bangladesh","b":"1F1E7-1F1E9","j":["flag","bd","nation","country","banner","bangladesh"]},"flag-belgium":{"a":"Flag: Belgium","b":"1F1E7-1F1EA","j":["flag","be","nation","country","banner","belgium"]},"flag-burkina-faso":{"a":"Flag: Burkina Faso","b":"1F1E7-1F1EB","j":["flag","burkina","faso","nation","country","banner","burkina_faso"]},"flag-bulgaria":{"a":"Flag: Bulgaria","b":"1F1E7-1F1EC","j":["flag","bg","nation","country","banner","bulgaria"]},"flag-bahrain":{"a":"Flag: Bahrain","b":"1F1E7-1F1ED","j":["flag","bh","nation","country","banner","bahrain"]},"flag-burundi":{"a":"Flag: Burundi","b":"1F1E7-1F1EE","j":["flag","bi","nation","country","banner","burundi"]},"flag-benin":{"a":"Flag: Benin","b":"1F1E7-1F1EF","j":["flag","bj","nation","country","banner","benin"]},"flag-st-barthlemy":{"a":"Flag: St. Barthélemy","b":"1F1E7-1F1F1","j":["flag","flag_st_barthelemy","saint","barthélemy","nation","country","banner","st_barthelemy"]},"flag-bermuda":{"a":"Flag: Bermuda","b":"1F1E7-1F1F2","j":["flag","bm","nation","country","banner","bermuda"]},"flag-brunei":{"a":"Flag: Brunei","b":"1F1E7-1F1F3","j":["flag","bn","darussalam","nation","country","banner","brunei"]},"flag-bolivia":{"a":"Flag: Bolivia","b":"1F1E7-1F1F4","j":["flag","bo","nation","country","banner","bolivia"]},"flag-caribbean-netherlands":{"a":"Flag: Caribbean Netherlands","b":"1F1E7-1F1F6","j":["flag","bonaire","nation","country","banner","caribbean_netherlands"]},"flag-brazil":{"a":"Flag: Brazil","b":"1F1E7-1F1F7","j":["flag","br","nation","country","banner","brazil"]},"flag-bahamas":{"a":"Flag: Bahamas","b":"1F1E7-1F1F8","j":["flag","bs","nation","country","banner","bahamas"]},"flag-bhutan":{"a":"Flag: Bhutan","b":"1F1E7-1F1F9","j":["flag","bt","nation","country","banner","bhutan"]},"flag-bouvet-island":{"a":"Flag: Bouvet Island","b":"1F1E7-1F1FB","j":["flag","norway"]},"flag-botswana":{"a":"Flag: Botswana","b":"1F1E7-1F1FC","j":["flag","bw","nation","country","banner","botswana"]},"flag-belarus":{"a":"Flag: Belarus","b":"1F1E7-1F1FE","j":["flag","by","nation","country","banner","belarus"]},"flag-belize":{"a":"Flag: Belize","b":"1F1E7-1F1FF","j":["flag","bz","nation","country","banner","belize"]},"flag-canada":{"a":"Flag: Canada","b":"1F1E8-1F1E6","j":["flag","ca","nation","country","banner","canada"]},"flag-cocos-keeling-islands":{"a":"Flag: Cocos (Keeling) Islands","b":"1F1E8-1F1E8","j":["flag","flag_cocos_islands","cocos","keeling","islands","nation","country","banner","cocos_islands"]},"flag-congo--kinshasa":{"a":"Flag: Congo - Kinshasa","b":"1F1E8-1F1E9","j":["flag","flag_congo_kinshasa","congo","democratic","republic","nation","country","banner","congo_kinshasa"]},"flag-central-african-republic":{"a":"Flag: Central African Republic","b":"1F1E8-1F1EB","j":["flag","central","african","republic","nation","country","banner","central_african_republic"]},"flag-congo--brazzaville":{"a":"Flag: Congo - Brazzaville","b":"1F1E8-1F1EC","j":["flag","flag_congo_brazzaville","congo","nation","country","banner","congo_brazzaville"]},"flag-switzerland":{"a":"Flag: Switzerland","b":"1F1E8-1F1ED","j":["flag","ch","nation","country","banner","switzerland"]},"flag-cte-divoire":{"a":"Flag: Côte D’Ivoire","b":"1F1E8-1F1EE","j":["flag","flag_cote_d_ivoire","ivory","coast","nation","country","banner","cote_d_ivoire"]},"flag-cook-islands":{"a":"Flag: Cook Islands","b":"1F1E8-1F1F0","j":["flag","cook","islands","nation","country","banner","cook_islands"]},"flag-chile":{"a":"Flag: Chile","b":"1F1E8-1F1F1","j":["flag","nation","country","banner","chile"]},"flag-cameroon":{"a":"Flag: Cameroon","b":"1F1E8-1F1F2","j":["flag","cm","nation","country","banner","cameroon"]},"flag-china":{"a":"Flag: China","b":"1F1E8-1F1F3","j":["flag","china","chinese","prc","country","nation","banner"]},"flag-colombia":{"a":"Flag: Colombia","b":"1F1E8-1F1F4","j":["flag","co","nation","country","banner","colombia"]},"flag-clipperton-island":{"a":"Flag: Clipperton Island","b":"1F1E8-1F1F5","j":["flag"]},"flag-costa-rica":{"a":"Flag: Costa Rica","b":"1F1E8-1F1F7","j":["flag","costa","rica","nation","country","banner","costa_rica"]},"flag-cuba":{"a":"Flag: Cuba","b":"1F1E8-1F1FA","j":["flag","cu","nation","country","banner","cuba"]},"flag-cape-verde":{"a":"Flag: Cape Verde","b":"1F1E8-1F1FB","j":["flag","cabo","verde","nation","country","banner","cape_verde"]},"flag-curaao":{"a":"Flag: Curaçao","b":"1F1E8-1F1FC","j":["flag","flag_curacao","curaçao","nation","country","banner","curacao"]},"flag-christmas-island":{"a":"Flag: Christmas Island","b":"1F1E8-1F1FD","j":["flag","christmas","island","nation","country","banner","christmas_island"]},"flag-cyprus":{"a":"Flag: Cyprus","b":"1F1E8-1F1FE","j":["flag","cy","nation","country","banner","cyprus"]},"flag-czechia":{"a":"Flag: Czechia","b":"1F1E8-1F1FF","j":["flag","cz","nation","country","banner","czechia"]},"flag-germany":{"a":"Flag: Germany","b":"1F1E9-1F1EA","j":["flag","german","nation","country","banner","germany"]},"flag-diego-garcia":{"a":"Flag: Diego Garcia","b":"1F1E9-1F1EC","j":["flag"]},"flag-djibouti":{"a":"Flag: Djibouti","b":"1F1E9-1F1EF","j":["flag","dj","nation","country","banner","djibouti"]},"flag-denmark":{"a":"Flag: Denmark","b":"1F1E9-1F1F0","j":["flag","dk","nation","country","banner","denmark"]},"flag-dominica":{"a":"Flag: Dominica","b":"1F1E9-1F1F2","j":["flag","dm","nation","country","banner","dominica"]},"flag-dominican-republic":{"a":"Flag: Dominican Republic","b":"1F1E9-1F1F4","j":["flag","dominican","republic","nation","country","banner","dominican_republic"]},"flag-algeria":{"a":"Flag: Algeria","b":"1F1E9-1F1FF","j":["flag","dz","nation","country","banner","algeria"]},"flag-ceuta--melilla":{"a":"Flag: Ceuta & Melilla","b":"1F1EA-1F1E6","j":["flag","flag_ceuta_melilla"]},"flag-ecuador":{"a":"Flag: Ecuador","b":"1F1EA-1F1E8","j":["flag","ec","nation","country","banner","ecuador"]},"flag-estonia":{"a":"Flag: Estonia","b":"1F1EA-1F1EA","j":["flag","ee","nation","country","banner","estonia"]},"flag-egypt":{"a":"Flag: Egypt","b":"1F1EA-1F1EC","j":["flag","eg","nation","country","banner","egypt"]},"flag-western-sahara":{"a":"Flag: Western Sahara","b":"1F1EA-1F1ED","j":["flag","western","sahara","nation","country","banner","western_sahara"]},"flag-eritrea":{"a":"Flag: Eritrea","b":"1F1EA-1F1F7","j":["flag","er","nation","country","banner","eritrea"]},"flag-spain":{"a":"Flag: Spain","b":"1F1EA-1F1F8","j":["flag","spain","nation","country","banner"]},"flag-ethiopia":{"a":"Flag: Ethiopia","b":"1F1EA-1F1F9","j":["flag","et","nation","country","banner","ethiopia"]},"flag-european-union":{"a":"Flag: European Union","b":"1F1EA-1F1FA","j":["flag","european","union","banner"]},"flag-finland":{"a":"Flag: Finland","b":"1F1EB-1F1EE","j":["flag","fi","nation","country","banner","finland"]},"flag-fiji":{"a":"Flag: Fiji","b":"1F1EB-1F1EF","j":["flag","fj","nation","country","banner","fiji"]},"flag-falkland-islands":{"a":"Flag: Falkland Islands","b":"1F1EB-1F1F0","j":["flag","falkland","islands","malvinas","nation","country","banner","falkland_islands"]},"flag-micronesia":{"a":"Flag: Micronesia","b":"1F1EB-1F1F2","j":["flag","micronesia","federated","states","nation","country","banner"]},"flag-faroe-islands":{"a":"Flag: Faroe Islands","b":"1F1EB-1F1F4","j":["flag","faroe","islands","nation","country","banner","faroe_islands"]},"flag-france":{"a":"Flag: France","b":"1F1EB-1F1F7","j":["flag","banner","nation","france","french","country"]},"flag-gabon":{"a":"Flag: Gabon","b":"1F1EC-1F1E6","j":["flag","ga","nation","country","banner","gabon"]},"flag-united-kingdom":{"a":"Flag: United Kingdom","b":"1F1EC-1F1E7","j":["flag","united","kingdom","great","britain","northern","ireland","nation","country","banner","british","UK","english","england","union jack","united_kingdom"]},"flag-grenada":{"a":"Flag: Grenada","b":"1F1EC-1F1E9","j":["flag","gd","nation","country","banner","grenada"]},"flag-georgia":{"a":"Flag: Georgia","b":"1F1EC-1F1EA","j":["flag","ge","nation","country","banner","georgia"]},"flag-french-guiana":{"a":"Flag: French Guiana","b":"1F1EC-1F1EB","j":["flag","french","guiana","nation","country","banner","french_guiana"]},"flag-guernsey":{"a":"Flag: Guernsey","b":"1F1EC-1F1EC","j":["flag","gg","nation","country","banner","guernsey"]},"flag-ghana":{"a":"Flag: Ghana","b":"1F1EC-1F1ED","j":["flag","gh","nation","country","banner","ghana"]},"flag-gibraltar":{"a":"Flag: Gibraltar","b":"1F1EC-1F1EE","j":["flag","gi","nation","country","banner","gibraltar"]},"flag-greenland":{"a":"Flag: Greenland","b":"1F1EC-1F1F1","j":["flag","gl","nation","country","banner","greenland"]},"flag-gambia":{"a":"Flag: Gambia","b":"1F1EC-1F1F2","j":["flag","gm","nation","country","banner","gambia"]},"flag-guinea":{"a":"Flag: Guinea","b":"1F1EC-1F1F3","j":["flag","gn","nation","country","banner","guinea"]},"flag-guadeloupe":{"a":"Flag: Guadeloupe","b":"1F1EC-1F1F5","j":["flag","gp","nation","country","banner","guadeloupe"]},"flag-equatorial-guinea":{"a":"Flag: Equatorial Guinea","b":"1F1EC-1F1F6","j":["flag","equatorial","gn","nation","country","banner","equatorial_guinea"]},"flag-greece":{"a":"Flag: Greece","b":"1F1EC-1F1F7","j":["flag","gr","nation","country","banner","greece"]},"flag-south-georgia--south-sandwich-islands":{"a":"Flag: South Georgia & South Sandwich Islands","b":"1F1EC-1F1F8","j":["flag","flag_south_georgia_south_sandwich_islands","south","georgia","sandwich","islands","nation","country","banner","south_georgia_south_sandwich_islands"]},"flag-guatemala":{"a":"Flag: Guatemala","b":"1F1EC-1F1F9","j":["flag","gt","nation","country","banner","guatemala"]},"flag-guam":{"a":"Flag: Guam","b":"1F1EC-1F1FA","j":["flag","gu","nation","country","banner","guam"]},"flag-guineabissau":{"a":"Flag: Guinea-Bissau","b":"1F1EC-1F1FC","j":["flag","flag_guinea_bissau","gw","bissau","nation","country","banner","guinea_bissau"]},"flag-guyana":{"a":"Flag: Guyana","b":"1F1EC-1F1FE","j":["flag","gy","nation","country","banner","guyana"]},"flag-hong-kong-sar-china":{"a":"Flag: Hong Kong Sar China","b":"1F1ED-1F1F0","j":["flag","hong","kong","nation","country","banner","hong_kong_sar_china"]},"flag-heard--mcdonald-islands":{"a":"Flag: Heard & Mcdonald Islands","b":"1F1ED-1F1F2","j":["flag","flag_heard_mcdonald_islands"]},"flag-honduras":{"a":"Flag: Honduras","b":"1F1ED-1F1F3","j":["flag","hn","nation","country","banner","honduras"]},"flag-croatia":{"a":"Flag: Croatia","b":"1F1ED-1F1F7","j":["flag","hr","nation","country","banner","croatia"]},"flag-haiti":{"a":"Flag: Haiti","b":"1F1ED-1F1F9","j":["flag","ht","nation","country","banner","haiti"]},"flag-hungary":{"a":"Flag: Hungary","b":"1F1ED-1F1FA","j":["flag","hu","nation","country","banner","hungary"]},"flag-canary-islands":{"a":"Flag: Canary Islands","b":"1F1EE-1F1E8","j":["flag","canary","islands","nation","country","banner","canary_islands"]},"flag-indonesia":{"a":"Flag: Indonesia","b":"1F1EE-1F1E9","j":["flag","nation","country","banner","indonesia"]},"flag-ireland":{"a":"Flag: Ireland","b":"1F1EE-1F1EA","j":["flag","ie","nation","country","banner","ireland"]},"flag-israel":{"a":"Flag: Israel","b":"1F1EE-1F1F1","j":["flag","il","nation","country","banner","israel"]},"flag-isle-of-man":{"a":"Flag: Isle of Man","b":"1F1EE-1F1F2","j":["flag","isle","man","nation","country","banner","isle_of_man"]},"flag-india":{"a":"Flag: India","b":"1F1EE-1F1F3","j":["flag","in","nation","country","banner","india"]},"flag-british-indian-ocean-territory":{"a":"Flag: British Indian Ocean Territory","b":"1F1EE-1F1F4","j":["flag","british","indian","ocean","territory","nation","country","banner","british_indian_ocean_territory"]},"flag-iraq":{"a":"Flag: Iraq","b":"1F1EE-1F1F6","j":["flag","iq","nation","country","banner","iraq"]},"flag-iran":{"a":"Flag: Iran","b":"1F1EE-1F1F7","j":["flag","iran","islamic","republic","nation","country","banner"]},"flag-iceland":{"a":"Flag: Iceland","b":"1F1EE-1F1F8","j":["flag","is","nation","country","banner","iceland"]},"flag-italy":{"a":"Flag: Italy","b":"1F1EE-1F1F9","j":["flag","italy","nation","country","banner"]},"flag-jersey":{"a":"Flag: Jersey","b":"1F1EF-1F1EA","j":["flag","je","nation","country","banner","jersey"]},"flag-jamaica":{"a":"Flag: Jamaica","b":"1F1EF-1F1F2","j":["flag","jm","nation","country","banner","jamaica"]},"flag-jordan":{"a":"Flag: Jordan","b":"1F1EF-1F1F4","j":["flag","jo","nation","country","banner","jordan"]},"flag-japan":{"a":"Flag: Japan","b":"1F1EF-1F1F5","j":["flag","japanese","nation","country","banner","japan"]},"flag-kenya":{"a":"Flag: Kenya","b":"1F1F0-1F1EA","j":["flag","ke","nation","country","banner","kenya"]},"flag-kyrgyzstan":{"a":"Flag: Kyrgyzstan","b":"1F1F0-1F1EC","j":["flag","kg","nation","country","banner","kyrgyzstan"]},"flag-cambodia":{"a":"Flag: Cambodia","b":"1F1F0-1F1ED","j":["flag","kh","nation","country","banner","cambodia"]},"flag-kiribati":{"a":"Flag: Kiribati","b":"1F1F0-1F1EE","j":["flag","ki","nation","country","banner","kiribati"]},"flag-comoros":{"a":"Flag: Comoros","b":"1F1F0-1F1F2","j":["flag","km","nation","country","banner","comoros"]},"flag-st-kitts--nevis":{"a":"Flag: St. Kitts & Nevis","b":"1F1F0-1F1F3","j":["flag","flag_st_kitts_nevis","saint","kitts","nevis","nation","country","banner","st_kitts_nevis"]},"flag-north-korea":{"a":"Flag: North Korea","b":"1F1F0-1F1F5","j":["flag","north","korea","nation","country","banner","north_korea"]},"flag-south-korea":{"a":"Flag: South Korea","b":"1F1F0-1F1F7","j":["flag","south","korea","nation","country","banner","south_korea"]},"flag-kuwait":{"a":"Flag: Kuwait","b":"1F1F0-1F1FC","j":["flag","kw","nation","country","banner","kuwait"]},"flag-cayman-islands":{"a":"Flag: Cayman Islands","b":"1F1F0-1F1FE","j":["flag","cayman","islands","nation","country","banner","cayman_islands"]},"flag-kazakhstan":{"a":"Flag: Kazakhstan","b":"1F1F0-1F1FF","j":["flag","kz","nation","country","banner","kazakhstan"]},"flag-laos":{"a":"Flag: Laos","b":"1F1F1-1F1E6","j":["flag","lao","democratic","republic","nation","country","banner","laos"]},"flag-lebanon":{"a":"Flag: Lebanon","b":"1F1F1-1F1E7","j":["flag","lb","nation","country","banner","lebanon"]},"flag-st-lucia":{"a":"Flag: St. Lucia","b":"1F1F1-1F1E8","j":["flag","saint","lucia","nation","country","banner","st_lucia"]},"flag-liechtenstein":{"a":"Flag: Liechtenstein","b":"1F1F1-1F1EE","j":["flag","li","nation","country","banner","liechtenstein"]},"flag-sri-lanka":{"a":"Flag: Sri Lanka","b":"1F1F1-1F1F0","j":["flag","sri","lanka","nation","country","banner","sri_lanka"]},"flag-liberia":{"a":"Flag: Liberia","b":"1F1F1-1F1F7","j":["flag","lr","nation","country","banner","liberia"]},"flag-lesotho":{"a":"Flag: Lesotho","b":"1F1F1-1F1F8","j":["flag","ls","nation","country","banner","lesotho"]},"flag-lithuania":{"a":"Flag: Lithuania","b":"1F1F1-1F1F9","j":["flag","lt","nation","country","banner","lithuania"]},"flag-luxembourg":{"a":"Flag: Luxembourg","b":"1F1F1-1F1FA","j":["flag","lu","nation","country","banner","luxembourg"]},"flag-latvia":{"a":"Flag: Latvia","b":"1F1F1-1F1FB","j":["flag","lv","nation","country","banner","latvia"]},"flag-libya":{"a":"Flag: Libya","b":"1F1F1-1F1FE","j":["flag","ly","nation","country","banner","libya"]},"flag-morocco":{"a":"Flag: Morocco","b":"1F1F2-1F1E6","j":["flag","ma","nation","country","banner","morocco"]},"flag-monaco":{"a":"Flag: Monaco","b":"1F1F2-1F1E8","j":["flag","mc","nation","country","banner","monaco"]},"flag-moldova":{"a":"Flag: Moldova","b":"1F1F2-1F1E9","j":["flag","moldova","republic","nation","country","banner"]},"flag-montenegro":{"a":"Flag: Montenegro","b":"1F1F2-1F1EA","j":["flag","me","nation","country","banner","montenegro"]},"flag-st-martin":{"a":"Flag: St. Martin","b":"1F1F2-1F1EB","j":["flag"]},"flag-madagascar":{"a":"Flag: Madagascar","b":"1F1F2-1F1EC","j":["flag","mg","nation","country","banner","madagascar"]},"flag-marshall-islands":{"a":"Flag: Marshall Islands","b":"1F1F2-1F1ED","j":["flag","marshall","islands","nation","country","banner","marshall_islands"]},"flag-north-macedonia":{"a":"Flag: North Macedonia","b":"1F1F2-1F1F0","j":["flag","macedonia","nation","country","banner","north_macedonia"]},"flag-mali":{"a":"Flag: Mali","b":"1F1F2-1F1F1","j":["flag","ml","nation","country","banner","mali"]},"flag-myanmar-burma":{"a":"Flag: Myanmar (Burma)","b":"1F1F2-1F1F2","j":["flag","flag_myanmar","mm","nation","country","banner","myanmar"]},"flag-mongolia":{"a":"Flag: Mongolia","b":"1F1F2-1F1F3","j":["flag","mn","nation","country","banner","mongolia"]},"flag-macao-sar-china":{"a":"Flag: Macao Sar China","b":"1F1F2-1F1F4","j":["flag","macao","nation","country","banner","macao_sar_china"]},"flag-northern-mariana-islands":{"a":"Flag: Northern Mariana Islands","b":"1F1F2-1F1F5","j":["flag","northern","mariana","islands","nation","country","banner","northern_mariana_islands"]},"flag-martinique":{"a":"Flag: Martinique","b":"1F1F2-1F1F6","j":["flag","mq","nation","country","banner","martinique"]},"flag-mauritania":{"a":"Flag: Mauritania","b":"1F1F2-1F1F7","j":["flag","mr","nation","country","banner","mauritania"]},"flag-montserrat":{"a":"Flag: Montserrat","b":"1F1F2-1F1F8","j":["flag","ms","nation","country","banner","montserrat"]},"flag-malta":{"a":"Flag: Malta","b":"1F1F2-1F1F9","j":["flag","mt","nation","country","banner","malta"]},"flag-mauritius":{"a":"Flag: Mauritius","b":"1F1F2-1F1FA","j":["flag","mu","nation","country","banner","mauritius"]},"flag-maldives":{"a":"Flag: Maldives","b":"1F1F2-1F1FB","j":["flag","mv","nation","country","banner","maldives"]},"flag-malawi":{"a":"Flag: Malawi","b":"1F1F2-1F1FC","j":["flag","mw","nation","country","banner","malawi"]},"flag-mexico":{"a":"Flag: Mexico","b":"1F1F2-1F1FD","j":["flag","mx","nation","country","banner","mexico"]},"flag-malaysia":{"a":"Flag: Malaysia","b":"1F1F2-1F1FE","j":["flag","my","nation","country","banner","malaysia"]},"flag-mozambique":{"a":"Flag: Mozambique","b":"1F1F2-1F1FF","j":["flag","mz","nation","country","banner","mozambique"]},"flag-namibia":{"a":"Flag: Namibia","b":"1F1F3-1F1E6","j":["flag","na","nation","country","banner","namibia"]},"flag-new-caledonia":{"a":"Flag: New Caledonia","b":"1F1F3-1F1E8","j":["flag","new","caledonia","nation","country","banner","new_caledonia"]},"flag-niger":{"a":"Flag: Niger","b":"1F1F3-1F1EA","j":["flag","ne","nation","country","banner","niger"]},"flag-norfolk-island":{"a":"Flag: Norfolk Island","b":"1F1F3-1F1EB","j":["flag","norfolk","island","nation","country","banner","norfolk_island"]},"flag-nigeria":{"a":"Flag: Nigeria","b":"1F1F3-1F1EC","j":["flag","nation","country","banner","nigeria"]},"flag-nicaragua":{"a":"Flag: Nicaragua","b":"1F1F3-1F1EE","j":["flag","ni","nation","country","banner","nicaragua"]},"flag-netherlands":{"a":"Flag: Netherlands","b":"1F1F3-1F1F1","j":["flag","nl","nation","country","banner","netherlands"]},"flag-norway":{"a":"Flag: Norway","b":"1F1F3-1F1F4","j":["flag","no","nation","country","banner","norway"]},"flag-nepal":{"a":"Flag: Nepal","b":"1F1F3-1F1F5","j":["flag","np","nation","country","banner","nepal"]},"flag-nauru":{"a":"Flag: Nauru","b":"1F1F3-1F1F7","j":["flag","nr","nation","country","banner","nauru"]},"flag-niue":{"a":"Flag: Niue","b":"1F1F3-1F1FA","j":["flag","nu","nation","country","banner","niue"]},"flag-new-zealand":{"a":"Flag: New Zealand","b":"1F1F3-1F1FF","j":["flag","new","zealand","nation","country","banner","new_zealand"]},"flag-oman":{"a":"Flag: Oman","b":"1F1F4-1F1F2","j":["flag","om_symbol","nation","country","banner","oman"]},"flag-panama":{"a":"Flag: Panama","b":"1F1F5-1F1E6","j":["flag","pa","nation","country","banner","panama"]},"flag-peru":{"a":"Flag: Peru","b":"1F1F5-1F1EA","j":["flag","pe","nation","country","banner","peru"]},"flag-french-polynesia":{"a":"Flag: French Polynesia","b":"1F1F5-1F1EB","j":["flag","french","polynesia","nation","country","banner","french_polynesia"]},"flag-papua-new-guinea":{"a":"Flag: Papua New Guinea","b":"1F1F5-1F1EC","j":["flag","papua","new","guinea","nation","country","banner","papua_new_guinea"]},"flag-philippines":{"a":"Flag: Philippines","b":"1F1F5-1F1ED","j":["flag","ph","nation","country","banner","philippines"]},"flag-pakistan":{"a":"Flag: Pakistan","b":"1F1F5-1F1F0","j":["flag","pk","nation","country","banner","pakistan"]},"flag-poland":{"a":"Flag: Poland","b":"1F1F5-1F1F1","j":["flag","pl","nation","country","banner","poland"]},"flag-st-pierre--miquelon":{"a":"Flag: St. Pierre & Miquelon","b":"1F1F5-1F1F2","j":["flag","flag_st_pierre_miquelon","saint","pierre","miquelon","nation","country","banner","st_pierre_miquelon"]},"flag-pitcairn-islands":{"a":"Flag: Pitcairn Islands","b":"1F1F5-1F1F3","j":["flag","pitcairn","nation","country","banner","pitcairn_islands"]},"flag-puerto-rico":{"a":"Flag: Puerto Rico","b":"1F1F5-1F1F7","j":["flag","puerto","rico","nation","country","banner","puerto_rico"]},"flag-palestinian-territories":{"a":"Flag: Palestinian Territories","b":"1F1F5-1F1F8","j":["flag","palestine","palestinian","territories","nation","country","banner","palestinian_territories"]},"flag-portugal":{"a":"Flag: Portugal","b":"1F1F5-1F1F9","j":["flag","pt","nation","country","banner","portugal"]},"flag-palau":{"a":"Flag: Palau","b":"1F1F5-1F1FC","j":["flag","pw","nation","country","banner","palau"]},"flag-paraguay":{"a":"Flag: Paraguay","b":"1F1F5-1F1FE","j":["flag","py","nation","country","banner","paraguay"]},"flag-qatar":{"a":"Flag: Qatar","b":"1F1F6-1F1E6","j":["flag","qa","nation","country","banner","qatar"]},"flag-runion":{"a":"Flag: Réunion","b":"1F1F7-1F1EA","j":["flag","flag_reunion","réunion","nation","country","banner","reunion"]},"flag-romania":{"a":"Flag: Romania","b":"1F1F7-1F1F4","j":["flag","ro","nation","country","banner","romania"]},"flag-serbia":{"a":"Flag: Serbia","b":"1F1F7-1F1F8","j":["flag","rs","nation","country","banner","serbia"]},"flag-russia":{"a":"Flag: Russia","b":"1F1F7-1F1FA","j":["flag","russian","federation","nation","country","banner","russia"]},"flag-rwanda":{"a":"Flag: Rwanda","b":"1F1F7-1F1FC","j":["flag","rw","nation","country","banner","rwanda"]},"flag-saudi-arabia":{"a":"Flag: Saudi Arabia","b":"1F1F8-1F1E6","j":["flag","nation","country","banner","saudi_arabia"]},"flag-solomon-islands":{"a":"Flag: Solomon Islands","b":"1F1F8-1F1E7","j":["flag","solomon","islands","nation","country","banner","solomon_islands"]},"flag-seychelles":{"a":"Flag: Seychelles","b":"1F1F8-1F1E8","j":["flag","sc","nation","country","banner","seychelles"]},"flag-sudan":{"a":"Flag: Sudan","b":"1F1F8-1F1E9","j":["flag","sd","nation","country","banner","sudan"]},"flag-sweden":{"a":"Flag: Sweden","b":"1F1F8-1F1EA","j":["flag","se","nation","country","banner","sweden"]},"flag-singapore":{"a":"Flag: Singapore","b":"1F1F8-1F1EC","j":["flag","sg","nation","country","banner","singapore"]},"flag-st-helena":{"a":"Flag: St. Helena","b":"1F1F8-1F1ED","j":["flag","saint","helena","ascension","tristan","cunha","nation","country","banner","st_helena"]},"flag-slovenia":{"a":"Flag: Slovenia","b":"1F1F8-1F1EE","j":["flag","si","nation","country","banner","slovenia"]},"flag-svalbard--jan-mayen":{"a":"Flag: Svalbard & Jan Mayen","b":"1F1F8-1F1EF","j":["flag","flag_svalbard_jan_mayen"]},"flag-slovakia":{"a":"Flag: Slovakia","b":"1F1F8-1F1F0","j":["flag","sk","nation","country","banner","slovakia"]},"flag-sierra-leone":{"a":"Flag: Sierra Leone","b":"1F1F8-1F1F1","j":["flag","sierra","leone","nation","country","banner","sierra_leone"]},"flag-san-marino":{"a":"Flag: San Marino","b":"1F1F8-1F1F2","j":["flag","san","marino","nation","country","banner","san_marino"]},"flag-senegal":{"a":"Flag: Senegal","b":"1F1F8-1F1F3","j":["flag","sn","nation","country","banner","senegal"]},"flag-somalia":{"a":"Flag: Somalia","b":"1F1F8-1F1F4","j":["flag","so","nation","country","banner","somalia"]},"flag-suriname":{"a":"Flag: Suriname","b":"1F1F8-1F1F7","j":["flag","sr","nation","country","banner","suriname"]},"flag-south-sudan":{"a":"Flag: South Sudan","b":"1F1F8-1F1F8","j":["flag","south","sd","nation","country","banner","south_sudan"]},"flag-so-tom--prncipe":{"a":"Flag: São Tomé & Príncipe","b":"1F1F8-1F1F9","j":["flag","flag_sao_tome_principe","sao","tome","principe","nation","country","banner","sao_tome_principe"]},"flag-el-salvador":{"a":"Flag: El Salvador","b":"1F1F8-1F1FB","j":["flag","el","salvador","nation","country","banner","el_salvador"]},"flag-sint-maarten":{"a":"Flag: Sint Maarten","b":"1F1F8-1F1FD","j":["flag","sint","maarten","dutch","nation","country","banner","sint_maarten"]},"flag-syria":{"a":"Flag: Syria","b":"1F1F8-1F1FE","j":["flag","syrian","arab","republic","nation","country","banner","syria"]},"flag-eswatini":{"a":"Flag: Eswatini","b":"1F1F8-1F1FF","j":["flag","sz","nation","country","banner","eswatini"]},"flag-tristan-da-cunha":{"a":"Flag: Tristan Da Cunha","b":"1F1F9-1F1E6","j":["flag"]},"flag-turks--caicos-islands":{"a":"Flag: Turks & Caicos Islands","b":"1F1F9-1F1E8","j":["flag","flag_turks_caicos_islands","turks","caicos","islands","nation","country","banner","turks_caicos_islands"]},"flag-chad":{"a":"Flag: Chad","b":"1F1F9-1F1E9","j":["flag","td","nation","country","banner","chad"]},"flag-french-southern-territories":{"a":"Flag: French Southern Territories","b":"1F1F9-1F1EB","j":["flag","french","southern","territories","nation","country","banner","french_southern_territories"]},"flag-togo":{"a":"Flag: Togo","b":"1F1F9-1F1EC","j":["flag","tg","nation","country","banner","togo"]},"flag-thailand":{"a":"Flag: Thailand","b":"1F1F9-1F1ED","j":["flag","th","nation","country","banner","thailand"]},"flag-tajikistan":{"a":"Flag: Tajikistan","b":"1F1F9-1F1EF","j":["flag","tj","nation","country","banner","tajikistan"]},"flag-tokelau":{"a":"Flag: Tokelau","b":"1F1F9-1F1F0","j":["flag","tk","nation","country","banner","tokelau"]},"flag-timorleste":{"a":"Flag: Timor-Leste","b":"1F1F9-1F1F1","j":["flag","flag_timor_leste","timor","leste","nation","country","banner","timor_leste"]},"flag-turkmenistan":{"a":"Flag: Turkmenistan","b":"1F1F9-1F1F2","j":["flag","nation","country","banner","turkmenistan"]},"flag-tunisia":{"a":"Flag: Tunisia","b":"1F1F9-1F1F3","j":["flag","tn","nation","country","banner","tunisia"]},"flag-tonga":{"a":"Flag: Tonga","b":"1F1F9-1F1F4","j":["flag","to","nation","country","banner","tonga"]},"flag-turkey":{"a":"Flag: Turkey","b":"1F1F9-1F1F7","j":["flag","turkey","nation","country","banner"]},"flag-trinidad--tobago":{"a":"Flag: Trinidad & Tobago","b":"1F1F9-1F1F9","j":["flag","flag_trinidad_tobago","trinidad","tobago","nation","country","banner","trinidad_tobago"]},"flag-tuvalu":{"a":"Flag: Tuvalu","b":"1F1F9-1F1FB","j":["flag","nation","country","banner","tuvalu"]},"flag-taiwan":{"a":"Flag: Taiwan","b":"1F1F9-1F1FC","j":["flag","tw","nation","country","banner","taiwan"]},"flag-tanzania":{"a":"Flag: Tanzania","b":"1F1F9-1F1FF","j":["flag","tanzania","united","republic","nation","country","banner"]},"flag-ukraine":{"a":"Flag: Ukraine","b":"1F1FA-1F1E6","j":["flag","ua","nation","country","banner","ukraine"]},"flag-uganda":{"a":"Flag: Uganda","b":"1F1FA-1F1EC","j":["flag","ug","nation","country","banner","uganda"]},"flag-us-outlying-islands":{"a":"Flag: U.S. Outlying Islands","b":"1F1FA-1F1F2","j":["flag","flag_u_s_outlying_islands"]},"flag-united-nations":{"a":"Flag: United Nations","b":"1F1FA-1F1F3","j":["flag","un","banner"]},"flag-united-states":{"a":"Flag: United States","b":"1F1FA-1F1F8","j":["flag","united","states","america","nation","country","banner","united_states"]},"flag-uruguay":{"a":"Flag: Uruguay","b":"1F1FA-1F1FE","j":["flag","uy","nation","country","banner","uruguay"]},"flag-uzbekistan":{"a":"Flag: Uzbekistan","b":"1F1FA-1F1FF","j":["flag","uz","nation","country","banner","uzbekistan"]},"flag-vatican-city":{"a":"Flag: Vatican City","b":"1F1FB-1F1E6","j":["flag","vatican","city","nation","country","banner","vatican_city"]},"flag-st-vincent--grenadines":{"a":"Flag: St. Vincent & Grenadines","b":"1F1FB-1F1E8","j":["flag","flag_st_vincent_grenadines","saint","vincent","grenadines","nation","country","banner","st_vincent_grenadines"]},"flag-venezuela":{"a":"Flag: Venezuela","b":"1F1FB-1F1EA","j":["flag","ve","bolivarian","republic","nation","country","banner","venezuela"]},"flag-british-virgin-islands":{"a":"Flag: British Virgin Islands","b":"1F1FB-1F1EC","j":["flag","british","virgin","islands","bvi","nation","country","banner","british_virgin_islands"]},"flag-us-virgin-islands":{"a":"Flag: U.S. Virgin Islands","b":"1F1FB-1F1EE","j":["flag","flag_u_s_virgin_islands","virgin","islands","us","nation","country","banner","u_s_virgin_islands"]},"flag-vietnam":{"a":"Flag: Vietnam","b":"1F1FB-1F1F3","j":["flag","viet","nam","nation","country","banner","vietnam"]},"flag-vanuatu":{"a":"Flag: Vanuatu","b":"1F1FB-1F1FA","j":["flag","vu","nation","country","banner","vanuatu"]},"flag-wallis--futuna":{"a":"Flag: Wallis & Futuna","b":"1F1FC-1F1EB","j":["flag","flag_wallis_futuna","wallis","futuna","nation","country","banner","wallis_futuna"]},"flag-samoa":{"a":"Flag: Samoa","b":"1F1FC-1F1F8","j":["flag","ws","nation","country","banner","samoa"]},"flag-kosovo":{"a":"Flag: Kosovo","b":"1F1FD-1F1F0","j":["flag","xk","nation","country","banner","kosovo"]},"flag-yemen":{"a":"Flag: Yemen","b":"1F1FE-1F1EA","j":["flag","ye","nation","country","banner","yemen"]},"flag-mayotte":{"a":"Flag: Mayotte","b":"1F1FE-1F1F9","j":["flag","yt","nation","country","banner","mayotte"]},"flag-south-africa":{"a":"Flag: South Africa","b":"1F1FF-1F1E6","j":["flag","south","africa","nation","country","banner","south_africa"]},"flag-zambia":{"a":"Flag: Zambia","b":"1F1FF-1F1F2","j":["flag","zm","nation","country","banner","zambia"]},"flag-zimbabwe":{"a":"Flag: Zimbabwe","b":"1F1FF-1F1FC","j":["flag","zw","nation","country","banner","zimbabwe"]},"flag-england":{"a":"Flag: England","b":"1F3F4-E0067-E0062-E0065-E006E-E0067-E007F","j":["flag","english"]},"flag-scotland":{"a":"Flag: Scotland","b":"1F3F4-E0067-E0062-E0073-E0063-E0074-E007F","j":["flag","scottish"]},"flag-wales":{"a":"Flag: Wales","b":"1F3F4-E0067-E0062-E0077-E006C-E0073-E007F","j":["flag","welsh"]}},"aliases":{}} \ No newline at end of file From 6cee8871f3a618cebab2b5d5dd065d983743a048 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 11 Oct 2021 16:34:32 +0300 Subject: [PATCH 110/144] Create a new cron Github Action workflow for syncing emojis & sas strings. It will run every Monday at 00:00. It will open two PRs and will be able to optimal update/delete them according to changes with the base branch --- .../workflows/sync-from-external-soruces.yml | 70 +++++++++++++++++++ changelog.d/4216.misc | 1 + 2 files changed, 71 insertions(+) create mode 100644 .github/workflows/sync-from-external-soruces.yml create mode 100644 changelog.d/4216.misc diff --git a/.github/workflows/sync-from-external-soruces.yml b/.github/workflows/sync-from-external-soruces.yml new file mode 100644 index 0000000000..d5207d4b96 --- /dev/null +++ b/.github/workflows/sync-from-external-soruces.yml @@ -0,0 +1,70 @@ +name: Sync Data From External Sources +on: + schedule: + # At 00:00 on every Monday UTC + - cron: '0 0 * * 1' + +jobs: + sync-emojis: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + - name: Install Prerequisite dependencies + run: | + pip install BeautifulSoup4 + pip install requests + - name: Run Emoji script + run: ./tools/import_emojis.py + - name: Create Pull Request for Emojis + uses: peter-evans/create-pull-request@v3 + with: + commit-message: Sync Emojis + title: Sync Emojis + body: | + - Update Emojis from Unicode.org + branch: sync-emojis + base: develop + + sync-sas-strings: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + - name: Install Prerequisite dependencies + run: | + pip install BeautifulSoup4 + pip install requests + - name: Run SAS String script + run: ./tools/import_sas_strings.py + - name: Create Pull Request for SAS Strings + uses: peter-evans/create-pull-request@v3 + with: + commit-message: Sync SAS Strings + title: Sync SAS Strings + body: | + - Update SAS Strings from matrix-doc. + branch: sync-sas-strings + base: develop \ No newline at end of file diff --git a/changelog.d/4216.misc b/changelog.d/4216.misc new file mode 100644 index 0000000000..7a3ec23ee5 --- /dev/null +++ b/changelog.d/4216.misc @@ -0,0 +1 @@ +Implement a new github action workflow to generate two PRs for emoji and sas string sync, issue 3902 | PR 4216 From a26e43e90cfe5c09f6b0edafada369c6f1948451 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 11 Oct 2021 17:31:27 +0200 Subject: [PATCH 111/144] Mavericks 2: clean after PR review --- changelog.d/3890.misc | 1 + docs/mavericks_migration.md | 11 +++++++++++ .../java/org/matrix/android/sdk/flow/FlowExt.kt | 13 ++++++------- .../java/org/matrix/android/sdk/flow/FlowSession.kt | 6 +++--- .../vector/app/core/platform/VectorBaseActivity.kt | 4 ++-- .../vector/app/core/platform/VectorBaseFragment.kt | 4 ++-- .../crypto/quads/SharedSecureStorageViewModel.kt | 1 - .../vector/app/features/home/HomeDetailFragment.kt | 4 ++-- .../login2/created/AccountCreatedFragment.kt | 2 +- .../spaces/create/ChoosePrivateSpaceTypeFragment.kt | 2 +- .../app/features/userdirectory/UserListFragment.kt | 2 +- 11 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 changelog.d/3890.misc create mode 100644 docs/mavericks_migration.md diff --git a/changelog.d/3890.misc b/changelog.d/3890.misc new file mode 100644 index 0000000000..3bace80fa7 --- /dev/null +++ b/changelog.d/3890.misc @@ -0,0 +1 @@ +Migrate to MvRx2 (Mavericks) \ No newline at end of file diff --git a/docs/mavericks_migration.md b/docs/mavericks_migration.md new file mode 100644 index 0000000000..a36ae8261a --- /dev/null +++ b/docs/mavericks_migration.md @@ -0,0 +1,11 @@ +Useful links: +- https://airbnb.io/mavericks/#/new-2x + +Mavericks 2 is replacing MvRx, by removing usage of Rx by Flow, both internally and in the API. +See the link ^ to have more intel, but basically, the changes are: + +session.rx() => session.flow() +room.rx() => room.flow() +subscribe { }.disposeOnClear() => onEach { }.launchIn(viewModelScope) + +Only using manually onEach requires to add launchIn,any other methods provided by Mavericks on viewModel and activity/fragment are already taking care of lifecycle. \ No newline at end of file diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt index dd8a5d7750..f974d89d45 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt @@ -22,11 +22,10 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withContext internal fun Flow.startWith(supplier: suspend () -> T): Flow { - return this - .onStart { - val value = withContext(Dispatchers.IO) { - supplier() - } - emit(value) - } + return onStart { + val value = withContext(Dispatchers.IO) { + supplier() + } + emit(value) + } } diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt index 563ae30b45..75f482adfd 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt @@ -42,7 +42,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo -class RxFlow(private val session: Session) { +class FlowSession(private val session: Session) { fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Flow> { return session.getRoomSummariesLive(queryParams).asFlow() @@ -175,6 +175,6 @@ class RxFlow(private val session: Session) { } } -fun Session.flow(): RxFlow { - return RxFlow(this) +fun Session.flow(): FlowSession { + return FlowSession(this) } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index e9973e82b8..fbfba10d21 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -40,7 +40,7 @@ import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentManager import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding -import com.airbnb.mvrx.MvRxView +import com.airbnb.mvrx.MavericksView import com.bumptech.glide.util.Util import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.snackbar.Snackbar @@ -89,7 +89,7 @@ import timber.log.Timber import java.util.concurrent.TimeUnit import kotlin.system.measureTimeMillis -abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector, MvRxView { +abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector, MavericksView { /* ========================================================================================== * View * ========================================================================================== */ diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index 5077982460..64b55291fb 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -30,7 +30,7 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding -import com.airbnb.mvrx.MvRxView +import com.airbnb.mvrx.MavericksView import com.bumptech.glide.util.Util.assertMainThread import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -50,7 +50,7 @@ import io.reactivex.disposables.Disposable import timber.log.Timber import java.util.concurrent.TimeUnit -abstract class VectorBaseFragment : Fragment(), MvRxView, HasScreenInjector { +abstract class VectorBaseFragment : Fragment(), MavericksView, HasScreenInjector { protected val vectorBaseActivity: VectorBaseActivity<*> by lazy { activity as VectorBaseActivity<*> diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index e175715710..bb4d4ce99a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.crypto.quads -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index a69b9060d6..c8fff5605b 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -161,7 +161,7 @@ class HomeDetailFragment @Inject constructor( } } - unknownDeviceDetectorSharedViewModel.subscribe { state -> + unknownDeviceDetectorSharedViewModel.onEach { state -> state.unknownSessions.invoke()?.let { unknownDevices -> // Timber.v("## Detector Triggerred in fragment - ${unknownDevices.firstOrNull()}") if (unknownDevices.firstOrNull()?.currentSessionTrust == true) { @@ -179,7 +179,7 @@ class HomeDetailFragment @Inject constructor( } } - unreadMessagesSharedViewModel.subscribe { state -> + unreadMessagesSharedViewModel.onEach { state -> views.drawerUnreadCounterBadgeView.render( UnreadCounterBadgeView.State( count = state.otherSpacesUnread.totalCount, diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt index e3d25ef283..5668214b50 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt @@ -72,7 +72,7 @@ class AccountCreatedFragment @Inject constructor( setupSubmitButton() observeViewEvents() - viewModel.subscribe { invalidateState(it) } + viewModel.onEach { invalidateState(it) } views.loginAccountCreatedTime.text = dateFormatter.format(System.currentTimeMillis(), DateFormatKind.MESSAGE_SIMPLE) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt index 4f079551eb..6d3003dfcf 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt @@ -49,7 +49,7 @@ class ChoosePrivateSpaceTypeFragment @Inject constructor( sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.MeAndTeammates)) } - sharedViewModel.subscribe { state -> + sharedViewModel.onEach { state -> views.accessInfoHelpText.text = stringProvider.getString(R.string.create_spaces_make_sure_access, state.name ?: "") } } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 8d649eaa71..8adabf9f02 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -81,7 +81,7 @@ class UserListFragment @Inject constructor( setupRecyclerView() setupSearchView() - homeServerCapabilitiesViewModel.subscribe { + homeServerCapabilitiesViewModel.onEach { views.userListE2EbyDefaultDisabled.isVisible = !it.isE2EByDefault } From 3265c604cfa56cb44f44e7b192d34b04c814511d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 23:05:08 +0000 Subject: [PATCH 112/144] Bump gradle from 7.0.2 to 7.0.3 Bumps gradle from 7.0.2 to 7.0.3. --- updated-dependencies: - dependency-name: com.android.tools.build:gradle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 92358952db..882a4fde09 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -7,7 +7,7 @@ ext.versions = [ 'targetCompat' : JavaVersion.VERSION_11, ] -def gradle = "7.0.2" +def gradle = "7.0.3" // Ref: https://kotlinlang.org/releases.html def kotlin = "1.5.31" def kotlinCoroutines = "1.5.2" From dcf98d93e637f4195080613e7164531a4adad522 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 12 Oct 2021 11:37:00 +0300 Subject: [PATCH 113/144] Remove BeautifulSoup4 dependency --- .github/workflows/sync-from-external-soruces.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sync-from-external-soruces.yml b/.github/workflows/sync-from-external-soruces.yml index d5207d4b96..6a4f8ef147 100644 --- a/.github/workflows/sync-from-external-soruces.yml +++ b/.github/workflows/sync-from-external-soruces.yml @@ -55,7 +55,6 @@ jobs: ${{ runner.os }}- - name: Install Prerequisite dependencies run: | - pip install BeautifulSoup4 pip install requests - name: Run SAS String script run: ./tools/import_sas_strings.py From 0d85299c5748bd3c85e29cafc7134431ed525587 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Oct 2021 20:23:16 +0200 Subject: [PATCH 114/144] Try to fix #4007 Wait for Realm instance to be effectively closed before deleting Realm files --- .../session/cleanup/CleanupSession.kt | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt index d4374e0702..113e0f218a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt @@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.session.cleanup import io.realm.Realm import io.realm.RealmConfiguration -import org.matrix.android.sdk.BuildConfig +import kotlinx.coroutines.delay import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.auth.SessionParamsStore import org.matrix.android.sdk.internal.crypto.CryptoModule @@ -51,6 +51,10 @@ internal class CleanupSession @Inject constructor( @UserMd5 private val userMd5: String ) { suspend fun handle() { + val sessionRealmCount = Realm.getGlobalInstanceCount(realmSessionConfiguration) + val cryptoRealmCount = Realm.getGlobalInstanceCount(realmCryptoConfiguration) + Timber.d("Realm instance ($sessionRealmCount - $cryptoRealmCount)") + Timber.d("Cleanup: delete session params...") sessionParamsStore.delete(sessionId) @@ -63,10 +67,6 @@ internal class CleanupSession @Inject constructor( Timber.d("Cleanup: clear crypto data...") clearCryptoDataTask.execute(Unit) - Timber.d("Cleanup: clear file system") - sessionFiles.deleteRecursively() - sessionCache.deleteRecursively() - Timber.d("Cleanup: clear the database keys") realmKeysUtils.clear(SessionModule.getKeyAlias(userMd5)) realmKeysUtils.clear(CryptoModule.getKeyAlias(userMd5)) @@ -74,14 +74,33 @@ internal class CleanupSession @Inject constructor( Timber.d("Cleanup: release session...") sessionManager.releaseSession(sessionId) - // Sanity check - if (BuildConfig.DEBUG) { - Realm.getGlobalInstanceCount(realmSessionConfiguration) - .takeIf { it > 0 } - ?.let { Timber.e("All realm instance for session has not been closed ($it)") } - Realm.getGlobalInstanceCount(realmCryptoConfiguration) - .takeIf { it > 0 } - ?.let { Timber.e("All realm instance for crypto has not been closed ($it)") } - } + // Wait for all the Realm instance to be released properly. Closing Realm instance is async. + // After that we can safely delete the Realm files + waitRealmRelease() + + Timber.d("Cleanup: clear file system") + sessionFiles.deleteRecursively() + sessionCache.deleteRecursively() + } + + private suspend fun waitRealmRelease() { + var timeToWaitMillis = MAX_TIME_TO_WAIT_MILLIS + do { + val sessionRealmCount = Realm.getGlobalInstanceCount(realmSessionConfiguration) + val cryptoRealmCount = Realm.getGlobalInstanceCount(realmCryptoConfiguration) + Timber.d("Wait for all Realm instance to be closed ($sessionRealmCount - $cryptoRealmCount)") + if (sessionRealmCount > 0 || cryptoRealmCount > 0) { + Timber.d("Waiting ${TIME_TO_WAIT_MILLIS}ms") + delay(TIME_TO_WAIT_MILLIS) + timeToWaitMillis -= TIME_TO_WAIT_MILLIS + } else { + timeToWaitMillis = 0 + } + } while (timeToWaitMillis > 0) + } + + companion object { + private const val MAX_TIME_TO_WAIT_MILLIS = 10_000L + private const val TIME_TO_WAIT_MILLIS = 10L } } From d26340993f0b34df94d77b4c31a18d8113d736a2 Mon Sep 17 00:00:00 2001 From: Aris Kotsomitopoulos <60798129+ariskotsomitopoulos@users.noreply.github.com> Date: Tue, 12 Oct 2021 11:45:34 +0300 Subject: [PATCH 115/144] Update release.yml Removed ./tools/import_emojis.py and ./tools/import_sas_strings.py from the release template while now there is an automated cron job in GitHub Action to run the scripts --- .github/ISSUE_TEMPLATE/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/release.yml b/.github/ISSUE_TEMPLATE/release.yml index 903c05c5d3..7ac55427a9 100644 --- a/.github/ISSUE_TEMPLATE/release.yml +++ b/.github/ISSUE_TEMPLATE/release.yml @@ -23,8 +23,6 @@ body: ### Do the release - [ ] Create release with gitflow, branch name `release/1.1.10` - - [ ] Run `./tools/import_emojis.py` and commit the change if any. - - [ ] Run `./tools/import_sas_strings.py` and commit the change if any. If there is no change since a while, ping Travis - [ ] Check the crashes from the PlayStore - [ ] Check the rageshake with the current dev version: https://github.com/matrix-org/element-android-rageshakes/labels/1.1.10-dev - [ ] Run the integration test, and especially `UiAllScreensSanityTest.allScreensTest()` From b7a54ead681c0396662d47b2df70605c16f9e0ad Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 12 Oct 2021 09:47:17 +0100 Subject: [PATCH 116/144] delaying the first sync until the first process onStart event - fixes push notifications starting the polling sync thread when the application is created due to push --- .../main/java/im/vector/app/VectorApplication.kt | 16 +++++++++++++++- .../im/vector/app/core/extensions/Session.kt | 6 ++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index 240bbca908..f57c143aa3 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -43,6 +43,7 @@ import im.vector.app.core.di.DaggerVectorComponent import im.vector.app.core.di.HasVectorInjector import im.vector.app.core.di.VectorComponent import im.vector.app.core.extensions.configureAndStart +import im.vector.app.core.extensions.startSyncing import im.vector.app.core.rx.RxConfig import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.configuration.VectorConfiguration @@ -162,11 +163,15 @@ class VectorApplication : // Do not display the name change popup doNotShowDisclaimerDialog(this) } + if (authenticationService.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) { val lastAuthenticatedSession = authenticationService.getLastAuthenticatedSession()!! activeSessionHolder.setActiveSession(lastAuthenticatedSession) - lastAuthenticatedSession.configureAndStart(applicationContext) + lastAuthenticatedSession.configureAndStart(applicationContext, startSyncing = false) } + + ProcessLifecycleOwner.get().lifecycle.addObserver(startSyncOnFirstStart) + ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun entersForeground() { @@ -199,6 +204,15 @@ class VectorApplication : EmojiManager.install(GoogleEmojiProvider()) } + private val startSyncOnFirstStart = object : LifecycleObserver { + @OnLifecycleEvent(Lifecycle.Event.ON_START) + fun onStart() { + Timber.i("App process started") + authenticationService.getLastAuthenticatedSession()?.startSyncing(appContext) + ProcessLifecycleOwner.get().lifecycle.removeObserver(this) + } + } + private fun enableStrictModeIfNeeded() { if (BuildConfig.ENABLE_STRICT_MODE_LOGS) { StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index 215c421291..f066fd6784 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -26,11 +26,13 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.sync.FilterService import timber.log.Timber -fun Session.configureAndStart(context: Context) { +fun Session.configureAndStart(context: Context, startSyncing: Boolean = true) { Timber.i("Configure and start session for $myUserId") open() setFilter(FilterService.FilterPreset.ElementFilter) - startSyncing(context) + if (startSyncing) { + startSyncing(context) + } refreshPushers() context.vectorComponent().webRtcCallManager().checkForProtocolsSupportIfNeeded() } From d1d66c3406327c09c7e25a920dc04051f5b68171 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 12 Oct 2021 09:49:23 +0100 Subject: [PATCH 117/144] adding changelog entry --- changelog.d/4167.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4167.bugfix diff --git a/changelog.d/4167.bugfix b/changelog.d/4167.bugfix new file mode 100644 index 0000000000..8df264d8f6 --- /dev/null +++ b/changelog.d/4167.bugfix @@ -0,0 +1 @@ +Fixing push notifications starting the looping background sync when the push notification causes the application to be created. From fc753fe11ec03c85a8a3e72f5587c0917135d807 Mon Sep 17 00:00:00 2001 From: Aris Kotsomitopoulos <60798129+ariskotsomitopoulos@users.noreply.github.com> Date: Tue, 12 Oct 2021 11:52:52 +0300 Subject: [PATCH 118/144] Update 4216.misc --- changelog.d/4216.misc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/4216.misc b/changelog.d/4216.misc index 7a3ec23ee5..acf5f1dbcb 100644 --- a/changelog.d/4216.misc +++ b/changelog.d/4216.misc @@ -1 +1 @@ -Implement a new github action workflow to generate two PRs for emoji and sas string sync, issue 3902 | PR 4216 +Implement a new github action workflow to generate two PRs for emoji and sas string sync From 8cea340100cbb4438bfa9384e799eba24d121c5d Mon Sep 17 00:00:00 2001 From: Ekaterina Gerasimova Date: Tue, 12 Oct 2021 10:03:41 +0100 Subject: [PATCH 119/144] Update defect issue template to improve wording Improve wording around rageshakes based on feedback. Signed-off-by: Ekaterina Gerasimova --- .github/ISSUE_TEMPLATE/bug.yml | 4 ++-- changelog.d/4226.misc | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog.d/4226.misc diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 38885e9cc7..c1ab98e85d 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -64,9 +64,9 @@ body: - type: dropdown id: rageshake attributes: - label: Have you submitted a rageshake? + label: Will you send logs? description: | - Did you know that you can shake your phone to submit logs for this issue? Trigger the defect, then shake your phone and you will see a popup asking if you would like to open the bug report screen. Click YES, and describe the issue, mentioning that you have also filed a bug. Submit the report to send anonymous logs to the developers. + Did you know that you can shake your phone to submit logs for this issue? Trigger the defect, then shake your phone and you will see a popup asking if you would like to open the bug report screen. Click YES, and describe the issue, mentioning that you have also filed a bug (it's helpful if you can include a link to the bug). Send the report to submit anonymous logs to the developers. options: - 'Yes' - 'No' diff --git a/changelog.d/4226.misc b/changelog.d/4226.misc new file mode 100644 index 0000000000..ac7a294f35 --- /dev/null +++ b/changelog.d/4226.misc @@ -0,0 +1 @@ +Improve wording around rageshakes in the defect issue template. From 73c08e2eeb3e57bf4886880832bc9c28f7edc53d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 Oct 2021 11:38:16 +0200 Subject: [PATCH 120/144] Avoid code duplication --- .../internal/crypto/store/IMXCryptoStore.kt | 3 ++- .../crypto/store/db/RealmCryptoStore.kt | 19 +------------------ 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index 238d06738c..9b75f88f91 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -379,7 +379,8 @@ internal interface IMXCryptoStore { fun getOrAddOutgoingSecretShareRequest(secretName: String, recipients: Map>): OutgoingSecretRequest? - fun saveGossipingEvent(event: Event) + fun saveGossipingEvent(event: Event) = saveGossipingEvents(listOf(event)) + fun saveGossipingEvents(events: List) fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 3c8f74d419..a8be0b5578 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -1163,8 +1163,8 @@ internal class RealmCryptoStore @Inject constructor( } override fun saveGossipingEvents(events: List) { - val now = System.currentTimeMillis() monarchy.writeAsync { realm -> + val now = System.currentTimeMillis() events.forEach { event -> val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now val entity = GossipingEventEntity( @@ -1182,23 +1182,6 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun saveGossipingEvent(event: Event) { - monarchy.writeAsync { realm -> - val now = System.currentTimeMillis() - val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now - val entity = GossipingEventEntity( - type = event.type, - sender = event.senderId, - ageLocalTs = ageLocalTs, - content = ContentMapper.map(event.content) - ).apply { - sendState = SendState.SYNCED - decryptionResultJson = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java).toJson(event.mxDecryptionResult) - decryptionErrorCode = event.mCryptoError?.name - } - realm.insertOrUpdate(entity) - } - } // override fun getOutgoingRoomKeyRequestByState(states: Set): OutgoingRoomKeyRequest? { // val statesIndex = states.map { it.ordinal }.toTypedArray() // return doRealmQueryAndCopy(realmConfiguration) { realm -> From 4a7e0a5d95c6dd6f3fbe18fe7f779be1a9dc40f5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 Oct 2021 11:57:07 +0200 Subject: [PATCH 121/144] CleanupSession: start by releasing the session, then empty the databases --- .../android/sdk/internal/session/cleanup/CleanupSession.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt index 113e0f218a..e8d3eb1a78 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt @@ -61,6 +61,9 @@ internal class CleanupSession @Inject constructor( Timber.d("Cleanup: cancel pending works...") workManagerProvider.cancelAllWorks() + Timber.d("Cleanup: release session...") + sessionManager.releaseSession(sessionId) + Timber.d("Cleanup: clear session data...") clearSessionDataTask.execute(Unit) @@ -71,9 +74,6 @@ internal class CleanupSession @Inject constructor( realmKeysUtils.clear(SessionModule.getKeyAlias(userMd5)) realmKeysUtils.clear(CryptoModule.getKeyAlias(userMd5)) - Timber.d("Cleanup: release session...") - sessionManager.releaseSession(sessionId) - // Wait for all the Realm instance to be released properly. Closing Realm instance is async. // After that we can safely delete the Realm files waitRealmRelease() From 2d9764037218ddc7661c1412831411b9c73966c4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 Oct 2021 12:08:14 +0200 Subject: [PATCH 122/144] Ensure no async transaction will occurs if the store is closed --- .../sdk/internal/crypto/store/db/RealmCryptoStore.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index a8be0b5578..f5e3916bb9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -100,6 +100,7 @@ import org.matrix.olm.OlmAccount import org.matrix.olm.OlmException import org.matrix.olm.OlmOutboundGroupSession import timber.log.Timber +import java.util.concurrent.Executors import javax.inject.Inject import kotlin.collections.set @@ -137,8 +138,11 @@ internal class RealmCryptoStore @Inject constructor( newSessionListeners.remove(listener) } + private val monarchyWriteAsyncExecutor = Executors.newSingleThreadExecutor() + private val monarchy = Monarchy.Builder() .setRealmConfiguration(realmConfiguration) + .setWriteAsyncExecutor(monarchyWriteAsyncExecutor) .build() init { @@ -199,6 +203,10 @@ internal class RealmCryptoStore @Inject constructor( } override fun close() { + // Ensure no async request will be run later + val tasks = monarchyWriteAsyncExecutor.shutdownNow() + Timber.w("Closing RealmCryptoStore, ${tasks.size} async task(s) cancelled") + olmSessionsToRelease.forEach { it.value.olmSession.releaseSession() } From 6c9fcc0d939e8c4b2286e3498e9ca9f7cc4a172a Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 1 Oct 2021 19:04:41 +0100 Subject: [PATCH 123/144] extracting the add pusher logic for the worker and delegating to the task from the worker --- .../sdk/api/session/pushers/PushersService.kt | 50 ++++--- .../internal/session/pushers/AddPusherTask.kt | 79 +++++++++++ .../session/pushers/AddPusherWorker.kt | 50 +------ .../session/pushers/DefaultPushersService.kt | 123 ++++++++++-------- .../internal/session/pushers/JsonPusher.kt | 10 +- .../internal/session/pushers/PushersModule.kt | 3 + .../vector/app/core/pushers/PushersManager.kt | 4 +- 7 files changed, 201 insertions(+), 118 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt index 2cd17952c6..03391ff8ba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt @@ -52,15 +52,33 @@ interface PushersService { * (LiveData status = workManager.getWorkInfoByIdLiveData()) * @throws [InvalidParameterException] if a parameter is not correct */ - fun addHttpPusher(pushkey: String, - appId: String, - profileTag: String, - lang: String, - appDisplayName: String, - deviceDisplayName: String, - url: String, - append: Boolean, - withEventIdOnly: Boolean): UUID + suspend fun addHttpPusher(pushkey: String, + appId: String, + profileTag: String, + lang: String, + appDisplayName: String, + deviceDisplayName: String, + url: String, + append: Boolean, + withEventIdOnly: Boolean) + + /** + * Enqueues a new HTTP pusher via the WorkManager API. + * Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set + * + * @return A work request uuid. Can be used to listen to the status + * (LiveData status = workManager.getWorkInfoByIdLiveData()) + * @throws [InvalidParameterException] if a parameter is not correct + */ + fun enqueueAddHttpPusher(pushkey: String, + appId: String, + profileTag: String, + lang: String, + appDisplayName: String, + deviceDisplayName: String, + url: String, + append: Boolean, + withEventIdOnly: Boolean): UUID /** * Add a new Email pusher. @@ -75,16 +93,14 @@ interface PushersService { * to any others with different user IDs. Otherwise, the homeserver must remove any other pushers * with the same App ID and pushkey for different users. Typically We always want to append for * email pushers since we don't want to stop other accounts notifying to the same email address. - * @return A work request uuid. Can be used to listen to the status - * (LiveData status = workManager.getWorkInfoByIdLiveData()) * @throws [InvalidParameterException] if a parameter is not correct */ - fun addEmailPusher(email: String, - lang: String, - emailBranding: String, - appDisplayName: String, - deviceDisplayName: String, - append: Boolean = true): UUID + suspend fun addEmailPusher(email: String, + lang: String, + emailBranding: String, + appDisplayName: String, + deviceDisplayName: String, + append: Boolean = true) /** * Directly ask the push gateway to send a push to this device diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt new file mode 100644 index 0000000000..b4a5ccd383 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.pushers + +import com.zhuinden.monarchy.Monarchy +import org.matrix.android.sdk.api.session.pushers.PusherState +import org.matrix.android.sdk.internal.database.mapper.toEntity +import org.matrix.android.sdk.internal.database.model.PusherEntity +import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.task.Task +import org.matrix.android.sdk.internal.util.awaitTransaction +import javax.inject.Inject + +internal interface AddPusherTask : Task { + data class Params(val pusher: JsonPusher) +} + +internal class DefaultAddPusherTask @Inject constructor( + private val pushersAPI: PushersAPI, + @SessionDatabase private val monarchy: Monarchy, + private val globalErrorReceiver: GlobalErrorReceiver +) : AddPusherTask { + override suspend fun execute(params: AddPusherTask.Params) { + val pusher = params.pusher + try { + setPusher(pusher) + } catch (error: Throwable) { + monarchy.awaitTransaction { realm -> + PusherEntity.where(realm, pusher.pushKey).findFirst()?.let { + // update it + it.state = PusherState.FAILED_TO_REGISTER + } + } + throw error + } + } + + private suspend fun setPusher(pusher: JsonPusher) { + executeRequest(globalErrorReceiver) { + pushersAPI.setPusher(pusher) + } + monarchy.awaitTransaction { realm -> + val echo = PusherEntity.where(realm, pusher.pushKey).findFirst() + if (echo != null) { + // update it + echo.appDisplayName = pusher.appDisplayName + echo.appId = pusher.appId + echo.kind = pusher.kind + echo.lang = pusher.lang + echo.profileTag = pusher.profileTag + echo.data?.format = pusher.data?.format + echo.data?.url = pusher.data?.url + echo.state = PusherState.REGISTERED + } else { + pusher.toEntity().also { + it.state = PusherState.REGISTERED + realm.insertOrUpdate(it) + } + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt index 63fd855c08..4df42b2cfb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt @@ -18,17 +18,8 @@ package org.matrix.android.sdk.internal.session.pushers import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass -import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.failure.Failure -import org.matrix.android.sdk.api.session.pushers.PusherState -import org.matrix.android.sdk.internal.database.mapper.toEntity -import org.matrix.android.sdk.internal.database.model.PusherEntity -import org.matrix.android.sdk.internal.database.query.where -import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.network.GlobalErrorReceiver -import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.SessionComponent -import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import javax.inject.Inject @@ -43,9 +34,7 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) : override val lastFailureMessage: String? = null ) : SessionWorkerParams - @Inject lateinit var pushersAPI: PushersAPI - @Inject @SessionDatabase lateinit var monarchy: Monarchy - @Inject lateinit var globalErrorReceiver: GlobalErrorReceiver + @Inject lateinit var addPusherTask: AddPusherTask override fun injectWith(injector: SessionComponent) { injector.inject(this) @@ -58,20 +47,12 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) : return Result.failure() } return try { - setPusher(pusher) + addPusherTask.execute(AddPusherTask.Params(pusher)) Result.success() } catch (exception: Throwable) { when (exception) { is Failure.NetworkConnection -> Result.retry() - else -> { - monarchy.awaitTransaction { realm -> - PusherEntity.where(realm, pusher.pushKey).findFirst()?.let { - // update it - it.state = PusherState.FAILED_TO_REGISTER - } - } - Result.failure() - } + else -> Result.failure() } } } @@ -79,29 +60,4 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) : override fun buildErrorParams(params: Params, message: String): Params { return params.copy(lastFailureMessage = params.lastFailureMessage ?: message) } - - private suspend fun setPusher(pusher: JsonPusher) { - executeRequest(globalErrorReceiver) { - pushersAPI.setPusher(pusher) - } - monarchy.awaitTransaction { realm -> - val echo = PusherEntity.where(realm, pusher.pushKey).findFirst() - if (echo != null) { - // update it - echo.appDisplayName = pusher.appDisplayName - echo.appId = pusher.appId - echo.kind = pusher.kind - echo.lang = pusher.lang - echo.profileTag = pusher.profileTag - echo.data?.format = pusher.data?.format - echo.data?.url = pusher.data?.url - echo.state = PusherState.REGISTERED - } else { - pusher.toEntity().also { - it.state = PusherState.REGISTERED - realm.insertOrUpdate(it) - } - } - } - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt index 9a50abfe35..8a510969e7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt @@ -30,7 +30,6 @@ import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotify import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.worker.WorkerParamsFactory -import java.security.InvalidParameterException import java.util.UUID import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -41,6 +40,7 @@ internal class DefaultPushersService @Inject constructor( @SessionId private val sessionId: String, private val getPusherTask: GetPushersTask, private val pushGatewayNotifyTask: PushGatewayNotifyTask, + private val addPusherTask: AddPusherTask, private val removePusherTask: RemovePusherTask, private val taskExecutor: TaskExecutor ) : PushersService { @@ -58,51 +58,79 @@ internal class DefaultPushersService @Inject constructor( .executeBy(taskExecutor) } - override fun addHttpPusher(pushkey: String, - appId: String, - profileTag: String, - lang: String, - appDisplayName: String, - deviceDisplayName: String, - url: String, - append: Boolean, - withEventIdOnly: Boolean - ) = addPusher( - JsonPusher( - pushKey = pushkey, - kind = Pusher.KIND_HTTP, - appId = appId, - profileTag = profileTag, - lang = lang, - appDisplayName = appDisplayName, - deviceDisplayName = deviceDisplayName, - data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), - append = append - ) - ) + override fun enqueueAddHttpPusher(pushkey: String, + appId: String, + profileTag: String, + lang: String, + appDisplayName: String, + deviceDisplayName: String, + url: String, + append: Boolean, + withEventIdOnly: Boolean + ): UUID { + return enqueueAddPusher( + JsonPusher( + pushKey = pushkey, + kind = "http", + appId = appId, + profileTag = profileTag, + lang = lang, + appDisplayName = appDisplayName, + deviceDisplayName = deviceDisplayName, + data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), + append = append + ) + ) + } - override fun addEmailPusher(email: String, - lang: String, - emailBranding: String, - appDisplayName: String, - deviceDisplayName: String, - append: Boolean - ) = addPusher( - JsonPusher( - pushKey = email, - kind = Pusher.KIND_EMAIL, - appId = Pusher.APP_ID_EMAIL, - profileTag = "", - lang = lang, - appDisplayName = appDisplayName, - deviceDisplayName = deviceDisplayName, - data = JsonPusherData(brand = emailBranding), - append = append - ) - ) + override suspend fun addHttpPusher(pushkey: String, + appId: String, + profileTag: String, + lang: String, + appDisplayName: String, + deviceDisplayName: String, + url: String, + append: Boolean, + withEventIdOnly: Boolean) { + addPusherTask.execute( + AddPusherTask.Params( + JsonPusher( + pushKey = pushkey, + kind = "http", + appId = appId, + profileTag = profileTag, + lang = lang, + appDisplayName = appDisplayName, + deviceDisplayName = deviceDisplayName, + data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), + append = append + ) + ) + ) + } - private fun addPusher(pusher: JsonPusher): UUID { - pusher.validateParameters() + override suspend fun addEmailPusher(email: String, + lang: String, + emailBranding: String, + appDisplayName: String, + deviceDisplayName: String, + append: Boolean) { + addPusherTask.execute( + AddPusherTask.Params(JsonPusher( + pushKey = email, + kind = Pusher.KIND_EMAIL, + appId = Pusher.APP_ID_EMAIL, + profileTag = "", + lang = lang, + appDisplayName = appDisplayName, + deviceDisplayName = deviceDisplayName, + data = JsonPusherData(brand = emailBranding), + append = append + )) + ) + } + + private fun enqueueAddPusher(pusher: JsonPusher): UUID { val params = AddPusherWorker.Params(sessionId, pusher) val request = workManagerProvider.matrixOneTimeWorkRequestBuilder() .setConstraints(WorkManagerProvider.workConstraints) @@ -113,13 +141,6 @@ internal class DefaultPushersService @Inject constructor( return request.id } - private fun JsonPusher.validateParameters() { - // Do some parameter checks. It's ok to throw Exception, to inform developer of the problem - if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars") - if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars") - data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") } - } - override suspend fun removePusher(pusher: Pusher) { removePusher(pusher.pushKey, pusher.appId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt index a594675e28..8dc0954694 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.pushers import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import org.matrix.android.sdk.internal.di.SerializeNulls +import java.security.InvalidParameterException /** * Example: @@ -112,4 +113,11 @@ internal data class JsonPusher( */ @Json(name = "append") val append: Boolean? = false -) +) { + init { + // Do some parameter checks. It's ok to throw Exception, to inform developer of the problem + if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars") + if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars") + data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt index 4030c63514..d53a4eed65 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt @@ -65,6 +65,9 @@ internal abstract class PushersModule { @Binds abstract fun bindSavePushRulesTask(task: DefaultSavePushRulesTask): SavePushRulesTask + @Binds + abstract fun bindAddPusherTask(task: DefaultAddPusherTask): AddPusherTask + @Binds abstract fun bindRemovePusherTask(task: DefaultRemovePusherTask): RemovePusherTask diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt index a27765bf4f..0ed7c91540 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt @@ -48,7 +48,7 @@ class PushersManager @Inject constructor( val currentSession = activeSessionHolder.getActiveSession() val profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(currentSession.myUserId.hashCode()) - return currentSession.addHttpPusher( + return currentSession.enqueueAddHttpPusher( pushKey, stringProvider.getString(R.string.pusher_app_id), profileTag, @@ -61,7 +61,7 @@ class PushersManager @Inject constructor( ) } - fun registerEmailForPush(email: String) { + suspend fun registerEmailForPush(email: String) { val currentSession = activeSessionHolder.getActiveSession() val appName = appNameProvider.getAppName() currentSession.addEmailPusher( From e24329e1393d1fc8977ffc7f2e4b3371324052c3 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 21 Sep 2021 15:09:57 +0100 Subject: [PATCH 124/144] reusing the transactional logic for the current session notifications toggle - uses the synchronous token registering which also means we get error handling --- .../sdk/api/session/pushers/PushersService.kt | 99 +++++++++++-------- .../session/pushers/DefaultPushersService.kt | 63 +++--------- .../troubleshoot/TestTokenRegistration.kt | 2 +- .../fcm/VectorFirebaseMessagingService.kt | 2 +- .../java/im/vector/app/push/fcm/FcmHelper.kt | 2 +- .../vector/app/core/pushers/PushersManager.kt | 34 ++++--- ...rSettingsNotificationPreferenceFragment.kt | 45 +++------ 7 files changed, 114 insertions(+), 133 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt index 03391ff8ba..757b55a3fa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt @@ -29,38 +29,9 @@ interface PushersService { * Add a new HTTP pusher. * Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set * - * @param pushkey This is a unique identifier for this pusher. The value you should use for - * this is the routing or destination address information for the notification, - * for example, the APNS token for APNS or the Registration ID for GCM. If your - * notification client has no such concept, use any unique identifier. Max length, 512 chars. - * @param appId the application id - * This is a reverse-DNS style identifier for the application. It is recommended - * that this end with the platform, such that different platform versions get - * different app identifiers. Max length, 64 chars. - * @param profileTag This string determines which set of device specific rules this pusher executes. - * @param lang The preferred language for receiving notifications (e.g. "en" or "en-US"). - * @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher. - * @param deviceDisplayName A human readable string that will allow the user to identify what device owns this pusher. - * @param url The URL to use to send notifications to. MUST be an HTTPS URL with a path of /_matrix/push/v1/notify. - * @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition - * to any others with different user IDs. Otherwise, the homeserver must remove any other pushers - * with the same App ID and pushkey for different users. - * @param withEventIdOnly true to limit the push content to only id and not message content - * Ref: https://matrix.org/docs/spec/push_gateway/r0.1.1#homeserver-behaviour - * - * @return A work request uuid. Can be used to listen to the status - * (LiveData status = workManager.getWorkInfoByIdLiveData()) * @throws [InvalidParameterException] if a parameter is not correct */ - suspend fun addHttpPusher(pushkey: String, - appId: String, - profileTag: String, - lang: String, - appDisplayName: String, - deviceDisplayName: String, - url: String, - append: Boolean, - withEventIdOnly: Boolean) + suspend fun addHttpPusher(httpPusher: HttpPusher) /** * Enqueues a new HTTP pusher via the WorkManager API. @@ -70,15 +41,7 @@ interface PushersService { * (LiveData status = workManager.getWorkInfoByIdLiveData()) * @throws [InvalidParameterException] if a parameter is not correct */ - fun enqueueAddHttpPusher(pushkey: String, - appId: String, - profileTag: String, - lang: String, - appDisplayName: String, - deviceDisplayName: String, - url: String, - append: Boolean, - withEventIdOnly: Boolean): UUID + fun enqueueAddHttpPusher(httpPusher: HttpPusher): UUID /** * Add a new Email pusher. @@ -144,4 +107,62 @@ interface PushersService { * Get the current pushers */ fun getPushers(): List + + data class HttpPusher( + + /** + * This is a unique identifier for this pusher. The value you should use for + * this is the routing or destination address information for the notification, + * for example, the APNS token for APNS or the Registration ID for GCM. If your + * notification client has no such concept, use any unique identifier. Max length, 512 chars. + * If the kind is "email", this is the email address to send notifications to. + */ + val pushkey: String, + + /** + * The application id + * This is a reverse-DNS style identifier for the application. It is recommended + * that this end with the platform, such that different platform versions get + * different app identifiers. Max length, 64 chars. + */ + val appId: String, + + /** + * This string determines which set of device specific rules this pusher executes. + */ + val profileTag: String, + + /** + * The preferred language for receiving notifications (e.g. "en" or "en-US"). + */ + val lang: String, + + /** + * A human readable string that will allow the user to identify what application owns this pusher. + */ + val appDisplayName: String, + + /** + * A human readable string that will allow the user to identify what device owns this pusher. + */ + val deviceDisplayName: String, + + /** + * The URL to use to send notifications to. MUST be an HTTPS URL with a path of /_matrix/push/v1/notify. + */ + val url: String, + + /** + * If true, the homeserver should add another pusher with the given pushkey and App ID in addition + * to any others with different user IDs. Otherwise, the homeserver must remove any other pushers + * with the same App ID and pushkey for different users. + */ + val append: Boolean, + + /** + * true to limit the push content to only id and not message content + * Ref: https://matrix.org/docs/spec/push_gateway/r0.1.1#homeserver-behaviour + */ + val withEventIdOnly: Boolean, + ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt index 8a510969e7..e87c27e601 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt @@ -58,57 +58,26 @@ internal class DefaultPushersService @Inject constructor( .executeBy(taskExecutor) } - override fun enqueueAddHttpPusher(pushkey: String, - appId: String, - profileTag: String, - lang: String, - appDisplayName: String, - deviceDisplayName: String, - url: String, - append: Boolean, - withEventIdOnly: Boolean - ): UUID { - return enqueueAddPusher( - JsonPusher( - pushKey = pushkey, - kind = "http", - appId = appId, - profileTag = profileTag, - lang = lang, - appDisplayName = appDisplayName, - deviceDisplayName = deviceDisplayName, - data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), - append = append - ) - ) + override fun enqueueAddHttpPusher(httpPusher: PushersService.HttpPusher): UUID { + return enqueueAddPusher(httpPusher.toJsonPusher()) } - override suspend fun addHttpPusher(pushkey: String, - appId: String, - profileTag: String, - lang: String, - appDisplayName: String, - deviceDisplayName: String, - url: String, - append: Boolean, - withEventIdOnly: Boolean) { - addPusherTask.execute( - AddPusherTask.Params( - JsonPusher( - pushKey = pushkey, - kind = "http", - appId = appId, - profileTag = profileTag, - lang = lang, - appDisplayName = appDisplayName, - deviceDisplayName = deviceDisplayName, - data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), - append = append - ) - ) - ) + override suspend fun addHttpPusher(httpPusher: PushersService.HttpPusher) { + addPusherTask.execute(AddPusherTask.Params(httpPusher.toJsonPusher())) } + private fun PushersService.HttpPusher.toJsonPusher() = JsonPusher( + pushKey = pushkey, + kind = "http", + appId = appId, + profileTag = profileTag, + lang = lang, + appDisplayName = appDisplayName, + deviceDisplayName = deviceDisplayName, + data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }), + append = append + ) + override suspend fun addEmailPusher(email: String, lang: String, emailBranding: String, diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt index 966d79c59b..94d4ec8a56 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt @@ -57,7 +57,7 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc stringProvider.getString(R.string.sas_error_unknown)) quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) { override fun doFix() { - val workId = pushersManager.registerPusherWithFcmKey(fcmToken) + val workId = pushersManager.enqueueRegisterPusherWithFcmKey(fcmToken) WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo -> if (workInfo != null) { if (workInfo.state == WorkInfo.State.SUCCEEDED) { diff --git a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt index ddedfb93e3..2ce51ba4c7 100755 --- a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt @@ -135,7 +135,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { Timber.tag(loggerTag.value).i("onNewToken: FCM Token has been updated") FcmHelper.storeFcmToken(this, refreshedToken) if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) { - pusherManager.registerPusherWithFcmKey(refreshedToken) + pusherManager.enqueueRegisterPusherWithFcmKey(refreshedToken) } } diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt index f3bdcafb1c..fe091ffda3 100755 --- a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt @@ -75,7 +75,7 @@ object FcmHelper { .addOnSuccessListener { token -> storeFcmToken(activity, token) if (registerPusher) { - pushersManager.registerPusherWithFcmKey(token) + pushersManager.enqueueRegisterPusherWithFcmKey(token) } } .addOnFailureListener { e -> diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt index 0ed7c91540..0f780d4504 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt @@ -21,6 +21,7 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.resources.AppNameProvider import im.vector.app.core.resources.LocaleProvider import im.vector.app.core.resources.StringProvider +import org.matrix.android.sdk.api.session.pushers.PushersService import java.util.UUID import javax.inject.Inject import kotlin.math.abs @@ -44,23 +45,28 @@ class PushersManager @Inject constructor( ) } - fun registerPusherWithFcmKey(pushKey: String): UUID { + fun enqueueRegisterPusherWithFcmKey(pushKey: String): UUID { val currentSession = activeSessionHolder.getActiveSession() - val profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(currentSession.myUserId.hashCode()) - - return currentSession.enqueueAddHttpPusher( - pushKey, - stringProvider.getString(R.string.pusher_app_id), - profileTag, - localeProvider.current().language, - appNameProvider.getAppName(), - currentSession.sessionParams.deviceId ?: "MOBILE", - stringProvider.getString(R.string.pusher_http_url), - append = false, - withEventIdOnly = true - ) + return currentSession.enqueueAddHttpPusher(httpPusher(pushKey)) } + suspend fun registerPusherWithFcmKey(pushKey: String) { + val currentSession = activeSessionHolder.getActiveSession() + currentSession.addHttpPusher(httpPusher(pushKey)) + } + + private fun httpPusher(pushKey: String) = PushersService.HttpPusher( + pushKey, + stringProvider.getString(R.string.pusher_app_id), + profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(activeSessionHolder.getActiveSession().myUserId.hashCode()), + localeProvider.current().language, + appNameProvider.getAppName(), + activeSessionHolder.getActiveSession().sessionParams.deviceId ?: "MOBILE", + stringProvider.getString(R.string.pusher_http_url), + append = false, + withEventIdOnly = true + ) + suspend fun registerEmailForPush(email: String) { val currentSession = activeSessionHolder.getActiveSession() val appName = appNameProvider.getAppName() diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 098d1b2caa..18ea2d6773 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -85,6 +85,21 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( (pref as SwitchPreference).isChecked = areNotifEnabledAtAccountLevel } + findPreference(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)?.let { + it.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked -> + if (isChecked) { + FcmHelper.getFcmToken(requireContext())?.let { + pushManager.registerPusherWithFcmKey(it) + } + } else { + FcmHelper.getFcmToken(requireContext())?.let { + pushManager.unregisterPusher(it) + session.refreshPushers() + } + } + } + } + findPreference(VectorPreferences.SETTINGS_FDROID_BACKGROUND_SYNC_MODE)?.let { it.onPreferenceClickListener = Preference.OnPreferenceClickListener { val initialMode = vectorPreferences.getFdroidSyncBackgroundMode() @@ -324,10 +339,6 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( override fun onPreferenceTreeClick(preference: Preference?): Boolean { return when (preference?.key) { - VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY -> { - updateEnabledForDevice(preference) - true - } VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY -> { updateEnabledForAccount(preference) true @@ -338,32 +349,6 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( } } - private fun updateEnabledForDevice(preference: Preference?) { - val switchPref = preference as SwitchPreference - if (switchPref.isChecked) { - FcmHelper.getFcmToken(requireContext())?.let { - pushManager.registerPusherWithFcmKey(it) - } - } else { - FcmHelper.getFcmToken(requireContext())?.let { - lifecycleScope.launch { - runCatching { pushManager.unregisterPusher(it) } - .fold( - { session.refreshPushers() }, - { - if (!isAdded) { - return@fold - } - // revert the check box - switchPref.isChecked = !switchPref.isChecked - Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show() - } - ) - } - } - } - } - private fun updateEnabledForAccount(preference: Preference?) { val pushRuleService = session val switchPref = preference as SwitchPreference From 46c338934eb06357703956255d90f37c96ae0b0e Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 21 Sep 2021 16:26:14 +0100 Subject: [PATCH 125/144] running lint --- .../matrix/android/sdk/api/session/pushers/PushersService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt index 757b55a3fa..f6e241ad5e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt @@ -163,6 +163,6 @@ interface PushersService { * true to limit the push content to only id and not message content * Ref: https://matrix.org/docs/spec/push_gateway/r0.1.1#homeserver-behaviour */ - val withEventIdOnly: Boolean, + val withEventIdOnly: Boolean ) } From 6672ab39663d8a3e5d917d095ac4c4073a7f031e Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 22 Sep 2021 10:14:53 +0100 Subject: [PATCH 126/144] removing comment which doesn't add additional context/information --- .../android/sdk/internal/session/pushers/AddPusherTask.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt index b4a5ccd383..431a009844 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt @@ -44,7 +44,6 @@ internal class DefaultAddPusherTask @Inject constructor( } catch (error: Throwable) { monarchy.awaitTransaction { realm -> PusherEntity.where(realm, pusher.pushKey).findFirst()?.let { - // update it it.state = PusherState.FAILED_TO_REGISTER } } @@ -59,7 +58,6 @@ internal class DefaultAddPusherTask @Inject constructor( monarchy.awaitTransaction { realm -> val echo = PusherEntity.where(realm, pusher.pushKey).findFirst() if (echo != null) { - // update it echo.appDisplayName = pusher.appDisplayName echo.appId = pusher.appId echo.kind = pusher.kind From 0a2d7d709b19301a3571b0724ad1a8c964287798 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 22 Sep 2021 12:37:30 +0100 Subject: [PATCH 127/144] creating an injectable request executor to enable unit tests network request (without hitting the network) --- .../internal/session/pushers/AddPusherTask.kt | 18 ++++++++++++++++-- .../internal/session/pushers/PushersModule.kt | 7 +++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt index 431a009844..2929e09a1f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt @@ -23,7 +23,6 @@ import org.matrix.android.sdk.internal.database.model.PusherEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.GlobalErrorReceiver -import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.awaitTransaction import javax.inject.Inject @@ -35,6 +34,7 @@ internal interface AddPusherTask : Task { internal class DefaultAddPusherTask @Inject constructor( private val pushersAPI: PushersAPI, @SessionDatabase private val monarchy: Monarchy, + private val requestExecutor: RequestExecutor, private val globalErrorReceiver: GlobalErrorReceiver ) : AddPusherTask { override suspend fun execute(params: AddPusherTask.Params) { @@ -52,7 +52,7 @@ internal class DefaultAddPusherTask @Inject constructor( } private suspend fun setPusher(pusher: JsonPusher) { - executeRequest(globalErrorReceiver) { + requestExecutor.executeRequest(globalErrorReceiver) { pushersAPI.setPusher(pusher) } monarchy.awaitTransaction { realm -> @@ -75,3 +75,17 @@ internal class DefaultAddPusherTask @Inject constructor( } } } + +internal interface RequestExecutor { + suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, + canRetry: Boolean = false, + maxDelayBeforeRetry: Long = 32_000L, + maxRetriesCount: Int = 4, + requestBlock: suspend () -> DATA): DATA +} + +internal object DefaultRequestExecutor : RequestExecutor { + override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, canRetry: Boolean, maxDelayBeforeRetry: Long, maxRetriesCount: Int, requestBlock: suspend () -> DATA): DATA { + return executeRequest(globalErrorReceiver, canRetry, maxDelayBeforeRetry, maxRetriesCount, requestBlock) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt index d53a4eed65..8cf4d9fcf0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt @@ -30,6 +30,7 @@ import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotify import org.matrix.android.sdk.internal.session.room.notification.DefaultSetRoomNotificationStateTask import org.matrix.android.sdk.internal.session.room.notification.SetRoomNotificationStateTask import retrofit2.Retrofit +import javax.inject.Singleton @Module internal abstract class PushersModule { @@ -48,6 +49,12 @@ internal abstract class PushersModule { fun providesPushRulesApi(retrofit: Retrofit): PushRulesApi { return retrofit.create(PushRulesApi::class.java) } + + @Provides + @JvmStatic + fun providesRequestExecutor(): RequestExecutor { + return DefaultRequestExecutor + } } @Binds From ced85964dac7d5dff802ba25cdbd9919d028a4d6 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 22 Sep 2021 12:39:24 +0100 Subject: [PATCH 128/144] including rx java dependency for the sdk tests because real (monarchy) tranisitive depends on rx but doesn't propagate it as an API dependency - without an explicit declaration we can't mock the realm instance --- matrix-sdk-android/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 86a257c7d6..3bcb08c02c 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -130,6 +130,8 @@ dependencies { // Database implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' + testImplementation libs.rx.rxKotlin + kapt 'dk.ilios:realmfieldnameshelper:2.0.0' // Work From 48d9dfb82dcb5fed7b5c5816c549ff05a4583c37 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 22 Sep 2021 13:05:42 +0100 Subject: [PATCH 129/144] adding test for the add pusher task happy flow - introduces the concepts of Fakes for handling the dependencies, unforuntately realm/monarchy aren't very testable in their current state so we'll need to use mocks --- .../pushers/DefaultAddPusherTaskTest.kt | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt new file mode 100644 index 0000000000..47730eba49 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.pushers + +import com.zhuinden.monarchy.Monarchy +import io.mockk.MockKVerificationScope +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.verify +import io.realm.Realm +import io.realm.RealmModel +import io.realm.RealmQuery +import io.realm.kotlin.where +import kotlinx.coroutines.runBlocking +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.session.pushers.PusherState +import org.matrix.android.sdk.internal.database.model.PusherEntity +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.util.awaitTransaction + +private val A_JSON_PUSHER = JsonPusher( + pushKey = "push-key", + kind = "http", + appId = "m.email", + appDisplayName = "Element", + deviceDisplayName = null, + profileTag = "", + lang = "en-GB", + data = JsonPusherData(brand = "Element") +) + +class DefaultAddPusherTaskTest { + + private val pushersAPI = FakePushersAPI() + private val monarchy = FakeMonarchy() + + private val addPusherTask = DefaultAddPusherTask( + pushersAPI = pushersAPI, + monarchy = monarchy.instance, + requestExecutor = FakeRequestExecutor(), + globalErrorReceiver = FakeGlobalErrorReceiver() + ) + + @Test + fun `given no persisted pusher when running task then fetches from api and inserts result with Registered state`() = runBlocking { + monarchy.givenWhereReturns(result = null) + + addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) + + pushersAPI.verifySetPusher(A_JSON_PUSHER) + monarchy.verifyInsertOrUpdate { + withArg { actual -> + actual.state shouldBeEqualTo PusherState.REGISTERED + } + } + } +} + +internal class FakePushersAPI : PushersAPI { + + private var setRequestPayload: JsonPusher? = null + private var error: Throwable? = null + + override suspend fun getPushers(): GetPushersResponse { + TODO("Not yet implemented") + } + + override suspend fun setPusher(jsonPusher: JsonPusher) { + error?.let { throw it } + setRequestPayload = jsonPusher + } + + fun verifySetPusher(payload: JsonPusher) { + this.setRequestPayload shouldBeEqualTo payload + } + + fun givenSetPusherErrors(error: Throwable) { + this.error = error + } +} + +internal class FakeMonarchy { + + val instance = mockk() + private val realm = mockk(relaxed = true) + + init { + mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt") + coEvery { + instance.awaitTransaction(any Any>()) + } coAnswers { + secondArg Any>().invoke(realm) + } + } + + inline fun givenWhereReturns(result: T?) { + val queryResult = mockk>(relaxed = true) + every { queryResult.findFirst() } returns result + every { realm.where() } returns queryResult + } + + inline fun verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) { + verify { realm.insertOrUpdate(verification()) } + } +} + +internal class FakeRequestExecutor : RequestExecutor { + + override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, + canRetry: Boolean, + maxDelayBeforeRetry: Long, + maxRetriesCount: Int, + requestBlock: suspend () -> DATA): DATA { + return requestBlock() + } +} + +internal class FakeGlobalErrorReceiver : GlobalErrorReceiver { + override fun handleGlobalError(globalError: GlobalError) { + // do nothing + } +} From 21479b2b28745e23f8983e1fb493e68629a2a106 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 22 Sep 2021 15:51:43 +0100 Subject: [PATCH 130/144] inverting if to favour positive ordering --- .../sdk/internal/session/pushers/AddPusherTask.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt index 2929e09a1f..a24202957c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt @@ -57,7 +57,12 @@ internal class DefaultAddPusherTask @Inject constructor( } monarchy.awaitTransaction { realm -> val echo = PusherEntity.where(realm, pusher.pushKey).findFirst() - if (echo != null) { + if (echo == null) { + pusher.toEntity().also { + it.state = PusherState.REGISTERED + realm.insertOrUpdate(it) + } + } else { echo.appDisplayName = pusher.appDisplayName echo.appId = pusher.appId echo.kind = pusher.kind @@ -66,11 +71,6 @@ internal class DefaultAddPusherTask @Inject constructor( echo.data?.format = pusher.data?.format echo.data?.url = pusher.data?.url echo.state = PusherState.REGISTERED - } else { - pusher.toEntity().also { - it.state = PusherState.REGISTERED - realm.insertOrUpdate(it) - } } } } From b7c911feeefd7a8653ce069106ecdb8a4d5e470f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 22 Sep 2021 16:17:39 +0100 Subject: [PATCH 131/144] adding test cases for when adding a pusher fails and when it already exists --- .../pushers/DefaultAddPusherTaskTest.kt | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt index 47730eba49..c3be7c613e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt @@ -28,6 +28,7 @@ import io.realm.RealmModel import io.realm.RealmQuery import io.realm.kotlin.where import kotlinx.coroutines.runBlocking +import org.amshove.kluent.internal.assertFailsWith import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.failure.GlobalError @@ -35,6 +36,7 @@ import org.matrix.android.sdk.api.session.pushers.PusherState import org.matrix.android.sdk.internal.database.model.PusherEntity import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.util.awaitTransaction +import java.net.SocketException private val A_JSON_PUSHER = JsonPusher( pushKey = "push-key", @@ -60,10 +62,10 @@ class DefaultAddPusherTaskTest { ) @Test - fun `given no persisted pusher when running task then fetches from api and inserts result with Registered state`() = runBlocking { + fun `given no persisted pusher when adding Pusher then updates api and inserts result with Registered state`() { monarchy.givenWhereReturns(result = null) - addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) + runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) } pushersAPI.verifySetPusher(A_JSON_PUSHER) monarchy.verifyInsertOrUpdate { @@ -72,6 +74,42 @@ class DefaultAddPusherTaskTest { } } } + + @Test + fun `given a persisted pusher when adding Pusher then updates api and mutates persisted result with Registered state`() { + val realmResult = PusherEntity(appDisplayName = null) + monarchy.givenWhereReturns(result = realmResult) + + runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) } + + pushersAPI.verifySetPusher(A_JSON_PUSHER) + + realmResult.appDisplayName shouldBeEqualTo A_JSON_PUSHER.appDisplayName + realmResult.state shouldBeEqualTo PusherState.REGISTERED + } + + @Test + fun `given a persisted push entity and SetPush API fails when adding Pusher then mutates persisted result with Failed registration state and rethrows error`() { + val realmResult = PusherEntity() + monarchy.givenWhereReturns(result = realmResult) + pushersAPI.givenSetPusherErrors(SocketException()) + + assertFailsWith { + runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) } + } + + realmResult.state shouldBeEqualTo PusherState.FAILED_TO_REGISTER + } + + @Test + fun `given no persisted push entity and SetPush API fails when adding Pusher then rethrows error`() { + monarchy.givenWhereReturns(result = null) + pushersAPI.givenSetPusherErrors(SocketException()) + + assertFailsWith { + runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) } + } + } } internal class FakePushersAPI : PushersAPI { From 8e84aea434583184cd629c0bd7c69db256c0400b Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 23 Sep 2021 09:32:02 +0100 Subject: [PATCH 132/144] removing unused import --- .../android/sdk/internal/session/pushers/AddPusherTask.kt | 6 +++++- .../android/sdk/internal/session/pushers/PushersModule.kt | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt index a24202957c..61f893572d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt @@ -85,7 +85,11 @@ internal interface RequestExecutor { } internal object DefaultRequestExecutor : RequestExecutor { - override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, canRetry: Boolean, maxDelayBeforeRetry: Long, maxRetriesCount: Int, requestBlock: suspend () -> DATA): DATA { + override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, + canRetry: Boolean, + maxDelayBeforeRetry: Long, + maxRetriesCount: Int, + requestBlock: suspend () -> DATA): DATA { return executeRequest(globalErrorReceiver, canRetry, maxDelayBeforeRetry, maxRetriesCount, requestBlock) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt index 8cf4d9fcf0..6af8c4ea47 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt @@ -30,7 +30,6 @@ import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotify import org.matrix.android.sdk.internal.session.room.notification.DefaultSetRoomNotificationStateTask import org.matrix.android.sdk.internal.session.room.notification.SetRoomNotificationStateTask import retrofit2.Retrofit -import javax.inject.Singleton @Module internal abstract class PushersModule { From aff787bb29cc887a03db81412e51a613a5f53e59 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 28 Sep 2021 08:52:55 +0100 Subject: [PATCH 133/144] extracting the test fakes to their own package --- .../pushers/DefaultAddPusherTaskTest.kt | 83 +------------------ .../sdk/test/fakes/FakeGlobalErrorReceiver.kt | 26 ++++++ .../android/sdk/test/fakes/FakeMonarchy.kt | 55 ++++++++++++ .../android/sdk/test/fakes/FakePushersAPI.kt | 45 ++++++++++ .../sdk/test/fakes/FakeRequestExecutor.kt | 31 +++++++ 5 files changed, 161 insertions(+), 79 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeGlobalErrorReceiver.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakePushersAPI.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt index c3be7c613e..c8be0f5487 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt @@ -16,26 +16,16 @@ package org.matrix.android.sdk.internal.session.pushers -import com.zhuinden.monarchy.Monarchy -import io.mockk.MockKVerificationScope -import io.mockk.coEvery -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import io.mockk.verify -import io.realm.Realm -import io.realm.RealmModel -import io.realm.RealmQuery -import io.realm.kotlin.where import kotlinx.coroutines.runBlocking import org.amshove.kluent.internal.assertFailsWith import org.amshove.kluent.shouldBeEqualTo import org.junit.Test -import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.session.pushers.PusherState import org.matrix.android.sdk.internal.database.model.PusherEntity -import org.matrix.android.sdk.internal.network.GlobalErrorReceiver -import org.matrix.android.sdk.internal.util.awaitTransaction +import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver +import org.matrix.android.sdk.test.fakes.FakeMonarchy +import org.matrix.android.sdk.test.fakes.FakePushersAPI +import org.matrix.android.sdk.test.fakes.FakeRequestExecutor import java.net.SocketException private val A_JSON_PUSHER = JsonPusher( @@ -111,68 +101,3 @@ class DefaultAddPusherTaskTest { } } } - -internal class FakePushersAPI : PushersAPI { - - private var setRequestPayload: JsonPusher? = null - private var error: Throwable? = null - - override suspend fun getPushers(): GetPushersResponse { - TODO("Not yet implemented") - } - - override suspend fun setPusher(jsonPusher: JsonPusher) { - error?.let { throw it } - setRequestPayload = jsonPusher - } - - fun verifySetPusher(payload: JsonPusher) { - this.setRequestPayload shouldBeEqualTo payload - } - - fun givenSetPusherErrors(error: Throwable) { - this.error = error - } -} - -internal class FakeMonarchy { - - val instance = mockk() - private val realm = mockk(relaxed = true) - - init { - mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt") - coEvery { - instance.awaitTransaction(any Any>()) - } coAnswers { - secondArg Any>().invoke(realm) - } - } - - inline fun givenWhereReturns(result: T?) { - val queryResult = mockk>(relaxed = true) - every { queryResult.findFirst() } returns result - every { realm.where() } returns queryResult - } - - inline fun verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) { - verify { realm.insertOrUpdate(verification()) } - } -} - -internal class FakeRequestExecutor : RequestExecutor { - - override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, - canRetry: Boolean, - maxDelayBeforeRetry: Long, - maxRetriesCount: Int, - requestBlock: suspend () -> DATA): DATA { - return requestBlock() - } -} - -internal class FakeGlobalErrorReceiver : GlobalErrorReceiver { - override fun handleGlobalError(globalError: GlobalError) { - // do nothing - } -} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeGlobalErrorReceiver.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeGlobalErrorReceiver.kt new file mode 100644 index 0000000000..ebddb3fafa --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeGlobalErrorReceiver.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.test.fakes + +import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver + +internal class FakeGlobalErrorReceiver : GlobalErrorReceiver { + override fun handleGlobalError(globalError: GlobalError) { + // do nothing + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt new file mode 100644 index 0000000000..0a22ef8996 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.test.fakes + +import com.zhuinden.monarchy.Monarchy +import io.mockk.MockKVerificationScope +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.verify +import io.realm.Realm +import io.realm.RealmModel +import io.realm.RealmQuery +import io.realm.kotlin.where +import org.matrix.android.sdk.internal.util.awaitTransaction + +internal class FakeMonarchy { + + val instance = mockk() + private val realm = mockk(relaxed = true) + + init { + mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt") + coEvery { + instance.awaitTransaction(any Any>()) + } coAnswers { + secondArg Any>().invoke(realm) + } + } + + inline fun givenWhereReturns(result: T?) { + val queryResult = mockk>(relaxed = true) + every { queryResult.findFirst() } returns result + every { realm.where() } returns queryResult + } + + inline fun verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) { + verify { realm.insertOrUpdate(verification()) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakePushersAPI.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakePushersAPI.kt new file mode 100644 index 0000000000..29f93c2faf --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakePushersAPI.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.test.fakes + +import org.amshove.kluent.shouldBeEqualTo +import org.matrix.android.sdk.internal.session.pushers.GetPushersResponse +import org.matrix.android.sdk.internal.session.pushers.JsonPusher +import org.matrix.android.sdk.internal.session.pushers.PushersAPI + +internal class FakePushersAPI : PushersAPI { + + private var setRequestPayload: JsonPusher? = null + private var error: Throwable? = null + + override suspend fun getPushers(): GetPushersResponse { + TODO("Not yet implemented") + } + + override suspend fun setPusher(jsonPusher: JsonPusher) { + error?.let { throw it } + setRequestPayload = jsonPusher + } + + fun verifySetPusher(payload: JsonPusher) { + this.setRequestPayload shouldBeEqualTo payload + } + + fun givenSetPusherErrors(error: Throwable) { + this.error = error + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt new file mode 100644 index 0000000000..95a0d0997d --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.test.fakes + +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.session.pushers.RequestExecutor + +internal class FakeRequestExecutor : RequestExecutor { + + override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, + canRetry: Boolean, + maxDelayBeforeRetry: Long, + maxRetriesCount: Int, + requestBlock: suspend () -> DATA): DATA { + return requestBlock() + } +} From 69bb554e208a33c1923629048b181b9d699cdefd Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 29 Sep 2021 10:10:01 +0100 Subject: [PATCH 134/144] lifting the request executor to its own file in the network package - also creates a dedicated RequestModule instead of providing the executor via the pushers module --- .../sdk/internal/network/RequestExecutor.kt | 36 +++++++++++++++++++ .../sdk/internal/network/RequestModule.kt | 29 +++++++++++++++ .../sdk/internal/session/SessionComponent.kt | 4 ++- .../internal/session/pushers/AddPusherTask.kt | 19 +--------- .../internal/session/pushers/PushersModule.kt | 6 ---- .../sdk/test/fakes/FakeRequestExecutor.kt | 2 +- 6 files changed, 70 insertions(+), 26 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestExecutor.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestModule.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestExecutor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestExecutor.kt new file mode 100644 index 0000000000..5cd2d88000 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestExecutor.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.network +import org.matrix.android.sdk.internal.network.executeRequest as internalExecuteRequest + +internal interface RequestExecutor { + suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, + canRetry: Boolean = false, + maxDelayBeforeRetry: Long = 32_000L, + maxRetriesCount: Int = 4, + requestBlock: suspend () -> DATA): DATA +} + +internal object DefaultRequestExecutor : RequestExecutor { + override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, + canRetry: Boolean, + maxDelayBeforeRetry: Long, + maxRetriesCount: Int, + requestBlock: suspend () -> DATA): DATA { + return internalExecuteRequest(globalErrorReceiver, canRetry, maxDelayBeforeRetry, maxRetriesCount, requestBlock) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestModule.kt new file mode 100644 index 0000000000..7b2bb9fcba --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RequestModule.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.network + +import dagger.Module +import dagger.Provides + +@Module +internal object RequestModule { + + @Provides + fun providesRequestExecutor(): RequestExecutor { + return DefaultRequestExecutor + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index 71031a4614..71ec6be840 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -29,6 +29,7 @@ import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessa import org.matrix.android.sdk.internal.di.MatrixComponent import org.matrix.android.sdk.internal.federation.FederationModule import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker +import org.matrix.android.sdk.internal.network.RequestModule import org.matrix.android.sdk.internal.session.account.AccountModule import org.matrix.android.sdk.internal.session.cache.CacheModule import org.matrix.android.sdk.internal.session.call.CallModule @@ -94,7 +95,8 @@ import org.matrix.android.sdk.internal.util.system.SystemModule CallModule::class, SearchModule::class, ThirdPartyModule::class, - SpaceModule::class + SpaceModule::class, + RequestModule::class ] ) @SessionScope diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt index 61f893572d..7d81e19265 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt @@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.database.model.PusherEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.RequestExecutor import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.awaitTransaction import javax.inject.Inject @@ -75,21 +76,3 @@ internal class DefaultAddPusherTask @Inject constructor( } } } - -internal interface RequestExecutor { - suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, - canRetry: Boolean = false, - maxDelayBeforeRetry: Long = 32_000L, - maxRetriesCount: Int = 4, - requestBlock: suspend () -> DATA): DATA -} - -internal object DefaultRequestExecutor : RequestExecutor { - override suspend fun executeRequest(globalErrorReceiver: GlobalErrorReceiver?, - canRetry: Boolean, - maxDelayBeforeRetry: Long, - maxRetriesCount: Int, - requestBlock: suspend () -> DATA): DATA { - return executeRequest(globalErrorReceiver, canRetry, maxDelayBeforeRetry, maxRetriesCount, requestBlock) - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt index 6af8c4ea47..d53a4eed65 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt @@ -48,12 +48,6 @@ internal abstract class PushersModule { fun providesPushRulesApi(retrofit: Retrofit): PushRulesApi { return retrofit.create(PushRulesApi::class.java) } - - @Provides - @JvmStatic - fun providesRequestExecutor(): RequestExecutor { - return DefaultRequestExecutor - } } @Binds diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt index 95a0d0997d..2f332a89a8 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRequestExecutor.kt @@ -17,7 +17,7 @@ package org.matrix.android.sdk.test.fakes import org.matrix.android.sdk.internal.network.GlobalErrorReceiver -import org.matrix.android.sdk.internal.session.pushers.RequestExecutor +import org.matrix.android.sdk.internal.network.RequestExecutor internal class FakeRequestExecutor : RequestExecutor { From 13f8494072bdff1e20267368ea5ba363a1641ffc Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 8 Oct 2021 08:55:55 +0100 Subject: [PATCH 135/144] grouping with other test deps and commenting the reason for rxKotlin dependency --- matrix-sdk-android/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 3bcb08c02c..b703b13c78 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -130,7 +130,6 @@ dependencies { // Database implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' - testImplementation libs.rx.rxKotlin kapt 'dk.ilios:realmfieldnameshelper:2.0.0' @@ -167,6 +166,8 @@ dependencies { implementation libs.jetbrains.coroutinesAndroid // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' + // Transitively required for mocking realm as monarchy doesn't expose Rx + testImplementation libs.rx.rxKotlin kaptAndroidTest libs.dagger.daggerCompiler androidTestImplementation libs.androidx.testCore From bdec6a3580fe85f1a7009cf6005567660d16bef4 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 8 Oct 2021 08:59:03 +0100 Subject: [PATCH 136/144] removing mention of email in the http pusher model, we have dedicated emails functions on the service instead --- .../org/matrix/android/sdk/api/session/pushers/PushersService.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt index f6e241ad5e..f884d3e890 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt @@ -115,7 +115,6 @@ interface PushersService { * this is the routing or destination address information for the notification, * for example, the APNS token for APNS or the Registration ID for GCM. If your * notification client has no such concept, use any unique identifier. Max length, 512 chars. - * If the kind is "email", this is the email address to send notifications to. */ val pushkey: String, From 1c1424eafc1c6502e7fcc1890d09ee523ab9ee04 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 8 Oct 2021 09:05:16 +0100 Subject: [PATCH 137/144] using verb prefix for http pusher creation function --- .../main/java/im/vector/app/core/pushers/PushersManager.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt index 0f780d4504..27d6d05708 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt @@ -47,15 +47,15 @@ class PushersManager @Inject constructor( fun enqueueRegisterPusherWithFcmKey(pushKey: String): UUID { val currentSession = activeSessionHolder.getActiveSession() - return currentSession.enqueueAddHttpPusher(httpPusher(pushKey)) + return currentSession.enqueueAddHttpPusher(createHttpPusher(pushKey)) } suspend fun registerPusherWithFcmKey(pushKey: String) { val currentSession = activeSessionHolder.getActiveSession() - currentSession.addHttpPusher(httpPusher(pushKey)) + currentSession.addHttpPusher(createHttpPusher(pushKey)) } - private fun httpPusher(pushKey: String) = PushersService.HttpPusher( + private fun createHttpPusher(pushKey: String) = PushersService.HttpPusher( pushKey, stringProvider.getString(R.string.pusher_app_id), profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(activeSessionHolder.getActiveSession().myUserId.hashCode()), From bd51eae741b62eda88010b991576aff5cf45c432 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 12 Oct 2021 11:44:30 +0100 Subject: [PATCH 138/144] refreshing the threePids when entering the preference screen, afterwards we're monitoring for changes --- .../VectorSettingsNotificationPreferenceFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 18ea2d6773..1c73fbb470 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -406,7 +406,7 @@ private fun Session.getEmailsWithPushInformation(): List>> { - return getThreePidsLive(refreshData = false) + return getThreePidsLive(refreshData = true) .distinctUntilChanged() .map { threePids -> val emailPushers = getPushers().filter { it.kind == Pusher.KIND_EMAIL } From a24a9b43fa10afcafe1b67da9c16a6306058ce12 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 12 Oct 2021 13:47:32 +0200 Subject: [PATCH 139/144] Mavericks 2: make the UT happy. Let SDK exposes MatrixCoroutineDispatchers. --- dependencies.gradle | 3 +- .../org/matrix/android/sdk/flow/FlowExt.kt | 6 +-- .../org/matrix/android/sdk/flow/FlowRoom.kt | 14 +++--- .../matrix/android/sdk/flow/FlowSession.kt | 30 ++++++------- .../sdk/SingleThreadCoroutineDispatcher.kt | 2 +- .../MatrixCoroutineDispatchers.kt | 6 +-- .../matrix/android/sdk/api/session/Session.kt | 3 ++ .../android/sdk/api/session/room/Room.kt | 3 ++ .../internal/crypto/DefaultCryptoService.kt | 2 +- .../sdk/internal/crypto/DeviceListManager.kt | 2 +- .../sdk/internal/crypto/EventDecryptor.kt | 2 +- .../crypto/InboundGroupSessionStore.kt | 2 +- .../crypto/IncomingGossipingRequestManager.kt | 2 +- .../crypto/OutgoingGossipingRequestManager.kt | 2 +- .../algorithms/megolm/MXMegolmDecryption.kt | 2 +- .../megolm/MXMegolmDecryptionFactory.kt | 2 +- .../algorithms/megolm/MXMegolmEncryption.kt | 2 +- .../megolm/MXMegolmEncryptionFactory.kt | 2 +- .../algorithms/olm/MXOlmEncryptionFactory.kt | 2 +- .../crypto/crosssigning/ComputeTrustTask.kt | 2 +- .../DefaultCrossSigningService.kt | 2 +- .../keysbackup/DefaultKeysBackupService.kt | 2 +- .../DefaultSharedSecretStorageService.kt | 2 +- .../DefaultVerificationService.kt | 2 +- .../sdk/internal/di/MatrixComponent.kt | 2 +- .../android/sdk/internal/di/MatrixModule.kt | 2 +- .../internal/session/DefaultFileService.kt | 2 +- .../sdk/internal/session/DefaultSession.kt | 2 + .../sdk/internal/session/SessionComponent.kt | 2 +- .../identity/DefaultIdentityService.kt | 2 +- .../session/profile/DefaultProfileService.kt | 2 +- .../sdk/internal/session/room/DefaultRoom.kt | 4 +- .../sdk/internal/session/room/RoomFactory.kt | 7 ++- .../session/room/draft/DefaultDraftService.kt | 2 +- .../internal/session/sync/job/SyncService.kt | 2 +- .../android/sdk/internal/task/TaskExecutor.kt | 2 +- vector/build.gradle | 2 + .../androidx/lifecycle/LifecycleRegistry.kt | 44 ------------------- .../quads/SharedSecureStorageViewModelTest.kt | 3 ++ .../app/test/TestCoroutineDispatchers.kt | 28 ++++++++++++ .../im/vector/app/test/fakes/FakeSession.kt | 3 ++ 41 files changed, 108 insertions(+), 102 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal/util => api}/MatrixCoroutineDispatchers.kt (85%) delete mode 100644 vector/src/test/java/androidx/lifecycle/LifecycleRegistry.kt create mode 100644 vector/src/test/java/im/vector/app/test/TestCoroutineDispatchers.kt diff --git a/dependencies.gradle b/dependencies.gradle index 4d1fdb6a60..d971d6fd47 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -100,7 +100,8 @@ ext.libs = [ 'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy", 'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy", 'mavericks' : "com.airbnb.android:mavericks:$mavericks", - 'mavericksRx' : "com.airbnb.android:mavericks-rxjava2:$mavericks" + 'mavericksRx' : "com.airbnb.android:mavericks-rxjava2:$mavericks", + 'mavericksTesting' : "com.airbnb.android:mavericks-testing:$mavericks" ], mockk : [ 'mockk' : "io.mockk:mockk:$mockk", diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt index f974d89d45..72493325c3 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt @@ -16,14 +16,14 @@ package org.matrix.android.sdk.flow -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withContext -internal fun Flow.startWith(supplier: suspend () -> T): Flow { +internal fun Flow.startWith(dispatcher: CoroutineDispatcher, supplier: suspend () -> T): Flow { return onStart { - val value = withContext(Dispatchers.IO) { + val value = withContext(dispatcher) { supplier() } emit(value) diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt index a78597ee46..42c1476b79 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt @@ -36,42 +36,42 @@ class FlowRoom(private val room: Room) { fun liveRoomSummary(): Flow> { return room.getRoomSummaryLive().asFlow() - .startWith { + .startWith(room.coroutineDispatchers.io) { room.roomSummary().toOptional() } } fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow> { return room.getRoomMembersLive(queryParams).asFlow() - .startWith { + .startWith(room.coroutineDispatchers.io) { room.getRoomMembers(queryParams) } } fun liveAnnotationSummary(eventId: String): Flow> { return room.getEventAnnotationsSummaryLive(eventId).asFlow() - .startWith { + .startWith(room.coroutineDispatchers.io) { room.getEventAnnotationsSummary(eventId).toOptional() } } fun liveTimelineEvent(eventId: String): Flow> { return room.getTimeLineEventLive(eventId).asFlow() - .startWith { + .startWith(room.coroutineDispatchers.io) { room.getTimeLineEvent(eventId).toOptional() } } fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow> { return room.getStateEventLive(eventType, stateKey).asFlow() - .startWith { + .startWith(room.coroutineDispatchers.io) { room.getStateEvent(eventType, stateKey).toOptional() } } fun liveStateEvents(eventTypes: Set): Flow> { return room.getStateEventsLive(eventTypes).asFlow() - .startWith { + .startWith(room.coroutineDispatchers.io) { room.getStateEvents(eventTypes) } } @@ -90,7 +90,7 @@ class FlowRoom(private val room: Room) { fun liveDraft(): Flow> { return room.getDraftLive().asFlow() - .startWith { + .startWith(room.coroutineDispatchers.io) { room.getDraft().toOptional() } } diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt index 75f482adfd..13fd097bcd 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt @@ -46,35 +46,35 @@ class FlowSession(private val session: Session) { fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Flow> { return session.getRoomSummariesLive(queryParams).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.getRoomSummaries(queryParams) } } fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow> { return session.getGroupSummariesLive(queryParams).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.getGroupSummaries(queryParams) } } fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Flow> { return session.spaceService().getSpaceSummariesLive(queryParams).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.spaceService().getSpaceSummaries(queryParams) } } fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow> { return session.getBreadcrumbsLive(queryParams).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.getBreadcrumbs(queryParams) } } fun liveMyDevicesInfo(): Flow> { return session.cryptoService().getLiveMyDevicesInfo().asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.cryptoService().getMyDevicesInfo() } } @@ -89,14 +89,14 @@ class FlowSession(private val session: Session) { fun liveUser(userId: String): Flow> { return session.getUserLive(userId).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.getUser(userId).toOptional() } } fun liveRoomMember(userId: String, roomId: String): Flow> { return session.getRoomMemberLive(userId, roomId).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.getRoomMember(userId, roomId).toOptional() } } @@ -115,45 +115,45 @@ class FlowSession(private val session: Session) { fun liveThreePIds(refreshData: Boolean): Flow> { return session.getThreePidsLive(refreshData).asFlow() - .startWith { session.getThreePids() } + .startWith(session.coroutineDispatchers.io) { session.getThreePids() } } fun livePendingThreePIds(): Flow> { return session.getPendingThreePidsLive().asFlow() - .startWith { session.getPendingThreePids() } + .startWith(session.coroutineDispatchers.io) { session.getPendingThreePids() } } fun liveUserCryptoDevices(userId: String): Flow> { return session.cryptoService().getLiveCryptoDeviceInfo(userId).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.cryptoService().getCryptoDeviceInfo(userId) } } fun liveCrossSigningInfo(userId: String): Flow> { return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.cryptoService().crossSigningService().getUserCrossSigningKeys(userId).toOptional() } } fun liveCrossSigningPrivateKeys(): Flow> { return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.cryptoService().crossSigningService().getCrossSigningPrivateKeys().toOptional() } } fun liveUserAccountData(types: Set): Flow> { return session.accountDataService().getLiveUserAccountDataEvents(types).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.accountDataService().getUserAccountDataEvents(types) } } fun liveRoomAccountData(types: Set): Flow> { return session.accountDataService().getLiveRoomAccountDataEvents(types).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.accountDataService().getRoomAccountDataEvents(types) } } @@ -165,7 +165,7 @@ class FlowSession(private val session: Session) { excludedTypes: Set? = null ): Flow> { return session.widgetService().getRoomWidgetsLive(roomId, widgetId, widgetTypes, excludedTypes).asFlow() - .startWith { + .startWith(session.coroutineDispatchers.io) { session.widgetService().getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt index 192f6442b2..3e3af10799 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt @@ -18,7 +18,7 @@ package org.matrix.android.sdk import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.asCoroutineDispatcher -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import java.util.concurrent.Executors internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/MatrixCoroutineDispatchers.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixCoroutineDispatchers.kt similarity index 85% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/MatrixCoroutineDispatchers.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixCoroutineDispatchers.kt index b44a543c1c..592974be74 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/MatrixCoroutineDispatchers.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixCoroutineDispatchers.kt @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.util +package org.matrix.android.sdk.api import kotlinx.coroutines.CoroutineDispatcher -internal data class MatrixCoroutineDispatchers( +data class MatrixCoroutineDispatchers( val io: CoroutineDispatcher, val computation: CoroutineDispatcher, val main: CoroutineDispatcher, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index bde68da9d7..124d7d9dae 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -20,6 +20,7 @@ import androidx.annotation.MainThread import androidx.lifecycle.LiveData import kotlinx.coroutines.flow.SharedFlow import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.federation.FederationService @@ -82,6 +83,8 @@ interface Session : SecureStorageService, AccountService { + val coroutineDispatchers: MatrixCoroutineDispatchers + /** * The params associated to the session */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt index ebe96b6382..6c0e730499 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.session.room import androidx.lifecycle.LiveData +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataService import org.matrix.android.sdk.api.session.room.alias.AliasService import org.matrix.android.sdk.api.session.room.call.RoomCallService @@ -61,6 +62,8 @@ interface Room : RoomAccountDataService, RoomVersionService { + val coroutineDispatchers: MatrixCoroutineDispatchers + /** * The roomId of this room */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 7115ff5db2..7b96148e2e 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.crypto.MXCryptoConfig @@ -93,7 +94,6 @@ import org.matrix.android.sdk.internal.task.TaskThread import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.task.launchToCallback import org.matrix.android.sdk.internal.util.JsonCanonicalizer -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.olm.OlmManager import timber.log.Timber import java.util.concurrent.atomic.AtomicBoolean diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt index 8a91376b60..494e6d7cc7 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto import kotlinx.coroutines.CancellationException import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel @@ -29,7 +30,6 @@ import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.sync.SyncTokenStore import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.logLimit import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt index fe17dd08e4..70081ebdd7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt @@ -20,6 +20,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType @@ -33,7 +34,6 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask import org.matrix.android.sdk.internal.extensions.foldToCallback import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import timber.log.Timber import javax.inject.Inject import kotlin.jvm.Throws diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt index 3825a5dab2..e7a46750b0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt @@ -19,10 +19,10 @@ package org.matrix.android.sdk.internal.crypto import android.util.LruCache import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import timber.log.Timber import java.util.Timer import java.util.TimerTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt index e8640d5011..220f25ec80 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.crypto.MXCryptoConfig import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME @@ -38,7 +39,6 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import timber.log.Timber import java.util.concurrent.Executors diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt index 6fc7103668..fd60e43260 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt @@ -19,13 +19,13 @@ package org.matrix.android.sdk.internal.crypto import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId import org.matrix.android.sdk.internal.crypto.util.RequestIdHelper import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index d2f6bd0382..d7411ad0be 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType @@ -41,7 +42,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import timber.log.Timber internal class MXMegolmDecryption(private val userId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt index 91640523fc..29f9d193f8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import kotlinx.coroutines.CoroutineScope +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.crypto.DeviceListManager import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager @@ -25,7 +26,6 @@ import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject internal class MXMegolmDecryptionFactory @Inject constructor( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 63fe678229..031bb4e194 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event @@ -39,7 +40,6 @@ import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepo import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask import org.matrix.android.sdk.internal.util.JsonCanonicalizer -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.convertToUTF8 import timber.log.Timber diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt index 9f6312ea97..238d7eed88 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import kotlinx.coroutines.CoroutineScope +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.crypto.DeviceListManager import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction @@ -27,7 +28,6 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject internal class MXMegolmEncryptionFactory @Inject constructor( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt index 68a95e395b..50ce2d2bf3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.crypto.algorithms.olm +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.crypto.DeviceListManager import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForUsersAction import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject internal class MXOlmEncryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt index 113255bb7e..b470ab34bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt @@ -16,13 +16,13 @@ package org.matrix.android.sdk.internal.crypto.crosssigning import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.task.Task -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject internal interface ComputeTrustTask : Task { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt index 8a851b1267..83de06a668 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt @@ -22,6 +22,7 @@ import androidx.work.ExistingWorkPolicy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService @@ -42,7 +43,6 @@ import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.TaskThread import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.JsonCanonicalizer -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.logLimit import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import org.matrix.olm.OlmPkSigning diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index c6e2c1217f..fe2de6aa9c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError @@ -83,7 +84,6 @@ import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.TaskThread import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.JsonCanonicalizer -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.olm.OlmException import org.matrix.olm.OlmPkDecryption diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt index ad3bc012df..e6d8b5e84f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.secrets import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService @@ -44,7 +45,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.util.computeRecoveryKey import org.matrix.android.sdk.internal.crypto.tools.HkdfSha256 import org.matrix.android.sdk.internal.crypto.tools.withOlmDecryption import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.olm.OlmPkMessage import java.security.SecureRandom import javax.crypto.Cipher diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt index 768109979d..388ecb9659 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt @@ -21,6 +21,7 @@ import android.os.Looper import dagger.Lazy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME @@ -83,7 +84,6 @@ import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import timber.log.Timber import java.util.UUID import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt index 5bc519e960..81a067f2c0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt @@ -24,6 +24,7 @@ import dagger.Component import okhttp3.OkHttpClient import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.raw.RawService @@ -35,7 +36,6 @@ import org.matrix.android.sdk.internal.session.MockHttpInterceptor import org.matrix.android.sdk.internal.session.TestInterceptor import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.system.SystemModule import org.matrix.olm.OlmManager import java.io.File diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt index 4cd960f426..9cab307c61 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt @@ -23,7 +23,7 @@ import dagger.Provides import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.android.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.createBackgroundHandler import org.matrix.olm.OlmManager import java.io.File diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt index 414c018074..46c5967876 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.completeWith import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.file.FileService @@ -33,7 +34,6 @@ import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.file.AtomicFileCreator import org.matrix.android.sdk.internal.util.md5 import org.matrix.android.sdk.internal.util.writeToFile diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index d41bf8a702..7260517ec5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -22,6 +22,7 @@ import io.realm.RealmConfiguration import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.withContext import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.federation.FederationService @@ -86,6 +87,7 @@ internal class DefaultSession @Inject constructor( private val globalErrorHandler: GlobalErrorHandler, @SessionId override val sessionId: String, + override val coroutineDispatchers: MatrixCoroutineDispatchers, @SessionDatabase private val realmConfiguration: RealmConfiguration, private val lifecycleObservers: Set<@JvmSuppressWildcards SessionLifecycleObserver>, private val sessionListeners: SessionListeners, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index 71031a4614..8ee3f44f20 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session import dagger.BindsInstance import dagger.Component +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.crypto.CancelGossipRequestWorker @@ -62,7 +63,6 @@ import org.matrix.android.sdk.internal.session.user.UserModule import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataModule import org.matrix.android.sdk.internal.session.widgets.WidgetModule import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.system.SystemModule @Component(dependencies = [MatrixComponent::class], diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt index da37948cd4..37d9a4e74f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt @@ -22,6 +22,7 @@ import androidx.lifecycle.LifecycleRegistry import dagger.Lazy import kotlinx.coroutines.withContext import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull @@ -51,7 +52,6 @@ import org.matrix.android.sdk.internal.session.profile.UnbindThreePidsTask import org.matrix.android.sdk.internal.session.sync.model.accountdata.IdentityServerContent import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.ensureProtocol import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt index 386fec8256..a19832c523 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt @@ -22,6 +22,7 @@ import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy import io.realm.kotlin.where import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.profile.ProfileService @@ -35,7 +36,6 @@ import org.matrix.android.sdk.internal.session.content.FileUploader import org.matrix.android.sdk.internal.session.user.UserStore import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject internal class DefaultProfileService @Inject constructor(private val taskExecutor: TaskExecutor, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index 8afd690f64..cb4bcdb606 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room import androidx.lifecycle.LiveData +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.Room @@ -71,7 +72,8 @@ internal class DefaultRoom(override val roomId: String, private val roomVersionService: RoomVersionService, private val sendStateTask: SendStateTask, private val viaParameterFinder: ViaParameterFinder, - private val searchTask: SearchTask + private val searchTask: SearchTask, + override val coroutineDispatchers: MatrixCoroutineDispatchers ) : Room, TimelineService by timelineService, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt index d44eb32529..4ab06338a2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.internal.session.SessionScope @@ -66,7 +67,8 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService: private val roomAccountDataServiceFactory: DefaultRoomAccountDataService.Factory, private val sendStateTask: SendStateTask, private val viaParameterFinder: ViaParameterFinder, - private val searchTask: SearchTask) : + private val searchTask: SearchTask, + private val coroutineDispatchers: MatrixCoroutineDispatchers) : RoomFactory { override fun create(roomId: String): Room { @@ -92,7 +94,8 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService: roomVersionService = roomVersionServiceFactory.create(roomId), sendStateTask = sendStateTask, searchTask = searchTask, - viaParameterFinder = viaParameterFinder + viaParameterFinder = viaParameterFinder, + coroutineDispatchers = coroutineDispatchers ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt index 046f8ba8ba..3867e0dc8d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt @@ -21,10 +21,10 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.session.room.send.DraftService import org.matrix.android.sdk.api.session.room.send.UserDraft import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers internal class DefaultDraftService @AssistedInject constructor(@Assisted private val roomId: String, private val draftRepository: DraftRepository, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt index cce169c246..9480cc73f1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.launch import org.matrix.android.sdk.api.Matrix +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.isTokenError import org.matrix.android.sdk.api.session.Session @@ -34,7 +35,6 @@ import org.matrix.android.sdk.internal.session.sync.SyncPresence import org.matrix.android.sdk.internal.session.sync.SyncTask import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import timber.log.Timber import java.net.SocketTimeoutException import java.util.concurrent.atomic.AtomicBoolean diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt index 86848d1018..57574a96fa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt @@ -21,10 +21,10 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.di.MatrixScope import org.matrix.android.sdk.internal.extensions.foldToCallback -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.toCancelable import timber.log.Timber import javax.inject.Inject diff --git a/vector/build.gradle b/vector/build.gradle index 55e03de3f3..ffc90f4750 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -391,6 +391,7 @@ dependencies { //TODO: remove when entirely migrated to Flow implementation libs.airbnb.mavericksRx + // Work implementation libs.androidx.work @@ -513,6 +514,7 @@ dependencies { testImplementation libs.mockk.mockk // Plant Timber tree for test testImplementation libs.tests.timberJunitRule + testImplementation libs.airbnb.mavericksTesting // Activate when you want to check for leaks, from time to time. //debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3' diff --git a/vector/src/test/java/androidx/lifecycle/LifecycleRegistry.kt b/vector/src/test/java/androidx/lifecycle/LifecycleRegistry.kt deleted file mode 100644 index 15a76f5e1e..0000000000 --- a/vector/src/test/java/androidx/lifecycle/LifecycleRegistry.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package androidx.lifecycle - -/** - * Manual test override to stop BaseMvRxViewModel from interacting with the android looper/main thread - * Tests will run on their original test worker threads - * - * This has been fixed is newer versions of Mavericks via LifecycleRegistry.createUnsafe - * https://github.com/airbnb/mavericks/blob/master/mvrx-rxjava2/src/main/kotlin/com/airbnb/mvrx/BaseMvRxViewModel.kt#L61 - */ -@Suppress("UNUSED") -class LifecycleRegistry(@Suppress("UNUSED_PARAMETER") lifecycleOwner: LifecycleOwner) : Lifecycle() { - - private var state = State.INITIALIZED - - fun setCurrentState(state: State) { - this.state = state - } - - override fun addObserver(observer: LifecycleObserver) { - TODO("Not yet implemented") - } - - override fun removeObserver(observer: LifecycleObserver) { - TODO("Not yet implemented") - } - - override fun getCurrentState() = state -} diff --git a/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt b/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt index 8f48f10868..0fff663972 100644 --- a/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt @@ -17,6 +17,7 @@ package im.vector.app.features.crypto.quads import com.airbnb.mvrx.Uninitialized +import com.airbnb.mvrx.test.MvRxTestRule import im.vector.app.test.InstantRxRule import im.vector.app.test.fakes.FakeSession import im.vector.app.test.fakes.FakeStringProvider @@ -40,6 +41,8 @@ class SharedSecureStorageViewModelTest { @get:Rule val instantRx = InstantRxRule() + @get:Rule + val mvrxTestRule = MvRxTestRule() private val stringProvider = FakeStringProvider() private val session = FakeSession() diff --git a/vector/src/test/java/im/vector/app/test/TestCoroutineDispatchers.kt b/vector/src/test/java/im/vector/app/test/TestCoroutineDispatchers.kt new file mode 100644 index 0000000000..bf24d146e6 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/TestCoroutineDispatchers.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test + +import kotlinx.coroutines.Dispatchers +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers + +internal val testCoroutineDispatchers = MatrixCoroutineDispatchers( + io = Dispatchers.Main, + computation = Dispatchers.Main, + main = Dispatchers.Main, + crypto = Dispatchers.Main, + dmVerif = Dispatchers.Main +) diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt index f5ee51b020..91403b3b2c 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt @@ -16,6 +16,7 @@ package im.vector.app.test.fakes +import im.vector.app.test.testCoroutineDispatchers import io.mockk.mockk import org.matrix.android.sdk.api.session.Session @@ -23,6 +24,8 @@ class FakeSession( val fakeCryptoService: FakeCryptoService = FakeCryptoService(), val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService() ) : Session by mockk(relaxed = true) { + override fun cryptoService() = fakeCryptoService override val sharedSecretStorageService = fakeSharedSecretStorageService + override val coroutineDispatchers = testCoroutineDispatchers } From 786dec5dc0f7834768fa320baa261477ec4e9752 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 12 Oct 2021 12:42:35 +0100 Subject: [PATCH 140/144] observing both the email pushers and email pids so that displayed email pushers are always in sync --- .../sdk/internal/extensions/LiveData.kt | 23 +++++++++++++++++++ ...rSettingsNotificationPreferenceFragment.kt | 18 +++++++-------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/LiveData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/LiveData.kt index 718e7869dd..fecbb874d0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/LiveData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/LiveData.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.extensions import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.Observer inline fun LiveData.observeK(owner: LifecycleOwner, crossinline observer: (T?) -> Unit) { @@ -27,3 +28,25 @@ inline fun LiveData.observeK(owner: LifecycleOwner, crossinline observer: inline fun LiveData.observeNotNull(owner: LifecycleOwner, crossinline observer: (T) -> Unit) { this.observe(owner, Observer { it?.run(observer) }) } + +fun combineLatest(source1: LiveData, source2: LiveData, mapper: (T1, T2) -> R): LiveData { + val combined = MediatorLiveData() + var source1Result: T1? = null + var source2Result: T2? = null + + fun notify() { + if (source1Result != null && source2Result != null) { + combined.value = mapper(source1Result!!, source2Result!!) + } + } + + combined.addSource(source1) { + source1Result = it + notify() + } + combined.addSource(source2) { + source2Result = it + notify() + } + return combined +} diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 1c73fbb470..b014b3d2dc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -54,6 +54,7 @@ import org.matrix.android.sdk.api.pushrules.RuleKind import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.pushers.Pusher +import org.matrix.android.sdk.internal.extensions.combineLatest import javax.inject.Inject // Referenced in vector_settings_preferences_root.xml @@ -339,11 +340,11 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( override fun onPreferenceTreeClick(preference: Preference?): Boolean { return when (preference?.key) { - VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY -> { + VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY -> { updateEnabledForAccount(preference) true } - else -> { + else -> { return super.onPreferenceTreeClick(preference) } } @@ -406,12 +407,9 @@ private fun Session.getEmailsWithPushInformation(): List>> { - return getThreePidsLive(refreshData = true) - .distinctUntilChanged() - .map { threePids -> - val emailPushers = getPushers().filter { it.kind == Pusher.KIND_EMAIL } - threePids - .filterIsInstance() - .map { it to emailPushers.any { pusher -> pusher.pushKey == it.email } } - } + val emailThreePids = getThreePidsLive(refreshData = true).map { it.filterIsInstance() } + val emailPushers = getPushersLive().map { it.filter { pusher -> pusher.kind == Pusher.KIND_EMAIL } } + return combineLatest(emailThreePids, emailPushers) { emailThreePidsResult, emailPushersResult -> + emailThreePidsResult.map { it to emailPushersResult.any { pusher -> pusher.pushKey == it.email } } + }.distinctUntilChanged() } From b79b7f5740a674ca87f3f75e078eabfddfa1f728 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 12 Oct 2021 12:59:26 +0100 Subject: [PATCH 141/144] adding changelog entry --- changelog.d/4106.bugfix | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog.d/4106.bugfix diff --git a/changelog.d/4106.bugfix b/changelog.d/4106.bugfix new file mode 100644 index 0000000000..6ca030f8c4 --- /dev/null +++ b/changelog.d/4106.bugfix @@ -0,0 +1,2 @@ +Fixes push notification emails list not refreshing the first time seeing the notifications page. +Also improves the error handling in the email notification toggling by using synchronous flows instead of the WorkManager \ No newline at end of file From d11f4e5e31071000f7be563d447e09190e6baf73 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 Oct 2021 14:06:24 +0200 Subject: [PATCH 142/144] Add changelog --- changelog.d/4193.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4193.bugfix diff --git a/changelog.d/4193.bugfix b/changelog.d/4193.bugfix new file mode 100644 index 0000000000..a395e70cee --- /dev/null +++ b/changelog.d/4193.bugfix @@ -0,0 +1 @@ +Fix random crash when user logs out just after the log in. \ No newline at end of file From a7ec76bae3f919771ebbdc823aa2de3c10f15465 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 Oct 2021 14:20:20 +0200 Subject: [PATCH 143/144] Also call monarchyWriteAsyncExecutor.awaitTermination --- .../sdk/internal/crypto/store/db/RealmCryptoStore.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index f5e3916bb9..40678a6ce6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -25,6 +25,7 @@ import io.realm.Realm import io.realm.RealmConfiguration import io.realm.Sort import io.realm.kotlin.where +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.send.SendState @@ -101,6 +102,7 @@ import org.matrix.olm.OlmException import org.matrix.olm.OlmOutboundGroupSession import timber.log.Timber import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.collections.set @@ -206,6 +208,10 @@ internal class RealmCryptoStore @Inject constructor( // Ensure no async request will be run later val tasks = monarchyWriteAsyncExecutor.shutdownNow() Timber.w("Closing RealmCryptoStore, ${tasks.size} async task(s) cancelled") + tryOrNull("Interrupted") { + // Wait 1 minute max + monarchyWriteAsyncExecutor.awaitTermination(1, TimeUnit.MINUTES) + } olmSessionsToRelease.forEach { it.value.olmSession.releaseSession() From 2b2f5be83e3049a207d4ac41f02212a84aff2323 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 12 Oct 2021 15:51:27 +0300 Subject: [PATCH 144/144] Fix typo in filename --- ...c-from-external-soruces.yml => sync-from-external-sources.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{sync-from-external-soruces.yml => sync-from-external-sources.yml} (100%) diff --git a/.github/workflows/sync-from-external-soruces.yml b/.github/workflows/sync-from-external-sources.yml similarity index 100% rename from .github/workflows/sync-from-external-soruces.yml rename to .github/workflows/sync-from-external-sources.yml