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 14123

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 2275

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 1490

Android Library for Logging RxJava Observables and Subscribers.

android10/arrow 445

Arrow is Lightweight library toolbox for Java and Android Development.

android10/Android-AOPExample 408

This is a simple example of Aspect Oriented Programming in Android

android10/Android-ReactiveProgramming 220

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 138

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/Inside_Android_Testing 52

This are android testing samples for the talk: How ANDROID TESTING changed how we think about Death

android10/DynamicProxy_Java_Sample 31

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

Pull request review commentwireapp/wire-android

Refactor SafeApiDataSource: Prettify request APIs

+package com.waz.zclient.core.functional++import com.waz.zclient.core.exception.Failure+import kotlinx.coroutines.runBlocking++/**+ * A helper class that provides fallback actions to suspend functions that return [Either], in a functional way.+ *+ * When [primaryAction] returns [Either.Left], it invokes [fallbackAction] before propagating the [Failure] to upstream.+ * If [fallbackAction] is of type [Either.Right], it propagates this value as if the operation was successful.+ *+ * If a [fallbackSuccessAction] is provided, it also invokes the action upon successful [fallbackAction].+ */+data class EitherFallbackWrapper<R>(

I would also change the name of EitherFallbackWrapper cause it is not very clear, at least from my perspective.

Maybe something like:

  • FallbackAction
  • FallbackOnFailure
gizemb

comment created time in 9 hours

Pull request review commentwireapp/wire-android

Refactor SafeApiDataSource: Prettify request APIs

+package com.waz.zclient.core.functional++import com.waz.zclient.core.exception.Failure+import kotlinx.coroutines.runBlocking++/**+ * A helper class that provides fallback actions to suspend functions that return [Either], in a functional way.+ *+ * When [primaryAction] returns [Either.Left], it invokes [fallbackAction] before propagating the [Failure] to upstream.+ * If [fallbackAction] is of type [Either.Right], it propagates this value as if the operation was successful.+ *+ * If a [fallbackSuccessAction] is provided, it also invokes the action upon successful [fallbackAction].+ */+data class EitherFallbackWrapper<R>(+    private val primaryAction: suspend () -> Either<Failure, R>,+    private val fallbackAction: suspend () -> Either<Failure, R>,+    private var fallbackSuccessAction: (suspend () -> Any)? = null+) {++    /**+     * Adds an optional [action] to be performed upon a successful [fallbackAction]. If [primaryAction] is+     * successful, and [fallbackAction] is never called, this [action] won't be called too.+     */+    fun finally(action: suspend () -> Any): EitherFallbackWrapper<R> = apply {+        fallbackSuccessAction = action+    }++    /**+     * Invokes given actions with the order of importance, if necessary.+     */+    suspend fun execute(): Either<Failure, R> =+        primaryAction().foldSuspendable({+            fallbackAction().onSuccess {+                runBlocking {+                    fallbackSuccessAction?.invoke()+                }+            }+        }) { Either.Right(it) }!!+}++/**+ * Creates an [EitherFallbackWrapper] from the given suspend function, with its [EitherFallbackWrapper.primaryAction]+ * being the function itself, and [EitherFallbackWrapper.fallbackAction] as the given parameter.+ */+fun <R> (suspend () -> Either<Failure, R>).withFallback(

Here for conciseness and consistency when chaining methods I would rename this to fallback only: This looks great:

    override suspend fun clientById(clientId: String): Either<Failure, Client> =
        clientByIdLocal(clientId)
            .fallback { clientByIdRemote(clientId) }
            .finally { saveClient() }
            .execute()

I guess this is a matter of taste.

gizemb

comment created time in 9 hours

Pull request review commentwireapp/wire-android

Refactor SafeApiDataSource: Prettify request APIs

+package com.waz.zclient.core.functional++import com.waz.zclient.core.exception.Failure+import kotlinx.coroutines.runBlocking++/**+ * A helper class that provides fallback actions to suspend functions that return [Either], in a functional way.+ *+ * When [primaryAction] returns [Either.Left], it invokes [fallbackAction] before propagating the [Failure] to upstream.+ * If [fallbackAction] is of type [Either.Right], it propagates this value as if the operation was successful.+ *+ * If a [fallbackSuccessAction] is provided, it also invokes the action upon successful [fallbackAction].+ */+data class EitherFallbackWrapper<R>(+    private val primaryAction: suspend () -> Either<Failure, R>,+    private val fallbackAction: suspend () -> Either<Failure, R>,+    private var fallbackSuccessAction: (suspend () -> Any)? = null+) {++    /**+     * Adds an optional [action] to be performed upon a successful [fallbackAction]. If [primaryAction] is+     * successful, and [fallbackAction] is never called, this [action] won't be called too.+     */+    fun finally(action: suspend () -> Any): EitherFallbackWrapper<R> = apply {+        fallbackSuccessAction = action+    }++    /**+     * Invokes given actions with the order of importance, if necessary.+     */+    suspend fun execute(): Either<Failure, R> =+        primaryAction().foldSuspendable({+            fallbackAction().onSuccess {+                runBlocking {+                    fallbackSuccessAction?.invoke()+                }+            }+        }) { Either.Right(it) }!!

In this case I would remove foldSuspendable() from Either<L,R> because it is not Either responsibility to know about coroutines and anything multi-threading related.

Remember Either responsibility and functionality: Either<L, R> is referred as a disjoint function, which means that this structure is designed to hold either a Left<T> or Right<T> value but never both. It is a functional programming monadic type.

I'm skeptical to break this apis: We want to be more concise for sure but that comes with a price of anti-patterns, at least in this case. Maybe we can try to find a way to add some kind of extension function to avoid the runBlocking statement but I would not make Either multi-purpose.

Plus I saw that we only used foldSuspendable() in 2 places so it should not be a big deal to use runBlocking, plus I guess, makes the code's intention more explicit.

gizemb

comment created time in 9 hours

Pull request review commentwireapp/wire-android

Refactor SafeApiDataSource: Prettify request APIs

 import android.database.sqlite.SQLiteException import com.waz.zclient.core.exception.DatabaseError import com.waz.zclient.core.exception.DatabaseFailure import com.waz.zclient.core.exception.DatabaseStateError-import com.waz.zclient.core.exception.Failure-import com.waz.zclient.core.exception.NetworkFailure import com.waz.zclient.core.exception.SQLError import com.waz.zclient.core.functional.Either-import com.waz.zclient.core.functional.onFailure-import com.waz.zclient.core.functional.onSuccess-import com.waz.zclient.core.logging.Logger-import kotlinx.coroutines.runBlocking--const val TAG = "SafeApiDataSource"

The first thing I would ask, is what is SafeApiDataSource. The name does not tell me much...It is a data source but did we want something like SecureApiDataSource?

gizemb

comment created time in 3 days

Pull request review commentwireapp/wire-android

Update room db version and fix migration versions

 import com.waz.zclient.storage.db.users.service.UserPreferenceDao  @Database(     entities = [UserPreferenceEntity::class, UserEntity::class, ClientEntity::class],-    version = 126,+    version = 127,

Maybe write a unit test that asserts there is consistency between all these variables and versions? I would feel safer since the build breaks if they are not equivalent.

gizemb

comment created time in 5 days

pull request commentwireapp/wire-android

Update room db version and fix migration versions

Should we somehow document this? I would say we need to find a better solution since every time we bump the database version, we need to remember to touch the other files, leading to errors and potential bugs.

Also, is this part of planned story on JIRA?

gizemb

comment created time in 5 days

pull request commentasdf-vm/asdf

asdf_data_dir() returning incorrect path when running as root.

@Stratus3D I will add some tests to it and here is the basic explanation to the problem.

Arch Linux users use pacman as a package manager. You can add hooks to it in order to upgrade specific components when a system upgrade happens. For example, in this use case scenario, every time a system upgrade happens, asdf should be automatically upgrade.

With that being said, these hooks are run as root and without that check, the update will never happens since the path for trying to find the asdf update command is wrong.

Let me also see if I can upload a video to showcase and make it even more clear of what I'm talking about.

android10

comment created time in 5 days

pull request commentwireapp/wire-android

Room Migration: Scala Compatibility

Is there a JIRA ticket you can link in the description?

BradleyWire

comment created time in 13 days

Pull request review commentwireapp/wire-android

AN-6639: Settings about screen

 fun AppCompatActivity.replaceFragment(frameId: Int, fragment: Fragment, addToBac  fun AppCompatActivity.removeFragment(fragment: Fragment) =     supportFragmentManager.doTransaction { remove(fragment) }++fun Activity.getDeviceLocale(): Locale =+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {

even in this case you could use the if statement in one line as an expression:

var deviceLocale = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) DeprecationUtils.getDefaultLocale(this)  else resources.configuration.locales.get(0)

I try to avoid curly braces most of the time in order to be more concise. It favors readability in my case. I'm no strong on this one as soon as it is consistent with the rest of the codebase.

BradleyWire

comment created time in 13 days

Pull request review commentwireapp/wire-android

AN-6639: Settings about screen

 fun AppCompatActivity.replaceFragment(frameId: Int, fragment: Fragment, addToBac  fun AppCompatActivity.removeFragment(fragment: Fragment) =     supportFragmentManager.doTransaction { remove(fragment) }++fun Activity.getDeviceLocale(): Locale =+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {

Let's discuss in the Android Collective. Keep in mind I did not request any changes...it was mostly a matters of taste. :)

BradleyWire

comment created time in 13 days

Pull request review commentwireapp/wire-android

AN-6639: Settings about screen

 fun AppCompatActivity.replaceFragment(frameId: Int, fragment: Fragment, addToBac  fun AppCompatActivity.removeFragment(fragment: Fragment) =     supportFragmentManager.doTransaction { remove(fragment) }++fun Activity.getDeviceLocale(): Locale =+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {

maybe use when here?

return when(Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
...
}
BradleyWire

comment created time in 13 days

Pull request review commentwireapp/wire-android

AN-6641: Settings Support Screen

 import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment+import androidx.lifecycle.observe import com.waz.zclient.R import com.waz.zclient.core.extension.openUrl import kotlinx.android.synthetic.main.fragment_settings_support.*+import org.koin.android.viewmodel.ext.android.viewModel  class SettingsSupportFragment : Fragment() {++    private val settingsSupportViewModel: SettingsSupportViewModel by viewModel()+     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {         return inflater.inflate(R.layout.fragment_settings_support, container, false)     }      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {         super.onViewCreated(view, savedInstanceState)+        initToolbar()+        initViewModel()++        settingsSupportSupportWebsiteButton.setOnClickListener {+            settingsSupportViewModel.onSupportWebsiteClicked()+        }+        settingsSupportContactButton.setOnClickListener {+            settingsSupportViewModel.onSupportContactClicked()+        }+    }++    private fun initViewModel() {

I'm not a big fan of this kind of naming telling us about implementation details (the fact we are using an Android ViewModel class) but instead naming things in relationship to what they are doing (their intention).

I know we have been doing it like this and for consistency I would keep it if this is the case, but a more proper for me would be something like observeData(), which is what it is really happening.

My 2 cents here.

BradleyWire

comment created time in 13 days

Pull request review commentwireapp/wire-android

Feature: Linear onBoarding flow m2

+<?xml version="1.0" encoding="utf-8"?>+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"+    xmlns:app="http://schemas.android.com/apk/res-auto"+    xmlns:tools="http://schemas.android.com/tools"+    android:layout_width="match_parent"+    android:layout_height="match_parent"+    android:background="@color/teams_background">+++    <androidx.constraintlayout.widget.Guideline+        android:id="@+id/customBackendSsoLoginGuideline"+        android:layout_width="wrap_content"+        android:layout_height="wrap_content"+        android:orientation="horizontal"+        app:layout_constraintGuide_percent="0.67" />++    <TextView+        android:id="@+id/customBackendTitleTextView"+        android:layout_width="0dp"+        android:layout_height="wrap_content"+        android:layout_marginTop="@dimen/wire__padding__26"+        android:textSize="@dimen/wire__text_size__regular"+        android:fontFamily="sans-serif"+        android:textStyle="bold"+        android:textColor="@color/outer_space"+        android:letterSpacing="0.02"+        android:gravity="center_horizontal"+        app:layout_constraintStart_toStartOf="parent"+        app:layout_constraintEnd_toEndOf="parent"+        app:layout_constraintTop_toTopOf="parent"+        tools:text="@string/custom_backend_info_title"+        />+++    <TextView+        android:id="@+id/customBackendSubtitleTextView"+        android:layout_width="0dp"+        android:layout_height="wrap_content"+        android:layout_marginTop="@dimen/wire__padding__8"+        android:layout_marginStart="@dimen/wire__margin_huge"+        android:ellipsize="end"+        android:fontFamily="sans-serif-medium"+        android:gravity="center_horizontal"+        android:lines="1"+        android:textColor="@color/gray_chateau"+        android:textSize="@dimen/wire__text_size__smaller"+        android:textStyle="normal"+        app:layout_constraintEnd_toStartOf="@+id/customBackendShowMoreTextView"+        app:layout_constraintStart_toStartOf="parent"+        app:layout_constraintTop_toBottomOf="@+id/customBackendTitleTextView"+        tools:text="S3-EU-1.INTERNAL.PINEAPPLE.COM…" />++    <TextView+        android:id="@+id/customBackendShowMoreTextView"+        android:layout_width="wrap_content"+        android:layout_height="wrap_content"+        android:layout_marginStart="@dimen/wire__padding__10"+        android:layout_marginTop="@dimen/wire__padding__8"+        android:layout_marginEnd="@dimen/wire__margin_huge"+        android:fontFamily="sans-serif-medium"+        android:gravity="center_horizontal"+        android:text="@string/custom_backend_info_show_more"+        android:textColor="@color/curious_blue"+        android:textSize="@dimen/wire__text_size__smaller"+        android:textStyle="normal"+        app:layout_constraintEnd_toEndOf="parent"+        app:layout_constraintStart_toEndOf="@+id/customBackendSubtitleTextView"+        app:layout_constraintTop_toBottomOf="@+id/customBackendTitleTextView" />++    <TextView+        android:id="@+id/customBackendWelcomeTextView"+        android:layout_width="0dp"+        android:layout_height="wrap_content"+        android:fontFamily="sans-serif-light"+        android:gravity="center_horizontal"+        android:lineSpacingExtra="8sp"+        android:textColor="@color/outer_space"+        android:textSize="@dimen/wire__text_size__huge"+        android:textStyle="normal"+        app:layout_constraintBottom_toTopOf="@+id/customBackendEmailLoginButton"+        app:layout_constraintEnd_toEndOf="parent"+        app:layout_constraintHorizontal_bias="0.497"+        app:layout_constraintStart_toStartOf="parent"+        app:layout_constraintTop_toBottomOf="@+id/customBackendSubtitleTextView"+        tools:text="@string/custom_backend_welcome" />++    <Button+        android:id="@+id/customBackendEmailLoginButton"+        android:layout_width="0dp"+        android:layout_height="wrap_content"+        android:layout_marginStart="@dimen/wire__padding__16"+        android:layout_marginEnd="@dimen/wire__padding__16"+        android:layout_marginBottom="@dimen/wire__padding__12"+        android:background="@drawable/blue_rounded_button"+        android:fontFamily="sans-serif"+        android:paddingTop="@dimen/wire__padding__18"+        android:paddingBottom="@dimen/wire__padding__18"+        android:text="@string/welcome_custom_backend_log_in_email"+        android:textColor="@color/white"+        android:textSize="@dimen/wire__text_size__small"+        android:textStyle="bold"+        app:layout_constraintBottom_toTopOf="@+id/customBackendSsoLoginButton"+        app:layout_constraintEnd_toEndOf="parent"+        app:layout_constraintHorizontal_bias="0.0"+        app:layout_constraintStart_toStartOf="parent" />++    <Button

I totally agree with you @mejdoo and I'm not very pushy with the Scala code as soon as we make sure it works and we are happy with the code quality we are shipping. It is unfortunate that we cannot write all this new code in Kotlin so we need to keep in mind how much effort we put in this code that will be at some point refactored.

mejdoo

comment created time in 13 days

pull request commentwireapp/wire-android

Feature: Linear onBoarding flow m2

@mejdoo as a good practice maybe we could link the JIRA tickets in the description. Also link this PR in the JIRA ticket itself for facilitating access.

mejdoo

comment created time in 13 days

delete branch wireapp/wire-android

delete branch : refactor_milestone_1

delete time in 14 days

push eventwireapp/wire-android

Mejdeddine Benzarti

commit sha 2763e18b249caa11adc753e015764023051b95c8

Refactor Milestone 1 - Feature onBoarding Flow (#2583) Refactor Milestone 1 - Feature onBoarding Flow (#2583)

view details

push time in 14 days

PR merged wireapp/wire-android

Refactor Milestone 1 - Feature onBoarding Flow size/L

What's new in this PR?

Using both Kotlin and Scala to create this feature makes the UI navigation flow very complicated. We decided to refactor Milestone 1 based on the following schema :

IMG_20200130_141737

APK

Download build #1117 Download build #1118 Download build #1132 Download build #1136

+308 -310

1 comment

18 changed files

mejdoo

pr closed time in 14 days

Pull request review commentwireapp/wire-android

Refactor Milestone 1 - Feature onBoarding Flow

+/**+ * Wire+ * Copyright (C) 2018 Wire Swiss GmbH+ *+ * This program is free software: you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation, either version 3 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.+ */+package com.waz.zclient.appentry++import android.os.Bundle+import android.view.{LayoutInflater, View, ViewGroup}+import android.widget.Button+import com.waz.utils.returning+import com.waz.zclient._+import com.waz.zclient.appentry.fragments.SignInFragment+import com.waz.zclient.appentry.fragments.SignInFragment.{Email, Login, SignInMethod}++class WelcomeFragment extends SSOFragment {

Same with this one.

mejdoo

comment created time in 14 days

Pull request review commentwireapp/wire-android

Refactor Milestone 1 - Feature onBoarding Flow

+/**+ * Wire+ * Copyright (C) 2018 Wire Swiss GmbH+ *+ * This program is free software: you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation, either version 3 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.+ */+package com.waz.zclient.appentry++import android.os.Bundle+import android.view.{LayoutInflater, View, ViewGroup}+import com.waz.utils.returning+import com.waz.zclient.appentry.fragments.SignInFragment._+import com.waz.zclient.appentry.fragments.{SignInFragment, TeamNameFragment}+import com.waz.zclient.utils.LayoutSpec+import com.waz.zclient.{FragmentHelper, R}+++class CreateAccountFragment extends FragmentHelper {

Is there any reason why this class cannot be written in Kotlin?

mejdoo

comment created time in 14 days

Pull request review commentwireapp/wire-android

Edit Phone screen (Part 3)

 fun Fragment.openUrl(url: String) =  fun Fragment.startActivityWithAction(intentAction: String) =     startActivity(Intent().apply { action = intentAction })++fun Fragment.replaceFragment(frameId: Int, fragment: Fragment, addToBackStack: Boolean) =+    (activity as AppCompatActivity).replaceFragment(frameId, fragment, addToBackStack)++fun Fragment.showBackArrow() =

I would change the name of this. and I do not think this is the fragment responsibility. It is calling the container activity right? In such a case I would move that to an Activity extension function.

BradleyWire

comment created time in 14 days

Pull request review commentwireapp/wire-android

Edit Phone screen (Part 3)

 fun Fragment.openUrl(url: String) =  fun Fragment.startActivityWithAction(intentAction: String) =     startActivity(Intent().apply { action = intentAction })++fun Fragment.replaceFragment(frameId: Int, fragment: Fragment, addToBackStack: Boolean) =+    (activity as AppCompatActivity).replaceFragment(frameId, fragment, addToBackStack)++fun Fragment.showBackArrow() =

Maybe a name for this would be something like enableHomeNavigation()

BradleyWire

comment created time in 14 days

Pull request review commentwireapp/wire-android

Edit Phone screen (Part 3)

             </intent-filter>         </activity> +        <activity+            android:name=".settings.account.phonenumber.editphone.EditPhoneNumberActivity"+            android:hardwareAccelerated="true"+            android:label="@string/empty_string"+            android:launchMode="singleTask"+            android:theme="@style/Theme.Dark.Preferences" />

do we need to define the theme at activity level?

BradleyWire

comment created time in 15 days

push eventwireapp/wire-android

Fernando Cejas

commit sha 1ef14d6a47f76664780e8700326c772b82703c6b

Remove unused classes.

view details

push time in 17 days

push eventwireapp/wire-android

BradleyWire

commit sha 2816d6961322de0cd50c3cd76558b20c17a6842c

Copy change to help with translation around the edit username changes (#2584)

view details

push time in 17 days

delete branch wireapp/wire-android

delete branch : bugfix/copy_change

delete time in 17 days

PR merged wireapp/wire-android

Reviewers
Copy change to help with translation around the edit username changes size/XS

What's new in this PR?

Issues

Better copy changes to be consistent with iOS and to help with other translations.

APK

Download build #1125

+2 -2

0 comment

1 changed file

BradleyWire

pr closed time in 17 days

push eventwireapp/wire-android

Fernando Cejas

commit sha 476ce61ce0a6bbafb84488d9371d56b52361e892

Fix some kotlin classes without proper override methods.

view details

push time in 18 days

Pull request review commentwireapp/wire-android

Edit phone dialog (Part 2)

 import org.koin.android.viewmodel.ext.android.viewModel class EditPhoneDialogFragment : DialogFragment() {      private val rootView: View by lazy {-        requireActivity().layoutInflater.inflate(R.layout.dialog_fragment_edit_phone, null)+        LayoutInflater.from(context).inflate(R.layout.dialog_fragment_edit_phone, null)     } -    private val editPhoneNumberViewModel: EditPhoneNumberViewModel by viewModel()+    private val phoneNumber: String by lazy {+        arguments?.getString(CURRENT_PHONE_NUMBER_KEY, String.empty()) ?: String.empty()+    }++    private val hasEmail: Boolean by lazy {+        arguments?.getBoolean(HAS_EMAIL_BUNDLE_KEY, true) ?: true+    } -    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =-        AlertDialog.Builder(requireActivity())+    private val settingsAccountPhoneNumberViewModel: SettingsAccountPhoneNumberViewModel by viewModel()++    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {+        val builder = AlertDialog.Builder(requireActivity())             .setTitle(getString(R.string.pref__account_action__dialog__edit_phone__title))             .setView(rootView)-            .setPositiveButton(android.R.string.ok) { _, _ ->-                editPhoneNumberViewModel.onOkButtonClicked(+            .setPositiveButton(android.R.string.ok, null)+            .setNegativeButton(android.R.string.cancel, null)+        if (hasEmail) {+            builder.setNeutralButton(R.string.pref_account_delete, null)+        }++        return builder.create()+    }++    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {+        initPhoneInput()+        initCountryCodeInput()+        initDeleteNumberButton()++        lifecycleScope.launchWhenResumed {+            settingsAccountPhoneNumberViewModel.loadPhoneNumberData(phoneNumber)+        }+        return rootView+    }++    override fun onStart() {+        super.onStart()+        val alertDialog = dialog

Maybe we can talk to Product or whoever is responsible of this? I agree with you here

BradleyWire

comment created time in 18 days

Pull request review commentwireapp/wire-android

Edit phone dialog (Part 2)

+package com.waz.zclient.settings.account.phonenumber.editphone++import android.app.Dialog+import android.os.Bundle+import android.view.View+import android.view.WindowManager+import androidx.appcompat.app.AlertDialog+import androidx.fragment.app.DialogFragment+import com.waz.zclient.R+import com.waz.zclient.core.extension.empty+import com.waz.zclient.core.extension.withArgs+import org.koin.android.viewmodel.ext.android.viewModel++class DeletePhoneDialogFragment : DialogFragment() {++    private val phoneNumberViewModel: SettingsAccountPhoneNumberViewModel by viewModel()++    private val phoneNumber: String by lazy {+        arguments?.getString(CURRENT_PHONE_NUMBER_KEY, String.empty())+            ?: String.empty()

maybe in the same line? All these styles seem a little bit weird....

BradleyWire

comment created time in 18 days

Pull request review commentwireapp/wire-android

Edit phone dialog (Part 2)

 class SettingsAccountFragment : Fragment() {      private fun initAccountPhoneNumber() {         settingsAccountViewModel.phoneNumberLiveData.observe(viewLifecycleOwner) { updateAccountPhoneNumber(it) }+        settingsAccountViewModel.phoneDialogLiveData.observe(viewLifecycleOwner) {+            when (it) {+                DialogDetail.EMPTY ->+                    showAddPhoneDialog()+                else ->+                    showEditPhoneDialog(it.number, it.hasEmail)

Same here for readability

BradleyWire

comment created time in 18 days

Pull request review commentwireapp/wire-android

Edit phone dialog (Part 2)

 class SettingsAccountViewModel(      private val profileLiveData = MutableLiveData<User>()

Interesting discussion here, so this approach is to avoid the ViewModel to accidentally modify LiveData by keeping it immutable from the outside right?

BradleyWire

comment created time in 18 days

Pull request review commentwireapp/wire-android

Edit phone dialog (Part 2)

 class SettingsAccountFragment : Fragment() {         EditHandleFragment.newInstance(settingsAccountHandleTitleTextView.text.toString())             .show(requireActivity().supportFragmentManager, String.empty()) -    private fun showEditPhoneDialog() =-        EditPhoneDialogFragment.newInstance(settingsAccountPhoneTitleTextView.text.toString())-            .show(requireActivity().supportFragmentManager, String.empty())+    private fun showEditPhoneDialog(phoneNumber: String, hasEmail: Boolean) =+        EditPhoneDialogFragment.newInstance(+            phoneNumber,

is this being automatically with the code style? It is more readable from my perspective as it was aligned before.

BradleyWire

comment created time in 18 days

Pull request review commentwireapp/wire-android

Edit phone dialog (Part 2)

 class SettingsAccountFragment : Fragment() {      private fun initAccountPhoneNumber() {         settingsAccountViewModel.phoneNumberLiveData.observe(viewLifecycleOwner) { updateAccountPhoneNumber(it) }+        settingsAccountViewModel.phoneDialogLiveData.observe(viewLifecycleOwner) {+            when (it) {+                DialogDetail.EMPTY ->+                    showAddPhoneDialog()

this can be in the same line

BradleyWire

comment created time in 18 days

push eventwireapp/wire-android

BradleyWire

commit sha 37724f15029aaeeefe3d4a617aa998397e027338

Reactive approach with Kotlin Flow API (Part 3) (#2540) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * Added verification to ensure no interactions are with the remote data source inside UserRepositoryTest Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

BradleyWire

commit sha 114060cd3bd30d4c85fb3cd8379c3ae33d529211

Fixed the issue around displaying the notification for Android 10 or below (#2546)

view details

Maciej Gorywoda

commit sha dfe8b999d97c8db0732491c23d6d3ba3803f2875

Don't put new password dialogs one over another (#2543) * Don't put new password dialogs one over another Fix to https://github.com/wireapp/wire-android/issues/2485 When the password dialog appears and the user goes back to the main screen and back, a new dialog prompt will appear on top of it. * Fix the hack around a Biometric Prompt error

view details

BradleyWire

commit sha 389c0c8c569279dae020a605620da3548052f1ce

Reactive approach with Kotlin Flow API (Part 4) (#2542) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored the SettingsAccountViewModel to accommodate the other profile detail UI components through a basic update dialog * Updated name for getProfile to getProfileDetails and non-compiling tests * Cleaned up SettingsAccountFragment * updated error scenarios to update live data rather than rely on logs * Refactored the ViewModel to accommodate let mutable live data objects and utilises Transformations * Updated the SettingsAccountViewModelTest for missing tests * Removed the profile UI model * Refactored SettingsAccountFragment to promote reusability * Addressed comments raised in Part 1 * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * removed UnitTestSuit * Updated ObservableUseCase to return a result when an error occurs * Rebased UserRepositoryTest * Updated test cases to use UnitTest, moved companion object up, changed when in view model to if/else and removed return @map * Improved the profile detail state and updated test cases to reflect that * Updated the network service to accept request body objects * Made the request body for the api calls to be JSON and updated the tests to be much cleaner * Updated unit tests to remove insert for should be to be consistent for all tests * More idiomatic approach to body requests * naming patterns and cosmetic issues around settings account fragment * Refactored constant value in the network service class to be less vague Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

Maciej Gorywoda

commit sha 50f1341786845fa8ab97c075c3527e731daf8e25

Refactoring of SendConnectRequestFragment (#2525) * Refactoring of SendConnectRequestFragment

view details

BradleyWire

commit sha 82e61f664ad1d107f72b739ea773171515000d3a

Removed testing_gallery from the project (#2553)

view details

Gizem Burhanoglu

commit sha 8ff4f87399311ccf1ca1114f8bb2f0f60cd3e95d

Kotlin network rewrite part4: Add data models for access token and refresh token (#2541) * Create data class for local data source of access token * Create AccessToken data class as a common model for remote and local data sources * todo: inversion bec. we receive response model to repo. * Create RefreshToken data classes - Remove AuthTokenHandler class, and use AccessTokenRepository directly * Move access token related code to a separate package * Add UserPreferences object to get SharedPreferences for particular user * Address review comments - Combined read/write functions for the access-refresh tokens into a more generic method.

view details

Mejdeddine Benzarti

commit sha 47f72f4df3f035f3fbd50db30462cadc67e77cf0

Cleanup scala logs (#2554) * cleanup scala logs * format code to the current styling for scala

view details

Maciej Gorywoda

commit sha 39ca2003e0e5054eb3aad624dfca79307557ccc2

PendingConnectRequestFragment (#2528) * PendingConnectRequestFragment * ConnectRequestFragment (#2531)

view details

BradleyWire

commit sha 492250dbfa74e9fdaed2ab8aab84a3d52807a4e5

Change handle fragment - DataSources + UseCases + Skeleton ViewModel/Fragment (Part 1) (#2549) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored the SettingsAccountViewModel to accommodate the other profile detail UI components through a basic update dialog * Updated name for getProfile to getProfileDetails and non-compiling tests * Cleaned up SettingsAccountFragment * updated error scenarios to update live data rather than rely on logs * Refactored the ViewModel to accommodate let mutable live data objects and utilises Transformations * Updated the SettingsAccountViewModelTest for missing tests * Removed the profile UI model * Refactored SettingsAccountFragment to promote reusability * Addressed comments raised in Part 1 * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * removed UnitTestSuit * Updated ObservableUseCase to return a result when an error occurs * Implemented the first stages of the edit handle fragment which opens when you click on your account handle to change it * Added experimental annotations * Rebased UserRepositoryTest * Updated test cases to use UnitTest, moved companion object up, changed when in view model to if/else and removed return @map * Improved the profile detail state and updated test cases to reflect that * Updated the network service to accept request body objects * Made the request body for the api calls to be JSON and updated the tests to be much cleaner * Updated unit tests to remove insert for should be to be consistent for all tests * More idiomatic approach to body requests * Made the validation use case more functional by using expressions rather than statements * formatting * Connected the edit handle dialog to the settings account fragment * Updated shortcut manager to be consistent with the rest of the app * Defined the scope properly for part 1 and updated tests associated with those changes * missing test case for remote data source * Cleaning up companion objects and removing redundant code in tests * companion objects being consistent * Cosmetic and function changes based on review comments * optimised imports in WireApplication * Overcome interface segregation principle issue * extracted conditions for length on handle to private methods * Updated remote data source tests, removed dialog listener from edit handle fragment and changed the text watcher to be more generic * updated network service to pass the correct variable to the correct path Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

Maciej Gorywoda

commit sha 88c50ef6ed7127763aa64762f2d46f02baf0dd2a

Remove unused old assets and sound media code (#2536) * Remove unused old assets and sound media code * Remove 'Spotify' from a few more places in the code

view details

Maciej Gorywoda

commit sha eb9860611cdc688ed37c9fac9f9d7511d070c32c

Blocked user fragment (#2534) * BlockedUserFragment * Fix back press issues in new connect request and block screens

view details

Gizem Burhanoglu

commit sha 47794350d92a9b70bc2ae5589f47b1c79dc676e0

Revert dependency changes for AVS (#2555)

view details

BradleyWire

commit sha 4cd310269ae250367ddc5b52a2019b8f04da021f

Change Handle Fragment - ViewModel logic + UI (Part 2) (#2551) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored the SettingsAccountViewModel to accommodate the other profile detail UI components through a basic update dialog * Updated name for getProfile to getProfileDetails and non-compiling tests * Cleaned up SettingsAccountFragment * updated error scenarios to update live data rather than rely on logs * Refactored the ViewModel to accommodate let mutable live data objects and utilises Transformations * Updated the SettingsAccountViewModelTest for missing tests * Removed the profile UI model * Refactored SettingsAccountFragment to promote reusability * Addressed comments raised in Part 1 * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * removed UnitTestSuit * Updated ObservableUseCase to return a result when an error occurs * Implemented the first stages of the edit handle fragment which opens when you click on your account handle to change it * Added experimental annotations * Rebased UserRepositoryTest * Updated test cases to use UnitTest, moved companion object up, changed when in view model to if/else and removed return @map * Improved the profile detail state and updated test cases to reflect that * Updated the network service to accept request body objects * Made the request body for the api calls to be JSON and updated the tests to be much cleaner * Updated unit tests to remove insert for should be to be consistent for all tests * More idiomatic approach to body requests * Made the validation use case more functional by using expressions rather than statements * formatting * Connected the edit handle dialog to the settings account fragment * More logic around the ViewModel, update to the data layer and fragment * Reverted change to change handle use case * Optimised the arguments passed to the backButton method inside the view model * Accomodated all API scenarios * First draft of the edit handle data flow * Updated the fragment to handle the observed object's behaviour * Updated shortcut manager to be consistent with the rest of the app * Dialog styling * Defined the scope properly for part 1 and updated tests associated with those changes * missing test case for remote data source * Cleaning up companion objects and removing redundant code in tests * companion objects being consistent * Cosmetic and function changes based on review comments * optimised imports in WireApplication * Overcome interface segregation principle issue * extracted conditions for length on handle to private methods * Finished the UI logic for the ViewModel and added all the test scenarios * Updated based on a self review * removed redundant TODO * cleaned up error message logic in the fragment * Implemented test cases for the EditHandleViewModel * Made 'when' statements more lenient for methods that don't get verified but we need a value from * removed redundant condition when settings error message * Updated remote data source tests, removed dialog listener from edit handle fragment and changed the text watcher to be more generic * updated network service to pass the correct variable to the correct path * Updated the test listener logic in the edithandlefragment * Updated test cases to be more consistent in scope and other review comments * updated core ktx dependency * Updated test names within EditHandleViewModelTest Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

Gizem Burhanoglu

commit sha 9c903233c3c29354d6d95b96cf492cb877b04b56

Bump custom AVS version to 5.4.24 (#2556)

view details

Gizem Burhanoglu

commit sha dcf6b9f5943691efac87bf2894c1f7973f7072a0

Add Detekt for coding style & linting (#2529) * Add detekt to project for linting and formatting * Fix detekt errors: NoConsecutiveBlankLines - This rule does not allow 2+ spaces after another * Fix detekt errors: MaximumLineLength - This rule does not allow for line length > 120 * Fix detekt errors: MaxLineLength. Also, use MaxLineLength instead of MaximumLineLength - MaxLineLength(detekt) and MaximumLineLength(ktlint-wrapper) rules are interchangeable. I disabled MaximumLineLength since MaxLineLength is more configurable and the other one can only be suppressed at file level. A disadvantage of MaxLineLength is that it detects false negative on function declarations for methods. It is open to debate, we can always enable the other one. * Fix detekt errors: SpacingAroundColon - This rule checks spacing around ":". The rule is compliant with Kotlin coding style guidelines: https://kotlinlang.org/docs/reference/coding-conventions.html#colon * Fix detekt errors: ParameterListWrapping - Detect misaligned parameter/constructor declaration in accordance with the Kotlin coding style guidelines: https://kotlinlang.org/docs/reference/coding-conventions.html#class-header-formatting * Disable CommentSpacing check. - This rule checks whether comments have a space after "//". It is recommended to put this space after comment indicator in Kotlin coding style guidelines but i took the liberty to suppress it since it also requires a space for TODO comments, which is pretty standard to write as "//TODO". It also has minimal effect to code quality. https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace * Fix detekt errors: NoBlankLineBeforeRBrace - This rule makes sure that there is no space before closing } of a class. * Fix detekt errors: StringTemplate * Suppress detekt's MagicNumber error for ApiService, since the numbers are self-explanatory * Fix detekt errors: ReturnCount. Simplify statements in DateAndTimeUtils class - ReturnCount rule doesn't allow more than two return statements in 1 method. * Fix detekt errors: ConstructorParameterNaming * Fix detekt errors: UnnecessaryAbstractClass - An abstract class without an abstract member can be refactored to a concrete class. * Do not suppress magic numbers for http error codes * Fix detekt errors after merge * Update threshold for TooManyFunctions to 14

view details

Gizem Burhanoglu

commit sha c199d97a2106ab54a289482ce9f615995d8404a1

Kotlin network rewrite part 5: Integrate network layer with Settings screen (#2550) * Create data class for local data source of access token * Create AccessToken data class as a common model for remote and local data sources * todo: inversion bec. we receive response model to repo. * Create RefreshToken data classes - Remove AuthTokenHandler class, and use AccessTokenRepository directly * Move access token related code to a separate package * Add UserPreferences object to get SharedPreferences for particular user * Migrate network layer to coroutines * Let services extend from ApiService instead of using it. This enables us to check parameters in unit test. * Migrate SafeApiDataSource.requestApi calls to ApiService.request calls * Delete sample code for network layer * Add test for no network connection case to ApiServiceTest * Move annotations to class level * Delete unused temporary Network class * Fix detekt errors

view details

Gizem Burhanoglu

commit sha 93e08adf97e8db3490b841322df38ad5d5f7579c

Fix detekt errors for storage module (#2561) * Fix detekt errors for storage module * Add detekt task to ci task - Create detektAll task to run detekt on all subprojects at once

view details

BradleyWire

commit sha 50c0a63044729bc155e2d3ac9f605041c1d28664

Change handle fragment (Part 3) (#2557) implemented the final edge cases for the Edit Handle fragment

view details

Gizem Burhanoglu

commit sha 9269325d7ce3c915c602df6ff50f2e4121882d14

Kotlin network integration part 1: Migrate global database to Room (#2563) * Migrate Global database to Room, along with ActiveAccounts data * Remove HttpError. Http errors should be handled in the network layer. * Update migration - Wrap it into KOTLIN_SETTINGS_MIGRATION condition

view details

push time in 18 days

push eventwireapp/wire-android

Fernando Cejas

commit sha eaf593bc35457fe577f0c6f9b1ec7ff776f898d1

Continue to clean stuff to make the plugin work.

view details

push time in 18 days

push eventwireapp/gradle-android-scala-plugin

Fernando Cejas

commit sha 747b89b80919eb487abc261f43a0d180d3677ae6

Fork plugin from original author. Tiny refactor.

view details

push time in 18 days

push eventwireapp/gradle-android-scala-plugin

Fernando Cejas

commit sha f61ffb7e33cef770ad6482202ec87833d91d7d40

Bump Android Build Tools and remove obsolet/deprecated variant property.

view details

push time in 18 days

push eventwireapp/wire-android

Fernando Cejas

commit sha 0c9845221f617459d30f955cbe5cd36762423eae

Remove obsolete/deprecated property.

view details

push time in 18 days

push eventwireapp/wire-android

BradleyWire

commit sha 1f7d8ef41e5883ee885d333a3f04d3c8ecf96700

Reactive approach with Kotlin Flow API (Part 3) (#2540) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * Added verification to ensure no interactions are with the remote data source inside UserRepositoryTest Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

BradleyWire

commit sha 0c9c940babb5453d03ed7fc915c6a035dc592b57

Fixed the issue around displaying the notification for Android 10 or below (#2546)

view details

Maciej Gorywoda

commit sha 5f338d2f2a0f81164ea53152870752daa5eb683b

Don't put new password dialogs one over another (#2543) * Don't put new password dialogs one over another Fix to https://github.com/wireapp/wire-android/issues/2485 When the password dialog appears and the user goes back to the main screen and back, a new dialog prompt will appear on top of it. * Fix the hack around a Biometric Prompt error

view details

BradleyWire

commit sha 1b77c9658259bc4c6ca6bdae8bf1ac25f203b2c4

Reactive approach with Kotlin Flow API (Part 4) (#2542) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored the SettingsAccountViewModel to accommodate the other profile detail UI components through a basic update dialog * Updated name for getProfile to getProfileDetails and non-compiling tests * Cleaned up SettingsAccountFragment * updated error scenarios to update live data rather than rely on logs * Refactored the ViewModel to accommodate let mutable live data objects and utilises Transformations * Updated the SettingsAccountViewModelTest for missing tests * Removed the profile UI model * Refactored SettingsAccountFragment to promote reusability * Addressed comments raised in Part 1 * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * removed UnitTestSuit * Updated ObservableUseCase to return a result when an error occurs * Rebased UserRepositoryTest * Updated test cases to use UnitTest, moved companion object up, changed when in view model to if/else and removed return @map * Improved the profile detail state and updated test cases to reflect that * Updated the network service to accept request body objects * Made the request body for the api calls to be JSON and updated the tests to be much cleaner * Updated unit tests to remove insert for should be to be consistent for all tests * More idiomatic approach to body requests * naming patterns and cosmetic issues around settings account fragment * Refactored constant value in the network service class to be less vague Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

Maciej Gorywoda

commit sha dd90e893dd083cdd9fad75ed8c8f6add1c393193

Refactoring of SendConnectRequestFragment (#2525) * Refactoring of SendConnectRequestFragment

view details

BradleyWire

commit sha bb0c3a70ba65fc63598c103a8d03eca2282162d2

Removed testing_gallery from the project (#2553)

view details

Gizem Burhanoglu

commit sha 76d52e2c85927ac3e82cc7ddd2519dd1055ad977

Kotlin network rewrite part4: Add data models for access token and refresh token (#2541) * Create data class for local data source of access token * Create AccessToken data class as a common model for remote and local data sources * todo: inversion bec. we receive response model to repo. * Create RefreshToken data classes - Remove AuthTokenHandler class, and use AccessTokenRepository directly * Move access token related code to a separate package * Add UserPreferences object to get SharedPreferences for particular user * Address review comments - Combined read/write functions for the access-refresh tokens into a more generic method.

view details

Mejdeddine Benzarti

commit sha 4c1d960a8ad8527e5654c2443092dbf8d269b24a

Cleanup scala logs (#2554) * cleanup scala logs * format code to the current styling for scala

view details

Maciej Gorywoda

commit sha 16146799802f093d7a142c4640f9dd0f674f4513

PendingConnectRequestFragment (#2528) * PendingConnectRequestFragment * ConnectRequestFragment (#2531)

view details

BradleyWire

commit sha 0b34ce61a53c777450edbea0b8ebd5c556998135

Change handle fragment - DataSources + UseCases + Skeleton ViewModel/Fragment (Part 1) (#2549) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored the SettingsAccountViewModel to accommodate the other profile detail UI components through a basic update dialog * Updated name for getProfile to getProfileDetails and non-compiling tests * Cleaned up SettingsAccountFragment * updated error scenarios to update live data rather than rely on logs * Refactored the ViewModel to accommodate let mutable live data objects and utilises Transformations * Updated the SettingsAccountViewModelTest for missing tests * Removed the profile UI model * Refactored SettingsAccountFragment to promote reusability * Addressed comments raised in Part 1 * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * removed UnitTestSuit * Updated ObservableUseCase to return a result when an error occurs * Implemented the first stages of the edit handle fragment which opens when you click on your account handle to change it * Added experimental annotations * Rebased UserRepositoryTest * Updated test cases to use UnitTest, moved companion object up, changed when in view model to if/else and removed return @map * Improved the profile detail state and updated test cases to reflect that * Updated the network service to accept request body objects * Made the request body for the api calls to be JSON and updated the tests to be much cleaner * Updated unit tests to remove insert for should be to be consistent for all tests * More idiomatic approach to body requests * Made the validation use case more functional by using expressions rather than statements * formatting * Connected the edit handle dialog to the settings account fragment * Updated shortcut manager to be consistent with the rest of the app * Defined the scope properly for part 1 and updated tests associated with those changes * missing test case for remote data source * Cleaning up companion objects and removing redundant code in tests * companion objects being consistent * Cosmetic and function changes based on review comments * optimised imports in WireApplication * Overcome interface segregation principle issue * extracted conditions for length on handle to private methods * Updated remote data source tests, removed dialog listener from edit handle fragment and changed the text watcher to be more generic * updated network service to pass the correct variable to the correct path Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

Maciej Gorywoda

commit sha eeeb184d71b94bd5fa23cf55c0ccb26452e4fb45

Remove unused old assets and sound media code (#2536) * Remove unused old assets and sound media code * Remove 'Spotify' from a few more places in the code

view details

Maciej Gorywoda

commit sha fc3b42cd513d842e1482a39322dacd41f40deaa3

Blocked user fragment (#2534) * BlockedUserFragment * Fix back press issues in new connect request and block screens

view details

Gizem Burhanoglu

commit sha 88da842bbeaade65dbb63c4e9dc6f0c1557eee96

Revert dependency changes for AVS (#2555)

view details

BradleyWire

commit sha 881d92f0adf70507772a69678f189dd80c46e00a

Change Handle Fragment - ViewModel logic + UI (Part 2) (#2551) * add title for empty email and phone number remove unused loading status * customize dialog fragment for edit text * reorganize package structure * implement change name logic through different layers * add ChangeNameUseCase in di module * revert perform fallback and fix ChangeNameUseCase error * fix remote put request problem * refactor based on reviews to respect kotlin idiomatic style * implement a workaround to handle empty response body for put requests * implement unit tests user for local and remote data source * refactor alert builder * migrate to coroutines flow api for profile and change name requests through different layers * revert safeApi class * refactor based on reviews * finalize the conversion to kotlin flow api through different layers * refactor based on reviews * Refactored SettingsAccountFragment end-to-end solution to have Flow observe the data needed * moved annotations to the class level * Moved dialog interface into dialog fragment, formatting, refactoring for conventions and readability * Ripped out non-compiling unit tests * Removed response extension function, updated literal in account fragment and added missing test classes (not test cases) * removed redundant code observing the update of the name * Removed global interface implementation for readability * Implemented the missing unit tests for the UserLocalDataSource * Updated missing test cases for UserRemoteDataSource * Removed the magic numbers for the data source tests * Implemented test cases for UserRepository * updated unit test for profile details to ensure the correct model is being emitted through the flow * Created and updated missing tests for the domain layer * Optimised Mockito imports to be consistent with other tests * Refactored the SettingsAccountViewModel to accommodate the other profile detail UI components through a basic update dialog * Updated name for getProfile to getProfileDetails and non-compiling tests * Cleaned up SettingsAccountFragment * updated error scenarios to update live data rather than rely on logs * Refactored the ViewModel to accommodate let mutable live data objects and utilises Transformations * Updated the SettingsAccountViewModelTest for missing tests * Removed the profile UI model * Refactored SettingsAccountFragment to promote reusability * Addressed comments raised in Part 1 * Refactored use case params to have new prefix attached to variable name, moved companion objects to top of class and extended test cases with UnitTest() * removed UnitTestSuit * Updated ObservableUseCase to return a result when an error occurs * Implemented the first stages of the edit handle fragment which opens when you click on your account handle to change it * Added experimental annotations * Rebased UserRepositoryTest * Updated test cases to use UnitTest, moved companion object up, changed when in view model to if/else and removed return @map * Improved the profile detail state and updated test cases to reflect that * Updated the network service to accept request body objects * Made the request body for the api calls to be JSON and updated the tests to be much cleaner * Updated unit tests to remove insert for should be to be consistent for all tests * More idiomatic approach to body requests * Made the validation use case more functional by using expressions rather than statements * formatting * Connected the edit handle dialog to the settings account fragment * More logic around the ViewModel, update to the data layer and fragment * Reverted change to change handle use case * Optimised the arguments passed to the backButton method inside the view model * Accomodated all API scenarios * First draft of the edit handle data flow * Updated the fragment to handle the observed object's behaviour * Updated shortcut manager to be consistent with the rest of the app * Dialog styling * Defined the scope properly for part 1 and updated tests associated with those changes * missing test case for remote data source * Cleaning up companion objects and removing redundant code in tests * companion objects being consistent * Cosmetic and function changes based on review comments * optimised imports in WireApplication * Overcome interface segregation principle issue * extracted conditions for length on handle to private methods * Finished the UI logic for the ViewModel and added all the test scenarios * Updated based on a self review * removed redundant TODO * cleaned up error message logic in the fragment * Implemented test cases for the EditHandleViewModel * Made 'when' statements more lenient for methods that don't get verified but we need a value from * removed redundant condition when settings error message * Updated remote data source tests, removed dialog listener from edit handle fragment and changed the text watcher to be more generic * updated network service to pass the correct variable to the correct path * Updated the test listener logic in the edithandlefragment * Updated test cases to be more consistent in scope and other review comments * updated core ktx dependency * Updated test names within EditHandleViewModelTest Co-authored-by: Mejdeddine Benzarti <43655743+mejdoo@users.noreply.github.com>

view details

Gizem Burhanoglu

commit sha bed0861403dedd49a6ff4865f61dc49fe4d581bd

Bump custom AVS version to 5.4.24 (#2556)

view details

Gizem Burhanoglu

commit sha 7d1de669212b70584e1028aae591a34eb7b4513b

Add Detekt for coding style & linting (#2529) * Add detekt to project for linting and formatting * Fix detekt errors: NoConsecutiveBlankLines - This rule does not allow 2+ spaces after another * Fix detekt errors: MaximumLineLength - This rule does not allow for line length > 120 * Fix detekt errors: MaxLineLength. Also, use MaxLineLength instead of MaximumLineLength - MaxLineLength(detekt) and MaximumLineLength(ktlint-wrapper) rules are interchangeable. I disabled MaximumLineLength since MaxLineLength is more configurable and the other one can only be suppressed at file level. A disadvantage of MaxLineLength is that it detects false negative on function declarations for methods. It is open to debate, we can always enable the other one. * Fix detekt errors: SpacingAroundColon - This rule checks spacing around ":". The rule is compliant with Kotlin coding style guidelines: https://kotlinlang.org/docs/reference/coding-conventions.html#colon * Fix detekt errors: ParameterListWrapping - Detect misaligned parameter/constructor declaration in accordance with the Kotlin coding style guidelines: https://kotlinlang.org/docs/reference/coding-conventions.html#class-header-formatting * Disable CommentSpacing check. - This rule checks whether comments have a space after "//". It is recommended to put this space after comment indicator in Kotlin coding style guidelines but i took the liberty to suppress it since it also requires a space for TODO comments, which is pretty standard to write as "//TODO". It also has minimal effect to code quality. https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace * Fix detekt errors: NoBlankLineBeforeRBrace - This rule makes sure that there is no space before closing } of a class. * Fix detekt errors: StringTemplate * Suppress detekt's MagicNumber error for ApiService, since the numbers are self-explanatory * Fix detekt errors: ReturnCount. Simplify statements in DateAndTimeUtils class - ReturnCount rule doesn't allow more than two return statements in 1 method. * Fix detekt errors: ConstructorParameterNaming * Fix detekt errors: UnnecessaryAbstractClass - An abstract class without an abstract member can be refactored to a concrete class. * Do not suppress magic numbers for http error codes * Fix detekt errors after merge * Update threshold for TooManyFunctions to 14

view details

Gizem Burhanoglu

commit sha 5217aa104e57fa7644d862eaada11b9ad47cd614

Kotlin network rewrite part 5: Integrate network layer with Settings screen (#2550) * Create data class for local data source of access token * Create AccessToken data class as a common model for remote and local data sources * todo: inversion bec. we receive response model to repo. * Create RefreshToken data classes - Remove AuthTokenHandler class, and use AccessTokenRepository directly * Move access token related code to a separate package * Add UserPreferences object to get SharedPreferences for particular user * Migrate network layer to coroutines * Let services extend from ApiService instead of using it. This enables us to check parameters in unit test. * Migrate SafeApiDataSource.requestApi calls to ApiService.request calls * Delete sample code for network layer * Add test for no network connection case to ApiServiceTest * Move annotations to class level * Delete unused temporary Network class * Fix detekt errors

view details

Gizem Burhanoglu

commit sha 022eede38a0487020df8a09d5ed623c27c0974af

Fix detekt errors for storage module (#2561) * Fix detekt errors for storage module * Add detekt task to ci task - Create detektAll task to run detekt on all subprojects at once

view details

BradleyWire

commit sha 3faed6383ed9df059b4eba6d8cea7b1f6a8f08c1

Change handle fragment (Part 3) (#2557) implemented the final edge cases for the Edit Handle fragment

view details

Gizem Burhanoglu

commit sha 131fa48bcc94c889cc2a9ecd9fda0e0b1e89915a

Kotlin network integration part 1: Migrate global database to Room (#2563) * Migrate Global database to Room, along with ActiveAccounts data * Remove HttpError. Http errors should be handled in the network layer. * Update migration - Wrap it into KOTLIN_SETTINGS_MIGRATION condition

view details

push time in 18 days

issue commentwireapp/wire-android

add landscape orientation for conversations

@FeepingCreature that the expected feedback we are looking for. Maybe that could be an option to setup a default mode when the app starts. This is something to consider, although I cannot promise that, I have to say that this functionality is definitely in our road map.

BTW, I'm curious about how you are forcing landscape mode :)

madprogger

comment created time in 18 days

issue commentwireapp/wire-android

add landscape orientation for conversations

@FeepingCreature there is way more than that. Maybe you can send a video or something to help us?

Android recreates activities when configuration changes (in this case from Portrait to Landscape). if you do not handle correctly garbage collection and you rotate the screen several times, the app is going to freeze, because it is retaining all these references. That leads to a lot of problems and as I said, it requires a big refactor, something we are working on.

@FeepingCreature also feel free to contribute by sending a PR, that is more than welcome. Complaining via chat is easy, we are doing our best to make Wire a robust Messaging Platform and that is why we also decided to open everything we are doing.

madprogger

comment created time in 18 days

Pull request review commentwireapp/gradle-android-scala-plugin

Support android build tools 3.5.2

+@rem

Auto generated file: NO NEED REVIEW.

android10

comment created time in 18 days

Pull request review commentwireapp/gradle-android-scala-plugin

Support android build tools 3.5.2

-#!/usr/bin/env bash+#!/usr/bin/env sh

Auto generated file, no need review

android10

comment created time in 18 days

Pull request review commentwireapp/gradle-android-scala-plugin

Support android build tools 3.5.2

 import org.gradle.api.tasks.TaskAction 
 public class AndroidScalaPluginIntegrationTestTask extends DefaultTask {
 
-    static def GRADLE_VERSION = "4.10.2"
-    static def ANDROID_GRADLE_PLUGIN_VERSION = "3.2.1"
-    static def ANDROID_BUILD_TOOLS_VERSION = "28.0.3"
+    static def GRADLE_VERSION = "5.6.4"

These are all the version we are supporting

android10

comment created time in 18 days

push eventwireapp/gradle-android-scala-plugin

Fernando Cejas

commit sha 0539c52631960e30a8b542caf50dbb608b8d1bd9

Some indentation.

view details

push time in 18 days

issue commentwireapp/wire-android

add landscape orientation for conversations

Hey everyone, just jumping in to make a couple of things more clear. First of all thanks for taking your time and suggesting new features.

Regarding landscape mode, we are basically refactoring/rewriting the app and it is something we have in our roadmap, but due to priorities, some internal coupling and legacy code based on some architectural design decisions, we need to address other problems before:

  • Introduce our new architecture based on clean architecture written in Kotlin, which involves: 1- Restructure Data Layer 2- Restructure Domain Layer 3- Refactor UI

Currently because of Scala and its ecosystem around android, it makes a bit difficult to do achieve all these challenges in a fast way.

We listen to you and the community, and that is why we make everything transparent and open source. You can check the progress on the codebase and keep track of the changes.

Any suggestion and pull requests are more than welcome.

Related our move to the US, this is something out of our hands (Engineering) but if you check the documentation on how Wire works, there is no impact on the privacy since we cannot decrypt/see any messages, that is end to end encryption that happens at a client level, so the data is totally safe and only users can access to it.

There is way more to understand and it is hard to explain it here, but here is further documentation: https://wire-docs.wire.com/download/Wire+Security+Whitepaper.pdf

@XenGi no one is forcing anyone to use Wire of course and constructive feedback and politeness is always more than appreciated it.

madprogger

comment created time in 19 days

push eventwireapp/gradle-android-scala-plugin

Fernando Cejas

commit sha fa65575a39df4c510cc4afea6b4b2a66c24a16fd

Revert removing repositories.

view details

push time in 20 days

push eventwireapp/gradle-android-scala-plugin

Fernando Cejas

commit sha d30702ef31c79e3463961b88316799d4df57d7f6

Remove unused repositories.

view details

push time in 20 days

pull request commentwireapp/wire-android

fix: read receipts now match the spec

@marcoconti83 thanks for the clarification. I do not have enough context on this one. Maybe @gizemb you can help us here?

CalumMcCall

comment created time in 21 days

pull request commentwireapp/wire-android

Settings Account: Logout (Part 1)

Can you link a ticket for this? Plus more context on the description would be great ;)

BradleyWire

comment created time in 22 days

Pull request review commentwireapp/wire-android

Feature: Linear onboarding - milestone 2

+package com.waz.model2.transport.responses++import com.waz.specs.ZSpec+import org.json.JSONObject+import org.junit.runner.RunWith+import org.robolectric.RobolectricTestRunner+import org.scalatest.RobolectricTests++@RunWith(classOf[RobolectricTestRunner])+class DomainVerificationResponseSpec extends ZSpec with RobolectricTests {

Happy to see your efforts for adding quality to the code by writing tests in our legacy codebase! :heart:

gizemb

comment created time in 22 days

Pull request review commentwireapp/wire-android

[Feature] Linear onboarding with custom backend - Milestone 1

+package com.waz.zclient.appentry++import androidx.fragment.app.{Fragment, FragmentActivity, FragmentTransaction}+import com.waz.zclient.R++class FragmentTransactionHelper() {

Maybe something like TransactionHandler or something without the 'helper' word since the responsibility is not clear

mejdoo

comment created time in 22 days

Pull request review commentwireapp/wire-android

[Feature] Linear onboarding with custom backend - Milestone 1

+package com.waz.zclient.auth++import android.content.Intent+import android.os.Bundle+import android.view.LayoutInflater+import android.view.View+import android.view.View.INVISIBLE+import android.view.View.VISIBLE+import android.view.ViewGroup+import androidx.fragment.app.Fragment+import com.waz.zclient.R+import com.waz.zclient.core.config.Config+import kotlinx.android.synthetic.main.fragment_welcome.*+++class WelcomeFragment : Fragment() {++    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {+        return inflater.inflate(R.layout.fragment_welcome, container, false)+    }++    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {+        super.onViewCreated(view, savedInstanceState)+        welcomeCreateAccountButton.setOnClickListener { startCreateAccountFlow() }+        welcomeLoginButton.setOnClickListener { startLoginFlow() }+        welcomeEnterpriseLoginButton.setOnClickListener { startSsoLoginFlow() }+        configureSSOButtonVisibility()+    }++    private fun startCreateAccountFlow() {+        val createAccountIntent = Intent().apply { action = ACTION_CREATE_ACCOUNT }+        startActivity(createAccountIntent)+    }++    private fun startLoginFlow() {+        val loginIntent = Intent().apply {+            action = ACTION_LOGIN+            putExtra(BUNDLE_KEY_FRAGMENT_TO_START, BUNDLE_VALUE_LOGIN_FRAGMENT)+        }+        startActivity(loginIntent)+    }++    private fun startSsoLoginFlow() {+        val loginIntent = Intent().apply {+            action = ACTION_SSO_LOGIN+        }+        startActivity(loginIntent)+    }++    private fun configureSSOButtonVisibility() {+        when (Config.allowSso()) {+            true -> welcomeEnterpriseLoginButton.visibility = VISIBLE+            false -> welcomeEnterpriseLoginButton.visibility = INVISIBLE+        }+    }++    companion object {

Maybe some spaces to separate the concerns?

companion object {
        fun newInstance() = WelcomeFragment()
        
        private val ACTION_LOGIN = Config.applicationId() + ".LOGIN_ACTION"
        private val ACTION_CREATE_ACCOUNT = Config.applicationId() + ".CREATE_ACCOUNT_ACTION"
        private val ACTION_SSO_LOGIN = Config.applicationId() + ".SSO_LOGIN_ACTION"
        
        const val BUNDLE_KEY_FRAGMENT_TO_START = "fragmentToStart"
        const val BUNDLE_VALUE_LOGIN_FRAGMENT = "LoginFragment"
    }
mejdoo

comment created time in 22 days

Pull request review commentwireapp/wire-android

[Feature] Linear onboarding with custom backend - Milestone 1

 package com.waz.zclient.core.config import com.waz.zclient.BuildConfig  object Config {+    fun applicationId() =  BuildConfig.APPLICATION_ID     fun developerSettingsEnabled() = BuildConfig.DEVELOPER_FEATURES_ENABLED     fun appLockForced() = BuildConfig.FORCE_APP_LOCK     fun hideScreenContentForced() = BuildConfig.FORCE_HIDE_SCREEN_CONTENT     fun versionName() = BuildConfig.VERSION_NAME     fun websiteUrl() = BuildConfig.WEBSITE_URL     fun accountsUrl() = BuildConfig.ACCOUNTS_URL+    fun allowSso() = BuildConfig.ALLOW_SSO

it is not clear for me either.

mejdoo

comment created time in 22 days

Pull request review commentwireapp/wire-android

[Feature] Linear onboarding with custom backend - Milestone 1

+package com.waz.zclient.auth++import android.content.Intent+import android.os.Bundle+import android.view.LayoutInflater+import android.view.View+import android.view.View.INVISIBLE+import android.view.View.VISIBLE+import android.view.ViewGroup+import androidx.fragment.app.Fragment+import com.waz.zclient.R+import com.waz.zclient.core.config.Config+import kotlinx.android.synthetic.main.fragment_welcome.*+++class WelcomeFragment : Fragment() {++    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {+        return inflater.inflate(R.layout.fragment_welcome, container, false)+    }++    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {+        super.onViewCreated(view, savedInstanceState)+        welcomeCreateAccountButton.setOnClickListener { startCreateAccountFlow() }+        welcomeLoginButton.setOnClickListener { startLoginFlow() }+        welcomeEnterpriseLoginButton.setOnClickListener { startSsoLoginFlow() }+        configureSSOButtonVisibility()+    }++    private fun startCreateAccountFlow() {+        val createAccountIntent = Intent().apply { action = ACTION_CREATE_ACCOUNT }+        startActivity(createAccountIntent)+    }++    private fun startLoginFlow() {+        val loginIntent = Intent().apply {+            action = ACTION_LOGIN+            putExtra(BUNDLE_KEY_FRAGMENT_TO_START, BUNDLE_VALUE_LOGIN_FRAGMENT)+        }+        startActivity(loginIntent)+    }++    private fun startSsoLoginFlow() {

This can be inline and simplified to:

private fun startSsoLoginFlow() = startActivity(Intent(ACTION_SSO_LOGIN))
mejdoo

comment created time in 22 days

Pull request review commentwireapp/wire-android

[Feature] Linear onboarding with custom backend - Milestone 1

+package com.waz.zclient.auth++import android.content.Intent+import android.os.Bundle+import android.view.LayoutInflater+import android.view.View+import android.view.View.INVISIBLE+import android.view.View.VISIBLE+import android.view.ViewGroup+import androidx.fragment.app.Fragment+import com.waz.zclient.R+import com.waz.zclient.core.config.Config+import kotlinx.android.synthetic.main.fragment_welcome.*+++class WelcomeFragment : Fragment() {++    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {+        return inflater.inflate(R.layout.fragment_welcome, container, false)+    }++    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {+        super.onViewCreated(view, savedInstanceState)+        welcomeCreateAccountButton.setOnClickListener { startCreateAccountFlow() }+        welcomeLoginButton.setOnClickListener { startLoginFlow() }+        welcomeEnterpriseLoginButton.setOnClickListener { startSsoLoginFlow() }+        configureSSOButtonVisibility()+    }++    private fun startCreateAccountFlow() {+        val createAccountIntent = Intent().apply { action = ACTION_CREATE_ACCOUNT }+        startActivity(createAccountIntent)+    }++    private fun startLoginFlow() {

this can also be more concise. It is also a matter of taste.

private fun startLoginFlow() =
        startActivity(Intent(ACTION_LOGIN).apply {
            putExtra(BUNDLE_KEY_FRAGMENT_TO_START, BUNDLE_VALUE_LOGIN_FRAGMENT)
        })
mejdoo

comment created time in 22 days

Pull request review commentwireapp/wire-android

[Feature] Linear onboarding with custom backend - Milestone 1

+package com.waz.zclient.auth++import android.content.Intent+import android.os.Bundle+import android.view.LayoutInflater+import android.view.View+import android.view.View.INVISIBLE+import android.view.View.VISIBLE+import android.view.ViewGroup+import androidx.fragment.app.Fragment+import com.waz.zclient.R+import com.waz.zclient.core.config.Config+import kotlinx.android.synthetic.main.fragment_welcome.*+++class WelcomeFragment : Fragment() {++    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {+        return inflater.inflate(R.layout.fragment_welcome, container, false)+    }++    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {+        super.onViewCreated(view, savedInstanceState)+        welcomeCreateAccountButton.setOnClickListener { startCreateAccountFlow() }+        welcomeLoginButton.setOnClickListener { startLoginFlow() }+        welcomeEnterpriseLoginButton.setOnClickListener { startSsoLoginFlow() }+        configureSSOButtonVisibility()+    }++    private fun startCreateAccountFlow() {

This can be inline:

    private fun startCreateAccountFlow() =
        startActivity(Intent().apply { action = ACTION_CREATE_ACCOUNT })
mejdoo

comment created time in 22 days

Pull request review commentwireapp/wire-android

Edit Phone dialog (Part 1)

+package com.waz.zclient.settings.account.phonenumber.editphone++import androidx.annotation.StringRes+import androidx.lifecycle.LiveData+import androidx.lifecycle.MutableLiveData+import androidx.lifecycle.ViewModel+import androidx.lifecycle.viewModelScope+import com.waz.zclient.R+import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.permissions.result.PermissionSuccess+import com.waz.zclient.user.domain.usecase.phonenumber.ChangePhoneNumberUseCase+import com.waz.zclient.user.domain.usecase.phonenumber.CountryCodeInvalid+import com.waz.zclient.user.domain.usecase.phonenumber.PhoneNumberInvalid+import com.waz.zclient.user.domain.usecase.phonenumber.ValidatePhoneNumberParams+import com.waz.zclient.user.domain.usecase.phonenumber.ValidatePhoneNumberUseCase++data class CountryCodeErrorMessage(@StringRes val errorMessage: Int)+data class PhoneNumberErrorMessage(@StringRes val errorMessage: Int)++class EditPhoneNumberViewModel(private val validatePhoneNumberUseCase: ValidatePhoneNumberUseCase,+                               private val changePhoneNumberNumberUseCase: ChangePhoneNumberUseCase) :+    ViewModel() {++    private var _countryCodeErrorLiveData = MutableLiveData<CountryCodeErrorMessage>()

why all this duplicated stuff?

BradleyWire

comment created time in 22 days

Pull request review commentwireapp/wire-android

Edit Phone dialog (Part 1)

+package com.waz.zclient.user.domain.usecase.phonenumber++import androidx.core.text.isDigitsOnly+import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.exception.FeatureFailure+import com.waz.zclient.core.functional.Either+import com.waz.zclient.core.usecase.UseCase++object CountryCodeInvalid : ValidatePhoneNumberError()+object PhoneNumberInvalid : ValidatePhoneNumberError()++sealed class ValidatePhoneNumberError : FeatureFailure()++class ValidatePhoneNumberUseCase : UseCase<String, ValidatePhoneNumberParams>() {++    override suspend fun run(params: ValidatePhoneNumberParams): Either<Failure, String> =+        if (!isCountryCodeValid(params.countryCode)) {+            Either.Left(CountryCodeInvalid)+        } else if (!params.phoneNumber.isDigitsOnly()) {+            Either.Left(PhoneNumberInvalid)+        } else {+            val phoneNumber = "${params.countryCode}${params.phoneNumber}"+            if (isPhoneNumberValid(phoneNumber)) {+                Either.Left(PhoneNumberInvalid)+            } else {+                Either.Right(phoneNumber)+            }+        }++    //TODO determine if this is enough to cover all supported countries?+    private fun isPhoneNumberValid(phoneNumber: String) =+        phoneNumber.matches(PHONE_NUMBER_REGEX)++    private fun isCountryCodeValid(countryCode: String) =+        countryCode.matches(COUNTRY_CODE_REGEX)++    companion object {+        //Matches E.164 phone number format+        private val PHONE_NUMBER_REGEX = "^\\+?[1-9]\\d{1,14}\$".toRegex()

Good job compiling this regular expressions up front :+1:

BradleyWire

comment created time in 22 days

Pull request review commentwireapp/wire-android

Edit Phone dialog (Part 1)

+package com.waz.zclient.user.domain.usecase.phonenumber++import androidx.core.text.isDigitsOnly+import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.exception.FeatureFailure+import com.waz.zclient.core.functional.Either+import com.waz.zclient.core.usecase.UseCase++object CountryCodeInvalid : ValidatePhoneNumberError()+object PhoneNumberInvalid : ValidatePhoneNumberError()++sealed class ValidatePhoneNumberError : FeatureFailure()++class ValidatePhoneNumberUseCase : UseCase<String, ValidatePhoneNumberParams>() {++    override suspend fun run(params: ValidatePhoneNumberParams): Either<Failure, String> =

How do you feel about removing all these nested if clauses and replace them with a more kotlin style-ish stuff like when?

override suspend fun run(params: ValidatePhoneNumberParams): Either<Failure, String> {
        return when (!isCountryCodeValid(params.countryCode)) {
            true -> Either.Left(CountryCodeInvalid)
            else -> when (!params.phoneNumber.isDigitsOnly()) {
                true -> Either.Left(PhoneNumberInvalid)
                else -> {
                    val phoneNumber = "${params.countryCode}${params.phoneNumber}"
                    when (isPhoneNumberValid(phoneNumber)) {
                        true -> Either.Left(PhoneNumberInvalid)
                        else -> Either.Right(phoneNumber)
                    }
                }
            }
        }
    }
BradleyWire

comment created time in 22 days

Pull request review commentwireapp/wire-android

Edit Phone dialog (Part 1)

+package com.waz.zclient.settings.account.phonenumber.editphone++import androidx.annotation.StringRes+import androidx.lifecycle.LiveData+import androidx.lifecycle.MutableLiveData+import androidx.lifecycle.ViewModel+import androidx.lifecycle.viewModelScope+import com.waz.zclient.R+import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.permissions.result.PermissionSuccess+import com.waz.zclient.user.domain.usecase.phonenumber.ChangePhoneNumberUseCase+import com.waz.zclient.user.domain.usecase.phonenumber.CountryCodeInvalid+import com.waz.zclient.user.domain.usecase.phonenumber.PhoneNumberInvalid+import com.waz.zclient.user.domain.usecase.phonenumber.ValidatePhoneNumberParams+import com.waz.zclient.user.domain.usecase.phonenumber.ValidatePhoneNumberUseCase++data class CountryCodeErrorMessage(@StringRes val errorMessage: Int)+data class PhoneNumberErrorMessage(@StringRes val errorMessage: Int)++class EditPhoneNumberViewModel(private val validatePhoneNumberUseCase: ValidatePhoneNumberUseCase,+                               private val changePhoneNumberNumberUseCase: ChangePhoneNumberUseCase) :+    ViewModel() {++    private var _countryCodeErrorLiveData = MutableLiveData<CountryCodeErrorMessage>()

plus I would argue about starting variables name with an underscore.

BradleyWire

comment created time in 22 days

Pull request review commentwireapp/wire-android

Edit Phone dialog (Part 1)

+package com.waz.zclient.settings.account.phonenumber.editphone++import android.app.Dialog+import android.os.Bundle+import android.view.View+import android.view.WindowManager+import androidx.appcompat.app.AlertDialog+import androidx.fragment.app.DialogFragment+import androidx.lifecycle.observe+import com.waz.zclient.R+import com.waz.zclient.core.extension.withArgs+import com.waz.zclient.core.permissions.PermissionManagerFactory+import com.waz.zclient.core.permissions.extension.strictRequestReadPhoneState+import kotlinx.android.synthetic.main.dialog_edit_phone.editPhoneDialogPhoneNumberTextInputLayout+import kotlinx.android.synthetic.main.dialog_edit_phone.view.editPhoneDialogCountryCodeTextInputEditText+import kotlinx.android.synthetic.main.dialog_edit_phone.view.editPhoneDialogPhoneNumberTextInputEditText+import org.koin.android.viewmodel.ext.android.viewModel++class EditPhoneDialogFragment : DialogFragment() {++    private val permissionManager by lazy {+        PermissionManagerFactory.getPermissionManager(this)+    }++    private val rootView: View by lazy {+        requireActivity().layoutInflater.inflate(R.layout.dialog_edit_phone, null)+    }++    private val editPhoneNumberViewModel: EditPhoneNumberViewModel by viewModel()++    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =+        AlertDialog.Builder(requireActivity())+            .setTitle(getString(R.string.pref__account_action__dialog__edit_phone__title))+            .setView(rootView)+            .setPositiveButton(android.R.string.ok) { _, _ ->+                editPhoneNumberViewModel.onOkButtonClicked(+                    rootView.editPhoneDialogCountryCodeTextInputEditText.text.toString(),+                    rootView.editPhoneDialogPhoneNumberTextInputEditText.text.toString()+                )+            }+            .setNegativeButton(android.R.string.cancel) { _, _ ->+                editPhoneNumberViewModel.onCancelButtonClicked()+            }.create()++    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {+        super.onViewCreated(view, savedInstanceState)+        dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)++        initPhoneInput()+        initCountryCodeInput()+    }++    private fun initCountryCodeInput() {+        editPhoneNumberViewModel.countryCodeErrorLiveData.observe(viewLifecycleOwner) {+            updateCountryCodeError(getString(it.errorMessage))+        }+    }++    private fun initPhoneInput() {+        editPhoneNumberViewModel.phoneNumberErrorLiveData.observe(viewLifecycleOwner) {+            updatePhoneNumberError(getString(it.errorMessage))+        }+        editPhoneNumberViewModel.phoneNumberLiveData.observe(viewLifecycleOwner) {+            showConfirmationDialog(it)+        }+    }++    private fun updateCountryCodeError(errorMessage: String) {+        editPhoneDialogPhoneNumberTextInputLayout.error = errorMessage+    }++    private fun updatePhoneNumberError(errorMessage: String) {+        editPhoneDialogPhoneNumberTextInputLayout.error = errorMessage+    }+++    private fun showConfirmationDialog(phoneNumber: String) {+        //Show confirmation dialog here+    }++    override fun onStart() {+        super.onStart()+        permissionManager.strictRequestReadPhoneState {+            it.fold(+                editPhoneNumberViewModel::onReadPhonePermissionDenied,+                editPhoneNumberViewModel::onReadPhonePermissionGranted+            )+        }+    }++    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)+        permissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults)+    }++    companion object {

You mentioned you moved all the companion objects to the top right? or we keep them to the bottom? Both are valid for me as soon as we keep consistency.

BradleyWire

comment created time in 22 days

pull request commentwireapp/wire-android

Use content descriptions instead of ids to identify views for QA

It is a good starting point towards accessibility, something that needs to be planned out.

makingthematrix

comment created time in 22 days

pull request commentwireapp/wire-android

Kotlin Migration: Permission Management

Is this related to a bug? Can you link a ticket or provide a bit more context on the description? Also for the next one, it would be nice to split this into 2 PRs :grinning:

BradleyWire

comment created time in 22 days

pull request commentwireapp/wire-android

fix: read receipts now match the spec

@makingthematrix @marcoconti83 do you know if we can either merge or close this one? AFAIK it is only adding tests.

CalumMcCall

comment created time in 22 days

pull request commentwireapp/wire-android

Bump gradle, android gradle plugin and platform tools.

@gizemb thank you very much for the tip! I will try it out. I was actually trying to modify ours in order to support it so we have full control on the plugin.

android10

comment created time in 22 days

push eventandroid10/vipi

Fernando Cejas

commit sha d9f46fc660e162d791be4716064727bf8e4d568e

Add command execution.

view details

push time in 24 days

push eventandroid10/vipi

Fernando Cejas

commit sha 474540dae5e9dc071c402121a52ce426450a7436

hello world with Cargo.

view details

Fernando Cejas

commit sha 3b3c0e8e825fee6c0674581b0035875df344b7e2

Reading parameter from the standard output.

view details

push time in 25 days

push eventandroid10/vipi

Fernando Cejas

commit sha 94ca1267a128242ff1be4a4701ff7ee71b392f49

Create README.md

view details

push time in 25 days

create barnchandroid10/vipi

branch : master

created branch time in 25 days

created repositoryandroid10/vipi

WIP

created time in 25 days

PR opened wireapp/wire-android

Bump gradle, android gradle plugin and platform tools. DO NOT MERGE Work In Progress

What's new in this PR?

WIP

+3 -3

0 comment

3 changed files

pr created time in a month

create barnchwireapp/wire-android

branch : bump-gradle-to-latest-version

created branch time in a month

push eventwireapp/wire-android

Fernando Cejas

commit sha d1cfa80ac3411d2f9c01ea5eca6037b4f8da5f78

Testing gallery 7.0 Minor Refactor and Tests (#2532) * Setup test environment and minor refactor. * Add Test Rule to Unit Test class for junit tests. * Minor refactor and code polishing. * Move filetypes array to FileUtils class. * Add a couple of tests for FileUtils and Video classes.

view details

push time in a month

delete branch wireapp/wire-android

delete branch : testing-gallery-7.0-tests

delete time in a month

PR merged wireapp/wire-android

Testing gallery 7.0 Minor Refactor and Tests QA

What's new in this PR?

  • This is a continuation of #2526 with some minor refactor.
  • This also aims to setup the testing environment and add missing unit tests.
+271 -61

1 comment

11 changed files

android10

pr closed time in a month

Pull request review commentwireapp/wire-android

Don't put new password dialogs one over another

 class RequestPasswordDialog extends DialogFragment with FragmentHelper with Deri       })     ) -    // FIXME: the delay is necessary because of a bug introduced in androidx.biometric:1.0.0-alpha04-    // https://stackoverflow.com/questions/55934108/fragmentmanager-is-already-executing-transactions-when-executing-biometricprompt-    // try to apply the proposed solution-    if (useBiometric) CancellableFuture.delay(100.millis).map { _ =>

:heart:

makingthematrix

comment created time in a month

pull request commentwireapp/wire-android

Testing gallery 7.0 Minor Refactor and Tests

build again

android10

comment created time in a month

push eventwireapp/wire-android

Fernando Cejas

commit sha 6648d71473de3f3e7a35f4f8cebbca8a16b41996

Minor refactor and code polishing.

view details

Fernando Cejas

commit sha b2f93ac30129455de7245ddd8276f55b0e09f3a8

Move filetypes array to FileUtils class.

view details

Fernando Cejas

commit sha a3f9f160e31fe6183dd73d4a5d2f8286bc3bc988

Add a couple of tests for FileUtils and Video classes.

view details

push time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 3)

+package com.waz.zclient.user.domain.usecase++import com.waz.zclient.eq+import com.waz.zclient.user.data.UsersRepository+import kotlinx.coroutines.ExperimentalCoroutinesApi+import kotlinx.coroutines.test.runBlockingTest+import org.junit.Before+import org.junit.Test+import org.mockito.Mock+import org.mockito.Mockito.`when`+import org.mockito.Mockito.verify+import org.mockito.MockitoAnnotations++@ExperimentalCoroutinesApi+class ChangeEmailUseCaseTest {++    private lateinit var changeEmailUseCase: ChangeEmailUseCase++    @Mock+    private lateinit var userRepository: UsersRepository++    @Mock+    private lateinit var changeEmailParams: ChangeEmailParams++    @Before+    fun setup() {+        MockitoAnnotations.initMocks(this)+        changeEmailUseCase = ChangeEmailUseCase(userRepository)+    }++    @Test+    fun `Given update email use case is executed, then the repository should update email`() = runBlockingTest {+        `when`(changeEmailParams.email).thenReturn(TEST_EMAIL)++        changeEmailUseCase.run(changeEmailParams)++        verify(userRepository).changeEmail(eq(TEST_EMAIL))+    }++    companion object {

Definitely something to talk about:

So the rule is logical order of the declarations. If your companion object is public and contains public constants, then I suppose it's logical to have it on top. If it's private and contains only e.g. configuration constants then you may want to have it on bottom.

I remember in Scala that was a rule of thumb in my previous job. https://github.com/android/kotlin-guides/issues/11

BradleyWire

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 3)

 package com.waz.zclient.user.domain.usecase -import org.junit.Assert.*+import com.waz.zclient.eq+import com.waz.zclient.user.data.UsersRepository+import kotlinx.coroutines.ExperimentalCoroutinesApi+import kotlinx.coroutines.test.runBlockingTest+import org.junit.Before+import org.junit.Test+import org.mockito.Mock+import org.mockito.Mockito.`when`+import org.mockito.Mockito.verify+import org.mockito.MockitoAnnotations +@ExperimentalCoroutinesApi class ChangeNameUseCaseTest { +    private lateinit var changeNameUseCase: ChangeNameUseCase++    @Mock+    private lateinit var userRepository: UsersRepository++    @Mock+    private lateinit var changeNameParams: ChangeNameParams++    @Before+    fun setup() {+        MockitoAnnotations.initMocks(this)+        changeNameUseCase = ChangeNameUseCase(userRepository)+    }++    @Test+    fun `Given update name use case is executed, then the repository should update name`() = runBlockingTest {+        `when`(changeNameParams.name).thenReturn(TEST_NAME)++        changeNameUseCase.run(changeNameParams)++        verify(userRepository).changeName(eq(TEST_NAME))+    }++    companion object {

More on this: https://kotlinlang.org/docs/reference/coding-conventions.html

Do not sort the method declarations alphabetically or by visibility, and do not separate regular methods from extension methods. Instead, put related stuff together, so that someone reading the class from top to bottom can follow the logic of what's happening. Choose an order (either higher-level stuff first, or vice versa) and stick to it.

Put nested classes next to the code that uses those classes. If the classes are intended to be used externally and aren't referenced inside the class, put them in the end, after the companion object.

I guess in the end it is a matter of taste but please let's make it consistent.

BradleyWire

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 3)

+package com.waz.zclient.user.domain.usecase++import com.waz.zclient.eq+import com.waz.zclient.user.data.UsersRepository+import kotlinx.coroutines.ExperimentalCoroutinesApi+import kotlinx.coroutines.test.runBlockingTest+import org.junit.Before+import org.junit.Test+import org.mockito.Mock+import org.mockito.Mockito.`when`+import org.mockito.Mockito.verify+import org.mockito.MockitoAnnotations++@ExperimentalCoroutinesApi+class ChangeEmailUseCaseTest {++    private lateinit var changeEmailUseCase: ChangeEmailUseCase++    @Mock+    private lateinit var userRepository: UsersRepository++    @Mock+    private lateinit var changeEmailParams: ChangeEmailParams++    @Before+    fun setup() {+        MockitoAnnotations.initMocks(this)+        changeEmailUseCase = ChangeEmailUseCase(userRepository)+    }++    @Test+    fun `Given update email use case is executed, then the repository should update email`() = runBlockingTest {+        `when`(changeEmailParams.email).thenReturn(TEST_EMAIL)++        changeEmailUseCase.run(changeEmailParams)++        verify(userRepository).changeEmail(eq(TEST_EMAIL))+    }++    companion object {

@makingthematrix says that in Scala, they are in the bottom, but it makes sense since in Scala they sit as a separate class and in kotlin they are contained in the same class.

BradleyWire

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 3)

+package com.waz.zclient.user.domain.usecase++import com.waz.zclient.eq+import com.waz.zclient.user.data.UsersRepository+import kotlinx.coroutines.ExperimentalCoroutinesApi+import kotlinx.coroutines.test.runBlockingTest+import org.junit.Before+import org.junit.Test+import org.mockito.Mock+import org.mockito.Mockito.`when`+import org.mockito.Mockito.verify+import org.mockito.MockitoAnnotations++@ExperimentalCoroutinesApi+class ChangeEmailUseCaseTest {++    private lateinit var changeEmailUseCase: ChangeEmailUseCase++    @Mock+    private lateinit var userRepository: UsersRepository++    @Mock+    private lateinit var changeEmailParams: ChangeEmailParams++    @Before+    fun setup() {+        MockitoAnnotations.initMocks(this)

As I mentioned before: https://github.com/wireapp/wire-android/blob/develop/app/src/test/kotlin/com/waz/zclient/UnitTest.kt

By extending this class for Unit Tests, you become framework agnostic and has some built-in functionality, you do not have to write MockitoAnnotations.initMocks every time

BradleyWire

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 3)

+package com.waz.zclient.user.domain.usecase++import com.waz.zclient.eq+import com.waz.zclient.user.data.UsersRepository+import kotlinx.coroutines.ExperimentalCoroutinesApi+import kotlinx.coroutines.test.runBlockingTest+import org.junit.Before+import org.junit.Test+import org.mockito.Mock+import org.mockito.Mockito.`when`+import org.mockito.Mockito.verify+import org.mockito.MockitoAnnotations++@ExperimentalCoroutinesApi+class ChangeEmailUseCaseTest {++    private lateinit var changeEmailUseCase: ChangeEmailUseCase++    @Mock+    private lateinit var userRepository: UsersRepository++    @Mock+    private lateinit var changeEmailParams: ChangeEmailParams++    @Before+    fun setup() {+        MockitoAnnotations.initMocks(this)

A test rule does this:

    @Suppress("LeakingThis")
    @Rule
    @JvmField
    val injectMocks = InjectMocksRule.create(this@UnitTest)
BradleyWire

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 3)

 import com.waz.zclient.user.data.UserRepositoryTest import com.waz.zclient.user.data.mapper.UserMapperTest import com.waz.zclient.user.data.source.local.UserLocalDataSourceTest import com.waz.zclient.user.data.source.remote.UserRemoteDataSourceTest+import kotlinx.coroutines.InternalCoroutinesApi import org.junit.runner.RunWith import org.junit.runners.Suite +@UseExperimental(InternalCoroutinesApi::class) @RunWith(Suite::class)

We need to come up with some consistency on this. There are a couple of classes for writing unit tests and reusing functionality:

  • https://github.com/wireapp/wire-android/blob/develop/app/src/test/kotlin/com/waz/zclient/UnitTest.kt

Also, every time I write tests I have to remember to add the class to this Suite class?

BradleyWire

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 3)

+package com.waz.zclient.user.domain.usecase++import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.functional.Either+import com.waz.zclient.core.usecase.UseCase+import com.waz.zclient.user.data.UsersRepository++class ChangeEmailUseCase(private val usersRepository: UsersRepository)+    : UseCase<Any, ChangeEmailParams>() {++    override suspend fun run(params: ChangeEmailParams): Either<Failure, Any> =+        usersRepository.changeEmail(params.email)+}++data class ChangeEmailParams(val email: String)

maybe newEmail?

BradleyWire

comment created time in a month

Pull request review commentwireapp/wire-android

Don't put new password dialogs one over another

 class RequestPasswordDialog extends DialogFragment with FragmentHelper with Deri   private lazy val prompt: BiometricPrompt = new BiometricPrompt(getActivity, ExecutorWrapper(Threading.Ui), new BiometricPrompt.AuthenticationCallback {     override def onAuthenticationError(errorCode: Int, errString: CharSequence): Unit = {       super.onAuthenticationError(errorCode, errString)-      verbose(l"onAuthenticationError, code: $errorCode, str: $errString")       onAnswer ! (if (errorCode == BiometricConstants.ERROR_NEGATIVE_BUTTON) BiometricCancelled else BiometricError(errString.toString))     }      override def onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult): Unit = {       super.onAuthenticationSucceeded(result)-      verbose(l"onAuthenticationSucceeded: $result")       onAnswer ! BiometricSuccess     }      override def onAuthenticationFailed(): Unit = {       super.onAuthenticationFailed()-      verbose(l"onAuthenticationFailed")       onAnswer ! BiometricFailure     }   }) +  private lazy val dialog: AlertDialog = {+    val builder = new AlertDialog.Builder(getActivity)+      .setView(root)+      .setTitle(title)+      .setMessage(message)+      .setPositiveButton(R.string.request_password_ok, null)++    Option(getBooleanArg(IsCancellable)).foreach(+      if (_) builder.setNegativeButton(R.string.request_password_cancel, new OnClickListener {+        override def onClick(dialog: DialogInterface, which: Int): Unit = onAnswer ! PasswordCancelled+      })+    )++    // FIXME: the delay is necessary because of a bug introduced in androidx.biometric:1.0.0-alpha04+    // https://stackoverflow.com/questions/55934108/fragmentmanager-is-already-executing-transactions-when-executing-biometricprompt+    // try to apply the proposed solution+    if (useBiometric) CancellableFuture.delay(100.millis).map { _ =>

This is a hack but can we use a different version of the biometric library? 1.0.0 is already stable and available and this was fix within beta 1. https://developer.android.com/jetpack/androidx/releases/biometric

makingthematrix

comment created time in a month

PR opened wireapp/wire-android

Fix AVS internal version for CI. Work In Progress

What's new in this PR?

WIP

+26 -4

0 comment

4 changed files

pr created time in a month

create barnchwireapp/wire-android

branch : fix-ci-avs-internal-version

created branch time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 class SettingsAccountFragment : Fragment() {      private fun initViewModel() {         with(settingsAccountViewModel) {-            loading.observe(viewLifecycleOwner) { isLoading ->-                updateLoadingVisibility(isLoading)+            name.observe(viewLifecycleOwner) { name ->+                updateAccountName(name)+            }+            handle.observe(viewLifecycleOwner) { handle ->+                updateAccountHandle(handle)+            }+            email.observe(viewLifecycleOwner) { emailState ->+                updateAccountEmail(emailState)+            }+            phone.observe(viewLifecycleOwner) { phoneState ->+                updateAccountPhoneNumber(phoneState)             }             error.observe(viewLifecycleOwner) { errorMessage ->                 showErrorMessage(errorMessage)             }-            profile.observe(viewLifecycleOwner) { profile ->-                updateProfile(profile)-            }+        }+    }++    private fun updateAccountHandle(handle: String) {+        preferences_account_handle_title.text = handle+    } +    private fun updateAccountName(name: String) {+        preferences_account_name_title.text = name+    }++    private fun updateAccountPhoneNumber(phoneState: ProfileDetailsState) {+        when (phoneState) {+            is ProfileDetailNull -> preferences_account_phone_title.text = getString(R.string.pref_account_add_email_title)+            is ProfileDetail -> preferences_account_phone_title.text = phoneState.value         }     } -    private fun setupListeners() {-        preferences_account_reset_password.setOnClickListener { openUrl(getString(R.string.url_password_forgot).replaceFirst(Accounts, Config.accountsUrl())) }+    private fun updateAccountEmail(emailState: ProfileDetailsState) {+        when (emailState) {+            is ProfileDetailNull -> preferences_account_email_title.text = getString(R.string.pref_account_add_email_title)+            is ProfileDetail -> preferences_account_email_title.text = emailState.value+        }     } -    private fun loadData() {+    private fun loadProfile() {         lifecycleScope.launchWhenResumed {-            settingsAccountViewModel.loadData()+            settingsAccountViewModel.loadProfile()         }     }      private fun showErrorMessage(errorMessage: String) {         Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_LONG).show()     } -    private fun updateLoadingVisibility(isLoading: Boolean?) {-        //Show hide progress indicator-    }--    private fun updateProfile(userProfileItem: UserProfileItem) {-        preferences_account_name_title.text = userProfileItem.name-        preferences_account_email_title.text = userProfileItem.email-        preferences_account_handle_title.text = userProfileItem.handle-        preferences_account_phone_title.text = userProfileItem.phone+    private fun showEditNameDialogFragment() {

:+1: good naming convention. More meaningful.

mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 class UserLocalDataSourceTest {     }      @Test-    fun `Given profile() is called, when dao result is successful, then return the data`() {+    fun `Given profile() is called, when dao result is successful, then return the data`() = runBlockingTest {+        usersLocalDataSource.profileDetails()++        verify(userDbService).byId(eq(TEST_USER_ID))+    }++    @Test+    fun `Given insertUser is called, when dao result is successful, then return the data`() {+        runBlocking {+            usersLocalDataSource.insertUser(user)++            verify(userDbService).insert(eq(user))++            assert(usersLocalDataSource.insertUser(user).isRight)+        }+    }++    @Test(expected = CancellationException::class)+    fun `Given insertUser is called, when dao result is cancelled, then return the data`() {         runBlocking {+            usersLocalDataSource.insertUser(user)++            verify(userDbService).insert(eq(user))++            cancel(CancellationException(TEST_EXCEPTION_MESSAGE))++            delay(200)++            assert(usersLocalDataSource.insertUser(user).isLeft)+        }+    } -            `when`(userDbService.selectById(TEST_USER_ID)).thenReturn(userDao) -            usersLocalDataSource.profile()+    @Test+    fun `Given changeName is called, when dao result is successful, then return the data`() {+        runBlocking {+            usersLocalDataSource.changeName(TEST_NAME) -            verify(userDbService).selectById(TEST_USER_ID)+            verify(userDbService).updateName(eq(TEST_USER_ID), eq(TEST_NAME)) -            assert(usersLocalDataSource.profile().isRight)+            assert(usersLocalDataSource.changeName(TEST_NAME).isRight)         }     }      @Test(expected = CancellationException::class)-    fun `Given profile() is called, when dao result is an error, then return the error`() {+    fun `Given changeName is called, when dao result is cancelled, then return error`() {         runBlocking { -            `when`(userDbService.selectById(TEST_USER_ID)).thenReturn(userDao)+            usersLocalDataSource.changeName(TEST_NAME)++            verify(userDbService).updateName(eq(TEST_USER_ID), eq(TEST_NAME))++            cancel(CancellationException(TEST_EXCEPTION_MESSAGE))++            delay(200)++            assert(usersLocalDataSource.changeName(TEST_NAME).isLeft)+        }++    }++    @Test+    fun `Given changeHandle is called, when dao result is successful, then return the data`() {+        runBlocking {+            usersLocalDataSource.changeHandle(TEST_HANDLE) -            usersLocalDataSource.profile()+            verify(userDbService).updateHandle(eq(TEST_USER_ID), eq(TEST_HANDLE)) -            verify(userDbService).selectById(TEST_USER_ID)+            assert(usersLocalDataSource.changeHandle(TEST_HANDLE).isRight)+        }+    }++    @Test(expected = CancellationException::class)+    fun `Given changeHandle is called, when dao result is cancelled, then return error`() {+        runBlocking {++            usersLocalDataSource.changeHandle(TEST_HANDLE)++            verify(userDbService).updateHandle(eq(TEST_USER_ID), eq(TEST_HANDLE))              cancel(CancellationException(TEST_EXCEPTION_MESSAGE))              delay(200) -            assert(usersLocalDataSource.profile().isLeft)+            assert(usersLocalDataSource.changeHandle(TEST_HANDLE).isLeft)+        }+    }++    @Test+    fun `Given changeEmail is called, when dao result is successful, then return the data`() {+        runBlocking {+            usersLocalDataSource.changeEmail(TEST_EMAIL)++            verify(userDbService).updateEmail(eq(TEST_USER_ID), eq(TEST_EMAIL))++            assert(usersLocalDataSource.changeEmail(TEST_EMAIL).isRight)         }+    }++    @Test(expected = CancellationException::class)+    fun `Given changeEmail is called, when dao result is cancelled, then return error`() {+        runBlocking {+            usersLocalDataSource.changeEmail(TEST_EMAIL)++            verify(userDbService).updateEmail(eq(TEST_USER_ID), eq(TEST_EMAIL))++            cancel(CancellationException(TEST_EXCEPTION_MESSAGE))++            delay(200) +            assert(usersLocalDataSource.changeEmail(TEST_EMAIL).isLeft)+        }+    }++    @Test+    fun `Given changePhone is called, when dao result is successful, then return the data`() {+        runBlocking {+            usersLocalDataSource.changePhone(TEST_PHONE)++            verify(userDbService).updatePhone(eq(TEST_USER_ID), eq(TEST_PHONE))++            assert(usersLocalDataSource.changePhone(TEST_PHONE).isRight)+        }+    }++    @Test(expected = CancellationException::class)+    fun `Given changePhone is called, when dao result is cancelled, then return error`() {+        runBlocking {+            usersLocalDataSource.changePhone(TEST_PHONE)++            verify(userDbService).updatePhone(eq(TEST_USER_ID), eq(TEST_PHONE))++            cancel(CancellationException(TEST_EXCEPTION_MESSAGE))++            delay(200)++            assert(usersLocalDataSource.changePhone(TEST_PHONE).isLeft)+        }     }      companion object {

For convention, move the companion object to the top of the class.

mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.waz.zclient.core.exception.Failure import com.waz.zclient.core.exception.HttpError-import com.waz.zclient.settings.account.model.UserProfileItem import com.waz.zclient.user.domain.model.User+import com.waz.zclient.user.domain.usecase.ChangeNameParams+import com.waz.zclient.user.domain.usecase.ChangeNameUseCase import com.waz.zclient.user.domain.usecase.GetUserProfileUseCase -class SettingsAccountViewModel constructor(private val getUserProfileUseCase: GetUserProfileUseCase)+data class ProfileDetail(val value: String) : ProfileDetailsState()+object ProfileDetailNull : ProfileDetailsState()++sealed class ProfileDetailsState++class SettingsAccountViewModel constructor(private val getUserProfileUseCase: GetUserProfileUseCase,+                                           private val changeNameUseCase: ChangeNameUseCase)     : ViewModel() { -    private val mutableLoading = MutableLiveData<Boolean>()+    private val mutableName = MutableLiveData<String>()+    private val mutableHandle = MutableLiveData<String>()+    private val mutableEmail = MutableLiveData<ProfileDetailsState>()+    private val mutablePhone = MutableLiveData<ProfileDetailsState>()     private val mutableError = MutableLiveData<String>()-    private val mutableProfile = MutableLiveData<UserProfileItem>() -    val loading: LiveData<Boolean>-        get() = mutableLoading+    val name: LiveData<String>+        get() = mutableName++    val handle: LiveData<String>+        get() = mutableHandle++    val email: LiveData<ProfileDetailsState>+        get() = mutableEmail++    val phone: LiveData<ProfileDetailsState>+        get() = mutablePhone      val error: LiveData<String>         get() = mutableError -    val profile: LiveData<UserProfileItem>-        get() = mutableProfile+    fun loadProfile() {+        getUserProfileUseCase(viewModelScope, Unit) {+            it.fold(::handleError, ::handleProfileSuccess)+        }+    } -    fun loadData() {-        handleLoading(true)-        getUserProfileUseCase(viewModelScope, Unit) { response ->-            response.fold(::handleProfileError, ::handleProfileSuccess)+    fun updateName(name: String) {+        changeNameUseCase(viewModelScope, ChangeNameParams(name)) {+            it.fold(::handleError) {}

No function for success case? I might be missing something here.

mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.waz.zclient.core.exception.Failure import com.waz.zclient.core.exception.HttpError-import com.waz.zclient.settings.account.model.UserProfileItem import com.waz.zclient.user.domain.model.User+import com.waz.zclient.user.domain.usecase.ChangeNameParams+import com.waz.zclient.user.domain.usecase.ChangeNameUseCase import com.waz.zclient.user.domain.usecase.GetUserProfileUseCase -class SettingsAccountViewModel constructor(private val getUserProfileUseCase: GetUserProfileUseCase)+data class ProfileDetail(val value: String) : ProfileDetailsState()+object ProfileDetailNull : ProfileDetailsState()++sealed class ProfileDetailsState++class SettingsAccountViewModel constructor(private val getUserProfileUseCase: GetUserProfileUseCase,+                                           private val changeNameUseCase: ChangeNameUseCase)     : ViewModel() { -    private val mutableLoading = MutableLiveData<Boolean>()+    private val mutableName = MutableLiveData<String>()+    private val mutableHandle = MutableLiveData<String>()+    private val mutableEmail = MutableLiveData<ProfileDetailsState>()+    private val mutablePhone = MutableLiveData<ProfileDetailsState>()     private val mutableError = MutableLiveData<String>()-    private val mutableProfile = MutableLiveData<UserProfileItem>() -    val loading: LiveData<Boolean>-        get() = mutableLoading+    val name: LiveData<String>+        get() = mutableName++    val handle: LiveData<String>+        get() = mutableHandle++    val email: LiveData<ProfileDetailsState>+        get() = mutableEmail++    val phone: LiveData<ProfileDetailsState>+        get() = mutablePhone

Yeah! :tada:

mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 class SettingsAccountFragment : Fragment() {      private fun initViewModel() {         with(settingsAccountViewModel) {-            loading.observe(viewLifecycleOwner) { isLoading ->-                updateLoadingVisibility(isLoading)+            name.observe(viewLifecycleOwner) { name ->+                updateAccountName(name)+            }+            handle.observe(viewLifecycleOwner) { handle ->+                updateAccountHandle(handle)+            }+            email.observe(viewLifecycleOwner) { emailState ->+                updateAccountEmail(emailState)+            }+            phone.observe(viewLifecycleOwner) { phoneState ->+                updateAccountPhoneNumber(phoneState)             }             error.observe(viewLifecycleOwner) { errorMessage ->                 showErrorMessage(errorMessage)             }-            profile.observe(viewLifecycleOwner) { profile ->-                updateProfile(profile)-            }+        }+    }++    private fun updateAccountHandle(handle: String) {+        preferences_account_handle_title.text = handle+    } +    private fun updateAccountName(name: String) {+        preferences_account_name_title.text = name+    }++    private fun updateAccountPhoneNumber(phoneState: ProfileDetailsState) {+        when (phoneState) {+            is ProfileDetailNull -> preferences_account_phone_title.text = getString(R.string.pref_account_add_email_title)+            is ProfileDetail -> preferences_account_phone_title.text = phoneState.value         }     } -    private fun setupListeners() {-        preferences_account_reset_password.setOnClickListener { openUrl(getString(R.string.url_password_forgot).replaceFirst(Accounts, Config.accountsUrl())) }+    private fun updateAccountEmail(emailState: ProfileDetailsState) {+        when (emailState) {+            is ProfileDetailNull -> preferences_account_email_title.text = getString(R.string.pref_account_add_email_title)+            is ProfileDetail -> preferences_account_email_title.text = emailState.value+        }     } -    private fun loadData() {+    private fun loadProfile() {         lifecycleScope.launchWhenResumed {-            settingsAccountViewModel.loadData()+            settingsAccountViewModel.loadProfile()         }     }      private fun showErrorMessage(errorMessage: String) {         Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_LONG).show()     } -    private fun updateLoadingVisibility(isLoading: Boolean?) {-        //Show hide progress indicator-    }--    private fun updateProfile(userProfileItem: UserProfileItem) {-        preferences_account_name_title.text = userProfileItem.name-        preferences_account_email_title.text = userProfileItem.email-        preferences_account_handle_title.text = userProfileItem.handle-        preferences_account_phone_title.text = userProfileItem.phone+    private fun showEditNameDialogFragment() {+        EditTextDialogFragment.newInstance(getString(R.string.pref_account_edit_name_title),+            preferences_account_name_title.text.toString(), object : EditTextDialogFragment.EditTextDialogFragmentListener {+            override fun onTextEdited(newValue: String) {+                settingsAccountViewModel.updateName(newValue)+            }+        }).show(requireActivity().supportFragmentManager, String.empty())     }      companion object {-        fun newInstance() = SettingsAccountFragment()         private const val Accounts = "|ACCOUNTS|"

What is this constant doing?

mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.waz.zclient.core.exception.Failure import com.waz.zclient.core.exception.HttpError-import com.waz.zclient.settings.account.model.UserProfileItem import com.waz.zclient.user.domain.model.User+import com.waz.zclient.user.domain.usecase.ChangeNameParams+import com.waz.zclient.user.domain.usecase.ChangeNameUseCase import com.waz.zclient.user.domain.usecase.GetUserProfileUseCase -class SettingsAccountViewModel constructor(private val getUserProfileUseCase: GetUserProfileUseCase)+data class ProfileDetail(val value: String) : ProfileDetailsState()+object ProfileDetailNull : ProfileDetailsState()

Yes, that was my point when we discuss this? Should we create a new State to pass to the UI so the UI component can decide which resource to use? Or should we manipulate string resources inside ViewModels?

From my perspective I would do that at a fragment to keep view models separated only with domain logic. Good point for discussion doh.

mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.waz.zclient.core.exception.Failure import com.waz.zclient.core.exception.HttpError-import com.waz.zclient.settings.account.model.UserProfileItem import com.waz.zclient.user.domain.model.User+import com.waz.zclient.user.domain.usecase.ChangeNameParams+import com.waz.zclient.user.domain.usecase.ChangeNameUseCase import com.waz.zclient.user.domain.usecase.GetUserProfileUseCase -class SettingsAccountViewModel constructor(private val getUserProfileUseCase: GetUserProfileUseCase)+data class ProfileDetail(val value: String) : ProfileDetailsState()+object ProfileDetailNull : ProfileDetailsState()

What is this for? No nulls not even in names. I would prefer to have some static constructor in this case inside ProfileDetail and when you need an empty ProfileDetail you will construct it something like this:

ProfileDetail.empty()
mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

+package com.waz.zclient.core.usecase++import com.waz.zclient.core.exception.Failure+import com.waz.zclient.core.exception.FlowError+import com.waz.zclient.core.functional.Either+import kotlinx.coroutines.*+import kotlinx.coroutines.flow.Flow+import kotlinx.coroutines.flow.collect++@ExperimentalCoroutinesApi+abstract class ObservableUseCase<out Type, in Params> where Type : Any {++    abstract suspend fun run(params: Params): Flow<Type>++    open operator fun invoke(+        scope: CoroutineScope,+        params: Params,+        onResult: (Either<Failure, Type>) -> Unit = {}) {+        val backgroundJob = scope.async(Dispatchers.IO) { run(params) }+        scope.launch {+            try {+                backgroundJob.await().collect { onResult(Either.Right(it)) }+            } catch (e: Throwable) {+                Either.Left(FlowError(e))

I see the purpose of this generic error. Even I would rename it to GenericUseCaseError but we definitely need to find a better way of doing this. Let's take it offline.

mejdoo

comment created time in a month

Pull request review commentwireapp/wire-android

Reactive approach with Kotlin Flow API (Part 1)

 class SettingsAccountFragment : Fragment() {      private fun initViewModel() {         with(settingsAccountViewModel) {-            loading.observe(viewLifecycleOwner) { isLoading ->-                updateLoadingVisibility(isLoading)+            name.observe(viewLifecycleOwner) { name ->+                updateAccountName(name)+            }+            handle.observe(viewLifecycleOwner) { handle ->+                updateAccountHandle(handle)+            }+            email.observe(viewLifecycleOwner) { emailState ->+                updateAccountEmail(emailState)+            }+            phone.observe(viewLifecycleOwner) { phoneState ->+                updateAccountPhoneNumber(phoneState)             }             error.observe(viewLifecycleOwner) { errorMessage ->                 showErrorMessage(errorMessage)             }-            profile.observe(viewLifecycleOwner) { profile ->-                updateProfile(profile)-            }+        }+    }++    private fun updateAccountHandle(handle: String) {+        preferences_account_handle_title.text = handle+    } +    private fun updateAccountName(name: String) {+        preferences_account_name_title.text = name+    }++    private fun updateAccountPhoneNumber(phoneState: ProfileDetailsState) {

As discussed, it is at this point when we use UiModels for drawing/painting

mejdoo

comment created time in a month

more