mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 09:25:49 +03:00
Merge branch 'develop' into feature/bma/url_preview_fixes
This commit is contained in:
commit
d2c912e5c4
16 changed files with 122 additions and 74 deletions
|
@ -1,4 +1,4 @@
|
|||
Changes in Element 1.X.X (2020-XX-XX)
|
||||
Changes in Element 1.0.17 (2020-XX-XX)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
|
@ -8,10 +8,12 @@ Improvements 🙌:
|
|||
- Open image from URL Preview (#2705)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Bug in WidgetContent.computeURL() (#2767)
|
||||
- Duplicate thumbs | Mobile reactions for 👍 and 👎 are not the same as web (#2776)
|
||||
- Join room by alias other federation error (#2778)
|
||||
- HTML unescaping for URL preview (#2766)
|
||||
- URL preview on reply fallback (#2756)
|
||||
- RTL: some arrows should be rotated in RTL (#2757)
|
||||
|
||||
Translations 🗣:
|
||||
-
|
||||
|
@ -26,7 +28,7 @@ Test:
|
|||
-
|
||||
|
||||
Other changes:
|
||||
-
|
||||
- Change app name from "Element (Riot.im)" to "Element"
|
||||
|
||||
Changes in Element 1.0.16 (2020-02-04)
|
||||
===================================================
|
||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=3db89524a3981819ff28c3f979236c1274a726e146ced0c8a2020417f9bc0782
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-all.zip
|
||||
distributionSha256Sum=1433372d903ffba27496f8d5af24265310d2da0d78bf6b4e5138831d4fe066e9
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -97,10 +97,11 @@ interface WidgetService {
|
|||
): LiveData<List<Widget>>
|
||||
|
||||
/**
|
||||
* Creates a new widget in a room. It makes sure you have the rights to handle this.
|
||||
* Creates and send a new widget in a room. It makes sure you have the rights to handle this.
|
||||
*
|
||||
* @param roomId: the room where you want to deactivate the widget.
|
||||
* @param widgetId: the widget to deactivate.
|
||||
* @param roomId the room where you want to create the widget.
|
||||
* @param widgetId the widget to create.
|
||||
* @param content the content of the widget
|
||||
* @param callback the matrix callback to listen for result.
|
||||
* @return Cancelable
|
||||
*/
|
||||
|
|
|
@ -35,3 +35,10 @@ fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder
|
|||
|
||||
return this
|
||||
}
|
||||
|
||||
fun StringBuilder.appendParamsToUrl(params: Map<String, String>): StringBuilder {
|
||||
params.forEach { (param, value) ->
|
||||
appendParamToUrl(param, value)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
|
|
@ -20,11 +20,12 @@ import org.matrix.android.sdk.api.MatrixConfiguration
|
|||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.widgets.WidgetURLFormatter
|
||||
import org.matrix.android.sdk.api.util.appendParamToUrl
|
||||
import org.matrix.android.sdk.api.util.appendParamsToUrl
|
||||
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
||||
import org.matrix.android.sdk.internal.session.widgets.token.GetScalarTokenTask
|
||||
import java.net.URLEncoder
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
|
@ -90,25 +91,4 @@ internal class DefaultWidgetURLFormatter @Inject constructor(private val integra
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendParamsToUrl(params: Map<String, String>): StringBuilder {
|
||||
params.forEach { (param, value) ->
|
||||
appendParamToUrl(param, value)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
|
||||
if (contains("?")) {
|
||||
append("&")
|
||||
} else {
|
||||
append("?")
|
||||
}
|
||||
|
||||
append(param)
|
||||
append("=")
|
||||
append(URLEncoder.encode(value, "utf-8"))
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,23 +65,31 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use
|
|||
)
|
||||
}
|
||||
|
||||
// Ref: https://github.com/matrix-org/matrix-widget-api/blob/master/src/templating/url-template.ts#L29-L33
|
||||
private fun WidgetContent.computeURL(roomId: String?, widgetId: String): String? {
|
||||
var computedUrl = url ?: return null
|
||||
val myUser = userDataSource.getUser(userId)
|
||||
computedUrl = computedUrl
|
||||
.replace("\$matrix_user_id", userId)
|
||||
.replace("\$matrix_display_name", myUser?.displayName ?: userId)
|
||||
.replace("\$matrix_avatar_url", myUser?.avatarUrl ?: "")
|
||||
.replace("\$matrix_widget_id", widgetId)
|
||||
|
||||
if (roomId != null) {
|
||||
computedUrl = computedUrl.replace("\$matrix_room_id", roomId)
|
||||
}
|
||||
for ((key, value) in data) {
|
||||
if (value is String) {
|
||||
computedUrl = computedUrl.replace("$$key", URLEncoder.encode(value, "utf-8"))
|
||||
}
|
||||
val keyValue = data.mapKeys { "\$${it.key}" }.toMutableMap()
|
||||
|
||||
keyValue[WIDGET_PATTERN_MATRIX_USER_ID] = userId
|
||||
keyValue[WIDGET_PATTERN_MATRIX_DISPLAY_NAME] = myUser?.getBestName() ?: userId
|
||||
keyValue[WIDGET_PATTERN_MATRIX_AVATAR_URL] = myUser?.avatarUrl ?: ""
|
||||
keyValue[WIDGET_PATTERN_MATRIX_WIDGET_ID] = widgetId
|
||||
keyValue[WIDGET_PATTERN_MATRIX_ROOM_ID] = roomId ?: ""
|
||||
|
||||
for ((key, value) in keyValue) {
|
||||
computedUrl = computedUrl.replace(key, URLEncoder.encode(value.toString(), "utf-8"))
|
||||
}
|
||||
return computedUrl
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Value to be replaced in URLS
|
||||
const val WIDGET_PATTERN_MATRIX_USER_ID = "\$matrix_user_id"
|
||||
const val WIDGET_PATTERN_MATRIX_DISPLAY_NAME = "\$matrix_display_name"
|
||||
const val WIDGET_PATTERN_MATRIX_AVATAR_URL = "\$matrix_avatar_url"
|
||||
const val WIDGET_PATTERN_MATRIX_WIDGET_ID = "\$matrix_widget_id"
|
||||
const val WIDGET_PATTERN_MATRIX_ROOM_ID = "\$matrix_room_id"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ android {
|
|||
}
|
||||
|
||||
release {
|
||||
resValue "string", "app_name", "Element (Riot.im)"
|
||||
resValue "string", "app_name", "Element"
|
||||
|
||||
resValue "bool", "debug_mode", "false"
|
||||
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package im.vector.app.features.call.conference
|
||||
|
||||
import android.net.Uri
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
|
@ -64,14 +63,12 @@ class JitsiCallViewModel @AssistedInject constructor(
|
|||
.subscribe {
|
||||
val jitsiWidget = it.firstOrNull()
|
||||
if (jitsiWidget != null) {
|
||||
val uri = Uri.parse(jitsiWidget.computedUrl)
|
||||
val confId = uri.getQueryParameter("confId")
|
||||
val ppt = jitsiWidget.computedUrl?.let { url -> JitsiWidgetProperties(url, stringProvider) }
|
||||
setState {
|
||||
copy(
|
||||
widget = Success(jitsiWidget),
|
||||
jitsiUrl = "https://${ppt?.domain}",
|
||||
confId = confId ?: "",
|
||||
confId = ppt?.confId ?: "",
|
||||
subject = roomName ?: ""
|
||||
)
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@ package im.vector.app.features.call.conference
|
|||
import android.net.Uri
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import java.net.URLDecoder
|
||||
|
||||
class JitsiWidgetProperties(private val uriString: String, val stringProvider: StringProvider) {
|
||||
val domain: String by lazy { configs["conferenceDomain"] ?: stringProvider.getString(R.string.preferred_jitsi_domain) }
|
||||
val confId: String? by lazy { configs["conferenceId"] }
|
||||
val displayName: String? by lazy { configs["displayName"] }
|
||||
val avatarUrl: String? by lazy { configs["avatarUrl"] }
|
||||
|
||||
|
@ -30,8 +32,9 @@ class JitsiWidgetProperties(private val uriString: String, val stringProvider: S
|
|||
private val configs: Map<String, String?> by lazy {
|
||||
configString?.split("&")
|
||||
?.map { it.split("=") }
|
||||
?.map { (key, value) -> key to value }
|
||||
?.filter { it.size == 2 }
|
||||
?.map { (key, value) -> key to URLDecoder.decode(value, "UTF-8") }
|
||||
?.toMap()
|
||||
?: mapOf()
|
||||
.orEmpty()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.jitsi.meet.sdk.JitsiMeetConferenceOptions
|
|||
import org.jitsi.meet.sdk.JitsiMeetView
|
||||
import org.jitsi.meet.sdk.JitsiMeetViewListener
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -154,13 +155,19 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
|
|||
}
|
||||
|
||||
override fun onConferenceTerminated(p0: MutableMap<String, Any>?) {
|
||||
finish()
|
||||
Timber.v("JitsiMeetViewListener.onConferenceTerminated()")
|
||||
// Do not finish if there is an error
|
||||
if (p0?.get("error") == null) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConferenceJoined(p0: MutableMap<String, Any>?) {
|
||||
Timber.v("JitsiMeetViewListener.onConferenceJoined()")
|
||||
}
|
||||
|
||||
override fun onConferenceWillJoin(p0: MutableMap<String, Any>?) {
|
||||
Timber.v("JitsiMeetViewListener.onConferenceWillJoin()")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -91,6 +91,7 @@ import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
|
|||
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
|
||||
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.appendParamToUrl
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
|
@ -400,15 +401,19 @@ 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.withVideo}" +
|
||||
"&displayName=\$matrix_display_name" +
|
||||
"&avatarUrl=\$matrix_avatar_url" +
|
||||
"&userId=\$matrix_user_id"
|
||||
|
||||
// https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/utils/WidgetUtils.ts#L469
|
||||
val url = buildString {
|
||||
append("https://app.element.io/jitsi.html")
|
||||
appendParamToUrl("confId", confId)
|
||||
append("#conferenceDomain=\$domain")
|
||||
append("&conferenceId=\$conferenceId")
|
||||
append("&isAudioOnly=\$isAudioOnly")
|
||||
append("&displayName=\$matrix_display_name")
|
||||
append("&avatarUrl=\$matrix_avatar_url")
|
||||
append("&userId=\$matrix_user_id")
|
||||
append("&roomId=\$matrix_room_id")
|
||||
append("&theme=\$theme")
|
||||
}
|
||||
val widgetEventContent = mapOf(
|
||||
"url" to url,
|
||||
"type" to WidgetType.Jitsi.legacy,
|
||||
|
|
|
@ -16,11 +16,16 @@
|
|||
|
||||
package im.vector.app.features.widgets
|
||||
|
||||
import android.content.Context
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||
import javax.inject.Inject
|
||||
|
||||
class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSessionHolder) {
|
||||
class WidgetArgsBuilder @Inject constructor(
|
||||
private val sessionHolder: ActiveSessionHolder,
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun buildIntegrationManagerArgs(roomId: String, integId: String?, screen: String?): WidgetArgs {
|
||||
|
@ -38,7 +43,8 @@ class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSes
|
|||
urlParams = mapOf(
|
||||
"screen" to normalizedScreen,
|
||||
"integ_id" to integId,
|
||||
"room_id" to roomId
|
||||
"room_id" to roomId,
|
||||
"theme" to getTheme()
|
||||
).filterNotNull()
|
||||
)
|
||||
}
|
||||
|
@ -54,7 +60,8 @@ class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSes
|
|||
widgetId = widgetId,
|
||||
urlParams = mapOf(
|
||||
"widgetId" to widgetId,
|
||||
"room_id" to roomId
|
||||
"room_id" to roomId,
|
||||
"theme" to getTheme()
|
||||
).filterNotNull()
|
||||
)
|
||||
}
|
||||
|
@ -66,7 +73,10 @@ class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSes
|
|||
baseUrl = baseUrl,
|
||||
kind = WidgetKind.ROOM,
|
||||
roomId = roomId,
|
||||
widgetId = widgetId
|
||||
widgetId = widgetId,
|
||||
urlParams = mapOf(
|
||||
"theme" to getTheme()
|
||||
).filterNotNull()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -74,4 +84,12 @@ class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSes
|
|||
private fun Map<String, String?>.filterNotNull(): Map<String, String> {
|
||||
return filterValues { it != null } as Map<String, String>
|
||||
}
|
||||
|
||||
private fun getTheme(): String {
|
||||
return if (ThemeUtils.isLightTheme(context)) {
|
||||
"light"
|
||||
} else {
|
||||
"dark"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
vector/src/main/res/drawable-ldrtl/ic_arrow_right.xml
Normal file
16
vector/src/main/res/drawable-ldrtl/ic_arrow_right.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="7dp"
|
||||
android:height="12dp"
|
||||
android:viewportWidth="7"
|
||||
android:viewportHeight="12">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M6,11l-5,-5 5,-5"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
tools:strokeColor="#FFAF0F" />
|
||||
</vector>
|
|
@ -1,14 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="7dp"
|
||||
android:height="12dp"
|
||||
android:viewportWidth="7"
|
||||
android:viewportHeight="12">
|
||||
<path
|
||||
android:pathData="M1,11l5,-5 -5,-5"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M1,11l5,-5 -5,-5"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
tools:strokeColor="#FFAF0F" />
|
||||
</vector>
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:padding="0dp"
|
||||
android:rotationY="@integer/rtl_mirror_flip"
|
||||
app:cornerRadius="17dp"
|
||||
app:icon="@drawable/ic_qr_code_add"
|
||||
app:iconGravity="textStart"
|
||||
|
|
|
@ -44,12 +44,13 @@
|
|||
android:id="@+id/messageVerificationRequestStub"
|
||||
style="@style/TimelineContentStubBaseParams"
|
||||
android:layout="@layout/item_timeline_event_verification_stub"
|
||||
tools:visibility="gone" />
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/messageVerificationDoneStub"
|
||||
style="@style/TimelineContentStubBaseParams"
|
||||
android:layout="@layout/item_timeline_event_status_tile_stub"
|
||||
tools:layout_marginTop="180dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
@ -59,8 +60,8 @@
|
|||
android:id="@+id/messageE2EDecoration"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_alignTop="@id/viewStubContainer"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_toStartOf="@id/viewStubContainer"
|
||||
android:visibility="gone"
|
||||
tools:src="@drawable/ic_shield_warning"
|
||||
|
@ -70,11 +71,11 @@
|
|||
android:id="@+id/messageFailToSendIndicator"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_alignTop="@+id/viewStubContainer"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_toEndOf="@+id/viewStubContainer"
|
||||
android:src="@drawable/ic_warning_badge"
|
||||
android:visibility="gone"
|
||||
android:layout_toEndOf="@+id/viewStubContainer"
|
||||
android:layout_alignTop="@+id/viewStubContainer"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue