From 03a419404734dde97302f4800888f8c4b60ea235 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 14 Dec 2021 14:44:09 +0300 Subject: [PATCH 001/281] Add location attachment icon with required permissions. --- vector/src/main/AndroidManifest.xml | 4 ++++ .../vector/app/core/utils/PermissionsTools.kt | 1 + .../attachments/AttachmentTypeSelectorView.kt | 23 +++++++++++-------- .../home/room/detail/RoomDetailFragment.kt | 15 ++++++------ .../drawable/ic_attachment_location_white.xml | 9 ++++++++ .../layout/view_attachment_type_selector.xml | 21 +++++++++++++++++ vector/src/main/res/values/strings.xml | 1 + 7 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_attachment_location_white.xml diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 93f4ac7632..f03b8735b4 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -42,6 +42,10 @@ android:name="android.permission.WRITE_CALENDAR" tools:node="remove" /> + + + + diff --git a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt index ba396ed252..5fc5e0de84 100644 --- a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt +++ b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt @@ -40,6 +40,7 @@ val PERMISSIONS_FOR_MEMBERS_SEARCH = listOf(Manifest.permission.READ_CONTACTS) val PERMISSIONS_FOR_ROOM_AVATAR = listOf(Manifest.permission.CAMERA) val PERMISSIONS_FOR_WRITING_FILES = listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) val PERMISSIONS_FOR_PICKING_CONTACT = listOf(Manifest.permission.READ_CONTACTS) +val PERMISSIONS_FOR_LOCATION_SHARING = listOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION) val PERMISSIONS_EMPTY = emptyList() diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt index ccc07ef118..da3d40b36e 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt @@ -40,6 +40,7 @@ import com.amulyakhare.textdrawable.util.ColorGenerator import im.vector.app.R import im.vector.app.core.extensions.getMeasurements import im.vector.app.core.utils.PERMISSIONS_EMPTY +import im.vector.app.core.utils.PERMISSIONS_FOR_LOCATION_SHARING import im.vector.app.core.utils.PERMISSIONS_FOR_PICKING_CONTACT import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.databinding.ViewAttachmentTypeSelectorBinding @@ -77,6 +78,7 @@ class AttachmentTypeSelectorView(context: Context, views.attachmentAudioButton.configure(Type.AUDIO) views.attachmentContactButton.configure(Type.CONTACT) views.attachmentPollButton.configure(Type.POLL) + views.attachmentLocationButton.configure(Type.LOCATION) width = LinearLayout.LayoutParams.MATCH_PARENT height = LinearLayout.LayoutParams.WRAP_CONTENT animationStyle = 0 @@ -110,7 +112,8 @@ class AttachmentTypeSelectorView(context: Context, animateButtonIn(views.attachmentAudioButton, 0) animateButtonIn(views.attachmentContactButton, ANIMATION_DURATION / 4) animateButtonIn(views.attachmentStickersButton, ANIMATION_DURATION / 2) - animateButtonIn(views.attachmentPollButton, ANIMATION_DURATION / 4) + animateButtonIn(views.attachmentPollButton, ANIMATION_DURATION / 2) + animateButtonIn(views.attachmentLocationButton, ANIMATION_DURATION / 4) } override fun dismiss() { @@ -124,13 +127,14 @@ class AttachmentTypeSelectorView(context: Context, fun setAttachmentVisibility(type: Type, isVisible: Boolean) { when (type) { - Type.CAMERA -> views.attachmentCameraButtonContainer - Type.GALLERY -> views.attachmentGalleryButtonContainer - Type.FILE -> views.attachmentFileButtonContainer - Type.STICKER -> views.attachmentStickersButtonContainer - Type.AUDIO -> views.attachmentAudioButtonContainer - Type.CONTACT -> views.attachmentContactButtonContainer - Type.POLL -> views.attachmentPollButtonContainer + Type.CAMERA -> views.attachmentCameraButtonContainer + Type.GALLERY -> views.attachmentGalleryButtonContainer + Type.FILE -> views.attachmentFileButtonContainer + Type.STICKER -> views.attachmentStickersButtonContainer + Type.AUDIO -> views.attachmentAudioButtonContainer + Type.CONTACT -> views.attachmentContactButtonContainer + Type.POLL -> views.attachmentPollButtonContainer + Type.LOCATION -> views.attachmentLocationButtonContainer }.let { it.isVisible = isVisible } @@ -230,6 +234,7 @@ class AttachmentTypeSelectorView(context: Context, STICKER(PERMISSIONS_EMPTY), AUDIO(PERMISSIONS_EMPTY), CONTACT(PERMISSIONS_FOR_PICKING_CONTACT), - POLL(PERMISSIONS_EMPTY) + POLL(PERMISSIONS_EMPTY), + LOCATION(PERMISSIONS_FOR_LOCATION_SHARING) } } 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 a77899a8b0..455f93f973 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 @@ -2207,18 +2207,19 @@ class RoomDetailFragment @Inject constructor( private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) { when (type) { - AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera( + AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera( activity = requireActivity(), vectorPreferences = vectorPreferences, cameraActivityResultLauncher = attachmentCameraActivityResultLauncher, cameraVideoActivityResultLauncher = attachmentCameraVideoActivityResultLauncher ) - AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher) - AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentMediaActivityResultLauncher) - AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher) - AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher) - AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment) - AttachmentTypeSelectorView.Type.POLL -> navigator.openCreatePoll(requireContext(), roomDetailArgs.roomId) + AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher) + AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentMediaActivityResultLauncher) + AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher) + AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher) + AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment) + AttachmentTypeSelectorView.Type.POLL -> navigator.openCreatePoll(requireContext(), roomDetailArgs.roomId) + AttachmentTypeSelectorView.Type.LOCATION -> Timber.d("On location attachment clicked") }.exhaustive } diff --git a/vector/src/main/res/drawable/ic_attachment_location_white.xml b/vector/src/main/res/drawable/ic_attachment_location_white.xml new file mode 100644 index 0000000000..865362312b --- /dev/null +++ b/vector/src/main/res/drawable/ic_attachment_location_white.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/layout/view_attachment_type_selector.xml b/vector/src/main/res/layout/view_attachment_type_selector.xml index 4cd5e1910d..9ca7b8cdb0 100644 --- a/vector/src/main/res/layout/view_attachment_type_selector.xml +++ b/vector/src/main/res/layout/view_attachment_type_selector.xml @@ -194,6 +194,27 @@ android:importantForAccessibility="no" android:text="@string/attachment_type_poll" /> + + + + + + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 34ac5fcddc..38c7d355c3 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2466,6 +2466,7 @@ "Gallery" "Sticker" Poll + Location Rotate and crop Couldn\'t handle share data From bf48617fc6354ed0f88a03e13b0ff1fb41525b28 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 15 Dec 2021 00:13:52 +0300 Subject: [PATCH 002/281] Create base UI components of location sharing screen. --- vector/src/main/AndroidManifest.xml | 1 + .../app/core/di/MavericksViewModelModule.kt | 6 ++ .../home/room/detail/RoomDetailFragment.kt | 5 +- .../location/LocationSharingAction.kt | 23 ++++++ .../location/LocationSharingActivity.kt | 80 +++++++++++++++++++ .../location/LocationSharingFragment.kt | 40 ++++++++++ .../location/LocationSharingViewEvents.kt | 23 ++++++ .../location/LocationSharingViewModel.kt | 41 ++++++++++ .../location/LocationSharingViewState.kt | 37 +++++++++ .../features/navigation/DefaultNavigator.kt | 11 +++ .../app/features/navigation/Navigator.kt | 3 + .../res/layout/activity_location_sharing.xml | 26 ++++++ .../res/layout/fragment_location_sharing.xml | 6 ++ vector/src/main/res/values/strings.xml | 4 + 14 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt create mode 100755 vector/src/main/res/layout/activity_location_sharing.xml create mode 100644 vector/src/main/res/layout/fragment_location_sharing.xml diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index f03b8735b4..28833ab333 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -336,6 +336,7 @@ + diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index d09cd21d19..b9757078ea 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -53,6 +53,7 @@ import im.vector.app.features.home.room.detail.upgrade.MigrateRoomViewModel import im.vector.app.features.home.room.list.RoomListViewModel import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel import im.vector.app.features.invite.InviteUsersToRoomViewModel +import im.vector.app.features.location.LocationSharingViewModel import im.vector.app.features.login.LoginViewModel import im.vector.app.features.login2.LoginViewModel2 import im.vector.app.features.login2.created.AccountCreatedViewModel @@ -576,4 +577,9 @@ interface MavericksViewModelModule { @IntoMap @MavericksViewModelKey(CreatePollViewModel::class) fun createPollViewModelFactory(factory: CreatePollViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(LocationSharingViewModel::class) + fun createLocationSharingViewModelFactory(factory: LocationSharingViewModel.Factory): MavericksAssistedViewModelFactory<*, *> } 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 455f93f973..22eeb7759b 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 @@ -165,6 +165,7 @@ import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillImageSpan import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.invite.VectorInviteView +import im.vector.app.features.location.LocationSharingMode import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import im.vector.app.features.notifications.NotificationDrawerManager @@ -2219,7 +2220,9 @@ class RoomDetailFragment @Inject constructor( AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher) AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment) AttachmentTypeSelectorView.Type.POLL -> navigator.openCreatePoll(requireContext(), roomDetailArgs.roomId) - AttachmentTypeSelectorView.Type.LOCATION -> Timber.d("On location attachment clicked") + AttachmentTypeSelectorView.Type.LOCATION -> { + navigator.openLocationSharing(requireContext(), roomDetailArgs.roomId, LocationSharingMode.STATIC_SHARING) + } }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt new file mode 100644 index 0000000000..5139888b08 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.location + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class LocationSharingAction : VectorViewModelAction { + data class OnShareLocation(val latitude: Double, val longitude: Double) : LocationSharingAction() +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt new file mode 100644 index 0000000000..fc685815db --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt @@ -0,0 +1,80 @@ +/* + * 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.location + +import android.content.Context +import android.content.Intent +import android.os.Parcelable +import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.extensions.addFragment +import im.vector.app.core.platform.ToolbarConfigurable +import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.databinding.ActivityLocationSharingBinding +import kotlinx.parcelize.Parcelize + +@Parcelize +data class LocationSharingArgs( + val roomId: String, + val mode: LocationSharingMode +) : Parcelable + +@AndroidEntryPoint +class LocationSharingActivity : VectorBaseActivity(), + ToolbarConfigurable { + + override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater) + + override fun configure(toolbar: MaterialToolbar) { + configureToolbar(toolbar) + } + + override fun initUiAndData() { + val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelable(EXTRA_LOCATION_SHARING_ARGS) + if (locationSharingArgs == null) { + finish() + return + } + configure(views.toolbar) + supportActionBar?.title = getString(locationSharingArgs.mode.titleRes) + + if (isFirstCreation()) { + when (locationSharingArgs.mode) { + LocationSharingMode.STATIC_SHARING -> { + addFragment( + views.fragmentContainer, + LocationSharingFragment::class.java, + locationSharingArgs + ) + } + LocationSharingMode.PREVIEW -> { + } + } + } + } + + companion object { + + private const val EXTRA_LOCATION_SHARING_ARGS = "EXTRA_LOCATION_SHARING_ARGS" + + fun getIntent(context: Context, locationSharingArgs: LocationSharingArgs): Intent { + return Intent(context, LocationSharingActivity::class.java).apply { + putExtra(EXTRA_LOCATION_SHARING_ARGS, locationSharingArgs) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt new file mode 100644 index 0000000000..fd2e48546d --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.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 im.vector.app.features.location + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentLocationSharingBinding +import javax.inject.Inject + +class LocationSharingFragment @Inject constructor() : + VectorBaseFragment() { + + private val viewModel: LocationSharingViewModel by activityViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding { + return FragmentLocationSharingBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt new file mode 100644 index 0000000000..1032a696ff --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.location + +import im.vector.app.core.platform.VectorViewEvents + +sealed class LocationSharingViewEvents : VectorViewEvents { + object Close : LocationSharingViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt new file mode 100644 index 0000000000..6da00f176d --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -0,0 +1,41 @@ +/* + * 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.location + +import com.airbnb.mvrx.MavericksViewModelFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.platform.VectorViewModel + +class LocationSharingViewModel @AssistedInject constructor( + @Assisted private val initialState: LocationSharingViewState +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: LocationSharingViewState): LocationSharingViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { + } + + override fun handle(action: LocationSharingAction) { + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt new file mode 100644 index 0000000000..d62e568fb2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt @@ -0,0 +1,37 @@ +/* + * 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.location + +import androidx.annotation.StringRes +import com.airbnb.mvrx.MavericksState +import im.vector.app.R + +enum class LocationSharingMode(@StringRes val titleRes: Int) { + STATIC_SHARING(R.string.location_activity_title_static_sharing), + PREVIEW(R.string.location_activity_title_preview) +} + +data class LocationSharingViewState( + val roomId: String, + val mode: LocationSharingMode +) : MavericksState { + + constructor(locationSharingArgs: LocationSharingArgs) : this( + roomId = locationSharingArgs.roomId, + mode = locationSharingArgs.mode + ) +} 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 6b035e7d49..16c50daf94 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 @@ -57,6 +57,9 @@ import im.vector.app.features.home.room.detail.search.SearchActivity import im.vector.app.features.home.room.detail.search.SearchArgs import im.vector.app.features.home.room.filtered.FilteredRoomsActivity import im.vector.app.features.invite.InviteUsersToRoomActivity +import im.vector.app.features.location.LocationSharingActivity +import im.vector.app.features.location.LocationSharingArgs +import im.vector.app.features.location.LocationSharingMode import im.vector.app.features.login.LoginActivity import im.vector.app.features.login.LoginConfig import im.vector.app.features.login2.LoginActivity2 @@ -533,6 +536,14 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } + override fun openLocationSharing(context: Context, roomId: String, mode: LocationSharingMode) { + val intent = LocationSharingActivity.getIntent( + context, + LocationSharingArgs(roomId = roomId, mode = mode) + ) + context.startActivity(intent) + } + private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) { if (buildTask) { val stackBuilder = TaskStackBuilder.create(context) 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 6778c39a22..2668e10694 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 @@ -25,6 +25,7 @@ import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.displayname.getBestName +import im.vector.app.features.location.LocationSharingMode import im.vector.app.features.login.LoginConfig import im.vector.app.features.media.AttachmentData import im.vector.app.features.pin.PinMode @@ -149,4 +150,6 @@ interface Navigator { fun openCallTransfer(context: Context, callId: String) fun openCreatePoll(context: Context, roomId: String) + + fun openLocationSharing(context: Context, roomId: String, mode: LocationSharingMode) } diff --git a/vector/src/main/res/layout/activity_location_sharing.xml b/vector/src/main/res/layout/activity_location_sharing.xml new file mode 100755 index 0000000000..b278bb5a1a --- /dev/null +++ b/vector/src/main/res/layout/activity_location_sharing.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_location_sharing.xml b/vector/src/main/res/layout/fragment_location_sharing.xml new file mode 100644 index 0000000000..77d9ef65f8 --- /dev/null +++ b/vector/src/main/res/layout/fragment_location_sharing.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 38c7d355c3..0f26861d36 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3702,4 +3702,8 @@ Poll ended Remove poll Are you sure you want to remove this poll? You won\'t be able to recover it once removed. + + + Share location + Location From 824e713c51c5aa5b89a85ed5e4c105f5e76a4ba8 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 15 Dec 2021 13:57:43 +0300 Subject: [PATCH 003/281] Add mapview to the layout. --- build.gradle | 2 ++ vector/build.gradle | 3 +++ .../location/LocationSharingFragment.kt | 19 +++++++++++++++++++ .../res/layout/fragment_location_sharing.xml | 5 +++++ 4 files changed, 29 insertions(+) diff --git a/build.gradle b/build.gradle index e17f357905..29c3e61a44 100644 --- a/build.gradle +++ b/build.gradle @@ -61,6 +61,8 @@ allprojects { groups.jitsi.group.each { includeGroup it } } } + // TODO. MapTiler + mavenCentral() google { content { groups.google.regex.each { includeGroupByRegex it } diff --git a/vector/build.gradle b/vector/build.gradle index a578fdb52f..c3dd6ba50c 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -486,6 +486,9 @@ dependencies { } implementation 'commons-codec:commons-codec:1.15' + // MapTiler + implementation 'org.maplibre.gl:android-sdk:9.5.2' + // TESTS testImplementation libs.tests.junit diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index fd2e48546d..7c59972871 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -21,6 +21,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.airbnb.mvrx.activityViewModel +import com.mapbox.mapboxsdk.Mapbox +import im.vector.app.BuildConfig import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentLocationSharingBinding import javax.inject.Inject @@ -34,7 +36,24 @@ class LocationSharingFragment @Inject constructor() : return FragmentLocationSharingBinding.inflate(inflater, container, false) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // Initialize Mapbox before inflating mapView + Mapbox.getInstance(requireContext()) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + initMapView(savedInstanceState) + } + + private fun initMapView(savedInstanceState: Bundle?) { + val key = BuildConfig.mapTilerKey + val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${key}" + views.mapView.onCreate(savedInstanceState) + views.mapView.getMapAsync { map -> + map.setStyle(styleUrl) + } } } diff --git a/vector/src/main/res/layout/fragment_location_sharing.xml b/vector/src/main/res/layout/fragment_location_sharing.xml index 77d9ef65f8..6399b41796 100644 --- a/vector/src/main/res/layout/fragment_location_sharing.xml +++ b/vector/src/main/res/layout/fragment_location_sharing.xml @@ -3,4 +3,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + \ No newline at end of file From 5904a5955fed630169ee8ee669407e2412204e6c Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 15 Dec 2021 21:07:16 +0300 Subject: [PATCH 004/281] Show and track the current location of the user on map. --- vector/build.gradle | 1 + .../im/vector/app/core/di/FragmentModule.kt | 6 ++ .../location/LocationSharingFragment.kt | 102 +++++++++++++++++- .../app/features/location/LocationTracker.kt | 80 ++++++++++++++ .../src/main/res/drawable/bg_map_user_pin.xml | 10 ++ .../res/layout/fragment_location_sharing.xml | 38 +++++++ vector/src/main/res/values/strings.xml | 2 + 7 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationTracker.kt create mode 100644 vector/src/main/res/drawable/bg_map_user_pin.xml diff --git a/vector/build.gradle b/vector/build.gradle index c3dd6ba50c..197349f0f1 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -488,6 +488,7 @@ dependencies { // MapTiler implementation 'org.maplibre.gl:android-sdk:9.5.2' + implementation 'org.maplibre.gl:android-plugin-annotation-v9:1.0.0' // TESTS diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index ff84a46dab..3314c0565e 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -61,6 +61,7 @@ import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.detail.RoomDetailFragment import im.vector.app.features.home.room.detail.search.SearchFragment import im.vector.app.features.home.room.list.RoomListFragment +import im.vector.app.features.location.LocationSharingFragment import im.vector.app.features.login.LoginCaptchaFragment import im.vector.app.features.login.LoginFragment import im.vector.app.features.login.LoginGenericTextInputFormFragment @@ -855,4 +856,9 @@ interface FragmentModule { @IntoMap @FragmentKey(CreatePollFragment::class) fun bindCreatePollFragment(fragment: CreatePollFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(LocationSharingFragment::class) + fun bindLocationSharingFragment(fragment: LocationSharingFragment): Fragment } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 7c59972871..390d1aafa5 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -16,22 +16,55 @@ package im.vector.app.features.location +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import com.airbnb.mvrx.activityViewModel +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition import com.mapbox.mapboxsdk.Mapbox +import com.mapbox.mapboxsdk.camera.CameraPosition +import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapbox.mapboxsdk.maps.MapboxMap +import com.mapbox.mapboxsdk.maps.Style +import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager +import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions +import com.mapbox.mapboxsdk.style.layers.Property import im.vector.app.BuildConfig +import im.vector.app.R +import im.vector.app.core.glide.GlideApp import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentLocationSharingBinding +import im.vector.app.features.home.AvatarRenderer +import org.billcarsonfr.jsonviewer.Utils +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject -class LocationSharingFragment @Inject constructor() : - VectorBaseFragment() { +class LocationSharingFragment @Inject constructor( + private val locationTracker: LocationTracker, + private val session: Session, + private val avatarRenderer: AvatarRenderer +) : VectorBaseFragment(), LocationTracker.Callback { + + init { + locationTracker.callback = this + } private val viewModel: LocationSharingViewModel by activityViewModel() + private val glideRequests by lazy { + GlideApp.with(this) + } + + private var map: MapboxMap? = null + private var symbolManager: SymbolManager? = null + private var lastZoomValue: Double = -1.0 + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding { return FragmentLocationSharingBinding.inflate(inflater, container, false) } @@ -48,12 +81,73 @@ class LocationSharingFragment @Inject constructor() : initMapView(savedInstanceState) } + override fun onDestroyView() { + super.onDestroyView() + locationTracker.stop() + } + private fun initMapView(savedInstanceState: Bundle?) { val key = BuildConfig.mapTilerKey - val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${key}" + val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=$key" views.mapView.onCreate(savedInstanceState) views.mapView.getMapAsync { map -> - map.setStyle(styleUrl) + map.setStyle(styleUrl) { style -> + addUserPinToMap(style) + this.symbolManager = SymbolManager(views.mapView, map, style) + this.map = map + // All set, start location tracker + locationTracker.start() + } } } + + private fun addUserPinToMap(style: Style) { + session.getUser(session.myUserId)?.toMatrixItem()?.let { + val size = Utils.dpToPx(44, requireContext()) + avatarRenderer.render(glideRequests, it, object : CustomTarget(size, size) { + override fun onResourceReady(resource: Drawable, transition: Transition?) { + val bgUserPin = ContextCompat.getDrawable(requireActivity(), R.drawable.bg_map_user_pin)!! + val layerDrawable = LayerDrawable(arrayOf(bgUserPin, resource)) + val horizontalInset = Utils.dpToPx(4, requireContext()) + val topInset = Utils.dpToPx(4, requireContext()) + val bottomInset = Utils.dpToPx(8, requireContext()) + layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset) + + style.addImage( + USER_PIN_NAME, + layerDrawable + ) + } + + override fun onLoadCleared(placeholder: Drawable?) { + // Is it possible? Put placeholder instead? + } + }) + } + } + + override fun onLocationUpdate(latitude: Double, longitude: Double) { + lastZoomValue = if (lastZoomValue == -1.0) INITIAL_ZOOM else map?.cameraPosition?.zoom ?: INITIAL_ZOOM + + val latLng = LatLng(latitude, longitude) + + map?.cameraPosition = CameraPosition.Builder() + .target(latLng) + .zoom(lastZoomValue) + .build() + + symbolManager?.deleteAll() + + symbolManager?.create( + SymbolOptions() + .withLatLng(latLng) + .withIconImage(USER_PIN_NAME) + .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) + ) + } + + companion object { + const val INITIAL_ZOOM = 12.0 + const val USER_PIN_NAME = "USER_PIN_NAME" + } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt new file mode 100644 index 0000000000..3cf2689aee --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -0,0 +1,80 @@ +/* + * 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.location + +import android.content.Context +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import timber.log.Timber +import javax.inject.Inject + +class LocationTracker @Inject constructor( + private val context: Context) : LocationListener { + + interface Callback { + fun onLocationUpdate(latitude: Double, longitude: Double) + } + + private var locationManager: LocationManager? = null + var callback: Callback? = null + + fun start() { + val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as? LocationManager + + locationManager?.let { + val isGpsEnabled = it.isProviderEnabled(LocationManager.GPS_PROVIDER) + val isNetworkEnabled = it.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + + val provider = when { + isGpsEnabled -> LocationManager.GPS_PROVIDER + isNetworkEnabled -> LocationManager.NETWORK_PROVIDER + else -> { + Timber.v("## LocationTracker. There is no location provider available") + return + } + } + + // Send last known location without waiting location updates + it.getLastKnownLocation(provider)?.let { lastKnownLocation -> + callback?.onLocationUpdate(lastKnownLocation.latitude, lastKnownLocation.longitude) + } + + it.requestLocationUpdates( + provider, + MIN_TIME_MILLIS_TO_UPDATE, + MIN_DISTANCE_METERS_TO_UPDATE, + this + ) + } ?: run { + Timber.v("## LocationTracker. LocationManager is not available") + } + } + + fun stop() { + locationManager?.removeUpdates(this) + } + + override fun onLocationChanged(location: Location) { + callback?.onLocationUpdate(location.latitude, location.longitude) + } + + companion object { + const val MIN_TIME_MILLIS_TO_UPDATE = 1 * 60 * 1000L // every 1 minute + const val MIN_DISTANCE_METERS_TO_UPDATE = 10f + } +} diff --git a/vector/src/main/res/drawable/bg_map_user_pin.xml b/vector/src/main/res/drawable/bg_map_user_pin.xml new file mode 100644 index 0000000000..148d3cfa29 --- /dev/null +++ b/vector/src/main/res/drawable/bg_map_user_pin.xml @@ -0,0 +1,10 @@ + + + diff --git a/vector/src/main/res/layout/fragment_location_sharing.xml b/vector/src/main/res/layout/fragment_location_sharing.xml index 6399b41796..bc587dc182 100644 --- a/vector/src/main/res/layout/fragment_location_sharing.xml +++ b/vector/src/main/res/layout/fragment_location_sharing.xml @@ -1,5 +1,6 @@ @@ -8,4 +9,41 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 0f26861d36..166f25c053 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3706,4 +3706,6 @@ Share location Location + Share location + Share location From 6495bd9e5ed373c33047a4135ec11d469cac8ecb Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 17 Dec 2021 15:09:02 +0300 Subject: [PATCH 005/281] Send location event. --- .../room/model/message/LocationInfo.kt | 22 +++--------- .../model/message/MessageLocationContent.kt | 8 ++--- .../sdk/api/session/room/send/SendService.kt | 8 +++++ .../session/room/send/DefaultSendService.kt | 6 ++++ .../room/send/LocalEchoEventFactory.kt | 35 +++++++++++++++++++ .../app/features/location/LocationData.kt | 24 +++++++++++++ .../location/LocationSharingAction.kt | 3 +- .../location/LocationSharingFragment.kt | 28 +++++++++++++-- .../location/LocationSharingViewEvents.kt | 1 + .../location/LocationSharingViewModel.kt | 29 ++++++++++++++- .../location/LocationSharingViewState.kt | 3 +- .../app/features/location/LocationTracker.kt | 6 ++-- vector/src/main/res/values/strings.xml | 2 ++ 13 files changed, 145 insertions(+), 30 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationData.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/LocationInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/LocationInfo.kt index a76c3c5b64..5110926294 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/LocationInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/LocationInfo.kt @@ -18,29 +18,17 @@ package org.matrix.android.sdk.api.session.room.model.message import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo @JsonClass(generateAdapter = true) data class LocationInfo( /** - * The URL to the thumbnail of the file. Only present if the thumbnail is unencrypted. + * Required. Required. RFC5870 formatted geo uri 'geo:latitude,longitude;uncertainty' like 'geo:40.05,29.24;30' representing this location. */ - @Json(name = "thumbnail_url") val thumbnailUrl: String? = null, + @Json(name = "uri") val geoUri: String? = null, /** - * Metadata about the image referred to in thumbnail_url. + * Required. A description of the location e.g. 'Big Ben, London, UK', or some kind + * of content description for accessibility e.g. 'location attachment'. */ - @Json(name = "thumbnail_info") val thumbnailInfo: ThumbnailInfo? = null, - - /** - * Information on the encrypted thumbnail file, as specified in End-to-end encryption. Only present if the thumbnail is encrypted. - */ - @Json(name = "thumbnail_file") val thumbnailFile: EncryptedFileInfo? = null + @Json(name = "description") val description: String? = null ) - -/** - * Get the url of the encrypted thumbnail or of the thumbnail - */ -fun LocationInfo.getThumbnailUrl(): String? { - return thumbnailFile?.url ?: thumbnailUrl -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt index 6881c09924..9571cc4280 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt @@ -26,7 +26,7 @@ data class MessageLocationContent( /** * Required. Must be 'm.location'. */ - @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String, + @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_LOCATION, /** * Required. A description of the location e.g. 'Big Ben, London, UK', or some kind @@ -35,14 +35,14 @@ data class MessageLocationContent( @Json(name = "body") override val body: String, /** - * Required. A geo URI representing this location. + * Required. RFC5870 formatted geo uri 'geo:latitude,longitude;uncertainty' like 'geo:40.05,29.24;30' representing this location. */ @Json(name = "geo_uri") val geoUri: String, /** - * + * See https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md */ - @Json(name = "info") val locationInfo: LocationInfo? = null, + @Json(name = "org.matrix.msc3488.location") val locationInfo: LocationInfo? = null, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.new_content") override val newContent: Content? = null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 5b387c3413..7983ad7068 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -122,6 +122,14 @@ interface SendService { */ fun resendMediaMessage(localEcho: TimelineEvent): Cancelable + /** + * Send a location event to the room + * @param latitude required latitude of the location + * @param longitude required longitude of the location + * @param uncertainty Accuracy of the location in meters + */ + fun sendLocation(latitude: Double, longitude: Double, uncertainty: Double?): Cancelable + /** * Remove this failed message from the timeline * @param localEcho the unsent local echo diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index d3162aef79..dbe4d5eb73 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -115,6 +115,12 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } + override fun sendLocation(latitude: Double, longitude: Double, uncertainty: Double?): Cancelable { + return localEchoEventFactory.createLocationEvent(roomId, latitude, longitude, uncertainty) + .also { createLocalEcho(it) } + .let { sendEvent(it) } + } + override fun redactEvent(event: Event, reason: String?): Cancelable { // TODO manage media/attachements? val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 85b22628d7..179df22326 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.message.AudioInfo import org.matrix.android.sdk.api.session.room.model.message.AudioWaveformInfo import org.matrix.android.sdk.api.session.room.model.message.FileInfo import org.matrix.android.sdk.api.session.room.model.message.ImageInfo +import org.matrix.android.sdk.api.session.room.model.message.LocationInfo import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody @@ -39,6 +40,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollConte import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent import org.matrix.android.sdk.api.session.room.model.message.MessageFormat import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent @@ -194,6 +196,22 @@ internal class LocalEchoEventFactory @Inject constructor( unsignedData = UnsignedData(age = null, transactionId = localId)) } + fun createLocationEvent(roomId: String, + latitude: Double, + longitude: Double, + uncertainty: Double?): Event { + val geoUri = buildGeoUri(latitude, longitude, uncertainty) + val content = MessageLocationContent( + geoUri = geoUri, + body = geoUri, + locationInfo = LocationInfo( + geoUri = geoUri, + description = geoUri + ) + ) + return createMessageEvent(roomId, content) + } + fun createReplaceTextOfReply(roomId: String, eventReplaced: TimelineEvent, originalEvent: TimelineEvent, @@ -463,6 +481,23 @@ internal class LocalEchoEventFactory @Inject constructor( } } + /** + * Returns RFC5870 formatted geo uri 'geo:latitude,longitude;uncertainty' like 'geo:40.05,29.24;30' + * Uncertainty of the location is in meters and not required. + */ + private fun buildGeoUri(latitude: Double, longitude: Double, uncertainty: Double?): String { + return buildString { + append("geo:") + append(latitude) + append(",") + append(longitude) + uncertainty?.let { + append(";") + append(it) + } + } + } + /* * { "content": { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt new file mode 100644 index 0000000000..f85b8713d9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt @@ -0,0 +1,24 @@ +/* + * 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.location + +data class LocationData( + val latitude: Double, + val longitude: Double, + val uncertainty: Double? +) + diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt index 5139888b08..0efaefaa5b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt @@ -19,5 +19,6 @@ package im.vector.app.features.location import im.vector.app.core.platform.VectorViewModelAction sealed class LocationSharingAction : VectorViewModelAction { - data class OnShareLocation(val latitude: Double, val longitude: Double) : LocationSharingAction() + data class OnLocationUpdate(val locationData: LocationData) : LocationSharingAction() + object OnShareLocation : LocationSharingAction() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 390d1aafa5..8cd86a2356 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -26,6 +26,7 @@ import androidx.core.content.ContextCompat import com.airbnb.mvrx.activityViewModel import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.mapbox.mapboxsdk.Mapbox import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng @@ -79,6 +80,17 @@ class LocationSharingFragment @Inject constructor( super.onViewCreated(view, savedInstanceState) initMapView(savedInstanceState) + + views.shareLocationContainer.debouncedClicks { + viewModel.handle(LocationSharingAction.OnShareLocation) + } + + viewModel.observeViewEvents { + when (it) { + LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError() + LocationSharingViewEvents.Close -> activity?.finish() + } + } } override fun onDestroyView() { @@ -126,10 +138,10 @@ class LocationSharingFragment @Inject constructor( } } - override fun onLocationUpdate(latitude: Double, longitude: Double) { + override fun onLocationUpdate(locationData: LocationData) { lastZoomValue = if (lastZoomValue == -1.0) INITIAL_ZOOM else map?.cameraPosition?.zoom ?: INITIAL_ZOOM - val latLng = LatLng(latitude, longitude) + val latLng = LatLng(locationData.latitude, locationData.longitude) map?.cameraPosition = CameraPosition.Builder() .target(latLng) @@ -144,10 +156,20 @@ class LocationSharingFragment @Inject constructor( .withIconImage(USER_PIN_NAME) .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) ) + + viewModel.handle(LocationSharingAction.OnLocationUpdate(locationData)) + } + + private fun handleLocationNotAvailableError() { + MaterialAlertDialogBuilder(requireActivity()) + .setTitle(R.string.location_not_available_dialog_title) + .setMessage(R.string.location_not_available_dialog_content) + .setPositiveButton(R.string.ok, null) + .show() } companion object { - const val INITIAL_ZOOM = 12.0 + const val INITIAL_ZOOM = 15.0 const val USER_PIN_NAME = "USER_PIN_NAME" } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt index 1032a696ff..743daaf5e0 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt @@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewEvents sealed class LocationSharingViewEvents : VectorViewEvents { object Close : LocationSharingViewEvents() + object LocationNotAvailableError : LocationSharingViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index 6da00f176d..4525cac446 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -23,11 +23,15 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel +import org.matrix.android.sdk.api.session.Session class LocationSharingViewModel @AssistedInject constructor( - @Assisted private val initialState: LocationSharingViewState + @Assisted private val initialState: LocationSharingViewState, + session: Session ) : VectorViewModel(initialState) { + private val room = session.getRoom(initialState.roomId)!! + @AssistedFactory interface Factory : MavericksAssistedViewModelFactory { override fun create(initialState: LocationSharingViewState): LocationSharingViewModel @@ -37,5 +41,28 @@ class LocationSharingViewModel @AssistedInject constructor( } override fun handle(action: LocationSharingAction) { + when (action) { + is LocationSharingAction.OnLocationUpdate -> handleLocationUpdate(action.locationData) + LocationSharingAction.OnShareLocation -> handleShareLocation() + } + } + + private fun handleShareLocation() = withState { state -> + state.lastKnownLocation?.let { location -> + room.sendLocation( + latitude = location.latitude, + longitude = location.longitude, + uncertainty = location.uncertainty + ) + _viewEvents.post(LocationSharingViewEvents.Close) + } ?: run { + _viewEvents.post(LocationSharingViewEvents.LocationNotAvailableError) + } + } + + private fun handleLocationUpdate(locationData: LocationData) { + setState { + copy(lastKnownLocation = locationData) + } } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt index d62e568fb2..2869929b12 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt @@ -27,7 +27,8 @@ enum class LocationSharingMode(@StringRes val titleRes: Int) { data class LocationSharingViewState( val roomId: String, - val mode: LocationSharingMode + val mode: LocationSharingMode, + val lastKnownLocation: LocationData? = null ) : MavericksState { constructor(locationSharingArgs: LocationSharingArgs) : this( diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 3cf2689aee..93993245f8 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -27,7 +27,7 @@ class LocationTracker @Inject constructor( private val context: Context) : LocationListener { interface Callback { - fun onLocationUpdate(latitude: Double, longitude: Double) + fun onLocationUpdate(locationData: LocationData) } private var locationManager: LocationManager? = null @@ -51,7 +51,7 @@ class LocationTracker @Inject constructor( // Send last known location without waiting location updates it.getLastKnownLocation(provider)?.let { lastKnownLocation -> - callback?.onLocationUpdate(lastKnownLocation.latitude, lastKnownLocation.longitude) + callback?.onLocationUpdate(LocationData(lastKnownLocation.latitude, lastKnownLocation.longitude, lastKnownLocation.accuracy.toDouble())) } it.requestLocationUpdates( @@ -70,7 +70,7 @@ class LocationTracker @Inject constructor( } override fun onLocationChanged(location: Location) { - callback?.onLocationUpdate(location.latitude, location.longitude) + callback?.onLocationUpdate(LocationData(location.latitude, location.longitude, location.accuracy.toDouble())) } companion object { diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 166f25c053..086469a1fd 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3708,4 +3708,6 @@ Location Share location Share location + Element could not access your location + Element could not access your location. Please try again later. From a0afab45fbc421e86e0647d96fa9aa37a0aec64f Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 27 Dec 2021 14:03:59 +0300 Subject: [PATCH 006/281] Show location preview and allow to share with external apps. --- .../java/im/vector/app/VectorApplication.kt | 4 + .../im/vector/app/core/di/FragmentModule.kt | 6 ++ .../core/utils/ExternalApplicationsUtil.kt | 20 ++++ .../home/room/detail/RoomDetailAction.kt | 4 + .../home/room/detail/RoomDetailFragment.kt | 21 +++- .../home/room/detail/RoomDetailViewEvents.kt | 3 + .../home/room/detail/RoomDetailViewModel.kt | 6 ++ .../timeline/factory/MessageItemFactory.kt | 40 +++++-- .../timeline/helper/LocationPinProvider.kt | 74 +++++++++++++ .../timeline/item/MessageLocationItem.kt | 80 ++++++++++++++ .../app/features/location/LocationData.kt | 49 ++++++++- .../location/LocationPreviewFragment.kt | 85 +++++++++++++++ .../location/LocationSharingActivity.kt | 9 +- .../location/LocationSharingFragment.kt | 102 +++--------------- .../app/features/location/LocationTracker.kt | 1 + .../app/features/location/MapTilerMapView.kt | 92 ++++++++++++++++ .../app/features/location/VectorMapView.kt | 36 +++++++ .../features/navigation/DefaultNavigator.kt | 9 +- .../app/features/navigation/Navigator.kt | 7 +- .../main/res/drawable/ic_share_external.xml | 5 + .../res/layout/fragment_location_preview.xml | 11 ++ .../res/layout/fragment_location_sharing.xml | 2 +- .../res/layout/item_timeline_event_base.xml | 5 + .../item_timeline_event_location_stub.xml | 17 +++ .../main/res/menu/menu_location_preview.xml | 12 +++ vector/src/main/res/values/strings.xml | 1 + 26 files changed, 601 insertions(+), 100 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt create mode 100644 vector/src/main/java/im/vector/app/features/location/VectorMapView.kt create mode 100644 vector/src/main/res/drawable/ic_share_external.xml create mode 100644 vector/src/main/res/layout/fragment_location_preview.xml create mode 100644 vector/src/main/res/layout/item_timeline_event_location_stub.xml create mode 100644 vector/src/main/res/menu/menu_location_preview.xml diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index 400fb7eb89..52de28a1cf 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -36,6 +36,7 @@ import com.airbnb.epoxy.EpoxyController import com.airbnb.mvrx.Mavericks import com.facebook.stetho.Stetho import com.gabrielittner.threetenbp.LazyThreeTen +import com.mapbox.mapboxsdk.Mapbox import com.vanniktech.emoji.EmojiManager import com.vanniktech.emoji.google.GoogleEmojiProvider import dagger.hilt.android.HiltAndroidApp @@ -195,6 +196,9 @@ class VectorApplication : }) EmojiManager.install(GoogleEmojiProvider()) + + // Initialize Mapbox before inflating mapViews + Mapbox.getInstance(this) } private val startSyncOnFirstStart = object : DefaultLifecycleObserver { diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 3314c0565e..4d70f49186 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -61,6 +61,7 @@ import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.detail.RoomDetailFragment import im.vector.app.features.home.room.detail.search.SearchFragment import im.vector.app.features.home.room.list.RoomListFragment +import im.vector.app.features.location.LocationPreviewFragment import im.vector.app.features.location.LocationSharingFragment import im.vector.app.features.login.LoginCaptchaFragment import im.vector.app.features.login.LoginFragment @@ -861,4 +862,9 @@ interface FragmentModule { @IntoMap @FragmentKey(LocationSharingFragment::class) fun bindLocationSharingFragment(fragment: LocationSharingFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(LocationPreviewFragment::class) + fun bindLocationPreviewFragment(fragment: LocationPreviewFragment): Fragment } diff --git a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt index bdaf520ba1..f3138218eb 100644 --- a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt +++ b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt @@ -297,6 +297,26 @@ fun openMedia(activity: Activity, savedMediaPath: String, mimeType: String) { } } +/** + * Open external location + * @param activity the activity + * @param latitude latitude of the location + * @param longitude longitude of the location + */ +fun openLocation(activity: Activity, latitude: Double, longitude: Double) { + val locationUri = buildString { + append("geo:") + append(latitude) + append(",") + append(longitude) + append("?q=") // This is required to drop a pin to the location + append(latitude) + append(",") + append(longitude) + } + openUri(activity, locationUri) +} + fun shareMedia(context: Context, file: File, mediaMimeType: String?) { val mediaUri = try { FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", file) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index f20a32848c..ce04b6812d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -20,6 +20,7 @@ import android.net.Uri import android.view.View import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.call.conference.ConferenceEvent +import im.vector.app.features.location.LocationData import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent @@ -110,4 +111,7 @@ sealed class RoomDetailAction : VectorViewModelAction { // Poll data class EndPoll(val eventId: String) : RoomDetailAction() + + // Location + data class ShowLocation(val locationData: LocationData, val userId: String) : RoomDetailAction() } 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 22eeb7759b..d530b35c0c 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 @@ -465,6 +465,7 @@ class RoomDetailFragment @Inject constructor( RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects() is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it) RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement() + is RoomDetailViewEvents.ShowLocation -> handleShowLocationPreview(it) }.exhaustive } @@ -596,6 +597,17 @@ class RoomDetailFragment @Inject constructor( } } + private fun handleShowLocationPreview(viewEvent: RoomDetailViewEvents.ShowLocation) { + navigator + .openLocationSharing( + context = requireContext(), + roomId = roomDetailArgs.roomId, + mode = LocationSharingMode.PREVIEW, + initialLocationData = viewEvent.locationData, + locationOwnerId = viewEvent.userId + ) + } + private fun requestNativeWidgetPermission(it: RoomDetailViewEvents.RequestNativeWidgetPermission) { val tag = RoomWidgetPermissionBottomSheet::class.java.name val dFrag = childFragmentManager.findFragmentByTag(tag) as? RoomWidgetPermissionBottomSheet @@ -2221,7 +2233,14 @@ class RoomDetailFragment @Inject constructor( AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment) AttachmentTypeSelectorView.Type.POLL -> navigator.openCreatePoll(requireContext(), roomDetailArgs.roomId) AttachmentTypeSelectorView.Type.LOCATION -> { - navigator.openLocationSharing(requireContext(), roomDetailArgs.roomId, LocationSharingMode.STATIC_SHARING) + navigator + .openLocationSharing( + context = requireContext(), + roomId = roomDetailArgs.roomId, + mode = LocationSharingMode.STATIC_SHARING, + initialLocationData = null, + locationOwnerId = session.myUserId + ) } }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index 2e7f2bfd63..de1110dfcb 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -20,6 +20,7 @@ import android.net.Uri import android.view.View import im.vector.app.core.platform.VectorViewEvents import im.vector.app.features.call.webrtc.WebRtcCall +import im.vector.app.features.location.LocationData import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode @@ -81,4 +82,6 @@ sealed class RoomDetailViewEvents : VectorViewEvents { data class StartChatEffect(val type: ChatEffect) : RoomDetailViewEvents() object StopChatEffects : RoomDetailViewEvents() object RoomReplacementStarted : RoomDetailViewEvents() + + data class ShowLocation(val locationData: LocationData, val userId: String) : RoomDetailViewEvents() } 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 f438c6e1e4..0c67c66af8 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 @@ -50,6 +50,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.location.LocationData import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorDataStore @@ -330,9 +331,14 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.OpenRoom(action.replacementRoomId, closeCurrentRoom = true)) } is RoomDetailAction.EndPoll -> handleEndPoll(action.eventId) + is RoomDetailAction.ShowLocation -> handleShowLocation(action.locationData, action.userId) }.exhaustive } + private fun handleShowLocation(locationData: LocationData, userId: String) { + _viewEvents.post(RoomDetailViewEvents.ShowLocation(locationData, userId)) + } + private fun handleJitsiCallJoinStatus(action: RoomDetailAction.UpdateJoinJitsiCallStatus) = withState { state -> if (state.jitsiState.confId == null) { // If jitsi widget is removed while on the call diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 22d282d567..76e8908d2d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -37,6 +37,7 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider @@ -49,6 +50,8 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData +import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem +import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem @@ -67,6 +70,7 @@ import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.html.SpanUtils import im.vector.app.features.html.VectorHtmlCompressor +import im.vector.app.features.location.LocationData import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import me.gujun.android.span.span @@ -82,6 +86,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithF import org.matrix.android.sdk.api.session.room.model.message.MessageEmoteContent import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent import org.matrix.android.sdk.api.session.room.model.message.MessageImageInfoContent +import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessageNoticeContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent @@ -116,7 +121,8 @@ class MessageItemFactory @Inject constructor( private val pillsPostProcessorFactory: PillsPostProcessor.Factory, private val spanUtils: SpanUtils, private val session: Session, - private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker) { + private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker, + private val locationPinProvider: LocationPinProvider) { // TODO inject this properly? private var roomId: String = "" @@ -168,16 +174,36 @@ class MessageItemFactory @Inject constructor( } } is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes) - is MessagePollContent -> buildPollContent(messageContent, informationData, highlight, callback, attributes) + is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes) + is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, callback, attributes) else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes) } } - private fun buildPollContent(pollContent: MessagePollContent, - informationData: MessageInformationData, - highlight: Boolean, - callback: TimelineEventController.Callback?, - attributes: AbsMessageItem.Attributes): PollItem? { + private fun buildLocationItem(locationContent: MessageLocationContent, + informationData: MessageInformationData, + highlight: Boolean, + callback: TimelineEventController.Callback?, + attributes: AbsMessageItem.Attributes): MessageLocationItem? { + + val geoUri = locationContent.locationInfo?.geoUri ?: locationContent.geoUri + val locationData = LocationData.create(geoUri) + + return MessageLocationItem_() + .attributes(attributes) + .locationData(locationData) + .userId(informationData.senderId) + .locationPinProvider(locationPinProvider) + .highlighted(highlight) + .leftGuideline(avatarSizeProvider.leftGuideline) + .callback(callback) + } + + private fun buildPollItem(pollContent: MessagePollContent, + informationData: MessageInformationData, + highlight: Boolean, + callback: TimelineEventController.Callback?, + attributes: AbsMessageItem.Attributes): PollItem? { val optionViewStates = mutableListOf() val pollResponseSummary = informationData.pollResponseAggregatedSummary diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt new file mode 100644 index 0000000000..92a4bb8473 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt @@ -0,0 +1,74 @@ +/* + * 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.home.room.detail.timeline.helper + +import android.content.Context +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import androidx.core.content.ContextCompat +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import im.vector.app.R +import im.vector.app.core.glide.GlideApp +import im.vector.app.features.home.AvatarRenderer +import org.billcarsonfr.jsonviewer.Utils +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LocationPinProvider @Inject constructor( + private val context: Context, + private val session: Session, + private val avatarRenderer: AvatarRenderer +) { + private val cache = mutableMapOf() + + private val glideRequests by lazy { + GlideApp.with(context) + } + + fun create(userId: String, callback: (Drawable) -> Unit) { + if (cache.contains(userId)) { + callback(cache[userId]!!) + return + } + + session.getUser(userId)?.toMatrixItem()?.let { + val size = Utils.dpToPx(44, context) + avatarRenderer.render(glideRequests, it, object : CustomTarget(size, size) { + override fun onResourceReady(resource: Drawable, transition: Transition?) { + val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!! + val layerDrawable = LayerDrawable(arrayOf(bgUserPin, resource)) + val horizontalInset = Utils.dpToPx(4, context) + val topInset = Utils.dpToPx(4, context) + val bottomInset = Utils.dpToPx(8, context) + layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset) + + cache[userId] = layerDrawable + + callback(layerDrawable) + } + + override fun onLoadCleared(placeholder: Drawable?) { + // Is it possible? Put placeholder instead? + } + }) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt new file mode 100644 index 0000000000..d4995d3fad --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -0,0 +1,80 @@ +/* + * 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.home.room.detail.timeline.item + +import androidx.constraintlayout.widget.ConstraintLayout +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.onClick +import im.vector.app.features.home.room.detail.RoomDetailAction +import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider +import im.vector.app.features.location.LocationData +import im.vector.app.features.location.MapTilerMapView +import im.vector.app.features.location.VectorMapListener + +@EpoxyModelClass(layout = R.layout.item_timeline_event_base) +abstract class MessageLocationItem : AbsMessageItem() { + + @EpoxyAttribute + var callback: TimelineEventController.Callback? = null + + @EpoxyAttribute + var locationData: LocationData? = null + + @EpoxyAttribute + var userId: String? = null + + @EpoxyAttribute + var locationPinProvider: LocationPinProvider? = null + + override fun bind(holder: Holder) { + super.bind(holder) + renderSendState(holder.mapViewContainer, null) + + val location = locationData ?: return + val locationOwnerId = userId ?: return + + holder.mapView.initialize(object : VectorMapListener { + override fun onMapReady() { + holder.mapView.zoomToLocation(location.latitude, location.longitude, INITIAL_ZOOM) + + locationPinProvider?.create(locationOwnerId) { pinDrawable -> + holder.mapView.addPinToMap(locationOwnerId, pinDrawable) + holder.mapView.updatePinLocation(locationOwnerId, location.latitude, location.longitude) + } + + holder.mapView.onClick { + callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(location, locationOwnerId)) + } + } + }) + } + + override fun getViewType() = STUB_ID + + class Holder : AbsMessageItem.Holder(STUB_ID) { + val mapViewContainer by bind(R.id.mapViewContainer) + val mapView by bind(R.id.mapView) + } + + companion object { + private const val STUB_ID = R.id.messageContentLocationStub + private const val INITIAL_ZOOM = 15.0 + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt index f85b8713d9..874e159e80 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationData.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt @@ -16,9 +16,56 @@ package im.vector.app.features.location +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize data class LocationData( val latitude: Double, val longitude: Double, val uncertainty: Double? -) +) : Parcelable { + + fun toGeoUri(): String { + return buildString { + append("geo:") + append(latitude) + append(",") + append(longitude) + append("?q=") + append(latitude) + append(",") + append(longitude) + } + } + + companion object { + + /** + * Creates location data from geo uri + * @param geoUri geo:latitude,longitude;uncertainty + * @return location data or null if geo uri is not valid + */ + fun create(geoUri: String): LocationData? { + val geoParts = geoUri + .split(":") + .takeIf { it.firstOrNull() == "geo" } + ?.getOrNull(1) + ?.split(",") + + val latitude = geoParts?.firstOrNull() + val geoTailParts = geoParts?.getOrNull(1)?.split(";") + val longitude = geoTailParts?.firstOrNull() + val uncertainty = geoTailParts?.getOrNull(1) + + return if (latitude != null && longitude != null) { + LocationData( + latitude = latitude.toDouble(), + longitude = longitude.toDouble(), + uncertainty = uncertainty?.toDouble() + ) + } else null + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt new file mode 100644 index 0000000000..e13456dbdc --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt @@ -0,0 +1,85 @@ +/* + * 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.location + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentLocationPreviewBinding +import javax.inject.Inject +import com.airbnb.mvrx.args +import im.vector.app.R +import im.vector.app.core.utils.openLocation +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider +import org.matrix.android.sdk.api.extensions.tryOrNull + +class LocationPreviewFragment @Inject constructor( + private val locationPinProvider: LocationPinProvider +) : VectorBaseFragment(), VectorMapListener { + + private val args: LocationSharingArgs by args() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationPreviewBinding { + return FragmentLocationPreviewBinding.inflate(layoutInflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + views.mapView.initialize(this) + } + + override fun getMenuRes() = R.menu.menu_location_preview + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.share_external -> { + onShareLocationExternal() + return true + } + } + return super.onOptionsItemSelected(item) + } + + private fun onShareLocationExternal() { + val location = args.initialLocationData ?: return + openLocation(requireActivity(), location.latitude, location.longitude) + } + + override fun onMapReady() { + val location = args.initialLocationData ?: return + val userId = args.locationOwnerId + + locationPinProvider.create(userId) { pinDrawable -> + views.mapView.apply { + zoomToLocation(location.latitude, location.longitude, INITIAL_ZOOM) + deleteAllPins() + addPinToMap(userId, pinDrawable) + updatePinLocation(userId, location.latitude, location.longitude) + } + } + } + + companion object { + const val INITIAL_ZOOM = 15.0 + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt index fc685815db..6a200435dc 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt @@ -30,7 +30,9 @@ import kotlinx.parcelize.Parcelize @Parcelize data class LocationSharingArgs( val roomId: String, - val mode: LocationSharingMode + val mode: LocationSharingMode, + val initialLocationData: LocationData?, + val locationOwnerId: String ) : Parcelable @AndroidEntryPoint @@ -62,6 +64,11 @@ class LocationSharingActivity : VectorBaseActivity { + addFragment( + views.fragmentContainer, + LocationPreviewFragment::class.java, + locationSharingArgs + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 8cd86a2356..c7c4c0fed1 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -16,41 +16,24 @@ package im.vector.app.features.location -import android.graphics.drawable.Drawable -import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.content.ContextCompat import com.airbnb.mvrx.activityViewModel -import com.bumptech.glide.request.target.CustomTarget -import com.bumptech.glide.request.transition.Transition import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.mapbox.mapboxsdk.Mapbox -import com.mapbox.mapboxsdk.camera.CameraPosition -import com.mapbox.mapboxsdk.geometry.LatLng -import com.mapbox.mapboxsdk.maps.MapboxMap -import com.mapbox.mapboxsdk.maps.Style -import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager -import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions -import com.mapbox.mapboxsdk.style.layers.Property -import im.vector.app.BuildConfig import im.vector.app.R -import im.vector.app.core.glide.GlideApp import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentLocationSharingBinding -import im.vector.app.features.home.AvatarRenderer -import org.billcarsonfr.jsonviewer.Utils +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class LocationSharingFragment @Inject constructor( private val locationTracker: LocationTracker, private val session: Session, - private val avatarRenderer: AvatarRenderer -) : VectorBaseFragment(), LocationTracker.Callback { + private val locationPinProvider: LocationPinProvider +) : VectorBaseFragment(), LocationTracker.Callback, VectorMapListener { init { locationTracker.callback = this @@ -58,28 +41,16 @@ class LocationSharingFragment @Inject constructor( private val viewModel: LocationSharingViewModel by activityViewModel() - private val glideRequests by lazy { - GlideApp.with(this) - } - - private var map: MapboxMap? = null - private var symbolManager: SymbolManager? = null private var lastZoomValue: Double = -1.0 override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding { return FragmentLocationSharingBinding.inflate(inflater, container, false) } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - // Initialize Mapbox before inflating mapView - Mapbox.getInstance(requireContext()) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initMapView(savedInstanceState) + views.mapView.initialize(this) views.shareLocationContainer.debouncedClicks { viewModel.handle(LocationSharingAction.OnShareLocation) @@ -98,64 +69,23 @@ class LocationSharingFragment @Inject constructor( locationTracker.stop() } - private fun initMapView(savedInstanceState: Bundle?) { - val key = BuildConfig.mapTilerKey - val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=$key" - views.mapView.onCreate(savedInstanceState) - views.mapView.getMapAsync { map -> - map.setStyle(styleUrl) { style -> - addUserPinToMap(style) - this.symbolManager = SymbolManager(views.mapView, map, style) - this.map = map - // All set, start location tracker - locationTracker.start() - } - } - } - - private fun addUserPinToMap(style: Style) { - session.getUser(session.myUserId)?.toMatrixItem()?.let { - val size = Utils.dpToPx(44, requireContext()) - avatarRenderer.render(glideRequests, it, object : CustomTarget(size, size) { - override fun onResourceReady(resource: Drawable, transition: Transition?) { - val bgUserPin = ContextCompat.getDrawable(requireActivity(), R.drawable.bg_map_user_pin)!! - val layerDrawable = LayerDrawable(arrayOf(bgUserPin, resource)) - val horizontalInset = Utils.dpToPx(4, requireContext()) - val topInset = Utils.dpToPx(4, requireContext()) - val bottomInset = Utils.dpToPx(8, requireContext()) - layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset) - - style.addImage( - USER_PIN_NAME, - layerDrawable - ) - } - - override fun onLoadCleared(placeholder: Drawable?) { - // Is it possible? Put placeholder instead? - } - }) + override fun onMapReady() { + locationPinProvider.create(session.myUserId) { + views.mapView.addPinToMap( + pinId = USER_PIN_NAME, + image = it, + ) + // All set, start location tracker + locationTracker.start() } } override fun onLocationUpdate(locationData: LocationData) { - lastZoomValue = if (lastZoomValue == -1.0) INITIAL_ZOOM else map?.cameraPosition?.zoom ?: INITIAL_ZOOM + lastZoomValue = if (lastZoomValue == -1.0) INITIAL_ZOOM else views.mapView.getCurrentZoom() ?: INITIAL_ZOOM - val latLng = LatLng(locationData.latitude, locationData.longitude) - - map?.cameraPosition = CameraPosition.Builder() - .target(latLng) - .zoom(lastZoomValue) - .build() - - symbolManager?.deleteAll() - - symbolManager?.create( - SymbolOptions() - .withLatLng(latLng) - .withIconImage(USER_PIN_NAME) - .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) - ) + views.mapView.zoomToLocation(locationData.latitude, locationData.longitude, lastZoomValue) + views.mapView.deleteAllPins() + views.mapView.updatePinLocation(USER_PIN_NAME, locationData.latitude, locationData.longitude) viewModel.handle(LocationSharingAction.OnLocationUpdate(locationData)) } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 93993245f8..0ea1e6810b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -67,6 +67,7 @@ class LocationTracker @Inject constructor( fun stop() { locationManager?.removeUpdates(this) + callback = null } override fun onLocationChanged(location: Location) { diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt new file mode 100644 index 0000000000..627b9e5ec3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.location + +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import com.mapbox.mapboxsdk.Mapbox +import com.mapbox.mapboxsdk.camera.CameraPosition +import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapbox.mapboxsdk.maps.MapView +import com.mapbox.mapboxsdk.maps.MapboxMap +import com.mapbox.mapboxsdk.maps.Style +import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager +import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions +import com.mapbox.mapboxsdk.style.layers.Property +import im.vector.app.BuildConfig + +class MapTilerMapView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : MapView(context, attrs, defStyleAttr), VectorMapView { + + private var map: MapboxMap? = null + private var symbolManager: SymbolManager? = null + private var style: Style? = null + + override fun initialize(listener: VectorMapListener) { + getMapAsync { map -> + map.setStyle(styleUrl) { style -> + this.symbolManager = SymbolManager(this, map, style) + this.map = map + this.style = style + listener.onMapReady() + } + } + } + + override fun addPinToMap(pinId: String, image: Drawable) { + style?.addImage(pinId, image) + } + + override fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) { + symbolManager?.create( + SymbolOptions() + .withLatLng(LatLng(latitude, longitude)) + .withIconImage(pinId) + .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) + ) + } + + override fun deleteAllPins() { + symbolManager?.deleteAll() + } + + override fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) { + map?.cameraPosition = CameraPosition.Builder() + .target(LatLng(latitude, longitude)) + .zoom(zoom) + .build() + } + + override fun getCurrentZoom(): Double? { + return map?.cameraPosition?.zoom + } + + override fun onClick(callback: () -> Unit) { + map?.addOnMapClickListener { + callback() + true + } + } + + companion object { + private const val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}" + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt b/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt new file mode 100644 index 0000000000..5dbeced175 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt @@ -0,0 +1,36 @@ +/* + * 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.location + +import android.graphics.drawable.Drawable + +interface VectorMapListener { + fun onMapReady() +} + +interface VectorMapView { + fun initialize(listener: VectorMapListener) + + fun addPinToMap(pinId: String, image: Drawable) + fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) + fun deleteAllPins() + + fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) + fun getCurrentZoom(): Double? + + fun onClick(callback: () -> Unit) +} 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 16c50daf94..fb923dabb2 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 @@ -57,6 +57,7 @@ import im.vector.app.features.home.room.detail.search.SearchActivity import im.vector.app.features.home.room.detail.search.SearchArgs import im.vector.app.features.home.room.filtered.FilteredRoomsActivity import im.vector.app.features.invite.InviteUsersToRoomActivity +import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationSharingActivity import im.vector.app.features.location.LocationSharingArgs import im.vector.app.features.location.LocationSharingMode @@ -536,10 +537,14 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } - override fun openLocationSharing(context: Context, roomId: String, mode: LocationSharingMode) { + override fun openLocationSharing(context: Context, + roomId: String, + mode: LocationSharingMode, + initialLocationData: LocationData?, + locationOwnerId: String) { val intent = LocationSharingActivity.getIntent( context, - LocationSharingArgs(roomId = roomId, mode = mode) + LocationSharingArgs(roomId = roomId, mode = mode, initialLocationData = initialLocationData, locationOwnerId = locationOwnerId) ) context.startActivity(intent) } 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 2668e10694..06f38b381d 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 @@ -25,6 +25,7 @@ import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.displayname.getBestName +import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationSharingMode import im.vector.app.features.login.LoginConfig import im.vector.app.features.media.AttachmentData @@ -151,5 +152,9 @@ interface Navigator { fun openCreatePoll(context: Context, roomId: String) - fun openLocationSharing(context: Context, roomId: String, mode: LocationSharingMode) + fun openLocationSharing(context: Context, + roomId: String, + mode: LocationSharingMode, + initialLocationData: LocationData?, + locationOwnerId: String) } diff --git a/vector/src/main/res/drawable/ic_share_external.xml b/vector/src/main/res/drawable/ic_share_external.xml new file mode 100644 index 0000000000..c4b78c8a83 --- /dev/null +++ b/vector/src/main/res/drawable/ic_share_external.xml @@ -0,0 +1,5 @@ + + + diff --git a/vector/src/main/res/layout/fragment_location_preview.xml b/vector/src/main/res/layout/fragment_location_preview.xml new file mode 100644 index 0000000000..ae3e8f1778 --- /dev/null +++ b/vector/src/main/res/layout/fragment_location_preview.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_location_sharing.xml b/vector/src/main/res/layout/fragment_location_sharing.xml index bc587dc182..8122b5def5 100644 --- a/vector/src/main/res/layout/fragment_location_sharing.xml +++ b/vector/src/main/res/layout/fragment_location_sharing.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - diff --git a/vector/src/main/res/layout/item_timeline_event_base.xml b/vector/src/main/res/layout/item_timeline_event_base.xml index 5a04acf677..6360b287d0 100644 --- a/vector/src/main/res/layout/item_timeline_event_base.xml +++ b/vector/src/main/res/layout/item_timeline_event_base.xml @@ -130,6 +130,11 @@ style="@style/TimelineContentStubBaseParams" android:layout="@layout/item_timeline_event_poll" /> + + + + + + + diff --git a/vector/src/main/res/menu/menu_location_preview.xml b/vector/src/main/res/menu/menu_location_preview.xml new file mode 100644 index 0000000000..2616674b8a --- /dev/null +++ b/vector/src/main/res/menu/menu_location_preview.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 086469a1fd..e5fe5ad5c7 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3710,4 +3710,5 @@ Share location Element could not access your location Element could not access your location. Please try again later. + Open with From ccd4396336927dc165374743e60e3ef285121788 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 27 Dec 2021 14:04:36 +0300 Subject: [PATCH 007/281] Allow to reply location messages. --- .../BottomSheetMessagePreviewItem.kt | 27 +++++++++++++++++++ .../action/MessageActionsEpoxyController.kt | 13 ++++++++- .../action/MessageActionsViewModel.kt | 5 ++-- .../item_bottom_sheet_message_preview.xml | 13 +++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index eef057efd4..efa4702532 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -30,10 +30,16 @@ import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.util.preventMutation import im.vector.app.core.extensions.setTextOrHide import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.home.room.detail.timeline.item.BindingOptions import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess +import im.vector.app.features.location.LocationData +import im.vector.app.features.location.MapTilerMapView +import im.vector.app.features.location.VectorMapListener import im.vector.app.features.media.ImageContentRenderer import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.room.model.message.LocationInfo +import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.util.MatrixItem /** @@ -66,6 +72,12 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel + holder.mapView.initialize(object : VectorMapListener { + override fun onMapReady() { + holder.mapView.zoomToLocation(location.latitude, location.longitude, 15.0) + locationPinProvider?.create(matrixItem.id) { pinDrawable -> + holder.mapView.addPinToMap(matrixItem.id, pinDrawable) + holder.mapView.updatePinLocation(matrixItem.id, location.latitude, location.longitude) + } + } + }) + } } override fun unbind(holder: Holder) { @@ -105,5 +131,6 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel(R.id.bottom_sheet_message_preview_body_details) val timestamp by bind(R.id.bottom_sheet_message_preview_timestamp) val imagePreview by bind(R.id.bottom_sheet_message_preview_image) + val mapView by bind(R.id.bottom_sheet_message_preview_location) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 3826c4cbad..0bd4f1bf21 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -33,14 +33,19 @@ import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.format.EventDetailsFormatter +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod import im.vector.app.features.home.room.detail.timeline.tools.linkify import im.vector.app.features.html.SpanUtils +import im.vector.app.features.location.LocationData import im.vector.app.features.media.ImageContentRenderer import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.failure.Failure +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.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.session.room.send.SendState import javax.inject.Inject @@ -56,7 +61,8 @@ class MessageActionsEpoxyController @Inject constructor( private val errorFormatter: ErrorFormatter, private val spanUtils: SpanUtils, private val eventDetailsFormatter: EventDetailsFormatter, - private val dateFormatter: VectorDateFormatter + private val dateFormatter: VectorDateFormatter, + private val locationPinProvider: LocationPinProvider ) : TypedEpoxyController() { var listener: MessageActionsEpoxyControllerListener? = null @@ -68,6 +74,9 @@ class MessageActionsEpoxyController @Inject constructor( val formattedDate = dateFormatter.format(date, DateFormatKind.MESSAGE_DETAIL) val body = state.messageBody.linkify(host.listener) val bindingOptions = spanUtils.getBindingOptions(body) + val locationData = state.timelineEvent()?.root?.getClearContent()?.toModel(catchError = true)?.let { + LocationData.create(it.locationInfo?.geoUri ?: it.geoUri) + } bottomSheetMessagePreviewItem { id("preview") avatarRenderer(host.avatarRenderer) @@ -80,6 +89,8 @@ class MessageActionsEpoxyController @Inject constructor( body(body) bodyDetails(host.eventDetailsFormatter.format(state.timelineEvent()?.root)) time(formattedDate) + locationData(locationData) + locationPinProvider(host.locationPinProvider) } // Send state 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 ff7d555ee3..2b3d391634 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 @@ -424,8 +424,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted MessageType.MSGTYPE_VIDEO, MessageType.MSGTYPE_AUDIO, MessageType.MSGTYPE_FILE, - MessageType.MSGTYPE_POLL_START -> true - else -> false + MessageType.MSGTYPE_POLL_START, + MessageType.MSGTYPE_LOCATION -> true + else -> false } } diff --git a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml index 771d4d10f0..1959cfc3ba 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml @@ -103,4 +103,17 @@ tools:text="1080 x 1024 - 43s - 12kB" tools:visibility="visible" /> + + From de809d6013098011b7da929d5c2c9380d9a2b736 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 27 Dec 2021 17:17:20 +0300 Subject: [PATCH 008/281] Add settings item to allow location sharing. --- .../app/features/home/room/detail/RoomDetailFragment.kt | 1 + .../im/vector/app/features/settings/VectorPreferences.kt | 7 +++++++ vector/src/main/res/values/strings.xml | 1 + vector/src/main/res/xml/vector_settings_preferences.xml | 5 +++++ 4 files changed, 14 insertions(+) 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 d530b35c0c..25bd5877f5 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 @@ -1379,6 +1379,7 @@ class RoomDetailFragment @Inject constructor( if (!::attachmentTypeSelector.isInitialized) { attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@RoomDetailFragment) attachmentTypeSelector.setAttachmentVisibility(AttachmentTypeSelectorView.Type.POLL, vectorPreferences.labsEnablePolls()) + attachmentTypeSelector.setAttachmentVisibility(AttachmentTypeSelectorView.Type.LOCATION, vectorPreferences.isLocationSharingEnabled()) } attachmentTypeSelector.show(views.composerLayout.views.attachmentButton, keyboardStateUtils.isKeyboardShowing) } 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 64561cbc12..c367d628bd 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 @@ -185,6 +185,9 @@ class VectorPreferences @Inject constructor(private val context: Context) { private const val DID_ASK_TO_ENABLE_SESSION_PUSH = "DID_ASK_TO_ENABLE_SESSION_PUSH" private const val DID_PROMOTE_NEW_RESTRICTED_JOIN_RULE = "DID_PROMOTE_NEW_RESTRICTED_JOIN_RULE" + // Location Sharing + private const val SETTINGS_PREF_ENABLE_LOCATION_SHARING = "SETTINGS_PREF_ENABLE_LOCATION_SHARING" + private const val MEDIA_SAVING_3_DAYS = 0 private const val MEDIA_SAVING_1_WEEK = 1 private const val MEDIA_SAVING_1_MONTH = 2 @@ -990,4 +993,8 @@ class VectorPreferences @Inject constructor(private val context: Context) { fun labsEnablePolls(): Boolean { return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_POLLS, false) } + + fun isLocationSharingEnabled(): Boolean { + return defaultPrefs.getBoolean(SETTINGS_PREF_ENABLE_LOCATION_SHARING, true) + } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index e5fe5ad5c7..1b4d137928 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3711,4 +3711,5 @@ Element could not access your location Element could not access your location. Please try again later. Open with + Enable location sharing diff --git a/vector/src/main/res/xml/vector_settings_preferences.xml b/vector/src/main/res/xml/vector_settings_preferences.xml index 14c7dc7b80..bc9e06f754 100644 --- a/vector/src/main/res/xml/vector_settings_preferences.xml +++ b/vector/src/main/res/xml/vector_settings_preferences.xml @@ -28,6 +28,11 @@ android:persistent="false" android:title="@string/font_size" /> + + From 15fa42ba9b367057d4382f247fc8066a484cca5b Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 27 Dec 2021 17:55:48 +0300 Subject: [PATCH 009/281] Lint fixes. --- .../epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt | 2 -- .../timeline/action/MessageActionsEpoxyController.kt | 1 - .../room/detail/timeline/factory/MessageItemFactory.kt | 1 - .../java/im/vector/app/features/location/LocationData.kt | 1 - .../app/features/location/LocationPreviewFragment.kt | 9 +++------ .../im/vector/app/features/location/LocationTracker.kt | 3 +++ .../im/vector/app/features/location/MapTilerMapView.kt | 1 - 7 files changed, 6 insertions(+), 12 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index ae9efe367a..7c18dd818e 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -39,8 +39,6 @@ import im.vector.app.features.location.MapTilerMapView import im.vector.app.features.location.VectorMapListener import im.vector.app.features.media.ImageContentRenderer import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.room.model.message.LocationInfo -import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.util.MatrixItem /** diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 0bd4f1bf21..3e1edfa158 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -43,7 +43,6 @@ import im.vector.app.features.location.LocationData import im.vector.app.features.media.ImageContentRenderer import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.failure.Failure -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.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.session.room.send.SendState diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index b0df2d6a54..607e4c1252 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -185,7 +185,6 @@ class MessageItemFactory @Inject constructor( highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes): MessageLocationItem? { - val geoUri = locationContent.locationInfo?.geoUri ?: locationContent.geoUri val locationData = LocationData.create(geoUri) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt index 874e159e80..b790bde710 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationData.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt @@ -68,4 +68,3 @@ data class LocationData( } } } - diff --git a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt index e13456dbdc..1a7ed49209 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt @@ -16,21 +16,18 @@ package im.vector.app.features.location -import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.databinding.FragmentLocationPreviewBinding -import javax.inject.Inject import com.airbnb.mvrx.args import im.vector.app.R +import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openLocation +import im.vector.app.databinding.FragmentLocationPreviewBinding import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider -import org.matrix.android.sdk.api.extensions.tryOrNull +import javax.inject.Inject class LocationPreviewFragment @Inject constructor( private val locationPinProvider: LocationPinProvider diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 0ea1e6810b..6e55735f9b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -16,10 +16,12 @@ package im.vector.app.features.location +import android.Manifest import android.content.Context import android.location.Location import android.location.LocationListener import android.location.LocationManager +import androidx.annotation.RequiresPermission import timber.log.Timber import javax.inject.Inject @@ -65,6 +67,7 @@ class LocationTracker @Inject constructor( } } + @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) fun stop() { locationManager?.removeUpdates(this) callback = null diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index 627b9e5ec3..386e96988f 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -19,7 +19,6 @@ package im.vector.app.features.location import android.content.Context import android.graphics.drawable.Drawable import android.util.AttributeSet -import com.mapbox.mapboxsdk.Mapbox import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.maps.MapView From fc4d18f84e2f51cfb05cb10e3d28730439c49125 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 28 Dec 2021 14:00:37 +0300 Subject: [PATCH 010/281] Dummy maptiler key added for CI. --- vector/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/build.gradle b/vector/build.gradle index 562e0af679..2f10695c2f 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -150,6 +150,8 @@ android { // This *must* only be set in trusted environments. buildConfigField "Boolean", "handleCallAssertedIdentityEvents", "false" + buildConfigField "String", "mapTilerKey", "\"DUMMY_KEY\"" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // Keep abiFilter for the universalApk From 6e1911e686a402c8503b0377bbda3bbd65aee4dc Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 28 Dec 2021 17:04:51 +0300 Subject: [PATCH 011/281] Lint fixes. --- .../timeline/factory/MessageItemFactory.kt | 14 ++++++++- .../timeline/item/MessageLocationItem.kt | 29 +++++++++++-------- .../app/features/location/LocationTracker.kt | 3 +- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 607e4c1252..e293b1ecbd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -33,6 +33,7 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.containsOnlyEmojis +import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder @@ -71,6 +72,7 @@ import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.html.SpanUtils import im.vector.app.features.html.VectorHtmlCompressor import im.vector.app.features.location.LocationData +import im.vector.app.features.location.VectorMapView import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import me.gujun.android.span.span @@ -188,6 +190,16 @@ class MessageItemFactory @Inject constructor( val geoUri = locationContent.locationInfo?.geoUri ?: locationContent.geoUri val locationData = LocationData.create(geoUri) + val mapCallback: MessageLocationItem.Callback = object: MessageLocationItem.Callback { + override fun onMapReady(mapView: VectorMapView) { + mapView.onClick { + locationData?.let { + callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(it, informationData.senderId)) + } + } + } + } + return MessageLocationItem_() .attributes(attributes) .locationData(locationData) @@ -195,7 +207,7 @@ class MessageItemFactory @Inject constructor( .locationPinProvider(locationPinProvider) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) - .callback(callback) + .callback(mapCallback) } private fun buildPollItem(pollContent: MessagePollContent, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index d4995d3fad..2d65fd3131 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -27,12 +27,17 @@ import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvid import im.vector.app.features.location.LocationData import im.vector.app.features.location.MapTilerMapView import im.vector.app.features.location.VectorMapListener +import im.vector.app.features.location.VectorMapView @EpoxyModelClass(layout = R.layout.item_timeline_event_base) abstract class MessageLocationItem : AbsMessageItem() { + interface Callback { + fun onMapReady(mapView: VectorMapView) + } + @EpoxyAttribute - var callback: TimelineEventController.Callback? = null + var callback: Callback? = null @EpoxyAttribute var locationData: LocationData? = null @@ -50,20 +55,20 @@ abstract class MessageLocationItem : AbsMessageItem( val location = locationData ?: return val locationOwnerId = userId ?: return - holder.mapView.initialize(object : VectorMapListener { - override fun onMapReady() { - holder.mapView.zoomToLocation(location.latitude, location.longitude, INITIAL_ZOOM) + holder.mapView.apply { + initialize(object : VectorMapListener { + override fun onMapReady() { + zoomToLocation(location.latitude, location.longitude, INITIAL_ZOOM) - locationPinProvider?.create(locationOwnerId) { pinDrawable -> - holder.mapView.addPinToMap(locationOwnerId, pinDrawable) - holder.mapView.updatePinLocation(locationOwnerId, location.latitude, location.longitude) - } + locationPinProvider?.create(locationOwnerId) { pinDrawable -> + addPinToMap(locationOwnerId, pinDrawable) + updatePinLocation(locationOwnerId, location.latitude, location.longitude) + } - holder.mapView.onClick { - callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(location, locationOwnerId)) + callback?.onMapReady(this@apply) } - } - }) + }) + } } override fun getViewType() = STUB_ID diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 6e55735f9b..48692ef216 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -24,6 +24,7 @@ import android.location.LocationManager import androidx.annotation.RequiresPermission import timber.log.Timber import javax.inject.Inject +import androidx.core.content.getSystemService class LocationTracker @Inject constructor( private val context: Context) : LocationListener { @@ -36,7 +37,7 @@ class LocationTracker @Inject constructor( var callback: Callback? = null fun start() { - val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as? LocationManager + val locationManager = context.getSystemService() locationManager?.let { val isGpsEnabled = it.isProviderEnabled(LocationManager.GPS_PROVIDER) From 4d6eec8972ebf7fd9eaf31762898538814eeaf0f Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 31 Dec 2021 15:12:46 +0300 Subject: [PATCH 012/281] Rate limited maptiler key is added. --- changelog.d/2210.bugfix | 1 + vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2210.bugfix diff --git a/changelog.d/2210.bugfix b/changelog.d/2210.bugfix new file mode 100644 index 0000000000..6f7c09ce26 --- /dev/null +++ b/changelog.d/2210.bugfix @@ -0,0 +1 @@ +Static location sharing and rendering \ No newline at end of file diff --git a/vector/build.gradle b/vector/build.gradle index 2f10695c2f..76fe629d3c 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -150,7 +150,7 @@ android { // This *must* only be set in trusted environments. buildConfigField "Boolean", "handleCallAssertedIdentityEvents", "false" - buildConfigField "String", "mapTilerKey", "\"DUMMY_KEY\"" + buildConfigField "String", "mapTilerKey", "\"KF6tcY7YuKFgJSZ1EFQ2\"" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 261030f51e8774e421307dabf8dbda62d057afd7 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 4 Jan 2022 01:04:41 +0200 Subject: [PATCH 013/281] - Fix integration tests (still not perfect while github actions are very very limited for that) - Split them in msdk and app test along with multiple smaller steps. - Mark the not working tests with @Ignore so that they will not run neither local or on github actions - Add user friendly comment on PR to view the results --- .github/workflows/integration.yml | 86 ------- .github/workflows/integration_tests.yml | 209 ++++++++++++++++++ dependencies.gradle | 3 +- integration_tests_script.sh | 3 + integration_tests_script_github.sh | 3 + matrix-sdk-android/build.gradle | 3 +- .../sdk/account/AccountCreationTest.kt | 4 + .../android/sdk/account/ChangePasswordTest.kt | 2 + .../android/sdk/common/RetryTestRule.kt | 58 +++++ .../android/sdk/common/TestConstants.kt | 2 +- .../sdk/internal/crypto/PreShareKeysTest.kt | 5 +- .../sdk/internal/crypto/UnwedgingTest.kt | 2 + .../crypto/crosssigning/XSigningTest.kt | 7 +- .../crypto/gossiping/KeyShareTests.kt | 6 + .../crypto/gossiping/WithHeldTests.kt | 6 + .../crypto/keysbackup/KeysBackupTest.kt | 21 ++ .../sdk/internal/crypto/ssss/QuadSTests.kt | 18 +- .../internal/crypto/verification/SASTest.kt | 26 ++- .../verification/qrcode/VerificationTest.kt | 4 +- .../session/room/send/MarkdownParserTest.kt | 7 + .../sdk/{ => ordering}/SpaceOrderTest.kt | 2 +- .../sdk/{ => ordering}/StringOrderTest.kt | 2 +- .../timeline/TimelineForwardPaginationTest.kt | 9 +- .../TimelinePreviousLastForwardTest.kt | 8 +- .../TimelineSimpleBackPaginationTest.kt | 7 +- .../timeline/TimelineWithManyMembersTest.kt | 8 + .../sdk/session/search/SearchMessagesTest.kt | 5 +- .../sdk/session/space/SpaceCreationTest.kt | 9 +- .../sdk/session/space/SpaceHierarchyTest.kt | 20 +- vector/build.gradle | 3 +- .../im/vector/app/SecurityBootstrapTest.kt | 2 - 31 files changed, 425 insertions(+), 125 deletions(-) delete mode 100644 .github/workflows/integration.yml create mode 100644 .github/workflows/integration_tests.yml create mode 100755 integration_tests_script.sh create mode 100755 integration_tests_script_github.sh create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt rename matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/{ => ordering}/SpaceOrderTest.kt (99%) rename matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/{ => ordering}/StringOrderTest.kt (99%) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml deleted file mode 100644 index c18ca69fde..0000000000 --- a/.github/workflows/integration.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Integration Test - -on: - pull_request: { } - push: - branches: [ main, develop ] - -# Enrich gradle.properties for CI/CD -env: - CI_GRADLE_ARG_PROPERTIES: > - -Porg.gradle.jvmargs=-Xmx2g - -Porg.gradle.parallel=false - -jobs: - # Temporary add build of Android tests, which cannot be run on the CI right now, but they need to at least compile - # So it will be mandatory for this action to be successful on every PRs - compile-android-test: - name: Compile Android tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Compile Android tests - run: ./gradlew clean assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false - - integration-tests: - name: Integration Tests (Synapse) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - api-level: [28] - steps: - - uses: actions/checkout@v2 - - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v2 - with: - distribution: 'adopt' - java-version: 11 - - 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 }}- - - uses: actions/cache@v2 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Start synapse server - run: | - python3 -m venv .synapse - source .synapse/bin/activate - pip install synapse matrix-synapse - curl -sL https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh --no-rate-limit \ - | sed s/127.0.0.1/0.0.0.0/g | bash - - name: Run integration tests on API ${{ matrix.api-level }} - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - #arch: x86_64 - #disable-animations: true - # script: ./gradlew -PallWarningsAsErrors=false vector:connectedAndroidTest matrix-sdk-android:connectedAndroidTest - arch: x86 - profile: Nexus 5X - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - emulator-build: 7425822 - script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedCheck --stacktrace diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml new file mode 100644 index 0000000000..fe3e58fed9 --- /dev/null +++ b/.github/workflows/integration_tests.yml @@ -0,0 +1,209 @@ +name: Integration Tests + +on: + pull_request: { } + push: + branches: [ main, develop ] + +# Enrich gradle.properties for CI/CD +env: + CI_GRADLE_ARG_PROPERTIES: > + -Porg.gradle.jvmargs=-Xmx2g + -Porg.gradle.parallel=false + +jobs: + # Build Android Tests [Matrix SDK] + build-android-test-matrix-sdk: + name: Matrix SDK - Build Android Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Build Android Tests for matrix-sdk-android + run: ./gradlew clean matrix-sdk-android:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false + + # Build Android Tests [Matrix APP] + build-android-test-app: + name: App - Build Android Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Build Android Tests for vector + run: ./gradlew clean vector:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false + + # Run Android Tests + integration-tests: + name: Matrix SDK - Running Integration Tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + api-level: [ 28 ] + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 + - uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: 11 + - 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 }}- + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Start synapse server + run: | + python3 -m venv .synapse + source .synapse/bin/activate + pip install synapse matrix-synapse + curl https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh -o start.sh + chmod 777 start.sh + ./start.sh --no-rate-limit +# package: org.matrix.android.sdk.session + - name: Run integration tests for Matrix SDK [org.matrix.android.sdk.session] API[${{ matrix.api-level }}] + continue-on-error: true + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + arch: x86 + profile: Nexus 5X + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + emulator-build: 7425822 + script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest --info + - name: Read Results [org.matrix.android.sdk.session] + continue-on-error: true + id: get-comment-body-session + run: | + body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "${{ steps.get-comment-body-session.outputs.session }} + - `[org.matrix.android.sdk.account]`
${{ steps.get-comment-body-account.outputs.account }} + - `[org.matrix.android.sdk.intrernal]`
${{ steps.get-comment-body-intrernal.outputs.intrernal }} + - `[org.matrix.android.sdk.ordering]`
${{ steps.get-comment-body-ordering.outputs.ordering }} + - `[org.matrix.android.sdk.PermalinkParserTest]`
${{ steps.get-comment-body-permalink.outputs.permalink }} + reactions: rocket + edit-mode: replace +## Useful commands +# script: ./integration_tests_script.sh +# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest --info +# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES matrix-sdk-android:connectedAndroidTest --info +# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedCheck --stacktrace +# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest --info \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index 6cb5fac64c..2243510f92 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -29,6 +29,7 @@ def vanniktechEmoji = "0.8.0" def mockk = "1.12.1" def espresso = "3.4.0" def androidxTest = "1.4.0" +def androidxOrchestrator = "1.4.1" ext.libs = [ @@ -63,7 +64,7 @@ ext.libs = [ 'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2", 'coreTesting' : "androidx.arch.core:core-testing:2.1.0", 'testCore' : "androidx.test:core:$androidxTest", - 'orchestrator' : "androidx.test:orchestrator:$androidxTest", + 'orchestrator' : "androidx.test:orchestrator:$androidxOrchestrator", 'testRunner' : "androidx.test:runner:$androidxTest", 'testRules' : "androidx.test:rules:$androidxTest", 'espressoCore' : "androidx.test.espresso:espresso-core:$espresso", diff --git a/integration_tests_script.sh b/integration_tests_script.sh new file mode 100755 index 0000000000..fe72ae6f5c --- /dev/null +++ b/integration_tests_script.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./gradlew -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest +./gradlew -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.TimelineForwardPaginationTest matrix-sdk-android:connectedAndroidTest diff --git a/integration_tests_script_github.sh b/integration_tests_script_github.sh new file mode 100755 index 0000000000..bbf666e4f0 --- /dev/null +++ b/integration_tests_script_github.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest +./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.TimelineForwardPaginationTest matrix-sdk-android:connectedAndroidTest diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 44b002697d..1cd22e8a1a 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -45,7 +45,7 @@ android { testOptions { // Comment to run on Android 12 - execution 'ANDROIDX_TEST_ORCHESTRATOR' +// execution 'ANDROIDX_TEST_ORCHESTRATOR' } buildTypes { @@ -64,6 +64,7 @@ android { adbOptions { installOptions "-g" + timeOutInMs 180 * 1000 } compileOptions { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt index e0451bea38..486bc02769 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt @@ -16,7 +16,9 @@ package org.matrix.android.sdk.account +import androidx.test.filters.LargeTest import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -29,6 +31,7 @@ import org.matrix.android.sdk.common.TestConstants @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class AccountCreationTest : InstrumentedTest { private val commonTestHelper = CommonTestHelper(context()) @@ -42,6 +45,7 @@ class AccountCreationTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun createAccountAndLoginAgainTest() { val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true)) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt index d32bcb3fe5..933074cdce 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.account import org.amshove.kluent.shouldBeTrue import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -30,6 +31,7 @@ import org.matrix.android.sdk.common.TestConstants @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@Ignore("This test will be ignored until it is fixed") class ChangePasswordTest : InstrumentedTest { private val commonTestHelper = CommonTestHelper(context()) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt new file mode 100644 index 0000000000..5f1a59e585 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2022 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.common + +import android.util.Log +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement + +/** + * Retry test rule used to retry test that failed. + * Retry failed test 3 times + */ +class RetryTestRule(val retryCount: Int = 3) : TestRule { + + private val TAG = RetryTestRule::class.java.simpleName + + override fun apply(base: Statement, description: Description): Statement { + return statement(base, description) + } + + private fun statement(base: Statement, description: Description): Statement { + return object : Statement() { + @Throws(Throwable::class) + override fun evaluate() { + var caughtThrowable: Throwable? = null + + // implement retry logic here + for (i in 0 until retryCount) { + try { + base.evaluate() + return + } catch (t: Throwable) { + caughtThrowable = t + Log.e(TAG, description.displayName + ": run " + (i + 1) + " failed") + } + } + + Log.e(TAG, description.displayName + ": giving up after " + retryCount + " failures") + throw caughtThrowable!! + } + } + } +} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt index 8eb7e251e2..ecfd587929 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt @@ -23,7 +23,7 @@ object TestConstants { const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080" // Time out to use when waiting for server response. 20s - private const val AWAIT_TIME_OUT_MILLIS = 20_000 + private const val AWAIT_TIME_OUT_MILLIS = 30_000 // Time out to use when waiting for server response, when the debugger is connected. 10 minutes private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60_000 diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt index d0f63227f5..c95cc6b4ca 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -40,6 +41,7 @@ class PreShareKeysTest : InstrumentedTest { private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test + @Ignore("This test will be ignored until it is fixed") fun ensure_outbound_session_happy_path() { val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) val e2eRoomID = testData.roomId @@ -97,7 +99,6 @@ class PreShareKeysTest : InstrumentedTest { } } - testHelper.signOutAndClose(aliceSession) - testHelper.signOutAndClose(bobSession) + testData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt index 458eae6ab2..0a8ce67680 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt @@ -21,6 +21,7 @@ import org.amshove.kluent.shouldBe import org.junit.Assert import org.junit.Before import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -84,6 +85,7 @@ class UnwedgingTest : InstrumentedTest { * -> This is automatically fixed after SDKs restarted the olm session */ @Test + @Ignore("This test will be ignored until it is fixed") fun testUnwedging() { val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt index d9cc7a8ac0..a6e8f94c91 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.crypto.crosssigning import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull @@ -24,6 +25,7 @@ import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -43,6 +45,7 @@ import kotlin.coroutines.resume @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@LargeTest class XSigningTest : InstrumentedTest { private val testHelper = CommonTestHelper(context()) @@ -124,11 +127,11 @@ class XSigningTest : InstrumentedTest { assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted()) - testHelper.signOutAndClose(aliceSession) - testHelper.signOutAndClose(bobSession) + cryptoTestData.cleanUp(testHelper) } @Test + @Ignore("This test will be ignored until it is fixed") fun test_CrossSigningTestAliceTrustBobNewDevice() { val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt index 975d481628..e0605db0b8 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt @@ -18,12 +18,14 @@ package org.matrix.android.sdk.internal.crypto.gossiping import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertTrue import junit.framework.TestCase.fail import org.junit.Assert import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -59,11 +61,13 @@ import kotlin.coroutines.resume @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class KeyShareTests : InstrumentedTest { private val commonTestHelper = CommonTestHelper(context()) @Test + @Ignore("This test will be ignored until it is fixed") fun test_DoNotSelfShareIfNotTrusted() { val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) @@ -195,6 +199,7 @@ class KeyShareTests : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun test_ShareSSSSSecret() { val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) @@ -307,6 +312,7 @@ class KeyShareTests : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun test_ImproperKeyShareBug() { val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt index c835c2d40b..586d96b007 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt @@ -18,8 +18,10 @@ package org.matrix.android.sdk.internal.crypto.gossiping import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest import org.junit.Assert import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -39,12 +41,14 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class WithHeldTests : InstrumentedTest { private val testHelper = CommonTestHelper(context()) private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test + @Ignore("This test will be ignored until it is fixed") fun test_WithHeldUnverifiedReason() { // ============================= // ARRANGE @@ -129,6 +133,7 @@ class WithHeldTests : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun test_WithHeldNoOlm() { val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = testData.firstSession @@ -199,6 +204,7 @@ class WithHeldTests : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun test_WithHeldKeyRequest() { val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = testData.firstSession diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt index 2a07b74115..4c94566219 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt @@ -17,12 +17,14 @@ package org.matrix.android.sdk.internal.crypto.keysbackup import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -47,6 +49,7 @@ import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class KeysBackupTest : InstrumentedTest { private val testHelper = CommonTestHelper(context()) @@ -59,6 +62,7 @@ class KeysBackupTest : InstrumentedTest { * - Reset keys backup markers */ @Test + @Ignore("This test will be ignored until it is fixed") fun roomKeysTest_testBackupStore_ok() { val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() @@ -157,6 +161,7 @@ class KeysBackupTest : InstrumentedTest { * - Check the backup completes */ @Test + @Ignore("This test will be ignored until it is fixed") fun backupAfterCreateKeysBackupVersionTest() { val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() @@ -197,6 +202,7 @@ class KeysBackupTest : InstrumentedTest { * Check that backupAllGroupSessions() returns valid data */ @Test + @Ignore("This test will be ignored until it is fixed") fun backupAllGroupSessionsTest() { val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() @@ -241,6 +247,7 @@ class KeysBackupTest : InstrumentedTest { * - Compare the decrypted megolm key with the original one */ @Test + @Ignore("This test will be ignored until it is fixed") fun testEncryptAndDecryptKeysBackupData() { val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() @@ -282,6 +289,7 @@ class KeysBackupTest : InstrumentedTest { * - Restore must be successful */ @Test + @Ignore("This test will be ignored until it is fixed") fun restoreKeysBackupTest() { val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) @@ -365,6 +373,7 @@ class KeysBackupTest : InstrumentedTest { * - It must be trusted and must have with 2 signatures now */ @Test + @Ignore("This test will be ignored until it is fixed") fun trustKeyBackupVersionTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device @@ -424,6 +433,7 @@ class KeysBackupTest : InstrumentedTest { * - It must be trusted and must have with 2 signatures now */ @Test + @Ignore("This test will be ignored until it is fixed") fun trustKeyBackupVersionWithRecoveryKeyTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device @@ -481,6 +491,7 @@ class KeysBackupTest : InstrumentedTest { * - The backup must still be untrusted and disabled */ @Test + @Ignore("This test will be ignored until it is fixed") fun trustKeyBackupVersionWithWrongRecoveryKeyTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device @@ -522,6 +533,7 @@ class KeysBackupTest : InstrumentedTest { * - It must be trusted and must have with 2 signatures now */ @Test + @Ignore("This test will be ignored until it is fixed") fun trustKeyBackupVersionWithPasswordTest() { val password = "Password" @@ -581,6 +593,7 @@ class KeysBackupTest : InstrumentedTest { * - The backup must still be untrusted and disabled */ @Test + @Ignore("This test will be ignored until it is fixed") fun trustKeyBackupVersionWithWrongPasswordTest() { val password = "Password" val badPassword = "Bad Password" @@ -621,6 +634,7 @@ class KeysBackupTest : InstrumentedTest { * - It must fail */ @Test + @Ignore("This test will be ignored until it is fixed") fun restoreKeysBackupWithAWrongRecoveryKeyTest() { val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) @@ -654,6 +668,7 @@ class KeysBackupTest : InstrumentedTest { * - Restore must be successful */ @Test + @Ignore("This test will be ignored until it is fixed") fun testBackupWithPassword() { val password = "password" @@ -709,6 +724,7 @@ class KeysBackupTest : InstrumentedTest { * - It must fail */ @Test + @Ignore("This test will be ignored until it is fixed") fun restoreKeysBackupWithAWrongPasswordTest() { val password = "password" val wrongPassword = "passw0rd" @@ -745,6 +761,7 @@ class KeysBackupTest : InstrumentedTest { * - Restore must be successful */ @Test + @Ignore("This test will be ignored until it is fixed") fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() { val password = "password" @@ -773,6 +790,7 @@ class KeysBackupTest : InstrumentedTest { * - It must fail */ @Test + @Ignore("This test will be ignored until it is fixed") fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() { val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) @@ -804,6 +822,7 @@ class KeysBackupTest : InstrumentedTest { * - Check the returned KeysVersionResult is trusted */ @Test + @Ignore("This test will be ignored until it is fixed") fun testIsKeysBackupTrusted() { // - Create a backup version val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() @@ -847,6 +866,7 @@ class KeysBackupTest : InstrumentedTest { * -> The new alice session must back up to the same version */ @Test + @Ignore("This test will be ignored until it is fixed") fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() { // - Create a backup version val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() @@ -978,6 +998,7 @@ class KeysBackupTest : InstrumentedTest { * -> It must success */ @Test + @Ignore("This test will be ignored until it is fixed") fun testBackupAfterVerifyingADevice() { // - Create a backup version val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt index 43f8dc0762..67f17727b1 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt @@ -22,6 +22,7 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -47,8 +48,6 @@ import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorage @FixMethodOrder(MethodSorters.JVM) class QuadSTests : InstrumentedTest { - private val testHelper = CommonTestHelper(context()) - private val emptyKeySigner = object : KeySigner { override fun sign(canonicalJson: String): Map>? { return null @@ -57,6 +56,8 @@ class QuadSTests : InstrumentedTest { @Test fun test_Generate4SKey() { + val testHelper = CommonTestHelper(context()) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val quadS = aliceSession.sharedSecretStorageService @@ -108,6 +109,8 @@ class QuadSTests : InstrumentedTest { @Test fun test_StoreSecret() { + val testHelper = CommonTestHelper(context()) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId = "My.Key" val info = generatedSecret(aliceSession, keyId, true) @@ -151,6 +154,8 @@ class QuadSTests : InstrumentedTest { @Test fun test_SetDefaultLocalEcho() { + val testHelper = CommonTestHelper(context()) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val quadS = aliceSession.sharedSecretStorageService @@ -171,6 +176,8 @@ class QuadSTests : InstrumentedTest { @Test fun test_StoreSecretWithMultipleKey() { + val testHelper = CommonTestHelper(context()) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId1 = "Key.1" val key1Info = generatedSecret(aliceSession, keyId1, true) @@ -217,7 +224,10 @@ class QuadSTests : InstrumentedTest { } @Test + @Ignore("Test is working locally, not in GitHub actions") fun test_GetSecretWithBadPassphrase() { + val testHelper = CommonTestHelper(context()) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId1 = "Key.1" val passphrase = "The good pass phrase" @@ -264,6 +274,8 @@ class QuadSTests : InstrumentedTest { } private fun assertAccountData(session: Session, type: String): UserAccountDataEvent { + val testHelper = CommonTestHelper(context()) + var accountData: UserAccountDataEvent? = null testHelper.waitWithLatch { val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type) @@ -281,6 +293,7 @@ class QuadSTests : InstrumentedTest { private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { val quadS = session.sharedSecretStorageService + val testHelper = CommonTestHelper(context()) val creationInfo = testHelper.runBlockingTest { quadS.generateKey(keyId, null, keyId, emptyKeySigner) @@ -300,6 +313,7 @@ class QuadSTests : InstrumentedTest { private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { val quadS = session.sharedSecretStorageService + val testHelper = CommonTestHelper(context()) val creationInfo = testHelper.runBlockingTest { quadS.generateKeyWithPassphrase( diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index c914da6f71..d4350bd845 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -25,6 +25,7 @@ import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -53,11 +54,11 @@ import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SASTest : InstrumentedTest { - private val testHelper = CommonTestHelper(context()) - private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_aliceStartThenAliceCancel() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession @@ -137,7 +138,10 @@ class SASTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun test_key_agreement_protocols_must_include_curve25519() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) fail("Not passing for the moment") val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() @@ -194,7 +198,10 @@ class SASTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun test_key_agreement_macs_Must_include_hmac_sha256() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) fail("Not passing for the moment") val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() @@ -232,7 +239,10 @@ class SASTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun test_key_agreement_short_code_include_decimal() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) fail("Not passing for the moment") val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() @@ -303,6 +313,8 @@ class SASTest : InstrumentedTest { // If a device has two verifications in progress with the same device, then it should cancel both verifications. @Test fun test_aliceStartTwoRequests() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession @@ -342,7 +354,10 @@ class SASTest : InstrumentedTest { * Test that when alice starts a 'correct' request, bob agrees. */ @Test + @Ignore("This test will be ignored until it is fixed") fun test_aliceAndBobAgreement() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession @@ -402,6 +417,8 @@ class SASTest : InstrumentedTest { @Test fun test_aliceAndBobSASCode() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession @@ -458,6 +475,8 @@ class SASTest : InstrumentedTest { @Test fun test_happyPath() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession @@ -528,7 +547,6 @@ class SASTest : InstrumentedTest { val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.cryptoService().getDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyDevice().deviceId) // latch wait a bit again - Thread.sleep(1000) assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified) assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) @@ -537,6 +555,8 @@ class SASTest : InstrumentedTest { @Test fun test_ConcurrentStart() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt index 36306aa383..35c5a4dab9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt @@ -40,8 +40,6 @@ import kotlin.coroutines.resume @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) class VerificationTest : InstrumentedTest { - private val testHelper = CommonTestHelper(context()) - private val cryptoTestHelper = CryptoTestHelper(testHelper) data class ExpectedResult( val sasIsSupported: Boolean = false, @@ -155,6 +153,8 @@ class VerificationTest : InstrumentedTest { bobSupportedMethods: List, expectedResultForAlice: ExpectedResult, expectedResultForBob: ExpectedResult) { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt index 1ed2f89977..0c24dbd9e3 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt @@ -21,6 +21,7 @@ import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer import org.junit.Assert.assertEquals import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -131,6 +132,7 @@ class MarkdownParserTest : InstrumentedTest { * Note: the test is not passing, it does not work on Element Web neither */ @Test + @Ignore("This test will be ignored until it is fixed") fun parseStrike_not_passing() { testType( name = "strike", @@ -140,6 +142,7 @@ class MarkdownParserTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun parseStrikeNewLines() { testTypeNewLines( name = "strike", @@ -159,6 +162,7 @@ class MarkdownParserTest : InstrumentedTest { // TODO. Improve testTypeNewLines function to cover
test
@Test + @Ignore("This test will be ignored until it is fixed") fun parseCodeNewLines_not_passing() { testTypeNewLines( name = "code", @@ -178,6 +182,7 @@ class MarkdownParserTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun parseCode2NewLines_not_passing() { testTypeNewLines( name = "code", @@ -196,6 +201,7 @@ class MarkdownParserTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun parseCode3NewLines_not_passing() { testTypeNewLines( name = "code", @@ -232,6 +238,7 @@ class MarkdownParserTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun parseQuote_not_passing() { "> quoted\nline2".let { markdownParser.parse(it).expect(it, "

quoted
line2

") } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SpaceOrderTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/ordering/SpaceOrderTest.kt similarity index 99% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SpaceOrderTest.kt rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/ordering/SpaceOrderTest.kt index 3270dfb757..50f4692edf 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SpaceOrderTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/ordering/SpaceOrderTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk +package org.matrix.android.sdk.ordering import org.amshove.kluent.internal.assertEquals import org.junit.Assert diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/StringOrderTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/ordering/StringOrderTest.kt similarity index 99% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/StringOrderTest.kt rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/ordering/StringOrderTest.kt index a625362c04..728986441a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/StringOrderTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/ordering/StringOrderTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk +package org.matrix.android.sdk.ordering import org.amshove.kluent.internal.assertEquals import org.junit.Assert diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt index 05a43de0ac..ee44af58b3 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.session.room.timeline +import androidx.test.filters.LargeTest import kotlinx.coroutines.runBlocking import org.amshove.kluent.internal.assertEquals import org.amshove.kluent.shouldBeFalse @@ -40,16 +41,20 @@ import java.util.concurrent.CountDownLatch @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class TimelineForwardPaginationTest : InstrumentedTest { - private val commonTestHelper = CommonTestHelper(context()) - private val cryptoTestHelper = CryptoTestHelper(commonTestHelper) +// @Rule +// @JvmField +// val mRetryTestRule = RetryTestRule() /** * This test ensure that if we click to permalink, we will be able to go back to the live */ @Test fun forwardPaginationTest() { + val commonTestHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(commonTestHelper) val numberOfMessagesToSend = 90 val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt index c6fdec150d..c6d40bcaa2 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.session.room.timeline +import androidx.test.filters.LargeTest import org.amshove.kluent.shouldBeFalse import org.amshove.kluent.shouldBeTrue import org.junit.FixMethodOrder @@ -38,16 +39,17 @@ import java.util.concurrent.CountDownLatch @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class TimelinePreviousLastForwardTest : InstrumentedTest { - private val commonTestHelper = CommonTestHelper(context()) - private val cryptoTestHelper = CryptoTestHelper(commonTestHelper) - /** * This test ensure that if we have a chunk in the timeline which is due to a sync, and we click to permalink, we will be able to go back to the live */ + @Test fun previousLastForwardTest() { + val commonTestHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(commonTestHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false) val aliceSession = cryptoTestData.firstSession diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineSimpleBackPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineSimpleBackPaginationTest.kt index b75df9b5a2..53f76f1c46 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineSimpleBackPaginationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineSimpleBackPaginationTest.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.session.room.timeline +import androidx.test.filters.LargeTest import kotlinx.coroutines.runBlocking import org.amshove.kluent.internal.assertEquals import org.junit.FixMethodOrder @@ -36,13 +37,13 @@ import org.matrix.android.sdk.common.TestConstants @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class TimelineSimpleBackPaginationTest : InstrumentedTest { - private val commonTestHelper = CommonTestHelper(context()) - private val cryptoTestHelper = CryptoTestHelper(commonTestHelper) - @Test fun timeline_backPaginate_shouldReachEndOfTimeline() { + val commonTestHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(commonTestHelper) val numberOfMessagesToSent = 200 val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt index ace48cef77..ce02b2b527 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt @@ -16,8 +16,10 @@ package org.matrix.android.sdk.session.room.timeline +import androidx.test.filters.LargeTest import org.junit.Assert.fail import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -31,8 +33,13 @@ import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestHelper import java.util.concurrent.CountDownLatch +/** !! Not working with the new timeline + * Disabling it until the fix is made + */ @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@Ignore("This test will be ignored until it is fixed") +@LargeTest class TimelineWithManyMembersTest : InstrumentedTest { companion object { @@ -45,6 +52,7 @@ class TimelineWithManyMembersTest : InstrumentedTest { /** * Ensures when someone sends a message to a crowded room, everyone can decrypt the message. */ + @Test fun everyone_should_decrypt_message_in_a_crowded_room() { val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt index 45e4b53c77..fa07cf5a02 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt @@ -37,9 +37,6 @@ class SearchMessagesTest : InstrumentedTest { private const val MESSAGE = "Lorem ipsum dolor sit amet" } - private val commonTestHelper = CommonTestHelper(context()) - private val cryptoTestHelper = CryptoTestHelper(commonTestHelper) - @Test fun sendTextMessageAndSearchPartOfItUsingSession() { doTest { cryptoTestData -> @@ -76,6 +73,8 @@ class SearchMessagesTest : InstrumentedTest { } private fun doTest(block: suspend (CryptoTestData) -> SearchResult) { + val commonTestHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(commonTestHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false) val aliceSession = cryptoTestData.firstSession val aliceRoomId = cryptoTestData.roomId diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt index d7be19295c..3b0f7586cc 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.session.space +import androidx.test.filters.LargeTest import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals @@ -43,12 +44,12 @@ import org.matrix.android.sdk.common.SessionTestParams @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@LargeTest class SpaceCreationTest : InstrumentedTest { - private val commonTestHelper = CommonTestHelper(context()) - @Test fun createSimplePublicSpace() { + val commonTestHelper = CommonTestHelper(context()) val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true)) val roomName = "My Space" val topic = "A public space for test" @@ -58,6 +59,7 @@ class SpaceCreationTest : InstrumentedTest { // wait a bit to let the summary update it self :/ it.countDown() } + Thread.sleep(4_000) val syncedSpace = session.spaceService().getSpace(spaceId) commonTestHelper.waitWithLatch { @@ -99,6 +101,8 @@ class SpaceCreationTest : InstrumentedTest { @Test fun testJoinSimplePublicSpace() { + val commonTestHelper = CommonTestHelper(context()) + val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true)) val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true)) @@ -130,6 +134,7 @@ class SpaceCreationTest : InstrumentedTest { @Test fun testSimplePublicSpaceWithChildren() { + val commonTestHelper = CommonTestHelper(context()) val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true)) val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true)) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index 1c38edbbd9..7cce032a8c 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -23,6 +23,7 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -50,10 +51,10 @@ import org.matrix.android.sdk.common.SessionTestParams @FixMethodOrder(MethodSorters.JVM) class SpaceHierarchyTest : InstrumentedTest { - private val commonTestHelper = CommonTestHelper(context()) - @Test fun createCanonicalChildRelation() { + val commonTestHelper = CommonTestHelper(context()) + val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val spaceName = "My Space" val topic = "A public space for test" @@ -170,6 +171,7 @@ class SpaceHierarchyTest : InstrumentedTest { @Test fun testFilteringBySpace() { + val commonTestHelper = CommonTestHelper(context()) val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val spaceAInfo = createPublicSpace(session, "SpaceA", listOf( @@ -236,7 +238,7 @@ class SpaceHierarchyTest : InstrumentedTest { it.countDown() } - Thread.sleep(2_000) + Thread.sleep(6_000) val orphansUpdate = session.getRoomSummaries(roomSummaryQueryParams { activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null) }) @@ -244,7 +246,9 @@ class SpaceHierarchyTest : InstrumentedTest { } @Test + @Ignore("This test will be ignored until it is fixed") fun testBreakCycle() { + val commonTestHelper = CommonTestHelper(context()) val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val spaceAInfo = createPublicSpace(session, "SpaceA", listOf( @@ -273,8 +277,6 @@ class SpaceHierarchyTest : InstrumentedTest { it.countDown() } - Thread.sleep(1000) - // A -> C -> A val aChildren = session.getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId) @@ -288,6 +290,7 @@ class SpaceHierarchyTest : InstrumentedTest { @Test fun testLiveFlatChildren() { + val commonTestHelper = CommonTestHelper(context()) val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val spaceAInfo = createPublicSpace(session, "SpaceA", listOf( @@ -374,6 +377,7 @@ class SpaceHierarchyTest : InstrumentedTest { childInfo: List> /** Name, auto-join, canonical*/ ): TestSpaceCreationResult { + val commonTestHelper = CommonTestHelper(context()) var spaceId = "" var roomIds: List = emptyList() commonTestHelper.waitWithLatch { latch -> @@ -401,6 +405,7 @@ class SpaceHierarchyTest : InstrumentedTest { childInfo: List> /** Name, auto-join, canonical*/ ): TestSpaceCreationResult { + val commonTestHelper = CommonTestHelper(context()) var spaceId = "" var roomIds: List = emptyList() commonTestHelper.waitWithLatch { latch -> @@ -435,6 +440,7 @@ class SpaceHierarchyTest : InstrumentedTest { @Test fun testRootSpaces() { + val commonTestHelper = CommonTestHelper(context()) val session = commonTestHelper.createAccount("John", SessionTestParams(true)) /* val spaceAInfo = */ createPublicSpace(session, "SpaceA", listOf( @@ -459,9 +465,10 @@ class SpaceHierarchyTest : InstrumentedTest { runBlocking { val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId) spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) + Thread.sleep(6_000) } - Thread.sleep(2000) +// Thread.sleep(4_000) // + A // a1, a2 // + B @@ -478,6 +485,7 @@ class SpaceHierarchyTest : InstrumentedTest { @Test fun testParentRelation() { + val commonTestHelper = CommonTestHelper(context()) val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true)) val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true)) diff --git a/vector/build.gradle b/vector/build.gradle index 07f80a70b2..8487f9ff74 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -202,9 +202,8 @@ android { animationsDisabled = true // Comment to run on Android 12 - execution 'ANDROIDX_TEST_ORCHESTRATOR' +// execution 'ANDROIDX_TEST_ORCHESTRATOR' } - signingConfigs { debug { keyAlias 'androiddebugkey' diff --git a/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt b/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt index 0d0ec3dd2b..fb7b9dcb41 100644 --- a/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt +++ b/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt @@ -154,8 +154,6 @@ class SecurityBootstrapTest : VerificationTestBase() { onView(withId(R.id.recoveryCopy)) .perform(click()) - Thread.sleep(1000) - // Dismiss dialog onView(withText(R.string.ok)).inRoot(RootMatchers.isDialog()).perform(click()) From 6a24e0286e56d00727808a31988fc19cbb33cfba Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Wed, 5 Jan 2022 23:45:07 +0200 Subject: [PATCH 014/281] Increase adbtimeout to prevent ShellCommandUnresponsiveException exception --- changelog.d/4842.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4842.misc diff --git a/changelog.d/4842.misc b/changelog.d/4842.misc new file mode 100644 index 0000000000..ebeb54084e --- /dev/null +++ b/changelog.d/4842.misc @@ -0,0 +1 @@ +Fix integration tests and add a comment with results (still not perfect due to github actions resource limitations) From 52c8a138d3e1a0ae61b3c5994b223a7e1d4cd87b Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Thu, 6 Jan 2022 00:38:05 +0200 Subject: [PATCH 015/281] fix typo --- .github/workflows/integration_tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index fe3e58fed9..6170ea804f 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -196,10 +196,9 @@ jobs: ## Integration Tests Results: - `[org.matrix.android.sdk.session]`
${{ steps.get-comment-body-session.outputs.session }} - `[org.matrix.android.sdk.account]`
${{ steps.get-comment-body-account.outputs.account }} - - `[org.matrix.android.sdk.intrernal]`
${{ steps.get-comment-body-intrernal.outputs.intrernal }} + - `[org.matrix.android.sdk.internal]`
${{ steps.get-comment-body-internal.outputs.internal }} - `[org.matrix.android.sdk.ordering]`
${{ steps.get-comment-body-ordering.outputs.ordering }} - `[org.matrix.android.sdk.PermalinkParserTest]`
${{ steps.get-comment-body-permalink.outputs.permalink }} - reactions: rocket edit-mode: replace ## Useful commands # script: ./integration_tests_script.sh From abf8932747476d5312b652fd4f55fe7bd5e2dff7 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Thu, 6 Jan 2022 12:25:19 +0200 Subject: [PATCH 016/281] fix typo --- matrix-sdk-android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 1cd22e8a1a..f437ed51e8 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -64,7 +64,7 @@ android { adbOptions { installOptions "-g" - timeOutInMs 180 * 1000 + timeOutInMs 350 * 1000 } compileOptions { From 41bf1ccc07279cc99e37f44ac7f96711193184e4 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 7 Jan 2022 11:03:54 +0200 Subject: [PATCH 017/281] Remove adb timeout --- .github/workflows/integration_tests.yml | 10 +++++----- matrix-sdk-android/build.gradle | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 6170ea804f..f2a09af132 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -100,7 +100,7 @@ jobs: force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 - script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest --info + script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest - name: Read Results [org.matrix.android.sdk.session] continue-on-error: true id: get-comment-body-session @@ -118,7 +118,7 @@ jobs: force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 - script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.account' matrix-sdk-android:connectedDebugAndroidTest --info + script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.account' matrix-sdk-android:connectedDebugAndroidTest - name: Read Results [org.matrix.android.sdk.account] continue-on-error: true id: get-comment-body-account @@ -136,7 +136,7 @@ jobs: force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 - script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.internal' matrix-sdk-android:connectedDebugAndroidTest --info + script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.internal' matrix-sdk-android:connectedDebugAndroidTest - name: Read Results [org.matrix.android.sdk.internal] continue-on-error: true id: get-comment-body-internal @@ -154,7 +154,7 @@ jobs: force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 - script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.ordering' matrix-sdk-android:connectedDebugAndroidTest --info + script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.ordering' matrix-sdk-android:connectedDebugAndroidTest - name: Read Results [org.matrix.android.sdk.ordering] continue-on-error: true id: get-comment-body-ordering @@ -172,7 +172,7 @@ jobs: force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 - script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class='org.matrix.android.sdk.PermalinkParserTest' matrix-sdk-android:connectedDebugAndroidTest --info + script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class='org.matrix.android.sdk.PermalinkParserTest' matrix-sdk-android:connectedDebugAndroidTest - name: Read Results [org.matrix.android.sd.PermalinkParserTest] continue-on-error: true id: get-comment-body-permalink diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index f437ed51e8..723bc52865 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -64,7 +64,7 @@ android { adbOptions { installOptions "-g" - timeOutInMs 350 * 1000 +// timeOutInMs 350 * 1000 } compileOptions { From 34efd91683e2cd737048d935261d899aa537cd3d Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 7 Jan 2022 11:07:21 +0200 Subject: [PATCH 018/281] Use 1 core for the emulator --- .github/workflows/integration_tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index f2a09af132..7c336fcc74 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -95,6 +95,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} + cores: 1 arch: x86 profile: Nexus 5X force-avd-creation: false @@ -113,6 +114,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} + cores: 1 arch: x86 profile: Nexus 5X force-avd-creation: false @@ -131,6 +133,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} + cores: 1 arch: x86 profile: Nexus 5X force-avd-creation: false @@ -149,6 +152,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} + cores: 1 arch: x86 profile: Nexus 5X force-avd-creation: false @@ -167,6 +171,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} + cores: 1 arch: x86 profile: Nexus 5X force-avd-creation: false From 2c8015c88f421389d278f1ddb2bad66fe51a4a05 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 7 Jan 2022 12:44:09 +0200 Subject: [PATCH 019/281] try with force-avd-creation = true --- .github/workflows/integration_tests.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 7c336fcc74..8c0607b075 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -98,7 +98,7 @@ jobs: cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: false + force-avd-creation: true emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest @@ -117,7 +117,7 @@ jobs: cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: false + force-avd-creation: true emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.account' matrix-sdk-android:connectedDebugAndroidTest @@ -136,7 +136,7 @@ jobs: cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: false + force-avd-creation: true emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.internal' matrix-sdk-android:connectedDebugAndroidTest @@ -155,7 +155,7 @@ jobs: cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: false + force-avd-creation: true emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.ordering' matrix-sdk-android:connectedDebugAndroidTest @@ -174,7 +174,7 @@ jobs: cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: false + force-avd-creation: true emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class='org.matrix.android.sdk.PermalinkParserTest' matrix-sdk-android:connectedDebugAndroidTest From e2616ba095a5c6f764d1bdfa871c8e48b6243e67 Mon Sep 17 00:00:00 2001 From: Aris Kotsomitopoulos <60798129+ariskotsomitopoulos@users.noreply.github.com> Date: Fri, 7 Jan 2022 13:59:43 +0200 Subject: [PATCH 020/281] Update integration_tests.yml Undo 1 core usage Undo force-avd-creation --- .github/workflows/integration_tests.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 8c0607b075..bf78356947 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -95,10 +95,9 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: true + force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest @@ -114,10 +113,9 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: true + force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.account' matrix-sdk-android:connectedDebugAndroidTest @@ -133,10 +131,9 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: true + force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.internal' matrix-sdk-android:connectedDebugAndroidTest @@ -152,10 +149,9 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: true + force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.ordering' matrix-sdk-android:connectedDebugAndroidTest @@ -171,10 +167,9 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - cores: 1 arch: x86 profile: Nexus 5X - force-avd-creation: true + force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-build: 7425822 script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class='org.matrix.android.sdk.PermalinkParserTest' matrix-sdk-android:connectedDebugAndroidTest @@ -210,4 +205,4 @@ jobs: # script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest --info # script: ./gradlew $CI_GRADLE_ARG_PROPERTIES matrix-sdk-android:connectedAndroidTest --info # script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedCheck --stacktrace -# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest --info \ No newline at end of file +# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest --info From 0fb26da35590721250c6e88bc6575b7bce841b4e Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 10 Jan 2022 11:26:03 +0200 Subject: [PATCH 021/281] PR Remarks --- .../java/org/matrix/android/sdk/common/RetryTestRule.kt | 6 ------ .../java/org/matrix/android/sdk/common/TestConstants.kt | 2 +- .../android/sdk/internal/crypto/verification/SASTest.kt | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt index 5f1a59e585..c4cc32d8e9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.common -import android.util.Log import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement @@ -27,8 +26,6 @@ import org.junit.runners.model.Statement */ class RetryTestRule(val retryCount: Int = 3) : TestRule { - private val TAG = RetryTestRule::class.java.simpleName - override fun apply(base: Statement, description: Description): Statement { return statement(base, description) } @@ -46,11 +43,8 @@ class RetryTestRule(val retryCount: Int = 3) : TestRule { return } catch (t: Throwable) { caughtThrowable = t - Log.e(TAG, description.displayName + ": run " + (i + 1) + " failed") } } - - Log.e(TAG, description.displayName + ": giving up after " + retryCount + " failures") throw caughtThrowable!! } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt index ecfd587929..5c9b79361e 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt @@ -22,7 +22,7 @@ object TestConstants { const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080" - // Time out to use when waiting for server response. 20s + // Time out to use when waiting for server response. private const val AWAIT_TIME_OUT_MILLIS = 30_000 // Time out to use when waiting for server response, when the debugger is connected. 10 minutes diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index d4350bd845..8cd725504d 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -546,8 +546,6 @@ class SASTest : InstrumentedTest { val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId) val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.cryptoService().getDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyDevice().deviceId) - // latch wait a bit again - assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified) assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) cryptoTestData.cleanUp(testHelper) From ff4bbf0a8a16d35847e9c59aeba1b481e330a0cd Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 28 Sep 2021 13:15:51 +0200 Subject: [PATCH 022/281] Add "Create room" shortcut in Explore Space screen --- changelog.d/3932.bugfix | 1 + .../features/navigation/DefaultNavigator.kt | 4 +- .../app/features/navigation/Navigator.kt | 2 +- .../RoomDirectorySharedAction.kt | 1 + .../createroom/CreateRoomActivity.kt | 35 +++++-- .../createroom/CreateRoomFragment.kt | 22 +++-- .../createroom/CreateRoomViewModel.kt | 19 ++-- .../createroom/CreateRoomViewState.kt | 6 +- .../features/spaces/SpaceExploreActivity.kt | 22 +++++ .../spaces/explore/SpaceDirectoryFragment.kt | 10 ++ .../explore/SpaceDirectoryViewAction.kt | 2 + .../explore/SpaceDirectoryViewEvents.kt | 1 + .../spaces/explore/SpaceDirectoryViewModel.kt | 97 ++++++++++++++++++- .../SpaceAddRoomSpaceChooserBottomSheet.kt | 8 ++ ...tom_sheet_add_rooms_or_spaces_to_space.xml | 32 +++--- .../res/layout/fragment_space_directory.xml | 14 +++ 16 files changed, 231 insertions(+), 45 deletions(-) create mode 100644 changelog.d/3932.bugfix diff --git a/changelog.d/3932.bugfix b/changelog.d/3932.bugfix new file mode 100644 index 0000000000..76e90f2bfd --- /dev/null +++ b/changelog.d/3932.bugfix @@ -0,0 +1 @@ +Explore Rooms overflow menu - content update include "Create room" \ No newline at end of file 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 30ead8a6bf..c219d7feff 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 @@ -309,8 +309,8 @@ class DefaultNavigator @Inject constructor( } } - override fun openCreateRoom(context: Context, initialName: String) { - val intent = CreateRoomActivity.getIntent(context, initialName) + override fun openCreateRoom(context: Context, initialName: String, openAfterCreate: Boolean) { + val intent = CreateRoomActivity.getIntent(context = context, initialName = initialName, openAfterCreate = openAfterCreate) context.startActivity(intent) } 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 6778c39a22..2f152b649f 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 @@ -76,7 +76,7 @@ interface Navigator { fun openMatrixToBottomSheet(context: Context, link: String) - fun openCreateRoom(context: Context, initialName: String = "") + fun openCreateRoom(context: Context, initialName: String = "", openAfterCreate: Boolean = true) fun openCreateDirectRoom(context: Context) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectorySharedAction.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectorySharedAction.kt index 9911ce6686..ea9211cc7b 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectorySharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectorySharedAction.kt @@ -25,5 +25,6 @@ sealed class RoomDirectorySharedAction : VectorSharedAction { object Back : RoomDirectorySharedAction() object CreateRoom : RoomDirectorySharedAction() object Close : RoomDirectorySharedAction() + data class CreateRoomSuccess(val createdRoomId: String) : RoomDirectorySharedAction() object ChangeProtocol : RoomDirectorySharedAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt index e9762d09d3..058bd97fbd 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt @@ -16,10 +16,12 @@ package im.vector.app.features.roomdirectory.createroom +import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle import androidx.lifecycle.lifecycleScope +import com.airbnb.mvrx.Mavericks import com.google.android.material.appbar.MaterialToolbar import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.addFragment @@ -49,13 +51,11 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarC override fun initUiAndData() { if (isFirstCreation()) { + val fragmentArgs: CreateRoomArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return addFragment( views.simpleFragmentContainer, CreateRoomFragment::class.java, - CreateRoomArgs( - intent?.getStringExtra(INITIAL_NAME) ?: "", - isSpace = intent?.getBooleanExtra(IS_SPACE, false) ?: false - ) + fragmentArgs ) } } @@ -68,20 +68,35 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarC .onEach { sharedAction -> when (sharedAction) { is RoomDirectorySharedAction.Back, - is RoomDirectorySharedAction.Close -> finish() + is RoomDirectorySharedAction.Close -> finish() + is RoomDirectorySharedAction.CreateRoomSuccess -> { + setResult(Activity.RESULT_OK, Intent().apply { putExtra(RESULT_CREATED_ROOM_ID, sharedAction.createdRoomId) }) + finish() + } + else -> { + // nop + } } } .launchIn(lifecycleScope) } companion object { - private const val INITIAL_NAME = "INITIAL_NAME" - private const val IS_SPACE = "IS_SPACE" - fun getIntent(context: Context, initialName: String = "", isSpace: Boolean = false): Intent { + const val RESULT_CREATED_ROOM_ID = "RESULT_CREATED_ROOM_ID" + + fun getIntent(context: Context, + initialName: String = "", + isSpace: Boolean = false, + openAfterCreate: Boolean = true, + currentSpaceId: String? = null): Intent { return Intent(context, CreateRoomActivity::class.java).apply { - putExtra(INITIAL_NAME, initialName) - putExtra(IS_SPACE, isSpace) + putExtra(Mavericks.KEY_ARG, CreateRoomArgs( + initialName = initialName, + isSpace = isSpace, + openAfterCreate = openAfterCreate, + parentSpaceId = currentSpaceId + )) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt index 1244a0f64e..c14c6be2e0 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -56,7 +56,8 @@ import javax.inject.Inject data class CreateRoomArgs( val initialName: String, val parentSpaceId: String? = null, - val isSpace: Boolean = false + val isSpace: Boolean = false, + val openAfterCreate: Boolean = true ) : Parcelable class CreateRoomFragment @Inject constructor( @@ -226,16 +227,19 @@ class CreateRoomFragment @Inject constructor( views.waitingView.root.isVisible = async is Loading if (async is Success) { // Navigate to freshly created room - if (state.isSubSpace) { - navigator.switchToSpace( - requireContext(), - async(), - Navigator.PostSwitchSpaceAction.None - ) - } else { - navigator.openRoom(requireActivity(), async()) + if (state.openAfterCreate) { + if (state.isSubSpace) { + navigator.switchToSpace( + requireContext(), + async(), + Navigator.PostSwitchSpaceAction.None + ) + } else { + navigator.openRoom(requireActivity(), async()) + } } + sharedActionViewModel.post(RoomDirectorySharedAction.CreateRoomSuccess(async())) sharedActionViewModel.post(RoomDirectorySharedAction.Close) } else { // Populate list with Epoxy 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 e0ffdc7a52..34f51b92a8 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 @@ -27,6 +27,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.AppStateHandler import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.raw.wellknown.getElementWellknown @@ -52,10 +53,11 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset import timber.log.Timber -class CreateRoomViewModel @AssistedInject constructor(@Assisted private val initialState: CreateRoomViewState, +class CreateRoomViewModel @AssistedInject constructor(@Assisted val initialState: CreateRoomViewState, private val session: Session, private val rawService: RawService, - vectorPreferences: VectorPreferences + private val vectorPreferences: VectorPreferences, + appStateHandler: AppStateHandler ) : VectorViewModel(initialState) { @AssistedFactory @@ -69,6 +71,8 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init initHomeServerName() initAdminE2eByDefault() + val parentSpaceId = initialState.parentSpaceId ?: appStateHandler.safeActiveSpaceId() + val restrictedSupport = session.getHomeServerCapabilities().isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED) val createRestricted = when (restrictedSupport) { HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED -> true @@ -76,7 +80,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init else -> false } - val defaultJoinRules = if (initialState.parentSpaceId != null && createRestricted) { + val defaultJoinRules = if (parentSpaceId != null && createRestricted) { RoomJoinRules.RESTRICTED } else { RoomJoinRules.INVITE @@ -84,9 +88,10 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init setState { copy( + parentSpaceId = parentSpaceId, supportsRestricted = createRestricted, roomJoinRules = defaultJoinRules, - parentSpaceSummary = initialState.parentSpaceId?.let { session.getRoomSummary(it) } + parentSpaceSummary = parentSpaceId?.let { session.getRoomSummary(it) } ) } } @@ -162,7 +167,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init CreateRoomViewState( isEncrypted = adminE2EByDefault, hsAdminHasDisabledE2E = !adminE2EByDefault, - parentSpaceId = initialState.parentSpaceId + parentSpaceId = this.parentSpaceId ) } @@ -298,11 +303,11 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init runCatching { session.createRoom(createRoomParams) }.fold( { roomId -> - if (initialState.parentSpaceId != null) { + if (state.parentSpaceId != null) { // add it as a child try { session.spaceService() - .getSpace(initialState.parentSpaceId) + .getSpace(state.parentSpaceId) ?.addChildren(roomId, viaServers = null, order = null) } catch (failure: Throwable) { Timber.w(failure, "Failed to add as a child") 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 389d365875..cf8cc669ab 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 @@ -39,13 +39,15 @@ data class CreateRoomViewState( val parentSpaceSummary: RoomSummary? = null, val supportsRestricted: Boolean = false, val aliasLocalPart: String? = null, - val isSubSpace: Boolean = false + val isSubSpace: Boolean = false, + val openAfterCreate: Boolean = true ) : MavericksState { constructor(args: CreateRoomArgs) : this( roomName = args.initialName, parentSpaceId = args.parentSpaceId, - isSubSpace = args.isSpace + isSubSpace = args.isSpace, + openAfterCreate = args.openAfterCreate ) /** 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 3361305c83..ead1736d3c 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 @@ -16,6 +16,7 @@ package im.vector.app.features.spaces +import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle @@ -25,13 +26,18 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.extensions.commitTransaction +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.matrixto.MatrixToBottomSheet +import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity import im.vector.app.features.navigation.Navigator import im.vector.app.features.spaces.explore.SpaceDirectoryArgs import im.vector.app.features.spaces.explore.SpaceDirectoryFragment +import im.vector.app.features.spaces.explore.SpaceDirectoryState +import im.vector.app.features.spaces.explore.SpaceDirectoryViewAction import im.vector.app.features.spaces.explore.SpaceDirectoryViewEvents import im.vector.app.features.spaces.explore.SpaceDirectoryViewModel @@ -44,6 +50,15 @@ class SpaceExploreActivity : VectorBaseActivity(), Matrix val sharedViewModel: SpaceDirectoryViewModel by viewModel() + private val createRoomResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + activityResult.data?.extras?.getString(CreateRoomActivity.RESULT_CREATED_ROOM_ID)?.let { + // we want to refresh from API + sharedViewModel.handle(SpaceDirectoryViewAction.RefreshUntilFound(it)) + } + } + } + private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { if (f is MatrixToBottomSheet) { @@ -84,6 +99,13 @@ class SpaceExploreActivity : VectorBaseActivity(), Matrix is SpaceDirectoryViewEvents.NavigateToMxToBottomSheet -> { MatrixToBottomSheet.withLink(it.link).show(supportFragmentManager, "ShowChild") } + is SpaceDirectoryViewEvents.NavigateToCreateNewRoom -> { + createRoomResultLauncher.launch(CreateRoomActivity.getIntent( + this, + openAfterCreate = false, + currentSpaceId = it.currentSpaceId + )) + } } } } 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 f630323790..14850ebaa6 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 @@ -89,6 +89,9 @@ class SpaceDirectoryFragment @Inject constructor( SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_SPACES -> { addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRoomsOnlySpaces)) } + SpaceAddRoomSpaceChooserBottomSheet.ACTION_CREATE_ROOM -> { + viewModel.handle(SpaceDirectoryViewAction.CreateNewRoom) + } else -> { // nop } @@ -114,6 +117,12 @@ class SpaceDirectoryFragment @Inject constructor( invalidateOptionsMenu() } + views.addOrCreateChatRoomButton.debouncedClicks { + withState(viewModel) { + addExistingRooms(it.spaceId) + } + } + views.spaceCard.matrixToCardMainButton.isVisible = false views.spaceCard.matrixToCardSecondaryButton.isVisible = false } @@ -142,6 +151,7 @@ class SpaceDirectoryFragment @Inject constructor( } spaceCardRenderer.render(state.currentRootSummary, emptyList(), this, views.spaceCard) + views.addOrCreateChatRoomButton.isVisible = state.canAddRooms } override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt index 3ced017d61..2166a7e306 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt @@ -24,7 +24,9 @@ sealed class SpaceDirectoryViewAction : VectorViewModelAction { data class JoinOrOpen(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction() data class ShowDetails(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction() data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction() + object CreateNewRoom : SpaceDirectoryViewAction() object HandleBack : SpaceDirectoryViewAction() object Retry : SpaceDirectoryViewAction() + data class RefreshUntilFound(val roomIdToFind: String) : SpaceDirectoryViewAction() object LoadAdditionalItemsIfNeeded : SpaceDirectoryViewAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewEvents.kt index 3ac0426de9..6359eff68d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewEvents.kt @@ -22,4 +22,5 @@ sealed class SpaceDirectoryViewEvents : VectorViewEvents { object Dismiss : SpaceDirectoryViewEvents() data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewEvents() data class NavigateToMxToBottomSheet(val link: String) : SpaceDirectoryViewEvents() + data class NavigateToCreateNewRoom(val currentSpaceId: String) : SpaceDirectoryViewEvents() } 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 d7bdf4f511..b1dafbccfe 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 @@ -71,6 +71,27 @@ class SpaceDirectoryViewModel @AssistedInject constructor( observeJoinedRooms() observeMembershipChanges() observePermissions() + observeKnownSummaries() + } + + private fun observeKnownSummaries() { + // A we prefer to use known summaries to have better name resolution + // it's important to have them up to date. Particularly after creation where + // resolved name is sometimes just "New Room" + session.rx().liveRoomSummaries( + roomSummaryQueryParams { + memberships = listOf(Membership.JOIN) + includeType = null + } + ).execute { + val updatedRoomSummaries = it + copy( + knownRoomSummaries = this.knownRoomSummaries.map { rs -> + updatedRoomSummaries.invoke()?.firstOrNull { it.roomId == rs.roomId } + ?: rs + } + ) + } } private fun observePermissions() { @@ -103,7 +124,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor( try { val query = session.spaceService().querySpaceChildren( spaceId, - limit = 10 + limit = PAGE_LENGTH ) val knownSummaries = query.children.mapNotNull { session.getRoomSummary(it.childRoomId) @@ -181,9 +202,17 @@ class SpaceDirectoryViewModel @AssistedInject constructor( SpaceDirectoryViewAction.Retry -> { handleRetry() } + is SpaceDirectoryViewAction.RefreshUntilFound -> { + handleRefreshUntilFound(action.roomIdToFind) + } SpaceDirectoryViewAction.LoadAdditionalItemsIfNeeded -> { loadAdditionalItemsIfNeeded() } + is SpaceDirectoryViewAction.CreateNewRoom -> { + withState { state -> + _viewEvents.post(SpaceDirectoryViewEvents.NavigateToCreateNewRoom(state.currentRootSummary?.roomId ?: initialState.spaceId)) + } + } } } @@ -207,6 +236,66 @@ class SpaceDirectoryViewModel @AssistedInject constructor( refreshFromApi(state.hierarchyStack.lastOrNull() ?: initialState.spaceId) } + private fun handleRefreshUntilFound(roomIdToFind: String?) = withState { state -> + val currentRootId = state.hierarchyStack.lastOrNull() ?: initialState.spaceId + + val mutablePaginationStatus = state.paginationStatus.toMutableMap().apply { + this[currentRootId] = Loading() + } + + // mark as paginating + setState { + copy( + paginationStatus = mutablePaginationStatus + ) + } + + viewModelScope.launch(Dispatchers.IO) { + var query = session.spaceService().querySpaceChildren( + currentRootId, + limit = PAGE_LENGTH + ) + + var knownSummaries = query.children.mapNotNull { + session.getRoomSummary(it.childRoomId) + ?.takeIf { it.membership == Membership.JOIN } // only take if joined because it will be up to date (synced) + }.distinctBy { it.roomId } + + while (!query.children.any { it.childRoomId == roomIdToFind } && query.nextToken != null) { + // continue to paginate until found + val paginate = session.spaceService().querySpaceChildren( + currentRootId, + limit = PAGE_LENGTH, + from = query.nextToken, + knownStateList = query.childrenState + ) + + knownSummaries = ( + knownSummaries + + (paginate.children.mapNotNull { + session.getRoomSummary(it.childRoomId) + ?.takeIf { it.membership == Membership.JOIN } // only take if joined because it will be up to date (synced) + }) + ).distinctBy { it.roomId } + + query = query.copy( + children = query.children + paginate.children, + nextToken = paginate.nextToken + ) + } + + setState { + copy( + apiResults = this.apiResults.toMutableMap().apply { + this[currentRootId] = Success(query) + }, + paginationStatus = this.paginationStatus.toMutableMap().apply { this[currentRootId] = Success(Unit) }.toMap(), + knownRoomSummaries = (state.knownRoomSummaries + knownSummaries).distinctBy { it.roomId }, + ) + } + } + } + private fun handleExploreSubSpace(action: SpaceDirectoryViewAction.ExploreSubSpace) = withState { state -> val newRootId = action.spaceChildInfo.childRoomId val curSum = RoomSummary( @@ -252,7 +341,9 @@ class SpaceDirectoryViewModel @AssistedInject constructor( if (mutablePaginationStatus[currentRootId] is Loading) return@withState setState { - copy(paginationStatus = mutablePaginationStatus.toMap()) + copy(paginationStatus = mutablePaginationStatus.apply { + this[currentRootId] = Loading() + }) } viewModelScope.launch(Dispatchers.IO) { @@ -268,7 +359,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor( } val query = session.spaceService().querySpaceChildren( currentRootId, - limit = 10, + limit = PAGE_LENGTH, from = currentResponse.nextToken, knownStateList = currentResponse.childrenState ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomSpaceChooserBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomSpaceChooserBottomSheet.kt index 971ff7e0b1..4ad7aab940 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomSpaceChooserBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomSpaceChooserBottomSheet.kt @@ -34,6 +34,13 @@ class SpaceAddRoomSpaceChooserBottomSheet : VectorBaseBottomSheetDialogFragment< override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + views.createRooms.views.bottomSheetActionClickableZone.debouncedClicks { + setFragmentResult(REQUEST_KEY, Bundle().apply { + putString(BUNDLE_KEY_ACTION, ACTION_CREATE_ROOM) + }) + dismiss() + } + views.addSpaces.views.bottomSheetActionClickableZone.debouncedClicks { setFragmentResult(REQUEST_KEY, Bundle().apply { putString(BUNDLE_KEY_ACTION, ACTION_ADD_SPACES) @@ -55,6 +62,7 @@ class SpaceAddRoomSpaceChooserBottomSheet : VectorBaseBottomSheetDialogFragment< const val BUNDLE_KEY_ACTION = "SpaceAddRoomSpaceChooserBottomSheet.Action" const val ACTION_ADD_ROOMS = "Action.AddRoom" const val ACTION_ADD_SPACES = "Action.AddSpaces" + const val ACTION_CREATE_ROOM = "Action.CreateRoom" fun newInstance(): SpaceAddRoomSpaceChooserBottomSheet { return SpaceAddRoomSpaceChooserBottomSheet() diff --git a/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml b/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml index 25c2d1c3e5..f17bcd16f4 100644 --- a/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml +++ b/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml @@ -7,24 +7,34 @@ android:background="?android:colorBackground" android:orientation="vertical"> - + + + + + + + + + + + + + + app:actionTitle="@string/create_new_room" + app:leftIcon="@drawable/ic_fab_add" + app:tint="?vctr_content_primary" + app:titleTextColor="?vctr_content_primary" + tools:actionDescription="" /> + + \ No newline at end of file From 2dbe2b5f309fdff559bdde69e71b3c6a9fdf5700 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 30 Sep 2021 09:58:50 +0200 Subject: [PATCH 023/281] show/hide fab on scroll --- .../spaces/explore/SpaceDirectoryFragment.kt | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) 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 14850ebaa6..2c7da43988 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 @@ -26,6 +26,7 @@ import android.view.ViewGroup import androidx.core.text.toSpannable import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState @@ -83,16 +84,16 @@ class SpaceDirectoryFragment @Inject constructor( bundle.getString(SpaceAddRoomSpaceChooserBottomSheet.BUNDLE_KEY_ACTION)?.let { action -> val spaceId = withState(viewModel) { it.spaceId } when (action) { - SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_ROOMS -> { + SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_ROOMS -> { addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRooms)) } - SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_SPACES -> { + SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_SPACES -> { addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRoomsOnlySpaces)) } SpaceAddRoomSpaceChooserBottomSheet.ACTION_CREATE_ROOM -> { viewModel.handle(SpaceDirectoryViewAction.CreateNewRoom) } - else -> { + else -> { // nop } } @@ -125,6 +126,24 @@ class SpaceDirectoryFragment @Inject constructor( views.spaceCard.matrixToCardMainButton.isVisible = false views.spaceCard.matrixToCardSecondaryButton.isVisible = false + + // Hide FAB when list is scrolling + views.spaceDirectoryList.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + views.addOrCreateChatRoomButton.removeCallbacks(showFabRunnable) + + when (newState) { + RecyclerView.SCROLL_STATE_IDLE -> { + views.addOrCreateChatRoomButton.postDelayed(showFabRunnable, 250) + } + RecyclerView.SCROLL_STATE_DRAGGING, + RecyclerView.SCROLL_STATE_SETTLING -> { + views.addOrCreateChatRoomButton.hide() + } + } + } + }) } override fun onDestroyView() { @@ -134,6 +153,12 @@ class SpaceDirectoryFragment @Inject constructor( super.onDestroyView() } + private val showFabRunnable = Runnable { + if (isAdded) { + views.addOrCreateChatRoomButton.show() + } + } + override fun invalidate() = withState(viewModel) { state -> epoxyController.setData(state) From 5c5f2766b6e09a98786ebf761ef1ef8cd81c358f Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 10 Jan 2022 17:17:35 +0100 Subject: [PATCH 024/281] post rebase fix --- .../app/features/spaces/explore/SpaceDirectoryViewModel.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 b1dafbccfe..0b357a900b 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 @@ -55,7 +55,9 @@ class SpaceDirectoryViewModel @AssistedInject constructor( override fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel } - companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { + private const val PAGE_LENGTH = 10 + } init { @@ -78,7 +80,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor( // A we prefer to use known summaries to have better name resolution // it's important to have them up to date. Particularly after creation where // resolved name is sometimes just "New Room" - session.rx().liveRoomSummaries( + session.flow().liveRoomSummaries( roomSummaryQueryParams { memberships = listOf(Membership.JOIN) includeType = null From 5cbb1d99c7c657f8801c777dbed5e137fa7dc97c Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 10 Jan 2022 17:38:35 +0100 Subject: [PATCH 025/281] Code review --- .../features/roomdirectory/createroom/CreateRoomActivity.kt | 6 +++++- .../roomdirectory/createroom/CreateRoomViewModel.kt | 6 +++--- .../im/vector/app/features/spaces/SpaceExploreActivity.kt | 6 ++---- .../app/features/spaces/explore/SpaceDirectoryViewModel.kt | 4 ++-- vector/src/main/res/layout/fragment_space_directory.xml | 4 +--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt index 058bd97fbd..88bead5244 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt @@ -83,7 +83,7 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarC companion object { - const val RESULT_CREATED_ROOM_ID = "RESULT_CREATED_ROOM_ID" + private const val RESULT_CREATED_ROOM_ID = "RESULT_CREATED_ROOM_ID" fun getIntent(context: Context, initialName: String = "", @@ -99,5 +99,9 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarC )) } } + + fun getCreatedRoomId(data: Intent?): String? { + return data?.extras?.getString(RESULT_CREATED_ROOM_ID) + } } } 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 34f51b92a8..cd2bcd9924 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 @@ -25,9 +25,9 @@ import com.airbnb.mvrx.Uninitialized import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.AppStateHandler import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory -import im.vector.app.AppStateHandler import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.raw.wellknown.getElementWellknown @@ -53,10 +53,10 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset import timber.log.Timber -class CreateRoomViewModel @AssistedInject constructor(@Assisted val initialState: CreateRoomViewState, +class CreateRoomViewModel @AssistedInject constructor(@Assisted private val initialState: CreateRoomViewState, private val session: Session, private val rawService: RawService, - private val vectorPreferences: VectorPreferences, + vectorPreferences: VectorPreferences, appStateHandler: AppStateHandler ) : VectorViewModel(initialState) { 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 ead1736d3c..f4610805bc 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 @@ -26,17 +26,15 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.matrixto.MatrixToBottomSheet -import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity import im.vector.app.features.navigation.Navigator +import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity import im.vector.app.features.spaces.explore.SpaceDirectoryArgs import im.vector.app.features.spaces.explore.SpaceDirectoryFragment -import im.vector.app.features.spaces.explore.SpaceDirectoryState import im.vector.app.features.spaces.explore.SpaceDirectoryViewAction import im.vector.app.features.spaces.explore.SpaceDirectoryViewEvents import im.vector.app.features.spaces.explore.SpaceDirectoryViewModel @@ -52,7 +50,7 @@ class SpaceExploreActivity : VectorBaseActivity(), Matrix private val createRoomResultLauncher = registerStartForActivityResult { activityResult -> if (activityResult.resultCode == Activity.RESULT_OK) { - activityResult.data?.extras?.getString(CreateRoomActivity.RESULT_CREATED_ROOM_ID)?.let { + CreateRoomActivity.getCreatedRoomId(activityResult.data)?.let { // we want to refresh from API sharedViewModel.handle(SpaceDirectoryViewAction.RefreshUntilFound(it)) } 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 0b357a900b..abc70ccbc1 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 @@ -273,8 +273,8 @@ class SpaceDirectoryViewModel @AssistedInject constructor( ) knownSummaries = ( - knownSummaries - + (paginate.children.mapNotNull { + knownSummaries + + (paginate.children.mapNotNull { session.getRoomSummary(it.childRoomId) ?.takeIf { it.membership == Membership.JOIN } // only take if joined because it will be up to date (synced) }) diff --git a/vector/src/main/res/layout/fragment_space_directory.xml b/vector/src/main/res/layout/fragment_space_directory.xml index c638ce25bd..bc77bb1474 100644 --- a/vector/src/main/res/layout/fragment_space_directory.xml +++ b/vector/src/main/res/layout/fragment_space_directory.xml @@ -60,9 +60,7 @@ android:layout_marginBottom="16dp " android:contentDescription="@string/a11y_create_room" android:scaleType="center" - app:maxImageSize="20dp" android:src="@drawable/ic_fab_add" - android:visibility="visible" - tools:visibility="visible" /> + app:maxImageSize="20dp" /> \ No newline at end of file From 7be01ab7ae677bb26dbb725e6f0b4de3e203decd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 10 Jan 2022 17:47:27 +0100 Subject: [PATCH 026/281] Avoid allowing null String for state_key. Should always be an empty String according to the Matrix specification. There is no functional change, just a change in the SDK API for clarity regarding the Matrix specs. --- .../crypto/encryption/EncryptionTest.kt | 2 +- .../sdk/api/session/room/state/StateService.kt | 5 ++++- .../sdk/internal/session/room/DefaultRoom.kt | 2 +- .../session/room/state/DefaultStateService.kt | 18 +++++++++--------- .../session/room/state/SendStateTask.kt | 4 ++-- .../features/devtools/RoomDevToolViewModel.kt | 6 +++--- .../composer/MessageComposerViewModel.kt | 4 ++-- .../RoomMemberProfileViewModel.kt | 2 +- .../permissions/RoomPermissionsViewModel.kt | 2 +- .../features/widgets/WidgetPostAPIHandler.kt | 2 +- 10 files changed, 25 insertions(+), 22 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt index 189fc405eb..060201d624 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt @@ -62,7 +62,7 @@ class EncryptionTest : InstrumentedTest { // Send an encryption Event as a State Event room.sendStateEvent( eventType = EventType.STATE_ROOM_ENCRYPTION, - stateKey = null, + stateKey = "", body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent() ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 4d3f95233d..e9b0e4f676 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -68,8 +68,11 @@ interface StateService { /** * Send a state event to the room + * @param eventType The type of event to send. + * @param stateKey The state_key for the state to send. Can be an empty string. + * @param body The content object of the event; the fields in this object will vary depending on the type of event */ - suspend fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict) + suspend fun sendStateEvent(eventType: String, stateKey: String, body: JsonDict) /** * Get a state event of the room 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 cb4bcdb606..8d96bf8603 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 @@ -130,7 +130,7 @@ internal class DefaultRoom(override val roomId: String, else -> { val params = SendStateTask.Params( roomId = roomId, - stateKey = null, + stateKey = "", eventType = EventType.STATE_ROOM_ENCRYPTION, body = mapOf( "algorithm" to algorithm diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 4ec27976a2..417417f439 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -68,7 +68,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private override suspend fun sendStateEvent( eventType: String, - stateKey: String?, + stateKey: String, body: JsonDict ) { val params = SendStateTask.Params( @@ -92,7 +92,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private sendStateEvent( eventType = EventType.STATE_ROOM_TOPIC, body = mapOf("topic" to topic), - stateKey = null + stateKey = "" ) } @@ -100,7 +100,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private sendStateEvent( eventType = EventType.STATE_ROOM_NAME, body = mapOf("name" to name), - stateKey = null + stateKey = "" ) } @@ -117,7 +117,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private // Sort for the cleanup .sorted() ).toContent(), - stateKey = null + stateKey = "" ) } @@ -125,7 +125,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private sendStateEvent( eventType = EventType.STATE_ROOM_HISTORY_VISIBILITY, body = mapOf("history_visibility" to readability), - stateKey = null + stateKey = "" ) } @@ -142,14 +142,14 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private sendStateEvent( eventType = EventType.STATE_ROOM_JOIN_RULES, body = body, - stateKey = null + stateKey = "" ) } if (guestAccess != null) { sendStateEvent( eventType = EventType.STATE_ROOM_GUEST_ACCESS, body = mapOf("guest_access" to guestAccess), - stateKey = null + stateKey = "" ) } } @@ -159,7 +159,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private sendStateEvent( eventType = EventType.STATE_ROOM_AVATAR, body = mapOf("url" to response.contentUri), - stateKey = null + stateKey = "" ) } @@ -167,7 +167,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private sendStateEvent( eventType = EventType.STATE_ROOM_AVATAR, body = emptyMap(), - stateKey = null + stateKey = "" ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt index 998e116a0e..56c69a05a6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt @@ -26,7 +26,7 @@ import javax.inject.Inject internal interface SendStateTask : Task { data class Params( val roomId: String, - val stateKey: String?, + val stateKey: String, val eventType: String, val body: JsonDict ) @@ -39,7 +39,7 @@ internal class DefaultSendStateTask @Inject constructor( override suspend fun execute(params: SendStateTask.Params) { return executeRequest(globalErrorReceiver) { - if (params.stateKey == null) { + if (params.stateKey.isEmpty()) { roomAPI.sendStateEvent( roomId = params.roomId, stateEventType = params.eventType, 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 04d90a63e7..c3524e2cdf 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 @@ -174,8 +174,8 @@ class RoomDevToolViewModel @AssistedInject constructor( ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_content)) room.sendStateEvent( - state.selectedEvent?.type ?: "", - state.selectedEvent?.stateKey, + state.selectedEvent?.type.orEmpty(), + state.selectedEvent?.stateKey.orEmpty(), json ) @@ -213,7 +213,7 @@ class RoomDevToolViewModel @AssistedInject constructor( if (isState) { room.sendStateEvent( eventType, - state.sendEventDraft.stateKey, + state.sendEventDraft.stateKey.orEmpty(), json ) } else { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index a63a06928a..940099ddca 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -552,7 +552,7 @@ class MessageComposerViewModel @AssistedInject constructor( ?: return launchSlashCommandFlowSuspendable { - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent) + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent) } } @@ -619,7 +619,7 @@ class MessageComposerViewModel @AssistedInject constructor( private fun handleChangeRoomAvatarSlashCommand(changeAvatar: ParsedCommand.ChangeRoomAvatar) { launchSlashCommandFlowSuspendable { - room.sendStateEvent(EventType.STATE_ROOM_AVATAR, null, RoomAvatarContent(changeAvatar.url).toContent()) + room.sendStateEvent(EventType.STATE_ROOM_AVATAR, stateKey = "", RoomAvatarContent(changeAvatar.url).toContent()) } } 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 4f3d9d0776..63378a7edd 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 @@ -210,7 +210,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor( viewModelScope.launch { _viewEvents.post(RoomMemberProfileViewEvents.Loading()) try { - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent) + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent) _viewEvents.post(RoomMemberProfileViewEvents.OnSetPowerLevelSuccess) } catch (failure: Throwable) { _viewEvents.post(RoomMemberProfileViewEvents.Failure(failure)) 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 011c4ea8ae..7e8a66d12a 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 @@ -124,7 +124,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat } ) } - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent.toContent()) + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent.toContent()) setState { copy( isLoading = false diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt index 99b3595d11..cdffbd5411 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt @@ -319,7 +319,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo launchWidgetAPIAction(widgetPostAPIMediator, eventData) { room.sendStateEvent( eventType = EventType.PLUMBING, - stateKey = null, + stateKey = "", body = params ) } From 3bdb05f7b466ffbccc17a04a08581f1095d3fbe1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 10 Jan 2022 17:49:47 +0100 Subject: [PATCH 027/281] Changelog --- changelog.d/4895.removal | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4895.removal diff --git a/changelog.d/4895.removal b/changelog.d/4895.removal new file mode 100644 index 0000000000..8b3e3adba4 --- /dev/null +++ b/changelog.d/4895.removal @@ -0,0 +1 @@ +`StateService.sendStateEvent()` now takes a non-nullable String for the parameter `stateKey`. If null was used, just now use an empty string. \ No newline at end of file From 7581a0b5493efa114b45647c60990f2551bc6f5f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 10 Jan 2022 18:36:04 +0100 Subject: [PATCH 028/281] Fix test compilation --- .../org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index 1c38edbbd9..6c91100fa5 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -542,7 +542,7 @@ class SpaceHierarchyTest : InstrumentedTest { ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) ?.toContent() - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!) + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!) it.countDown() } From 8adeab0ddd4caac494bea6e22f4b0ef57233101d Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 11 Jan 2022 16:14:26 +0200 Subject: [PATCH 029/281] - fix lint error --- .../java/org/matrix/android/sdk/common/RetryTestRule.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt index c4cc32d8e9..b16ab98e6c 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/RetryTestRule.kt @@ -27,10 +27,10 @@ import org.junit.runners.model.Statement class RetryTestRule(val retryCount: Int = 3) : TestRule { override fun apply(base: Statement, description: Description): Statement { - return statement(base, description) + return statement(base) } - private fun statement(base: Statement, description: Description): Statement { + private fun statement(base: Statement): Statement { return object : Statement() { @Throws(Throwable::class) override fun evaluate() { From 9ec662ccdc9be0768bf4403252df5d961b46a8b0 Mon Sep 17 00:00:00 2001 From: fedrunov Date: Tue, 11 Jan 2022 10:17:29 +0100 Subject: [PATCH 030/281] replace "kick" with "remove" --- changelog.d/4865.misc | 1 + .../session/room/members/MembershipService.kt | 2 +- .../membership/DefaultMembershipService.kt | 2 +- .../im/vector/app/features/command/Command.kt | 2 +- .../app/features/command/CommandParser.kt | 10 +++--- .../app/features/command/ParsedCommand.kt | 2 +- .../composer/MessageComposerViewModel.kt | 8 ++--- .../RoomMemberProfileViewModel.kt | 2 +- vector/src/main/res/values/strings.xml | 32 +++++++++---------- 9 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 changelog.d/4865.misc diff --git a/changelog.d/4865.misc b/changelog.d/4865.misc new file mode 100644 index 0000000000..a253ab24c7 --- /dev/null +++ b/changelog.d/4865.misc @@ -0,0 +1 @@ +"/kick" command is replaced with "/remove" \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/members/MembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/members/MembershipService.kt index 198d6677a0..6dacd9cfd1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/members/MembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/members/MembershipService.kt @@ -77,7 +77,7 @@ interface MembershipService { /** * Kick a user from the room */ - suspend fun kick(userId: String, reason: String? = null) + suspend fun remove(userId: String, reason: String? = null) /** * Join the room, or accept an invitation. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 6cf82dde44..49b58aa765 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -125,7 +125,7 @@ internal class DefaultMembershipService @AssistedInject constructor( membershipAdminTask.execute(params) } - override suspend fun kick(userId: String, reason: String?) { + override suspend fun remove(userId: String, reason: String?) { val params = MembershipAdminTask.Params(MembershipAdminTask.Type.KICK, roomId, userId, reason) membershipAdminTask.execute(params) } diff --git a/vector/src/main/java/im/vector/app/features/command/Command.kt b/vector/src/main/java/im/vector/app/features/command/Command.kt index 1950038691..c90d06a7b0 100644 --- a/vector/src/main/java/im/vector/app/features/command/Command.kt +++ b/vector/src/main/java/im/vector/app/features/command/Command.kt @@ -37,7 +37,7 @@ enum class Command(val command: String, val parameters: String, @StringRes val d JOIN_ROOM("/join", " [reason]", R.string.command_description_join_room, false), PART("/part", "[]", R.string.command_description_part_room, false), TOPIC("/topic", "", R.string.command_description_topic, false), - KICK_USER("/kick", " [reason]", R.string.command_description_kick_user, false), + REMOVE_USER("/remove", " [reason]", R.string.command_description_kick_user, false), CHANGE_DISPLAY_NAME("/nick", "", R.string.command_description_nick, false), CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", "", R.string.command_description_nick_for_room, false), ROOM_AVATAR("/roomavatar", "", R.string.command_description_room_avatar, true /* Since user has to know the mxc url */), diff --git a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt index 4b2a4aa28c..22e1091c24 100644 --- a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt +++ b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt @@ -200,22 +200,22 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.INVITE) } } - Command.KICK_USER.command -> { + Command.REMOVE_USER.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] if (MatrixPatterns.isUserId(userId)) { - ParsedCommand.KickUser( + ParsedCommand.RemoveUser( userId, - textMessage.substring(Command.KICK_USER.length + userId.length) + textMessage.substring(Command.REMOVE_USER.length + userId.length) .trim() .takeIf { it.isNotBlank() } ) } else { - ParsedCommand.ErrorSyntax(Command.KICK_USER) + ParsedCommand.ErrorSyntax(Command.REMOVE_USER) } } else { - ParsedCommand.ErrorSyntax(Command.KICK_USER) + ParsedCommand.ErrorSyntax(Command.REMOVE_USER) } } Command.BAN_USER.command -> { diff --git a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt index 4f8d19abb6..584272f3f4 100644 --- a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt +++ b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt @@ -51,7 +51,7 @@ sealed class ParsedCommand { class JoinRoom(val roomAlias: String, val reason: String?) : ParsedCommand() class PartRoom(val roomAlias: String?) : ParsedCommand() class ChangeTopic(val topic: String) : ParsedCommand() - class KickUser(val userId: String, val reason: String?) : ParsedCommand() + class RemoveUser(val userId: String, val reason: String?) : ParsedCommand() class ChangeDisplayName(val displayName: String) : ParsedCommand() class ChangeDisplayNameForRoom(val displayName: String) : ParsedCommand() class ChangeRoomAvatar(val url: String) : ParsedCommand() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index a63a06928a..d787978aa9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -217,8 +217,8 @@ class MessageComposerViewModel @AssistedInject constructor( is ParsedCommand.UnignoreUser -> { handleUnignoreSlashCommand(slashCommandResult) } - is ParsedCommand.KickUser -> { - handleKickSlashCommand(slashCommandResult) + is ParsedCommand.RemoveUser -> { + handleRemoveSlashCommand(slashCommandResult) } is ParsedCommand.JoinRoom -> { handleJoinToAnotherRoomSlashCommand(slashCommandResult) @@ -576,9 +576,9 @@ class MessageComposerViewModel @AssistedInject constructor( } } - private fun handleKickSlashCommand(kick: ParsedCommand.KickUser) { + private fun handleRemoveSlashCommand(removeUser: ParsedCommand.RemoveUser) { launchSlashCommandFlowSuspendable { - room.kick(kick.userId, kick.reason) + room.remove(removeUser.userId, removeUser.reason) } } 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 4f3d9d0776..d6ba11d929 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 @@ -254,7 +254,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor( viewModelScope.launch { try { _viewEvents.post(RoomMemberProfileViewEvents.Loading()) - room.kick(initialState.userId, action.reason) + room.remove(initialState.userId, action.reason) _viewEvents.post(RoomMemberProfileViewEvents.OnKickActionSuccess) } catch (failure: Throwable) { _viewEvents.post(RoomMemberProfileViewEvents.Failure(failure)) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index dbdf104dff..11a3eb0a0d 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -25,8 +25,8 @@ You left the room %1$s rejected the invitation You rejected the invitation - %1$s kicked %2$s - You kicked %1$s + %1$s removed %2$s + You removed %1$s %1$s unbanned %2$s You unbanned %1$s %1$s banned %2$s @@ -229,8 +229,8 @@ You left. Reason: %1$s %1$s rejected the invitation. Reason: %2$s You rejected the invitation. Reason: %1$s - %1$s kicked %2$s. Reason: %3$s - You kicked %1$s. Reason: %2$s + %1$s removed %2$s. Reason: %3$s + You removed %1$s. Reason: %2$s %1$s unbanned %2$s. Reason: %3$s You unbanned %1$s. Reason: %2$s %1$s banned %2$s. Reason: %3$s @@ -884,7 +884,7 @@ Remove from this room Ban Unban - Kick + Remove from chat Reset to normal user Make moderator Make admin @@ -908,15 +908,15 @@ Cancel invite Are you sure you want to cancel the invite for this user? - Kick user - Reason to kick - kicking user will remove them from this room.\n\nTo prevent them from joining again, you should ban them instead. - kicking user will remove them from this space.\n\nTo prevent them from joining again, you should ban them instead. + Remove user + Reason to remove + The user will be removed from this room.\n\nTo prevent them from joining again, you should ban them instead. + The user will be removed from this space.\n\nTo prevent them from joining again, you should ban them instead. Ban user Reason to ban Unban user - Banning user will kick them from this room and prevent them from joining again. - Banning user will kick them from this space and prevent them from joining again. + Banning user will remove them from this room and prevent them from joining again. + Banning user will remove them from this space and prevent them from joining again. Unbanning user will allow them to join the room again. Unbanning user will allow them to join the space again. @@ -996,7 +996,7 @@ Send messages Invite users Change settings - Kick users + Remove users Ban users Remove messages sent by others Notify everyone @@ -1323,9 +1323,9 @@ Show room member state events Show chat effects Use /confetti command or send a message containing ❄️ or 🎉 - Includes invite/join/left/kick/ban events and avatar/display name changes. + Includes invite/join/left/remove/ban events and avatar/display name changes. Show join and leave events - Invites, kicks, and bans are unaffected. + Invites, removes, and bans are unaffected. Show account events Includes avatar and display name changes. Vibrate when mentioning a user @@ -1853,7 +1853,7 @@ Joins room with given address Leave room Set the room topic - Kicks user with given id + Removes user with given id from this room Changes your display nickname Changes your display nickname in the current room only Changes the avatar of the current room @@ -1903,7 +1903,7 @@ The community admin has not provided a long description for this community. - You have been kicked from %1$s by %2$s + You have been removed from %1$s by %2$s You have been banned from %1$s by %2$s Reason: %1$s Rejoin From 2aabbf0aa6ded0d97a08c13aece00d6019395de5 Mon Sep 17 00:00:00 2001 From: fedrunov Date: Tue, 11 Jan 2022 12:23:26 +0100 Subject: [PATCH 031/281] added aliases to commands --- .../command/AutocompleteCommandPresenter.kt | 2 +- .../im/vector/app/features/command/Command.kt | 78 +++--- .../app/features/command/CommandParser.kt | 222 ++++++++---------- .../home/room/detail/RoomDetailFragment.kt | 2 +- .../composer/MessageComposerViewModel.kt | 2 +- 5 files changed, 147 insertions(+), 159 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandPresenter.kt index 5ad31aeaa6..9888f1e35e 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandPresenter.kt @@ -50,7 +50,7 @@ class AutocompleteCommandPresenter @Inject constructor(context: Context, if (query.isNullOrEmpty()) { true } else { - it.command.startsWith(query, 1, true) + it.startsWith(query) } } controller.setData(data) diff --git a/vector/src/main/java/im/vector/app/features/command/Command.kt b/vector/src/main/java/im/vector/app/features/command/Command.kt index c90d06a7b0..4edd62a488 100644 --- a/vector/src/main/java/im/vector/app/features/command/Command.kt +++ b/vector/src/main/java/im/vector/app/features/command/Command.kt @@ -24,42 +24,46 @@ import im.vector.app.R * the user can write theses messages to perform some actions * the list will be displayed in this order */ -enum class Command(val command: String, val parameters: String, @StringRes val description: Int, val isDevCommand: Boolean) { - EMOTE("/me", "", R.string.command_description_emote, false), - BAN_USER("/ban", " [reason]", R.string.command_description_ban_user, false), - UNBAN_USER("/unban", " [reason]", R.string.command_description_unban_user, false), - IGNORE_USER("/ignore", " [reason]", R.string.command_description_ignore_user, false), - UNIGNORE_USER("/unignore", "", R.string.command_description_unignore_user, false), - SET_USER_POWER_LEVEL("/op", " []", R.string.command_description_op_user, false), - RESET_USER_POWER_LEVEL("/deop", "", R.string.command_description_deop_user, false), - ROOM_NAME("/roomname", "", R.string.command_description_room_name, false), - INVITE("/invite", " [reason]", R.string.command_description_invite_user, false), - JOIN_ROOM("/join", " [reason]", R.string.command_description_join_room, false), - PART("/part", "[]", R.string.command_description_part_room, false), - TOPIC("/topic", "", R.string.command_description_topic, false), - REMOVE_USER("/remove", " [reason]", R.string.command_description_kick_user, false), - CHANGE_DISPLAY_NAME("/nick", "", R.string.command_description_nick, false), - CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", "", R.string.command_description_nick_for_room, false), - ROOM_AVATAR("/roomavatar", "", R.string.command_description_room_avatar, true /* Since user has to know the mxc url */), - CHANGE_AVATAR_FOR_ROOM("/myroomavatar", "", R.string.command_description_avatar_for_room, true /* Since user has to know the mxc url */), - MARKDOWN("/markdown", "", R.string.command_description_markdown, false), - RAINBOW("/rainbow", "", R.string.command_description_rainbow, false), - RAINBOW_EMOTE("/rainbowme", "", R.string.command_description_rainbow_emote, false), - CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token, false), - SPOILER("/spoiler", "", R.string.command_description_spoiler, false), - SHRUG("/shrug", "", R.string.command_description_shrug, false), - LENNY("/lenny", "", R.string.command_description_lenny, false), - PLAIN("/plain", "", R.string.command_description_plain, false), - WHOIS("/whois", "", R.string.command_description_whois, false), - DISCARD_SESSION("/discardsession", "", R.string.command_description_discard_session, false), - CONFETTI("/confetti", "", R.string.command_confetti, false), - SNOWFALL("/snowfall", "", R.string.command_snow, false), - CREATE_SPACE("/createspace", " *", R.string.command_description_create_space, true), - ADD_TO_SPACE("/addToSpace", "spaceId", R.string.command_description_add_to_space, true), - JOIN_SPACE("/joinSpace", "spaceId", R.string.command_description_join_space, true), - LEAVE_ROOM("/leave", "", R.string.command_description_leave_room, true), - UPGRADE_ROOM("/upgraderoom", "newVersion", R.string.command_description_upgrade_room, true); +enum class Command(val command: String, val aliases: Array?, val parameters: String, @StringRes val description: Int, val isDevCommand: Boolean) { + EMOTE("/me", null, "", R.string.command_description_emote, false), + BAN_USER("/ban", null, " [reason]", R.string.command_description_ban_user, false), + UNBAN_USER("/unban", null, " [reason]", R.string.command_description_unban_user, false), + IGNORE_USER("/ignore", null, " [reason]", R.string.command_description_ignore_user, false), + UNIGNORE_USER("/unignore", null, "", R.string.command_description_unignore_user, false), + SET_USER_POWER_LEVEL("/op", null, " []", R.string.command_description_op_user, false), + RESET_USER_POWER_LEVEL("/deop", null, "", R.string.command_description_deop_user, false), + ROOM_NAME("/roomname", null, "", R.string.command_description_room_name, false), + INVITE("/invite", null, " [reason]", R.string.command_description_invite_user, false), + JOIN_ROOM("/join", null, " [reason]", R.string.command_description_join_room, false), + PART("/part", null, "[]", R.string.command_description_part_room, false), + TOPIC("/topic", null, "", R.string.command_description_topic, false), + REMOVE_USER("/remove", arrayOf("/kick"), " [reason]", R.string.command_description_kick_user, false), + CHANGE_DISPLAY_NAME("/nick", null, "", R.string.command_description_nick, false), + CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", null, "", R.string.command_description_nick_for_room, false), + ROOM_AVATAR("/roomavatar", null, "", R.string.command_description_room_avatar, true /* Since user has to know the mxc url */), + CHANGE_AVATAR_FOR_ROOM("/myroomavatar", null, "", R.string.command_description_avatar_for_room, true /* Since user has to know the mxc url */), + MARKDOWN("/markdown", null, "", R.string.command_description_markdown, false), + RAINBOW("/rainbow", null, "", R.string.command_description_rainbow, false), + RAINBOW_EMOTE("/rainbowme", null, "", R.string.command_description_rainbow_emote, false), + CLEAR_SCALAR_TOKEN("/clear_scalar_token", null, "", R.string.command_description_clear_scalar_token, false), + SPOILER("/spoiler", null, "", R.string.command_description_spoiler, false), + SHRUG("/shrug", null, "", R.string.command_description_shrug, false), + LENNY("/lenny", null, "", R.string.command_description_lenny, false), + PLAIN("/plain", null, "", R.string.command_description_plain, false), + WHOIS("/whois", null, "", R.string.command_description_whois, false), + DISCARD_SESSION("/discardsession", null, "", R.string.command_description_discard_session, false), + CONFETTI("/confetti", null, "", R.string.command_confetti, false), + SNOWFALL("/snowfall", null, "", R.string.command_snow, false), + CREATE_SPACE("/createspace", null, " *", R.string.command_description_create_space, true), + ADD_TO_SPACE("/addToSpace", null, "spaceId", R.string.command_description_add_to_space, true), + JOIN_SPACE("/joinSpace", null, "spaceId", R.string.command_description_join_space, true), + LEAVE_ROOM("/leave", null, "", R.string.command_description_leave_room, true), + UPGRADE_ROOM("/upgraderoom", null, "newVersion", R.string.command_description_upgrade_room, true); - val length - get() = command.length + 1 + val allAliases = arrayOf(command, *aliases.orEmpty()) + + fun matches(inputCommand: CharSequence) = allAliases.any { it.contentEquals(inputCommand, true) } + + fun startsWith(input: CharSequence) = + allAliases.any { it.startsWith(input, 1, true) } } diff --git a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt index 22e1091c24..c6de96859a 100644 --- a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt +++ b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt @@ -32,12 +32,12 @@ object CommandParser { * @param textMessage the text message * @return a parsed slash command (ok or error) */ - fun parseSplashCommand(textMessage: CharSequence): ParsedCommand { + fun parseSlashCommand(textMessage: CharSequence): ParsedCommand { // check if it has the Slash marker if (!textMessage.startsWith("/")) { return ParsedCommand.ErrorNotACommand } else { - Timber.v("parseSplashCommand") + Timber.v("parseSlashCommand") // "/" only if (textMessage.length == 1) { @@ -52,7 +52,7 @@ object CommandParser { val messageParts = try { textMessage.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() } } catch (e: Exception) { - Timber.e(e, "## manageSplashCommand() : split failed") + Timber.e(e, "## manageSlashCommand() : split failed") null } @@ -61,35 +61,32 @@ object CommandParser { return ParsedCommand.ErrorEmptySlashCommand } - return when (val slashCommand = messageParts.first()) { - Command.PLAIN.command -> { - val text = textMessage.substring(Command.PLAIN.command.length).trim() + val slashCommand = messageParts.first() + val message = textMessage.substring(slashCommand.length).trim() - if (text.isNotEmpty()) { - ParsedCommand.SendPlainText(text) + return when { + Command.PLAIN.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.SendPlainText(message = message) } else { ParsedCommand.ErrorSyntax(Command.PLAIN) } } - Command.CHANGE_DISPLAY_NAME.command -> { - val newDisplayName = textMessage.substring(Command.CHANGE_DISPLAY_NAME.command.length).trim() - - if (newDisplayName.isNotEmpty()) { - ParsedCommand.ChangeDisplayName(newDisplayName) + Command.CHANGE_DISPLAY_NAME.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.ChangeDisplayName(displayName = message) } else { ParsedCommand.ErrorSyntax(Command.CHANGE_DISPLAY_NAME) } } - Command.CHANGE_DISPLAY_NAME_FOR_ROOM.command -> { - val newDisplayName = textMessage.substring(Command.CHANGE_DISPLAY_NAME_FOR_ROOM.command.length).trim() - - if (newDisplayName.isNotEmpty()) { - ParsedCommand.ChangeDisplayNameForRoom(newDisplayName) + Command.CHANGE_DISPLAY_NAME_FOR_ROOM.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.ChangeDisplayNameForRoom(displayName = message) } else { ParsedCommand.ErrorSyntax(Command.CHANGE_DISPLAY_NAME_FOR_ROOM) } } - Command.ROOM_AVATAR.command -> { + Command.ROOM_AVATAR.matches(slashCommand) -> { if (messageParts.size == 2) { val url = messageParts[1] @@ -102,7 +99,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.ROOM_AVATAR) } } - Command.CHANGE_AVATAR_FOR_ROOM.command -> { + Command.CHANGE_AVATAR_FOR_ROOM.matches(slashCommand) -> { if (messageParts.size == 2) { val url = messageParts[1] @@ -115,40 +112,42 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.CHANGE_AVATAR_FOR_ROOM) } } - Command.TOPIC.command -> { - val newTopic = textMessage.substring(Command.TOPIC.command.length).trim() - - if (newTopic.isNotEmpty()) { - ParsedCommand.ChangeTopic(newTopic) + Command.TOPIC.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.ChangeTopic(topic = message) } else { ParsedCommand.ErrorSyntax(Command.TOPIC) } } - Command.EMOTE.command -> { - val message = textMessage.subSequence(Command.EMOTE.command.length, textMessage.length).trim() - - ParsedCommand.SendEmote(message) + Command.EMOTE.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.SendEmote(message) + } else { + ParsedCommand.ErrorSyntax(Command.EMOTE) + } } - Command.RAINBOW.command -> { - val message = textMessage.subSequence(Command.RAINBOW.command.length, textMessage.length).trim() - - ParsedCommand.SendRainbow(message) + Command.RAINBOW.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.SendRainbow(message) + } else { + ParsedCommand.ErrorSyntax(Command.RAINBOW) + } } - Command.RAINBOW_EMOTE.command -> { - val message = textMessage.subSequence(Command.RAINBOW_EMOTE.command.length, textMessage.length).trim() - - ParsedCommand.SendRainbowEmote(message) + Command.RAINBOW_EMOTE.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.SendRainbowEmote(message) + } else { + ParsedCommand.ErrorSyntax(Command.RAINBOW_EMOTE) + } } - Command.JOIN_ROOM.command -> { + Command.JOIN_ROOM.matches(slashCommand) -> { if (messageParts.size >= 2) { val roomAlias = messageParts[1] if (roomAlias.isNotEmpty()) { ParsedCommand.JoinRoom( roomAlias, - textMessage.substring(Command.JOIN_ROOM.length + roomAlias.length) - .trim() - .takeIf { it.isNotBlank() } + trimParts(textMessage, messageParts.take(2)) ) } else { ParsedCommand.ErrorSyntax(Command.JOIN_ROOM) @@ -157,23 +156,21 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.JOIN_ROOM) } } - Command.PART.command -> { + Command.PART.matches(slashCommand) -> { when (messageParts.size) { 1 -> ParsedCommand.PartRoom(null) 2 -> ParsedCommand.PartRoom(messageParts[1]) else -> ParsedCommand.ErrorSyntax(Command.PART) } } - Command.ROOM_NAME.command -> { - val newRoomName = textMessage.substring(Command.ROOM_NAME.command.length).trim() - - if (newRoomName.isNotEmpty()) { - ParsedCommand.ChangeRoomName(newRoomName) + Command.ROOM_NAME.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.ChangeRoomName(name = message) } else { ParsedCommand.ErrorSyntax(Command.ROOM_NAME) } } - Command.INVITE.command -> { + Command.INVITE.matches(slashCommand) -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -181,9 +178,7 @@ object CommandParser { MatrixPatterns.isUserId(userId) -> { ParsedCommand.Invite( userId, - textMessage.substring(Command.INVITE.length + userId.length) - .trim() - .takeIf { it.isNotBlank() } + trimParts(textMessage, messageParts.take(2)) ) } userId.isEmail() -> { @@ -200,16 +195,14 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.INVITE) } } - Command.REMOVE_USER.command -> { + Command.REMOVE_USER.matches(slashCommand) -> { if (messageParts.size >= 2) { val userId = messageParts[1] if (MatrixPatterns.isUserId(userId)) { ParsedCommand.RemoveUser( userId, - textMessage.substring(Command.REMOVE_USER.length + userId.length) - .trim() - .takeIf { it.isNotBlank() } + trimParts(textMessage, messageParts.take(2)) ) } else { ParsedCommand.ErrorSyntax(Command.REMOVE_USER) @@ -218,16 +211,14 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.REMOVE_USER) } } - Command.BAN_USER.command -> { + Command.BAN_USER.matches(slashCommand) -> { if (messageParts.size >= 2) { val userId = messageParts[1] if (MatrixPatterns.isUserId(userId)) { ParsedCommand.BanUser( userId, - textMessage.substring(Command.BAN_USER.length + userId.length) - .trim() - .takeIf { it.isNotBlank() } + trimParts(textMessage, messageParts.take(2)) ) } else { ParsedCommand.ErrorSyntax(Command.BAN_USER) @@ -236,16 +227,14 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.BAN_USER) } } - Command.UNBAN_USER.command -> { + Command.UNBAN_USER.matches(slashCommand) -> { if (messageParts.size >= 2) { val userId = messageParts[1] if (MatrixPatterns.isUserId(userId)) { ParsedCommand.UnbanUser( userId, - textMessage.substring(Command.UNBAN_USER.length + userId.length) - .trim() - .takeIf { it.isNotBlank() } + trimParts(textMessage, messageParts.take(2)) ) } else { ParsedCommand.ErrorSyntax(Command.UNBAN_USER) @@ -254,7 +243,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.UNBAN_USER) } } - Command.IGNORE_USER.command -> { + Command.IGNORE_USER.matches(slashCommand) -> { if (messageParts.size == 2) { val userId = messageParts[1] @@ -267,7 +256,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.IGNORE_USER) } } - Command.UNIGNORE_USER.command -> { + Command.UNIGNORE_USER.matches(slashCommand) -> { if (messageParts.size == 2) { val userId = messageParts[1] @@ -280,7 +269,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.UNIGNORE_USER) } } - Command.SET_USER_POWER_LEVEL.command -> { + Command.SET_USER_POWER_LEVEL.matches(slashCommand) -> { if (messageParts.size == 3) { val userId = messageParts[1] if (MatrixPatterns.isUserId(userId)) { @@ -300,7 +289,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.SET_USER_POWER_LEVEL) } } - Command.RESET_USER_POWER_LEVEL.command -> { + Command.RESET_USER_POWER_LEVEL.matches(slashCommand) -> { if (messageParts.size == 2) { val userId = messageParts[1] @@ -313,7 +302,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.SET_USER_POWER_LEVEL) } } - Command.MARKDOWN.command -> { + Command.MARKDOWN.matches(slashCommand) -> { if (messageParts.size == 2) { when { "on".equals(messageParts[1], true) -> ParsedCommand.SetMarkdown(true) @@ -324,31 +313,34 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.MARKDOWN) } } - Command.CLEAR_SCALAR_TOKEN.command -> { + Command.CLEAR_SCALAR_TOKEN.matches(slashCommand) -> { if (messageParts.size == 1) { ParsedCommand.ClearScalarToken } else { ParsedCommand.ErrorSyntax(Command.CLEAR_SCALAR_TOKEN) } } - Command.SPOILER.command -> { - val message = textMessage.substring(Command.SPOILER.command.length).trim() - ParsedCommand.SendSpoiler(message) + Command.SPOILER.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.SendSpoiler(message) + } else { + ParsedCommand.ErrorSyntax(Command.SPOILER) + } } - Command.SHRUG.command -> { - val message = textMessage.substring(Command.SHRUG.command.length).trim() - + Command.SHRUG.matches(slashCommand) -> { ParsedCommand.SendShrug(message) } - Command.LENNY.command -> { - val message = textMessage.substring(Command.LENNY.command.length).trim() - + Command.LENNY.matches(slashCommand) -> { ParsedCommand.SendLenny(message) } - Command.DISCARD_SESSION.command -> { - ParsedCommand.DiscardSession + Command.DISCARD_SESSION.matches(slashCommand) -> { + if (messageParts.size == 1) { + ParsedCommand.DiscardSession + } else { + ParsedCommand.ErrorSyntax(Command.DISCARD_SESSION) + } } - Command.WHOIS.command -> { + Command.WHOIS.matches(slashCommand) -> { if (messageParts.size == 2) { val userId = messageParts[1] @@ -361,57 +353,49 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.WHOIS) } } - Command.CONFETTI.command -> { - val message = textMessage.substring(Command.CONFETTI.command.length).trim() + Command.CONFETTI.matches(slashCommand) -> { ParsedCommand.SendChatEffect(ChatEffect.CONFETTI, message) } - Command.SNOWFALL.command -> { - val message = textMessage.substring(Command.SNOWFALL.command.length).trim() + Command.SNOWFALL.matches(slashCommand) -> { ParsedCommand.SendChatEffect(ChatEffect.SNOWFALL, message) } - Command.CREATE_SPACE.command -> { - val rawCommand = textMessage.substring(Command.CREATE_SPACE.command.length).trim() - val split = rawCommand.split(" ").map { it.trim() } - if (split.isEmpty()) { - ParsedCommand.ErrorSyntax(Command.CREATE_SPACE) - } else { + Command.CREATE_SPACE.matches(slashCommand) -> { + if (messageParts.size >= 2) { ParsedCommand.CreateSpace( - split[0], - split.subList(1, split.size) + messageParts[1], + messageParts.drop(2) ) - } - } - Command.ADD_TO_SPACE.command -> { - val rawCommand = textMessage.substring(Command.ADD_TO_SPACE.command.length).trim() - ParsedCommand.AddToSpace( - rawCommand - ) - } - Command.JOIN_SPACE.command -> { - val spaceIdOrAlias = textMessage.substring(Command.JOIN_SPACE.command.length).trim() - ParsedCommand.JoinSpace( - spaceIdOrAlias - ) - } - Command.LEAVE_ROOM.command -> { - val spaceIdOrAlias = textMessage.substring(Command.LEAVE_ROOM.command.length).trim() - ParsedCommand.LeaveRoom( - spaceIdOrAlias - ) - } - Command.UPGRADE_ROOM.command -> { - val newVersion = textMessage.substring(Command.UPGRADE_ROOM.command.length).trim() - if (newVersion.isEmpty()) { - ParsedCommand.ErrorSyntax(Command.UPGRADE_ROOM) } else { - ParsedCommand.UpgradeRoom(newVersion) + ParsedCommand.ErrorSyntax(Command.CREATE_SPACE) } } - else -> { + Command.ADD_TO_SPACE.matches(slashCommand) -> { + ParsedCommand.AddToSpace(spaceId = message) + } + Command.JOIN_SPACE.matches(slashCommand) -> { + ParsedCommand.JoinSpace(spaceIdOrAlias = message) + } + Command.LEAVE_ROOM.matches(slashCommand) -> { + ParsedCommand.LeaveRoom(roomId = message) + } + Command.UPGRADE_ROOM.matches(slashCommand) -> { + if (message.isNotEmpty()) { + ParsedCommand.UpgradeRoom(newVersion = message) + } else { + ParsedCommand.ErrorSyntax(Command.UPGRADE_ROOM) + } + } + else -> { // Unknown command ParsedCommand.ErrorUnknownSlashCommand(slashCommand) } } } } + + private fun trimParts(message: CharSequence, messageParts: List): String? { + val partsSize = messageParts.sumOf { it.length } + val gapsNumber = messageParts.size - 1 + return message.substring(partsSize + gapsNumber).trim().takeIf { it.isNotEmpty() } + } } 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 566cb2d2de..eaa684dea1 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 @@ -2098,7 +2098,7 @@ class RoomDetailFragment @Inject constructor( userId == session.myUserId) { // Empty composer, current user: start an emote views.composerLayout.views.composerEditText.setText(Command.EMOTE.command + " ") - views.composerLayout.views.composerEditText.setSelection(Command.EMOTE.length) + views.composerLayout.views.composerEditText.setSelection(Command.EMOTE.command.length + 1) } else { val roomMember = roomDetailViewModel.getMember(userId) // TODO move logic outside of fragment diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index d787978aa9..e907db0328 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -161,7 +161,7 @@ class MessageComposerViewModel @AssistedInject constructor( withState { state -> when (state.sendMode) { is SendMode.Regular -> { - when (val slashCommandResult = CommandParser.parseSplashCommand(action.text)) { + when (val slashCommandResult = CommandParser.parseSlashCommand(action.text)) { is ParsedCommand.ErrorNotACommand -> { // Send the text message to the room room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown) From 9fa38c5cc5d27d00c1e9faf74356f214e50d4ee3 Mon Sep 17 00:00:00 2001 From: fedrunov Date: Tue, 11 Jan 2022 15:50:51 +0100 Subject: [PATCH 032/281] web version aliases --- .../src/main/java/im/vector/app/features/command/Command.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/command/Command.kt b/vector/src/main/java/im/vector/app/features/command/Command.kt index 4edd62a488..385cf1f3db 100644 --- a/vector/src/main/java/im/vector/app/features/command/Command.kt +++ b/vector/src/main/java/im/vector/app/features/command/Command.kt @@ -34,12 +34,12 @@ enum class Command(val command: String, val aliases: Array?, val p RESET_USER_POWER_LEVEL("/deop", null, "", R.string.command_description_deop_user, false), ROOM_NAME("/roomname", null, "", R.string.command_description_room_name, false), INVITE("/invite", null, " [reason]", R.string.command_description_invite_user, false), - JOIN_ROOM("/join", null, " [reason]", R.string.command_description_join_room, false), + JOIN_ROOM("/join", arrayOf("/j", "/goto"), " [reason]", R.string.command_description_join_room, false), PART("/part", null, "[]", R.string.command_description_part_room, false), TOPIC("/topic", null, "", R.string.command_description_topic, false), REMOVE_USER("/remove", arrayOf("/kick"), " [reason]", R.string.command_description_kick_user, false), CHANGE_DISPLAY_NAME("/nick", null, "", R.string.command_description_nick, false), - CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", null, "", R.string.command_description_nick_for_room, false), + CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", arrayOf("/roomnick"), "", R.string.command_description_nick_for_room, false), ROOM_AVATAR("/roomavatar", null, "", R.string.command_description_room_avatar, true /* Since user has to know the mxc url */), CHANGE_AVATAR_FOR_ROOM("/myroomavatar", null, "", R.string.command_description_avatar_for_room, true /* Since user has to know the mxc url */), MARKDOWN("/markdown", null, "", R.string.command_description_markdown, false), From 672d4e591c7b01b7d1e03b32eabe7aaa2901ed7f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 11 Jan 2022 16:54:37 +0000 Subject: [PATCH 033/281] disabling the automatic carousel transitions on user interaction - extracts all the logic to its own extension --- .../FtueAuthSplashCarouselFragment.kt | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt index 152754f241..659508448a 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt @@ -80,17 +80,27 @@ class FtueAuthSplashCarouselFragment @Inject constructor( "Branch: ${BuildConfig.GIT_BRANCH_NAME}" views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) } } + views.splashCarousel.registerAutomaticUntilInteractionTransitions() + } - views.splashCarousel.apply { - var scheduledTransition: Job? = null - registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - scheduledTransition?.cancel() + private fun ViewPager2.registerAutomaticUntilInteractionTransitions() { + var scheduledTransition: Job? = null + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + private var hasUserManuallyInteractedWithCarousel: Boolean = false + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + hasUserManuallyInteractedWithCarousel = !isFakeDragging + } + + override fun onPageSelected(position: Int) { + scheduledTransition?.cancel() + if (hasUserManuallyInteractedWithCarousel) { + // stop the automatic transitions + } else { scheduledTransition = scheduleCarouselTransition() } - }) - scheduledTransition = scheduleCarouselTransition() - } + } + }) } private fun ViewPager2.scheduleCarouselTransition(): Job { From ce7a93bcae5a1dbb43396cf453c7667d26c66518 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 11 Jan 2022 17:28:29 +0000 Subject: [PATCH 034/281] locking phones to portait during the ftue auth onboarding flow - uses a resource bucket flag for determining if the device is big enough to be considered a tablet and in turn, enable a landscape experience --- .../src/main/res/values-sw600dp/tablet.xml | 1 + .../ui-styles/src/main/res/values/tablet.xml | 1 + .../core/platform/ScreenOrientationLocker.kt | 42 +++++++++++++++++++ .../onboarding/OnboardingVariantFactory.kt | 5 ++- .../onboarding/ftueauth/FtueAuthVariant.kt | 6 ++- 5 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/platform/ScreenOrientationLocker.kt diff --git a/library/ui-styles/src/main/res/values-sw600dp/tablet.xml b/library/ui-styles/src/main/res/values-sw600dp/tablet.xml index 39f467cf0d..86bab06371 100644 --- a/library/ui-styles/src/main/res/values-sw600dp/tablet.xml +++ b/library/ui-styles/src/main/res/values-sw600dp/tablet.xml @@ -2,5 +2,6 @@ 0.6 + true \ No newline at end of file diff --git a/library/ui-styles/src/main/res/values/tablet.xml b/library/ui-styles/src/main/res/values/tablet.xml index a5df8fe17c..8460f0ccf8 100644 --- a/library/ui-styles/src/main/res/values/tablet.xml +++ b/library/ui-styles/src/main/res/values/tablet.xml @@ -2,5 +2,6 @@ 1 + false \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/platform/ScreenOrientationLocker.kt b/vector/src/main/java/im/vector/app/core/platform/ScreenOrientationLocker.kt new file mode 100644 index 0000000000..4b62090d3f --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/platform/ScreenOrientationLocker.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 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.core.platform + +import android.annotation.SuppressLint +import android.content.pm.ActivityInfo +import android.content.res.Resources +import androidx.appcompat.app.AppCompatActivity +import im.vector.app.R +import javax.inject.Inject + +class ScreenOrientationLocker @Inject constructor( + private val resources: Resources +) { + + // Some screens do not provide enough value for us to provide phone landscape experiences + @SuppressLint("SourceLockedOrientationActivity") + fun lockPhonesToPortrait(activity: AppCompatActivity) { + when (resources.getBoolean(R.bool.is_tablet)) { + true -> { + // do nothing + } + false -> { + activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingVariantFactory.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingVariantFactory.kt index c171fc223d..52423d7019 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingVariantFactory.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingVariantFactory.kt @@ -16,6 +16,7 @@ package im.vector.app.features.onboarding +import im.vector.app.core.platform.ScreenOrientationLocker import im.vector.app.databinding.ActivityLoginBinding import im.vector.app.features.VectorFeatures import im.vector.app.features.login2.LoginViewModel2 @@ -24,6 +25,7 @@ import javax.inject.Inject class OnboardingVariantFactory @Inject constructor( private val vectorFeatures: VectorFeatures, + private val orientationLocker: ScreenOrientationLocker, ) { fun create(activity: OnboardingActivity, @@ -37,7 +39,8 @@ class OnboardingVariantFactory @Inject constructor( onboardingViewModel = onboardingViewModel.value, activity = activity, supportFragmentManager = activity.supportFragmentManager, - vectorFeatures = vectorFeatures + vectorFeatures = vectorFeatures, + orientationLocker = orientationLocker ) VectorFeatures.OnboardingVariant.LOGIN_2 -> Login2Variant( views = views, diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt index f177eda114..06dbad50f8 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.onboarding.ftueauth + import android.content.Intent import android.view.View import android.view.ViewGroup @@ -31,6 +32,7 @@ import im.vector.app.core.extensions.POP_BACK_STACK_EXCLUSIVE import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.ScreenOrientationLocker import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityLoginBinding import im.vector.app.features.VectorFeatures @@ -62,7 +64,8 @@ class FtueAuthVariant( private val onboardingViewModel: OnboardingViewModel, private val activity: VectorBaseActivity, private val supportFragmentManager: FragmentManager, - private val vectorFeatures: VectorFeatures + private val vectorFeatures: VectorFeatures, + private val orientationLocker: ScreenOrientationLocker, ) : OnboardingVariant { private val enterAnim = R.anim.enter_fade_in @@ -91,6 +94,7 @@ class FtueAuthVariant( } with(activity) { + orientationLocker.lockPhonesToPortrait(this) onboardingViewModel.onEach { updateWithState(it) } From 1951130c43b3b607bff2948e78fda7dddf962299 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 23:07:26 +0000 Subject: [PATCH 035/281] Bump libphonenumber from 8.12.40 to 8.12.41 Bumps [libphonenumber](https://github.com/google/libphonenumber) from 8.12.40 to 8.12.41. - [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.40...v8.12.41) --- 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 c34ba8d21a..ac23de338a 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -158,7 +158,7 @@ dependencies { implementation libs.apache.commonsImaging // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.40' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41' testImplementation libs.tests.junit testImplementation 'org.robolectric:robolectric:4.7.3' diff --git a/vector/build.gradle b/vector/build.gradle index dbff6cdd11..f178813bc5 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -362,7 +362,7 @@ dependencies { implementation 'com.facebook.stetho:stetho:1.6.0' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.40' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41' // FlowBinding implementation libs.github.flowBinding From 01ef46517e0358d0446a9b13fd0612e6286c4883 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 12 Jan 2022 09:16:46 +0000 Subject: [PATCH 036/281] flattening branch to avoid empty code block --- .../onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt index 659508448a..6c1c57e0fc 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt @@ -94,9 +94,8 @@ class FtueAuthSplashCarouselFragment @Inject constructor( override fun onPageSelected(position: Int) { scheduledTransition?.cancel() - if (hasUserManuallyInteractedWithCarousel) { - // stop the automatic transitions - } else { + // only schedule automatic transitions whilst the user has not interacted with the carousel + if (!hasUserManuallyInteractedWithCarousel) { scheduledTransition = scheduleCarouselTransition() } } From c1d89c5304dfd1ae886c0fabc36725e81a04261a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Jan 2022 12:56:19 +0100 Subject: [PATCH 037/281] 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 a1fb006e88..c34ba8d21a 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.14\"" + buildConfigField "String", "SDK_VERSION", "\"1.3.16\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" diff --git a/vector/build.gradle b/vector/build.gradle index f136543a2e..dbff6cdd11 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -18,7 +18,7 @@ ext.versionMinor = 3 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -ext.versionPatch = 14 +ext.versionPatch = 16 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From 7cea0352f921b4721f4a2c3dd93ee464da3fa0f6 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 13 Dec 2021 14:21:00 +0000 Subject: [PATCH 038/281] adding debug feature flag for the splash carousel --- .../app/features/debug/features/DebugFeaturesStateFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt index 6ddbb53134..14d0fdc254 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt @@ -40,7 +40,7 @@ class DebugFeaturesStateFactory @Inject constructor( key = DebugFeatureKeys.alreadyHaveAnAccount ), createBooleanFeature( - label = "FTUE Splash - Carousel", + label = "FTUE Splash - carousel", factory = VectorFeatures::isSplashCarouselEnabled, key = DebugFeatureKeys.splashCarousel ) From 1ae112daaefce0c147428dd47cd41e004eb1218f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 6 Jan 2022 12:10:30 +0000 Subject: [PATCH 039/281] adding feature flag and entry point for the _wip_ usecase screen --- .../debug/features/DebugFeaturesStateFactory.kt | 13 +++++++++---- .../debug/features/DebugVectorFeatures.kt | 13 ++++++++----- .../im/vector/app/features/VectorFeatures.kt | 12 ++++++------ .../features/onboarding/OnboardingViewEvents.kt | 1 + .../features/onboarding/OnboardingViewModel.kt | 17 ++++++++++++++--- .../ftueauth/FtueAuthSplashCarouselFragment.kt | 4 ++-- .../ftueauth/FtueAuthSplashFragment.kt | 4 ++-- .../onboarding/ftueauth/FtueAuthVariant.kt | 6 +++++- 8 files changed, 47 insertions(+), 23 deletions(-) diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt index 14d0fdc254..fb803162a7 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt @@ -36,13 +36,18 @@ class DebugFeaturesStateFactory @Inject constructor( ), createBooleanFeature( label = "FTUE Splash - I already have an account", - factory = VectorFeatures::isAlreadyHaveAccountSplashEnabled, - key = DebugFeatureKeys.alreadyHaveAnAccount + key = DebugFeatureKeys.onboardingAlreadyHaveAnAccount, + factory = VectorFeatures::isOnboardingAlreadyHaveAccountSplashEnabled ), createBooleanFeature( label = "FTUE Splash - carousel", - factory = VectorFeatures::isSplashCarouselEnabled, - key = DebugFeatureKeys.splashCarousel + key = DebugFeatureKeys.onboardingSplashCarousel, + factory = VectorFeatures::isOnboardingSplashCarouselEnabled + ), + createBooleanFeature( + label = "FTUE Use Case", + key = DebugFeatureKeys.onboardingUseCase, + factory = VectorFeatures::isOnboardingUseCaseEnabled ) )) } diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt index 2e11017ef3..e0598067db 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt @@ -43,10 +43,12 @@ class DebugVectorFeatures( return readPreferences().getEnum() ?: vectorFeatures.onboardingVariant() } - override fun isAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.alreadyHaveAnAccount) - ?: vectorFeatures.isAlreadyHaveAccountSplashEnabled() + override fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.onboardingAlreadyHaveAnAccount) + ?: vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled() - override fun isSplashCarouselEnabled(): Boolean = read(DebugFeatureKeys.splashCarousel) ?: vectorFeatures.isSplashCarouselEnabled() + override fun isOnboardingSplashCarouselEnabled(): Boolean = read(DebugFeatureKeys.onboardingSplashCarousel) ?: vectorFeatures.isOnboardingSplashCarouselEnabled() + + override fun isOnboardingUseCaseEnabled(): Boolean = read(DebugFeatureKeys.onboardingUseCase) ?: vectorFeatures.isOnboardingUseCaseEnabled() fun override(value: T?, key: Preferences.Key) = updatePreferences { if (value == null) { @@ -96,6 +98,7 @@ private inline fun > enumPreferencesKey() = enumPreferencesK private fun > enumPreferencesKey(type: KClass) = stringPreferencesKey("enum-${type.simpleName}") object DebugFeatureKeys { - val alreadyHaveAnAccount = booleanPreferencesKey("already-have-an-account") - val splashCarousel = booleanPreferencesKey("splash-carousel") + val onboardingAlreadyHaveAnAccount = booleanPreferencesKey("onboarding-already-have-an-account") + val onboardingSplashCarousel = booleanPreferencesKey("onboarding-splash-carousel") + val onboardingUseCase = booleanPreferencesKey("onbboarding-splash-carousel") } diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt index c53ff0f433..25c204f2ef 100644 --- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -21,10 +21,9 @@ import im.vector.app.BuildConfig interface VectorFeatures { fun onboardingVariant(): OnboardingVariant - - fun isAlreadyHaveAccountSplashEnabled(): Boolean - - fun isSplashCarouselEnabled(): Boolean + fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean + fun isOnboardingSplashCarouselEnabled(): Boolean + fun isOnboardingUseCaseEnabled(): Boolean enum class OnboardingVariant { LEGACY, @@ -35,6 +34,7 @@ interface VectorFeatures { class DefaultVectorFeatures : VectorFeatures { override fun onboardingVariant(): VectorFeatures.OnboardingVariant = BuildConfig.ONBOARDING_VARIANT - override fun isAlreadyHaveAccountSplashEnabled() = true - override fun isSplashCarouselEnabled() = false + override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true + override fun isOnboardingSplashCarouselEnabled() = false + override fun isOnboardingUseCaseEnabled() = false } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt index ab782a9908..d6105cda13 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt @@ -34,6 +34,7 @@ sealed class OnboardingViewEvents : VectorViewEvents { // Navigation event + object OpenUseCaseSelection : OnboardingViewEvents() object OpenServerSelection : OnboardingViewEvents() data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents() object OnLoginFlowRetrieved : OnboardingViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 5d1e0fdade..39c2e9bcdb 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -35,6 +35,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.core.utils.ensureTrailingSlash +import im.vector.app.features.VectorFeatures import im.vector.app.features.login.HomeServerConnectionConfigFactory import im.vector.app.features.login.LoginConfig import im.vector.app.features.login.LoginMode @@ -71,7 +72,8 @@ class OnboardingViewModel @AssistedInject constructor( private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory, private val reAuthHelper: ReAuthHelper, private val stringProvider: StringProvider, - private val homeServerHistoryService: HomeServerHistoryService + private val homeServerHistoryService: HomeServerHistoryService, + private val vectorFeatures: VectorFeatures ) : VectorViewModel(initialState) { @AssistedFactory @@ -154,15 +156,24 @@ class OnboardingViewModel @AssistedInject constructor( if (homeServerConnectionConfig == null) { // Url is invalid, in this case, just use the regular flow Timber.w("Url from config url was invalid: $configUrl") - _viewEvents.post(OnboardingViewEvents.OpenServerSelection) + continueToPageAfterSplash() } else { getLoginFlow(homeServerConnectionConfig, ServerType.Other) } } else { - _viewEvents.post(OnboardingViewEvents.OpenServerSelection) + continueToPageAfterSplash() } } + private fun continueToPageAfterSplash() { + val nextOnboardingStep = if (vectorFeatures.isOnboardingUseCaseEnabled()) { + OnboardingViewEvents.OpenUseCaseSelection + } else { + OnboardingViewEvents.OpenServerSelection + } + _viewEvents.post(nextOnboardingStep) + } + private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) { // It happens when we get the login flow, or during direct authentication. // So alter the homeserver config and retrieve again the login flow diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt index 152754f241..f946e4828c 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashCarouselFragment.kt @@ -69,7 +69,7 @@ class FtueAuthSplashCarouselFragment @Inject constructor( views.loginSplashSubmit.debouncedClicks { getStarted() } views.loginSplashAlreadyHaveAccount.apply { - isVisible = vectorFeatures.isAlreadyHaveAccountSplashEnabled() + isVisible = vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled() debouncedClicks { alreadyHaveAnAccount() } } @@ -102,7 +102,7 @@ class FtueAuthSplashCarouselFragment @Inject constructor( } private fun getStarted() { - val getStartedFlow = if (vectorFeatures.isAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp + val getStartedFlow = if (vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp viewModel.handle(OnboardingAction.OnGetStarted(resetLoginConfig = false, onboardingFlow = getStartedFlow)) } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt index f8f1d7919b..fd63889fd6 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt @@ -55,7 +55,7 @@ class FtueAuthSplashFragment @Inject constructor( private fun setupViews() { views.loginSplashSubmit.debouncedClicks { getStarted() } views.loginSplashAlreadyHaveAccount.apply { - isVisible = vectorFeatures.isAlreadyHaveAccountSplashEnabled() + isVisible = vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled() debouncedClicks { alreadyHaveAnAccount() } } @@ -70,7 +70,7 @@ class FtueAuthSplashFragment @Inject constructor( } private fun getStarted() { - val getStartedFlow = if (vectorFeatures.isAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp + val getStartedFlow = if (vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp viewModel.handle(OnboardingAction.OnGetStarted(resetLoginConfig = false, onboardingFlow = getStartedFlow)) } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt index f177eda114..08258062e7 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.onboarding.ftueauth + import android.content.Intent import android.view.View import android.view.ViewGroup @@ -109,7 +110,7 @@ class FtueAuthVariant( } private fun addFirstFragment() { - val splashFragment = when (vectorFeatures.isSplashCarouselEnabled()) { + val splashFragment = when (vectorFeatures.isOnboardingSplashCarouselEnabled()) { true -> FtueAuthSplashCarouselFragment::class.java else -> FtueAuthSplashFragment::class.java } @@ -208,6 +209,9 @@ class FtueAuthVariant( is OnboardingViewEvents.Loading -> // This is handled by the Fragments Unit + OnboardingViewEvents.OpenUseCaseSelection -> { + TODO() + } }.exhaustive } From b6ff6aa4cc43dcf1f6b49b91335fde9e8a7c9433 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 7 Jan 2022 12:56:06 +0000 Subject: [PATCH 040/281] adding barebones ftue use case fragment --- .../im/vector/app/core/di/FragmentModule.kt | 6 + .../features/onboarding/OnboardingAction.kt | 1 + .../onboarding/OnboardingViewModel.kt | 7 + .../ftueauth/FtueAuthUseCaseFragment.kt | 54 +++++++ .../onboarding/ftueauth/FtueAuthVariant.kt | 4 +- .../src/main/res/drawable/ic_communities.xml | 18 +++ .../res/drawable/ic_friends_and_family.xml | 16 ++ .../drawable/ic_onboarding_use_case_icon.xml | 14 ++ vector/src/main/res/drawable/ic_teams.xml | 26 ++++ .../layout/fragment_ftue_auth_use_case.xml | 144 ++++++++++++++++++ 10 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt create mode 100644 vector/src/main/res/drawable/ic_communities.xml create mode 100644 vector/src/main/res/drawable/ic_friends_and_family.xml create mode 100644 vector/src/main/res/drawable/ic_onboarding_use_case_icon.xml create mode 100644 vector/src/main/res/drawable/ic_teams.xml create mode 100644 vector/src/main/res/layout/fragment_ftue_auth_use_case.xml diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index c27309fad6..626cf90592 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -104,6 +104,7 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthServerSelectionFragmen import im.vector.app.features.onboarding.ftueauth.FtueAuthSignUpSignInSelectionFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthSplashCarouselFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthSplashFragment +import im.vector.app.features.onboarding.ftueauth.FtueAuthUseCaseFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthWaitForEmailFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthWebFragment import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsFragment @@ -449,6 +450,11 @@ interface FragmentModule { @FragmentKey(FtueAuthSplashCarouselFragment::class) fun bindFtueAuthSplashCarouselFragment(fragment: FtueAuthSplashCarouselFragment): Fragment + @Binds + @IntoMap + @FragmentKey(FtueAuthUseCaseFragment::class) + fun bindFtueAuthUseCaseFragment(fragment: FtueAuthUseCaseFragment): Fragment + @Binds @IntoMap @FragmentKey(FtueAuthWaitForEmailFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt index bb1d3cc52d..ea549b214a 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt @@ -31,6 +31,7 @@ sealed class OnboardingAction : VectorViewModelAction { data class UpdateServerType(val serverType: ServerType) : OnboardingAction() data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction() + data class UpdateUseCase(val todo: String) : OnboardingAction() data class UpdateSignMode(val signMode: SignMode) : OnboardingAction() data class LoginWithToken(val loginToken: String) : OnboardingAction() data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 39c2e9bcdb..5a098ebb56 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -125,6 +125,7 @@ class OnboardingViewModel @AssistedInject constructor( when (action) { is OnboardingAction.OnGetStarted -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) is OnboardingAction.OnIAlreadyHaveAnAccount -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) + is OnboardingAction.UpdateUseCase -> handleUpdateUseCase(action) is OnboardingAction.UpdateServerType -> handleUpdateServerType(action) is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action) is OnboardingAction.InitWith -> handleInitWith(action) @@ -452,6 +453,12 @@ class OnboardingViewModel @AssistedInject constructor( } } + private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { + // TODO act on the use case selection + action.todo + _viewEvents.post(OnboardingViewEvents.OpenServerSelection) + } + private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) { setState { copy( diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt new file mode 100644 index 0000000000..13304bf124 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt @@ -0,0 +1,54 @@ +/* + * 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.onboarding.ftueauth + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import im.vector.app.databinding.FragmentFtueAuthUseCaseBinding +import im.vector.app.features.login.ServerType +import im.vector.app.features.onboarding.OnboardingAction +import javax.inject.Inject + +class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment() { + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueAuthUseCaseBinding { + return FragmentFtueAuthUseCaseBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupViews() + } + + private fun setupViews() { + views.useCaseOptionOne.debouncedClicks { + viewModel.handle(OnboardingAction.UpdateUseCase("todo")) + } + views.useCaseOptionTwo.debouncedClicks { + viewModel.handle(OnboardingAction.UpdateUseCase("todo")) + } + views.useCaseOptionThree.debouncedClicks { + viewModel.handle(OnboardingAction.UpdateUseCase("todo")) + } + } + + override fun resetViewModel() { + // Nothing to do + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt index 08258062e7..4ef99e355e 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt @@ -210,7 +210,9 @@ class FtueAuthVariant( // This is handled by the Fragments Unit OnboardingViewEvents.OpenUseCaseSelection -> { - TODO() + activity.addFragmentToBackstack(views.loginFragmentContainer, + FtueAuthUseCaseFragment::class.java, + option = commonOption) } }.exhaustive } diff --git a/vector/src/main/res/drawable/ic_communities.xml b/vector/src/main/res/drawable/ic_communities.xml new file mode 100644 index 0000000000..f550de8106 --- /dev/null +++ b/vector/src/main/res/drawable/ic_communities.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/vector/src/main/res/drawable/ic_friends_and_family.xml b/vector/src/main/res/drawable/ic_friends_and_family.xml new file mode 100644 index 0000000000..d7ac86f240 --- /dev/null +++ b/vector/src/main/res/drawable/ic_friends_and_family.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/vector/src/main/res/drawable/ic_onboarding_use_case_icon.xml b/vector/src/main/res/drawable/ic_onboarding_use_case_icon.xml new file mode 100644 index 0000000000..35b45aa69a --- /dev/null +++ b/vector/src/main/res/drawable/ic_onboarding_use_case_icon.xml @@ -0,0 +1,14 @@ + + + + diff --git a/vector/src/main/res/drawable/ic_teams.xml b/vector/src/main/res/drawable/ic_teams.xml new file mode 100644 index 0000000000..8745cfd2d4 --- /dev/null +++ b/vector/src/main/res/drawable/ic_teams.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml new file mode 100644 index 0000000000..fbc4db43be --- /dev/null +++ b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 00bbede8025e3a5d399727628ba1709aae8bb58c Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 10 Jan 2022 11:57:05 +0000 Subject: [PATCH 041/281] respecting the underline parameter by only applying an underline when it's set - updates the default value to true and that was the existing intentional behaviour --- vector/src/main/java/im/vector/app/core/extensions/TextView.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt index adb655f169..048dddf9e5 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt @@ -82,7 +82,7 @@ fun TextView.setTextWithColoredPart(@StringRes fullTextRes: Int, fun TextView.setTextWithColoredPart(fullText: String, coloredPart: String, @AttrRes colorAttribute: Int = R.attr.colorPrimary, - underline: Boolean = false, + underline: Boolean = true, onClick: (() -> Unit)? = null) { val color = ThemeUtils.getColor(context, colorAttribute) @@ -101,7 +101,6 @@ fun TextView.setTextWithColoredPart(fullText: String, override fun updateDrawState(ds: TextPaint) { ds.color = color - ds.isUnderlineText = !underline } } setSpan(clickableSpan, index, index + coloredPart.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) From 3d4caeaa75c6eabc49bb7ab7065bcf1341ea0d61 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 10 Jan 2022 11:57:26 +0000 Subject: [PATCH 042/281] extracting use case copy to the resources --- .../ftueauth/FtueAuthUseCaseFragment.kt | 16 +++++++ .../layout/fragment_ftue_auth_use_case.xml | 43 ++++++++++++++----- vector/src/main/res/values/strings.xml | 10 +++++ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt index 13304bf124..67476077bd 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt @@ -20,9 +20,12 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import im.vector.app.R +import im.vector.app.core.extensions.setTextWithColoredPart import im.vector.app.databinding.FragmentFtueAuthUseCaseBinding import im.vector.app.features.login.ServerType import im.vector.app.features.onboarding.OnboardingAction +import me.saket.bettermovementmethod.BetterLinkMovementMethod import javax.inject.Inject class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment() { @@ -46,6 +49,19 @@ class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment + + + app:layout_constraintTop_toBottomOf="@id/titleContentSpacing" /> Cut the slack from teams. As universal as email, Element is a completely new type of collaboration. + Who will you chat to the most? + We\'ll help you get connected. + Friends and family + Teams + Communities + Not sure yet? %s + You can skip this question + Looking to join an existing server? + Connect to server + It\'s your conversation. Own it. Chat with people directly or in groups Keep conversations private with encryption From 8c67cc007661c8dcdf5911a406350aa93fc5d330 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 10 Jan 2022 12:07:55 +0000 Subject: [PATCH 043/281] only showing the use case screen for the sign up flow --- .../features/onboarding/OnboardingViewModel.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 5a098ebb56..a61db35832 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -157,20 +157,24 @@ class OnboardingViewModel @AssistedInject constructor( if (homeServerConnectionConfig == null) { // Url is invalid, in this case, just use the regular flow Timber.w("Url from config url was invalid: $configUrl") - continueToPageAfterSplash() + continueToPageAfterSplash(onboardingFlow) } else { getLoginFlow(homeServerConnectionConfig, ServerType.Other) } } else { - continueToPageAfterSplash() + continueToPageAfterSplash(onboardingFlow) } } - private fun continueToPageAfterSplash() { - val nextOnboardingStep = if (vectorFeatures.isOnboardingUseCaseEnabled()) { - OnboardingViewEvents.OpenUseCaseSelection - } else { - OnboardingViewEvents.OpenServerSelection + private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) { + val nextOnboardingStep = when (onboardingFlow) { + OnboardingFlow.SignUp -> if (vectorFeatures.isOnboardingUseCaseEnabled()) { + OnboardingViewEvents.OpenUseCaseSelection + } else { + OnboardingViewEvents.OpenServerSelection + } + OnboardingFlow.SignIn, + OnboardingFlow.SignInSignUp -> OnboardingViewEvents.OpenServerSelection } _viewEvents.post(nextOnboardingStep) } From 0ba6f55ad4cb71291036c2c2ba2eb2d66734cf0a Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 10 Jan 2022 13:06:44 +0000 Subject: [PATCH 044/281] aligning the use case image padding to the other onboarding pages --- vector/src/main/res/layout/fragment_ftue_auth_use_case.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml index ab1b7f1718..1f2968a428 100644 --- a/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml +++ b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml @@ -25,7 +25,7 @@ android:id="@+id/useCaseHeaderIcon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="36dp" + android:layout_marginTop="52dp" android:contentDescription="@null" android:src="@drawable/ic_onboarding_use_case_icon" app:layout_constraintBottom_toTopOf="@id/useCaseHeaderTitle" From 878371cd9a6d50171b8e5ec7b7cee8cb058b6df8 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 10 Jan 2022 13:07:12 +0000 Subject: [PATCH 045/281] fading the server selection when coming from the use case page --- .../onboarding/ftueauth/FtueAuthVariant.kt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt index 4ef99e355e..eab6591984 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt @@ -152,13 +152,16 @@ class FtueAuthVariant( activity.addFragmentToBackstack(views.loginFragmentContainer, FtueAuthServerSelectionFragment::class.java, option = { ft -> - activity.findViewById(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // Disable transition of text - // findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // No transition here now actually - // findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // TODO Disabled because it provokes a flickering - // ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) + if (vectorFeatures.isOnboardingUseCaseEnabled()) { + ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) + } else { + activity.findViewById(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // TODO Disabled because it provokes a flickering + // Disable transition of text + // findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // No transition here now actually + // findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + } }) is OnboardingViewEvents.OnServerSelectionDone -> onServerSelectionDone(viewEvents) is OnboardingViewEvents.OnSignModeSelected -> onSignModeSelected(viewEvents) From ae8ca5356ff34947e3f11642f04ef8b7b467a2b8 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 12 Jan 2022 15:14:13 +0300 Subject: [PATCH 046/281] Fix recyclerview issues on async map functions. --- .../detail/timeline/factory/MessageItemFactory.kt | 9 +++------ .../room/detail/timeline/item/MessageLocationItem.kt | 12 ++++++++---- .../res/layout/item_timeline_event_location_stub.xml | 9 +++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 51aa51f070..4070532f80 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -73,7 +73,6 @@ import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.html.SpanUtils import im.vector.app.features.html.VectorHtmlCompressor import im.vector.app.features.location.LocationData -import im.vector.app.features.location.VectorMapView import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import me.gujun.android.span.span @@ -192,11 +191,9 @@ class MessageItemFactory @Inject constructor( val locationData = LocationData.create(geoUri) val mapCallback: MessageLocationItem.Callback = object : MessageLocationItem.Callback { - override fun onMapReady(mapView: VectorMapView) { - mapView.onClick { - locationData?.let { - callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(it, informationData.senderId)) - } + override fun onMapClicked() { + locationData?.let { + callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(it, informationData.senderId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index cb37901245..d8ccf3761e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -16,21 +16,22 @@ package im.vector.app.features.home.room.detail.timeline.item +import android.widget.FrameLayout import androidx.constraintlayout.widget.ConstraintLayout import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R +import im.vector.app.core.epoxy.onClick import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.location.LocationData import im.vector.app.features.location.MapTilerMapView import im.vector.app.features.location.VectorMapListener -import im.vector.app.features.location.VectorMapView @EpoxyModelClass(layout = R.layout.item_timeline_event_base) abstract class MessageLocationItem : AbsMessageItem() { interface Callback { - fun onMapReady(mapView: VectorMapView) + fun onMapClicked() } @EpoxyAttribute @@ -52,6 +53,10 @@ abstract class MessageLocationItem : AbsMessageItem( val location = locationData ?: return val locationOwnerId = userId ?: return + holder.clickableMapArea.onClick { + callback?.onMapClicked() + } + holder.mapView.apply { initialize(object : VectorMapListener { override fun onMapReady() { @@ -61,8 +66,6 @@ abstract class MessageLocationItem : AbsMessageItem( addPinToMap(locationOwnerId, pinDrawable) updatePinLocation(locationOwnerId, location.latitude, location.longitude) } - - callback?.onMapReady(this@apply) } }) } @@ -73,6 +76,7 @@ abstract class MessageLocationItem : AbsMessageItem( class Holder : AbsMessageItem.Holder(STUB_ID) { val mapViewContainer by bind(R.id.mapViewContainer) val mapView by bind(R.id.mapView) + val clickableMapArea by bind(R.id.clickableMapArea) } companion object { diff --git a/vector/src/main/res/layout/item_timeline_event_location_stub.xml b/vector/src/main/res/layout/item_timeline_event_location_stub.xml index b126231ba1..0b211ec5e0 100644 --- a/vector/src/main/res/layout/item_timeline_event_location_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_location_stub.xml @@ -14,4 +14,13 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + From c29dc8975693cdd342f924f441510f1f15de1b90 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 10 Jan 2022 13:07:46 +0000 Subject: [PATCH 047/281] typing the use case selections and binding the label and usecase type at the same time for visibility --- .../features/onboarding/OnboardingAction.kt | 10 +++++++- .../onboarding/OnboardingViewModel.kt | 2 +- .../ftueauth/FtueAuthUseCaseFragment.kt | 25 +++++++++++-------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt index ea549b214a..9ce84211f1 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt @@ -31,7 +31,15 @@ sealed class OnboardingAction : VectorViewModelAction { data class UpdateServerType(val serverType: ServerType) : OnboardingAction() data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction() - data class UpdateUseCase(val todo: String) : OnboardingAction() + data class UpdateUseCase(val useCase: UseCase) : OnboardingAction() { + enum class UseCase { + FRIENDS_FAMILY, + TEAMS, + COMMUNITIES, + SKIP + } + } + data class UpdateSignMode(val signMode: SignMode) : OnboardingAction() data class LoginWithToken(val loginToken: String) : OnboardingAction() data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index a61db35832..0fa0a34dc6 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -459,7 +459,7 @@ class OnboardingViewModel @AssistedInject constructor( private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { // TODO act on the use case selection - action.todo + action.useCase _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt index 67476077bd..39e8c3d8e0 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt @@ -20,12 +20,14 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TextView +import androidx.annotation.StringRes import im.vector.app.R import im.vector.app.core.extensions.setTextWithColoredPart import im.vector.app.databinding.FragmentFtueAuthUseCaseBinding import im.vector.app.features.login.ServerType import im.vector.app.features.onboarding.OnboardingAction -import me.saket.bettermovementmethod.BetterLinkMovementMethod +import im.vector.app.features.onboarding.OnboardingAction.UpdateUseCase.UseCase import javax.inject.Inject class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment() { @@ -40,15 +42,9 @@ class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment Date: Mon, 10 Jan 2022 14:35:42 +0000 Subject: [PATCH 048/281] removing unused action instance (will be needed once we decide how to act on the use caseselection) --- .../im/vector/app/features/onboarding/OnboardingViewModel.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 0fa0a34dc6..5e0c294b3e 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -125,7 +125,7 @@ class OnboardingViewModel @AssistedInject constructor( when (action) { is OnboardingAction.OnGetStarted -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) is OnboardingAction.OnIAlreadyHaveAnAccount -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) - is OnboardingAction.UpdateUseCase -> handleUpdateUseCase(action) + is OnboardingAction.UpdateUseCase -> handleUpdateUseCase() is OnboardingAction.UpdateServerType -> handleUpdateServerType(action) is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action) is OnboardingAction.InitWith -> handleInitWith(action) @@ -457,9 +457,8 @@ class OnboardingViewModel @AssistedInject constructor( } } - private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { + private fun handleUpdateUseCase() { // TODO act on the use case selection - action.useCase _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } From ea6347b1c78a6f386c5c22b932f2bb553059f396 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Jan 2022 14:06:17 +0100 Subject: [PATCH 049/281] Add signing config for the release buildType. No secret added To be able to test the release build sooner during the release process. --- changelog.d/4926.misc | 1 + vector/build.gradle | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 changelog.d/4926.misc diff --git a/changelog.d/4926.misc b/changelog.d/4926.misc new file mode 100644 index 0000000000..c84ff0d9eb --- /dev/null +++ b/changelog.d/4926.misc @@ -0,0 +1 @@ +Add signing config for the release buildType. No secret added \ No newline at end of file diff --git a/vector/build.gradle b/vector/build.gradle index dbff6cdd11..1960af41b1 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -215,6 +215,12 @@ android { storeFile file('./signature/debug.keystore') storePassword 'android' } + release { + keyAlias project.property("signing.element.keyId") + keyPassword project.property("signing.element.keyPassword") + storeFile file(project.property("signing.element.storePath")) + storePassword project.property("signing.element.storePassword") + } } buildTypes { @@ -245,6 +251,7 @@ android { optimizeCode true proguardFiles 'proguard-rules.pro' } + signingConfig signingConfigs.release } } From 53531bb2f30a2e344a977c72d9b039f75b5a5f49 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Jan 2022 14:21:14 +0100 Subject: [PATCH 050/281] Dummy values for signing secrets --- gradle.properties | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 23538c5285..5c99297107 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,10 @@ vector.httpLogLevel=BASIC # Note: to debug, you can put and uncomment the following lines in the file ~/.gradle/gradle.properties to override the value above #vector.debugPrivateData=true -#vector.httpLogLevel=BODY \ No newline at end of file +#vector.httpLogLevel=BODY + +# Dummy values for signing secrets +signing.element.storePath=pathTo.keystore +signing.element.storePassword=Secret +signing.element.keyId=Secret +signing.element.keyPassword=Secret From b92cb753f94347dca498ef5fe1c2c80a69816234 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Wed, 12 Jan 2022 15:31:54 +0200 Subject: [PATCH 051/281] Fix stuck local echo events at the bottom of the screen --- changelog.d/516.bugfix | 1 + .../sync/handler/room/RoomSyncHandler.kt | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 changelog.d/516.bugfix diff --git a/changelog.d/516.bugfix b/changelog.d/516.bugfix new file mode 100644 index 0000000000..7a5f745c32 --- /dev/null +++ b/changelog.d/516.bugfix @@ -0,0 +1 @@ +Fix for stuck local event messages at the bottom of the screen diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 24722445be..4a33363e82 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -129,7 +129,10 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle } else { handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountJoinedRooms, 0.6f) { handleJoinedRoom(realm, it.key, it.value, insertType, syncLocalTimeStampMillis, aggregator) + }.also { + fixStuckLocalEcho(it) } + } } is HandlingStrategy.INVITED -> @@ -478,4 +481,47 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle return result } + + /** + * There are multiple issues like #516 that report stuck local echo events + * at the bottom of each room timeline. + * + * That can happen when a message is SENT but not received back from the /sync. + * Until now we use unsignedData.transactionId to determine whether or not the local + * event should be deleted on every /sync. However, this is partially correct, lets have a look + * at the following scenario: + * + * [There is no Internet connection] --> [10 Messages are sent] --> [The 10 messages are in the queue] --> + * [Internet comes back for 1 second] --> [3 messages are sent] --> [Internet drops again] --> + * [No /sync response is triggered | home server can even replied with /sync but never arrived while we are offline] + * + * So the state until now is that we have 7 pending events to send and 3 sent but not received them back from /sync + * Subsequently, those 3 local messages will not be deleted while there is no transactionId from the /sync + * + * lets continue: + * [Now lets assume that in the same room another user sent 15 events] --> + * [We are finally back online!] --> + * [We will receive the 10 latest events for the room and of course sent the pending 7 messages] --> + * Now /sync response will NOT contain the 3 local messages so our events will stuck in the device. + * + * Someone can say, yes but it will come with the rooms/{roomId}/messages while paginating, + * so the problem will be solved. No that is not the case for two reasons: + * 1. rooms/{roomId}/messages response do not contain the unsignedData.transactionId so we cannot know which event + * to delete + * 2. even if transactionId was there, currently we are not deleting it from the pagination + * + * --------------------------------------------------------------------------------------------- + * While we cannot know when a specific event arrived from the pagination (no transactionId included), after each room /sync + * we clear all SENT events, and we are sure that we will receive it from /sync or pagination + */ + private fun fixStuckLocalEcho(rooms: List){ + rooms.forEach { roomEntity -> + roomEntity.sendingTimelineEvents.filter { timelineEvent -> + timelineEvent.root?.sendState == SendState.SENT + }.forEach { + roomEntity.sendingTimelineEvents.remove(it) + it.deleteOnCascade(true) + } + } + } } From 42e6c54e8f5641580889db58b0292ce6bf205906 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 12 Jan 2022 13:51:53 +0000 Subject: [PATCH 052/281] fixing line length --- .../vector/app/features/debug/features/DebugVectorFeatures.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt index e0598067db..6ca33ca968 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt @@ -46,7 +46,8 @@ class DebugVectorFeatures( override fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.onboardingAlreadyHaveAnAccount) ?: vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled() - override fun isOnboardingSplashCarouselEnabled(): Boolean = read(DebugFeatureKeys.onboardingSplashCarousel) ?: vectorFeatures.isOnboardingSplashCarouselEnabled() + override fun isOnboardingSplashCarouselEnabled(): Boolean = read(DebugFeatureKeys.onboardingSplashCarousel) + ?: vectorFeatures.isOnboardingSplashCarouselEnabled() override fun isOnboardingUseCaseEnabled(): Boolean = read(DebugFeatureKeys.onboardingUseCase) ?: vectorFeatures.isOnboardingUseCaseEnabled() From 6f5f773360addb43dc3ee9b20c6853e60f53e600 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Wed, 12 Jan 2022 16:45:15 +0200 Subject: [PATCH 053/281] Fix kltint errors --- .../sdk/internal/session/sync/handler/room/RoomSyncHandler.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 4a33363e82..7d34d41e52 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -132,7 +132,6 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle }.also { fixStuckLocalEcho(it) } - } } is HandlingStrategy.INVITED -> @@ -514,7 +513,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle * While we cannot know when a specific event arrived from the pagination (no transactionId included), after each room /sync * we clear all SENT events, and we are sure that we will receive it from /sync or pagination */ - private fun fixStuckLocalEcho(rooms: List){ + private fun fixStuckLocalEcho(rooms: List) { rooms.forEach { roomEntity -> roomEntity.sendingTimelineEvents.filter { timelineEvent -> timelineEvent.root?.sendState == SendState.SENT From cae2011d0e2cc63cc2e2ab58e25cadc447e397fa Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Wed, 12 Jan 2022 07:02:37 +0000 Subject: [PATCH 054/281] Translated using Weblate (Czech) Currently translated at 100.0% (2729 of 2729 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 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index d02b80c78a..1b483c6adb 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -3117,4 +3117,22 @@ Restartujte aplikaci, aby se změna projevila. Povolit matematické výrazy LaTeXu Do této místnosti vám není dovoleno vstoupit + Vytvořit hlasování + Otevřít kontakty + Odeslat nálepku + Odeslání souboru + Odesílání obrázků a videí + Otevřít fotoaparát + Váš systém bude automaticky odesílat chybové protokoly, když dojde k chybě dešifrování + Automaticky nahlašovat chyby dešifrování. + Přepsat barvu přezdívky + Již mám účet + Element je stejně univerzální jako e-mail a představuje zcela nový typ spolupráce. + Zlepšete týmovou komunikaci. + Element funguje se všemi aplikacemi založenými na protokolu Matrix a dokáže se propojit i s proprietárními messengery. + Spojte se s kýmkoli. + Element vám umožňuje zvolit si, kde jsou vaše zprávy uložené, takže máte svá data pod kontrolou. + Máte vše pod kontrolou. + Koncově šifrovaná, bezpečná a nezávislá komunikace, připojená přes Matrix. + Vlastněte své konverzace. \ No newline at end of file From bc003a547b5573b4152a65386f214c4e6a605e24 Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Tue, 11 Jan 2022 20:59:03 +0000 Subject: [PATCH 055/281] Translated using Weblate (Persian) Currently translated at 99.8% (2726 of 2729 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 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index b3d5c00f2f..a60f6bcbde 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -3058,4 +3058,19 @@ برای اثربخشی تغییر، برنامه را دوباره اجرا کنید. به کار انداختن ریاضیات لاتک مجاز به پیوستن به این گروه نیستید + ایجاد نظرسنجی + گشودن آشنایان + فرستادن عکس‌برگردان + بارگذاری پرونده + فرستادن تصویرها و ویدیوها + گشودن دوربین + سامانه‌تان هنگام‌ مواجهه با خطای ناتوانی در رمزگشایی، گزارش‌ها را به صورت خودکار خواهد فرستاد + گزارش خودکار خطاهای رمزگشایی. + از پیش حساب دارم + المنت با تمامی کاره‌های مبتنی بر ماتریکس کار کرده و حتا می‌تواند به پیام‌رسان‌های مالکیتیتان پل بزند. + ارتباط با هرکسی. + المنت می‌گذارد مکان ذخیرهٔ پیام‌هایتان را برگزینید تا داده‌هایتان را کنترل کنید. + شما کنترل می‌کنید. + پیام‌رسانی رمزنگاشتهٔ سرتاسری برای ارتباط مستقل امن، وصل شده با ماتریکس. + صاحب گفت‌وگوهایتان باشید. \ No newline at end of file From 95496a0cd71963497be79d010d195e91c5335f3b Mon Sep 17 00:00:00 2001 From: Szimszon Date: Tue, 11 Jan 2022 20:43:42 +0000 Subject: [PATCH 056/281] Translated using Weblate (Hungarian) Currently translated at 99.3% (2712 of 2729 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 | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml index e8bb1c464e..1b01de628f 100644 --- a/vector/src/main/res/values-hu/strings.xml +++ b/vector/src/main/res/values-hu/strings.xml @@ -3057,4 +3057,5 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró A változások életbelépéséhez indítsd újra az alkalmazást. LaTeX matematikai szintaxis engedélyezése Nem léphetsz be ebbe a szobába + Az ön beszélgetései csak az öné. \ No newline at end of file From b12cf686ef9cef0edf047789e56223074dd955a8 Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 11 Jan 2022 13:50:19 +0000 Subject: [PATCH 057/281] Translated using Weblate (Indonesian) Currently translated at 100.0% (2729 of 2729 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 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 4f774bac76..4502d15c23 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -3003,4 +3003,22 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.
Mulai ulang aplikasi ini untuk menerapkan perubahan. Aktifkan matematika LaTeX Anda tidak diperbolehkan untuk bergabung ke ruangan ini + Sistem Anda akan mengirimkan log secara otomatis ketika sebuah kesalahan dekripsi terjadi + Buat poll + Buka kontak + Kirim stiker + Unggah file + Kirim gambar dan video + Buka kamera + Laporkan Kesalahan Dekripsi Secara Otomatis. + Ubah warna nama tampilan + Saya sudah punya akun + Universal seperti email, Element adalah jenis kolaborasi yang benar-benar baru. + Bebaskan. + Elemen berfungsi dengan semua aplikasi berbasis Matrix dan bahkan dapat menjembatani ke perpesanan sumber tertutup. + Hubung dengan siapa pun. + Elemen memungkinkan Anda untuk memilih di mana pesan Anda disimpan, membuat Anda tetap mengendali data Anda. + Anda mengendalikannya. + Perpesanan terenkripsi secara ujung-ke-ujung untuk komunikasi aman dan independen, terhubung via Matrix. + Miliki percakapan Anda. \ No newline at end of file From 3f81ec4d868a4ef0b42eca4dd2ccd14a2e94511e Mon Sep 17 00:00:00 2001 From: random Date: Wed, 12 Jan 2022 09:41:02 +0000 Subject: [PATCH 058/281] Translated using Weblate (Italian) Currently translated at 100.0% (2729 of 2729 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/it/ --- vector/src/main/res/values-it/strings.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml index ec88bbe89d..cfa145c082 100644 --- a/vector/src/main/res/values-it/strings.xml +++ b/vector/src/main/res/values-it/strings.xml @@ -3048,4 +3048,22 @@ Riavvia l\'applicazione per applicare le modifiche. Attiva la matematica LaTeX Non ti è permesso entrare in questa stanza + Crea sondaggio + Apri i contatti + Invia sticker + Invia file + Invia immagini e video + Apri fotocamera + Il tuo sistema invierà automaticamente i log quando si verifica un errore di decifrazione + Auto-segnala errori di decifrazione. + Sovrascrivi colore nick + Ho già un account + Universale come le email, Element è un tipo di collaborazione completamente nuovo. + Riduci il carico ai team. + Element funziona con tutte le app basate su Matrix e può anche fare da ponte tra messaggistiche proprietarie. + Connettiti con chiunque. + Element ti lascia scegliere dove i messaggi vengono salvati, dandoti il controllo dei tuoi dati. + Tu hai il controllo. + Messaggistica cifrata end-to-end per comunicazioni sicure e indipendenti, connesse via Matrix. + Prendi il controllo delle tue conversazioni. \ No newline at end of file From 099c0ddc36a92a07811db331341dda3964557e7b Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Tue, 11 Jan 2022 14:17:47 +0000 Subject: [PATCH 059/281] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2729 of 2729 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 | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index 6c10294c02..4bae8d0640 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -221,18 +221,18 @@ %1$s, %2$s e %3$s 🎉 Todos os servidores estão banidos de participar! Esta sala não pode mais ser usada. Nenhuma mudança. - • Servidores correspondendo a literais de IP estão agora banidos. - • Servidores correspondendo a literais de IP estão agora permitidos. - • Servidores correspondendo a %s foram removidos da lista de permitidos. - • Servidores correspondendo a %s estão agora permitidos. - • Servidores correspondendo a %s foram removidos da lista de banimento. - • Servidores correspondendo a %s estão agora banidos. + • Servidores correspondendo com literais de IP estão agora banidos. + • Servidores correspondendo com literais de IP estão agora permitidos. + • Servidores correspondendo com %s foram removidos da lista de permitidos. + • Servidores correspondendo com %s estão agora permitidos. + • Servidores correspondendo com %s foram removidos da lista de banimento. + • Servidores correspondendo com %s estão agora banidos. Você mudou as LCAs do servidor para esta sala. %s mudou as LCAs do servidor para esta sala. - • Servidores correspondendo a literais de IP estão banidos. - • Servidores correspondendo a literais de IP estão permitidos. - • Servidores correspondendo a %s estão permitidos. - • Servidores correspondendo a %s estão banidos. + • Servidores correspondendo com literais de IP estão banidos. + • Servidores correspondendo com literais de IP estão permitidos. + • Servidores correspondendo com %s estão permitidos. + • Servidores correspondendo com %s estão banidos. Você definiu as LCAs do servidor para esta sala. %s definiu as LCAs do servidor para esta sala. Você mudou os endereços alternativos para esta sala. @@ -549,7 +549,7 @@ Impressão digital (%s): Não foi possível verificar identidade de servidor remoto. Isto poderia significar que alguém está maliciosamente interceptando seu tráfico, ou que seu celular não confia no certificado provido pelo servidor remoto. - Se o/a administrador(a) de servidor tem dito que isto é esperado, assegure que a impressão digital abaixo corresponde à impressão digital provida por ele(a). + Se o/a administrador(a) de servidor tem dito que isto é esperado, assegure que a impressão digital abaixo corresponde com a impressão digital provida por ele(a). O certificado tem mudado de um que era confiado por seu telefone. Isto é ALTAMENTE INCOMUM. É recomendado que você NÃO ACEITE este novo certificado. O certificado tem mudado de um previamente confiado para um que não é confiado. O servidor pode ter renovado seu certificado. Contacte o/a administrador(a) de servidor para a impressão digital esperada. Somente aceite o certificado se o/a administrador(a) de servidor tem publicado uma impressão digital que corresponde com a acima. @@ -759,7 +759,7 @@ Retirar da lista negra Verificar sessão Confirme ao comparar o seguinte com as Configurações de Usuária(o) em sua outra sessão: - Se não corresponderem, a segurança de sua comunicação pode estar comprometida. + Se não correspondem, a segurança de sua comunicação pode estar comprometida. Eu verifico que as chaves correspondem Sala contém sessões desconhecidas Esta sala contém sessões desconhecidas que não têm sido verificadas. @@ -1543,8 +1543,8 @@ O SAS não correspondeu A sessão recebeu uma mensagem inesperada Uma mensagem inválida foi recebida - Disparidade de chave - Disparidade de usuária(o) + Correspondência errada de chave + Correspondência errada de usuária(o) Erro Desconhecido Você não está usando nenhum servidor de identidade Nenhum servidor de identidade está configurado, ele é requerido para resettar sua senha. @@ -3058,4 +3058,22 @@ Recomece o aplicativo para a mudançar tomar efeito. Habilitar matemática LaTeX Você não é permitida(o) a juntar-se a esta sala + Criar sondagem + Abrir contatos + Enviar sticker + Fazer upload de arquivo + Enviar imagens e vídeos + Abrir câmera + Seu sistema vai automaticamente enviar logs quando um erro incapaz de decriptar ocorre + Auro Reportar Erros de Decriptação. + Sobrepor cor de nick + Eu já tenho uma conta + Tão universal quanto email, Element é um tipo de colaboração completamente novo. + Dê liberdade a times. + Element funciona com todos os apps baseados em Matrix e pode até construir pontes para mensageiros proprietários. + Conecte-se com qualquer pessoa. + Element lhe permite escolher onde suas mensagens são armazenadas, mantendo você em controle de seus dados. + Você está em controle. + Mensageria encriptada ponta-a-ponta para comunicação segura e independente, conectada via Matrix. + Tenha posse de suas conversas. \ No newline at end of file From b8f72879d31a44e9bf8976b2c435c2ac4bdb7054 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 11 Jan 2022 15:03:13 +0000 Subject: [PATCH 060/281] Translated using Weblate (Albanian) Currently translated at 99.4% (2715 of 2729 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 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index 5985bd40fe..08eab1ed6e 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -3046,4 +3046,21 @@ Që ndryshimi të hyjë në fuqi, rinisni aplikacionin. Aktivizo elementë LaTeX për matematikë S’keni leje të hyni në këtë dhomë + Krijoni anketim + Hap kontakte + Dërgoni ngjitës + Ngarkoni kartelë + Dërgoni figura dhe video + Hapni kamerën + Sistemi juaj do të dërgojë automatikisht regjistra, kur ndodh një gabim “s’arrihet të shfshehtëzohet” + Raporto Vetvetiu Gabime Shfshehtëzimi. + Anashkalo ngjyrë nofke + Kam tashmë një llogari + Po aq universal sa email-i, Element-i është një lloj plotësisht i ri bashkëpunimi. + Element-i funksionon me krejt aplikacionet e bazuar në Matrix dhe mund të shërbejë madje si urë për shkëmbyes pronësorë mesazhesh. + Lidhuni me këdo. + Element-i ju lejon të zgjidhni ku depozitohen mesazhet tuaja, duke ju mbajtur ju në kontroll të të dhënave tuaja. + Kontrollin e keni ju. + Shkëmbim mesazhesh të fshehtëzuar skaj më skaj, për komunikim të siguruar dhe të pavarur, me lidhje përmes Matrix-it. + Jini zot i bisedave tuaja. \ No newline at end of file From 10c6153542d1a6efbfe1f2cda9ab15c23b1a5c86 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 11 Jan 2022 15:05:30 +0000 Subject: [PATCH 061/281] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2729 of 2729 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 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index 4ad5ceae60..3003ece5a4 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -3160,4 +3160,22 @@ Перезапустіть застосунок, щоб зміни набули чинності. Увімкнути підтримку LaTeX Вам не дозволено приєднуватися до цієї кімнати + Створити опитування + Відкрити контакти + Надіслати наліпку + Вивантажити файл + Надіслати зображення й відео + Відкрити камеру + Ваша система автоматично надсилатиме журнали, коли виникне помилка неможливості розшифрування + Автозвіт про помилки шифрування. + Замінити колір псевдоніма + Я вже маю обліковий запис + Універсальний, як електронна пошта, Element — це абсолютно новий тип співпраці. + Удоскональте спілкування в команді. + Element працює з усіма застосунками на базі Matrix і може навіть з\'єднуватися з власницькими месенджерами. + З\'єднуйтеся з будь-ким. + Element дає змогу обрати де зберігати повідомлення, надаючи вам контроль над вашими даними. + Ви контролюєте все. + Наскрізно зашифроване спілкування для безпечного та незалежного зв’язку, під\'єднаного через Matrix. + Володійте своїми розмовами. \ No newline at end of file From 97ff707e06032550e0eedc27fd52ef0c65a063da Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 12 Jan 2022 02:18:57 +0000 Subject: [PATCH 062/281] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2729 of 2729 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/ --- vector/src/main/res/values-zh-rTW/strings.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml index c5a33b7072..a73871e8c8 100644 --- a/vector/src/main/res/values-zh-rTW/strings.xml +++ b/vector/src/main/res/values-zh-rTW/strings.xml @@ -2999,4 +2999,22 @@ 重新啟動應用程式以讓變更生效。 啟用 LaTeX 數學 您無法加入此聊天室 + 建立投票 + 開啟通訊錄 + 傳送貼圖 + 上傳檔案 + 傳送圖片與影片 + 開啟攝影機 + 發生無法解密錯誤時,您的系統將會自動傳送紀錄檔 + 自動回報解密錯誤。 + 覆寫暱稱色彩 + 我已有一個帳號 + 與電子郵件一樣普遍,Element 是一種全新的協作類型。 + 減少團隊的懈怠。 + Element 適用於所有以 Matrix 為基礎的應用程式,甚至可以橋接到專有的通訊軟體。 + 與任何人聯絡。 + Element 可讓您選擇您要儲存訊息的地方,讓您掌控您自己的資料。 + 您已掌控了您的資料。 + 透過 Matrix 連結的端到端加密訊息傳遞,用於安全與獨立的通訊。 + 擁有您的對話。 \ No newline at end of file From acbfda83c9aabfe869ba507dfc878f2c466b21fa Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 11 Jan 2022 15:05:55 +0000 Subject: [PATCH 063/281] Translated using Weblate (Albanian) Currently translated at 100.0% (43 of 43 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sq/ --- fastlane/metadata/android/sq/changelogs/40103100.txt | 2 ++ fastlane/metadata/android/sq/changelogs/40103110.txt | 2 ++ fastlane/metadata/android/sq/changelogs/40103120.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/sq/changelogs/40103100.txt create mode 100644 fastlane/metadata/android/sq/changelogs/40103110.txt create mode 100644 fastlane/metadata/android/sq/changelogs/40103120.txt diff --git a/fastlane/metadata/android/sq/changelogs/40103100.txt b/fastlane/metadata/android/sq/changelogs/40103100.txt new file mode 100644 index 0000000000..c6916fa0ab --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40103100.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Shtim mbulimi për anketime (në zhvillim). Skemë e re grafike për paraprje URL-sh. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.10 diff --git a/fastlane/metadata/android/sq/changelogs/40103110.txt b/fastlane/metadata/android/sq/changelogs/40103110.txt new file mode 100644 index 0000000000..f66779b5ef --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40103110.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.11 diff --git a/fastlane/metadata/android/sq/changelogs/40103120.txt b/fastlane/metadata/android/sq/changelogs/40103120.txt new file mode 100644 index 0000000000..279c523a82 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40103120.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.12 From 18359fedb37b1984786db469e3dea2d1b60cabcb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Jan 2022 18:06:32 +0100 Subject: [PATCH 064/281] Fix #4919 --- changelog.d/4935.bugfix | 1 + .../android/sdk/internal/session/terms/DefaultTermsService.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/4935.bugfix diff --git a/changelog.d/4935.bugfix b/changelog.d/4935.bugfix new file mode 100644 index 0000000000..18967ed4a5 --- /dev/null +++ b/changelog.d/4935.bugfix @@ -0,0 +1 @@ +Fix a wrong network error issue in the Legals screen \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt index 313fb6319d..6205e3e4b1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt @@ -64,7 +64,7 @@ internal class DefaultTermsService @Inject constructor( */ override suspend fun getHomeserverTerms(baseUrl: String): TermsResponse { return try { - val request = baseUrl + NetworkConstants.URI_API_PREFIX_PATH_R0 + "register" + val request = baseUrl.ensureTrailingSlash() + NetworkConstants.URI_API_PREFIX_PATH_R0 + "register" executeRequest(null) { termsAPI.register(request) } From 6f8533c7d6f35b3b26bbf45c7b91283700f31ba1 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 13 Jan 2022 09:46:18 +0000 Subject: [PATCH 065/281] locking the analytics opt in screen to portrait for phones --- .../features/analytics/ui/consent/AnalyticsOptInActivity.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/analytics/ui/consent/AnalyticsOptInActivity.kt b/vector/src/main/java/im/vector/app/features/analytics/ui/consent/AnalyticsOptInActivity.kt index f6a06ebdb7..c84031d2fd 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/ui/consent/AnalyticsOptInActivity.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/ui/consent/AnalyticsOptInActivity.kt @@ -20,8 +20,10 @@ import com.airbnb.mvrx.viewModel import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.ScreenOrientationLocker import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding +import javax.inject.Inject /** * Simple container for AnalyticsOptInFragment @@ -29,6 +31,8 @@ import im.vector.app.databinding.ActivitySimpleBinding @AndroidEntryPoint class AnalyticsOptInActivity : VectorBaseActivity() { + @Inject lateinit var orientationLocker: ScreenOrientationLocker + private val viewModel: AnalyticsConsentViewModel by viewModel() override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater) @@ -36,6 +40,7 @@ class AnalyticsOptInActivity : VectorBaseActivity() { override fun getCoordinatorLayout() = views.coordinatorLayout override fun initUiAndData() { + orientationLocker.lockPhonesToPortrait(this) if (isFirstCreation()) { addFragment(views.simpleFragmentContainer, AnalyticsOptInFragment::class.java) } From ab6ed2376fb24545998938f97c564939a75b7269 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 13 Jan 2022 09:56:15 +0000 Subject: [PATCH 066/281] trigger CI From 5d0c55b6179cc8335ee3ab2cc16322ff7a687c12 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 13 Jan 2022 10:05:07 +0000 Subject: [PATCH 067/281] extracting usecase enum to its own file --- .../app/features/onboarding/FtueUseCase.kt | 24 +++++++++++++++++++ .../features/onboarding/OnboardingAction.kt | 10 +------- .../ftueauth/FtueAuthUseCaseFragment.kt | 12 +++++----- 3 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt new file mode 100644 index 0000000000..e720b7307c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 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.onboarding + +enum class FtueUseCase { + FRIENDS_FAMILY, + TEAMS, + COMMUNITIES, + SKIP +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt index 9ce84211f1..cfacd7a8d9 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt @@ -31,15 +31,7 @@ sealed class OnboardingAction : VectorViewModelAction { data class UpdateServerType(val serverType: ServerType) : OnboardingAction() data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction() - data class UpdateUseCase(val useCase: UseCase) : OnboardingAction() { - enum class UseCase { - FRIENDS_FAMILY, - TEAMS, - COMMUNITIES, - SKIP - } - } - + data class UpdateUseCase(val useCase: FtueUseCase) : OnboardingAction() data class UpdateSignMode(val signMode: SignMode) : OnboardingAction() data class LoginWithToken(val loginToken: String) : OnboardingAction() data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt index 39e8c3d8e0..da909c9352 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt @@ -27,7 +27,7 @@ import im.vector.app.core.extensions.setTextWithColoredPart import im.vector.app.databinding.FragmentFtueAuthUseCaseBinding import im.vector.app.features.login.ServerType import im.vector.app.features.onboarding.OnboardingAction -import im.vector.app.features.onboarding.OnboardingAction.UpdateUseCase.UseCase +import im.vector.app.features.onboarding.FtueUseCase import javax.inject.Inject class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment() { @@ -42,9 +42,9 @@ class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment Date: Thu, 13 Jan 2022 10:22:46 +0000 Subject: [PATCH 068/281] reusing existing helper to replace partial in string template --- .../features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt index da909c9352..5df10b7f5e 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt @@ -46,10 +46,9 @@ class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment Date: Thu, 13 Jan 2022 10:30:40 +0000 Subject: [PATCH 069/281] adding reset use case action, is todo until the persistence is implemented --- .../im/vector/app/features/onboarding/OnboardingAction.kt | 1 + .../im/vector/app/features/onboarding/OnboardingViewModel.kt | 5 +++++ .../features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt index cfacd7a8d9..2ca6a1f2fd 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt @@ -32,6 +32,7 @@ sealed class OnboardingAction : VectorViewModelAction { data class UpdateServerType(val serverType: ServerType) : OnboardingAction() data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction() data class UpdateUseCase(val useCase: FtueUseCase) : OnboardingAction() + object ResetUseCase : OnboardingAction() data class UpdateSignMode(val signMode: SignMode) : OnboardingAction() data class LoginWithToken(val loginToken: String) : OnboardingAction() data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 5e0c294b3e..3ae0ecccd2 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -126,6 +126,7 @@ class OnboardingViewModel @AssistedInject constructor( is OnboardingAction.OnGetStarted -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) is OnboardingAction.OnIAlreadyHaveAnAccount -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) is OnboardingAction.UpdateUseCase -> handleUpdateUseCase() + OnboardingAction.ResetUseCase -> resetUseCase() is OnboardingAction.UpdateServerType -> handleUpdateServerType(action) is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action) is OnboardingAction.InitWith -> handleInitWith(action) @@ -462,6 +463,10 @@ class OnboardingViewModel @AssistedInject constructor( _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } + private fun resetUseCase() { + // TODO remove stored use case + } + private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) { setState { copy( diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt index 5df10b7f5e..d86eb8a508 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthUseCaseFragment.kt @@ -60,7 +60,7 @@ class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment Date: Thu, 13 Jan 2022 10:36:42 +0000 Subject: [PATCH 070/281] making the text only buttons a button widget to give them touch feedback --- vector/src/main/res/layout/fragment_ftue_auth_splash.xml | 2 +- vector/src/main/res/layout/fragment_ftue_auth_use_case.xml | 4 ++-- vector/src/main/res/layout/fragment_ftue_splash_carousel.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/res/layout/fragment_ftue_auth_splash.xml b/vector/src/main/res/layout/fragment_ftue_auth_splash.xml index 39c0ad3007..803ad700db 100644 --- a/vector/src/main/res/layout/fragment_ftue_auth_splash.xml +++ b/vector/src/main/res/layout/fragment_ftue_auth_splash.xml @@ -190,7 +190,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/loginSplashSpace4" /> - - - Date: Thu, 13 Jan 2022 10:39:34 +0000 Subject: [PATCH 071/281] adding text to design preview for programatically set text --- vector/src/main/res/layout/fragment_ftue_auth_use_case.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml index 89d059dab9..33737c0f1e 100644 --- a/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml +++ b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml @@ -1,6 +1,7 @@ + app:layout_constraintTop_toBottomOf="@id/useCaseOptionThree" + tools:text="Not sure yet? You can skip this question" /> Date: Thu, 13 Jan 2022 10:57:04 +0000 Subject: [PATCH 072/281] moving the work in progress strings out of the production strings files to avoid translations before they're signed off --- vector/src/main/res/values/donottranslate.xml | 11 +++++++++++ vector/src/main/res/values/strings.xml | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/vector/src/main/res/values/donottranslate.xml b/vector/src/main/res/values/donottranslate.xml index d22391ed8c..3a9a60f912 100755 --- a/vector/src/main/res/values/donottranslate.xml +++ b/vector/src/main/res/values/donottranslate.xml @@ -14,4 +14,15 @@ Not implemented yet in ${app_name} + + Who will you chat to the most? + We\'ll help you get connected. + Friends and family + Teams + Communities + Not sure yet? %s + You can skip this question + Looking to join an existing server? + Connect to server + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 6d9684352b..c9adbc9d4a 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2528,16 +2528,6 @@ Cut the slack from teams. As universal as email, Element is a completely new type of collaboration. - Who will you chat to the most? - We\'ll help you get connected. - Friends and family - Teams - Communities - Not sure yet? %s - You can skip this question - Looking to join an existing server? - Connect to server - It\'s your conversation. Own it. Chat with people directly or in groups Keep conversations private with encryption From b371e24d9fd5ef88c976191134db7e0ddde4d820 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 13 Jan 2022 12:00:59 +0000 Subject: [PATCH 073/281] wrapping the use case into a scrollview to avoid overlapping on smaller devices --- .../layout/fragment_ftue_auth_use_case.xml | 320 +++++++++--------- 1 file changed, 166 insertions(+), 154 deletions(-) diff --git a/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml index 33737c0f1e..594fc80696 100644 --- a/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml +++ b/vector/src/main/res/layout/fragment_ftue_auth_use_case.xml @@ -1,169 +1,181 @@ - + android:fillViewport="true" + android:paddingTop="0dp" + android:paddingBottom="0dp"> - - - - - - - - - - - - - + android:paddingTop="@dimen/layout_vertical_margin" + android:paddingBottom="@dimen/layout_vertical_margin"> - + - + - + - + - + + + + + + + + + + + + + + + + +