Blip-chat-android não abre em aplicativo publicado na Google Play

Sumário

Estamos implementando o blip-chat em um aplicativo feito em Flutter, utilizando código nativo em ambas as plataformas (Swift para iOS, e Kotlin para Android).

Conseguimos implementar com sucesso em ambas as plataformas, porém, na versão publicada do app na Google Play, o chat-bot não abre. Nos emuladores Android locais (pelo Android Studio / VS Code) o widget aparece, mas em produção, não.

Screenshots

Na tela abaixo, é possível observar que, rodando localmente, o blip-chat-android widget abre normalmente.

Já, ao baixar o App Credifit diretamente da Google Play, fica parado nesta tela abaixo. Como acontece apenas no App publicado na Google Play, não consegui debugar o retorno para investigar.

O app publicado na App Store funciona normalmente.

Steps to Reproduce

  1. Instalar aplicativo da Credifit pela Google Play
  2. Utilizar usuário de teste (fornecerei para a equipe técnica do Blip para análise)
  3. Abrir o chat-bot

O que já tentamos

  • Incluir os uses-permissions necessários para o Android, e não funcionou;
  • Remover os blipOptions apenas chamando o blip-chat com a nossa key, e não funcionou;

Análises adicionais possíveis

Entendemos ser possível que a equipe do Blip, ao acessar o app de produção e “não funcionar”, consiga analisar internamente os logs do nosso chat-bot para ver que tipo de “recusa” está ocorrendo.

Código para reprodução

Importante ressaltar que, em ambiente local, utilizando a mesma branch de produção, abre normalmente. Apenas no deploy na Google Play não roda.

No lado do Flutter

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_modular/flutter_modular.dart';

import 'customer_support_controller.dart';

class ChatBotAndroidNativePage extends StatefulWidget {
  const ChatBotAndroidNativePage({Key key}) : super(key: key);

  @override
  _ChatBotAndroidNativePageState createState() =>
      _ChatBotAndroidNativePageState();
}

class _ChatBotAndroidNativePageState
    extends ModularState<ChatBotAndroidNativePage, CustomerSupportController> {
  @override
  Widget build(BuildContext context) {
    controller.logEvent;
    return PlatformViewLink(
      viewType: controller.viewType,
      surfaceFactory:
          (BuildContext context, PlatformViewController controller) {
        return AndroidViewSurface(
          controller: controller as AndroidViewController,
          gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
          hitTestBehavior: PlatformViewHitTestBehavior.opaque,
        );
      },
      onCreatePlatformView: (PlatformViewCreationParams params) {
        return PlatformViewsService.initSurfaceAndroidView(
          id: params.id,
          viewType: controller.viewType,
          layoutDirection: TextDirection.ltr,
          creationParams: controller.creationParams,
          creationParamsCodec: const StandardMessageCodec(),
          onFocus: () => params.onFocusChanged(true),
        )
          ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
          ..create().then((_) => controller.pop());
      },
    );
  }
}

Na parte nativa do Android

MainActivity.kt

package br.com.credifit.consignado

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        flutterEngine
                .platformViewsController
                .registry
                .registerViewFactory("<blip-chat-native-view>", NativeViewFactory())
    }
}

NativeViewFactory.kt

package br.com.credifit.consignado

import android.content.Context
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory


class NativeViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
    override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
        val creationParams = args as Map<*, *>?
        return NativeView(context, creationParams)
    }
}

NativeView.kt

package br.com.credifit.consignado

import android.content.Context
import android.view.View
import io.flutter.plugin.platform.PlatformView
import net.take.blipchat.AuthType
import net.take.blipchat.BlipClient
import net.take.blipchat.models.Account
import net.take.blipchat.models.AuthConfig
import net.take.blipchat.models.BlipOptions

internal class NativeView(context: Context, creationParams: Map<*, *>?) : PlatformView {
    private val view: View = View(context)

    private val chatBotKey: String = creationParams?.get("chatBotKey") as String
    private val customCommonUrl: String = creationParams?.get("customCommonUrl") as String
    private val userIdentity: String = creationParams?.get("userIdentity") as String
    private val userPassword: String = creationParams?.get("userPassword") as String
    private val userName: String = creationParams?.get("userName") as String
    private val userEmail: String = creationParams?.get("userEmail") as String
    private val userCpf: String = creationParams?.get("userCpf") as String
    private val userCompany: String = creationParams?.get("userCompany") as String
    private val userPhone: String = creationParams?.get("userPhone") as String
    private val userAppVersion: String = creationParams?.get("userAppVersion") as String
    override fun getView(): View {
        return view
    }

    override fun dispose() {}

    init {
        val authConfig = AuthConfig(AuthType.Dev, userIdentity, userPassword)

        val extras: MutableMap<String, String> = mutableMapOf()
        extras["cpf"] = userCpf
        extras["company"] = userCompany
        extras["userAppVersion"] = userAppVersion

        val account = Account()
        account.fullName = userName
        account.email = userEmail
        account.phoneNumber = userPhone
        account.extras = extras

        val blipOptions = BlipOptions()
        blipOptions.authConfig = authConfig
        blipOptions.account = account

        blipOptions.customCommonUrl = customCommonUrl

        try {
            BlipClient.openBlipThread(context, chatBotKey, blipOptions)
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        }
    }
}
1 curtida

Após múltiplos testes, conseguimos identificar que era a falta do ProGuard rules de acordo com a documentação do blip-chat-android.

O que gerou a dificuldade, no nosso lado, de ter saber que precisaríamos incluir os ProGuard rules, é que o R8 é o novo padrão de “code shrinking” do Google, e não percebemos que, por padrão, ele é incluído, e que os proguard-rules.pro também funcionam para o R8.

Como sugestão para a equipe Blip, adicionaria na documentação do blip-chat-android esta necessidade de incluir o proguard-rules.pro MESMO se o padrão do shrink já seja R8.

2 curtidas

@Eduardo_Sorensen super obrigada pela sugestão. É muito importante para gente ter o feedback da comunidade sobre os erros e falhas. E o mais legal é que vc descreveu todas as etapas. Já troquei com a nossa equipe e vou mostrar sobre a sua sugestão :dizzy: