Merge branch 'release/0.7.0'

This commit is contained in:
Benoit Marty 2019-10-24 14:37:52 +02:00
commit 01452efd8d
1132 changed files with 12341 additions and 8569 deletions

View File

@ -54,4 +54,13 @@ steps:
# Code quality
- label: "Code quality"
command: "./tools/check/check_code_quality.sh"
command:
- "./tools/check/check_code_quality.sh"
- label: "ktlint"
command:
- "curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.34.2/ktlint && chmod a+x ktlint"
- "./ktlint --android --experimental -v"
plugins:
- docker#v3.1.0:
image: "openjdk"

32
.editorconfig Normal file
View File

@ -0,0 +1,32 @@
# For ktlint configuration. Ref: https://ktlint.github.io/
[*.{kt,kts}]
# possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely)
indent_size=unset
# true (recommended) / false
insert_final_newline=true
# possible values: number (e.g. 120) (package name, imports & comments are ignored), "off"
# it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide)
max_line_length=off
# Comma-separated list of rules to disable (Since 0.34.0)
# Note that rules in any ruleset other than the standard ruleset will need to be prefixed
# by the ruleset identifier.
disabled_rules=no-wildcard-imports,no-multi-spaces,colon-spacing,chain-wrapping,import-ordering,experimental:annotation
# The following (so far identified) rules are kept:
# no-blank-line-before-rbrace
# final-newline
# no-consecutive-blank-lines
# comment-spacing
# filename
# comma-spacing
# paren-spacing
# op-spacing
# string-template
# no-unused-imports
# curly-spacing
# no-semi
# no-empty-class-body
# experimental:multiline-if-else
# experimental:no-empty-first-line-in-method-block

14
.gitignore vendored
View File

