Benoit's review

- Cleanup
- Force refresh of HomeServerCapabilities
- add some doc
- remove dead code
- remove commented code
- remove duplicated comment
- use getBestName()
- improve code formatting
- Fix isAudioOnly parameter in jitsi url
- Fix layout issue between "Active conference" banner and "Jump to first unread message banner"
- Improve "Active conference" banner
- Remove Calendar permission from Manifest
This commit is contained in:
Benoit Marty 2020-08-14 15:40:02 +02:00
parent 157f22ac2d
commit 4f8fd7b994
25 changed files with 174 additions and 133 deletions

View File

@ -3,7 +3,7 @@ Changes in Element 1.0.5 (2020-XX-XX)
Features ✨:
- Protect access to the app by a pin code (#1700)
- Conference with Jitsi support (#43)
- Conference with Jitsi support (#43)
Improvements 🙌:
- Give user the possibility to prevent accidental call (#1869)

View File

@ -137,7 +137,7 @@ dependencies {
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.8.1"))
implementation 'com.squareup.okhttp3:okhttp'
implementation 'com.squareup.okhttp3:logging-interceptor'
implementation("com.squareup.okhttp3:okhttp-urlconnection")
implementation 'com.squareup.okhttp3:okhttp-urlconnection'
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"

View File

@ -74,9 +74,7 @@ data class E2EWellKnownConfig(
)
@JsonClass(generateAdapter = true)
class WellKnownPreferredConfig {
@JvmField
@Json(name = "preferredDomain")
var preferredDomain: String? = null
}
data class WellKnownPreferredConfig(
@Json(name = "preferredDomain")
val preferredDomain: String? = null
)

View File

@ -39,7 +39,9 @@ data class HomeServerCapabilities(
* (as it was before) for various environments where this is desired.
*/
val adminE2EByDefault: Boolean = true,
/**
* Preferred Jitsi domain, provided in Wellknown
*/
val preferredJitsiDomain: String? = null
) {
companion object {

View File

@ -17,10 +17,10 @@
package org.matrix.android.sdk.internal.database
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import io.realm.DynamicRealm
import io.realm.RealmMigration
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import timber.log.Timber
import javax.inject.Inject
@ -55,8 +55,12 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
}
private fun migrateTo3(realm: DynamicRealm) {
Timber.d("Step 1 -> 2")
Timber.d("Step 2 -> 3")
realm.schema.get("HomeServerCapabilitiesEntity")
?.addField(HomeServerCapabilitiesEntityFields.PREFERRED_JITSI_DOMAIN, String::class.java)
?.transform { obj ->
// Schedule a refresh of the capabilities
obj.setLong(HomeServerCapabilitiesEntityFields.LAST_UPDATED_TIMESTAMP, 0)
}
}
}

View File

@ -28,6 +28,14 @@
<!-- Needed for incoming calls -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- Jitsi libs adds CALENDAR permissions, but we can remove them safely according to https://github.com/jitsi/jitsi-meet/issues/4068#issuecomment-480482481 -->
<uses-permission
android:name="android.permission.READ_CALENDAR"
tools:node="remove" />
<uses-permission
android:name="android.permission.WRITE_CALENDAR"
tools:node="remove" />
<!-- Adding CAMERA permission prevents Chromebooks to see the application on the PlayStore -->
<!-- Tell that the Camera is not mandatory to install the application -->
<uses-feature

View File

@ -36,7 +36,7 @@ abstract class GenericButtonItem : VectorEpoxyModel<GenericButtonItem.Holder>()
var text: String? = null
@EpoxyAttribute
var itemClickAction: View.OnClickListener? = null
var buttonClickAction: View.OnClickListener? = null
@EpoxyAttribute
@ColorInt
@ -57,7 +57,7 @@ abstract class GenericButtonItem : VectorEpoxyModel<GenericButtonItem.Holder>()
holder.button.icon = null
}
itemClickAction?.let { holder.button.setOnClickListener(it) }
buttonClickAction?.let { holder.button.setOnClickListener(it) }
}
class Holder : VectorEpoxyHolder() {

View File

@ -92,7 +92,7 @@ class ActiveConferenceView @JvmOverloads constructor(
val summary = state.asyncRoomSummary()
if (summary?.membership == Membership.JOIN) {
// We only display banner for 'live' widgets
val activeConf = // for now only jitsi?
val activeConf =
state.activeRoomWidgets()?.firstOrNull {
// for now only jitsi?
it.type == WidgetType.Jitsi

View File

@ -0,0 +1,21 @@
/*
* 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.conference
import im.vector.app.core.platform.VectorViewModelAction
sealed class JitsiCallViewActions : VectorViewModelAction

View File

@ -0,0 +1,21 @@
/*
* 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.conference
import im.vector.app.core.platform.VectorViewEvents
sealed class JitsiCallViewEvents : VectorViewEvents

View File

@ -23,11 +23,8 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.call.WebRtcPeerConnectionManager
import org.jitsi.meet.sdk.JitsiMeetUserInfo
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
@ -36,16 +33,11 @@ import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.rx.asObservable
import java.net.URL
sealed class JitsiCallViewActions : VectorViewModelAction
sealed class JitsiCallViewEvents : VectorViewEvents
class JitsiCallViewModel @AssistedInject constructor(
@Assisted initialState: JitsiCallViewState,
@Assisted val args: VectorJitsiActivity.Args,
val session: Session,
val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
val stringProvider: StringProvider
private val session: Session,
private val stringProvider: StringProvider
) : VectorViewModel<JitsiCallViewState, JitsiCallViewActions, JitsiCallViewEvents>(initialState) {
@AssistedInject.Factory
@ -56,7 +48,7 @@ class JitsiCallViewModel @AssistedInject constructor(
init {
val me = session.getUser(session.myUserId)?.toMatrixItem()
val userInfo = JitsiMeetUserInfo().apply {
displayName = me?.displayName
displayName = me?.getBestName()
avatar = me?.avatarUrl?.let { session.contentUrlResolver().resolveFullSize(it) }?.let { URL(it) }
}
val roomName = session.getRoomSummary(args.roomId)?.displayName
@ -73,7 +65,7 @@ class JitsiCallViewModel @AssistedInject constructor(
if (jitsiWidget != null) {
val uri = Uri.parse(jitsiWidget.computedUrl)
val confId = uri.getQueryParameter("confId")
val ppt = jitsiWidget.computedUrl?.let { JitsiWidgetProperties(it, stringProvider) }
val ppt = jitsiWidget.computedUrl?.let { url -> JitsiWidgetProperties(url, stringProvider) }
setState {
copy(
widget = Success(jitsiWidget),
@ -89,7 +81,8 @@ class JitsiCallViewModel @AssistedInject constructor(
)
}
}
}.disposeOnClear()
}
.disposeOnClear()
}
override fun handle(action: JitsiCallViewActions) {
@ -108,7 +101,6 @@ class JitsiCallViewModel @AssistedInject constructor(
override fun initialState(viewModelContext: ViewModelContext): JitsiCallViewState? {
val args: VectorJitsiActivity.Args = viewModelContext.args()
// val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
return JitsiCallViewState(
roomId = args.roomId,

View File

@ -55,7 +55,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface, Ji
@Inject lateinit var viewModelFactory: JitsiCallViewModel.Factory
var jitsiMeetView: JitsiMeetView? = null
private var jitsiMeetView: JitsiMeetView? = null
private val jitsiViewModel: JitsiCallViewModel by viewModel()

View File

@ -82,7 +82,8 @@ sealed class RoomDetailAction : VectorViewModelAction {
object SelectStickerAttachment : RoomDetailAction()
object OpenIntegrationManager: RoomDetailAction()
object ManageIntegrations: RoomDetailAction()
data class AddJitsiWidget(val video: Boolean): RoomDetailAction()
data class AddJitsiWidget(val withVideo: Boolean): RoomDetailAction()
data class RemoveWidget(val widgetId: String): RoomDetailAction()
data class EnsureNativeWidgetAllowed(val widget: Widget, val grantedEvents: RoomDetailViewEvents) : RoomDetailAction()
data class EnsureNativeWidgetAllowed(val widget: Widget,
val grantedEvents: RoomDetailViewEvents) : RoomDetailAction()
}

View File

@ -154,6 +154,14 @@ import im.vector.app.features.widgets.WidgetActivity
import im.vector.app.features.widgets.WidgetArgs
import im.vector.app.features.widgets.WidgetKind
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_detail.*
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import org.billcarsonfr.jsonviewer.JSonViewerDialog
import org.commonmark.parser.Parser
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.permalinks.PermalinkFactory
import org.matrix.android.sdk.api.session.Session
@ -176,21 +184,13 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.widgets.model.Widget
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_detail.*
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import org.billcarsonfr.jsonviewer.JSonViewerDialog
import org.commonmark.parser.Parser
import org.matrix.android.sdk.api.session.widgets.model.Widget
import timber.log.Timber
import java.io.File
import java.net.URL
@ -370,24 +370,24 @@ class RoomDetailFragment @Inject constructor(
private fun requestNativeWidgetPermission(it: RoomDetailViewEvents.RequestNativeWidgetPermission) {
val tag = RoomWidgetPermissionBottomSheet::class.java.name
val dFrag = childFragmentManager
.findFragmentByTag(tag) as? RoomWidgetPermissionBottomSheet
val dFrag = childFragmentManager.findFragmentByTag(tag) as? RoomWidgetPermissionBottomSheet
if (dFrag != null && dFrag.dialog?.isShowing == true && !dFrag.isRemoving) {
return
} else {
RoomWidgetPermissionBottomSheet
.newInstance(WidgetArgs(
RoomWidgetPermissionBottomSheet.newInstance(
WidgetArgs(
baseUrl = it.domain,
kind = WidgetKind.ROOM,
roomId = roomDetailArgs.roomId,
widgetId = it.widget.widgetId
)).apply {
directListener = { granted ->
if (granted) {
roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed(it.widget, it.grantedEvents))
}
}
)
).apply {
directListener = { granted ->
if (granted) {
roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed(it.widget, it.grantedEvents))
}
}
}
.show(childFragmentManager, tag)
}
}
@ -592,7 +592,8 @@ class RoomDetailFragment @Inject constructor(
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
menu.findItem(R.id.open_matrix_apps).let { menuItem ->
// We use a custom layout for this menu item, so we need to set a ClickListener
menu.findItem(R.id.open_matrix_apps)?.let { menuItem ->
menuItem.actionView.setOnClickListener {
onOptionsItemSelected(menuItem)
}
@ -604,24 +605,23 @@ class RoomDetailFragment @Inject constructor(
it.isVisible = roomDetailViewModel.isMenuItemVisible(it.itemId)
}
withState(roomDetailViewModel) { state ->
val findItem = menu.findItem(R.id.open_matrix_apps)
val widgetsCount = state.activeRoomWidgets.invoke()?.size
if (widgetsCount ?: 0 > 0) {
val actionView = findItem.actionView
val matrixAppsMenuItem = menu.findItem(R.id.open_matrix_apps)
val widgetsCount = state.activeRoomWidgets.invoke()?.size ?: 0
if (widgetsCount > 0) {
val actionView = matrixAppsMenuItem.actionView
actionView
.findViewById<ImageView>(R.id.action_view_icon_image)
.setColorFilter(ContextCompat.getColor(requireContext(), R.color.riotx_accent))
actionView.findViewById<TextView>(R.id.cart_badge).isVisible = true
actionView.findViewById<TextView>(R.id.cart_badge).text = "$widgetsCount"
findItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
actionView.findViewById<TextView>(R.id.cart_badge).setTextOrHide("$widgetsCount")
matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
} else {
// icon should be default color no badge
val actionView = findItem.actionView
val actionView = matrixAppsMenuItem.actionView
actionView
.findViewById<ImageView>(R.id.action_view_icon_image)
.setColorFilter(ThemeUtils.getColor(requireContext(), R.attr.riotx_text_secondary))
actionView.findViewById<TextView>(R.id.cart_badge).isVisible = false
findItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
}
}
}
@ -646,6 +646,7 @@ class RoomDetailFragment @Inject constructor(
R.id.voice_call,
R.id.video_call -> {
handleCallRequest(item)
true
}
R.id.hangup_call -> {
roomDetailViewModel.handle(RoomDetailAction.EndCall)
@ -655,10 +656,10 @@ class RoomDetailFragment @Inject constructor(
}
}
private fun handleCallRequest(item: MenuItem): Boolean = withState(roomDetailViewModel) { state ->
val roomSummary = state.asyncRoomSummary.invoke() ?: return@withState true
private fun handleCallRequest(item: MenuItem) = withState(roomDetailViewModel) { state ->
val roomSummary = state.asyncRoomSummary.invoke() ?: return@withState
val isVideoCall = item.itemId == R.id.video_call
return@withState when (roomSummary.joinedMembersCount) {
when (roomSummary.joinedMembersCount) {
1 -> {
val pendingInvite = roomSummary.invitedMembersCount ?: 0 > 0
if (pendingInvite) {
@ -668,7 +669,6 @@ class RoomDetailFragment @Inject constructor(
// You cannot place a call with yourself.
showDialogWithMessage(getString(R.string.cannot_call_yourself))
}
true
}
2 -> {
val activeCall = sharedCallActionViewModel.activeCall.value
@ -685,7 +685,6 @@ class RoomDetailFragment @Inject constructor(
} else {
safeStartCall(isVideoCall)
}
true
}
else -> {
// it's jitsi call
@ -709,7 +708,6 @@ class RoomDetailFragment @Inject constructor(
.show()
}
}
true
}
}
}
@ -1725,10 +1723,6 @@ class RoomDetailFragment @Inject constructor(
.show()
}
// private fun joinCurrentJitsiCall(withVideo: Boolean) {
//
// }
// VectorInviteView.Callback
override fun onAcceptInvite() {

View File

@ -72,7 +72,9 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
object OpenIntegrationManager: RoomDetailViewEvents()
object OpenActiveWidgetBottomSheet: RoomDetailViewEvents()
data class RequestNativeWidgetPermission(val widget: Widget, val domain: String, val grantedEvents: RoomDetailViewEvents) : RoomDetailViewEvents()
data class RequestNativeWidgetPermission(val widget: Widget,
val domain: String,
val grantedEvents: RoomDetailViewEvents) : RoomDetailViewEvents()
object MessageSent : SendMessageResult()
data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult()

View File

@ -332,15 +332,9 @@ class RoomDetailViewModel @AssistedInject constructor(
private fun handleAddJitsiConference(action: RoomDetailAction.AddJitsiWidget) {
_viewEvents.post(RoomDetailViewEvents.ShowWaitingView)
viewModelScope.launch(Dispatchers.IO) {
// Build data for a jitsi widget
// Build data for a jitsi widget
val widgetId: String = WidgetType.Jitsi.preferred + "_" + session.myUserId + "_" + System.currentTimeMillis()
// Create a random enough jitsi conference id
// Note: the jitsi server automatically creates conference when the conference
// id does not exist yet
// Create a random enough jitsi conference id
// Note: the jitsi server automatically creates conference when the conference
// id does not exist yet
@ -356,9 +350,14 @@ class RoomDetailViewModel @AssistedInject constructor(
// We use the default element wrapper for this widget
// https://github.com/vector-im/element-web/blob/develop/docs/jitsi-dev.md
val url = "https://app.element.io/jitsi.html?" +
"confId=$confId#conferenceDomain=\$domain&conferenceId=\$conferenceId&isAudioOnly=${action.video}" +
"&displayName=\$matrix_display_name&avatarUrl=\$matrix_avatar_url&userId=\$matrix_user_id"
val url = "https://app.element.io/jitsi.html" +
"?confId=$confId" +
"#conferenceDomain=\$domain" +
"&conferenceId=\$conferenceId" +
"&isAudioOnly=${!action.withVideo}" +
"&displayName=\$matrix_display_name" +
"&avatarUrl=\$matrix_avatar_url" +
"&userId=\$matrix_user_id"
val widgetEventContent = mapOf(
"url" to url,
@ -366,7 +365,7 @@ class RoomDetailViewModel @AssistedInject constructor(
"data" to mapOf(
"conferenceId" to confId,
"domain" to jitsiDomain,
"isAudioOnly" to !action.video
"isAudioOnly" to !action.withVideo
),
"creatorUserId" to session.myUserId,
"id" to widgetId,
@ -377,7 +376,7 @@ class RoomDetailViewModel @AssistedInject constructor(
val widget = awaitCallback<Widget> {
session.widgetService().createRoomWidget(roomId, widgetId, widgetEventContent, it)
}
_viewEvents.post(RoomDetailViewEvents.JoinJitsiConference(widget, action.video))
_viewEvents.post(RoomDetailViewEvents.JoinJitsiConference(widget, action.withVideo))
} catch (failure: Throwable) {
_viewEvents.post(RoomDetailViewEvents.ShowMessage(stringProvider.getString(R.string.failed_to_add_widget)))
} finally {

View File

@ -38,9 +38,9 @@ import javax.inject.Inject
/**
* Bottom sheet displaying active widgets in a room
*/
class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidgetController.Listener {
class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidgetsController.Listener {
@Inject lateinit var epoxyController: RoomWidgetController
@Inject lateinit var epoxyController: RoomWidgetsController
@Inject lateinit var colorProvider: ColorProvider
@Inject lateinit var navigator: Navigator

View File

@ -29,7 +29,10 @@ import javax.inject.Inject
/**
* Epoxy controller for room widgets list
*/
class RoomWidgetController @Inject constructor(val stringProvider: StringProvider, val colorProvider: ColorProvider) : TypedEpoxyController<List<Widget>>() {
class RoomWidgetsController @Inject constructor(
val stringProvider: StringProvider,
val colorProvider: ColorProvider)
: TypedEpoxyController<List<Widget>>() {
var listener: Listener? = null
@ -41,18 +44,18 @@ class RoomWidgetController @Inject constructor(val stringProvider: StringProvide
}
} else {
widgets.forEach {
RoomWidgetItem_()
.id(it.widgetId)
.widget(it)
.widgetClicked { listener?.didSelectWidget(it) }
.addTo(this)
roomWidgetItem {
id(it.widgetId)
widget(it)
widgetClicked { listener?.didSelectWidget(it) }
}
}
}
genericButtonItem {
id("addIntegration")
text(stringProvider.getString(R.string.room_manage_integrations))
textColor(colorProvider.getColor(R.color.riotx_accent))
itemClickAction(View.OnClickListener { listener?.didSelectManageWidgets() })
buttonClickAction(View.OnClickListener { listener?.didSelectManageWidgets() })
}
}

View File

@ -111,7 +111,7 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
// id("complete_security")
// iconRes(R.drawable.ic_shield_warning)
// text(stringProvider.getString(R.string.complete_security))
// itemClickAction(DebouncedClickListener(View.OnClickListener { _ ->
// buttonClickAction(DebouncedClickListener(View.OnClickListener { _ ->
// callback?.completeSecurity()
// }))
// }

View File

@ -48,7 +48,7 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() {
injector.inject(this)
}
// Use this if you don't need to full activity view model
// Use this if you don't need the full activity view model
var directListener: ((Boolean) -> Unit)? = null
override fun invalidate() = withState(viewModel) { state ->
@ -91,16 +91,16 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() {
@OnClick(R.id.widgetPermissionDecline)
fun doDecline() {
viewModel.handle(RoomWidgetPermissionActions.BlockWidget)
// optimistic dismiss
directListener?.invoke(false)
// optimistic dismiss
dismiss()
}
@OnClick(R.id.widgetPermissionContinue)
fun doAccept() {
viewModel.handle(RoomWidgetPermissionActions.AllowWidget)
// optimistic dismiss
directListener?.invoke(true)
// optimistic dismiss
dismiss()
}

View File

@ -129,17 +129,17 @@
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/activeCallView">
app:layout_constraintTop_toBottomOf="@id/activeConferenceView">
<!-- <im.vector.app.features.home.room.detail.widget.RoomWidgetsBannerView-->
<!-- android:id="@+id/roomWidgetsBannerView"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="8dp"-->
<!-- android:layout_marginTop="8dp"-->
<!-- android:layout_marginEnd="8dp"-->
<!-- android:visibility="gone"-->
<!-- tools:visibility="visible" />-->
<!-- <im.vector.app.features.home.room.detail.widget.RoomWidgetsBannerView-->
<!-- android:id="@+id/roomWidgetsBannerView"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="8dp"-->
<!-- android:layout_marginTop="8dp"-->
<!-- android:layout_marginEnd="8dp"-->
<!-- android:visibility="gone"-->
<!-- tools:visibility="visible" />-->
<im.vector.app.core.ui.views.JumpToReadMarkerView
android:id="@+id/jumpToReadMarkerView"
@ -198,7 +198,7 @@
android:focusable="true"
app:cardCornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/activeCallView">
app:layout_constraintTop_toBottomOf="@id/activeConferenceView">
<org.webrtc.SurfaceViewRenderer
android:id="@+id/activeCallPiP"

View File

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="64dp"
@ -14,36 +13,35 @@
<ImageView
android:id="@+id/roomWidgetAvatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<TextView
app:layout_constraintStart_toEndOf="@id/roomWidgetAvatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/roomWidgetUrl"
app:layout_constraintVertical_chainStyle="packed"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:id="@+id/roomWidgetName"
style="@style/BottomSheetItemTextMain"
tools:text="@sample/matrix.json/data/displayName" />
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toTopOf="@id/roomWidgetUrl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/roomWidgetAvatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Widget name" />
<TextView
app:layout_constraintStart_toStartOf="@id/roomWidgetName"
app:layout_constraintEnd_toEndOf="@id/roomWidgetName"
app:layout_constraintTop_toBottomOf="@id/roomWidgetName"
app:layout_constraintBottom_toBottomOf="parent"
android:textStyle="normal"
android:id="@+id/roomWidgetUrl"
style="@style/BottomSheetItemTextSecondary"
android:textStyle="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/roomWidgetName"
app:layout_constraintStart_toStartOf="@id/roomWidgetName"
app:layout_constraintTop_toBottomOf="@id/roomWidgetName"
tools:text="https://foobar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -25,8 +25,9 @@
android:textColor="@color/white"
app:drawableTint="@color/white" />
<TextView
<com.google.android.material.button.MaterialButton
android:id="@+id/returnToCallButton"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/activeCallInfo"
@ -38,7 +39,6 @@
android:paddingStart="8dp"
android:paddingEnd="16dp"
android:text="@string/return_to_call"
android:textAllCaps="true"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold" />

View File

@ -5,7 +5,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:foreground="?attr/selectableItemBackground"
tools:parentTag="android.widget.RelativeLayout">
<TextView
@ -13,7 +12,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/deleteWidgetButton"
android:background="?attr/selectableItemBackground"
android:drawableStart="@drawable/ic_call"
android:drawablePadding="10dp"
android:gravity="center_vertical"
@ -26,8 +24,9 @@
app:drawableTint="@color/white"
tools:text="@string/ongoing_conference_call" />
<TextView
<com.google.android.material.button.MaterialButton
android:id="@+id/deleteWidgetButton"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/activeConferenceInfo"
@ -39,7 +38,6 @@
android:paddingStart="8dp"
android:paddingEnd="16dp"
android:text="@string/action_close"
android:textAllCaps="true"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold" />

View File

@ -93,7 +93,7 @@
<string name="conference_call_in_progress">A conference is already in progress!</string>
<string name="video_meeting">Start video meeting</string>
<string name="audio_meeting">Start audio meeting</string>
<string name="audio_video_meeting_description">Meetings use Jitsi security and permission policies. All people currently in the room will see an invite to join while your meeting is happening.</string>
<string name="audio_video_meeting_description">Meetings use Jitsi security and permission policies. All people currently in the room will see an invite to join while your meeting is happening.</string>
<string name="missing_permissions_title_to_start_conf_call">Cannot start call</string>
<string name="cannot_call_yourself">You cannot place a call with yourself</string>
<string name="cannot_call_yourself_with_invite">You cannot place a call with yourself, wait for participants to accept invitation</string>