diff --git a/CHANGELOG.md b/CHANGELOG.md index 950d295b0..766f01748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...dev) + +### Added + +- Add support for hybrid apps. ([#561](https://github.com/Instabug/Instabug-Flutter/pull/561)) + + ## [14.3.0](https://github.com/Instabug/Instabug-Flutter/compare/v14.1.0...14.3.0) (April 21, 2025) ### Added diff --git a/android/build.gradle b/android/build.gradle index a6c41ffd0..cae901fce 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -44,7 +44,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:14.3.0' + api 'com.instabug.library:instabug:14.2.1.6702511-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" testImplementation "io.mockk:mockk:1.13.13" diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index bb3b043fa..dc1cdd726 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -1,15 +1,17 @@ package com.instabug.flutter; -import android.annotation.SuppressLint; import android.app.Activity; +import android.app.Application; import android.content.Context; import android.graphics.Bitmap; +import android.os.Bundle; import android.util.Log; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.instabug.flutter.generated.InstabugFlutterPigeon; import com.instabug.flutter.modules.ApmApi; import com.instabug.flutter.modules.BugReportingApi; import com.instabug.flutter.modules.CrashReportingApi; @@ -19,6 +21,7 @@ import com.instabug.flutter.modules.RepliesApi; import com.instabug.flutter.modules.SessionReplayApi; import com.instabug.flutter.modules.SurveysApi; +import com.instabug.flutter.util.ThreadManager; import java.util.concurrent.Callable; @@ -31,9 +34,72 @@ public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { private static final String TAG = InstabugFlutterPlugin.class.getName(); - @SuppressLint("StaticFieldLeak") - private static Activity activity; + private Activity flutterActivity; + public InstabugFlutterPigeon.InstabugFlutterApi instabugFlutterApi; + + private boolean isLastScreenInstabug = false; + Application.ActivityLifecycleCallbacks callbacks = new Application.ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { + + } + + @Override + public void onActivityStarted(@NonNull Activity activity) { + if (isLastScreenInstabug) { + isLastScreenInstabug = false; + return; + } + + isLastScreenInstabug = activity.getComponentName().getClassName().contains("com.instabug."); + if (activity == flutterActivity) { + ThreadManager.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + if (instabugApi != null) { + instabugApi.reportLastScreenChange(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + } + + } + + @Override + public void onActivityResumed(@NonNull Activity activity) { + + } + + + @Override + public void onActivityPaused(@NonNull Activity activity) { + + } + + @Override + public void onActivityStopped(@NonNull Activity activity) { + + + } + + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { + + } + + @Override + public void onActivityDestroyed(@NonNull Activity activity) { + + } + }; + + private InstabugApi instabugApi; @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { @@ -42,30 +108,40 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - activity = null; + flutterActivity = null; } @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); + flutterActivity = binding.getActivity(); + initOnActivityResultListener(); + } + + private void initOnActivityResultListener() { + Application app = flutterActivity.getApplication(); + app.unregisterActivityLifecycleCallbacks(callbacks); + app.registerActivityLifecycleCallbacks(callbacks); } + @Override public void onDetachedFromActivityForConfigChanges() { - activity = null; + flutterActivity = null; + } @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); + flutterActivity = binding.getActivity(); + initOnActivityResultListener(); } @Override public void onDetachedFromActivity() { - activity = null; + flutterActivity = null; } - private static void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { + private void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { final Callable screenshotProvider = new Callable() { @Override public Bitmap call() { @@ -77,17 +153,19 @@ public Bitmap call() { BugReportingApi.init(messenger); CrashReportingApi.init(messenger); FeatureRequestsApi.init(messenger); - InstabugApi.init(messenger, context, screenshotProvider); + instabugApi = InstabugApi.init(messenger, context, screenshotProvider); InstabugLogApi.init(messenger); RepliesApi.init(messenger); SessionReplayApi.init(messenger); SurveysApi.init(messenger); + instabugFlutterApi = new InstabugFlutterPigeon.InstabugFlutterApi(messenger); + } @Nullable - private static Bitmap takeScreenshot(FlutterRenderer renderer) { + private Bitmap takeScreenshot(FlutterRenderer renderer) { try { - final View view = activity.getWindow().getDecorView().getRootView(); + final View view = flutterActivity.getWindow().getDecorView().getRootView(); view.setDrawingCacheEnabled(true); final Bitmap bitmap = renderer.getBitmap(); diff --git a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index edfde055a..887923e29 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -6,9 +6,11 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; + import com.instabug.flutter.generated.InstabugPigeon; import com.instabug.flutter.util.ArgsRegistry; import com.instabug.flutter.util.Reflection; @@ -30,9 +32,11 @@ import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; import com.instabug.library.ui.onboarding.WelcomeMessage; + import io.flutter.FlutterInjector; import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.plugin.common.BinaryMessenger; + import org.jetbrains.annotations.NotNull; import org.json.JSONObject; @@ -55,11 +59,14 @@ public class InstabugApi implements InstabugPigeon.InstabugHostApi { private final InstabugPigeon.FeatureFlagsFlutterApi featureFlagsFlutterApi; - public static void init(BinaryMessenger messenger, Context context, Callable screenshotProvider) { + private String lastScreenChanged = null; + + public static InstabugApi init(BinaryMessenger messenger, Context context, Callable screenshotProvider) { final InstabugPigeon.FeatureFlagsFlutterApi flutterApi = new InstabugPigeon.FeatureFlagsFlutterApi(messenger); final InstabugApi api = new InstabugApi(context, screenshotProvider, flutterApi); InstabugPigeon.InstabugHostApi.setup(messenger, api); + return api; } public InstabugApi(Context context, Callable screenshotProvider, InstabugPigeon.FeatureFlagsFlutterApi featureFlagsFlutterApi) { @@ -122,7 +129,15 @@ public void init(@NonNull String token, @NonNull List invocationEvents, .setSdkDebugLogsLevel(parsedLogLevel) .build(); + initHybridMode(); + + } + + @Override + public void initHybridMode() { + setCurrentPlatform(); Instabug.setScreenshotProvider(screenshotProvider); + } @Override @@ -354,6 +369,7 @@ public void setReproStepsConfig(@Nullable String bugMode, @Nullable String crash @Override public void reportScreenChange(@NonNull String screenName) { try { + lastScreenChanged = screenName; Method method = Reflection.getMethod(Class.forName("com.instabug.library.Instabug"), "reportScreenChange", Bitmap.class, String.class); if (method != null) { @@ -370,6 +386,12 @@ public void reportScreenChange(@NonNull String screenName) { } } + public void reportLastScreenChange(){ + if(lastScreenChanged != null){ + reportScreenChange(lastScreenChanged); + } + } + @VisibleForTesting public Bitmap getBitmapForAsset(String assetName) { try { @@ -500,13 +522,13 @@ public void willRedirectToStore() { Instabug.willRedirectToStore(); } - + @Override public void setNetworkLogBodyEnabled(@NonNull Boolean isEnabled) { - try { - Instabug.setNetworkLogBodyEnabled(isEnabled); - } catch (Exception e) { - e.printStackTrace(); - } + try { +// Instabug.setNetworkLogBodyEnabled(isEnabled); + } catch (Exception e) { + e.printStackTrace(); + } } } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 484d0ae99..fb6db9601 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -25,7 +25,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Instabug: 97a4e694731f46bbc02dbe49ab29cc552c5e2f41 - instabug_flutter: 4e4a9b162d77d5624d08ccdf81745d923745e062 + instabug_flutter: ef9cb6bf3c9dd29b1e4bf3129a16dcbcb2d332e6 OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 PODFILE CHECKSUM: 8f7552fd115ace1988c3db54a69e4a123c448f84 diff --git a/example/lib/main.dart b/example/lib/main.dart index 91b0a67e7..0c4308203 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -56,6 +56,7 @@ void main() { FlutterError.onError = (FlutterErrorDetails details) { Zone.current.handleUncaughtError(details.exception, details.stack!); }; + Instabug.initializeHybridMode() runApp(const MyApp()); }, diff --git a/example/pubspec.lock b/example/pubspec.lock index 93971dc8b..ba4cc0a6d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" fake_async: dependency: transitive description: @@ -120,18 +120,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -200,7 +200,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -213,10 +213,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" sync_http: dependency: transitive description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" typed_data: dependency: transitive description: @@ -277,18 +277,18 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" webdriver: dependency: transitive description: name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/ios/Classes/Modules/InstabugApi.m b/ios/Classes/Modules/InstabugApi.m index 95222a4c8..e42b52bd4 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/ios/Classes/Modules/InstabugApi.m @@ -396,4 +396,9 @@ - (void)setNetworkLogBodyEnabledIsEnabled:(NSNumber *)isEnabled IBGNetworkLogger.logBodyEnabled = [isEnabled boolValue]; } +- (void)initHybridModeWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + +} + + @end diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index 6bba8ed1f..b005760ab 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -482,4 +482,12 @@ class Instabug { static Future willRedirectToStore() async { return _host.willRedirectToStore(); } + + /// Initializes hybrid mode for applications that combine Flutter and native screens. + /// + /// This method should be called when using the Flutter SDK in a hybrid app to enable + /// proper communication and integration between native and Flutter screens. + static Future initializeHybridMode() { + return _host.initHybridMode(); + } } diff --git a/pigeons/instabug.api.dart b/pigeons/instabug.api.dart index 275306987..c2ca17f93 100644 --- a/pigeons/instabug.api.dart +++ b/pigeons/instabug.api.dart @@ -15,6 +15,7 @@ abstract class InstabugHostApi { bool isEnabled(); bool isBuilt(); void init(String token, List invocationEvents, String debugLogsLevel); + void initHybridMode(); void show(); void showWelcomeMessageWithMode(String mode); diff --git a/scripts/pigeon.sh b/scripts/pigeon.sh old mode 100644 new mode 100755