pendo-io / pendo-mobile-sdk

Pendo captures product usage data, gathers user feedback, and lets you communicate in-app to onboard, educate, and guide users to value
https://www.pendo.io
Other
57 stars 2 forks source link

Error while integrating Pendo for Flutter Android #165

Closed amrgetment closed 1 month ago

amrgetment commented 1 month ago

Platform + Version Android 13

SDK Version pendo_sdk: ^3.3.2

Framework e.g Native, SwiftUI,
Flutter 3.24.0

Describe the bug I got some errors while integrating Pendo for Android Flutter

  1. Couldn't remove listener from GoRouter's nested branch
  2. PendoScan: primaryFocusHash is 0, might cause issues with current scan
  3. Retro Element Info NOT FOUND

To Reproduce Steps to reproduce the behavior: Check the sample code section

Expected behavior No errors

Logs

PendoFlutterPlugin method name: setup PendoFlutterPlugin method name: track PendoFlutterPlugin method name: startSession [Pendo] [20:28:02.394] ⛔ Couldn't remove listener from GoRouter's nested branch. Seems like the listener is different then the one added.


✅ Pendo Mobile SDK was successfully integrated and connected to the server. You can start adding guides to your app in our web console.

App version identified: '1.1.25' SDK version identified: '3.3.0.8585' Plugin version identified: '3.3.2'


PendoFlutterPlugin method name: screenChanged PendoFlutterPlugin method name: sendAction PendoFlutterPlugin method name: sendAction flutter: [Pendo] [20:28:45.303] ❗️ PendoScan: primaryFocusHash is 0, might cause issues with current scan PendoFlutterPlugin method name: screenChanged flutter: [Pendo] [20:28:45.353] ⛔ Retro Element Info NOT FOUND!

Sample Code In main.dart

 await PendoSDK.setDebugMode(isDebugEnabled: kDebugMode);
  await PendoSDK.setup(pendoKey);
  final String visitorId = await getDeviceIdentifier();
  final Map<String, dynamic> visitorData = <String, dynamic>{
    'Device Type': UniversalPlatform.operatingSystem,
  };
  final Map<String, dynamic> accountData = <String, dynamic>{};

  await PendoSDK.startSession(
    visitorId,
    null,
    visitorData,
    accountData,
  );

    await PendoSDK.track(
    'name',
    <String, dynamic>{
      'firstProperty': 'firstPropertyValue',
      'secondProperty': 'secondPropertyValue',
    },
  );

and for app.dart

class _AppState extends State<App> {
  static final NestedBranchesObserver _pendoGoRouterObserver =
      NestedBranchesObserver(); // Pendo observer for the GoRouter

  void _addRouterToPendoObserver() {
    _pendoGoRouterObserver
      ..removeListener(router)
      ..addListener(router);
  }

  @override
  void initState() {
    super.initState();
    _addRouterToPendoObserver(); // Add your GoRouter instance to the Pendo observer
  }

  @override
  Future<void> dispose() async {
    _pendoGoRouterObserver.removeListener(router);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => PendoActionListener(
        child: KeyboardVisibilityProvider(
                    child: MaterialApp.router(
                      routerConfig: router,

and for go router

final GoRouter router = GoRouter(
  // redirect is no longer needed to handle the deep links
  observers: <NavigatorObserver>[
    PendoNavigationObserver(),
  ],
MikePendo commented 1 month ago

Hi @amrgetment Few things

  1. ⛔ Couldn't remove listener from GoRouter's nested branch. Seems like the listener is different then the one added. please ignore it we are working on new (one line integration that will be much simpler then the current one and this logs will be removed)
  2. If you are using GoRouter please remove PendoNavigationObserver() from the observers we have explained it Flutter Integration ,( Now looking at it maybe our doc wasn't that clear, We will fix it with new integration.)

3. Important PendoScan: primaryFocusHash is 0, might cause issues with current scan PendoFlutterPlugin method name: screenChanged that log is not so good Few words of explanation: In general we are scaning the area under the primary focus to get click analytics on the page. By default, we are using primary focus as staring point for the scan of clickable elements and features for the analytics . So first question why would primary focus will be null in your app? (WidgetsBinding.instance.focusManager.primaryFocus why would that be null?) Do you use builder callback in your Material app? (I have seen some issues with that callback and primary focus) (as a side note: we are working on a new more precise and efficient way to scan the relevant route without the dependency on primary focus) Thanks

amrgetment commented 1 month ago

@MikePendo Yes I have builder before Material.router , when do you expect to scan the relevant route without the dependency on primary focus?

@override
  Widget build(BuildContext context) => PendoActionListener(
        child: AppRepositoriesProvider(
          child: AppBlocProvider(
            child: Builder(
              builder: (BuildContext context) => MultiBlocListener(
                listeners: [ ],
                child: ScreenUtilInit(
                  builder: (_, __) => KeyboardVisibilityProvider(
                    child: MaterialApp.router(
amrgetment commented 1 month ago

I followed proguard in the following guide for Android but I got the following error https://github.com/pendo-io/pendo-mobile-sdk/blob/master/android/pnddocs/pendo-proguard.cfg java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/navigation/NavController$OnDestinationChangedListener

Logs ```dart Launching lib/main.dart on Android SDK built for x86 in debug mode... ✓ Built build/app/outputs/flutter-apk/app-dev-debug.apk Connecting to VM Service at ws://127.0.0.1:65505/FUSy6rZYMdU=/ws Connected to the VM Service. I/zygote ( 6044): Rejecting re-init on previously-failed class java.lang.Class: java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/navigation/NavController$OnDestinationChangedListener; I/zygote ( 6044): at sdk.pendo.io.f9.f sdk.pendo.io.f9.l.a(sdk.pendo.io.Pendo$PendoOptions$Framework, sdk.pendo.io.Pendo$PendoOptions) (SourceFile:-1) I/zygote ( 6044): at void sdk.pendo.io.PendoInternal.a(android.content.Context, java.lang.String, sdk.pendo.io.Pendo$PendoOptions, sdk.pendo.io.PendoPhasesCallbackInterface) (SourceFile:21) I/zygote ( 6044): at void sdk.pendo.io.Pendo.setup(android.content.Context, java.lang.String, sdk.pendo.io.Pendo$PendoOptions, sdk.pendo.io.PendoPhasesCallbackInterface) (SourceFile:-1) I/zygote ( 6044): at void pendo.io.pendo_sdk.PendoFlutterPlugin.setup(io.flutter.plugin.common.MethodCall, io.flutter.plugin.common.MethodChannel$Result) (PendoFlutterPlugin.java:280) I/zygote ( 6044): at void pendo.io.pendo_sdk.PendoFlutterPlugin.onMethodCall(io.flutter.plugin.common.MethodCall, io.flutter.plugin.common.MethodChannel$Result) (PendoFlutterPlugin.java:147) I/zygote ( 6044): at void io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(java.nio.ByteBuffer, io.flutter.plugin.common.BinaryMessenger$BinaryReply) (MethodChannel.java:267) I/zygote ( 6044): at void io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(io.flutter.embedding.engine.dart.DartMessenger$HandlerInfo, java.nio.ByteBuffer, int) (DartMessenger.java:292) I/zygote ( 6044): at void io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(java.lang.String, int, io.flutter.embedding.engine.dart.DartMessenger$HandlerInfo, java.nio.ByteBuffer, long) (DartMessenger.java:319) I/zygote ( 6044): at void io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run() (D8$$SyntheticClass:0) I/zygote ( 6044): at void android.os.Handler.handleCallback(android.os.Message) (Handler.java:790) I/zygote ( 6044): at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:99) I/zygote ( 6044): at void android.os.Looper.loop() (Looper.java:164) I/zygote ( 6044): at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6494) I/zygote ( 6044): at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2) I/zygote ( 6044): at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:438) I/zygote ( 6044): at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:807) I/zygote ( 6044): Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.navigation.NavController$OnDestinationChangedListener" on path: DexPathList[[zip file "/data/app/io.getme.app.dev-o4xmS8729ViNXWuZanFYnA==/base.apk"],nativeLibraryDirectories=[/data/app/io.getme.app.dev-o4xmS8729ViNXWuZanFYnA==/lib/x86, /data/app/io.getme.app.dev-o4xmS8729ViNXWuZanFYnA==/base.apk!/lib/x86, /system/lib, /vendor/lib]] I/zygote ( 6044): at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:125) I/zygote ( 6044): at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379) I/zygote ( 6044): at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312) I/zygote ( 6044): at sdk.pendo.io.f9.f sdk.pendo.io.f9.l.a(sdk.pendo.io.Pendo$PendoOptions$Framework, sdk.pendo.io.Pendo$PendoOptions) (SourceFile:-1) I/zygote ( 6044): at void sdk.pendo.io.PendoInternal.a(android.content.Context, java.lang.String, sdk.pendo.io.Pendo$PendoOptions, sdk.pendo.io.PendoPhasesCallbackInterface) (SourceFile:21) I/zygote ( 6044): at void sdk.pendo.io.Pendo.setup(android.content.Context, java.lang.String, sdk.pendo.io.Pendo$PendoOptions, sdk.pendo.io.PendoPhasesCallbackInterface) (SourceFile:-1) I/zygote ( 6044): at void pendo.io.pendo_sdk.PendoFlutterPlugin.setup(io.flutter.plugin.common.MethodCall, io.flutter.plugin.common.MethodChannel$Result) (PendoFlutterPlugin.java:280) I/zygote ( 6044): at void pendo.io.pendo_sdk.PendoFlutterPlugin.onMethodCall(io.flutter.plugin.common.MethodCall, io.flutter.plugin.common.MethodChannel$Result) (PendoFlutterPlugin.java:147) I/zygote ( 6044): at void io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(java.nio.ByteBuffer, io.flutter.plugin.common.BinaryMessenger$BinaryReply) (MethodChannel.java:267) I/zygote ( 6044): at void io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(io.flutter.embedding.engine.dart.DartMessenger$HandlerInfo, java.nio.ByteBuffer, int) (DartMessenger.java:292) I/zygote ( 6044): at void io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(java.lang.String, int, io.flutter.embedding.engine.dart.DartMessenger$HandlerInfo, java.nio.ByteBuffer, long) (DartMessenger.java:319) I/zygote ( 6044): at void io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run() (D8$$SyntheticClass:0) I/zygote ( 6044): at void android.os.Handler.handleCallback(android.os.Message) (Handler.java:790) I/zygote ( 6044): at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:99) I/zygote ( 6044): at void android.os.Looper.loop() (Looper.java:164) I/zygote ( 6044): at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6494) I/zygote ( 6044): at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2) I/zygote ( 6044): at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:438) I/zygote ( 6044): at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:807) I/zygote ( 6044): ```
DavidPendo commented 1 month ago

@amrgetment, we are checking the issue you have with a proguard... Please tell us as more details as possible:

Thanks.

MikePendo commented 1 month ago

@amrgetment We are working to avoid that dependency on primaryFocus. I really hope we will be bale to do it in 3.4. I saw that issue for one client in the builder of MaterialApp do you use that builder.

amrgetment commented 1 month ago

@MikePendo Yes I need that builder to pass the context from the blocs to the child

AppRepositoriesProvider(
          child: AppBlocProvider(
            child: Builder(
              builder: (BuildContext context) =>

otherwise, I will get an error

Error: Could not find the correct Provider<ThemeBloc> above this App Widget I will wait next release 3.4 or 3.5 if possible Thanks for your efforts

amrgetment commented 1 month ago

@DavidPendo I found that the proguard issue happened for Android API 27 or less but not on API 29, so it is not an issue for me as I have min SDK API 29

amrgetment commented 1 month ago

Any expectation for when 3.4 will be released

MikePendo commented 1 month ago

mid September, that will be the GA We have a design partner program, in that case we can provide you with a beta build much earlier @amrgetment maybe you can share some insight why would you primary focus might be null WidgetsBinding.instance.focusManager.primaryFocus why the following might be null in your app?

amrgetment commented 4 weeks ago

I don't know the reason but it is nullable anyway FocusNode? get primaryFocus => _primaryFocus; https://api.flutter.dev/flutter/widgets/FocusManager/primaryFocus.html Yes, I ok with joining the partner program to test the beta for you