profile
viewpoint
Fernando Cejas android10 @wireapp Berlin, Germany http://www.fernandocejas.com/ Director of Mobile @wireapp. DevRel. Engineering. @soundcloud Alumni. Former @IBM. Curious learner. Passionate Software Engineer. Geek. Quantum Computing

android10/Android-CleanArchitecture 14500

This is a sample app that is part of a series of blog posts I have written about how to architect an android application using Uncle Bob's clean architecture approach.

android10/Android-CleanArchitecture-Kotlin 2749

This is a movies sample app in Kotlin, which is part of a serie of blog posts I have written about architecting android application using different approaches.

android10/frodo 1497

Android Library for Logging RxJava Observables and Subscribers.

android10/arrow 446

Arrow is Lightweight library toolbox for Java and Android Development.

android10/Android-AOPExample 414

This is a simple example of Aspect Oriented Programming in Android

android10/Android-ReactiveProgramming 225

This is a sample app that is part of a serie of blog posts I will be writting about experiences with Reactive Programming on Android

android10/frodo2 143

Android Library for Logging RxJava2 Components

android10/Android-KotlinInTests 87

This is a sample app that is part of blog posts I have written about how to test android applications using Kotlin.

android10/DynamicProxy_Java_Sample 34

This is an example written in Java that demonstrates how to implement a simple dynamic proxy for intercepting method calls.

android10/Cognitive-Samples 23

This is meant to be a repository with examples of cognitive computing brought to the android platform

PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentwireapp/wire-android