@ -1,14 +1,18 @@
*.iml
.gradle
/local.properties
.idea/*
/.idea/*
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
# idea files: exclude everything except dictionnaries
.idea/caches
.idea/codeStyles
.idea/libraries
.idea/*.xml
.DS_Store
/build
/captures
.externalNativeBuild
/tmp
ktlint
.idea/copyright/New_vector.xml
.idea/copyright/profiles_settings.xml

View File

@ -0,0 +1,19 @@
<component name="ProjectDictionaryState">
<dictionary name="bmarty">
<words>
<w>backstack</w>
<w>bytearray</w>
<w>ciphertext</w>
<w>decryptor</w>
<w>emoji</w>
<w>emojis</w>
<w>hmac</w>
<w>ktlint</w>
<w>linkified</w>
<w>linkify</w>
<w>megolm</w>
<w>pbkdf</w>
<w>pkcs</w>
</words>
</dictionary>
</component>

View File

@ -1,3 +1,29 @@
Changes in RiotX 0.7.0 (2019-10-24)
===================================================
Features:
- Share elements from other app to RiotX (#58)
- Read marker (#84)
- Add ability to report content (#515)
Improvements:
- Persist active tab between sessions (#503)
- Do not upload file too big for the homeserver (#587)
- Attachments: start using system pickers (#52)
- Mark all messages as read (#396)
Other changes:
- Accessibility improvements to read receipts in the room timeline and reactions emoji chooser
Bugfix:
- Fix issue on upload error in loop (#587)
- Fix opening a permalink: the targeted event is displayed twice (#556)
- Fix opening a permalink paginates all the history up to the last event (#282)
- after login, the icon in the top left is a green 'A' for (all communities) rather than my avatar (#267)
- Picture uploads are unreliable, pictures are shown in wrong aspect ratio on desktop client (#517)
- Invitation notifications are not dismissed automatically if room is joined from another client (#347)
- Opening links from RiotX reuses browser tab (#599)
Changes in RiotX 0.6.1 (2019-09-24)
===================================================
@ -141,21 +167,21 @@ Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-a
Changes in RiotX 0.0.0 (2019-XX-XX)
===================================================
Features:
Features:
-
Improvements:
Improvements 🙌:
-
Other changes:
-
Bugfix:
Bugfix 🐛:
-
Translations:
Translations 🗣:
-
Build:
Build 🧱:
-

View File

@ -40,19 +40,45 @@ Please add a line to the top of the file `CHANGES.md` describing your change.
Make sure the following commands execute without any error:
> ./tools/check/check_code_quality.sh
#### Internal tool
> ./gradlew lintGplayRelease
<pre>
./tools/check/check_code_quality.sh
</pre>
#### ktlint
<pre>
curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.34.2/ktlint && chmod a+x ktlint
./ktlint --android --experimental -v
</pre>
Note that you can run
<pre>
./ktlint --android --experimental -v -F
</pre>
For ktlint to fix some detected errors for you (you still have to check and commit the fix of course)
#### lint
<pre>
./gradlew lintGplayRelease
./gradlew lintFdroidRelease
</pre>
### Unit tests
Make sure the following commands execute without any error:
> ./gradlew testGplayReleaseUnitTest
<pre>
./gradlew testGplayReleaseUnitTest
</pre>
### Tests
RiotX is currently supported on Android Jelly Bean (API 16+): please test your change on an Android device (or Android emulator) running with API 16. Many issues can happen (including crashes) on older devices.
RiotX is currently supported on Android KitKat (API 19+): please test your change on an Android device (or Android emulator) running with API 19. Many issues can happen (including crashes) on older devices.
Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
### Internationalisation

View File

@ -1,9 +1,7 @@
import javax.tools.JavaCompiler
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.21'
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
@ -12,11 +10,11 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'com.google.gms:google-services:4.2.0'
classpath "com.airbnb.okreplay:gradle-plugin:1.4.0"
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.google.gms:google-services:4.3.2'
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
classpath 'com.google.android.gms:oss-licenses-plugin:0.9.5'
// NOTE: Do not place your application dependencies here; they belong
@ -61,6 +59,11 @@ allprojects {
]
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
// Warnings are potential errors, so stop ignoring them
kotlinOptions.allWarningsAsErrors = true
}
afterEvaluate {
extensions.findByName("kapt")?.arguments {
arg("dagger.gradle.incremental", "enabled")

View File

@ -1,6 +1,6 @@
#Tue Mar 19 09:53:05 CET 2019
#Fri Sep 27 10:10:35 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

View File

@ -5,8 +5,6 @@ apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
@ -14,7 +12,6 @@ android {
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -29,12 +26,14 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation project(":matrix-sdk-android")
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
// Paging

View File

@ -20,7 +20,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import io.reactivex.Observable
import io.reactivex.android.MainThreadDisposable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
private class LiveDataObservable<T>(

View File

@ -19,7 +19,6 @@ package im.vector.matrix.rx
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import io.reactivex.CompletableEmitter
import io.reactivex.SingleEmitter
internal class MatrixCallbackCompletable<T>(private val completableEmitter: CompletableEmitter) : MatrixCallback<T> {

View File

@ -14,13 +14,11 @@
* limitations under the License.
*/
package im.vector.riotx.core.dialogs
package im.vector.matrix.rx
import android.content.Context
import im.vector.matrix.android.api.util.Optional
import io.reactivex.Observable
internal class DialogSendItemAdapter(context: Context, items: MutableList<DialogListItem>) : DialogAdapter(context) {
init {
addAll(items)
}
fun <T : Any> Observable<Optional<T>>.unwrap(): Observable<T> {
return filter { it.hasValue() }.map { it.get() }
}

View File

@ -22,25 +22,34 @@ import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.Optional
import io.reactivex.Observable
import io.reactivex.Single
class RxRoom(private val room: Room) {
fun liveRoomSummary(): Observable<RoomSummary> {
return room.liveRoomSummary().asObservable()
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
return room.getRoomSummaryLive().asObservable()
}
fun liveRoomMemberIds(): Observable<List<String>> {
return room.getRoomMemberIdsLive().asObservable()
}
fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> {
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {
return room.getEventSummaryLive(eventId).asObservable()
}
fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> {
return room.liveTimeLineEvent(eventId).asObservable()
fun liveTimelineEvent(eventId: String): Observable<Optional<TimelineEvent>> {
return room.getTimeLineEventLive(eventId).asObservable()
}
fun liveReadMarker(): Observable<Optional<String>> {
return room.getReadMarkerLive().asObservable()
}
fun liveReadReceipt(): Observable<Optional<String>> {
return room.getMyReadReceiptLive().asObservable()
}
fun loadRoomMembersIfNeeded(): Single<Unit> = Single.create {
@ -58,7 +67,6 @@ class RxRoom(private val room: Room) {
fun liveDrafts(): Observable<List<UserDraft>> {
return room.getDraftsLive().asObservable()
}
}
fun Room.rx(): RxRoom {

View File

@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.Optional
import io.reactivex.Observable
import io.reactivex.Single
@ -45,6 +46,10 @@ class RxSession(private val session: Session) {
return session.livePushers().asObservable()
}
fun liveUser(userId: String): Observable<Optional<User>> {
return session.liveUser(userId).asObservable().distinctUntilChanged()
}
fun liveUsers(): Observable<List<User>> {
return session.liveUsers().asObservable()
}
@ -66,7 +71,6 @@ class RxSession(private val session: Session) {
fun joinRoom(roomId: String, viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
session.joinRoom(roomId, viaServers, MatrixCallbackSingle(it)).toSingle(it)
}
}
fun Session.rx(): RxSession {

View File

@ -67,6 +67,10 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
static def gitRevision() {
@ -86,41 +90,43 @@ static def gitRevisionDate() {
dependencies {
def arrow_version = "0.8.0"
def support_version = '1.1.0-beta01'
def arrow_version = "0.8.2"
def moshi_version = '1.8.0'
def lifecycle_version = '2.0.0'
def coroutines_version = "1.0.1"
def markwon_version = '3.0.0'
def daggerVersion = '2.23.1'
def lifecycle_version = '2.1.0'
def coroutines_version = "1.3.2"
def markwon_version = '3.1.0'
def daggerVersion = '2.24'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation "androidx.appcompat:appcompat:1.1.0-rc01"
implementation "androidx.recyclerview:recyclerview:1.1.0-beta01"
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.1.0-beta05"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// Network
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
implementation 'com.squareup.okhttp3:okhttp:3.14.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-moshi:2.6.2'
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
implementation 'com.novoda:merlin:1.2.0'
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
implementation "ru.noties.markwon:core:$markwon_version"
// Image
implementation 'androidx.exifinterface:exifinterface:1.0.0'
// Database
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
// Work
implementation "androidx.work:work-runtime-ktx:2.1.0-rc01"
implementation "androidx.work:work-runtime-ktx:2.3.0-alpha01"
// FP
implementation "io.arrow-kt:arrow-core:$arrow_version"
@ -132,24 +138,24 @@ dependencies {
// DI
implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
// Logging
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
// Bus
implementation 'org.greenrobot:eventbus:3.1.1'
debugImplementation 'com.airbnb.okreplay:okreplay:1.4.0'
releaseImplementation 'com.airbnb.okreplay:noop:1.4.0'
androidTestImplementation 'com.airbnb.okreplay:espresso:1.4.0'
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:4.0.2'
testImplementation 'org.robolectric:robolectric:4.3'
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
testImplementation "io.mockk:mockk:1.8.13.kotlin13"
testImplementation 'io.mockk:mockk:1.9.3.kotlin12'
testImplementation 'org.amshove.kluent:kluent-android:1.44'
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
@ -159,7 +165,7 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
androidTestImplementation "io.mockk:mockk-android:1.8.13.kotlin13"
androidTestImplementation 'io.mockk:mockk-android:1.9.3.kotlin12'
androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

View File

@ -28,7 +28,6 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
internal class AuthenticatorTest : InstrumentedTest {
@ -50,7 +49,6 @@ internal class AuthenticatorTest : InstrumentedTest {
@UiThreadTest
@OkReplay(tape = "auth", mode = TapeMode.READ_WRITE)
fun auth() {
}
companion object {
@ -59,6 +57,4 @@ internal class AuthenticatorTest : InstrumentedTest {
val grantExternalStoragePermissionRule: GrantPermissionRule =
GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}

View File

@ -62,8 +62,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
JsonCanonicalizer.canonicalize("{\"a\":\"\\\"\"}"))
}
/* ==========================================================================================
* Test from https://matrix.org/docs/spec/appendices.html#examples
* ========================================================================================== */
@ -74,7 +72,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
JsonCanonicalizer.canonicalize("""{}"""))
}
@Test
fun matrixOrg002Test() {
assertEquals("""{"one":1,"two":"Two"}""",
@ -84,7 +81,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
}"""))
}
@Test
fun matrixOrg003Test() {
assertEquals("""{"a":"1","b":"2"}""",
@ -94,14 +90,12 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
}"""))
}
@Test
fun matrixOrg004Test() {
assertEquals("""{"a":"1","b":"2"}""",
JsonCanonicalizer.canonicalize("""{"b":"2","a":"1"}"""))
}
@Test
fun matrixOrg005Test() {
assertEquals("""{"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}""",
@ -126,7 +120,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
}"""))
}
@Test
fun matrixOrg006Test() {
assertEquals("""{"a":"日本語"}""",
@ -135,7 +128,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
}"""))
}
@Test
fun matrixOrg007Test() {
assertEquals("""{"日":1,"本":2}""",
@ -145,7 +137,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
}"""))
}
@Test
fun matrixOrg008Test() {
assertEquals("""{"a":"日"}""",

View File

@ -35,7 +35,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
internal class ChunkEntityTest : InstrumentedTest {
@ -48,7 +47,6 @@ internal class ChunkEntityTest : InstrumentedTest {
monarchy = Monarchy.Builder().setRealmConfiguration(testConfig).build()
}
@Test
fun add_shouldAdd_whenNotAlreadyIncluded() {
monarchy.runTransactionSync { realm ->
@ -194,5 +192,4 @@ internal class ChunkEntityTest : InstrumentedTest {
chunk1.nextToken shouldEqual nextToken
}
}
}

View File

@ -32,6 +32,4 @@ internal class FakeGetContextOfEventTask constructor(private val tokenChunkEvent
)
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, PaginationDirection.BACKWARDS)
}
}

View File

@ -28,6 +28,4 @@ internal class FakePaginationTask @Inject constructor(private val tokenChunkEven
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
}
}

View File

@ -88,6 +88,4 @@ object RoomDataHelper {
roomEntity.addOrUpdate(chunkEntity)
}
}
}

View File

@ -81,6 +81,4 @@ internal class TimelineTest : InstrumentedTest {
// timelineEvents.size shouldEqual initialLoad + paginationCount
// timeline.dispose()
// }
}

View File

@ -22,6 +22,7 @@ import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import timber.log.Timber
import java.io.IOException
import java.nio.charset.Charset
import javax.inject.Inject
@ -51,13 +52,18 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
var compressed = false
var curlCmd = "curl"
if (curlOptions != null) {
curlCmd += " " + curlOptions!!
curlOptions?.let {
curlCmd += " $it"
}
curlCmd += " -X " + request.method()
curlCmd += " -X " + request.method
val requestBody = request.body()
val requestBody = request.body
if (requestBody != null) {
if (requestBody.contentLength() > 100_000) {
Timber.w("Unable to log curl command data, size is too big (${requestBody.contentLength()})")
// Ensure the curl command will failed
curlCmd += "DATA IS TOO BIG"
} else {
val buffer = Buffer()
requestBody.writeTo(buffer)
var charset: Charset? = UTF8
@ -68,10 +74,11 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
// try to keep to a single line and use a subshell to preserve any line breaks
curlCmd += " --data $'" + buffer.readString(charset!!).replace("\n", "\\n") + "'"
}
}
val headers = request.headers()
val headers = request.headers
var i = 0
val count = headers.size()
val count = headers.size
while (i < count) {
val name = headers.name(i)
val value = headers.value(i)
@ -82,7 +89,7 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
i++
}
curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url().toString()
curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url.toString()
// Replace localhost for emulator by localhost for shell
.replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/")
+ "'")
@ -90,7 +97,7 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
// Add Json formatting
curlCmd += " | python -m json.tool"
logger.log("--- cURL (" + request.url() + ")")
logger.log("--- cURL (" + request.url + ")")
logger.log(curlCmd)
return chain.proceed(request)

View File

@ -51,7 +51,6 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
// Finally this is not a JSON string...
Timber.e(e)
}
} else if (message.startsWith("[")) {
// JSON Array detected
try {
@ -61,7 +60,6 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
// Finally not JSON...
Timber.e(e)
}
}
// Else not a json string to log
}

View File

@ -38,7 +38,6 @@ data class MatrixConfiguration(
interface Provider {
fun providesMatrixConfiguration(): MatrixConfiguration
}
}
/**
@ -98,5 +97,4 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
}
}
}

View File

@ -27,7 +27,7 @@ interface MatrixCallback<in T> {
* @param data the data successfully returned from the async function
*/
fun onSuccess(data: T) {
//no-op
// no-op
}
/**
@ -35,7 +35,6 @@ interface MatrixCallback<in T> {
* @param failure the failure data returned from the async function
*/
fun onFailure(failure: Throwable) {
//no-op
// no-op
}
}

View File

@ -16,7 +16,6 @@
package im.vector.matrix.android.api
/**
* This class contains pattern to match the different Matrix ids
*/
@ -154,6 +153,5 @@ object MatrixPatterns {
return if (index == -1) {
null
} else matrixId.substring(index + 1)
}
}

View File

@ -253,13 +253,5 @@ data class HomeServerConnectionConfig(
forceUsageTlsVersions
)
}
}
}

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.api.extensions
import im.vector.matrix.android.api.comparators.DatedObjectComparators
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import java.util.Collections
/* ==========================================================================================
* MXDeviceInfo
@ -29,7 +28,6 @@ fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint()
?.chunked(4)
?.joinToString(separator = " ")
fun List<DeviceInfo>.sortByLastSeen() {
Collections.sort(this, DatedObjectComparators.descComparator)
fun MutableList<DeviceInfo>.sortByLastSeen() {
sortWith(DatedObjectComparators.descComparator)
}

View File

@ -42,5 +42,4 @@ sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
data class CryptoError(val error: MXCryptoError) : Failure(error)
abstract class FeatureFailure : Failure()
}

View File

@ -33,7 +33,6 @@ data class MatrixError(
@Json(name = "limit_type") val limitType: String? = null,
@Json(name = "admin_contact") val adminUri: String? = null) {
companion object {
const val FORBIDDEN = "M_FORBIDDEN"
const val UNKNOWN = "M_UNKNOWN"

View File

@ -30,9 +30,9 @@ object MatrixLinkify {
*
* @param spannable the text in which the matrix items has to be clickable.
*/
fun addLinks(spannable: Spannable?, callback: MatrixPermalinkSpan.Callback?): Boolean {
fun addLinks(spannable: Spannable, callback: MatrixPermalinkSpan.Callback?): Boolean {
// sanity checks
if (spannable.isNullOrEmpty()) {
if (spannable.isEmpty()) {
return false
}
val text = spannable.toString()
@ -51,5 +51,4 @@ object MatrixLinkify {
}
return hasMatch
}
}

View File

@ -35,6 +35,4 @@ class MatrixPermalinkSpan(private val url: String,
override fun onClick(widget: View) {
callback?.onUrlClicked(url)
}
}

View File

@ -33,5 +33,4 @@ sealed class PermalinkData {
data class GroupLink(val groupId: String) : PermalinkData()
data class FallbackLink(val uri: Uri) : PermalinkData()
}

View File

@ -16,7 +16,6 @@
package im.vector.matrix.android.api.permalinks
import android.text.TextUtils
import im.vector.matrix.android.api.session.events.model.Event
/**
@ -48,10 +47,9 @@ object PermalinkFactory {
* @return the permalink, or null in case of error
*/
fun createPermalink(id: String): String? {
return if (TextUtils.isEmpty(id)) {
return if (id.isEmpty()) {
null
} else MATRIX_TO_URL_BASE + escape(id)
}
/**
@ -72,16 +70,14 @@ object PermalinkFactory {
* @param url the universal link, Ex: "https://matrix.to/#/@benoit:matrix.org"
* @return the id from the url, ex: "@benoit:matrix.org", or null if the url is not a permalink
*/
fun getLinkedId(url: String?): String? {
val isSupported = url != null && url.startsWith(MATRIX_TO_URL_BASE)
fun getLinkedId(url: String): String? {
val isSupported = url.startsWith(MATRIX_TO_URL_BASE)
return if (isSupported) {
url!!.substring(MATRIX_TO_URL_BASE.length)
url.substring(MATRIX_TO_URL_BASE.length)
} else null
}
/**
* Escape '/' in id, because it is used as a separator
*
@ -89,6 +85,6 @@ object PermalinkFactory {
* @return the escaped id
*/
private fun escape(id: String): String {
return id.replace("/".toRegex(), "%2F")
return id.replace("/", "%2F")
}
}

View File

@ -72,5 +72,4 @@ object PermalinkParser {
else -> PermalinkData.FallbackLink(uri)
}
}
}

View File

@ -18,7 +18,6 @@ package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.pushrules.rest.PushRule
import timber.log.Timber
sealed class Action {
object Notify : Action()
object DoNotNotify : Action()
@ -26,7 +25,6 @@ sealed class Action {
data class Highlight(val highlight: Boolean) : Action()
}
private const val ACTION_NOTIFY = "notify"
private const val ACTION_DONT_NOTIFY = "dont_notify"
private const val ACTION_COALESCE = "coalesce"
@ -80,7 +78,6 @@ fun PushRule.getActions(): List<Action> {
}
// When the value is not there, default sound (not specified by the spec)
?: Action.Sound(ACTION_OBJECT_VALUE_VALUE_DEFAULT)
}
ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT -> {
(actionStrOrObj[ACTION_OBJECT_VALUE_KEY] as? Boolean)?.let { boolValue ->
@ -104,7 +101,5 @@ fun PushRule.getActions(): List<Action> {
}
}
return result
}

View File

@ -35,9 +35,7 @@ abstract class Condition(val kind: Kind) {
else -> UNRECOGNIZE
}
}
}
}
abstract fun isSatisfied(conditionResolver: ConditionResolver): Boolean

View File

@ -15,13 +15,11 @@
*/
package im.vector.matrix.android.api.pushrules
import android.text.TextUtils
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import timber.log.Timber
import java.util.regex.Pattern
class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
@ -34,11 +32,11 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
}
fun isSatisfied(event: Event, displayName: String): Boolean {
var message = when (event.type) {
val message = when (event.type) {
EventType.MESSAGE -> {
event.content.toModel<MessageContent>()
}
//TODO the spec says:
// TODO the spec says:
// Matches any message whose content is unencrypted and contains the user's current display name
// EventType.ENCRYPTED -> {
// event.root.getClearContent()?.toModel<MessageContent>()
@ -49,7 +47,6 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
return caseInsensitiveFind(displayName, message.body)
}
companion object {
/**
* Returns whether a string contains an occurrence of another, as a standalone word, regardless of case.
@ -60,20 +57,18 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
*/
fun caseInsensitiveFind(subString: String, longString: String): Boolean {
// add sanity checks
if (TextUtils.isEmpty(subString) || TextUtils.isEmpty(longString)) {
if (subString.isEmpty() || longString.isEmpty()) {
return false
}
var res = false
try {
val pattern = Pattern.compile("(\\W|^)" + Pattern.quote(subString) + "(\\W|$)", Pattern.CASE_INSENSITIVE)
res = pattern.matcher(longString).find()
val regex = Regex("(\\W|^)" + Regex.escape(subString) + "(\\W|$)", RegexOption.IGNORE_CASE)
return regex.containsMatchIn(longString)
} catch (e: Exception) {
Timber.e(e, "## caseInsensitiveFind() : failed")
}
return res
return false
}
}
}

View File

@ -29,28 +29,25 @@ class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind
return "'$key' Matches '$pattern'"
}
fun isSatisfied(event: Event): Boolean {
//TODO encrypted events?
// TODO encrypted events?
val rawJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJsonValue(event) as? Map<*, *>
?: return false
val value = extractField(rawJson, key) ?: return false
//Patterns with no special glob characters should be treated as having asterisks prepended
// Patterns with no special glob characters should be treated as having asterisks prepended
// and appended when testing the condition.
try {
val modPattern = if (hasSpecialGlobChar(pattern)) simpleGlobToRegExp(pattern) else simpleGlobToRegExp("*$pattern*")
val regex = Regex(modPattern, RegexOption.DOT_MATCHES_ALL)
return regex.containsMatchIn(value)
} catch (e: Throwable) {
//e.g PatternSyntaxException
// e.g PatternSyntaxException
Timber.e(e, "Failed to evaluate push condition")
return false
}
}
private fun extractField(jsonObject: Map<*, *>, fieldPath: String): String? {
val fieldParts = fieldPath.split(".")
if (fieldParts.isEmpty()) return null
@ -77,9 +74,9 @@ class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind
return glob.contains("*") || glob.contains("?")
}
//Very simple glob to regexp converter
// Very simple glob to regexp converter
private fun simpleGlobToRegExp(glob: String): String {
var out = ""//"^"
var out = "" // "^"
for (i in 0 until glob.length) {
val c = glob[i]
when (c) {
@ -90,7 +87,7 @@ class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind
else -> out += c
}
}
out += ""//'$'.toString()
out += "" // '$'.toString()
return out
}
}

View File

@ -27,10 +27,10 @@ interface PushRuleService {
*/
fun fetchPushRules(scope: String = RuleScope.GLOBAL)
//TODO get push rule set
// TODO get push rule set
fun getPushRules(scope: String = RuleScope.GLOBAL): List<PushRule>
//TODO update rule
// TODO update rule
fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
@ -42,6 +42,7 @@ interface PushRuleService {
interface PushRuleListener {
fun onMatchRule(event: Event, actions: List<Action>)
fun onRoomJoined(roomId: String)
fun onRoomLeft(roomId: String)
fun onEventRedacted(redactedEventId: String)
fun batchFinish()

View File

@ -62,6 +62,5 @@ class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_coun
Timber.d(t)
}
return null
}
}

View File

@ -18,7 +18,6 @@ package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.PowerLevels
class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) {
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
@ -29,7 +28,6 @@ class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.se
return "User power level <$key>"
}
fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean {
return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key)
}

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.api.pushrules.rest
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class PushRule(
/**
@ -47,4 +46,3 @@ data class PushRule(
*/
val pattern: String? = null
)

View File

@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session
import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.failure.ConsentNotGivenError
import im.vector.matrix.android.api.pushrules.PushRuleService
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
@ -26,6 +27,7 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.pushers.PushersService
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
@ -52,6 +54,7 @@ interface Session :
PushRuleService,
PushersService,
InitialSyncProgressService,
HomeServerCapabilitiesService,
SecureStorageService {
/**
@ -65,7 +68,6 @@ interface Session :
val myUserId: String
get() = sessionParams.credentials.userId
/**
* This method allow to open a session. It does start some service on the background.
*/
@ -138,6 +140,9 @@ interface Session :
*/
fun onInvalidToken()
/**
* A M_CONSENT_NOT_GIVEN error has been received from the homeserver
*/
fun onConsentNotGivenError(consentNotGivenError: ConsentNotGivenError)
}
}

View File

@ -27,5 +27,4 @@ interface CacheService {
* Clear the whole cached data, except credentials. Once done, the session is closed and has to be opened again
*/
fun clearCache(callback: MatrixCallback<Unit>)
}

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.content
import android.os.Parcelable
import androidx.exifinterface.media.ExifInterface
import kotlinx.android.parcel.Parcelize
@Parcelize
@ -26,6 +27,7 @@ data class ContentAttachmentData(
val date: Long = 0,
val height: Long? = 0,
val width: Long? = 0,
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
val name: String? = null,
val path: String,
val mimeType: String,
@ -38,5 +40,4 @@ data class ContentAttachmentData(
AUDIO,
VIDEO
}
}

View File

@ -112,5 +112,4 @@ interface CryptoService {
fun addNewSessionListener(newSessionListener: NewSessionListener)
fun removeSessionListener(listener: NewSessionListener)
}

View File

@ -210,5 +210,4 @@ interface KeysBackupService {
val isEnabled: Boolean
val isStucked: Boolean
val state: KeysBackupState
}

View File

@ -43,7 +43,7 @@ enum class SasVerificationTxState {
Verifying,
Verified,
//Global: The verification has been cancelled (by me or other), see cancelReason for details
// Global: The verification has been cancelled (by me or other), see cancelReason for details
Cancelled,
OnCancelled
}

View File

@ -34,7 +34,7 @@ import com.squareup.moshi.JsonClass
*/
@JsonClass(generateAdapter = true)
data class AggregatedAnnotation (
data class AggregatedAnnotation(
override val limited: Boolean? = false,
override val count: Int? = 0,
val chunk: List<RelationChunkInfo>? = null

View File

@ -16,7 +16,6 @@
package im.vector.matrix.android.api.session.events.model
import android.text.TextUtils
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.crypto.MXCryptoError
@ -35,11 +34,10 @@ typealias Content = JsonDict
* This methods is a facility method to map a json content to a model.
*/
inline fun <reified T> Content?.toModel(catchError: Boolean = true): T? {
return this?.let {
val moshi = MoshiProvider.providesMoshi()
val moshiAdapter = moshi.adapter(T::class.java)
return try {
moshiAdapter.fromJsonValue(it)
moshiAdapter.fromJsonValue(this)
} catch (e: Exception) {
if (catchError) {
Timber.e(e, "To model failed : $e")
@ -48,19 +46,16 @@ inline fun <reified T> Content?.toModel(catchError: Boolean = true): T? {
throw e
}
}
}
}
/**
* This methods is a facility method to map a model to a json Content
*/
@Suppress("UNCHECKED_CAST")
inline fun <reified T> T?.toContent(): Content? {
return this?.let {
inline fun <reified T> T.toContent(): Content {
val moshi = MoshiProvider.providesMoshi()
val moshiAdapter = moshi.adapter(T::class.java)
return moshiAdapter.toJsonValue(it) as Content
}
return moshiAdapter.toJsonValue(this) as Content
}
/**
@ -81,7 +76,6 @@ data class Event(
@Json(name = "redacts") val redacts: String? = null
) {
@Transient
var mxDecryptionResult: OlmDecryptionResult? = null
@ -91,7 +85,6 @@ data class Event(
@Transient
var sendState: SendState = SendState.UNKNOWN
/**
* Check if event is a state event.
* @return true if event is state event.
@ -100,15 +93,15 @@ data class Event(
return EventType.isStateEvent(getClearType())
}
//==============================================================================================================
// ==============================================================================================================
// Crypto
//==============================================================================================================
// ==============================================================================================================
/**
* @return true if this event is encrypted.
*/
fun isEncrypted(): Boolean {
return TextUtils.equals(type, EventType.ENCRYPTED)
return type == EventType.ENCRYPTED
}
/**
@ -136,11 +129,12 @@ data class Event(
* @return the event content
*/
fun getClearContent(): Content? {
@Suppress("UNCHECKED_CAST")
return mxDecryptionResult?.payload?.get("content") as? Content ?: content
}
fun toContentStringWithIndent(): String {
val contentMap = toContent()?.toMutableMap() ?: HashMap()
val contentMap = toContent().toMutableMap()
return JSONObject(contentMap).toString(4)
}
@ -194,10 +188,8 @@ data class Event(
result = 31 * result + sendState.hashCode()
return result
}
}
fun Event.isTextMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.type) {

View File

@ -16,7 +16,6 @@
package im.vector.matrix.android.api.session.events.model
/**
* Constants defining known event types from Matrix specifications.
*/
@ -93,7 +92,6 @@ object EventType {
STATE_PINNED_EVENT
)
fun isStateEvent(type: String): Boolean {
return STATE_EVENTS.contains(type)
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.events.model
import java.util.*
object LocalEcho {
private const val PREFIX = "local."
fun isLocalEchoId(eventId: String) = eventId.startsWith(PREFIX)
fun createLocalEchoId() = "${PREFIX}${UUID.randomUUID()}"
}

View File

@ -15,7 +15,6 @@
*/
package im.vector.matrix.android.api.session.events.model
/**
* Constants defining known event relation types from Matrix specifications
*/
@ -27,5 +26,4 @@ object RelationType {
const val REPLACE = "m.replace"
/** Lets you define an event which references an existing event.*/
const val REFERENCE = "m.reference"
}

View File

@ -15,7 +15,6 @@
*/
package im.vector.matrix.android.api.session.events.model
interface UnsignedRelationInfo {
val limited : Boolean?
val count: Int?

View File

@ -20,7 +20,6 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
import java.io.File
/**
* This interface defines methods to get files.
*/

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.api.session.group
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.session.group.model.GroupSummary
/**
* This interface defines methods to get groups. It's implemented at the session level.
*/

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.homeserver
data class HomeServerCapabilities(
/**
* Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet
*/
val maxUploadFileSize: Long = MAX_UPLOAD_FILE_SIZE_UNKNOWN
) {
companion object {
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.homeserver
/**
* This interface defines a method to retrieve the homeserver capabilities.
*/
interface HomeServerCapabilitiesService {
/**
* Get the HomeServer capabilities
*/
fun getHomeServerCapabilities(): HomeServerCapabilities
}

View File

@ -19,7 +19,6 @@ import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import java.util.UUID
interface PushersService {
/**
@ -53,7 +52,6 @@ interface PushersService {
append: Boolean,
withEventIdOnly: Boolean): UUID
fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback<Unit>)
companion object {

View File

@ -21,11 +21,13 @@ import im.vector.matrix.android.api.session.room.crypto.RoomCryptoService
import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.relation.RelationService
import im.vector.matrix.android.api.session.room.reporting.ReportingService
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.send.DraftService
import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.state.StateService
import im.vector.matrix.android.api.session.room.timeline.TimelineService
import im.vector.matrix.android.api.util.Optional
/**
* This interface defines methods to interact within a room.
@ -37,6 +39,7 @@ interface Room :
ReadService,
MembershipService,
StateService,
ReportingService,
RelationService,
RoomCryptoService {
@ -49,8 +52,7 @@ interface Room :
* A live [RoomSummary] associated with the room
* You can observe this summary to get dynamic data from this room.
*/
fun liveRoomSummary(): LiveData<RoomSummary>
fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>>
fun roomSummary(): RoomSummary?
}

View File

@ -42,5 +42,4 @@ interface RoomDirectoryService {
* Includes both the available protocols and all fields required for queries against each protocol.
*/
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable
}

View File

@ -54,4 +54,8 @@ interface RoomService {
*/
fun liveRoomSummaries(): LiveData<List<RoomSummary>>
/**
* Mark all rooms as read
*/
fun markAllAsRead(roomIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
}

View File

@ -21,5 +21,4 @@ import im.vector.matrix.android.api.failure.Failure
sealed class CreateRoomFailure : Failure.FeatureFailure() {
object CreatedWithTimeout: CreateRoomFailure()
}

View File

@ -21,5 +21,4 @@ import im.vector.matrix.android.api.failure.Failure
sealed class JoinRoomFailure : Failure.FeatureFailure() {
object JoinedWithTimeout : JoinRoomFailure()
}

View File

@ -64,5 +64,4 @@ interface MembershipService {
* Leave the room, or reject an invitation.
*/
fun leave(callback: MatrixCallback<Unit>): Cancelable
}

View File

@ -43,5 +43,4 @@ enum class Membership(val value: String) {
fun isLeft(): Boolean {
return this == KNOCK || this == LEAVE || this == BAN
}
}

View File

@ -16,11 +16,9 @@
package im.vector.matrix.android.api.session.room.model
import android.text.TextUtils
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.EventType
import java.util.*
/**
* Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content.
@ -46,13 +44,7 @@ data class PowerLevels(
* @return the power level
*/
fun getUserPowerLevel(userId: String): Int {
// sanity check
if (!TextUtils.isEmpty(userId)) {
val powerLevel = users[userId]
return powerLevel ?: usersDefault
}
return usersDefault
return users.getOrElse(userId) { usersDefault }
}
/**
@ -61,10 +53,8 @@ data class PowerLevels(
* @param userId the user
* @param powerLevel the new power level
*/
fun setUserPowerLevel(userId: String?, powerLevel: Int) {
if (null != userId) {
users[userId] = Integer.valueOf(powerLevel)
}
fun setUserPowerLevel(userId: String, powerLevel: Int) {
users[userId] = powerLevel
}
/**
@ -75,10 +65,9 @@ data class PowerLevels(
* @return true if the user can send the event
*/
fun maySendEventOfType(eventTypeString: String, userId: String): Boolean {
return if (!TextUtils.isEmpty(eventTypeString) && !TextUtils.isEmpty(userId)) {
return if (eventTypeString.isNotEmpty() && userId.isNotEmpty()) {
getUserPowerLevel(userId) >= minimumPowerLevelForSendingEventAsMessage(eventTypeString)
} else false
}
/**
@ -113,25 +102,20 @@ data class PowerLevels(
return events[eventTypeString] ?: stateDefault
}
/**
* Get the notification level for a dedicated key.
*
* @param key the notification key
* @return the level
*/
fun notificationLevel(key: String?): Int {
if (null != key && notifications.containsKey(key)) {
val valAsVoid = notifications[key]
fun notificationLevel(key: String): Int {
val valAsVoid = notifications[key] ?: return 50
// the first implementation was a string value
return if (valAsVoid is String) {
Integer.parseInt(valAsVoid)
valAsVoid.toInt()
} else {
valAsVoid as Int
}
}
return 50
}
}

View File

@ -38,9 +38,13 @@ data class RoomSummary(
val tags: List<RoomTag> = emptyList(),
val membership: Membership = Membership.NONE,
val versioningState: VersioningState = VersioningState.NONE,
val readMarkerId: String? = null,
val userDrafts: List<UserDraft> = emptyList()
) {
val isVersioned: Boolean
get() = versioningState != VersioningState.NONE
val hasNewMessages: Boolean
get() = notificationCount != 0
}

View File

@ -31,5 +31,4 @@ data class CallAnswerContent(
@Json(name = "type") val type: String,
@Json(name = "sdp") val sdp: String
)
}

View File

@ -32,5 +32,4 @@ data class CallCandidatesContent(
@Json(name = "sdpMLineIndex") val sdpMLineIndex: String,
@Json(name = "candidate") val candidate: String
)
}

View File

@ -37,6 +37,5 @@ data class CallInviteContent(
}
}
fun isVideo(): Boolean = offer.sdp.contains(Offer.SDP_VIDEO)
}

View File

@ -145,15 +145,7 @@ class CreateRoomParams {
*/
fun setHistoryVisibility(historyVisibility: RoomHistoryVisibility?) {
// Remove the existing value if any.
if (initialStates != null && !initialStates!!.isEmpty()) {
val newInitialStates = ArrayList<Event>()
for (event in initialStates!!) {
if (event.getClearType() != EventType.STATE_HISTORY_VISIBILITY) {
newInitialStates.add(event)
}
}
initialStates = newInitialStates
}
initialStates?.removeAll { it.getClearType() == EventType.STATE_HISTORY_VISIBILITY }
if (historyVisibility != null) {
val contentMap = HashMap<String, RoomHistoryVisibility>()

View File

@ -28,5 +28,3 @@ data class RoomCreateContent(
@Json(name = "room_version") val roomVersion: String? = null,
@Json(name = "predecessor") val predecessor: Predecessor? = null
)

View File

@ -42,16 +42,6 @@ data class ImageInfo(
*/
@Json(name = "size") val size: Int = 0,
/**
* Not documented
*/
@Json(name = "rotation") val rotation: Int = 0,
/**
* Not documented
*/
@Json(name = "orientation") val orientation: Int = 0,
/**
* Metadata about the image referred to in thumbnail_url.
*/

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.api.session.room.model.message
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
interface MessageContent {
val type: String
val body: String
@ -27,7 +26,6 @@ interface MessageContent {
val newContent: Content?
}
fun MessageContent?.isReply(): Boolean {
return this?.relatesTo?.inReplyTo?.eventId != null
}

View File

@ -18,7 +18,6 @@ package im.vector.matrix.android.api.session.room.model.message
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
/**
* Interface for message which can contains an encrypted file
*/

View File

@ -24,6 +24,6 @@ data class ReactionInfo(
@Json(name = "rel_type") override val type: String?,
@Json(name = "event_id") override val eventId: String,
val key: String,
//always null for reaction
// always null for reaction
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null
) : RelationContent

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.Optional
/**
* In some cases, events may wish to reference other events.
@ -47,26 +48,21 @@ import im.vector.matrix.android.api.util.Cancelable
*/
interface RelationService {
/**
* Sends a reaction (emoji) to the targetedEvent.
* @param reaction the reaction (preferably emoji)
* @param targetEventId the id of the event being reacted
* @param reaction the reaction (preferably emoji)
*/
fun sendReaction(reaction: String,
targetEventId: String): Cancelable
fun sendReaction(targetEventId: String,
reaction: String): Cancelable
/**
* Undo a reaction (emoji) to the targetedEvent.
* @param reaction the reaction (preferably emoji)
* @param targetEventId the id of the event being reacted
* @param myUserId used to know if a reaction event was made by the user
* @param reaction the reaction (preferably emoji)
*/
fun undoReaction(reaction: String,
targetEventId: String,
myUserId: String)//: Cancelable
fun undoReaction(targetEventId: String,
reaction: String): Cancelable
/**
* Edit a text message body. Limited to "m.text" contentType
@ -80,7 +76,6 @@ interface RelationService {
newBodyAutoMarkdown: Boolean,
compatibilityBodyText: String = "* $newBodyText"): Cancelable
/**
* Edit a reply. This is a special case because replies contains fallback text as a prefix.
* This method will take the new body (stripped from fallbacks) and re-add them before sending.
@ -95,11 +90,10 @@ interface RelationService {
compatibilityBodyText: String = "* $newBodyText"): Cancelable
/**
* Get's the edit history of the given event
* Get the edit history of the given event
*/
fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>)
/**
* Reply to an event in the timeline (must be in same room)
* https://matrix.org/docs/spec/client_server/r0.4.0.html#id350
@ -111,7 +105,5 @@ interface RelationService {
replyText: String,
autoMarkdown: Boolean = false): Cancelable?
fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary>
fun getEventSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>>
}

View File

@ -27,5 +27,4 @@ data class RoomTag(
val ROOM_TAG_NO_TAG = "m.recent"
val ROOM_TAG_SERVER_NOTICE = "m.server_notice"
}
}

View File

@ -51,5 +51,4 @@ data class RoomDirectoryData(
companion object {
const val DEFAULT_HOME_SERVER_NAME = "Matrix"
}
}

View File

@ -51,7 +51,6 @@ data class ThirdPartyProtocolInstance(
@Json(name = "instance_id")
var instanceId: String? = null,
/**
* FIXDOC Not documented on matrix.org doc
*/

View File

@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session.room.read
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.util.Optional
/**
* This interface defines methods to handle read receipts and read marker in a room. It's implemented at the room level.
@ -40,7 +41,24 @@ interface ReadService {
*/
fun setReadMarker(fullyReadEventId: String, callback: MatrixCallback<Unit>)
/**
* Check if an event is already read, ie. your read receipt is set on a more recent event.
*/
fun isEventRead(eventId: String): Boolean
/**
* Returns a live read marker id for the room.
*/
fun getReadMarkerLive(): LiveData<Optional<String>>
/**
* Returns a live read receipt id for the room.
*/
fun getMyReadReceiptLive(): LiveData<Optional<String>>
/**
* Returns a live list of read receipts for a given event
* @param eventId: the event
*/
fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>>
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.room.reporting
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
/**
* This interface defines methods to report content of an event.
*/
interface ReportingService {
/**
* Report content
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-rooms-roomid-report-eventid
*/
fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback<Unit>): Cancelable
}

View File

@ -35,5 +35,4 @@ interface DraftService {
* The draft list can contain one draft for {regular, reply, quote} and an arbitrary number of {edit} drafts
*/
fun getDraftsLive(): LiveData<List<UserDraft>>
}

View File

@ -22,7 +22,6 @@ import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.Cancelable
/**
* This interface defines methods to send events in a room. It's implemented at the room level.
*/
@ -66,7 +65,6 @@ interface SendService {
*/
fun redactEvent(event: Event, reason: String?): Cancelable
/**
* Schedule this message to be resent
* @param localEcho the unsent local echo
@ -79,7 +77,6 @@ interface SendService {
*/
fun resendMediaMessage(localEcho: TimelineEvent): Cancelable?
/**
* Remove this failed message from the timeline
* @param localEcho the unsent local echo
@ -92,5 +89,4 @@ interface SendService {
* Resend all failed messages one by one (and keep order)
*/
fun resendAllFailedMessages()
}

View File

@ -16,7 +16,6 @@
package im.vector.matrix.android.api.session.room.send
enum class SendState {
UNKNOWN,
// the event has not been sent
@ -46,7 +45,4 @@ enum class SendState {
fun hasFailed() = HAS_FAILED_STATES.contains(this)
fun isSending() = IS_SENDING_STATES.contains(this)
}

View File

@ -27,5 +27,4 @@ interface StateService {
fun updateTopic(topic: String, callback: MatrixCallback<Unit>)
fun getStateEvent(eventType: String): Event?
}

View File

@ -32,6 +32,8 @@ interface Timeline {
var listener: Listener?
val isLive: Boolean
/**
* This should be called before any other method after creating the timeline. It ensures the underlying database is open
*/
@ -42,6 +44,12 @@ interface Timeline {
*/
fun dispose()
/**
* This method restarts the timeline, erases all built events and pagination states.
* It then loads events around the eventId. If eventId is null, it does restart the live timeline.
*/
fun restartWithEventId(eventId: String?)
/**
* Check if the timeline can be enriched by paginating.
* @param the direction to check in
@ -56,9 +64,36 @@ interface Timeline {
*/
fun paginate(direction: Direction, count: Int)
fun pendingEventCount() : Int
/**
* Returns the number of sending events
*/
fun pendingEventCount(): Int
fun failedToDeliverEventCount() : Int
/**
* Returns the number of failed sending events.
*/
fun failedToDeliverEventCount(): Int
/**
* Returns the index of a built event or null.
*/
fun getIndexOfEvent(eventId: String?): Int?
/**
* Returns the built [TimelineEvent] at index or null
*/
fun getTimelineEventAtIndex(index: Int): TimelineEvent?
/**
* Returns the built [TimelineEvent] with eventId or null
*/
fun getTimelineEventWithId(eventId: String?): TimelineEvent?
/**
* Returns the first displayable events starting from eventId.
* It does depend on the provided [TimelineSettings].
*/
fun getFirstDisplayableEventId(eventId: String): String?
interface Listener {
/**
@ -81,5 +116,4 @@ interface Timeline {
*/
BACKWARDS
}
}

View File

@ -40,7 +40,8 @@ data class TimelineEvent(
val isUniqueDisplayName: Boolean,
val senderAvatar: String?,
val annotations: EventAnnotationsSummary? = null,
val readReceipts: List<ReadReceipt> = emptyList()
val readReceipts: List<ReadReceipt> = emptyList(),
val hasReadMarker: Boolean = false
) {
val metadata = HashMap<String, Any>()
@ -87,7 +88,6 @@ data class TimelineEvent(
}
}
/**
* Tells if the event has been edited
*/
@ -106,7 +106,6 @@ fun TimelineEvent.getEditedEventId(): String? {
fun TimelineEvent.getLastMessageContent(): MessageContent? = annotations?.editSummary?.aggregatedContent?.toModel()
?: root.getClearContent().toModel()
/**
* Get last Message body, after a possible edition
*/
@ -120,7 +119,6 @@ fun TimelineEvent.getLastMessageBody(): String? {
return null
}
fun TimelineEvent.getTextEditableContent(): String? {
val originalContent = root.getClearContent().toModel<MessageContent>() ?: return null
val isReply = originalContent.isReply() || root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId != null

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.room.timeline
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.util.Optional
/**
* This interface defines methods to interact with the timeline. It's implemented at the room level.
@ -32,9 +33,7 @@ interface TimelineService {
*/
fun createTimeline(eventId: String?, settings: TimelineSettings): Timeline
fun getTimeLineEvent(eventId: String): TimelineEvent?
fun liveTimeLineEvent(eventId: String): LiveData<TimelineEvent>
fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>>
}

View File

@ -24,5 +24,4 @@ interface SecureStorageService {
fun securelyStoreObject(any: Any, keyAlias: String, outputStream: OutputStream)
fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T?
}

View File

@ -27,5 +27,4 @@ interface SignOutService {
* Sign out
*/
fun signOut(callback: MatrixCallback<Unit>)
}

View File

@ -21,6 +21,7 @@ import androidx.paging.PagedList
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.Optional
/**
* This interface defines methods to get users. It's implemented at the session level.
@ -47,9 +48,9 @@ interface UserService {
/**
* Observe a live user from a userId
* @param userId the userId to look for.
* @return a Livedata of user with userId
* @return a LiveData of user with userId
*/
fun liveUser(userId: String): LiveData<User?>
fun liveUser(userId: String): LiveData<Optional<User>>
/**
* Observe a live list of users sorted alphabetically
@ -63,5 +64,4 @@ interface UserService {
* @return a Livedata of users
*/
fun livePagedUsers(filter: String? = null): LiveData<PagedList<User>>
}

View File

@ -26,7 +26,6 @@ interface Cancelable {
* The cancel method, it does nothing by default.
*/
fun cancel() {
//no-op
// no-op
}
}

View File

@ -15,7 +15,6 @@
*/
package im.vector.matrix.android.api.util
object ContentUtils {
fun extractUsefulTextFromReply(repliedBody: String): String {
val lines = repliedBody.lines()
@ -39,9 +38,10 @@ object ContentUtils {
fun extractUsefulTextFromHtmlReply(repliedBody: String): String {
if (repliedBody.startsWith("<mx-reply>")) {
val closingTagIndex = repliedBody.lastIndexOf("</mx-reply>")
if (closingTagIndex != -1)
if (closingTagIndex != -1) {
return repliedBody.substring(closingTagIndex + "</mx-reply>".length).trim()
}
}
return repliedBody
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.util
data class Optional<T : Any> constructor(private val value: T?) {
fun get(): T {
return value!!
}
fun getOrNull(): T? {
return value
}
fun getOrElse(fn: () -> T): T {
return value ?: fn()
}
fun hasValue(): Boolean {
return value != null
}
companion object {
fun <T : Any> from(value: T?): Optional<T> {
return Optional(value)
}
}
}
fun <T : Any> T?.toOptional() = Optional(this)

View File

@ -47,5 +47,4 @@ 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>
}

Some files were not shown because too many files have changed in this diff Show More