diff --git a/AUTHORS.md b/AUTHORS.md index 823dbc7311..ad20133d83 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -39,7 +39,7 @@ We do not forget all translators, for their work of translating Element into man Feel free to add your name below, when you contribute to the project! -Name | Matrix ID | GitHub ---------|---------------------|-------------------------------------- -gjpower | @gjpower:matrix.org | [gjpower](https://github.com/gjpower) - +Name | Matrix ID | GitHub +----------|-----------------------------|-------------------------------------- +gjpower | @gjpower:matrix.org | [gjpower](https://github.com/gjpower) +TR_SLimey | @tr_slimey:an-atom-in.space | [TR-SLimey](https://github.com/TR-SLimey) diff --git a/CHANGES.md b/CHANGES.md index 80bb2c66b8..4873a999b6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ Changes in Element 1.0.11 (2020-XX-XX) =================================================== Features ✨: - - + - Create DMs with users by scanning their QR code (#2025) Improvements 🙌: - New room creation tile with quick action (#2346) diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index 10ab1673e4..2035ee50f6 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -22,6 +22,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.appcompat.app.AlertDialog import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail @@ -37,6 +38,8 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.utils.PERMISSIONS_FOR_MEMBERS_SEARCH +import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO +import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_READ_CONTACTS import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions @@ -72,35 +75,45 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { super.onCreate(savedInstanceState) toolbar.visibility = View.GONE sharedActionViewModel = viewModelProvider.get(UserDirectorySharedActionViewModel::class.java) - sharedActionViewModel - .observe() - .subscribe { sharedAction -> - when (sharedAction) { - UserDirectorySharedAction.OpenUsersDirectory -> - addFragmentToBackstack(R.id.container, UserDirectoryFragment::class.java) - UserDirectorySharedAction.Close -> finish() - UserDirectorySharedAction.GoBack -> onBackPressed() - is UserDirectorySharedAction.OnMenuItemSelected -> onMenuItemSelected(sharedAction) - UserDirectorySharedAction.OpenPhoneBook -> openPhoneBook() - }.exhaustive - } - .disposeOnDestroy() - if (isFirstCreation()) { - addFragment( - R.id.container, - KnownUsersFragment::class.java, - KnownUsersFragmentArgs( - title = getString(R.string.fab_menu_create_chat), - menuResId = R.menu.vector_create_direct_room, - isCreatingRoom = true - ) - ) + if (intent?.getBooleanExtra(BY_QR_CODE, false)!!) { + if (isFirstCreation()) { openAddByQrCode() } + } else { + sharedActionViewModel + .observe() + .subscribe { sharedAction -> + when (sharedAction) { + UserDirectorySharedAction.OpenUsersDirectory -> + addFragmentToBackstack(R.id.container, UserDirectoryFragment::class.java) + UserDirectorySharedAction.Close -> finish() + UserDirectorySharedAction.GoBack -> onBackPressed() + is UserDirectorySharedAction.OnMenuItemSelected -> onMenuItemSelected(sharedAction) + UserDirectorySharedAction.OpenPhoneBook -> openPhoneBook() + }.exhaustive + } + .disposeOnDestroy() + if (isFirstCreation()) { + addFragment( + R.id.container, + KnownUsersFragment::class.java, + KnownUsersFragmentArgs( + title = getString(R.string.fab_menu_create_chat), + menuResId = R.menu.vector_create_direct_room, + isCreatingRoom = true + ) + ) + } } viewModel.selectSubscribe(this, CreateDirectRoomViewState::createAndInviteState) { renderCreateAndInviteState(it) } } + private fun openAddByQrCode() { + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA, 0)) { + addFragment(R.id.container, CreateDirectRoomByQrCodeFragment::class.java) + } + } + private fun openPhoneBook() { // Check permission first if (checkPermissions(PERMISSIONS_FOR_MEMBERS_SEARCH, @@ -116,6 +129,13 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { if (allGranted(grantResults)) { if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) { doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } + } else if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && intent?.getBooleanExtra(BY_QR_CODE, false)!!) { + addFragment(R.id.container, CreateDirectRoomByQrCodeFragment::class.java) + } + } else { + Toast.makeText(baseContext, R.string.missing_permissions_error, Toast.LENGTH_SHORT).show() + if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && intent?.getBooleanExtra(BY_QR_CODE, false)!!) { + finish() } } } @@ -178,8 +198,12 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { } companion object { - fun getIntent(context: Context): Intent { - return Intent(context, CreateDirectRoomActivity::class.java) + private const val BY_QR_CODE = "BY_QR_CODE" + + fun getIntent(context: Context, byQrCode: Boolean = false): Intent { + return Intent(context, CreateDirectRoomActivity::class.java).apply { + putExtra(BY_QR_CODE, byQrCode) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomByQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomByQrCodeFragment.kt new file mode 100644 index 0000000000..f03368fdd5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomByQrCodeFragment.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2020 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.createdirect + +import android.widget.Toast +import com.airbnb.mvrx.activityViewModel +import com.google.zxing.Result +import com.google.zxing.ResultMetadataType +import im.vector.app.R +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.features.userdirectory.PendingInvitee +import kotlinx.android.synthetic.main.fragment_qr_code_scanner.* +import me.dm7.barcodescanner.zxing.ZXingScannerView +import org.matrix.android.sdk.api.session.permalinks.PermalinkData +import org.matrix.android.sdk.api.session.permalinks.PermalinkParser +import org.matrix.android.sdk.api.session.user.model.User +import javax.inject.Inject + +class CreateDirectRoomByQrCodeFragment @Inject constructor() : VectorBaseFragment(), ZXingScannerView.ResultHandler { + + private val viewModel: CreateDirectRoomViewModel by activityViewModel() + + override fun getLayoutResId() = R.layout.fragment_qr_code_scanner + + override fun onResume() { + super.onResume() + // Register ourselves as a handler for scan results. + scannerView.setResultHandler(null) + // Start camera on resume + scannerView.startCamera() + } + + override fun onPause() { + super.onPause() + // Stop camera on pause + scannerView.stopCamera() + } + + // Copied from https://github.com/markusfisch/BinaryEye/blob/ + // 9d57889b810dcaa1a91d7278fc45c262afba1284/app/src/main/kotlin/de/markusfisch/android/binaryeye/activity/CameraActivity.kt#L434 + private fun getRawBytes(result: Result): ByteArray? { + val metadata = result.resultMetadata ?: return null + val segments = metadata[ResultMetadataType.BYTE_SEGMENTS] ?: return null + var bytes = ByteArray(0) + @Suppress("UNCHECKED_CAST") + for (seg in segments as Iterable) { + bytes += seg + } + // byte segments can never be shorter than the text. + // Zxing cuts off content prefixes like "WIFI:" + return if (bytes.size >= result.text.length) bytes else null + } + + private fun addByQrCode(value: String) { + val mxid = (PermalinkParser.parse(value) as? PermalinkData.UserLink)?.userId + + if (mxid === null) { + Toast.makeText(requireContext(), R.string.invalid_qr_code_uri, Toast.LENGTH_SHORT).show() + requireActivity().finish() + } else { + val existingDm = viewModel.session.getExistingDirectRoomWithUser(mxid) + + if (existingDm === null) { + // The following assumes MXIDs are case insensitive + if (mxid.equals(other = viewModel.session.myUserId, ignoreCase = true)) { + Toast.makeText(requireContext(), R.string.cannot_dm_self, Toast.LENGTH_SHORT).show() + requireActivity().finish() + } else { + // Try to get user from known users and fall back to creating a User object from MXID + val qrInvitee = if (viewModel.session.getUser(mxid) != null) viewModel.session.getUser(mxid)!! else User(mxid, null, null) + + viewModel.handle( + CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(setOf(PendingInvitee.UserPendingInvitee(qrInvitee))) + ) + } + } else { + navigator.openRoom(requireContext(), existingDm, null, false) + requireActivity().finish() + } + } + } + + override fun handleResult(result: Result?) { + if (result === null) { + Toast.makeText(requireContext(), R.string.qr_code_not_scanned, Toast.LENGTH_SHORT).show() + requireActivity().finish() + } else { + val rawBytes = getRawBytes(result) + val rawBytesStr = rawBytes?.toString(Charsets.ISO_8859_1) + val value = rawBytesStr ?: result.text + addByQrCode(value) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt index be9449b77a..d074c93587 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt @@ -38,7 +38,7 @@ import org.matrix.android.sdk.rx.rx class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateDirectRoomViewState, private val rawService: RawService, - private val session: Session) + val session: Session) : VectorViewModel(initialState) { @AssistedInject.Factory diff --git a/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt b/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt index 0884844777..42bef2ddae 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt @@ -22,7 +22,7 @@ import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel -import im.vector.app.features.home.room.list.widget.FabMenuView +import im.vector.app.features.home.room.list.widget.NotifsFabMenuView @EpoxyModelClass(layout = R.layout.item_room_filter_footer) abstract class FilteredRoomFooterItem : VectorEpoxyModel() { @@ -46,7 +46,7 @@ abstract class FilteredRoomFooterItem : VectorEpoxyModel(R.id.roomFilterFooterOpenRoomDirectory) } - interface FilteredRoomFooterItemListener : FabMenuView.Listener { + interface FilteredRoomFooterItemListener : NotifsFabMenuView.Listener { fun createRoom(initialName: String) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index f1d35a74d5..e47072d0b0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -45,7 +45,8 @@ import im.vector.app.features.home.room.list.actions.RoomListActionsArgs import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel -import im.vector.app.features.home.room.list.widget.FabMenuView +import im.vector.app.features.home.room.list.widget.DmsFabMenuView +import im.vector.app.features.home.room.list.widget.NotifsFabMenuView import im.vector.app.features.notifications.NotificationDrawerManager import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_list.* @@ -66,8 +67,7 @@ class RoomListFragment @Inject constructor( val roomListViewModelFactory: RoomListViewModel.Factory, private val notificationDrawerManager: NotificationDrawerManager, private val sharedViewPool: RecyclerView.RecycledViewPool - -) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener { +) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, DmsFabMenuView.Listener, NotifsFabMenuView.Listener { private var modelBuildListener: OnModelBuildFinishedListener? = null private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel @@ -111,6 +111,7 @@ class RoomListFragment @Inject constructor( }.exhaustive } + createDmFabMenu.listener = this createChatFabMenu.listener = this sharedActionViewModel @@ -129,6 +130,7 @@ class RoomListFragment @Inject constructor( roomListView.cleanup() roomController.listener = null stateRestorer.clear() + createDmFabMenu.listener = null createChatFabMenu.listener = null super.onDestroyView() } @@ -140,33 +142,32 @@ class RoomListFragment @Inject constructor( private fun setupCreateRoomButton() { when (roomListParams.displayMode) { RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.isVisible = true - RoomListDisplayMode.PEOPLE -> createChatRoomButton.isVisible = true + RoomListDisplayMode.PEOPLE -> createDmFabMenu.isVisible = true RoomListDisplayMode.ROOMS -> createGroupRoomButton.isVisible = true else -> Unit // No button in this mode } - createChatRoomButton.debouncedClicks { - createDirectChat() - } createGroupRoomButton.debouncedClicks { openRoomDirectory() } - // Hide FAB when list is scrolling + // Hide FABs when list is scrolling roomListView.addOnScrollListener( object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + createDmFabMenu.removeCallbacks(showFabRunnable) createChatFabMenu.removeCallbacks(showFabRunnable) when (newState) { RecyclerView.SCROLL_STATE_IDLE -> { + createDmFabMenu.postDelayed(showFabRunnable, 250) createChatFabMenu.postDelayed(showFabRunnable, 250) } RecyclerView.SCROLL_STATE_DRAGGING, RecyclerView.SCROLL_STATE_SETTLING -> { when (roomListParams.displayMode) { RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.hide() - RoomListDisplayMode.PEOPLE -> createChatRoomButton.hide() + RoomListDisplayMode.PEOPLE -> createDmFabMenu.hide() RoomListDisplayMode.ROOMS -> createGroupRoomButton.hide() else -> Unit } @@ -191,6 +192,10 @@ class RoomListFragment @Inject constructor( navigator.openCreateDirectRoom(requireActivity()) } + override fun createDirectChatByQrCode() { + navigator.openCreateDirectRoom(requireContext(), true) + } + private fun setupRecyclerView() { val layoutManager = LinearLayoutManager(context) stateRestorer = LayoutManagerStateRestorer(layoutManager).register() @@ -209,7 +214,7 @@ class RoomListFragment @Inject constructor( if (isAdded) { when (roomListParams.displayMode) { RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.show() - RoomListDisplayMode.PEOPLE -> createChatRoomButton.show() + RoomListDisplayMode.PEOPLE -> createDmFabMenu.show() RoomListDisplayMode.ROOMS -> createGroupRoomButton.show() else -> Unit } @@ -338,6 +343,9 @@ class RoomListFragment @Inject constructor( } override fun onBackPressed(toolbarButton: Boolean): Boolean { + if (createDmFabMenu.onBackPressed()) { + return true + } if (createChatFabMenu.onBackPressed()) { return true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/widget/DmsFabMenuView.kt b/vector/src/main/java/im/vector/app/features/home/room/list/widget/DmsFabMenuView.kt new file mode 100644 index 0000000000..9659b7b12b --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/widget/DmsFabMenuView.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2019 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.list.widget + +import android.content.Context +import android.util.AttributeSet +import androidx.constraintlayout.motion.widget.MotionLayout +import androidx.core.view.isVisible +import com.google.android.material.floatingactionbutton.FloatingActionButton +import im.vector.app.R +import kotlinx.android.synthetic.main.motion_dms_fab_menu_merge.view.* + +class DmsFabMenuView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, + defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr) { + + var listener: Listener? = null + + init { + inflate(context, R.layout.motion_dms_fab_menu_merge, this) + } + + override fun onFinishInflate() { + super.onFinishInflate() + + listOf(createDmByMxid, createDmByMxidLabel) + .forEach { + it.setOnClickListener { + closeFabMenu() + listener?.createDirectChat() + } + } + listOf(createDmByQrCode, createDmByQrCodeLabel) + .forEach { + it.setOnClickListener { + closeFabMenu() + listener?.createDirectChatByQrCode() + } + } + + dmsCreateRoomTouchGuard.setOnClickListener { + closeFabMenu() + } + } + + override fun transitionToEnd() { + super.transitionToEnd() + + dmsCreateRoomButton.contentDescription = context.getString(R.string.a11y_create_menu_close) + } + + override fun transitionToStart() { + super.transitionToStart() + + dmsCreateRoomButton.contentDescription = context.getString(R.string.a11y_create_menu_open) + } + + fun show() { + isVisible = true + dmsCreateRoomButton.show() + } + + fun hide() { + dmsCreateRoomButton.hide(object : FloatingActionButton.OnVisibilityChangedListener() { + override fun onHidden(fab: FloatingActionButton?) { + super.onHidden(fab) + isVisible = false + } + }) + } + + private fun closeFabMenu() { + transitionToStart() + } + + fun onBackPressed(): Boolean { + if (currentState == R.id.constraint_set_fab_menu_open) { + closeFabMenu() + return true + } + + return false + } + + interface Listener { + fun createDirectChat() + fun createDirectChatByQrCode() + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/widget/FabMenuView.kt b/vector/src/main/java/im/vector/app/features/home/room/list/widget/NotifsFabMenuView.kt similarity index 88% rename from vector/src/main/java/im/vector/app/features/home/room/list/widget/FabMenuView.kt rename to vector/src/main/java/im/vector/app/features/home/room/list/widget/NotifsFabMenuView.kt index f9058840d2..7c96f40dbf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/widget/FabMenuView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/widget/NotifsFabMenuView.kt @@ -22,15 +22,15 @@ import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.view.isVisible import com.google.android.material.floatingactionbutton.FloatingActionButton import im.vector.app.R -import kotlinx.android.synthetic.main.motion_fab_menu_merge.view.* +import kotlinx.android.synthetic.main.motion_notifs_fab_menu_merge.view.* -class FabMenuView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr) { +class NotifsFabMenuView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, + defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr) { var listener: Listener? = null init { - inflate(context, R.layout.motion_fab_menu_merge, this) + inflate(context, R.layout.motion_notifs_fab_menu_merge, this) } override fun onFinishInflate() { 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 2d0ca86d52..9ff103113f 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 @@ -203,8 +203,8 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } - override fun openCreateDirectRoom(context: Context) { - val intent = CreateDirectRoomActivity.getIntent(context) + override fun openCreateDirectRoom(context: Context, byQrCode: Boolean) { + val intent = CreateDirectRoomActivity.getIntent(context, byQrCode) 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 504fccb63a..23d24b709c 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 @@ -56,7 +56,7 @@ interface Navigator { fun openCreateRoom(context: Context, initialName: String = "") - fun openCreateDirectRoom(context: Context) + fun openCreateDirectRoom(context: Context, byQrCode: Boolean = false) fun openInviteUsersToRoom(context: Context, roomId: String) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt index 8f57acee47..7b2e329b6a 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt @@ -62,7 +62,7 @@ abstract class RoomDirectoryItem : VectorEpoxyModel() holder.avatarView.isInvisible = directoryAvatarUrl.isNullOrBlank() && includeAllNetworks holder.nameView.text = directoryName - holder.descritionView.setTextOrHide(directoryDescription) + holder.descriptionView.setTextOrHide(directoryDescription) } class Holder : VectorEpoxyHolder() { @@ -70,6 +70,6 @@ abstract class RoomDirectoryItem : VectorEpoxyModel() val avatarView by bind(R.id.itemRoomDirectoryAvatar) val nameView by bind(R.id.itemRoomDirectoryName) - val descritionView by bind(R.id.itemRoomDirectoryDescription) + val descriptionView by bind(R.id.itemRoomDirectoryDescription) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index d60b5580fa..e994a3c3ec 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -294,12 +294,20 @@ class RoomMemberProfileFragment @Inject constructor( } private fun handleShareRoomMemberProfile(permalink: String) { - startSharePlainTextIntent( - fragment = this, - activityResultLauncher = null, - chooserTitle = null, - text = permalink - ) + val view = layoutInflater.inflate(R.layout.dialog_share_qr_code, null) + val qrCode = view.findViewById(R.id.itemShareQrCodeImage) + qrCode.setData(permalink) + AlertDialog.Builder(requireContext()) + .setView(view) + .setNeutralButton(R.string.ok, null) + .setPositiveButton(R.string.share_by_text) { _, _ -> + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = null, + text = permalink + ) + }.show() } private fun onAvatarClicked(view: View, userMatrixItem: MatrixItem) { diff --git a/vector/src/main/res/drawable/ic_fab_add_by_mxid.xml b/vector/src/main/res/drawable/ic_fab_add_by_mxid.xml new file mode 100644 index 0000000000..50768871ab --- /dev/null +++ b/vector/src/main/res/drawable/ic_fab_add_by_mxid.xml @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/vector/src/main/res/drawable/ic_fab_add_by_qr_code.xml b/vector/src/main/res/drawable/ic_fab_add_by_qr_code.xml new file mode 100644 index 0000000000..50768871ab --- /dev/null +++ b/vector/src/main/res/drawable/ic_fab_add_by_qr_code.xml @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/vector/src/main/res/layout/dialog_share_qr_code.xml b/vector/src/main/res/layout/dialog_share_qr_code.xml new file mode 100644 index 0000000000..d209376edb --- /dev/null +++ b/vector/src/main/res/layout/dialog_share_qr_code.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/vector/src/main/res/layout/fragment_room_list.xml b/vector/src/main/res/layout/fragment_room_list.xml index f0bc336cbd..855c45f7c5 100644 --- a/vector/src/main/res/layout/fragment_room_list.xml +++ b/vector/src/main/res/layout/fragment_room_list.xml @@ -14,29 +14,25 @@ android:overScrollMode="always" tools:listitem="@layout/item_room" /> - - + + + + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/motion_fab_menu_merge.xml b/vector/src/main/res/layout/motion_notifs_fab_menu_merge.xml similarity index 98% rename from vector/src/main/res/layout/motion_fab_menu_merge.xml rename to vector/src/main/res/layout/motion_notifs_fab_menu_merge.xml index e564d16aaf..8130ea0637 100644 --- a/vector/src/main/res/layout/motion_fab_menu_merge.xml +++ b/vector/src/main/res/layout/motion_notifs_fab_menu_merge.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - app:layoutDescription="@xml/motion_scene_fab_menu" + app:layoutDescription="@xml/motion_scene_notifs_fab_menu" tools:motionProgress="0.65" tools:parentTag="androidx.constraintlayout.motion.widget.MotionLayout" tools:showPaths="true"> diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index cbfb95dc85..1eb602e4c3 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1761,6 +1761,7 @@ Link copied to clipboard Add by matrix ID + Add by QR code "Creating room…" "No result found, use Add by matrix ID to search on server." "Start typing to get results" @@ -1828,6 +1829,8 @@ Open the create room menu Close the create room menu… Create a new direct conversation + Create a new direct conversation by Matrix ID + Create a new direct conversation by scanning a QR code Create a new room Close keys backup banner Show password @@ -2674,4 +2677,10 @@ The room is not yet created. Cancel the room creation? There are unsaved changes. Discard the changes? Discard changes + + + Share by text + Cannot DM yourself! + Invalid QR code (Invalid URI)! + QR code not scanned! diff --git a/vector/src/main/res/xml/motion_scene_dms_fab_menu.xml b/vector/src/main/res/xml/motion_scene_dms_fab_menu.xml new file mode 100644 index 0000000000..8bb1c55df5 --- /dev/null +++ b/vector/src/main/res/xml/motion_scene_dms_fab_menu.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/xml/motion_scene_fab_menu.xml b/vector/src/main/res/xml/motion_scene_notifs_fab_menu.xml similarity index 100% rename from vector/src/main/res/xml/motion_scene_fab_menu.xml rename to vector/src/main/res/xml/motion_scene_notifs_fab_menu.xml