Backup 11: List batch readers

 suspend fun <L, R, T> Either<L, R>.foldSuspendable(fnL: suspend (L) -> T?, fnR:         is Either.Left -> fnL(a)         is Either.Right -> fnR(b)     }++/**+ * Maps over an iterable of T where the mapping function returns an Either<L, R>.+ * If the mapping function returns Left<L>, the whole mapRight is interrupted and+ * the left value is returned. If the mapping function returns Right for all elements, the result+ * is Right<List<R>>. You can think of this method as a functional version of a map wrapped in+ * a try/catch because the mapping function can throw an exception.+ */+fun <T, L, R> Iterable<T>.mapRight(fn: (T) -> Either<L, R>): Either<L, List<R>> {

I know you want to turn much of this stuff into FP because this is the way you like to do things (it is totally fine because we all have our opinions). I also have to say I'm interested in a real use case and not potential use cases.

This is up to you and the team. You are the ones deciding what goes in and out. I'm not a big fan of this because from my perspective all this compromises readability and makes the code more complex.

I'm personally skeptical to introduce too much of FP (due to bad experiences), I think what Kotlin provides should be enough. We even introduced Flow which is a reactive framework with reactive operators if we want to use it.

I would like to involve everyone in this kind of discussions: @mejdoo @BradleyWire @gizemb.

These are my 2 cents. I will be neutral on this decision and leave it up to you, as soon as everyone is comfortable and confident that this is the way to go.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Feat: Back up - Part 8 : Reusable test data + integration tests

 class CreateBackUpUseCaseTest : UnitTest() {             val repo2 = mockBackUpRepo(false)             val repo3 = mockBackUpRepo(true) -            createBackUpUseCase = CreateBackUpUseCase(listOf(repo1, repo2, repo3))+            createBackUpUseCase = CreateBackUpUseCase(listOf(repo1, repo2, repo3), testCoroutineScope)              val result = createBackUpUseCase.run(Unit)              verify(repo1).backUp()             verify(repo2).backUp()-            verifyNoInteractions(repo3)+//            verifyNoInteractions(repo3)

The TODO() is ok but keep this in mind. Search for all the TODO() we currently have and how old they are.

In my experience we never review them again. That is why my point.

BradleyWire

comment created time in 2 months

Pull request review commentwireapp/wire-android

Feat: Back up - Part 8 : Reusable test data + integration tests

 class KeyValuesBackUpMapperTest : UnitTest() {      @Test     fun `given a KeyValuesEntity, when fromEntity() is called, then maps it into a KeyValueBackUpModel`() {-        val entity = KeyValuesEntity(key =  KEY, value = VALUE)+        val data = KeyValueTestDataProvider.data()

I cannot suggest because I do not know what kind of data we are providing. That is why I was asking, what is this doing.

BradleyWire

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup 11: List batch readers

 suspend fun <L, R, T> Either<L, R>.foldSuspendable(fnL: suspend (L) -> T?, fnR:         is Either.Left -> fnL(a)         is Either.Right -> fnR(b)     }++/**+ * Maps over an iterable of T where the mapping function returns an Either<L, R>.+ * If the mapping function returns Left<L>, the whole mapRight is interrupted and+ * the left value is returned. If the mapping function returns Right for all elements, the result+ * is Right<List<R>>. You can think of this method as a functional version of a map wrapped in+ * a try/catch because the mapping function can throw an exception.+ */+fun <T, L, R> Iterable<T>.mapRight(fn: (T) -> Either<L, R>): Either<L, List<R>> {

I would ask for more arguments. I think that could be useful is not valid for me. What are the use cases?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Feat: Back up - Part 10: Single batched tables

+package com.waz.zclient.feature.backup.conversations++import com.waz.zclient.feature.backup.io.database.SingleReadDao+import com.waz.zclient.storage.db.conversations.ConversationRoleActionDao+import com.waz.zclient.storage.db.conversations.ConversationRoleActionEntity++class ConversationRolesBackupDao(private val conversationRoleActionDao: ConversationRoleActionDao) :

Is there any especial reason why you need this extra layer? What I see here is that there is no extra logic, only delegation, which means that you could use directly the parent?

BradleyWire

comment created time in 2 months

Pull request review commentwireapp/wire-android

Feat: Back up - Part 8 : Reusable test data + integration tests

 object BuildDependencies { object ModuleDependencies {     val storage = ":storage"     val syncEngine = ":wire-android-sync-engine:zmessaging"+    val commonTest = ":common-test"

I'm totally fine for this here, given the project structure. Maybe for the future is something to consider. Maybe we can bring it up in the Collective.

BradleyWire

comment created time in 2 months

Pull request review commentwireapp/wire-android

Feat: Back up - Part 8 : Reusable test data + integration tests

 class CreateBackUpUseCaseTest : UnitTest() {             val repo2 = mockBackUpRepo(false)             val repo3 = mockBackUpRepo(true) -            createBackUpUseCase = CreateBackUpUseCase(listOf(repo1, repo2, repo3))+            createBackUpUseCase = CreateBackUpUseCase(listOf(repo1, repo2, repo3), testCoroutineScope)              val result = createBackUpUseCase.run(Unit)              verify(repo1).backUp()             verify(repo2).backUp()-            verifyNoInteractions(repo3)+//            verifyNoInteractions(repo3)

Why the comment? If this does not make sense I would even delete it. Commented code leads to confusion and uncertainty.

BradleyWire

comment created time in 2 months

Pull request review commentwireapp/wire-android

Feat: Back up - Part 8 : Reusable test data + integration tests

 class KeyValuesBackUpMapperTest : UnitTest() {      @Test     fun `given a KeyValuesEntity, when fromEntity() is called, then maps it into a KeyValueBackUpModel`() {-        val entity = KeyValuesEntity(key =  KEY, value = VALUE)+        val data = KeyValueTestDataProvider.data()

Is this too generic? When I read it I do not know what that is...It could be anything...

BradleyWire

comment created time in 2 months

Pull request review commentwireapp/wire-android

Feat: Back up - Part 4: Add file i/o handler for backup files

+package com.waz.zclient.feature.backup.io.file++import com.waz.zclient.feature.backup.BackUpIOHandler+import java.io.File++class BackUpFileIOHandler<T>(+    private val fileName: String,+    private val jsonConverter: JsonConverter<T>+) : BackUpIOHandler<T> {++    override fun write(iterator: Iterator<T>) {+        val file = File(fileName).also {+            it.delete()+            it.createNewFile()+        }+        while (iterator.hasNext()) {+            val jsonStr = jsonConverter.toJson(iterator.next())+            file.appendText("$jsonStr\n")

Is this going to work? Is the carry return blocking the string interpolation? Would this be something like this?

file.appendText("${jsonStr}\n")
gizemb

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

+package com.waz.zclient.shared.backup.datasources++import com.waz.zclient.UnitTest+import com.waz.zclient.shared.backup.datasources.local.KeyValuesLocalDataSource+import com.waz.zclient.shared.backup.datasources.local.PropertiesLocalDataSource+import com.waz.zclient.storage.db.property.KeyValuesDao+import com.waz.zclient.storage.db.property.KeyValuesEntity+import com.waz.zclient.storage.db.property.PropertiesDao+import com.waz.zclient.storage.db.property.PropertiesEntity+import kotlinx.coroutines.ExperimentalCoroutinesApi+import kotlinx.coroutines.runBlocking+import org.amshove.kluent.shouldEqual+import org.junit.Before+import org.junit.Test+import org.mockito.Mock+import org.mockito.Mockito.`when`+import java.nio.file.Files++@ExperimentalCoroutinesApi+class BackupDataSourceTest : UnitTest() {+    @Mock+    private lateinit var keyValuesDao: KeyValuesDao++    @Mock+    private lateinit var propertiesDao: PropertiesDao++    private lateinit var keyValuesLocalDataSource: KeyValuesLocalDataSource+    private lateinit var propertiesLocalDataSource: PropertiesLocalDataSource+    private lateinit var backupDataSource: BackupDataSource++    private val keyValues = listOf(+        KeyValuesEntity("kv1", "a"),+        KeyValuesEntity("kv2", "b"),+        KeyValuesEntity("kv3", "c"),+        KeyValuesEntity("kv4", "d"),+        KeyValuesEntity("kv5", "e")+    )++    private val properties = listOf(+        PropertiesEntity("p1", "A"),+        PropertiesEntity("p2", "B"),+        PropertiesEntity("p3", "C"),+        PropertiesEntity("p4", "D"),+        PropertiesEntity("p5", "E")+    )++    @Before+    fun setup() {+        keyValuesLocalDataSource = KeyValuesLocalDataSource(keyValuesDao, 3)+        propertiesLocalDataSource = PropertiesLocalDataSource(propertiesDao, 2)+        val dataSources = listOf(keyValuesLocalDataSource, propertiesLocalDataSource)+        backupDataSource = BackupDataSource(dataSources)+    }++    @Test+    fun `create files and write down json arrays to them`() = runBlocking {

:+1: that is also the type of consistency I commented above.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 package com.waz.zclient.shared.backup.datasources.local +import com.waz.zclient.core.extension.empty+import com.waz.zclient.storage.db.BatchReader+import kotlinx.coroutines.runBlocking import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.list import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration  abstract class BackupLocalDataSource<EntityType, JSONType>(-    private val serializer: KSerializer<JSONType>,-    private val batchSize: Int = BatchSize-) {-    private var currentOffset: Int = 0--    protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<EntityType>+    val name: String,+    protected val dao: BatchReader<EntityType>,+    protected val batchSize: Int,+    private val serializer: KSerializer<JSONType>+) : Iterable<String> {

I'm with @gizemb on this one. I think the code is not consistent with the structure we defined at architecture level, plus it does not respect many of the good practices.

I also do not think it makes sense to merge it if we are not happy about it.
Is there any good reason for this? I would say it is always better to fix it.

Can we have a meeting to discuss all this? My points to treat:

  • Architecture consistency
  • Smaller PR with only a tiny backup that does not include everything (divide and conquer approach)
  • Kotlin Programming Style (This should not be a port from any other Programming language)
  • Tests: I see there is room for improvement since these are more integration than unit tests.

Maybe @makingthematrix you can schedule a meeting? It is really hard and time consuming to review such a big PR.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 import com.waz.zclient.storage.db.conversations.ConversationFoldersDao import com.waz.zclient.storage.db.conversations.ConversationFoldersEntity import kotlinx.serialization.Serializable -class ConversationFoldersLocalDataSource(private val conversationFoldersDao: ConversationFoldersDao, batchSize: Int = BatchSize) :-BackupLocalDataSource<ConversationFoldersEntity, ConversationFoldersJSONEntity>(ConversationFoldersJSONEntity.serializer(), batchSize) {-    override suspend fun getInBatch(batchSize: Int, offset: Int): List<ConversationFoldersEntity> =-        conversationFoldersDao.getConversationFoldersInBatch(batchSize, offset)--    override fun toJSON(entity: ConversationFoldersEntity): ConversationFoldersJSONEntity = ConversationFoldersJSONEntity.from(entity)-    override fun toEntity(json: ConversationFoldersJSONEntity): ConversationFoldersEntity = json.toEntity()+class ConversationFoldersLocalDataSource(dao: ConversationFoldersDao, batchSize: Int = BatchSize) :+BackupLocalDataSource<ConversationFoldersEntity, ConversationFoldersJSONEntity>+    ("conversationFolders", dao, batchSize, ConversationFoldersJSONEntity.serializer()) {+    override fun toJSON(entity: ConversationFoldersEntity) = ConversationFoldersJSONEntity.from(entity)+    override fun toEntity(json: ConversationFoldersJSONEntity) = json.toEntity() }  @Serializable data class ConversationFoldersJSONEntity(

Format again. This should look like this:

data class ConversationFoldersJSONEntity(val convId: String = "", val folderId: String = "") {
...
}
makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 import com.waz.zclient.storage.db.conversations.ConversationMembersDao import com.waz.zclient.storage.db.conversations.ConversationMembersEntity import kotlinx.serialization.Serializable -class ConversationMembersLocalDataSource(private val conversationMembersDao: ConversationMembersDao, batchSize: Int = BatchSize) :-BackupLocalDataSource<ConversationMembersEntity, ConversationMembersJSONEntity>(ConversationMembersJSONEntity.serializer(), batchSize) {-    override suspend fun getInBatch(batchSize: Int, offset: Int): List<ConversationMembersEntity> =-        conversationMembersDao.getConversationMembersInBatch(batchSize, offset)--    override fun toJSON(entity: ConversationMembersEntity): ConversationMembersJSONEntity = ConversationMembersJSONEntity.from(entity)-    override fun toEntity(json: ConversationMembersJSONEntity): ConversationMembersEntity = json.toEntity()+class ConversationMembersLocalDataSource(dao: ConversationMembersDao, batchSize: Int = BatchSize) :+BackupLocalDataSource<ConversationMembersEntity, ConversationMembersJSONEntity>+    ("conversationMembers", dao, batchSize, ConversationMembersJSONEntity.serializer()) {+    override fun toJSON(entity: ConversationMembersEntity) = ConversationMembersJSONEntity.from(entity)+    override fun toEntity(json: ConversationMembersJSONEntity) = json.toEntity() }  @Serializable data class ConversationMembersJSONEntity(val userId: String = "", val conversationId: String = "", val role: String = "") {-    fun toEntity(): ConversationMembersEntity = ConversationMembersEntity(+    fun toEntity() = ConversationMembersEntity(         userId = userId,         conversationId = conversationId,         role = role     )      companion object {-        fun from(entity: ConversationMembersEntity): ConversationMembersJSONEntity = ConversationMembersJSONEntity(+        fun from(entity: ConversationMembersEntity) = ConversationMembersJSONEntity(

Please check all this code format

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 import com.waz.zclient.storage.db.conversations.ConversationMembersDao import com.waz.zclient.storage.db.conversations.ConversationMembersEntity import kotlinx.serialization.Serializable -class ConversationMembersLocalDataSource(private val conversationMembersDao: ConversationMembersDao, batchSize: Int = BatchSize) :-BackupLocalDataSource<ConversationMembersEntity, ConversationMembersJSONEntity>(ConversationMembersJSONEntity.serializer(), batchSize) {-    override suspend fun getInBatch(batchSize: Int, offset: Int): List<ConversationMembersEntity> =-        conversationMembersDao.getConversationMembersInBatch(batchSize, offset)--    override fun toJSON(entity: ConversationMembersEntity): ConversationMembersJSONEntity = ConversationMembersJSONEntity.from(entity)-    override fun toEntity(json: ConversationMembersJSONEntity): ConversationMembersEntity = json.toEntity()+class ConversationMembersLocalDataSource(dao: ConversationMembersDao, batchSize: Int = BatchSize) :+BackupLocalDataSource<ConversationMembersEntity, ConversationMembersJSONEntity>+    ("conversationMembers", dao, batchSize, ConversationMembersJSONEntity.serializer()) {+    override fun toJSON(entity: ConversationMembersEntity) = ConversationMembersJSONEntity.from(entity)+    override fun toEntity(json: ConversationMembersJSONEntity) = json.toEntity() }  @Serializable data class ConversationMembersJSONEntity(val userId: String = "", val conversationId: String = "", val role: String = "") {-    fun toEntity(): ConversationMembersEntity = ConversationMembersEntity(+    fun toEntity() = ConversationMembersEntity(

Format

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 data class ButtonJSONEntity(     )      companion object {-        fun from(entity: ButtonEntity): ButtonJSONEntity = ButtonJSONEntity(+        fun from(entity: ButtonEntity) = ButtonJSONEntity(

same here with the format.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 data class ButtonJSONEntity(     val ordinal: Int = 0,     val state: Int = 0 ) {-    fun toEntity(): ButtonEntity = ButtonEntity(+    fun toEntity() = ButtonEntity(

This format is a bit weird, it can fit in the same line right? We already discussed this to have 140 chars per line. Is Detekt swallowing this?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 package com.waz.zclient.shared.backup.datasources.local +import com.waz.zclient.core.extension.empty+import com.waz.zclient.storage.db.BatchReader+import kotlinx.coroutines.runBlocking import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.list import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration  abstract class BackupLocalDataSource<EntityType, JSONType>(-    private val serializer: KSerializer<JSONType>,-    private val batchSize: Int = BatchSize-) {-    private var currentOffset: Int = 0--    protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<EntityType>+    val name: String,+    protected val dao: BatchReader<EntityType>,+    protected val batchSize: Int,+    private val serializer: KSerializer<JSONType>+) : Iterable<String> {     protected abstract fun toJSON(entity: EntityType): JSONType     protected abstract fun toEntity(json: JSONType): EntityType -    fun serialize(entity: EntityType): String = json.stringify(serializer, toJSON(entity))+    fun serialize(entity: EntityType) = json.stringify(serializer, toJSON(entity)) -    fun deserialize(jsonStr: String): EntityType = toEntity(json.parse(serializer, jsonStr))+    fun deserialize(jsonStr: String) = toEntity(json.parse(serializer, jsonStr)) -    fun serializeList(list: List<EntityType>): String =-        json.stringify(serializer.list, list.map { entity -> toJSON(entity) })+    fun serializeList(list: List<EntityType>) =+        json.stringify(serializer.list, list.map { toJSON(it) }) -    fun deserializeList(jsonListStr: String): List<EntityType> =-        json.parse(serializer.list, jsonListStr).map { jsonEntity -> toEntity(jsonEntity) }+    fun deserializeList(jsonListStr: String) =+        json.parse(serializer.list, jsonListStr).map { toEntity(it) } -    fun resetCurrentOffset() {-        currentOffset = 0-    }+    @SuppressWarnings("IteratorNotThrowingNoSuchElementException")+    override fun iterator(): Iterator<String> = object : Iterator<String> {+        private var currentOffset = 0+        private val listSize by lazy { runBlocking { dao.size() } } -    fun getCurrentOffset(): Int = currentOffset+        override fun hasNext(): Boolean = currentOffset < listSize -    suspend fun nextJSONArrayAsString(): String? {-        val list = getInBatch(batchSize, currentOffset)-        return if (list.isEmpty()) {-            null-        } else {-            currentOffset += list.size-            serializeList(list)+        override fun next(): String = runBlocking {

why this runBlocking block here?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 package com.waz.zclient.shared.backup  import com.waz.model.Handle import com.waz.model.UserId+import com.waz.zclient.core.exception.Failure import com.waz.zclient.core.functional.Either import java.io.File  interface BackupRepository {     suspend fun exportDatabase(userId: UserId, userHandle: Handle, backupPassword: String, targetDir: File): Either<String, File>++    fun writeAllToFiles(targetDir: File): Either<Failure, List<File>>

What does mean All here?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

+package com.waz.zclient.core.utilities++import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.exception.FeatureFailure+import com.waz.zclient.core.functional.Either+import java.io.File+import java.io.IOException++object UiUtils {+    data class IOFailure(val exception: IOException) : FeatureFailure()++    fun writeTextToFile(targetDir: File, fileName: String, text: () -> String): Either<Failure, File> =

I do not understand the purpose of this function. What is is doing? Would me more convenient to maybe extend the File class and also get rid of this Utils object?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

+package com.waz.zclient.core.utilities++import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.exception.FeatureFailure+import com.waz.zclient.core.functional.Either+import java.io.File+import java.io.IOException++object UiUtils {

Change the name of this object to something like: IOHandler for example since naming things like, managers and utils means that the intention of this is not clear and anything could fall inside this class which creates an anti-pattern

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

+package com.waz.zclient.core.utilities++import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.exception.FeatureFailure+import com.waz.zclient.core.functional.Either+import java.io.File+import java.io.IOException++object UiUtils {+    data class IOFailure(val exception: IOException) : FeatureFailure()++    fun writeTextToFile(targetDir: File, fileName: String, text: () -> String): Either<Failure, File> =+        try {+            val file = File(targetDir, fileName)+            if (file.exists()) file.delete()

Use curly braces here. I guess all these methods are part of the same block right?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

+package com.waz.zclient.core.utilities++import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.exception.FeatureFailure+import com.waz.zclient.core.functional.Either+import java.io.File+import java.io.IOException++object UiUtils {+    data class IOFailure(val exception: IOException) : FeatureFailure()

This makes me thing whether an Input/Output Failures is a FeatureFailure or we can maybe extend any other Failure?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

New backups (4): Writing JSON strings to files

 fun <L, R> Either<L, R>.getOrElse(value: R): R =         is Either.Left -> value         is Either.Right -> b     }++/**+ * Maps over an iterable of T where the mapping function returns an Either<F, S>.+ * If the mapping function returns Left<F>, signifying an error, the whole mapRight is interrupted and+ * the error is returned. If the mapping function returns Right for all elements, the result+ * is Right<List<S>>. You can think of this method as a functional version of a map wrapped in+ * a try/catch because the mapping function can throw an exception.+ */+fun <T, S, F> Iterable<T>.mapRight(fn: (T) -> Either<F, S>): Either<F, List<S>> {

Can we change the F and S for L and R? Just for consistency? Is this right or am I missing something?

makingthematrix

comment created time in 2 months

push eventandroid10/Android-CleanArchitecture-Kotlin

Fernando Cejas

commit sha b272234c5bfe2d819de651425850eb876780de41

Fix MoviesViewModelTest.

view details

push time in 2 months

push eventandroid10/Android-CleanArchitecture-Kotlin

Fernando Cejas

commit sha cae93d0797026eec484d69d9e10767ee7d8d1c1a

Hide MuttableLiveData under a only read property in ViewModels.

view details

push time in 2 months

push eventmimilupion/mimilupion.github.io

Fernando Cejas

commit sha ca3a8d7cb7b7f23a8fefaae39ba1ee676ec966b6

First commit and version.

view details

push time in 2 months

push eventmimilupion/mimilupion.github.io

Fernando Cejas

commit sha 360fb56904b208e0f4c2363732f66ca9ad96d56a

Update README.md

view details

push time in 2 months

Pull request review commentwireapp/wire-android

New backups (3): Changes requested in the code reviews and by detekt

 abstract class BackupLocalDataSource<EntityType, JSONType>(     }      companion object {-        fun toIntArray(bytes: ByteArray?): IntArray? = bytes?.map { it.toInt() }?.toIntArray()-        fun toByteArray(ints: IntArray?): ByteArray? = ints?.map { it.toByte() }?.toByteArray()+        protected val json by lazy { Json(JsonConfiguration.Stable.copy(isLenient = true, ignoreUnknownKeys = true)) }

Good catch! Agree!

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources++import com.waz.model.Handle+import com.waz.model.UserId+import com.waz.zclient.core.functional.Either+import com.waz.zclient.shared.backup.BackupRepository+import com.waz.zclient.shared.backup.Password+import com.waz.zclient.shared.backup.datasources.local.*+import java.io.File++class BackupDataSource(

This is clearly an anti-pattern, this class is doing too many things. If we never had any maintenance problem was mainly because of luck. This class is doing too many things.

You have too many collaborators. If its difficult to test which means design could be improved. It is just a code smell:

  • https://refactoring.guru/smells/long-parameter-list
  • https://www.codingrevolution.com/long-parameter-list/
makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toByteArray+import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toIntArray+import com.waz.zclient.storage.db.assets.AssetsDao+import com.waz.zclient.storage.db.assets.AssetsEntity+import kotlinx.serialization.Serializable++class AssetLocalDataSource(private val assetsDao: AssetsDao): BackupLocalDataSource<AssetsEntity>() {+    override suspend fun getAll(): List<AssetsEntity> = assetsDao.allAssets()++    override fun serialize(entity: AssetsEntity): String =+        json.stringify(AssetsJSONEntity.serializer(), AssetsJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): AssetsEntity =+        json.parse(AssetsJSONEntity.serializer(), jsonStr).toEntity()+}++@Serializable+data class AssetsJSONEntity(+        val id: String,+        val token: String? = null,+        val name: String = "",+        val encryption: String = "",+        val mime: String = "",+        val sha: IntArray? = null,+        val size: Int = 0,+        val source: String? = null,+        val preview: String? = null,+        val details: String = "",+        val conversationId: String? = null+) {+    override fun hashCode(): Int =+        id.hashCode() + token.hashCode() + name.hashCode() + encryption.hashCode() ++                mime.hashCode() + size.hashCode() + source.hashCode() + preview.hashCode() ++                details.hashCode() + conversationId.hashCode() //+ (sha?.size ?: 0)

Fair enough!

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 abstract class BackupLocalDataSource<EntityType, JSONType>(     private var currentOffset: Int = 0      protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<EntityType>-    protected abstract fun toJSONType(entity: EntityType): JSONType-    protected abstract fun toEntityType(json: JSONType): EntityType+    protected abstract fun toJSON(entity: EntityType): JSONType+    protected abstract fun toEntity(json: JSONType): EntityType++    fun serialize(entity: EntityType): String = json.stringify(serializer, toJSON(entity))++    fun deserialize(jsonStr: String): EntityType = toEntity(json.parse(serializer, jsonStr)) -    fun serialize(entity: EntityType): String = json.stringify(serializer, toJSONType(entity))-    fun deserialize(jsonStr: String): EntityType = toEntityType(json.parse(serializer, jsonStr))     fun serializeList(list: List<EntityType>): String =-        json.stringify(serializer.list, list.map { entity -> toJSONType(entity) } )+        json.stringify(serializer.list, list.map { entity -> toJSON(entity) } )+     fun deserializeList(jsonListStr: String): List<EntityType> =-        json.parse(serializer.list, jsonListStr).map { entity -> toEntityType(entity) }+        json.parse(serializer.list, jsonListStr).map { jsonEntity -> toEntity(jsonEntity) } -    suspend fun next(): List<EntityType> = returning(getInBatch(batchSize, currentOffset)) {-        currentOffset += it.size+    fun resetCurrentOffset() {+        currentOffset = 0     } -    suspend fun nextJSONString(): String? {-        val list = next()-        return if (list.isNotEmpty()) serializeList(list) else null-    }+    fun getCurrentOffset(): Int = currentOffset -    fun reset(): Unit {-        currentOffset = 0+    suspend fun nextJSONArrayAsString(): String? {

Just wanted to be clear on the meaning of null since it could mean anything and always tends to confuse. Fair enough!

makingthematrix

comment created time in 2 months

pull request commentwireapp/wire-android

Backup functionality

Something else I would like to point out is the fact that this PR is so hard to follow due to its size. We agreed as a team to keep them not so big. So please next time, try to split them up into smaller ones.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.storage.db.conversations.ConversationFoldersDao+import com.waz.zclient.storage.db.conversations.ConversationFoldersEntity+import kotlinx.serialization.Serializable++class ConversationFoldersLocalDataSource(private val conversationFoldersDao: ConversationFoldersDao): BackupLocalDataSource<ConversationFoldersEntity>() {+    override suspend fun getAll(): List<ConversationFoldersEntity> = conversationFoldersDao.allConversationFolders()++    override fun serialize(entity: ConversationFoldersEntity): String =+        json.stringify(ConversationFoldersJSONEntity.serializer(), ConversationFoldersJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): ConversationFoldersEntity =+        json.parse(ConversationFoldersJSONEntity.serializer(), jsonStr).toEntity()+}++@Serializable+data class ConversationFoldersJSONEntity(

Format here

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toByteArray+import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toIntArray+import com.waz.zclient.storage.db.messages.MessagesDao+import com.waz.zclient.storage.db.messages.MessagesEntity+import kotlinx.serialization.Serializable++class MessagesLocalDataSource(private val messagesDao: MessagesDao): BackupLocalDataSource<MessagesEntity>() {+    override suspend fun getAll(): List<MessagesEntity> = messagesDao.allMessages()++    override fun serialize(entity: MessagesEntity): String =+        json.stringify(MessagesJSONEntity.serializer(), MessagesJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): MessagesEntity =+        json.parse(MessagesJSONEntity.serializer(), jsonStr).toEntity()+}++@Serializable+data class MessagesJSONEntity(+    val id: String,+    val conversationId: String = "",+    val messageType: String = "",+    val userId: String = "",+    val content: String? = null,+    val protos: IntArray? = null,+    val time: Int = 0,+    val firstMessage: Boolean = false,+    val members: String? = null,+    val recipient: String? = null,+    val email: String? = null,+    val name: String? = null,+    val messageState: String = "",+    val contentSize: Int = 0,+    val localTime: Int = 0,+    val editTime: Int = 0,+    val ephemeral: Int? = null,+    val expiryTime: Int? = null,+    val expired: Boolean = false,+    val duration: Int? = null,+    val quote: String? = null,+    val quoteValidity: Int = 0,+    val forceReadReceipts: Int? = null,+    val assetId: String? = null+) {+    override fun hashCode(): Int =

Check again my comment. These methods are not necessary. The compiler will do the magic here.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toByteArray+import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toIntArray+import com.waz.zclient.storage.db.assets.AssetsDao+import com.waz.zclient.storage.db.assets.AssetsEntity+import kotlinx.serialization.Serializable++class AssetLocalDataSource(private val assetsDao: AssetsDao): BackupLocalDataSource<AssetsEntity>() {+    override suspend fun getAll(): List<AssetsEntity> = assetsDao.allAssets()++    override fun serialize(entity: AssetsEntity): String =+        json.stringify(AssetsJSONEntity.serializer(), AssetsJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): AssetsEntity =+        json.parse(AssetsJSONEntity.serializer(), jsonStr).toEntity()+}++@Serializable+data class AssetsJSONEntity(+        val id: String,+        val token: String? = null,+        val name: String = "",+        val encryption: String = "",+        val mime: String = "",+        val sha: IntArray? = null,+        val size: Int = 0,+        val source: String? = null,+        val preview: String? = null,+        val details: String = "",+        val conversationId: String? = null+) {+    override fun hashCode(): Int =+        id.hashCode() + token.hashCode() + name.hashCode() + encryption.hashCode() ++                mime.hashCode() + size.hashCode() + source.hashCode() + preview.hashCode() ++                details.hashCode() + conversationId.hashCode() //+ (sha?.size ?: 0)++    override fun equals(other: Any?): Boolean =+        other != null && other is AssetsJSONEntity && other.id == id && other.token == token &&+                other.name == name && other.encryption == encryption && other.mime == mime &&+                other.size == size && other.source == source && other.preview == preview &&+                other.details == details && other.conversationId == conversationId &&+                ((other.sha == null && sha == null) || other.sha != null && sha !== null &&+                    other.sha.zip(sha).all { it.first == it.second }+                )

Check my previous comment. In java that was needed. In Kotlin it is not required, especially by declaring this as a data class.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.storage.db.buttons.ButtonDao+import com.waz.zclient.storage.db.buttons.ButtonEntity+import kotlinx.serialization.Serializable++class ButtonLocalDataSource(private val buttonDao: ButtonDao): BackupLocalDataSource<ButtonEntity>() {+    override suspend fun getAll(): List<ButtonEntity> = buttonDao.allButtons()++    override fun serialize(entity: ButtonEntity): String =

Check the rest of the code, you will see many places where you can get rid of this. Especially coming from Scala this is something familiar for you :)

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.storage.db.buttons.ButtonDao+import com.waz.zclient.storage.db.buttons.ButtonEntity+import kotlinx.serialization.Serializable++class ButtonLocalDataSource(private val buttonDao: ButtonDao): BackupLocalDataSource<ButtonEntity>() {+    override suspend fun getAll(): List<ButtonEntity> = buttonDao.allButtons()++    override fun serialize(entity: ButtonEntity): String =+        json.stringify(ButtonJSONEntity.serializer(), ButtonJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): ButtonEntity =

Same here

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toByteArray+import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toIntArray+import com.waz.zclient.storage.db.assets.AssetsDao+import com.waz.zclient.storage.db.assets.AssetsEntity+import kotlinx.serialization.Serializable++class AssetLocalDataSource(private val assetsDao: AssetsDao): BackupLocalDataSource<AssetsEntity>() {+    override suspend fun getAll(): List<AssetsEntity> = assetsDao.allAssets()++    override fun serialize(entity: AssetsEntity): String =+        json.stringify(AssetsJSONEntity.serializer(), AssetsJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): AssetsEntity =+        json.parse(AssetsJSONEntity.serializer(), jsonStr).toEntity()+}++@Serializable+data class AssetsJSONEntity(+        val id: String,+        val token: String? = null,+        val name: String = "",+        val encryption: String = "",+        val mime: String = "",+        val sha: IntArray? = null,+        val size: Int = 0,+        val source: String? = null,+        val preview: String? = null,+        val details: String = "",+        val conversationId: String? = null+) {+    override fun hashCode(): Int =+        id.hashCode() + token.hashCode() + name.hashCode() + encryption.hashCode() ++                mime.hashCode() + size.hashCode() + source.hashCode() + preview.hashCode() ++                details.hashCode() + conversationId.hashCode() //+ (sha?.size ?: 0)++    override fun equals(other: Any?): Boolean =+        other != null && other is AssetsJSONEntity && other.id == id && other.token == token &&+                other.name == name && other.encryption == encryption && other.mime == mime &&+                other.size == size && other.source == source && other.preview == preview &&+                other.details == details && other.conversationId == conversationId &&+                ((other.sha == null && sha == null) || other.sha != null && sha !== null &&+                    other.sha.zip(sha).all { it.first == it.second }+                )++    fun toEntity(): AssetsEntity = AssetsEntity(

AFAIK the compiler will not complain if you get rid of the explicit type.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.storage.db.buttons.ButtonDao+import com.waz.zclient.storage.db.buttons.ButtonEntity+import kotlinx.serialization.Serializable++class ButtonLocalDataSource(private val buttonDao: ButtonDao): BackupLocalDataSource<ButtonEntity>() {+    override suspend fun getAll(): List<ButtonEntity> = buttonDao.allButtons()++    override fun serialize(entity: ButtonEntity): String =+        json.stringify(ButtonJSONEntity.serializer(), ButtonJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): ButtonEntity =+        json.parse(ButtonJSONEntity.serializer(), jsonStr).toEntity()+}++@Serializable+data class ButtonJSONEntity(

Format here as discussed.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.storage.db.buttons.ButtonDao+import com.waz.zclient.storage.db.buttons.ButtonEntity+import kotlinx.serialization.Serializable++class ButtonLocalDataSource(private val buttonDao: ButtonDao): BackupLocalDataSource<ButtonEntity>() {+    override suspend fun getAll(): List<ButtonEntity> = buttonDao.allButtons()++    override fun serialize(entity: ButtonEntity): String =

All these explicit types could be removed.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources.local++import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toByteArray+import com.waz.zclient.shared.backup.datasources.local.BackupLocalDataSource.Companion.toIntArray+import com.waz.zclient.storage.db.assets.AssetsDao+import com.waz.zclient.storage.db.assets.AssetsEntity+import kotlinx.serialization.Serializable++class AssetLocalDataSource(private val assetsDao: AssetsDao): BackupLocalDataSource<AssetsEntity>() {+    override suspend fun getAll(): List<AssetsEntity> = assetsDao.allAssets()++    override fun serialize(entity: AssetsEntity): String =+        json.stringify(AssetsJSONEntity.serializer(), AssetsJSONEntity.from(entity))+    override fun deserialize(jsonStr: String): AssetsEntity =+        json.parse(AssetsJSONEntity.serializer(), jsonStr).toEntity()+}++@Serializable+data class AssetsJSONEntity(+        val id: String,+        val token: String? = null,+        val name: String = "",+        val encryption: String = "",+        val mime: String = "",+        val sha: IntArray? = null,+        val size: Int = 0,+        val source: String? = null,+        val preview: String? = null,+        val details: String = "",+        val conversationId: String? = null+) {+    override fun hashCode(): Int =+        id.hashCode() + token.hashCode() + name.hashCode() + encryption.hashCode() ++                mime.hashCode() + size.hashCode() + source.hashCode() + preview.hashCode() ++                details.hashCode() + conversationId.hashCode() //+ (sha?.size ?: 0)

Let's remove this to overridden methods. That is the purpose of a data class. This is very prone to introduce bugs and issues.

Data class: The compiler automatically derives the following members from all properties declared in the primary constructor:

    equals()/hashCode() pair;
    toString() of the form "User(name=John, age=42)";
    componentN() functions corresponding to the properties in their order of declaration;
    copy() function (see below).
makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources++import com.waz.model.Handle+import com.waz.model.UserId+import com.waz.zclient.core.functional.Either+import com.waz.zclient.shared.backup.BackupRepository+import com.waz.zclient.shared.backup.Password+import com.waz.zclient.shared.backup.datasources.local.*+import java.io.File++class BackupDataSource(+        private val assetLocalDataSource: AssetLocalDataSource,+        private val buttonLocalDataSource: ButtonLocalDataSource,+        private val conversationFoldersLocalDataSource: ConversationFoldersLocalDataSource,+        private val conversationLocalDataSource: ConversationsLocalDataSource,+        private val conversationMemberLocalDataSource: ConversationMemberLocalDataSource,+        private val conversationRoleActionLocalDataSource: ConversationRoleActionLocalDataSource,+        private val foldersLocalDataSource: FoldersLocalDataSource,+        private val keyValueLocalDataSource: KeyValuesLocalDataSource,+        private val likesLocalDataSource: LikesLocalDataSource,+        private val messagesLocalDataSource: MessagesLocalDataSource,+        private val propertiesLocalDataSource: PropertiesLocalDataSource,+        private val readReceiptsLocalDataSource: ReadReceiptsLocalDataSource,+        private val userLocalDataSource: UserLocalDataSource

It is a good point! If we have another repository containing and grouping other DataSources, let's access through the repository. That might also fix my comment above. Good catch @gizemb

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources++import com.waz.model.Handle+import com.waz.model.UserId+import com.waz.zclient.core.functional.Either+import com.waz.zclient.shared.backup.BackupRepository+import com.waz.zclient.shared.backup.Password+import com.waz.zclient.shared.backup.datasources.local.*+import java.io.File++class BackupDataSource(

Well, for example I see 4 DataSources starting with conversation.... Maybe group them in one generic ConversationDataSource (or something like this). Let's be more granular.

Otherwise this will end up in an anti-pattern called God Class which will bring up a maintenance nightmare

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

+package com.waz.zclient.shared.backup.datasources++import com.waz.zclient.UnitTest+import com.waz.zclient.shared.backup.datasources.local.KeyValuesLocalDataSource+import com.waz.zclient.storage.db.property.KeyValuesDao+import com.waz.zclient.storage.db.property.KeyValuesEntity+import kotlinx.coroutines.ExperimentalCoroutinesApi+import kotlinx.coroutines.runBlocking+import org.amshove.kluent.shouldEqual+import org.junit.Test+import org.mockito.ArgumentMatchers+import org.mockito.Mock+import org.mockito.Mockito++@ExperimentalCoroutinesApi+class BackupLocalDataSourceTest : UnitTest() {++    @Mock+    private lateinit var keyValuesDao: KeyValuesDao++    @Test+    fun `read all data as a json array string`(): Unit = runBlocking {+        val dataSource = KeyValuesLocalDataSource(keyValuesDao)+        val keyValuesEntity = KeyValuesEntity("key", "value")++        Mockito.`when`(keyValuesDao.getKeyValuesInBatch(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())).thenReturn(listOf(keyValuesEntity))

You can get rid of the Mockito. static call and add the import.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 import com.waz.zclient.storage.db.folders.FoldersDao import com.waz.zclient.storage.db.folders.FoldersEntity import kotlinx.serialization.Serializable -class FoldersLocalDataSource(private val foldersDao: FoldersDao):-    BackupLocalDataSource<FoldersEntity, FoldersJSONEntity>(FoldersJSONEntity.serializer()) {+class FoldersLocalDataSource(

Format as discussed looks a bit weird here. Is detekt also complaining about it?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 import com.waz.zclient.storage.db.messages.MessagesDao import com.waz.zclient.storage.db.messages.MessagesEntity import kotlinx.serialization.Serializable -class MessagesLocalDataSource(private val messagesDao: MessagesDao):-    BackupLocalDataSource<MessagesEntity, MessagesJSONEntity>(MessagesJSONEntity.serializer()) {+class MessagesLocalDataSource(+    private val messagesDao: MessagesDao,+    batchSize: Int = BatchSize+): BackupLocalDataSource<MessagesEntity, MessagesJSONEntity>(MessagesJSONEntity.serializer(), batchSize) {

Same. Not strong about all this formatting if we agree on something and we maintain it across the entire codebase.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 import com.waz.zclient.storage.db.property.KeyValuesDao import com.waz.zclient.storage.db.property.KeyValuesEntity import kotlinx.serialization.Serializable -class KeyValuesLocalDataSource(private val keyValuesDao: KeyValuesDao):-    BackupLocalDataSource<KeyValuesEntity, KeyValuesJSONEntity>(KeyValuesJSONEntity.serializer()) {+class KeyValuesLocalDataSource(+        private val keyValuesDao: KeyValuesDao,+        batchSize: Int = BatchSize+): BackupLocalDataSource<KeyValuesEntity, KeyValuesJSONEntity>(KeyValuesJSONEntity.serializer(), batchSize) {

Same point as above with the code format.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 abstract class BackupLocalDataSource<EntityType, JSONType>(     private var currentOffset: Int = 0      protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<EntityType>-    protected abstract fun toJSONType(entity: EntityType): JSONType-    protected abstract fun toEntityType(json: JSONType): EntityType+    protected abstract fun toJSON(entity: EntityType): JSONType+    protected abstract fun toEntity(json: JSONType): EntityType++    fun serialize(entity: EntityType): String = json.stringify(serializer, toJSON(entity))

I do not think you need the type here. Should be implicit.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 abstract class BackupLocalDataSource<EntityType, JSONType>(     private var currentOffset: Int = 0      protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<EntityType>-    protected abstract fun toJSONType(entity: EntityType): JSONType-    protected abstract fun toEntityType(json: JSONType): EntityType+    protected abstract fun toJSON(entity: EntityType): JSONType+    protected abstract fun toEntity(json: JSONType): EntityType++    fun serialize(entity: EntityType): String = json.stringify(serializer, toJSON(entity))++    fun deserialize(jsonStr: String): EntityType = toEntity(json.parse(serializer, jsonStr))

Same here: type should be implicit.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 abstract class BackupLocalDataSource<EntityType, JSONType>(     private var currentOffset: Int = 0      protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<EntityType>-    protected abstract fun toJSONType(entity: EntityType): JSONType-    protected abstract fun toEntityType(json: JSONType): EntityType+    protected abstract fun toJSON(entity: EntityType): JSONType+    protected abstract fun toEntity(json: JSONType): EntityType++    fun serialize(entity: EntityType): String = json.stringify(serializer, toJSON(entity))++    fun deserialize(jsonStr: String): EntityType = toEntity(json.parse(serializer, jsonStr)) -    fun serialize(entity: EntityType): String = json.stringify(serializer, toJSONType(entity))-    fun deserialize(jsonStr: String): EntityType = toEntityType(json.parse(serializer, jsonStr))     fun serializeList(list: List<EntityType>): String =-        json.stringify(serializer.list, list.map { entity -> toJSONType(entity) } )+        json.stringify(serializer.list, list.map { entity -> toJSON(entity) } )+     fun deserializeList(jsonListStr: String): List<EntityType> =-        json.parse(serializer.list, jsonListStr).map { entity -> toEntityType(entity) }+        json.parse(serializer.list, jsonListStr).map { jsonEntity -> toEntity(jsonEntity) } -    suspend fun next(): List<EntityType> = returning(getInBatch(batchSize, currentOffset)) {-        currentOffset += it.size+    fun resetCurrentOffset() {+        currentOffset = 0     } -    suspend fun nextJSONString(): String? {-        val list = next()-        return if (list.isNotEmpty()) serializeList(list) else null-    }+    fun getCurrentOffset(): Int = currentOffset -    fun reset(): Unit {-        currentOffset = 0+    suspend fun nextJSONArrayAsString(): String? {

Do we want to return a null here? Maybe an empty string?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 package com.waz.zclient.shared.backup.datasources.local +import com.waz.zclient.core.functional.Utils.returning import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration -abstract class BackupLocalDataSource<T> {+abstract class BackupLocalDataSource<T>(val batchSize: Int = BatchSize) {     protected val json by lazy { Json(JsonConfiguration.Stable.copy(isLenient = true, ignoreUnknownKeys = true)) } +    private var currentOffset: Int = 0+     abstract suspend fun getAll(): List<T>+    protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<T>+     abstract fun serialize(entity: T): String     abstract fun deserialize(jsonStr: String): T +    suspend fun next(): List<T> = returning(getInBatch(batchSize, currentOffset)) {

Maybe rename this to something more meaningful? This could mean many things, even the first thing that came to my mind when I saw it, it is the next() method of a RxJava monadic type: Observable.

I think in this case, this means something completely different.

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

+package com.waz.zclient.core.functional++object Utils {

When using names like: Utils or Managers as part of something's name, means we are not clear about the intention of this. Even what does Util mean? And what does returning mean?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 package com.waz.zclient.shared.backup.datasources.local +import com.waz.zclient.core.functional.Utils.returning import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration -abstract class BackupLocalDataSource<T> {+abstract class BackupLocalDataSource<T>(val batchSize: Int = BatchSize) {     protected val json by lazy { Json(JsonConfiguration.Stable.copy(isLenient = true, ignoreUnknownKeys = true)) } +    private var currentOffset: Int = 0+     abstract suspend fun getAll(): List<T>+    protected abstract suspend fun getInBatch(batchSize: Int, offset: Int): List<T>+     abstract fun serialize(entity: T): String     abstract fun deserialize(jsonStr: String): T +    suspend fun next(): List<T> = returning(getInBatch(batchSize, currentOffset)) {+        currentOffset += this.batchSize+    }++    fun reset(): Unit {

What is here being reset?

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Reading entities from db in batches

 import kotlinx.serialization.Serializable  class AssetLocalDataSource(private val assetsDao: AssetsDao): BackupLocalDataSource<AssetsEntity>() {     override suspend fun getAll(): List<AssetsEntity> = assetsDao.allAssets()+    override suspend fun getInBatch(batchSize: Int, offset: Int): List<AssetsEntity> =

:+1:

makingthematrix

comment created time in 2 months

Pull request review commentwireapp/wire-android

Backup functionality

+package com.waz.zclient.shared.backup.datasources++import com.waz.model.Handle+import com.waz.model.UserId+import com.waz.zclient.core.functional.Either+import com.waz.zclient.shared.backup.BackupRepository+import com.waz.zclient.shared.backup.Password+import com.waz.zclient.shared.backup.datasources.local.*+import java.io.File++class BackupDataSource(

This is for me already a code smell: This class is doing too many things by just looking at the number of collaborators.

makingthematrix

comment created time in 2 months

push eventwireapp/wireapp.github.io

Fernando Cejas

commit sha cbef538ab5fa3965ef39bd7d2be6b8a0f1d768fc

Change article intro paragraph. (#13)

view details

push time in 3 months

delete branch wireapp/wireapp.github.io

delete branch : fix/accessibility-article-intro-paragraph

delete time in 3 months

push eventwireapp/wireapp.github.io

BradleyWire

commit sha b221d3ea60b72d151896a682438094933c8e0d65

Duplicated title in accessibility article (#12) * Article for Android Accessibility Development Doesn't Have to Be Scary * added author to config * Duplicated title * rebased with master * moved medium article line down for main page reference to change

view details

push time in 3 months

delete branch wireapp/wireapp.github.io

delete branch : android-accessibility-article

delete time in 3 months

pull request commentwireapp/wireapp.github.io

Wire Zurihack Post Fixes

@arianvp I fixed a bunch of things here. Check it out. Mainly for consistency across the entire project. cc @fisx

android10

comment created time in 3 months

delete branch wireapp/wireapp.github.io

delete branch : fix/wire-zurihack-image-and-feature

delete time in 3 months

push eventwireapp/wireapp.github.io

Fernando Cejas

commit sha 777778008df180f3c18c5ec7f49846aca66ff2d9

Wire Zurihack Post Fixes (#11) * Change featured image for Zurihac article. * Fix article format. * Change featured image. * Add author and rename post for consistency.

view details

push time in 3 months

PR merged wireapp/wireapp.github.io

Wire Zurihack Post Fixes enhancement

A bunch of fixes:

  • Post file name for consistency
  • Feature image for consistency
  • Add author to article to not keep it anonymous
  • Fix weird article format
+78 -114

0 comment

5 changed files

android10

pr closed time in 3 months

issue closedwireapp/wireapp.github.io

posts don't show up if they aren't featured

i.e. the "All Stories" header is just empty

closed time in 3 months

arianvp

issue commentwireapp/wireapp.github.io

posts don't show up if they aren't featured

This is fixed here #11

arianvp

comment created time in 3 months

PR opened wireapp/wireapp.github.io

Wire Zurihack Post Fixes enhancement

A bunch of fixes:

  • Post file name for consistency
  • Feature image for consistency
  • Add author to article to not keep it anonymous
  • Fix weird article format
+78 -114

0 comment

5 changed files

pr created time in 3 months

create barnchwireapp/wireapp.github.io

branch : fix/wire-zurihack-image-and-feature

created branch time in 3 months

push eventwireapp/wireapp.github.io

Fernando Cejas

commit sha e15c89c4168f1283e6a073c5ff5ae9d44daef57c

Android Accessibility post fixes. (#10)

view details

push time in 3 months

delete branch wireapp/wireapp.github.io

delete branch : fix/accessibility-post-fixes

delete time in 3 months

PR merged wireapp/wireapp.github.io

Android Accessibility post fixes.

Fix format and typos on the Android Accessibility Blog post.

+14 -15

0 comment

1 changed file

android10

pr closed time in 3 months

PR opened wireapp/wireapp.github.io

Android Accessibility post fixes.

Fix format and typos on the Android Accessibility Blog post.

+14 -15

0 comment

1 changed file

pr created time in 3 months

create barnchwireapp/wireapp.github.io

branch : fix/accessibility-post-fixes

created branch time in 3 months

delete branch wireapp/wireapp.github.io

delete branch : android-accessibility-article

delete time in 3 months

push eventwireapp/wireapp.github.io

BradleyWire

commit sha 5fe95a83b424ab61da30db0b083903cede7db626

Blog post: Android accessibility development doesn't have to be scary (#9) * Article for Android Accessibility Development Doesn't Have to Be Scary * added author to config

view details

push time in 3 months

PR merged wireapp/wireapp.github.io

Blog post: Android accessibility development doesn't have to be scary

I wrote an article on Medium and thought it was also be fitting to do some knowledge sharing on Wire too.

+263 -0

0 comment

3 changed files

BradleyWire

pr closed time in 3 months

delete branch wireapp/wire-android

delete branch : test-pr-sub-branch

delete time in 3 months

PR closed wireapp/wire-android

test change DO NOT MERGE size/XS

testing the new pr builder

APK

Download build #2113

+1 -1

2 comments

1 changed file

Gardosen

pr closed time in 3 months

pull request commentwireapp/wire-android

test change

Closing this one since there is no activity. We can open it back if needed/required.

Gardosen

comment created time in 3 months

delete branch wireapp/wire-android

delete branch : functional-testing-framework

delete time in 3 months

PR closed wireapp/wire-android

Reviewers
Android Functional(UI) Testing: Introducing Espresso DO NOT MERGE Work In Progress build issue size/M

What's new in this PR?

In an effort to improve the quality of our android client and in hand with the creation of a new android infrastructure system, this PR aims to introduce Espresso as our framework for testing UI components and workflow.

Blockers

At the time being and due to our current codebase using a Scala Plugin for compilation which leads to the following issue because of the usage of an older gradle version:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:transformClassesWithDexBuilderForDevDebugAndroidTest'.
> com.android.build.api.transform.TransformException: 
java.lang.UnsupportedOperationException: 
java.lang.UnsupportedOperationException: This feature requires ASM6

Running tests

./gradlew connectedDevDebugAndroidTest

Notes

There are existing Instrumentation tests not in usage that need to be checked:

Useful Links

  • https://developer.android.com/training/testing/espresso
  • https://developer.android.com/training/testing/espresso/setup
  • https://developer.android.com/training/testing/ui-testing/espresso-testing

APK

Download build #2047

+49 -2

1 comment

6 changed files

android10

pr closed time in 3 months

pull request commentwireapp/wire-android

Android Functional(UI) Testing: Introducing Espresso

Closing this one, since it does not make any sense any more until we are able to bump android build tool. I will bring it back when required.

android10

comment created time in 3 months

issue commentwireapp/wire-android

Android App: Files being shared are not getting stored on the minio servers

Hello @ramesh8830. I'm not sure if I understood your problem correctly:

Are you trying to see all pictures and files being shared in a conversation through the android client, to exist in the server and have access to them?

ramesh8830

comment created time in 3 months

Pull request review commentwireapp/wire-android

[Feature] Conference Calls : VideoGrid - up to 12 participants

         android:id="@+id/video_grid"         android:layout_width="match_parent"         android:layout_height="match_parent"-        app:rowCount="2"+        app:rowCount="6"

this variable looks like a hack. No meaningful name, no intention and a magic number :).

mejdoo

comment created time in 3 months

Pull request review commentwireapp/wire-android

[Feature] Conference Calls : VideoGrid - up to 12 participants

 class CallingFragment extends FragmentHelper {          gridViews.zipWithIndex.foreach { case (r, index) =>           val (row, col, span) = index match {+             case 0 if !isVideoBeingSent && gridViews.size == 2 => (0, 0, 2)             case 0                                             => (0, 0, 1)             case 1 if !isVideoBeingSent && gridViews.size == 2 => (1, 0, 2)             case 1                                             => (0, 1, 1)+             case 2 if gridViews.size == 3                      => (1, 0, 2)             case 2                                             => (1, 0, 1)             case 3                                             => (1, 1, 1)

:open_mouth:

mejdoo

comment created time in 3 months

Pull request review commentwireapp/wire-android

Wire Signals library

 dependencies {     implementation "com.wire:icu4j-shrunk:57.1"     implementation "com.googlecode.mp4parser:isoparser:1.1.7"     implementation "com.github.joshjdevl.libsodiumjni:libsodium-jni-aar:2.0.2"+    implementation "com.wire:wire-signals_${LegacyDependencies.SCALA_MAJOR_VERSION}:${Versions.WIRE_SIGNALS}"

Great Point Gizem. I did not see this one :heart:

makingthematrix

comment created time in 3 months

issue commentwireapp/wireapp.github.io

Feature Image and Author: Wire goes ZuriHac Post

@mheinzel I'm totally fine, this is something that you have to decide. Whatever fits the best. It is mostly a cosmetic change in order to keep the UI consistent and attractive. Maybe send a PR?

android10

comment created time in 3 months

issue openedwireapp/wireapp.github.io

Add <center> style for images

Images on posts are not correctly aligned due to markdown limitations which does not provide a good way for doing it. A solution would be to add a <center> style as a fix by default to keep a consistent UI.

issue_blog

created time in 3 months

pull request commentwireapp/wireapp.github.io

blog post: zurihac

Hey @fisx, @arianvp, @mheinzel, thanks for the contribution. I opened an issue related to the article: https://github.com/wireapp/wireapp.github.io/issues/6

In any case, great job everyone!

fisx

comment created time in 3 months

more