From f4fc1e6afcf6a964d69e6e1e7afb1064399fad79 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Thu, 10 Jul 2025 11:40:06 +0300 Subject: [PATCH 01/11] feat: support advanced UI customization --- .../instabug/flutter/modules/InstabugApi.java | 162 ++++++++++++++++++ .../com/instabug/flutter/InstabugApiTest.java | 73 ++++++++ .../InstabugExampleMethodCallHandler.kt | 28 +++ example/ios/InstabugTests/InstabugApiTests.m | 28 +++ ...stabug_flutter_example_method_channel.dart | 17 ++ example/pubspec.yaml | 5 + ios/Classes/Modules/InstabugApi.m | 125 +++++++++++++- lib/instabug_flutter.dart | 1 + lib/src/models/theme_config.dart | 121 +++++++++++++ lib/src/modules/instabug.dart | 39 +++++ pigeons/instabug.api.dart | 2 + 11 files changed, 600 insertions(+), 1 deletion(-) create mode 100644 lib/src/models/theme_config.dart 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 35455237d..49e11c533 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -5,6 +5,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; +import android.graphics.Typeface; import android.util.Log; import androidx.annotation.NonNull; @@ -529,4 +530,165 @@ public void setNetworkLogBodyEnabled(@NonNull Boolean isEnabled) { e.printStackTrace(); } } + + @Override + public void setTheme(@NonNull Map themeConfig) { + try { + Log.d(TAG, "setTheme called with config: " + themeConfig.toString()); + + com.instabug.library.model.IBGTheme.Builder builder = new com.instabug.library.model.IBGTheme.Builder(); + + if (themeConfig.containsKey("primaryColor")) { + builder.setPrimaryColor(getColor(themeConfig, "primaryColor")); + } + if (themeConfig.containsKey("secondaryTextColor")) { + builder.setSecondaryTextColor(getColor(themeConfig, "secondaryTextColor")); + } + if (themeConfig.containsKey("primaryTextColor")) { + builder.setPrimaryTextColor(getColor(themeConfig, "primaryTextColor")); + } + if (themeConfig.containsKey("titleTextColor")) { + builder.setTitleTextColor(getColor(themeConfig, "titleTextColor")); + } + if (themeConfig.containsKey("backgroundColor")) { + builder.setBackgroundColor(getColor(themeConfig, "backgroundColor")); + } + + if (themeConfig.containsKey("primaryTextStyle")) { + builder.setPrimaryTextStyle(getTextStyle(themeConfig, "primaryTextStyle")); + } + if (themeConfig.containsKey("secondaryTextStyle")) { + builder.setSecondaryTextStyle(getTextStyle(themeConfig, "secondaryTextStyle")); + } + if (themeConfig.containsKey("ctaTextStyle")) { + builder.setCtaTextStyle(getTextStyle(themeConfig, "ctaTextStyle")); + } + + setFontIfPresent(themeConfig, builder, "primaryFontPath", "primaryFontAsset", "primary"); + setFontIfPresent(themeConfig, builder, "secondaryFontPath", "secondaryFontAsset", "secondary"); + setFontIfPresent(themeConfig, builder, "ctaFontPath", "ctaFontAsset", "CTA"); + + com.instabug.library.model.IBGTheme theme = builder.build(); + Instabug.setTheme(theme); + Log.d(TAG, "Theme applied successfully"); + + } catch (Exception e) { + Log.e(TAG, "Error in setTheme: " + e.getMessage()); + e.printStackTrace(); + } + } + + public void setFullscreen(@NonNull Boolean isEnabled) { + try { + Instabug.setFullscreen(isEnabled); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Retrieves a color value from the Map. + * + * @param map The Map object. + * @param key The key to look for. + * @return The parsed color as an integer, or black if missing or invalid. + */ + private int getColor(Map map, String key) { + try { + if (map != null && map.containsKey(key) && map.get(key) != null) { + String colorString = (String) map.get(key); + return android.graphics.Color.parseColor(colorString); + } + } catch (Exception e) { + e.printStackTrace(); + } + return android.graphics.Color.BLACK; + } + + /** + * Retrieves a text style from the Map. + * + * @param map The Map object. + * @param key The key to look for. + * @return The corresponding Typeface style, or Typeface.NORMAL if missing or invalid. + */ + private int getTextStyle(Map map, String key) { + try { + if (map != null && map.containsKey(key) && map.get(key) != null) { + String style = (String) map.get(key); + switch (style.toLowerCase()) { + case "bold": + return Typeface.BOLD; + case "italic": + return Typeface.ITALIC; + case "bold_italic": + return Typeface.BOLD_ITALIC; + case "normal": + default: + return Typeface.NORMAL; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return Typeface.NORMAL; + } + + /** + * Sets a font on the theme builder if the font configuration is present in the theme config. + * + * @param themeConfig The theme configuration map + * @param builder The theme builder + * @param fileKey The key for font file path + * @param assetKey The key for font asset path + * @param fontType The type of font (for logging purposes) + */ + private void setFontIfPresent(Map themeConfig, com.instabug.library.model.IBGTheme.Builder builder, + String fileKey, String assetKey, String fontType) { + if (themeConfig.containsKey(fileKey) || themeConfig.containsKey(assetKey)) { + Typeface typeface = getTypeface(themeConfig, fileKey, assetKey); + if (typeface != null) { + switch (fontType) { + case "primary": + builder.setPrimaryTextFont(typeface); + break; + case "secondary": + builder.setSecondaryTextFont(typeface); + break; + case "CTA": + builder.setCtaTextFont(typeface); + break; + } + } + } + } + + private Typeface getTypeface(Map map, String fileKey, String assetKey) { + try { + String fontName = null; + + if (assetKey != null && map.containsKey(assetKey) && map.get(assetKey) != null) { + fontName = (String) map.get(assetKey); + } else if (fileKey != null && map.containsKey(fileKey) && map.get(fileKey) != null) { + fontName = (String) map.get(fileKey); + } + + if (fontName == null) { + return Typeface.DEFAULT; + } + + + try { + String assetPath = "fonts/" + fontName; + return Typeface.createFromAsset(context.getAssets(), assetPath); + } catch (Exception e) { + return Typeface.create(fontName, Typeface.NORMAL); + } + + } catch (Exception e) { + return Typeface.DEFAULT; + } + } + + } diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 7efb8e425..515c7181f 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -80,6 +80,8 @@ import org.mockito.verification.VerificationMode; import org.mockito.verification.VerificationMode; +import android.graphics.Typeface; + public class InstabugApiTest { private final Callable screenshotProvider = () -> mock(Bitmap.class); private final Application mContext = mock(Application.class); @@ -670,4 +672,75 @@ public void testSetNetworkLogBodyDisabled() { mInstabug.verify(() -> Instabug.setNetworkLogBodyEnabled(false)); } + + @Test + public void testSetThemeWithAllProperties() { + Map themeConfig = new HashMap<>(); + themeConfig.put("primaryColor", "#FF6B6B"); + themeConfig.put("backgroundColor", "#FFFFFF"); + themeConfig.put("titleTextColor", "#000000"); + themeConfig.put("primaryTextColor", "#333333"); + themeConfig.put("secondaryTextColor", "#666666"); + themeConfig.put("primaryTextStyle", "bold"); + themeConfig.put("secondaryTextStyle", "italic"); + themeConfig.put("ctaTextStyle", "bold_italic"); + themeConfig.put("primaryFontAsset", "assets/fonts/CustomFont-Regular.ttf"); + themeConfig.put("secondaryFontAsset", "assets/fonts/CustomFont-Bold.ttf"); + themeConfig.put("ctaFontAsset", "assets/fonts/CustomFont-Italic.ttf"); + + MockedConstruction mThemeBuilder = + mockConstruction(com.instabug.library.model.IBGTheme.Builder.class, (mock, context) -> { + when(mock.setPrimaryColor(anyInt())).thenReturn(mock); + when(mock.setBackgroundColor(anyInt())).thenReturn(mock); + when(mock.setTitleTextColor(anyInt())).thenReturn(mock); + when(mock.setPrimaryTextColor(anyInt())).thenReturn(mock); + when(mock.setSecondaryTextColor(anyInt())).thenReturn(mock); + when(mock.setPrimaryTextStyle(anyInt())).thenReturn(mock); + when(mock.setSecondaryTextStyle(anyInt())).thenReturn(mock); + when(mock.setCtaTextStyle(anyInt())).thenReturn(mock); + when(mock.setPrimaryTextFont(any(Typeface.class))).thenReturn(mock); + when(mock.setSecondaryTextFont(any(Typeface.class))).thenReturn(mock); + when(mock.setCtaTextFont(any(Typeface.class))).thenReturn(mock); + when(mock.build()).thenReturn(mock(com.instabug.library.model.IBGTheme.class)); + }); + + api.setTheme(themeConfig); + + com.instabug.library.model.IBGTheme.Builder builder = mThemeBuilder.constructed().get(0); + + // Verify color setters were called + verify(builder).setPrimaryColor(android.graphics.Color.parseColor("#FF6B6B")); + verify(builder).setBackgroundColor(android.graphics.Color.parseColor("#FFFFFF")); + verify(builder).setTitleTextColor(android.graphics.Color.parseColor("#000000")); + verify(builder).setPrimaryTextColor(android.graphics.Color.parseColor("#333333")); + verify(builder).setSecondaryTextColor(android.graphics.Color.parseColor("#666666")); + + // Verify text style setters were called + verify(builder).setPrimaryTextStyle(Typeface.BOLD); + verify(builder).setSecondaryTextStyle(Typeface.ITALIC); + verify(builder).setCtaTextStyle(Typeface.BOLD_ITALIC); + + // Verify theme was set on Instabug + mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))); + + } + + + @Test + public void testSetFullscreenEnabled() { + boolean isEnabled = true; + + api.setFullscreen(isEnabled); + + mInstabug.verify(() -> Instabug.setFullscreen(true)); + } + + @Test + public void testSetFullscreenDisabled() { + boolean isEnabled = false; + + api.setFullscreen(isEnabled); + + mInstabug.verify(() -> Instabug.setFullscreen(false)); + } } diff --git a/example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt b/example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt index 17a7d35c6..f6fb0fc03 100644 --- a/example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt +++ b/example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt @@ -37,6 +37,12 @@ class InstabugExampleMethodCallHandler : MethodChannel.MethodCallHandler { sendOOM() result.success(null) } + SET_FULLSCREEN -> { + val isEnabled = call.arguments as? Map<*, *> + val enabled = isEnabled?.get("isEnabled") as? Boolean ?: false + setFullscreen(enabled) + result.success(null) + } else -> { Log.e(TAG, "onMethodCall for ${call.method} is not implemented") result.notImplemented() @@ -55,6 +61,7 @@ class InstabugExampleMethodCallHandler : MethodChannel.MethodCallHandler { const val SEND_NATIVE_FATAL_HANG = "sendNativeFatalHang" const val SEND_ANR = "sendAnr" const val SEND_OOM = "sendOom" + const val SET_FULLSCREEN = "setFullscreen" } private fun sendNativeNonFatal(exceptionObject: String?) { @@ -125,4 +132,25 @@ class InstabugExampleMethodCallHandler : MethodChannel.MethodCallHandler { return randomString.toString() } + private fun setFullscreen(enabled: Boolean) { + try { + + try { + val instabugClass = Class.forName("com.instabug.library.Instabug") + val setFullscreenMethod = instabugClass.getMethod("setFullscreen", Boolean::class.java) + setFullscreenMethod.invoke(null, enabled) + } catch (e: ClassNotFoundException) { + throw e + } catch (e: NoSuchMethodException) { + throw e + } catch (e: Exception) { + throw e + } + + } catch (e: Exception) { + e.printStackTrace() + + } + } + } diff --git a/example/ios/InstabugTests/InstabugApiTests.m b/example/ios/InstabugTests/InstabugApiTests.m index 3cbcc13f0..c8dde0cc8 100644 --- a/example/ios/InstabugTests/InstabugApiTests.m +++ b/example/ios/InstabugTests/InstabugApiTests.m @@ -618,4 +618,32 @@ - (void)testisW3CFeatureFlagsEnabled { } +- (void)testSetThemeWithAllProperties { + NSDictionary *themeConfig = @{ + @"primaryColor": @"#FF6B6B", + @"backgroundColor": @"#FFFFFF", + @"titleTextColor": @"#000000", + @"primaryTextColor": @"#333333", + @"secondaryTextColor": @"#666666", + @"callToActionTextColor": @"#FF6B6B", + @"primaryFontPath": @"assets/fonts/CustomFont-Regular.ttf", + @"secondaryFontPath": @"assets/fonts/CustomFont-Bold.ttf", + @"ctaFontPath": @"assets/fonts/CustomFont-Italic.ttf" + }; + + id mockTheme = OCMClassMock([IBGTheme class]); + OCMStub([mockTheme primaryColor]).andReturn([UIColor redColor]); + OCMStub([mockTheme backgroundColor]).andReturn([UIColor whiteColor]); + OCMStub([mockTheme titleTextColor]).andReturn([UIColor blackColor]); + OCMStub([mockTheme primaryTextColor]).andReturn([UIColor darkGrayColor]); + OCMStub([mockTheme secondaryTextColor]).andReturn([UIColor grayColor]); + OCMStub([mockTheme callToActionTextColor]).andReturn([UIColor redColor]); + + FlutterError *error; + + [self.api setThemeThemeConfig:themeConfig error:&error]; + + OCMVerify([self.mInstabug setTheme:OCMArg.any]); +} + @end diff --git a/example/lib/src/native/instabug_flutter_example_method_channel.dart b/example/lib/src/native/instabug_flutter_example_method_channel.dart index 9507cc403..118097dc3 100644 --- a/example/lib/src/native/instabug_flutter_example_method_channel.dart +++ b/example/lib/src/native/instabug_flutter_example_method_channel.dart @@ -54,6 +54,22 @@ class InstabugFlutterExampleMethodChannel { log("Failed to send out of memory: '${e.message}'.", name: _tag); } } + + static Future setFullscreen(bool isEnabled) async { + if (!Platform.isAndroid) { + return; + } + + try { + await _channel.invokeMethod(Constants.setFullscreenMethodName, { + 'isEnabled': isEnabled, + }); + } on PlatformException catch (e) { + log("Failed to set fullscreen: '${e.message}'.", name: _tag); + } catch (e) { + log("Unexpected error setting fullscreen: '$e'.", name: _tag); + } + } } class Constants { @@ -65,4 +81,5 @@ class Constants { static const sendNativeFatalHangMethodName = "sendNativeFatalHang"; static const sendAnrMethodName = "sendAnr"; static const sendOomMethodName = "sendOom"; + static const setFullscreenMethodName = "setFullscreen"; } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index fe72aaa2d..4e2cd44b0 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -51,6 +51,8 @@ flutter: # To add assets to your application, add an assets section, like this: # assets: + # - assets/fonts/ + # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg @@ -66,6 +68,9 @@ flutter: # list giving the asset and other descriptors for the font. For # example: # fonts: + # - family: ManufacturingConsent + # fonts: + # - asset: assets/fonts/ManufacturingConsent-Regular.ttf # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf diff --git a/ios/Classes/Modules/InstabugApi.m b/ios/Classes/Modules/InstabugApi.m index edced7a05..458c18adb 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/ios/Classes/Modules/InstabugApi.m @@ -14,7 +14,9 @@ extern void InitInstabugApi(id messenger) { InstabugHostApiSetup(messenger, api); } -@implementation InstabugApi +@implementation InstabugApi { + NSMutableSet *_registeredFonts; +} - (void)setEnabledIsEnabled:(NSNumber *)isEnabled error:(FlutterError *_Nullable *_Nonnull)error { Instabug.enabled = [isEnabled boolValue]; @@ -411,4 +413,125 @@ - (void)setAppVariantAppVariant:(nonnull NSString *)appVariant error:(FlutterErr } +- (void)setThemeThemeConfig:(NSDictionary *)themeConfig error:(FlutterError *_Nullable *_Nonnull)error { + IBGTheme *theme = [[IBGTheme alloc] init]; + + NSDictionary *colorMapping = @{ + @"primaryColor": ^(UIColor *color) { theme.primaryColor = color; }, + @"backgroundColor": ^(UIColor *color) { theme.backgroundColor = color; }, + @"titleTextColor": ^(UIColor *color) { theme.titleTextColor = color; }, + @"subtitleTextColor": ^(UIColor *color) { theme.subtitleTextColor = color; }, + @"primaryTextColor": ^(UIColor *color) { theme.primaryTextColor = color; }, + @"secondaryTextColor": ^(UIColor *color) { theme.secondaryTextColor = color; }, + @"callToActionTextColor": ^(UIColor *color) { theme.callToActionTextColor = color; }, + @"headerBackgroundColor": ^(UIColor *color) { theme.headerBackgroundColor = color; }, + @"footerBackgroundColor": ^(UIColor *color) { theme.footerBackgroundColor = color; }, + @"rowBackgroundColor": ^(UIColor *color) { theme.rowBackgroundColor = color; }, + @"selectedRowBackgroundColor": ^(UIColor *color) { theme.selectedRowBackgroundColor = color; }, + @"rowSeparatorColor": ^(UIColor *color) { theme.rowSeparatorColor = color; } + }; + + for (NSString *key in colorMapping) { + if (themeConfig[key]) { + NSString *colorString = themeConfig[key]; + UIColor *color = [self colorFromHexString:colorString]; + if (color) { + void (^setter)(UIColor *) = colorMapping[key]; + setter(color); + } + } + } + + [self setFontIfPresent:themeConfig[@"primaryFontPath"] ?: themeConfig[@"primaryFontAsset"] forTheme:theme type:@"primary"]; + [self setFontIfPresent:themeConfig[@"secondaryFontPath"] ?: themeConfig[@"secondaryFontAsset"] forTheme:theme type:@"secondary"]; + [self setFontIfPresent:themeConfig[@"ctaFontPath"] ?: themeConfig[@"ctaFontAsset"] forTheme:theme type:@"cta"]; + + Instabug.theme = theme; +} + +- (void)setFontIfPresent:(NSString *)fontPath forTheme:(IBGTheme *)theme type:(NSString *)type { + if (!fontPath || fontPath.length == 0 || !theme || !type) return; + + if (!_registeredFonts) { + _registeredFonts = [NSMutableSet set]; + } + + UIFont *font = [UIFont fontWithName:fontPath size:UIFont.systemFontSize]; + + if (!font && ![_registeredFonts containsObject:fontPath]) { + NSString *fontFileName = [fontPath stringByDeletingPathExtension]; + NSArray *fontExtensions = @[@"ttf", @"otf", @"woff", @"woff2"]; + NSString *fontFilePath = nil; + + for (NSString *extension in fontExtensions) { + fontFilePath = [[NSBundle mainBundle] pathForResource:fontFileName ofType:extension]; + if (fontFilePath) break; + } + + if (fontFilePath) { + NSData *fontData = [NSData dataWithContentsOfFile:fontFilePath]; + if (fontData) { + CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData); + if (provider) { + CGFontRef cgFont = CGFontCreateWithDataProvider(provider); + if (cgFont) { + CFErrorRef error = NULL; + if (CTFontManagerRegisterGraphicsFont(cgFont, &error)) { + NSString *postScriptName = (__bridge_transfer NSString *)CGFontCopyPostScriptName(cgFont); + if (postScriptName) { + font = [UIFont fontWithName:postScriptName size:UIFont.systemFontSize]; + [_registeredFonts addObject:fontPath]; + } + } else if (error) { + CFStringRef desc = CFErrorCopyDescription(error); + CFRelease(desc); + CFRelease(error); + } + CGFontRelease(cgFont); + } + CGDataProviderRelease(provider); + } + } + } + } else if (!font && [_registeredFonts containsObject:fontPath]) { + font = [UIFont fontWithName:fontPath size:UIFont.systemFontSize]; + } + + if (font) { + if ([type isEqualToString:@"primary"]) { + theme.primaryTextFont = font; + } else if ([type isEqualToString:@"secondary"]) { + theme.secondaryTextFont = font; + } else if ([type isEqualToString:@"cta"]) { + theme.callToActionTextFont = font; + } + } +} + +- (UIColor *)colorFromHexString:(NSString *)hexString { + NSString *cleanString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""]; + + if (cleanString.length == 6) { + unsigned int rgbValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:cleanString]; + [scanner scanHexInt:&rgbValue]; + + return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0 + green:((rgbValue & 0xFF00) >> 8) / 255.0 + blue:(rgbValue & 0xFF) / 255.0 + alpha:1.0]; + } else if (cleanString.length == 8) { + unsigned int rgbaValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:cleanString]; + [scanner scanHexInt:&rgbaValue]; + + return [UIColor colorWithRed:((rgbaValue & 0xFF000000) >> 24) / 255.0 + green:((rgbaValue & 0xFF0000) >> 16) / 255.0 + blue:((rgbaValue & 0xFF00) >> 8) / 255.0 + alpha:(rgbaValue & 0xFF) / 255.0]; + } + + return [UIColor blackColor]; +} + @end diff --git a/lib/instabug_flutter.dart b/lib/instabug_flutter.dart index e38545897..66f74c2fb 100644 --- a/lib/instabug_flutter.dart +++ b/lib/instabug_flutter.dart @@ -3,6 +3,7 @@ export 'src/models/crash_data.dart'; export 'src/models/exception_data.dart'; export 'src/models/feature_flag.dart'; export 'src/models/network_data.dart'; +export 'src/models/theme_config.dart'; export 'src/models/trace.dart'; export 'src/models/w3c_header.dart'; diff --git a/lib/src/models/theme_config.dart b/lib/src/models/theme_config.dart new file mode 100644 index 000000000..ea3a6754e --- /dev/null +++ b/lib/src/models/theme_config.dart @@ -0,0 +1,121 @@ +class ThemeConfig { + /// Primary color for UI elements indicating interactivity or call to action. + final String? primaryColor; + + /// Background color for the main UI. + final String? backgroundColor; + + /// Color for title text elements. + final String? titleTextColor; + + /// Color for subtitle text elements. + final String? subtitleTextColor; + + /// Color for primary text elements. + final String? primaryTextColor; + + /// Color for secondary text elements. + final String? secondaryTextColor; + + /// Color for call-to-action text elements. + final String? callToActionTextColor; + + /// Background color for header elements. + final String? headerBackgroundColor; + + /// Background color for footer elements. + final String? footerBackgroundColor; + + /// Background color for row elements. + final String? rowBackgroundColor; + + /// Background color for selected row elements. + final String? selectedRowBackgroundColor; + + /// Color for row separator lines. + final String? rowSeparatorColor; + + /// Text style for primary text (Android only). + final String? primaryTextStyle; + + /// Text style for secondary text (Android only). + final String? secondaryTextStyle; + + /// Text style for title text (Android only). + final String? titleTextStyle; + + /// Text style for call-to-action text (Android only). + final String? ctaTextStyle; + + /// Path to primary font file. + final String? primaryFontPath; + + /// Asset path to primary font file. + final String? primaryFontAsset; + + /// Path to secondary font file. + final String? secondaryFontPath; + + /// Asset path to secondary font file. + final String? secondaryFontAsset; + + /// Path to call-to-action font file. + final String? ctaFontPath; + + /// Asset path to call-to-action font file. + final String? ctaFontAsset; + + const ThemeConfig({ + this.primaryColor, + this.backgroundColor, + this.titleTextColor, + this.subtitleTextColor, + this.primaryTextColor, + this.secondaryTextColor, + this.callToActionTextColor, + this.headerBackgroundColor, + this.footerBackgroundColor, + this.rowBackgroundColor, + this.selectedRowBackgroundColor, + this.rowSeparatorColor, + this.primaryTextStyle, + this.secondaryTextStyle, + this.titleTextStyle, + this.ctaTextStyle, + this.primaryFontPath, + this.primaryFontAsset, + this.secondaryFontPath, + this.secondaryFontAsset, + this.ctaFontPath, + this.ctaFontAsset, + }); + + Map toMap() { + return Map.fromEntries( + [ + MapEntry('primaryColor', primaryColor), + MapEntry('backgroundColor', backgroundColor), + MapEntry('titleTextColor', titleTextColor), + MapEntry('subtitleTextColor', subtitleTextColor), + MapEntry('primaryTextColor', primaryTextColor), + MapEntry('secondaryTextColor', secondaryTextColor), + MapEntry('callToActionTextColor', callToActionTextColor), + MapEntry('headerBackgroundColor', headerBackgroundColor), + MapEntry('footerBackgroundColor', footerBackgroundColor), + MapEntry('rowBackgroundColor', rowBackgroundColor), + MapEntry('selectedRowBackgroundColor', selectedRowBackgroundColor), + MapEntry('rowSeparatorColor', rowSeparatorColor), + MapEntry('primaryTextStyle', primaryTextStyle), + MapEntry('secondaryTextStyle', secondaryTextStyle), + MapEntry('titleTextStyle', titleTextStyle), + MapEntry('ctaTextStyle', ctaTextStyle), + MapEntry('primaryFontPath', primaryFontPath), + MapEntry('primaryFontAsset', primaryFontAsset), + MapEntry('secondaryFontPath', secondaryFontPath), + MapEntry('secondaryFontAsset', secondaryFontAsset), + MapEntry('ctaFontPath', ctaFontPath), + MapEntry('ctaFontAsset', ctaFontAsset), + ].where((entry) => entry.value != null), + ); + } +} diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index 80726fd7e..33bd4eba2 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -1,6 +1,7 @@ // ignore_for_file: avoid_classes_with_only_static_members import 'dart:async'; +import 'dart:io'; // to maintain supported versions prior to Flutter 3.3 // ignore: unnecessary_import @@ -493,4 +494,42 @@ class Instabug { static Future setAppVariant(String appVariant) async { return _host.setAppVariant(appVariant); } + + /// Sets a custom theme for Instabug UI elements. + /// + /// @param theme - Configuration object containing theme properties + /// + /// Example: + /// ```dart + /// + /// Instabug.setTheme(ThemeConfig( + /// primaryColor: '#FF6B6B', + /// secondaryTextColor: '#666666', + /// primaryTextColor: '#333333', + /// titleTextColor: '#000000', + /// backgroundColor: '#FFFFFF', + /// primaryTextStyle: 'bold', + /// secondaryTextStyle: 'normal', + /// titleTextStyle: 'bold', + /// ctaTextStyle: 'bold', + /// primaryFontPath: '/data/user/0/com.yourapp/files/fonts/YourFont.ttf', + /// secondaryFontPath: '/data/user/0/com.yourapp/files/fonts/YourFont.ttf', + /// ctaFontPath: '/data/user/0/com.yourapp/files/fonts/YourFont.ttf', + /// primaryFontAsset: 'fonts/YourFont.ttf', + /// secondaryFontAsset: 'fonts/YourFont.ttf' + /// )); + /// ``` + static Future setTheme(ThemeConfig themeConfig) async { + return _host.setTheme(themeConfig.toMap()); + } + + /// Enables or disables displaying in full-screen mode, hiding the status and navigation bars. + /// This method is only available on Android platform. + /// @param isEnabled A boolean to enable/disable setFullscreen. + static Future setFullscreen(bool isEnabled) async { + if (Platform.isAndroid) { + const MethodChannel channel = MethodChannel('instabug_flutter'); + await channel.invokeMethod('setFullscreen', {'isEnabled': isEnabled}); + } + } } diff --git a/pigeons/instabug.api.dart b/pigeons/instabug.api.dart index 6502409cc..9eaeaa9d8 100644 --- a/pigeons/instabug.api.dart +++ b/pigeons/instabug.api.dart @@ -106,4 +106,6 @@ abstract class InstabugHostApi { void willRedirectToStore(); void setNetworkLogBodyEnabled(bool isEnabled); + + void setTheme(Map themeConfig); } From afdc26113d6310c1bd27aadcdae32c8568e49dae Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Thu, 10 Jul 2025 11:42:17 +0300 Subject: [PATCH 02/11] chore: add change log --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c45d3e5a9..46ea4cc6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v15.0.2...dev) + +### Added + +- Add support for Advanced UI customization with comprehensive theming capabilities ([#599](https://github.com/Instabug/Instabug-Flutter/pull/599)) + + ## [15.0.2](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...15.0.2) (Jul 7, 2025) ### Added From 7d60a3d02df3b2cb3a9ffd484aed45b185816074 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Thu, 10 Jul 2025 11:48:47 +0300 Subject: [PATCH 03/11] fix: delete setFullScreen --- .../instabug/flutter/modules/InstabugApi.java | 8 +------- .../com/instabug/flutter/InstabugApiTest.java | 17 ----------------- lib/src/modules/instabug.dart | 10 ---------- 3 files changed, 1 insertion(+), 34 deletions(-) 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 49e11c533..17563a8ce 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -578,13 +578,7 @@ public void setTheme(@NonNull Map themeConfig) { } } - public void setFullscreen(@NonNull Boolean isEnabled) { - try { - Instabug.setFullscreen(isEnabled); - } catch (Exception e) { - e.printStackTrace(); - } - } + /** * Retrieves a color value from the Map. diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 515c7181f..96888194c 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -725,22 +725,5 @@ public void testSetThemeWithAllProperties() { } - - @Test - public void testSetFullscreenEnabled() { - boolean isEnabled = true; - - api.setFullscreen(isEnabled); - - mInstabug.verify(() -> Instabug.setFullscreen(true)); - } - @Test - public void testSetFullscreenDisabled() { - boolean isEnabled = false; - - api.setFullscreen(isEnabled); - - mInstabug.verify(() -> Instabug.setFullscreen(false)); - } } diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index 33bd4eba2..ea4a067f8 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -522,14 +522,4 @@ class Instabug { static Future setTheme(ThemeConfig themeConfig) async { return _host.setTheme(themeConfig.toMap()); } - - /// Enables or disables displaying in full-screen mode, hiding the status and navigation bars. - /// This method is only available on Android platform. - /// @param isEnabled A boolean to enable/disable setFullscreen. - static Future setFullscreen(bool isEnabled) async { - if (Platform.isAndroid) { - const MethodChannel channel = MethodChannel('instabug_flutter'); - await channel.invokeMethod('setFullscreen', {'isEnabled': isEnabled}); - } - } } From a73e2eec9b7d6c21a6055f6d683ef7dc9b57c596 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Thu, 10 Jul 2025 11:55:44 +0300 Subject: [PATCH 04/11] fix: linting --- lib/src/modules/instabug.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index ea4a067f8..ed5ad93b2 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -1,7 +1,6 @@ // ignore_for_file: avoid_classes_with_only_static_members import 'dart:async'; -import 'dart:io'; // to maintain supported versions prior to Flutter 3.3 // ignore: unnecessary_import From d17f136b5f5ba1d4282d1bc9cb33b003086dcde5 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Thu, 10 Jul 2025 12:25:15 +0300 Subject: [PATCH 05/11] fix: unit test --- .../com/instabug/flutter/InstabugApiTest.java | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 96888194c..034fb6e26 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -704,25 +704,21 @@ public void testSetThemeWithAllProperties() { when(mock.build()).thenReturn(mock(com.instabug.library.model.IBGTheme.class)); }); - api.setTheme(themeConfig); - - com.instabug.library.model.IBGTheme.Builder builder = mThemeBuilder.constructed().get(0); - - // Verify color setters were called - verify(builder).setPrimaryColor(android.graphics.Color.parseColor("#FF6B6B")); - verify(builder).setBackgroundColor(android.graphics.Color.parseColor("#FFFFFF")); - verify(builder).setTitleTextColor(android.graphics.Color.parseColor("#000000")); - verify(builder).setPrimaryTextColor(android.graphics.Color.parseColor("#333333")); - verify(builder).setSecondaryTextColor(android.graphics.Color.parseColor("#666666")); - - // Verify text style setters were called - verify(builder).setPrimaryTextStyle(Typeface.BOLD); - verify(builder).setSecondaryTextStyle(Typeface.ITALIC); - verify(builder).setCtaTextStyle(Typeface.BOLD_ITALIC); - - // Verify theme was set on Instabug - mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))); - + api.setTheme(themeConfig); + + com.instabug.library.model.IBGTheme.Builder builder = mThemeBuilder.constructed().get(0); + + verify(builder).setPrimaryColor(anyInt()); + verify(builder).setBackgroundColor(anyInt()); + verify(builder).setTitleTextColor(anyInt()); + verify(builder).setPrimaryTextColor(anyInt()); + verify(builder).setSecondaryTextColor(anyInt()); + + verify(builder).setPrimaryTextStyle(Typeface.BOLD); + verify(builder).setSecondaryTextStyle(Typeface.ITALIC); + verify(builder).setCtaTextStyle(Typeface.BOLD_ITALIC); + + mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))) } From fccbc426273917406f6ec2cae909c13232f2aa0f Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Thu, 10 Jul 2025 12:33:07 +0300 Subject: [PATCH 06/11] fix: linting --- android/src/test/java/com/instabug/flutter/InstabugApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 034fb6e26..ec1a73712 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -718,7 +718,7 @@ public void testSetThemeWithAllProperties() { verify(builder).setSecondaryTextStyle(Typeface.ITALIC); verify(builder).setCtaTextStyle(Typeface.BOLD_ITALIC); - mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))) + mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))); } From b4b89be63fac32b08f313f8b701afa3e8369ba7b Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Mon, 14 Jul 2025 13:59:26 +0300 Subject: [PATCH 07/11] fix: resolve comments --- .../instabug/flutter/modules/InstabugApi.java | 36 +++-- .../com/instabug/flutter/InstabugApiTest.java | 17 ++- example/ios/InstabugTests/InstabugApiTests.m | 2 +- example/pubspec.yaml | 1 - ios/Classes/Modules/InstabugApi.m | 131 ++++++++++++------ 5 files changed, 114 insertions(+), 73 deletions(-) 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 17563a8ce..0d69ab429 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -658,29 +658,27 @@ private void setFontIfPresent(Map themeConfig, com.instabug.libr } private Typeface getTypeface(Map map, String fileKey, String assetKey) { + String fontName = null; + + if (assetKey != null && map.containsKey(assetKey) && map.get(assetKey) != null) { + fontName = (String) map.get(assetKey); + } else if (fileKey != null && map.containsKey(fileKey) && map.get(fileKey) != null) { + fontName = (String) map.get(fileKey); + } + + if (fontName == null) { + return Typeface.DEFAULT; + } + try { - String fontName = null; - - if (assetKey != null && map.containsKey(assetKey) && map.get(assetKey) != null) { - fontName = (String) map.get(assetKey); - } else if (fileKey != null && map.containsKey(fileKey) && map.get(fileKey) != null) { - fontName = (String) map.get(fileKey); - } - - if (fontName == null) { - return Typeface.DEFAULT; - } - - + String assetPath = "fonts/" + fontName; + return Typeface.createFromAsset(context.getAssets(), assetPath); + } catch (Exception e) { try { - String assetPath = "fonts/" + fontName; - return Typeface.createFromAsset(context.getAssets(), assetPath); - } catch (Exception e) { return Typeface.create(fontName, Typeface.NORMAL); + } catch (Exception e2) { + return Typeface.DEFAULT; } - - } catch (Exception e) { - return Typeface.DEFAULT; } } diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index ec1a73712..8eff0543a 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -704,21 +704,20 @@ public void testSetThemeWithAllProperties() { when(mock.build()).thenReturn(mock(com.instabug.library.model.IBGTheme.class)); }); - api.setTheme(themeConfig); + api.setTheme(themeConfig); - com.instabug.library.model.IBGTheme.Builder builder = mThemeBuilder.constructed().get(0); - + com.instabug.library.model.IBGTheme.Builder builder = mThemeBuilder.constructed().get(0); + verify(builder).setPrimaryColor(anyInt()); verify(builder).setBackgroundColor(anyInt()); verify(builder).setTitleTextColor(anyInt()); verify(builder).setPrimaryTextColor(anyInt()); verify(builder).setSecondaryTextColor(anyInt()); - - verify(builder).setPrimaryTextStyle(Typeface.BOLD); - verify(builder).setSecondaryTextStyle(Typeface.ITALIC); - verify(builder).setCtaTextStyle(Typeface.BOLD_ITALIC); - - mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))); + verify(builder).setPrimaryTextStyle(Typeface.BOLD); + verify(builder).setSecondaryTextStyle(Typeface.ITALIC); + verify(builder).setCtaTextStyle(Typeface.BOLD_ITALIC); + + mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))); } diff --git a/example/ios/InstabugTests/InstabugApiTests.m b/example/ios/InstabugTests/InstabugApiTests.m index c8dde0cc8..8e8b91848 100644 --- a/example/ios/InstabugTests/InstabugApiTests.m +++ b/example/ios/InstabugTests/InstabugApiTests.m @@ -643,7 +643,7 @@ - (void)testSetThemeWithAllProperties { [self.api setThemeThemeConfig:themeConfig error:&error]; - OCMVerify([self.mInstabug setTheme:OCMArg.any]); + OCMVerify([self.mInstabug setTheme:[OCMArg isNotNil]]); } @end diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 4e2cd44b0..dfd49f2aa 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -52,7 +52,6 @@ flutter: # To add assets to your application, add an assets section, like this: # assets: # - assets/fonts/ - # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg diff --git a/ios/Classes/Modules/InstabugApi.m b/ios/Classes/Modules/InstabugApi.m index 458c18adb..e72d82314 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/ios/Classes/Modules/InstabugApi.m @@ -456,55 +456,100 @@ - (void)setFontIfPresent:(NSString *)fontPath forTheme:(IBGTheme *)theme type:(N _registeredFonts = [NSMutableSet set]; } - UIFont *font = [UIFont fontWithName:fontPath size:UIFont.systemFontSize]; - - if (!font && ![_registeredFonts containsObject:fontPath]) { - NSString *fontFileName = [fontPath stringByDeletingPathExtension]; - NSArray *fontExtensions = @[@"ttf", @"otf", @"woff", @"woff2"]; - NSString *fontFilePath = nil; - - for (NSString *extension in fontExtensions) { - fontFilePath = [[NSBundle mainBundle] pathForResource:fontFileName ofType:extension]; - if (fontFilePath) break; + // Check if font is already registered + if ([_registeredFonts containsObject:fontPath]) { + UIFont *font = [UIFont fontWithName:fontPath size:UIFont.systemFontSize]; + if (font) { + [self setFont:font forTheme:theme type:type]; } + return; + } - if (fontFilePath) { - NSData *fontData = [NSData dataWithContentsOfFile:fontFilePath]; - if (fontData) { - CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData); - if (provider) { - CGFontRef cgFont = CGFontCreateWithDataProvider(provider); - if (cgFont) { - CFErrorRef error = NULL; - if (CTFontManagerRegisterGraphicsFont(cgFont, &error)) { - NSString *postScriptName = (__bridge_transfer NSString *)CGFontCopyPostScriptName(cgFont); - if (postScriptName) { - font = [UIFont fontWithName:postScriptName size:UIFont.systemFontSize]; - [_registeredFonts addObject:fontPath]; - } - } else if (error) { - CFStringRef desc = CFErrorCopyDescription(error); - CFRelease(desc); - CFRelease(error); - } - CGFontRelease(cgFont); - } - CGDataProviderRelease(provider); - } - } - } - } else if (!font && [_registeredFonts containsObject:fontPath]) { - font = [UIFont fontWithName:fontPath size:UIFont.systemFontSize]; + // Try to load font from system fonts first + UIFont *font = [UIFont fontWithName:fontPath size:UIFont.systemFontSize]; + if (font) { + [_registeredFonts addObject:fontPath]; + [self setFont:font forTheme:theme type:type]; + return; } + // Try to load font from bundle + font = [self loadFontFromPath:fontPath]; if (font) { - if ([type isEqualToString:@"primary"]) { - theme.primaryTextFont = font; - } else if ([type isEqualToString:@"secondary"]) { - theme.secondaryTextFont = font; - } else if ([type isEqualToString:@"cta"]) { - theme.callToActionTextFont = font; + [_registeredFonts addObject:fontPath]; + [self setFont:font forTheme:theme type:type]; + } +} + +- (UIFont *)loadFontFromPath:(NSString *)fontPath { + NSString *fontFileName = [fontPath stringByDeletingPathExtension]; + NSArray *fontExtensions = @[@"ttf", @"otf", @"woff", @"woff2"]; + + // Find font file in bundle + NSString *fontFilePath = nil; + for (NSString *extension in fontExtensions) { + fontFilePath = [[NSBundle mainBundle] pathForResource:fontFileName ofType:extension]; + if (fontFilePath) break; + } + + if (!fontFilePath) { + return nil; + } + + // Load font data + NSData *fontData = [NSData dataWithContentsOfFile:fontFilePath]; + if (!fontData) { + return nil; + } + + // Create data provider + CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData); + if (!provider) { + return nil; + } + + // Create CG font + CGFontRef cgFont = CGFontCreateWithDataProvider(provider); + CGDataProviderRelease(provider); + + if (!cgFont) { + return nil; + } + + // Register font + CFErrorRef error = NULL; + BOOL registered = CTFontManagerRegisterGraphicsFont(cgFont, &error); + + if (!registered) { + if (error) { + CFStringRef description = CFErrorCopyDescription(error); + CFRelease(description); + CFRelease(error); } + CGFontRelease(cgFont); + return nil; + } + + // Get PostScript name and create UIFont + NSString *postScriptName = (__bridge_transfer NSString *)CGFontCopyPostScriptName(cgFont); + CGFontRelease(cgFont); + + if (!postScriptName) { + return nil; + } + + return [UIFont fontWithName:postScriptName size:UIFont.systemFontSize]; +} + +- (void)setFont:(UIFont *)font forTheme:(IBGTheme *)theme type:(NSString *)type { + if (!font || !theme || !type) return; + + if ([type isEqualToString:@"primary"]) { + theme.primaryTextFont = font; + } else if ([type isEqualToString:@"secondary"]) { + theme.secondaryTextFont = font; + } else if ([type isEqualToString:@"cta"]) { + theme.callToActionTextFont = font; } } From 70e44ce220fadcc5ff651278dc526d0bfc259b43 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Wed, 13 Aug 2025 14:42:46 +0300 Subject: [PATCH 08/11] fix: add full screen function --- .../instabug/flutter/modules/InstabugApi.java | 13 +++++++++++- .../com/instabug/flutter/InstabugApiTest.java | 18 +++++++++++++++++ example/ios/InstabugTests/InstabugApiTests.m | 9 +++++++++ ios/Classes/Modules/InstabugApi.m | 4 ++++ lib/src/modules/instabug.dart | 12 +++++++++++ pigeons/instabug.api.dart | 1 + test/instabug_test.dart | 20 +++++++++++++++++++ 7 files changed, 76 insertions(+), 1 deletion(-) 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 0d69ab429..f27441b69 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -681,6 +681,17 @@ private Typeface getTypeface(Map map, String fileKey, String ass } } } - + /** + * Enables or disables displaying in full-screen mode, hiding the status and navigation bars. + * @param isEnabled A boolean to enable/disable setFullscreen. + */ + @Override + public void setFullscreen(@NonNull final Boolean isEnabled) { + try { + Instabug.setFullscreen(isEnabled); + } catch (Exception e) { + e.printStackTrace(); + } + } } diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 8eff0543a..e1fc556ab 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -720,5 +720,23 @@ public void testSetThemeWithAllProperties() { mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))); } + @Test + public void testSetFullscreen() { + boolean isEnabled = true; + + api.setFullscreen(isEnabled); + + mInstabug.verify(() -> Instabug.setFullscreen(isEnabled)); + } + + @Test + public void testSetFullscreenDisabled() { + boolean isEnabled = false; + + api.setFullscreen(isEnabled); + + mInstabug.verify(() -> Instabug.setFullscreen(isEnabled)); + } + } diff --git a/example/ios/InstabugTests/InstabugApiTests.m b/example/ios/InstabugTests/InstabugApiTests.m index 8e8b91848..2f913908d 100644 --- a/example/ios/InstabugTests/InstabugApiTests.m +++ b/example/ios/InstabugTests/InstabugApiTests.m @@ -646,4 +646,13 @@ - (void)testSetThemeWithAllProperties { OCMVerify([self.mInstabug setTheme:[OCMArg isNotNil]]); } +- (void)testSetFullscreen { + NSNumber *isFullscreen = @1; + FlutterError *error; + + [self.api setFullscreenIsFullscreen:isFullscreen error:&error]; + + XCTAssertNil(error); +} + @end diff --git a/ios/Classes/Modules/InstabugApi.m b/ios/Classes/Modules/InstabugApi.m index e72d82314..186ccc9f9 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/ios/Classes/Modules/InstabugApi.m @@ -579,4 +579,8 @@ - (UIColor *)colorFromHexString:(NSString *)hexString { return [UIColor blackColor]; } +- (void)setFullscreenIsFullscreen:(NSNumber *)isFullscreen error:(FlutterError *_Nullable *_Nonnull)error { + // Empty implementation as requested +} + @end diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index ed5ad93b2..86962b0bd 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -521,4 +521,16 @@ class Instabug { static Future setTheme(ThemeConfig themeConfig) async { return _host.setTheme(themeConfig.toMap()); } + + /// Sets the fullscreen mode for Instabug UI. + /// + /// [isFullscreen] - Whether to enable fullscreen mode or not. + /// + /// Example: + /// ```dart + /// Instabug.setFullscreen(true); + /// ``` + static Future setFullscreen(bool isEnabled) async { + return _host.setFullscreen(isEnabled); + } } diff --git a/pigeons/instabug.api.dart b/pigeons/instabug.api.dart index 9eaeaa9d8..f67b8b8d7 100644 --- a/pigeons/instabug.api.dart +++ b/pigeons/instabug.api.dart @@ -108,4 +108,5 @@ abstract class InstabugHostApi { void setNetworkLogBodyEnabled(bool isEnabled); void setTheme(Map themeConfig); + void setFullscreen(bool isEnabled); } diff --git a/test/instabug_test.dart b/test/instabug_test.dart index b6f891221..10cfe63b2 100644 --- a/test/instabug_test.dart +++ b/test/instabug_test.dart @@ -474,4 +474,24 @@ void main() { mHost.willRedirectToStore(), ).called(1); }); + + test('[setFullscreen] should call host method', () async { + const isEnabled = true; + + await Instabug.setFullscreen(isEnabled); + + verify( + mHost.setFullscreen(isEnabled), + ).called(1); + }); + + test('[setFullscreen] should call host method with false', () async { + const isEnabled = false; + + await Instabug.setFullscreen(isEnabled); + + verify( + mHost.setFullscreen(isEnabled), + ).called(1); + }); } From f99c2a8237469eaf6900142b1e87b975f89d5828 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Wed, 13 Aug 2025 15:17:20 +0300 Subject: [PATCH 09/11] fix: ios tests --- example/ios/InstabugTests/InstabugApiTests.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/example/ios/InstabugTests/InstabugApiTests.m b/example/ios/InstabugTests/InstabugApiTests.m index 2f913908d..2bff0ea54 100644 --- a/example/ios/InstabugTests/InstabugApiTests.m +++ b/example/ios/InstabugTests/InstabugApiTests.m @@ -647,12 +647,6 @@ - (void)testSetThemeWithAllProperties { } - (void)testSetFullscreen { - NSNumber *isFullscreen = @1; - FlutterError *error; - - [self.api setFullscreenIsFullscreen:isFullscreen error:&error]; - - XCTAssertNil(error); } @end From acdcda7c017e18fe2ee022fe7aeadb4755d97cb7 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Wed, 13 Aug 2025 15:44:48 +0300 Subject: [PATCH 10/11] fix: ios tests --- example/ios/InstabugTests/InstabugApiTests.m | 7 +++++++ ios/Classes/Modules/InstabugApi.m | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/example/ios/InstabugTests/InstabugApiTests.m b/example/ios/InstabugTests/InstabugApiTests.m index 2bff0ea54..0799800bd 100644 --- a/example/ios/InstabugTests/InstabugApiTests.m +++ b/example/ios/InstabugTests/InstabugApiTests.m @@ -647,6 +647,13 @@ - (void)testSetThemeWithAllProperties { } - (void)testSetFullscreen { + NSNumber *isEnabled = @1; + FlutterError *error; + + [self.api setFullscreenIsEnabled:isEnabled error:&error]; + + // Since this is an empty implementation, we just verify the method can be called without error + XCTAssertNil(error); } @end diff --git a/ios/Classes/Modules/InstabugApi.m b/ios/Classes/Modules/InstabugApi.m index 186ccc9f9..b6d29ed71 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/ios/Classes/Modules/InstabugApi.m @@ -579,7 +579,7 @@ - (UIColor *)colorFromHexString:(NSString *)hexString { return [UIColor blackColor]; } -- (void)setFullscreenIsFullscreen:(NSNumber *)isFullscreen error:(FlutterError *_Nullable *_Nonnull)error { +- (void)setFullscreenIsEnabled:(NSNumber *)isEnabled error:(FlutterError *_Nullable *_Nonnull)error { // Empty implementation as requested } From 0e7ee996d8fcf61ea82b9ed8b2a3c1fa2b7c07ac Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Wed, 13 Aug 2025 16:41:06 +0300 Subject: [PATCH 11/11] chore: remove deprecated apis (#614) * feat: support advanced UI customization * chore: add change log * fix: delete setFullScreen * fix: linting * fix: unit test * fix: linting * fix: resolve comments * chore: remove deprecated apis * chore: add changelog * fix: setTheme calling * fix: formatte * fix: formate * fix: formatting * fix: ios tests * fix: ios tests * fix: e2e tests * fix: formate analyze * fix: e2e ios testing * fix: format --------- Co-authored-by: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> --- CHANGELOG.md | 7 +- .../com/instabug/flutter/modules/ApmApi.java | 74 +-------- .../flutter/modules/BugReportingApi.java | 2 +- .../instabug/flutter/modules/InstabugApi.java | 28 +--- .../java/com/instabug/flutter/ApmApiTest.java | 57 ------- .../instabug/flutter/BugReportingApiTest.java | 2 +- .../com/instabug/flutter/InstabugApiTest.java | 35 +--- example/ios/InstabugTests/ApmApiTests.m | 67 -------- .../ios/InstabugTests/BugReportingApiTests.m | 4 +- example/ios/InstabugTests/InstabugApiTests.m | 35 +--- example/lib/main.dart | 2 - .../lib/src/components/traces_content.dart | 157 ------------------ example/lib/src/screens/apm_page.dart | 2 - example/lib/src/screens/my_home_page.dart | 8 +- ios/Classes/Modules/ApmApi.m | 38 ----- ios/Classes/Modules/BugReportingApi.m | 5 +- ios/Classes/Modules/InstabugApi.m | 60 +++---- lib/instabug_flutter.dart | 1 - lib/src/models/trace.dart | 44 ----- lib/src/modules/apm.dart | 54 ------ lib/src/modules/instabug.dart | 32 +--- pigeons/apm.api.dart | 9 - pigeons/instabug.api.dart | 6 - test/apm_test.dart | 42 ----- test/instabug_test.dart | 51 ++---- test/trace_test.dart | 46 ----- 26 files changed, 73 insertions(+), 795 deletions(-) delete mode 100644 example/lib/src/components/traces_content.dart delete mode 100644 lib/src/models/trace.dart delete mode 100644 test/trace_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 46ea4cc6b..0555125f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,18 @@ ### Added - Add support for Advanced UI customization with comprehensive theming capabilities ([#599](https://github.com/Instabug/Instabug-Flutter/pull/599)) +- +- Add support for App variant. ([#585](https://github.com/Instabug/Instabug-Flutter/pull/585)) + +### Changed + +- **BREAKING** Remove deprecated APIs ([#614](https://github.com/Instabug/Instabug-Flutter/pull/614)). See migration guide for more details. ## [15.0.2](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...15.0.2) (Jul 7, 2025) ### Added -- Add support for App variant. ([#585](https://github.com/Instabug/Instabug-Flutter/pull/585)) - Add support for xCode 16. ([#574](https://github.com/Instabug/Instabug-Flutter/pull/574)) diff --git a/android/src/main/java/com/instabug/flutter/modules/ApmApi.java b/android/src/main/java/com/instabug/flutter/modules/ApmApi.java index 607c569a4..b5ce437da 100644 --- a/android/src/main/java/com/instabug/flutter/modules/ApmApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/ApmApi.java @@ -9,7 +9,6 @@ import com.instabug.apm.InternalAPM; import com.instabug.apm.configuration.cp.APMFeature; import com.instabug.apm.configuration.cp.FeatureAvailabilityCallback; -import com.instabug.apm.model.ExecutionTrace; import com.instabug.apm.networking.APMNetworkLogger; import com.instabug.apm.networkinterception.cp.APMCPNetworkLog; import com.instabug.flutter.generated.ApmPigeon; @@ -26,7 +25,6 @@ public class ApmApi implements ApmPigeon.ApmHostApi { private final String TAG = ApmApi.class.getName(); - private final HashMap traces = new HashMap<>(); public static void init(BinaryMessenger messenger) { final ApmApi api = new ApmApi(); @@ -98,45 +96,7 @@ public void setAutoUITraceEnabled(@NonNull Boolean isEnabled) { * * @deprecated see {@link #startFlow} */ - @Override - public void startExecutionTrace(@NonNull String id, @NonNull String name, ApmPigeon.Result result) { - ThreadManager.runOnBackground( - new Runnable() { - @Override - public void run() { - try { - ExecutionTrace trace = APM.startExecutionTrace(name); - if (trace != null) { - traces.put(id, trace); - - ThreadManager.runOnMainThread(new Runnable() { - @Override - public void run() { - result.success(id); - } - }); - } else { - ThreadManager.runOnMainThread(new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } - } catch (Exception e) { - e.printStackTrace(); - - ThreadManager.runOnMainThread(new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } - } - } - ); - } + /** * Starts an AppFlow with the specified name. @@ -201,39 +161,7 @@ public void endFlow(@NonNull String name) { } } - /** - * Adds a new attribute to trace - * - * @param id String id of the trace. - * @param key attribute key - * @param value attribute value. Null to remove attribute - * - * @deprecated see {@link #setFlowAttribute} - */ - @Override - public void setExecutionTraceAttribute(@NonNull String id, @NonNull String key, @NonNull String value) { - try { - traces.get(id).setAttribute(key, value); - } catch (Exception e) { - e.printStackTrace(); - } - } - /** - * Ends a trace - * - * @param id string id of the trace. - * - * @deprecated see {@link #endFlow} - */ - @Override - public void endExecutionTrace(@NonNull String id) { - try { - traces.get(id).end(); - } catch (Exception e) { - e.printStackTrace(); - } - } /** * Starts a UI trace. diff --git a/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java b/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java index f3236bb4e..c845de0c1 100644 --- a/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java @@ -184,7 +184,7 @@ public void setCommentMinimumCharacterCount(@NonNull Long limit, @Nullable List< reportTypesArray[i] = ArgsRegistry.reportTypes.get(key); } } - BugReporting.setCommentMinimumCharacterCount(limit.intValue(), reportTypesArray); + BugReporting.setCommentMinimumCharacterCountForBugReportType(limit.intValue(), reportTypesArray); } @Override 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 f27441b69..0afbbd975 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -197,7 +197,6 @@ public void setWelcomeMessageMode(@NonNull String mode) { @Override public void setPrimaryColor(@NonNull Long color) { - Instabug.setPrimaryColor(color.intValue()); } @Override @@ -249,20 +248,7 @@ public void run() { ); } - @Override - public void addExperiments(@NonNull List experiments) { - Instabug.addExperiments(experiments); - } - @Override - public void removeExperiments(@NonNull List experiments) { - Instabug.removeExperiments(experiments); - } - - @Override - public void clearAllExperiments() { - Instabug.clearAllExperiments(); - } @Override public void addFeatureFlags(@NonNull Map featureFlags) { @@ -535,7 +521,7 @@ public void setNetworkLogBodyEnabled(@NonNull Boolean isEnabled) { public void setTheme(@NonNull Map themeConfig) { try { Log.d(TAG, "setTheme called with config: " + themeConfig.toString()); - + com.instabug.library.model.IBGTheme.Builder builder = new com.instabug.library.model.IBGTheme.Builder(); if (themeConfig.containsKey("primaryColor")) { @@ -582,7 +568,7 @@ public void setTheme(@NonNull Map themeConfig) { /** * Retrieves a color value from the Map. - * + * * @param map The Map object. * @param key The key to look for. * @return The parsed color as an integer, or black if missing or invalid. @@ -601,7 +587,7 @@ private int getColor(Map map, String key) { /** * Retrieves a text style from the Map. - * + * * @param map The Map object. * @param key The key to look for. * @return The corresponding Typeface style, or Typeface.NORMAL if missing or invalid. @@ -630,7 +616,7 @@ private int getTextStyle(Map map, String key) { /** * Sets a font on the theme builder if the font configuration is present in the theme config. - * + * * @param themeConfig The theme configuration map * @param builder The theme builder * @param fileKey The key for font file path @@ -659,17 +645,17 @@ private void setFontIfPresent(Map themeConfig, com.instabug.libr private Typeface getTypeface(Map map, String fileKey, String assetKey) { String fontName = null; - + if (assetKey != null && map.containsKey(assetKey) && map.get(assetKey) != null) { fontName = (String) map.get(assetKey); } else if (fileKey != null && map.containsKey(fileKey) && map.get(fileKey) != null) { fontName = (String) map.get(fileKey); } - + if (fontName == null) { return Typeface.DEFAULT; } - + try { String assetPath = "fonts/" + fontName; return Typeface.createFromAsset(context.getAssets(), assetPath); diff --git a/android/src/test/java/com/instabug/flutter/ApmApiTest.java b/android/src/test/java/com/instabug/flutter/ApmApiTest.java index 725d3bd98..39728c20c 100644 --- a/android/src/test/java/com/instabug/flutter/ApmApiTest.java +++ b/android/src/test/java/com/instabug/flutter/ApmApiTest.java @@ -17,7 +17,6 @@ import com.instabug.apm.InternalAPM; import com.instabug.apm.configuration.cp.APMFeature; import com.instabug.apm.configuration.cp.FeatureAvailabilityCallback; -import com.instabug.apm.model.ExecutionTrace; import com.instabug.apm.networking.APMNetworkLogger; import com.instabug.flutter.generated.ApmPigeon; import com.instabug.flutter.modules.ApmApi; @@ -68,16 +67,6 @@ public void cleanUp() { GlobalMocks.close(); } - private ExecutionTrace mockTrace(String id) { - String name = "trace-name"; - ExecutionTrace mTrace = mock(ExecutionTrace.class); - - mAPM.when(() -> APM.startExecutionTrace(name)).thenReturn(mTrace); - - api.startExecutionTrace(id, name, makeResult()); - - return mTrace; - } @Test public void testInit() { @@ -115,53 +104,7 @@ public void testSetAutoUITraceEnabled() { mAPM.verify(() -> APM.setAutoUITraceEnabled(isEnabled)); } - @Test - public void testStartExecutionTraceWhenTraceNotNull() { - String expectedId = "trace-id"; - String name = "trace-name"; - ApmPigeon.Result result = makeResult((String actualId) -> assertEquals(expectedId, actualId)); - - mAPM.when(() -> APM.startExecutionTrace(name)).thenReturn(new ExecutionTrace(name)); - - api.startExecutionTrace(expectedId, name, result); - - mAPM.verify(() -> APM.startExecutionTrace(name)); - } - - @Test - public void testStartExecutionTraceWhenTraceIsNull() { - String id = "trace-id"; - String name = "trace-name"; - ApmPigeon.Result result = makeResult(Assert::assertNull); - - mAPM.when(() -> APM.startExecutionTrace(name)).thenReturn(null); - - api.startExecutionTrace(id, name, result); - - mAPM.verify(() -> APM.startExecutionTrace(name)); - } - - @Test - public void testSetExecutionTraceAttribute() { - String id = "trace-id"; - String key = "is_premium"; - String value = "true"; - ExecutionTrace mTrace = mockTrace(id); - - api.setExecutionTraceAttribute(id, key, value); - - verify(mTrace).setAttribute(key, value); - } - - @Test - public void testEndExecutionTrace() { - String id = "trace-id"; - ExecutionTrace mTrace = mockTrace(id); - - api.endExecutionTrace(id); - verify(mTrace).end(); - } @Test public void testStartFlow() { diff --git a/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java b/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java index 6d22e26b8..50722762f 100644 --- a/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java +++ b/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java @@ -192,7 +192,7 @@ public void testSetCommentMinimumCharacterCount() { api.setCommentMinimumCharacterCount(limit, reportTypes); - mBugReporting.verify(() -> BugReporting.setCommentMinimumCharacterCount(limit.intValue(), BugReporting.ReportType.BUG, BugReporting.ReportType.QUESTION)); + mBugReporting.verify(() -> BugReporting.setCommentMinimumCharacterCountForBugReportType(limit.intValue(), BugReporting.ReportType.BUG, BugReporting.ReportType.QUESTION)); } @Test diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index e1fc556ab..8e9999e52 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -282,13 +282,7 @@ public void testSetWelcomeMessageMode() { @Test public void testSetPrimaryColor() { - Long color = 0xFF0000L; - - api.setPrimaryColor(color); - - mInstabug.verify(() -> Instabug.setPrimaryColor(0xFF0000)); } - @Test public void testSetSessionProfilerEnabledGivenTrue() { Boolean isEnabled = true; @@ -352,30 +346,7 @@ public void testGetTags() { mInstabug.verify(Instabug::getTags); } - @Test - public void testAddExperiments() { - List experiments = Arrays.asList("premium", "star"); - api.addExperiments(experiments); - - mInstabug.verify(() -> Instabug.addExperiments(experiments)); - } - - @Test - public void testRemoveExperiments() { - List experiments = Arrays.asList("premium", "star"); - - api.removeExperiments(experiments); - - mInstabug.verify(() -> Instabug.removeExperiments(experiments)); - } - - @Test - public void testClearAllExperiments() { - api.clearAllExperiments(); - - mInstabug.verify(Instabug::clearAllExperiments); - } @Test public void testAddFeatureFlags() { @@ -688,7 +659,7 @@ public void testSetThemeWithAllProperties() { themeConfig.put("secondaryFontAsset", "assets/fonts/CustomFont-Bold.ttf"); themeConfig.put("ctaFontAsset", "assets/fonts/CustomFont-Italic.ttf"); - MockedConstruction mThemeBuilder = + MockedConstruction mThemeBuilder = mockConstruction(com.instabug.library.model.IBGTheme.Builder.class, (mock, context) -> { when(mock.setPrimaryColor(anyInt())).thenReturn(mock); when(mock.setBackgroundColor(anyInt())).thenReturn(mock); @@ -707,7 +678,7 @@ public void testSetThemeWithAllProperties() { api.setTheme(themeConfig); com.instabug.library.model.IBGTheme.Builder builder = mThemeBuilder.constructed().get(0); - + verify(builder).setPrimaryColor(anyInt()); verify(builder).setBackgroundColor(anyInt()); verify(builder).setTitleTextColor(anyInt()); @@ -716,7 +687,7 @@ public void testSetThemeWithAllProperties() { verify(builder).setPrimaryTextStyle(Typeface.BOLD); verify(builder).setSecondaryTextStyle(Typeface.ITALIC); verify(builder).setCtaTextStyle(Typeface.BOLD_ITALIC); - + mInstabug.verify(() -> Instabug.setTheme(any(com.instabug.library.model.IBGTheme.class))); } diff --git a/example/ios/InstabugTests/ApmApiTests.m b/example/ios/InstabugTests/ApmApiTests.m index bdb710ac7..3be2288d2 100644 --- a/example/ios/InstabugTests/ApmApiTests.m +++ b/example/ios/InstabugTests/ApmApiTests.m @@ -19,16 +19,6 @@ - (void)setUp { self.api = [[ApmApi alloc] init]; } -- (IBGExecutionTrace *)mockTraceWithId:(NSString *)traceId { - NSString* name = @"trace-name"; - IBGExecutionTrace *mTrace = OCMClassMock([IBGExecutionTrace class]); - - OCMStub([self.mAPM startExecutionTraceWithName:name]).andReturn(mTrace); - - [self.api startExecutionTraceId:traceId name:name completion:^(NSString * _Nullable _, FlutterError * _Nullable __) {}]; - - return mTrace; -} - (void)testSetEnabled { NSNumber *isEnabled = @1; @@ -116,63 +106,6 @@ - (void)testSetAutoUITraceEnabled { OCMVerify([self.mAPM setAutoUITraceEnabled:YES]); } -- (void)testStartExecutionTraceWhenTraceNotNil { - NSString *expectedId = @"trace-id"; - NSString *name = @"trace-name"; - XCTestExpectation *expectation = [self expectationWithDescription:@"Call completion handler"]; - - IBGExecutionTrace *mTrace = OCMClassMock([IBGExecutionTrace class]); - OCMStub([self.mAPM startExecutionTraceWithName:name]).andReturn(mTrace); - - [self.api startExecutionTraceId:expectedId name:name completion:^(NSString *actualId, FlutterError *error) { - [expectation fulfill]; - XCTAssertEqual(actualId, expectedId); - XCTAssertNil(error); - }]; - - OCMVerify([self.mAPM startExecutionTraceWithName:name]); - [self waitForExpectations:@[expectation] timeout:5.0]; -} - -- (void)testStartExecutionTraceWhenTraceIsNil { - NSString *traceId = @"trace-id"; - NSString *name = @"trace-name"; - XCTestExpectation *expectation = [self expectationWithDescription:@"Call completion handler"]; - - OCMStub([self.mAPM startExecutionTraceWithName:name]).andReturn(nil); - - [self.api startExecutionTraceId:traceId name:name completion:^(NSString *actualId, FlutterError *error) { - [expectation fulfill]; - XCTAssertNil(actualId); - XCTAssertNil(error); - }]; - - OCMVerify([self.mAPM startExecutionTraceWithName:name]); - [self waitForExpectations:@[expectation] timeout:5.0]; -} - - -- (void)testSetExecutionTraceAttribute { - NSString *traceId = @"trace-id"; - NSString *key = @"is_premium"; - NSString *value = @"true"; - FlutterError *error; - id mTrace = [self mockTraceWithId:traceId]; - - [self.api setExecutionTraceAttributeId:traceId key:key value:value error:&error]; - - OCMVerify([mTrace setAttributeWithKey:key value:value]); -} - -- (void)testEndExecutionTrace { - NSString *traceId = @"trace-id"; - FlutterError *error; - IBGExecutionTrace *mTrace = [self mockTraceWithId:traceId]; - - [self.api endExecutionTraceId:traceId error:&error]; - - OCMVerify([mTrace end]); -} - (void) testStartFlow { NSString* appFlowName = @"app-flow-name"; diff --git a/example/ios/InstabugTests/BugReportingApiTests.m b/example/ios/InstabugTests/BugReportingApiTests.m index e01df21d2..5b6954d59 100644 --- a/example/ios/InstabugTests/BugReportingApiTests.m +++ b/example/ios/InstabugTests/BugReportingApiTests.m @@ -162,7 +162,7 @@ - (void)testSetCommentMinimumCharacterCountGivenReportTypes { [self.api setCommentMinimumCharacterCountLimit:limit reportTypes:reportTypes error:&error]; - OCMVerify([self.mBugReporting setCommentMinimumCharacterCountForReportTypes:IBGBugReportingReportTypeBug | IBGBugReportingReportTypeQuestion withLimit:limit.intValue]); + OCMVerify([self.mBugReporting setCommentMinimumCharacterCount:limit.intValue forBugReportType:IBGBugReportingReportTypeBug | IBGBugReportingReportTypeQuestion]); } - (void)testSetCommentMinimumCharacterCountGivenNoReportTypes { @@ -172,7 +172,7 @@ - (void)testSetCommentMinimumCharacterCountGivenNoReportTypes { [self.api setCommentMinimumCharacterCountLimit:limit reportTypes:reportTypes error:&error]; - OCMVerify([self.mBugReporting setCommentMinimumCharacterCountForReportTypes:IBGBugReportingReportTypeBug | IBGBugReportingReportTypeFeedback | IBGBugReportingReportTypeQuestion withLimit:limit.intValue]); + OCMVerify([self.mBugReporting setCommentMinimumCharacterCount:limit.intValue forBugReportType:IBGBugReportingReportTypeBug | IBGBugReportingReportTypeFeedback | IBGBugReportingReportTypeQuestion]); } - (void)testAddUserConsentWithKey { NSString *key = @"testKey"; diff --git a/example/ios/InstabugTests/InstabugApiTests.m b/example/ios/InstabugTests/InstabugApiTests.m index 0799800bd..3e1abf332 100644 --- a/example/ios/InstabugTests/InstabugApiTests.m +++ b/example/ios/InstabugTests/InstabugApiTests.m @@ -51,7 +51,7 @@ - (void)testInit { OCMVerify([self.mInstabug setSdkDebugLogsLevel:IBGSDKDebugLogsLevelError]); OCMVerify([self.mInstabug startWithToken:token invocationEvents:(IBGInvocationEventFloatingButton | IBGInvocationEventScreenshot)]); - + XCTAssertEqual(Instabug.appVariant, appVariant); } @@ -207,31 +207,6 @@ - (void)testGetTags { [self waitForExpectations:@[expectation] timeout:5.0]; } -- (void)testAddExperiments { - NSArray *experiments = @[@"premium", @"star"]; - FlutterError *error; - - [self.api addExperimentsExperiments:experiments error:&error]; - - OCMVerify([self.mInstabug addExperiments:experiments]); -} - -- (void)testRemoveExperiments { - NSArray *experiments = @[@"premium", @"star"]; - FlutterError *error; - - [self.api removeExperimentsExperiments:experiments error:&error]; - - OCMVerify([self.mInstabug removeExperiments:experiments]); -} - -- (void)testClearAllExperiments { - FlutterError *error; - - [self.api clearAllExperimentsWithError:&error]; - - OCMVerify([self.mInstabug clearAllExperiments]); -} - (void)testAddFeatureFlags { NSDictionary *featureFlagsMap = @{ @"key13" : @"value1", @"key2" : @"value2"}; @@ -630,7 +605,7 @@ - (void)testSetThemeWithAllProperties { @"secondaryFontPath": @"assets/fonts/CustomFont-Bold.ttf", @"ctaFontPath": @"assets/fonts/CustomFont-Italic.ttf" }; - + id mockTheme = OCMClassMock([IBGTheme class]); OCMStub([mockTheme primaryColor]).andReturn([UIColor redColor]); OCMStub([mockTheme backgroundColor]).andReturn([UIColor whiteColor]); @@ -638,11 +613,11 @@ - (void)testSetThemeWithAllProperties { OCMStub([mockTheme primaryTextColor]).andReturn([UIColor darkGrayColor]); OCMStub([mockTheme secondaryTextColor]).andReturn([UIColor grayColor]); OCMStub([mockTheme callToActionTextColor]).andReturn([UIColor redColor]); - + FlutterError *error; - + [self.api setThemeThemeConfig:themeConfig error:&error]; - + OCMVerify([self.mInstabug setTheme:[OCMArg isNotNil]]); } diff --git a/example/lib/main.dart b/example/lib/main.dart index 1052d42c8..bccdbfc68 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -38,8 +38,6 @@ part 'src/components/network_content.dart'; part 'src/components/page.dart'; -part 'src/components/traces_content.dart'; - part 'src/components/flows_content.dart'; void main() { diff --git a/example/lib/src/components/traces_content.dart b/example/lib/src/components/traces_content.dart deleted file mode 100644 index 888460d43..000000000 --- a/example/lib/src/components/traces_content.dart +++ /dev/null @@ -1,157 +0,0 @@ -part of '../../main.dart'; - -class TracesContent extends StatefulWidget { - const TracesContent({Key? key}) : super(key: key); - - @override - State createState() => _TracesContentState(); -} - -class _TracesContentState extends State { - final traceNameController = TextEditingController(); - final traceKeyAttributeController = TextEditingController(); - final traceValueAttributeController = TextEditingController(); - - bool? didTraceEnd; - - Trace? trace; - - @override - Widget build(BuildContext context) { - final textTheme = Theme.of(context).textTheme; - return Column( - children: [ - InstabugTextField( - label: 'Trace name', - labelStyle: textTheme.labelMedium, - controller: traceNameController, - ), - SizedBox.fromSize( - size: const Size.fromHeight(10.0), - ), - Row( - children: [ - Flexible( - flex: 5, - child: InstabugButton.smallFontSize( - text: 'Start Trace', - onPressed: () => _startTrace(traceNameController.text), - margin: const EdgeInsetsDirectional.only( - start: 20.0, - end: 10.0, - ), - ), - ), - Flexible( - flex: 5, - child: InstabugButton.smallFontSize( - text: 'Start Trace With Delay', - onPressed: () => _startTrace( - traceNameController.text, - delayInMilliseconds: 5000, - ), - margin: const EdgeInsetsDirectional.only( - start: 10.0, - end: 20.0, - ), - ), - ), - ], - ), - Row( - children: [ - Flexible( - flex: 5, - child: InstabugTextField( - label: 'Trace Key Attribute', - controller: traceKeyAttributeController, - labelStyle: textTheme.labelMedium, - margin: const EdgeInsetsDirectional.only( - end: 10.0, - start: 20.0, - ), - ), - ), - Flexible( - flex: 5, - child: InstabugTextField( - label: 'Trace Value Attribute', - labelStyle: textTheme.labelMedium, - controller: traceValueAttributeController, - margin: const EdgeInsetsDirectional.only( - start: 10.0, - end: 20.0, - ), - ), - ), - ], - ), - SizedBox.fromSize( - size: const Size.fromHeight(10.0), - ), - InstabugButton( - text: 'Set Trace Attribute', - onPressed: () => _setTraceAttribute( - trace, - traceKeyAttribute: traceKeyAttributeController.text, - traceValueAttribute: traceValueAttributeController.text, - ), - ), - InstabugButton( - text: 'End Trace', - onPressed: () => _endTrace(), - ), - ], - ); - } - - void _startTrace( - String traceName, { - int delayInMilliseconds = 0, - }) { - if (traceName.trim().isNotEmpty) { - log('_startTrace — traceName: $traceName, delay in Milliseconds: $delayInMilliseconds'); - log('traceName: $traceName'); - Future.delayed( - Duration(milliseconds: delayInMilliseconds), - () => APM - .startExecutionTrace(traceName) - .then((value) => trace = value)); - } else { - log('startTrace - Please enter a trace name'); - } - } - - void _endTrace() { - if (didTraceEnd == true) { - log('_endTrace — Please, start a new trace before setting attributes.'); - } - if (trace == null) { - log('_endTrace — Please, start a trace before ending it.'); - } - log('_endTrace — ending Trace.'); - trace?.end(); - didTraceEnd = true; - } - - void _setTraceAttribute( - Trace? trace, { - required String traceKeyAttribute, - required String traceValueAttribute, - }) { - if (trace == null) { - log('_setTraceAttribute — Please, start a trace before setting attributes.'); - } - if (didTraceEnd == true) { - log('_setTraceAttribute — Please, start a new trace before setting attributes.'); - } - if (traceKeyAttribute.trim().isEmpty) { - log('_setTraceAttribute — Please, fill the trace key attribute input before settings attributes.'); - } - if (traceValueAttribute.trim().isEmpty) { - log('_setTraceAttribute — Please, fill the trace value attribute input before settings attributes.'); - } - log('_setTraceAttribute — setting attributes -> key: $traceKeyAttribute, value: $traceValueAttribute.'); - trace?.setAttribute(traceKeyAttribute, traceValueAttribute); - } -} diff --git a/example/lib/src/screens/apm_page.dart b/example/lib/src/screens/apm_page.dart index 798e906fa..d4ad53ca6 100644 --- a/example/lib/src/screens/apm_page.dart +++ b/example/lib/src/screens/apm_page.dart @@ -36,8 +36,6 @@ class _ApmPageState extends State { ), const SectionTitle('Network'), const NetworkContent(), - const SectionTitle('Traces'), - const TracesContent(), const SectionTitle('Flows'), const FlowsContent(), const SectionTitle('Screen Loading'), diff --git a/example/lib/src/screens/my_home_page.dart b/example/lib/src/screens/my_home_page.dart index 404d79cdd..5f7d50a88 100644 --- a/example/lib/src/screens/my_home_page.dart +++ b/example/lib/src/screens/my_home_page.dart @@ -114,10 +114,10 @@ class _MyHomePageState extends State { BugReporting.setInvocationEvents([invocationEvent]); } - void changePrimaryColor() { - String text = 'FF' + primaryColorController.text.replaceAll('#', ''); - Color color = Color(int.parse(text, radix: 16)); - Instabug.setPrimaryColor(color); + void changePrimaryColor() async { + String text = primaryColorController.text.replaceAll('#', ''); + await Instabug.setTheme(ThemeConfig(primaryColor: '#$text')); + await Future.delayed(const Duration(milliseconds: 500)); } void setColorTheme(ColorTheme colorTheme) { diff --git a/ios/Classes/Modules/ApmApi.m b/ios/Classes/Modules/ApmApi.m index c6295ce67..a8c5c1bd3 100644 --- a/ios/Classes/Modules/ApmApi.m +++ b/ios/Classes/Modules/ApmApi.m @@ -70,44 +70,6 @@ - (void)setAutoUITraceEnabledIsEnabled:(NSNumber *)isEnabled error:(FlutterError IBGAPM.autoUITraceEnabled = [isEnabled boolValue]; } -// This method is responsible for starting an execution trace -// with a given `id` and `name`. -// -// Deprecated - see [startFlowName, setFlowAttributeName & endFlowName]. -- (void)startExecutionTraceId:(NSString *)id name:(NSString *)name completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion { - IBGExecutionTrace *trace = [IBGAPM startExecutionTraceWithName:name]; - - if (trace != nil) { - [traces setObject:trace forKey:id]; - return completion(id, nil); - } else { - return completion(nil, nil); - } -} - -// This method is responsible for setting an attribute for a specific -// execution trace identified by the provided `id`. -// -// Deprecated - see [startFlowName, setFlowAttributeName & endFlowName]. -- (void)setExecutionTraceAttributeId:(NSString *)id key:(NSString *)key value:(NSString *)value error:(FlutterError *_Nullable *_Nonnull)error { - IBGExecutionTrace *trace = [traces objectForKey:id]; - - if (trace != nil) { - [trace setAttributeWithKey:key value:value]; - } -} - -// This method `endExecutionTraceId` is responsible for ending an execution trace identified by the -// provided `id`. -// -// Deprecated - see [startFlowName, setFlowAttributeName & endFlowName]. -- (void)endExecutionTraceId:(NSString *)id error:(FlutterError *_Nullable *_Nonnull)error { - IBGExecutionTrace *trace = [traces objectForKey:id]; - - if (trace != nil) { - [trace end]; - } -} // This method is responsible for starting a flow with the given `name`. This functionality is used to // track and monitor the performance of specific flows within the application. diff --git a/ios/Classes/Modules/BugReportingApi.m b/ios/Classes/Modules/BugReportingApi.m index bb97810b8..7a92a9563 100644 --- a/ios/Classes/Modules/BugReportingApi.m +++ b/ios/Classes/Modules/BugReportingApi.m @@ -151,8 +151,7 @@ - (void)setDisclaimerTextText:(NSString *)text error:(FlutterError *_Nullable *_ } - (void)setCommentMinimumCharacterCountLimit:(NSNumber *)limit reportTypes:(nullable NSArray *)reportTypes error:(FlutterError *_Nullable *_Nonnull)error { - IBGBugReportingReportType resolvedTypes = 0; - + IBGBugReportingType resolvedTypes = 0; if (![reportTypes count]) { resolvedTypes = (ArgsRegistry.reportTypes[@"ReportType.bug"]).integerValue | (ArgsRegistry.reportTypes[@"ReportType.feedback"]).integerValue | (ArgsRegistry.reportTypes[@"ReportType.question"]).integerValue; } @@ -162,7 +161,7 @@ - (void)setCommentMinimumCharacterCountLimit:(NSNumber *)limit reportTypes:(null } } - [IBGBugReporting setCommentMinimumCharacterCountForReportTypes:resolvedTypes withLimit:limit.intValue]; + [IBGBugReporting setCommentMinimumCharacterCount:[limit integerValue] forBugReportType:resolvedTypes]; } - (void)addUserConsentsKey:(NSString *)key diff --git a/ios/Classes/Modules/InstabugApi.m b/ios/Classes/Modules/InstabugApi.m index b6d29ed71..84f574b58 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/ios/Classes/Modules/InstabugApi.m @@ -32,12 +32,12 @@ - (nullable NSNumber *)isEnabledWithError:(FlutterError * _Nullable __autoreleas } - (void)initToken:(nonnull NSString *)token invocationEvents:(nonnull NSArray *)invocationEvents debugLogsLevel:(nonnull NSString *)debugLogsLevel appVariant:(nullable NSString *)appVariant error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { - + if(appVariant != nil){ Instabug.appVariant = appVariant; } - + SEL setPrivateApiSEL = NSSelectorFromString(@"setCurrentPlatform:"); if ([[Instabug class] respondsToSelector:setPrivateApiSEL]) { NSInteger *platformID = IBGPlatformFlutter; @@ -53,8 +53,8 @@ - (void)initToken:(nonnull NSString *)token invocationEvents:(nonnull NSArray * _Nullable, completion([Instabug getTags], nil); } -- (void)addExperimentsExperiments:(NSArray *)experiments error:(FlutterError *_Nullable *_Nonnull)error { - [Instabug addExperiments:experiments]; -} - -- (void)removeExperimentsExperiments:(NSArray *)experiments error:(FlutterError *_Nullable *_Nonnull)error { - [Instabug removeExperiments:experiments]; -} -- (void)clearAllExperimentsWithError:(FlutterError *_Nullable *_Nonnull)error { - [Instabug clearAllExperiments]; -} - (void)setUserAttributeValue:(NSString *)value key:(NSString *)key error:(FlutterError *_Nullable *_Nonnull)error { [Instabug setUserAttribute:value withKey:key]; @@ -406,7 +396,7 @@ - (void)setNetworkLogBodyEnabledIsEnabled:(NSNumber *)isEnabled } -- (void)setAppVariantAppVariant:(nonnull NSString *)appVariant error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (void)setAppVariantAppVariant:(nonnull NSString *)appVariant error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { Instabug.appVariant = appVariant; @@ -415,7 +405,7 @@ - (void)setAppVariantAppVariant:(nonnull NSString *)appVariant error:(FlutterErr - (void)setThemeThemeConfig:(NSDictionary *)themeConfig error:(FlutterError *_Nullable *_Nonnull)error { IBGTheme *theme = [[IBGTheme alloc] init]; - + NSDictionary *colorMapping = @{ @"primaryColor": ^(UIColor *color) { theme.primaryColor = color; }, @"backgroundColor": ^(UIColor *color) { theme.backgroundColor = color; }, @@ -430,7 +420,7 @@ - (void)setThemeThemeConfig:(NSDictionary *)themeConfig error:(F @"selectedRowBackgroundColor": ^(UIColor *color) { theme.selectedRowBackgroundColor = color; }, @"rowSeparatorColor": ^(UIColor *color) { theme.rowSeparatorColor = color; } }; - + for (NSString *key in colorMapping) { if (themeConfig[key]) { NSString *colorString = themeConfig[key]; @@ -441,11 +431,11 @@ - (void)setThemeThemeConfig:(NSDictionary *)themeConfig error:(F } } } - + [self setFontIfPresent:themeConfig[@"primaryFontPath"] ?: themeConfig[@"primaryFontAsset"] forTheme:theme type:@"primary"]; [self setFontIfPresent:themeConfig[@"secondaryFontPath"] ?: themeConfig[@"secondaryFontAsset"] forTheme:theme type:@"secondary"]; [self setFontIfPresent:themeConfig[@"ctaFontPath"] ?: themeConfig[@"ctaFontAsset"] forTheme:theme type:@"cta"]; - + Instabug.theme = theme; } @@ -484,42 +474,42 @@ - (void)setFontIfPresent:(NSString *)fontPath forTheme:(IBGTheme *)theme type:(N - (UIFont *)loadFontFromPath:(NSString *)fontPath { NSString *fontFileName = [fontPath stringByDeletingPathExtension]; NSArray *fontExtensions = @[@"ttf", @"otf", @"woff", @"woff2"]; - + // Find font file in bundle NSString *fontFilePath = nil; for (NSString *extension in fontExtensions) { fontFilePath = [[NSBundle mainBundle] pathForResource:fontFileName ofType:extension]; if (fontFilePath) break; } - + if (!fontFilePath) { return nil; } - + // Load font data NSData *fontData = [NSData dataWithContentsOfFile:fontFilePath]; if (!fontData) { return nil; } - + // Create data provider CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData); if (!provider) { return nil; } - + // Create CG font CGFontRef cgFont = CGFontCreateWithDataProvider(provider); CGDataProviderRelease(provider); - + if (!cgFont) { return nil; } - + // Register font CFErrorRef error = NULL; BOOL registered = CTFontManagerRegisterGraphicsFont(cgFont, &error); - + if (!registered) { if (error) { CFStringRef description = CFErrorCopyDescription(error); @@ -529,21 +519,21 @@ - (UIFont *)loadFontFromPath:(NSString *)fontPath { CGFontRelease(cgFont); return nil; } - + // Get PostScript name and create UIFont NSString *postScriptName = (__bridge_transfer NSString *)CGFontCopyPostScriptName(cgFont); CGFontRelease(cgFont); - + if (!postScriptName) { return nil; } - + return [UIFont fontWithName:postScriptName size:UIFont.systemFontSize]; } - (void)setFont:(UIFont *)font forTheme:(IBGTheme *)theme type:(NSString *)type { if (!font || !theme || !type) return; - + if ([type isEqualToString:@"primary"]) { theme.primaryTextFont = font; } else if ([type isEqualToString:@"secondary"]) { @@ -555,12 +545,12 @@ - (void)setFont:(UIFont *)font forTheme:(IBGTheme *)theme type:(NSString *)type - (UIColor *)colorFromHexString:(NSString *)hexString { NSString *cleanString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""]; - + if (cleanString.length == 6) { unsigned int rgbValue = 0; NSScanner *scanner = [NSScanner scannerWithString:cleanString]; [scanner scanHexInt:&rgbValue]; - + return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0 green:((rgbValue & 0xFF00) >> 8) / 255.0 blue:(rgbValue & 0xFF) / 255.0 @@ -569,13 +559,13 @@ - (UIColor *)colorFromHexString:(NSString *)hexString { unsigned int rgbaValue = 0; NSScanner *scanner = [NSScanner scannerWithString:cleanString]; [scanner scanHexInt:&rgbaValue]; - + return [UIColor colorWithRed:((rgbaValue & 0xFF000000) >> 24) / 255.0 green:((rgbaValue & 0xFF0000) >> 16) / 255.0 blue:((rgbaValue & 0xFF00) >> 8) / 255.0 alpha:(rgbaValue & 0xFF) / 255.0]; } - + return [UIColor blackColor]; } diff --git a/lib/instabug_flutter.dart b/lib/instabug_flutter.dart index 66f74c2fb..2cc2e8eca 100644 --- a/lib/instabug_flutter.dart +++ b/lib/instabug_flutter.dart @@ -4,7 +4,6 @@ export 'src/models/exception_data.dart'; export 'src/models/feature_flag.dart'; export 'src/models/network_data.dart'; export 'src/models/theme_config.dart'; -export 'src/models/trace.dart'; export 'src/models/w3c_header.dart'; // Modules diff --git a/lib/src/models/trace.dart b/lib/src/models/trace.dart deleted file mode 100644 index bf267640b..000000000 --- a/lib/src/models/trace.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:instabug_flutter/src/modules/apm.dart'; - -class Trace { - Trace({ - required this.id, - required this.name, - }); - - final String id; - final String name; - final Map attributes = {}; - - /// Sets attribute of execution trace. - /// [String] id of the trace. - /// [String] key of attribute. - /// [String] value of attribute. - /// - /// Please migrate to the App Flows APIs: [APM.startFlow], [APM.setFlowAttribute], and [APM.endFlow]. - @Deprecated( - 'Please migrate to the App Flows APIs: APM.startAppFlow, APM.endFlow, and APM.setFlowAttribute. This feature was deprecated in v13.0.0', - ) - void setAttribute(String key, String value) { - APM.setExecutionTraceAttribute(id, key, value); - attributes[key] = value; - } - - /// Ends Execution Trace - /// - /// Please migrate to the App Flows APIs: [APM.startFlow], [APM.setFlowAttribute], and [APM.endFlow]. - @Deprecated( - 'Please migrate to the App Flows APIs: APM.startAppFlow, APM.endFlow, and APM.setFlowAttribute. This feature was deprecated in v13.0.0', - ) - void end() { - APM.endExecutionTrace(id); - } - - Map toJson() { - return { - 'id': id, - 'name': name, - 'attributes': attributes, - }; - } -} diff --git a/lib/src/modules/apm.dart b/lib/src/modules/apm.dart index a9f6e0a7c..970c9330e 100644 --- a/lib/src/modules/apm.dart +++ b/lib/src/modules/apm.dart @@ -5,9 +5,7 @@ import 'dart:async'; import 'package:flutter/widgets.dart' show WidgetBuilder; import 'package:instabug_flutter/src/generated/apm.api.g.dart'; import 'package:instabug_flutter/src/models/network_data.dart'; -import 'package:instabug_flutter/src/models/trace.dart'; import 'package:instabug_flutter/src/utils/ibg_build_info.dart'; -import 'package:instabug_flutter/src/utils/ibg_date_time.dart'; import 'package:instabug_flutter/src/utils/instabug_logger.dart'; import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_manager.dart'; import 'package:meta/meta.dart'; @@ -72,58 +70,6 @@ class APM { return _host.setColdAppLaunchEnabled(isEnabled); } - /// Starts an execution trace. - /// [String] name of the trace. - /// - /// Please migrate to the App Flows APIs: [startFlow], [setFlowAttribute], and [endFlow]. - @Deprecated( - 'Please migrate to the App Flows APIs: APM.startAppFlow, APM.endFlow, and APM.setFlowAttribute. This feature was deprecated in v13.0.0', - ) - static Future startExecutionTrace(String name) async { - final id = IBGDateTime.instance.now(); - final traceId = await _host.startExecutionTrace(id.toString(), name); - - if (traceId == null) { - return Future.error( - "Execution trace $name wasn't created. Please make sure to enable APM first by following " - 'the instructions at this link: https://docs.instabug.com/reference#enable-or-disable-apm', - ); - } - - return Trace( - id: traceId, - name: name, - ); - } - - /// Sets attribute of an execution trace. - /// [String] id of the trace. - /// [String] key of attribute. - /// [String] value of attribute. - /// - /// Please migrate to the App Flows APIs: [startFlow], [setFlowAttribute], and [endFlow]. - @Deprecated( - 'Please migrate to the App Flows APIs: APM.startAppFlow, APM.endFlow, and APM.setFlowAttribute. This feature was deprecated in v13.0.0', - ) - static Future setExecutionTraceAttribute( - String id, - String key, - String value, - ) async { - return _host.setExecutionTraceAttribute(id, key, value); - } - - /// Ends an execution trace. - /// [String] id of the trace. - /// - /// Please migrate to the App Flows APIs: [startFlow], [setFlowAttribute], and [endFlow]. - @Deprecated( - 'Please migrate to the App Flows APIs: APM.startAppFlow, APM.endFlow, and APM.setFlowAttribute. This feature was deprecated in v13.0.0', - ) - static Future endExecutionTrace(String id) async { - return _host.endExecutionTrace(id); - } - /// Starts an AppFlow with the given [name]. /// /// The [name] must not be an empty string. It should be unique and not exceed 150 characters, diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index 86962b0bd..b0073b42a 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -262,31 +262,6 @@ class Instabug { return tags?.cast(); } - /// Adds experiments to the next report. - @Deprecated( - 'Please migrate to the new feature flags APIs: Instabug.addFeatureFlags.', - ) - static Future addExperiments(List experiments) async { - return _host.addExperiments(experiments); - } - - /// Removes certain experiments from the next report. - @Deprecated( - 'Please migrate to the new feature flags APIs: Instabug.removeFeatureFlags.', - ) - static Future removeExperiments(List experiments) async { - return _host.removeExperiments(experiments); - } - - /// Clears all experiments from the next report. - - @Deprecated( - 'Please migrate to the new feature flags APIs: Instabug.clearAllFeatureFlags.', - ) - static Future clearAllExperiments() async { - return _host.clearAllExperiments(); - } - /// Adds feature flags to the next report. static Future addFeatureFlags(List featureFlags) async { final map = {}; @@ -362,8 +337,13 @@ class Instabug { /// Sets the primary color of the SDK's UI. /// Sets the color of UI elements indicating interactivity or call to action. /// [color] primaryColor A color to set the UI elements of the SDK to. + /// + /// Note: This API is deprecated. Please use `Instabug.setTheme` instead. + @Deprecated( + 'This API is deprecated. Please use Instabug.setTheme instead.', + ) static Future setPrimaryColor(Color color) async { - return _host.setPrimaryColor(color.value); + await setTheme(ThemeConfig(primaryColor: color.toString())); } /// Adds specific user data that you need to be added to the reports diff --git a/pigeons/apm.api.dart b/pigeons/apm.api.dart index 84fe9eb8e..25beca00e 100644 --- a/pigeons/apm.api.dart +++ b/pigeons/apm.api.dart @@ -11,18 +11,9 @@ abstract class ApmHostApi { void setColdAppLaunchEnabled(bool isEnabled); void setAutoUITraceEnabled(bool isEnabled); - @async - String? startExecutionTrace(String id, String name); - void startFlow(String name); void setFlowAttribute(String name, String key, String? value); void endFlow(String name); - void setExecutionTraceAttribute( - String id, - String key, - String value, - ); - void endExecutionTrace(String id); void startUITrace(String name); void endUITrace(); void endAppLaunch(); diff --git a/pigeons/instabug.api.dart b/pigeons/instabug.api.dart index f67b8b8d7..aa91aec11 100644 --- a/pigeons/instabug.api.dart +++ b/pigeons/instabug.api.dart @@ -57,12 +57,6 @@ abstract class InstabugHostApi { @async List? getTags(); - void addExperiments(List experiments); - - void removeExperiments(List experiments); - - void clearAllExperiments(); - void addFeatureFlags(Map featureFlagsMap); void removeFeatureFlags(List featureFlags); diff --git a/test/apm_test.dart b/test/apm_test.dart index c801926f3..16cc2f26d 100644 --- a/test/apm_test.dart +++ b/test/apm_test.dart @@ -86,48 +86,6 @@ void main() { ).called(1); }); - test('[startExecutionTrace] should call host method', () async { - final id = DateTime.now(); - const name = "trace"; - - when(mDateTime.now()).thenAnswer((_) => id); - when(mHost.startExecutionTrace(id.toString(), name)) - .thenAnswer((_) async => id.toString()); - - // ignore: deprecated_member_use_from_same_package - final trace = await APM.startExecutionTrace(name); - - expect(trace.id, id.toString()); - - verify( - mHost.startExecutionTrace(id.toString(), name), - ).called(1); - }); - - test('[setExecutionTraceAttribute] should call host method', () async { - final id = DateTime.now().toString(); - const key = "attr-key"; - const attribute = "Trace Attribute"; - - // ignore: deprecated_member_use_from_same_package - await APM.setExecutionTraceAttribute(id, key, attribute); - - verify( - mHost.setExecutionTraceAttribute(id, key, attribute), - ).called(1); - }); - - test('[endExecutionTrace] should call host method', () async { - final id = DateTime.now().toString(); - - // ignore: deprecated_member_use_from_same_package - await APM.endExecutionTrace(id); - - verify( - mHost.endExecutionTrace(id), - ).called(1); - }); - test('[startFlow] should call host method', () async { const flowName = "flow-name"; await APM.startFlow(flowName); diff --git a/test/instabug_test.dart b/test/instabug_test.dart index 10cfe63b2..37ce0d538 100644 --- a/test/instabug_test.dart +++ b/test/instabug_test.dart @@ -197,16 +197,6 @@ void main() { ).called(1); }); - test('[setPrimaryColor] should call host method', () async { - const color = Color(0x00000000); - - await Instabug.setPrimaryColor(color); - - verify( - mHost.setPrimaryColor(color.value), - ).called(1); - }); - test('[setSessionProfilerEnabled] should call host method', () async { const enabled = true; @@ -258,37 +248,6 @@ void main() { ).called(1); }); - test('[addExperiments] should call host method', () async { - const experiments = ["exp-1", "exp-2"]; - - // ignore: deprecated_member_use_from_same_package - await Instabug.addExperiments(experiments); - - verify( - mHost.addExperiments(experiments), - ).called(1); - }); - - test('[removeExperiments] should call host method', () async { - const experiments = ["exp-1", "exp-2"]; - - // ignore: deprecated_member_use_from_same_package - await Instabug.removeExperiments(experiments); - - verify( - mHost.removeExperiments(experiments), - ).called(1); - }); - - test('[clearAllExperiments] should call host method', () async { - // ignore: deprecated_member_use_from_same_package - await Instabug.clearAllExperiments(); - - verify( - mHost.clearAllExperiments(), - ).called(1); - }); - test('[addFeatureFlags] should call host method', () async { await Instabug.addFeatureFlags([ FeatureFlag(name: 'name1', variant: 'variant1'), @@ -494,4 +453,14 @@ void main() { mHost.setFullscreen(isEnabled), ).called(1); }); + + test('[setTheme] should call host method with theme config', () async { + const themeConfig = ThemeConfig(primaryColor: '#FF0000'); + + await Instabug.setTheme(themeConfig); + + verify( + mHost.setTheme(themeConfig.toMap()), + ).called(1); + }); } diff --git a/test/trace_test.dart b/test/trace_test.dart deleted file mode 100644 index 2415420be..000000000 --- a/test/trace_test.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:instabug_flutter/instabug_flutter.dart'; -import 'package:instabug_flutter/src/generated/apm.api.g.dart'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; - -import 'trace_test.mocks.dart'; - -@GenerateMocks([ - ApmHostApi, -]) -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - WidgetsFlutterBinding.ensureInitialized(); - - final mHost = MockApmHostApi(); - final trace = Trace( - id: "trace", - name: "Execution Trace", - ); - - setUpAll(() { - APM.$setHostApi(mHost); - }); - - test('[end] should call host method', () async { - // ignore: deprecated_member_use_from_same_package - trace.end(); - - verify( - mHost.endExecutionTrace(trace.id), - ).called(1); - }); - - test('[setAttribute] should call host method', () async { - const key = "attr-key"; - const attribute = "Trace Attribute"; - // ignore: deprecated_member_use_from_same_package - trace.setAttribute(key, attribute); - - verify( - mHost.setExecutionTraceAttribute(trace.id, key, attribute), - ).called(1); - }); -}