diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 5035811b24..d1289563e9 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -243,6 +243,7 @@ + diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index f56a6a3d70..979c35dde7 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -28,6 +28,7 @@ import im.vector.app.features.MainActivity import im.vector.app.features.call.CallControlsBottomSheet import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.conference.VectorJitsiActivity +import im.vector.app.features.call.transfer.CallTransferActivity import im.vector.app.features.createdirect.CreateDirectRoomActivity import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity import im.vector.app.features.crypto.quads.SharedSecureStorageActivity @@ -146,6 +147,7 @@ interface ScreenComponent { fun inject(activity: VectorJitsiActivity) fun inject(activity: SearchActivity) fun inject(activity: UserCodeActivity) + fun inject(callTransferActivity: CallTransferActivity) /* ========================================================================================== * BottomSheets diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt new file mode 100644 index 0000000000..d038a0e836 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt @@ -0,0 +1,136 @@ +/* + * Copyright (c) 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.call.transfer + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import android.widget.Toast +import com.airbnb.mvrx.MvRx +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.addFragment +import im.vector.app.core.extensions.addFragmentToBackstack +import im.vector.app.core.platform.SimpleFragmentActivity +import im.vector.app.core.utils.PERMISSIONS_FOR_MEMBERS_SEARCH +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 +import im.vector.app.features.contactsbook.ContactsBookFragment +import im.vector.app.features.contactsbook.ContactsBookViewModel +import im.vector.app.features.contactsbook.ContactsBookViewState +import im.vector.app.features.userdirectory.UserListFragment +import im.vector.app.features.userdirectory.UserListFragmentArgs +import im.vector.app.features.userdirectory.UserListSharedAction +import im.vector.app.features.userdirectory.UserListSharedActionViewModel +import im.vector.app.features.userdirectory.UserListViewModel +import im.vector.app.features.userdirectory.UserListViewState +import kotlinx.parcelize.Parcelize +import javax.inject.Inject + +@Parcelize +data class CallTransferArgs(val callId: String) : Parcelable + +class CallTransferActivity : SimpleFragmentActivity(), CallTransferViewModel.Factory, UserListViewModel.Factory, ContactsBookViewModel.Factory { + + private lateinit var sharedActionViewModel: UserListSharedActionViewModel + @Inject lateinit var userListViewModelFactory: UserListViewModel.Factory + @Inject lateinit var callTransferViewModelFactory: CallTransferViewModel.Factory + @Inject lateinit var contactsBookViewModelFactory: ContactsBookViewModel.Factory + @Inject lateinit var errorFormatter: ErrorFormatter + + override fun injectWith(injector: ScreenComponent) { + super.injectWith(injector) + injector.inject(this) + } + + override fun create(initialState: UserListViewState): UserListViewModel { + return userListViewModelFactory.create(initialState) + } + + override fun create(initialState: CallTransferViewState): CallTransferViewModel { + return callTransferViewModelFactory.create(initialState) + } + + override fun create(initialState: ContactsBookViewState): ContactsBookViewModel { + return contactsBookViewModelFactory.create(initialState) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + views.toolbar.visibility = View.GONE + + sharedActionViewModel = viewModelProvider.get(UserListSharedActionViewModel::class.java) + sharedActionViewModel + .observe() + .subscribe { sharedAction -> + when (sharedAction) { + UserListSharedAction.Close -> finish() + UserListSharedAction.GoBack -> onBackPressed() + UserListSharedAction.OpenPhoneBook -> openPhoneBook() + // not exhaustive because it's a sharedAction + else -> { + } + } + } + .disposeOnDestroy() + if (isFirstCreation()) { + addFragment( + R.id.container, + UserListFragment::class.java, + UserListFragmentArgs( + title = "Call transfer", + menuResId = -1 + ) + ) + } + } + + private fun openPhoneBook() { + // Check permission first + if (checkPermissions(PERMISSIONS_FOR_MEMBERS_SEARCH, + this, + PERMISSION_REQUEST_CODE_READ_CONTACTS, + 0)) { + addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (allGranted(grantResults)) { + if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) { + doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } + } + } else { + Toast.makeText(baseContext, R.string.missing_permissions_error, Toast.LENGTH_SHORT).show() + } + } + + companion object { + + fun newIntent(context: Context, callId: String): Intent { + return Intent(context, CallTransferActivity::class.java).also { + it.putExtra(MvRx.KEY_ARG, CallTransferArgs(callId)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt new file mode 100644 index 0000000000..fd9557315c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 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.call.transfer + +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import org.matrix.android.sdk.api.session.Session +import timber.log.Timber + +class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: CallTransferViewState) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: CallTransferViewState): CallTransferViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: CallTransferViewState): CallTransferViewModel? { + val activity: CallTransferActivity = (viewModelContext as ActivityViewModelContext).activity() + return activity.callTransferViewModelFactory.create(state) + } + } + + override fun handle(action: EmptyAction) {} + +} diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewState.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewState.kt new file mode 100644 index 0000000000..2b29d9f6f2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewState.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 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.call.transfer + +import com.airbnb.mvrx.MvRxState + +data class CallTransferViewState( + val callId: String +) : MvRxState { + + constructor(args: CallTransferArgs) : this(callId = args.callId) +} diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index 6aaa69fbc0..3e85702cf4 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -44,9 +44,9 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject class ContactsBookFragment @Inject constructor( - val contactsBookViewModelFactory: ContactsBookViewModel.Factory, + private val contactsBookViewModelFactory: ContactsBookViewModel.Factory, private val contactsBookController: ContactsBookController -) : VectorBaseFragment(), ContactsBookController.Callback { +) : VectorBaseFragment(), ContactsBookController.Callback, ContactsBookViewModel.Factory { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentContactsBookBinding { return FragmentContactsBookBinding.inflate(inflater, container, false) @@ -59,6 +59,10 @@ class ContactsBookFragment @Inject constructor( private lateinit var sharedActionViewModel: UserListSharedActionViewModel + override fun create(initialState: ContactsBookViewState): ContactsBookViewModel { + return contactsBookViewModelFactory.create(initialState) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(UserListSharedActionViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index 2c4c5d0596..9721425f56 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -33,6 +33,7 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.createdirect.CreateDirectRoomActivity import im.vector.app.features.invite.InviteUsersToRoomActivity +import im.vector.app.features.userdirectory.UserListViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback @@ -56,17 +57,11 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted companion object : MvRxViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: ContactsBookViewState): ContactsBookViewModel? { - return when (viewModelContext) { - is FragmentViewModelContext -> (viewModelContext.fragment() as ContactsBookFragment).contactsBookViewModelFactory.create(state) - is ActivityViewModelContext -> { - when (viewModelContext.activity()) { - is CreateDirectRoomActivity -> viewModelContext.activity().contactsBookViewModelFactory.create(state) - is InviteUsersToRoomActivity -> viewModelContext.activity().contactsBookViewModelFactory.create(state) - else -> error("Wrong activity or fragment") - } - } - else -> error("Wrong activity or fragment") + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") } } 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 beb7931fd4..347424ee8a 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 @@ -45,6 +45,7 @@ import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.onPermissionDeniedSnackbar import im.vector.app.features.contactsbook.ContactsBookFragment import im.vector.app.features.contactsbook.ContactsBookViewModel +import im.vector.app.features.contactsbook.ContactsBookViewState import im.vector.app.features.userdirectory.UserListFragment import im.vector.app.features.userdirectory.UserListFragmentArgs import im.vector.app.features.userdirectory.UserListSharedAction @@ -57,7 +58,7 @@ import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import java.net.HttpURLConnection import javax.inject.Inject -class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Factory { +class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Factory, CreateDirectRoomViewModel.Factory, ContactsBookViewModel.Factory { private val viewModel: CreateDirectRoomViewModel by viewModel() private lateinit var sharedActionViewModel: UserListSharedActionViewModel @@ -71,9 +72,11 @@ class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Fac injector.inject(this) } - override fun create(initialState: UserListViewState): UserListViewModel { - return userListViewModelFactory.create(initialState) - } + override fun create(initialState: UserListViewState) = userListViewModelFactory.create(initialState) + + override fun create(initialState: CreateDirectRoomViewState) = createDirectRoomViewModelFactory.create(initialState) + + override fun create(initialState: ContactsBookViewState) = contactsBookViewModelFactory.create(initialState) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) 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 d074c93587..fdf16f2664 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 @@ -18,6 +18,7 @@ package im.vector.app.features.createdirect import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext @@ -25,6 +26,7 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.contactsbook.ContactsBookViewModel import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.isE2EByDefault import im.vector.app.features.userdirectory.PendingInvitee @@ -50,8 +52,11 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted @JvmStatic override fun create(viewModelContext: ViewModelContext, state: CreateDirectRoomViewState): CreateDirectRoomViewModel? { - val activity: CreateDirectRoomActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.createDirectRoomViewModelFactory.create(state) + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") } } diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt index 9949255e32..f923bb7306 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt @@ -39,6 +39,7 @@ import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.toast import im.vector.app.features.contactsbook.ContactsBookFragment import im.vector.app.features.contactsbook.ContactsBookViewModel +import im.vector.app.features.contactsbook.ContactsBookViewState import im.vector.app.features.userdirectory.UserListFragment import im.vector.app.features.userdirectory.UserListFragmentArgs import im.vector.app.features.userdirectory.UserListSharedAction @@ -53,7 +54,7 @@ import javax.inject.Inject @Parcelize data class InviteUsersToRoomArgs(val roomId: String) : Parcelable -class InviteUsersToRoomActivity : SimpleFragmentActivity(), UserListViewModel.Factory { +class InviteUsersToRoomActivity : SimpleFragmentActivity(), UserListViewModel.Factory, ContactsBookViewModel.Factory, InviteUsersToRoomViewModel.Factory { private val viewModel: InviteUsersToRoomViewModel by viewModel() private lateinit var sharedActionViewModel: UserListSharedActionViewModel @@ -67,9 +68,11 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity(), UserListViewModel.Fa injector.inject(this) } - override fun create(initialState: UserListViewState): UserListViewModel { - return userListViewModelFactory.create(initialState) - } + override fun create(initialState: UserListViewState) = userListViewModelFactory.create(initialState) + + override fun create(initialState: ContactsBookViewState) = contactsBookViewModelFactory.create(initialState) + + override fun create(initialState: InviteUsersToRoomViewState) = inviteUsersToRoomViewModelFactory.create(initialState) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt index 21a998d8e2..984eed33b0 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.invite import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted @@ -24,6 +25,7 @@ import com.squareup.inject.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.contactsbook.ContactsBookViewModel import im.vector.app.features.userdirectory.PendingInvitee import io.reactivex.Observable import org.matrix.android.sdk.api.session.Session @@ -46,8 +48,11 @@ class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted @JvmStatic override fun create(viewModelContext: ViewModelContext, state: InviteUsersToRoomViewState): InviteUsersToRoomViewModel? { - val activity: InviteUsersToRoomActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.inviteUsersToRoomViewModelFactory.create(state) + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") } } 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 0a6197e424..5f785acbaf 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 @@ -34,6 +34,8 @@ import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.toast import im.vector.app.features.call.conference.JitsiCallViewModel import im.vector.app.features.call.conference.VectorJitsiActivity +import im.vector.app.features.call.transfer.CallTransferActivity +import im.vector.app.features.call.transfer.CallTransferArgs import im.vector.app.features.createdirect.CreateDirectRoomActivity import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity @@ -344,6 +346,11 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } + override fun openCallTransfer(context: Context, callId: String) { + val intent = CallTransferActivity.newIntent(context, callId) + 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 504fccb63a..bc13e99bbf 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 @@ -113,4 +113,6 @@ interface Navigator { options: ((MutableList>) -> Unit)?) fun openSearch(context: Context, roomId: String) + + fun openCallTransfer(context: Context, callId: String) }