mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 08:12:46 +02:00
Compare commits
3 Commits
sync-analy
...
feature/ex
Author | SHA1 | Date | |
---|---|---|---|
|
d932c5598b | ||
|
06770ca010 | ||
|
4b099dfef9 |
@@ -137,6 +137,7 @@ dependencies {
|
||||
|
||||
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||
kapt project(':moshi:codegen')
|
||||
|
||||
implementation "ru.noties.markwon:core:$markwon_version"
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.util
|
||||
|
||||
data class Extended<T : Any>(
|
||||
val wrapped: T,
|
||||
val undefined: JsonDict
|
||||
)
|
@@ -17,6 +17,7 @@
|
||||
package org.matrix.android.sdk.internal.auth
|
||||
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
|
||||
import org.matrix.android.sdk.internal.auth.data.PasswordLoginParams
|
||||
import org.matrix.android.sdk.internal.auth.data.RiotConfig
|
||||
@@ -97,12 +98,12 @@ internal interface AuthAPI {
|
||||
*/
|
||||
@Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000")
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
|
||||
fun login(@Body loginParams: PasswordLoginParams): Call<Credentials>
|
||||
fun login(@Body loginParams: PasswordLoginParams): Call<Extended<Credentials>>
|
||||
|
||||
// Unfortunately we cannot use interface for @Body parameter, so I duplicate the method for the type TokenLoginParams
|
||||
@Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000")
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
|
||||
fun login(@Body loginParams: TokenLoginParams): Call<Credentials>
|
||||
fun login(@Body loginParams: TokenLoginParams): Call<Extended<Credentials>>
|
||||
|
||||
/**
|
||||
* Ask the homeserver to reset the password associated with the provided email.
|
||||
|
@@ -41,6 +41,7 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
|
||||
internal class DefaultLoginWizard(
|
||||
okHttpClient: OkHttpClient,
|
||||
@@ -73,11 +74,11 @@ internal class DefaultLoginWizard(
|
||||
val loginParams = TokenLoginParams(
|
||||
token = loginToken
|
||||
)
|
||||
val credentials = executeRequest<Credentials>(null) {
|
||||
val credentials = executeRequest<Extended<Credentials>>(null) {
|
||||
apiCall = authAPI.login(loginParams)
|
||||
}
|
||||
|
||||
sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
|
||||
sessionCreator.createSession(credentials.wrapped, pendingSessionData.homeServerConnectionConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,11 +90,11 @@ internal class DefaultLoginWizard(
|
||||
} else {
|
||||
PasswordLoginParams.userIdentifier(login, password, deviceName)
|
||||
}
|
||||
val credentials = executeRequest<Credentials>(null) {
|
||||
val credentials = executeRequest<Extended<Credentials>>(null) {
|
||||
apiCall = authAPI.login(loginParams)
|
||||
}
|
||||
|
||||
sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
|
||||
sessionCreator.createSession(credentials.wrapped, pendingSessionData.homeServerConnectionConfig)
|
||||
}
|
||||
|
||||
override fun resetPassword(email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
|
@@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory
|
||||
import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import okhttp3.OkHttpClient
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface DirectLoginTask : Task<DirectLoginTask.Params, Session> {
|
||||
@@ -59,7 +60,7 @@ internal class DefaultDirectLoginTask @Inject constructor(
|
||||
val loginParams = PasswordLoginParams.userIdentifier(params.userId, params.password, params.deviceName)
|
||||
|
||||
val credentials = try {
|
||||
executeRequest<Credentials>(null) {
|
||||
executeRequest<Extended<Credentials>>(null) {
|
||||
apiCall = authAPI.login(loginParams)
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
@@ -75,7 +76,7 @@ internal class DefaultDirectLoginTask @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
return sessionCreator.createSession(credentials, params.homeServerConnectionConfig)
|
||||
return sessionCreator.createSession(credentials.wrapped, params.homeServerConnectionConfig)
|
||||
}
|
||||
|
||||
private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient {
|
||||
|
@@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
|
||||
import org.matrix.android.sdk.internal.network.parsing.CipherSuiteMoshiAdapter
|
||||
import org.matrix.android.sdk.internal.network.parsing.ExtendedAdapterFactory
|
||||
import org.matrix.android.sdk.internal.network.parsing.ForceToBooleanJsonAdapter
|
||||
import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory
|
||||
import org.matrix.android.sdk.internal.network.parsing.TlsVersionMoshiAdapter
|
||||
@@ -44,6 +45,7 @@ object MoshiProvider {
|
||||
.add(ForceToBooleanJsonAdapter())
|
||||
.add(CipherSuiteMoshiAdapter())
|
||||
.add(TlsVersionMoshiAdapter())
|
||||
.add(ExtendedAdapterFactory())
|
||||
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
|
||||
.registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT)
|
||||
.registerSubtype(MessageNoticeContent::class.java, MessageType.MSGTYPE_NOTICE)
|
||||
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.network.parsing
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.JsonKeys
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
|
||||
internal class ExtendedAdapterFactory() : JsonAdapter.Factory {
|
||||
|
||||
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||
if (Types.getRawType(type) != Extended::class.java) {
|
||||
return null
|
||||
}
|
||||
val wrappedType = (type as ParameterizedType).actualTypeArguments.firstOrNull() ?: return null
|
||||
val rawType = Types.getRawType(wrappedType)
|
||||
val adapter = moshi.adapter(rawType)
|
||||
return ExtendedAdapter(rawType, adapter, moshi.adapter(JSON_DICT_PARAMETERIZED_TYPE))
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExtendedAdapter(private val rawType: Class<*>,
|
||||
private val wrappedAdapter: JsonAdapter<*>,
|
||||
private val jsonDictAdapter: JsonAdapter<JsonDict>) : JsonAdapter<Extended<*>>() {
|
||||
|
||||
override fun fromJson(reader: JsonReader): Extended<*>? {
|
||||
val wrapped = wrappedAdapter.fromJson(reader.peekJson()) ?: return null
|
||||
val allValues = jsonDictAdapter.fromJson(reader) ?: return null
|
||||
val definedKeys = JsonKeys.jsonKeysByClasses[rawType].orEmpty()
|
||||
val filteredValues = allValues.filterNot {
|
||||
definedKeys.contains(it.key)
|
||||
}
|
||||
return Extended(wrapped, filteredValues)
|
||||
}
|
||||
|
||||
override fun toJson(writer: JsonWriter, value: Extended<*>?) {
|
||||
// WILL SEE
|
||||
}
|
||||
}
|
@@ -50,7 +50,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||
}
|
||||
val newJoinEvents = params.syncResponse.join
|
||||
.mapNotNull { (key, value) ->
|
||||
value.timeline?.events?.map { it.copy(roomId = key) }
|
||||
value.wrapped.timeline?.events?.map { it.copy(roomId = key) }
|
||||
}
|
||||
.flatten()
|
||||
val inviteEvents = params.syncResponse.invite
|
||||
@@ -80,7 +80,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||
|
||||
val allRedactedEvents = params.syncResponse.join
|
||||
.asSequence()
|
||||
.mapNotNull { (_, value) -> value.timeline?.events }
|
||||
.mapNotNull { (_, value) -> value.wrapped.timeline?.events }
|
||||
.flatten()
|
||||
.filter { it.type == EventType.REDACTION }
|
||||
.mapNotNull { it.redacts }
|
||||
|
@@ -204,7 +204,7 @@ internal interface RoomAPI {
|
||||
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state")
|
||||
fun getRoomState(@Path("roomId") roomId: String) : Call<List<Event>>
|
||||
fun getRoomState(@Path("roomId") roomId: String): Call<List<Event>>
|
||||
|
||||
/**
|
||||
* Send a relation event to a room.
|
||||
|
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomNameContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService
|
||||
@@ -47,7 +48,6 @@ import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver
|
||||
import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver
|
||||
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
|
||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncSummary
|
||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -62,7 +62,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
||||
roomId: String,
|
||||
membership: Membership? = null,
|
||||
roomSummary: RoomSyncSummary? = null,
|
||||
unreadNotifications: RoomSyncUnreadNotifications? = null,
|
||||
unreadNotifications: JsonDict? = null,
|
||||
updateMembers: Boolean = false,
|
||||
inviterId: String? = null) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
|
||||
@@ -78,8 +78,8 @@ internal class RoomSummaryUpdater @Inject constructor(
|
||||
roomSummaryEntity.joinedMembersCount = roomSummary.joinedMembersCount
|
||||
}
|
||||
}
|
||||
roomSummaryEntity.highlightCount = unreadNotifications?.highlightCount ?: 0
|
||||
roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?: 0
|
||||
roomSummaryEntity.highlightCount = (unreadNotifications?.get("highlight_count") as? Double)?.toInt() ?: 0
|
||||
roomSummaryEntity.notificationCount = (unreadNotifications?.get("notification_count") as? Double)?.toInt() ?: 0
|
||||
|
||||
if (membership != null) {
|
||||
roomSummaryEntity.membership = membership
|
||||
|
@@ -27,6 +27,8 @@ import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
@@ -78,7 +80,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
private val timelineInput: TimelineInput) {
|
||||
|
||||
sealed class HandlingStrategy {
|
||||
data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
|
||||
data class JOINED(val data: Map<String, Extended<RoomSync>>) : HandlingStrategy()
|
||||
data class INVITED(val data: Map<String, InvitedRoomSync>) : HandlingStrategy()
|
||||
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
|
||||
}
|
||||
@@ -125,12 +127,12 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
|
||||
private fun handleJoinedRoom(realm: Realm,
|
||||
roomId: String,
|
||||
roomSync: RoomSync,
|
||||
roomSyncExtended: Extended<RoomSync>,
|
||||
isInitialSync: Boolean,
|
||||
insertType: EventInsertType,
|
||||
syncLocalTimestampMillis: Long): RoomEntity {
|
||||
Timber.v("Handle join sync for room $roomId")
|
||||
|
||||
val roomSync = roomSyncExtended.wrapped
|
||||
var ephemeralResult: EphemeralResult? = null
|
||||
if (roomSync.ephemeral?.events?.isNotEmpty() == true) {
|
||||
ephemeralResult = handleEphemeral(realm, roomId, roomSync.ephemeral, isInitialSync)
|
||||
@@ -186,12 +188,13 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
|
||||
roomTypingUsersHandler.handle(realm, roomId, ephemeralResult)
|
||||
roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.JOIN)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
roomSummaryUpdater.update(
|
||||
realm,
|
||||
roomId,
|
||||
Membership.JOIN,
|
||||
roomSync.summary,
|
||||
roomSync.unreadNotifications,
|
||||
roomSyncExtended.undefined["unread_notifications"] as? JsonDict,
|
||||
updateMembers = hasRoomMember
|
||||
)
|
||||
return roomEntity
|
||||
@@ -267,7 +270,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
roomEntity.chunks.clearWith { it.deleteOnCascade(deleteStateEvents = true, canDeleteRoot = true) }
|
||||
roomTypingUsersHandler.handle(realm, roomId, null)
|
||||
roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.LEAVE)
|
||||
roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications)
|
||||
roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, null)
|
||||
return roomEntity
|
||||
}
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import org.matrix.android.sdk.internal.network.TimeOutInterceptor
|
||||
import org.matrix.android.sdk.internal.session.sync.model.SyncResponse
|
||||
@@ -33,5 +34,5 @@ internal interface SyncAPI {
|
||||
@Header(TimeOutInterceptor.CONNECT_TIMEOUT) connectTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT,
|
||||
@Header(TimeOutInterceptor.READ_TIMEOUT) readTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT,
|
||||
@Header(TimeOutInterceptor.WRITE_TIMEOUT) writeTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT
|
||||
): Call<SyncResponse>
|
||||
): Call<Extended<SyncResponse>>
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
|
||||
import org.matrix.android.sdk.R
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.TimeOutInterceptor
|
||||
@@ -81,13 +82,14 @@ internal class DefaultSyncTask @Inject constructor(
|
||||
|
||||
val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT)
|
||||
|
||||
val syncResponse = executeRequest<SyncResponse>(globalErrorReceiver) {
|
||||
val syncResponse = executeRequest<Extended<SyncResponse>>(globalErrorReceiver) {
|
||||
apiCall = syncAPI.sync(
|
||||
params = requestParams,
|
||||
readTimeOut = readTimeOut
|
||||
)
|
||||
}
|
||||
syncResponseHandler.handleResponse(syncResponse, token)
|
||||
Timber.v("Sync Response undefined: ${syncResponse.undefined}")
|
||||
syncResponseHandler.handleResponse(syncResponse.wrapped, token)
|
||||
if (isInitialSync) {
|
||||
initialSyncProgressService.endAll()
|
||||
}
|
||||
|
@@ -41,11 +41,6 @@ internal data class RoomSync(
|
||||
*/
|
||||
@Json(name = "account_data") val accountData: RoomSyncAccountData? = null,
|
||||
|
||||
/**
|
||||
* The notification counts for the room.
|
||||
*/
|
||||
@Json(name = "unread_notifications") val unreadNotifications: RoomSyncUnreadNotifications? = null,
|
||||
|
||||
/**
|
||||
* The room summary
|
||||
*/
|
||||
|
@@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.sync.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.util.Extended
|
||||
|
||||
// RoomsSyncResponse represents the rooms list in server sync v2 response.
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -24,7 +25,7 @@ internal data class RoomsSyncResponse(
|
||||
/**
|
||||
* Joined rooms: keys are rooms ids.
|
||||
*/
|
||||
@Json(name = "join") val join: Map<String, RoomSync> = emptyMap(),
|
||||
@Json(name = "join") val join: Map<String, Extended<RoomSync>> = emptyMap(),
|
||||
|
||||
/**
|
||||
* Invitations. The rooms that the user has been invited to: keys are rooms ids.
|
||||
|
1
moshi/codegen/.gitignore
vendored
Normal file
1
moshi/codegen/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
53
moshi/codegen/build.gradle
Normal file
53
moshi/codegen/build.gradle
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
|
||||
def kotlin_poet_version = "1.6.0"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation("com.squareup.moshi:moshi:1.11.0")
|
||||
implementation("com.squareup:kotlinpoet:$kotlin_poet_version")
|
||||
implementation("com.squareup:kotlinpoet-metadata-specs:$kotlin_poet_version")
|
||||
implementation("com.squareup:kotlinpoet-classinspector-elements:$kotlin_poet_version")
|
||||
}
|
||||
|
||||
sourceCompatibility = "1.7"
|
||||
targetCompatibility = "1.7"
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.moshi.codegen
|
||||
|
||||
import com.squareup.kotlinpoet.AnnotationSpec
|
||||
import com.squareup.kotlinpoet.ClassName
|
||||
import com.squareup.kotlinpoet.CodeBlock
|
||||
import com.squareup.kotlinpoet.FileSpec
|
||||
import com.squareup.kotlinpoet.KModifier
|
||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||
import com.squareup.kotlinpoet.PropertySpec
|
||||
import com.squareup.kotlinpoet.TypeSpec
|
||||
import com.squareup.kotlinpoet.TypeVariableName
|
||||
import com.squareup.kotlinpoet.asClassName
|
||||
import com.squareup.kotlinpoet.asTypeName
|
||||
import com.squareup.kotlinpoet.classinspector.elements.ElementsClassInspector
|
||||
import com.squareup.kotlinpoet.joinToCode
|
||||
import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
|
||||
import com.squareup.kotlinpoet.metadata.isData
|
||||
import com.squareup.kotlinpoet.metadata.isInner
|
||||
import com.squareup.kotlinpoet.metadata.specs.ClassInspector
|
||||
import com.squareup.kotlinpoet.metadata.specs.toTypeSpec
|
||||
import com.squareup.kotlinpoet.metadata.toImmutableKmClass
|
||||
import com.squareup.kotlinpoet.tag
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.io.IOException
|
||||
import javax.annotation.processing.AbstractProcessor
|
||||
import javax.annotation.processing.Filer
|
||||
import javax.annotation.processing.Messager
|
||||
import javax.annotation.processing.ProcessingEnvironment
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.SourceVersion
|
||||
import javax.lang.model.element.AnnotationMirror
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.lang.model.util.Elements
|
||||
import javax.lang.model.util.Types
|
||||
import javax.tools.Diagnostic
|
||||
|
||||
@KotlinPoetMetadataPreview class JsonClassProcessor : AbstractProcessor() {
|
||||
|
||||
private val annotation = JsonClass::class.java
|
||||
|
||||
private lateinit var filer: Filer
|
||||
private lateinit var messager: Messager
|
||||
private lateinit var elements: Elements
|
||||
private lateinit var types: Types
|
||||
private lateinit var options: Map<String, String>
|
||||
private lateinit var classInspector: ClassInspector
|
||||
|
||||
override fun init(processingEnv: ProcessingEnvironment) {
|
||||
super.init(processingEnv)
|
||||
filer = processingEnv.filer
|
||||
messager = processingEnv.messager
|
||||
elements = processingEnv.elementUtils
|
||||
types = processingEnv.typeUtils
|
||||
options = processingEnv.options
|
||||
classInspector = ElementsClassInspector.create(elements, types)
|
||||
}
|
||||
|
||||
override fun getSupportedAnnotationTypes(): Set<String> = setOf(annotation.canonicalName)
|
||||
|
||||
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
|
||||
|
||||
@KotlinPoetMetadataPreview
|
||||
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
if (roundEnv.errorRaised()) {
|
||||
return false
|
||||
}
|
||||
val jsonKeysByClass = HashMap<ClassName, Set<String>>()
|
||||
roundEnv.getElementsAnnotatedWith(annotation)
|
||||
.asSequence()
|
||||
.map { it as TypeElement }
|
||||
.forEach { type ->
|
||||
val jsonClass = type.getAnnotation(JsonClass::class.java)
|
||||
if (!jsonClass.generateAdapter) return@forEach
|
||||
val kmClass = type.toImmutableKmClass()
|
||||
if (!kmClass.isData) {
|
||||
return@forEach
|
||||
}
|
||||
if (kmClass.isInner) {
|
||||
return@forEach
|
||||
}
|
||||
val dataClassSpec = kmClass.toTypeSpec(classInspector)
|
||||
val jsonKeys = dataClassSpec.primaryConstructor?.parameters?.map {
|
||||
it.annotations.jsonName() ?: it.name
|
||||
}.orEmpty().toSet()
|
||||
val typeName: String = type.qualifiedName.toString()
|
||||
val className = ClassName("", typeName)
|
||||
jsonKeysByClass[className] = jsonKeys
|
||||
}
|
||||
|
||||
val packageName = "org.matrix.android.sdk.internal"
|
||||
val generatedClassName = ClassName(packageName, "JsonKeys")
|
||||
val objectBuilder = TypeSpec.objectBuilder(generatedClassName).addModifiers(KModifier.INTERNAL)
|
||||
val setTypeName = Set::class.asClassName().parameterizedBy(String::class.asTypeName())
|
||||
val anyClassName = Class::class.asClassName().parameterizedBy(TypeVariableName("*"))
|
||||
val mapTypeName = Map::class.asClassName().parameterizedBy(anyClassName, setTypeName)
|
||||
|
||||
val jsonKeysPropertySpec = PropertySpec.builder("jsonKeysByClasses", mapTypeName)
|
||||
.addModifiers(KModifier.PUBLIC)
|
||||
.initializer(
|
||||
"mapOf(\n%L\n)",
|
||||
jsonKeysByClass
|
||||
.map {
|
||||
val setOfBlock = it.value.map { jsonKey ->
|
||||
CodeBlock.of("%S", jsonKey)
|
||||
}.joinToCode(", ")
|
||||
CodeBlock.of("%L::class.java to setOf(%L)", it.key, setOfBlock)
|
||||
}.joinToCode(", \n")
|
||||
)
|
||||
.build()
|
||||
|
||||
objectBuilder.addProperty(jsonKeysPropertySpec)
|
||||
|
||||
try {
|
||||
val fileSpec = FileSpec.builder(packageName, "JsonKeys")
|
||||
.addType(objectBuilder.build())
|
||||
.build()
|
||||
fileSpec.writeTo(filer)
|
||||
} catch (e: IOException) {
|
||||
messager.printMessage(Diagnostic.Kind.NOTE, e.toString())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun List<AnnotationSpec>?.jsonName(): String? {
|
||||
if (this == null) return null
|
||||
return find { it.typeName == Json::class.asClassName() }?.let { annotation ->
|
||||
val mirror = requireNotNull(annotation.tag<AnnotationMirror>()) {
|
||||
"Could not get the annotation mirror from the annotation spec"
|
||||
}
|
||||
mirror.elementValues.entries.single {
|
||||
it.key.simpleName.contentEquals("name")
|
||||
}.value.value as String
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
org.matrix.moshi.codegen.JsonClassProcessor
|
@@ -1,3 +1,4 @@
|
||||
include ':moshi:codegen'
|
||||
include ':vector'
|
||||
include ':matrix-sdk-android'
|
||||
include ':matrix-sdk-android-rx'
|
||||
|
Reference in New Issue
Block a user