瀏覽代碼

rn: add ability to disable crash reporting

j8
tmoldovan8x8 5 年之前
父節點
當前提交
b3f16926d4
沒有連結到貢獻者的電子郵件帳戶。

+ 4
- 7
android/app/build.gradle 查看文件

1
 apply plugin: 'com.android.application'
1
 apply plugin: 'com.android.application'
2
 
2
 
3
-boolean googleServicesEnabled \
4
-    = project.file('google-services.json').exists() && !rootProject.ext.libreBuild
5
-
6
 // Crashlytics integration is done as part of Firebase now, so it gets
3
 // Crashlytics integration is done as part of Firebase now, so it gets
7
 // automagically activated with google-services.json
4
 // automagically activated with google-services.json
8
 if (googleServicesEnabled) {
5
 if (googleServicesEnabled) {
13
 // This lets us upload a new build at most every 10 seconds for the
10
 // This lets us upload a new build at most every 10 seconds for the
14
 // next ~680 years.
11
 // next ~680 years.
15
 // https://stackoverflow.com/a/38643838
12
 // https://stackoverflow.com/a/38643838
16
-def vcode = (int)(((new Date().getTime()/1000) - 1546297200) / 10)
13
+def vcode = (int) (((new Date().getTime() / 1000) - 1546297200) / 10)
17
 
14
 
18
 android {
15
 android {
19
     compileSdkVersion rootProject.ext.compileSdkVersion
16
     compileSdkVersion rootProject.ext.compileSdkVersion
143
         def targetName = variant.name.capitalize()
140
         def targetName = variant.name.capitalize()
144
 
141
 
145
         def currentRunPackagerTask = tasks.create(
142
         def currentRunPackagerTask = tasks.create(
146
-                name: "run${targetName}ReactPackager",
147
-                type: Exec) {
143
+            name: "run${targetName}ReactPackager",
144
+            type: Exec) {
148
             group = "react"
145
             group = "react"
149
             description = "Run the React packager."
146
             description = "Run the React packager."
150
 
147
 
175
 }
172
 }
176
 
173
 
177
 if (googleServicesEnabled) {
174
 if (googleServicesEnabled) {
178
-   apply plugin: 'com.google.gms.google-services'
175
+    apply plugin: 'com.google.gms.google-services'
179
 }
176
 }

+ 4
- 1
android/app/src/main/java/org/jitsi/meet/GoogleServicesHelper.java 查看文件

7
 import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
7
 import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
8
 import io.fabric.sdk.android.Fabric;
8
 import io.fabric.sdk.android.Fabric;
9
 
9
 
10
+import org.jitsi.meet.sdk.JitsiMeet;
10
 import org.jitsi.meet.sdk.JitsiMeetActivity;
11
 import org.jitsi.meet.sdk.JitsiMeetActivity;
11
 
12
 
12
 /**
13
 /**
21
         if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
22
         if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
22
             Log.d(activity.getClass().getSimpleName(), "Initializing Google Services");
23
             Log.d(activity.getClass().getSimpleName(), "Initializing Google Services");
23
 
24
 
24
-            Fabric.with(activity, new Crashlytics());
25
+            if (!JitsiMeet.isCrashReportingDisabled(activity)) {
26
+                Fabric.with(activity, new Crashlytics());
27
+            }
25
 
28
 
26
             FirebaseDynamicLinks.getInstance().getDynamicLink(activity.getIntent())
29
             FirebaseDynamicLinks.getInstance().getDynamicLink(activity.getIntent())
27
                 .addOnSuccessListener(activity, pendingDynamicLinkData -> {
30
                 .addOnSuccessListener(activity, pendingDynamicLinkData -> {

+ 2
- 0
android/build.gradle 查看文件

161
 
161
 
162
     // Libre build
162
     // Libre build
163
     libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
163
     libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
164
+
165
+    googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
164
 }
166
 }
165
 
167
 
166
 // Force the version of the Android build tools we have chosen on all
168
 // Force the version of the Android build tools we have chosen on all

+ 3
- 0
android/sdk/build.gradle 查看文件

14
     buildTypes {
14
     buildTypes {
15
         debug {
15
         debug {
16
             buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
16
             buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
17
+            buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${rootProject.ext.googleServicesEnabled}"
17
         }
18
         }
18
         release {
19
         release {
19
             minifyEnabled false
20
             minifyEnabled false
20
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21
             buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
22
             buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
23
+            buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${rootProject.ext.googleServicesEnabled}"
22
         }
24
         }
23
     }
25
     }
24
 
26
 
70
     implementation project(':react-native-calendar-events')
72
     implementation project(':react-native-calendar-events')
71
     implementation project(':react-native-community-async-storage')
73
     implementation project(':react-native-community-async-storage')
72
     implementation project(':react-native-community_netinfo')
74
     implementation project(':react-native-community_netinfo')
75
+    implementation project(':react-native-default-preference')
73
     implementation project(':react-native-immersive')
76
     implementation project(':react-native-immersive')
74
     implementation project(':react-native-keep-awake')
77
     implementation project(':react-native-keep-awake')
75
     implementation project(':react-native-linear-gradient')
78
     implementation project(':react-native-linear-gradient')

+ 1
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/AppInfoModule.java 查看文件

76
             "version",
76
             "version",
77
             packageInfo == null ? "" : packageInfo.versionName);
77
             packageInfo == null ? "" : packageInfo.versionName);
78
         constants.put("LIBRE_BUILD", BuildConfig.LIBRE_BUILD);
78
         constants.put("LIBRE_BUILD", BuildConfig.LIBRE_BUILD);
79
+        constants.put("GOOGLE_SERVICES_ENABLED", BuildConfig.GOOGLE_SERVICES_ENABLED);
79
 
80
 
80
         return constants;
81
         return constants;
81
     }
82
     }

+ 10
- 1
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeet.java 查看文件

16
  */
16
  */
17
 package org.jitsi.meet.sdk;
17
 package org.jitsi.meet.sdk;
18
 
18
 
19
+import android.content.Context;
20
+import android.content.SharedPreferences;
19
 import android.os.Bundle;
21
 import android.os.Bundle;
20
 
22
 
21
 import com.facebook.react.ReactInstanceManager;
23
 import com.facebook.react.ReactInstanceManager;
22
 
24
 
23
 public class JitsiMeet {
25
 public class JitsiMeet {
26
+
24
     /**
27
     /**
25
      * Default {@link JitsiMeetConferenceOptions} which will be used for all conferences. When
28
      * Default {@link JitsiMeetConferenceOptions} which will be used for all conferences. When
26
      * joining a conference these options will be merged with the ones passed to
29
      * joining a conference these options will be merged with the ones passed to
72
             reactInstanceManager.showDevOptionsDialog();
75
             reactInstanceManager.showDevOptionsDialog();
73
         }
76
         }
74
     }
77
     }
75
-}
78
+
79
+    public static boolean isCrashReportingDisabled(Context context) {
80
+        SharedPreferences preferences = context.getSharedPreferences("jitsi-default-preferences", Context.MODE_PRIVATE);
81
+        String value = preferences.getString("isCrashReportingDisabled", "");
82
+        return Boolean.parseBoolean(value);
83
+    }
84
+}

+ 1
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java 查看文件

190
                 new com.corbt.keepawake.KCKeepAwakePackage(),
190
                 new com.corbt.keepawake.KCKeepAwakePackage(),
191
                 new com.facebook.react.shell.MainReactPackage(),
191
                 new com.facebook.react.shell.MainReactPackage(),
192
                 new com.horcrux.svg.SvgPackage(),
192
                 new com.horcrux.svg.SvgPackage(),
193
+                new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
193
                 new com.ocetnik.timer.BackgroundTimerPackage(),
194
                 new com.ocetnik.timer.BackgroundTimerPackage(),
194
                 new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
195
                 new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
195
                 new com.reactnativecommunity.netinfo.NetInfoPackage(),
196
                 new com.reactnativecommunity.netinfo.NetInfoPackage(),

+ 3
- 1
android/settings.gradle 查看文件

9
 project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
9
 project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
10
 include ':react-native-community_netinfo'
10
 include ':react-native-community_netinfo'
11
 project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
11
 project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
12
+include ':react-native-default-preference'
13
+project(':react-native-default-preference').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-default-preference/android')
12
 include ':react-native-google-signin'
14
 include ':react-native-google-signin'
13
 project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
15
 project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
14
 include ':react-native-immersive'
16
 include ':react-native-immersive'
24
 include ':react-native-webrtc'
26
 include ':react-native-webrtc'
25
 project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
27
 project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
26
 include ':react-native-webview'
28
 include ':react-native-webview'
27
-project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
29
+project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')

+ 1
- 0
ios/Podfile 查看文件

66
   pod 'RNSound', :path => '../node_modules/react-native-sound'
66
   pod 'RNSound', :path => '../node_modules/react-native-sound'
67
   pod 'RNSVG', :path => '../node_modules/react-native-svg'
67
   pod 'RNSVG', :path => '../node_modules/react-native-svg'
68
   pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
68
   pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
69
+  pod 'RNDefaultPreference', :path => '../node_modules/react-native-default-preference'
69
 
70
 
70
   # Native pod dependencies
71
   # Native pod dependencies
71
   #
72
   #

+ 10
- 5
ios/Podfile.lock 查看文件

353
     - ReactCommon/turbomodule/core (= 0.61.5-jitsi.1)
353
     - ReactCommon/turbomodule/core (= 0.61.5-jitsi.1)
354
   - RNCAsyncStorage (1.3.4):
354
   - RNCAsyncStorage (1.3.4):
355
     - React
355
     - React
356
+  - RNDefaultPreference (1.4.2):
357
+    - React
356
   - RNGoogleSignin (3.0.1):
358
   - RNGoogleSignin (3.0.1):
357
     - GoogleSignIn (~> 5.0.0)
359
     - GoogleSignIn (~> 5.0.0)
358
     - React
360
     - React
409
   - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
411
   - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
410
   - ReactCommon/turbomodule (from `../node_modules/react-native/ReactCommon`)
412
   - ReactCommon/turbomodule (from `../node_modules/react-native/ReactCommon`)
411
   - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
413
   - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
414
+  - RNDefaultPreference (from `../node_modules/react-native-default-preference`)
412
   - "RNGoogleSignin (from `../node_modules/@react-native-community/google-signin`)"
415
   - "RNGoogleSignin (from `../node_modules/@react-native-community/google-signin`)"
413
   - RNSound (from `../node_modules/react-native-sound`)
416
   - RNSound (from `../node_modules/react-native-sound`)
414
   - RNSVG (from `../node_modules/react-native-svg`)
417
   - RNSVG (from `../node_modules/react-native-svg`)
416
   - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
419
   - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
417
 
420
 
418
 SPEC REPOS:
421
 SPEC REPOS:
419
-  https://github.com/CocoaPods/Specs.git:
422
+  trunk:
420
     - Amplitude-iOS
423
     - Amplitude-iOS
424
+    - AppAuth
421
     - boost-for-react-native
425
     - boost-for-react-native
422
     - CocoaLumberjack
426
     - CocoaLumberjack
423
-    - ObjectiveDropboxOfficial
424
-  trunk:
425
-    - AppAuth
426
     - Crashlytics
427
     - Crashlytics
427
     - Fabric
428
     - Fabric
428
     - Firebase
429
     - Firebase
442
     - GTMAppAuth
443
     - GTMAppAuth
443
     - GTMSessionFetcher
444
     - GTMSessionFetcher
444
     - nanopb
445
     - nanopb
446
+    - ObjectiveDropboxOfficial
445
     - PromisesObjC
447
     - PromisesObjC
446
 
448
 
447
 EXTERNAL SOURCES:
449
 EXTERNAL SOURCES:
509
     :path: "../node_modules/react-native/ReactCommon"
511
     :path: "../node_modules/react-native/ReactCommon"
510
   RNCAsyncStorage:
512
   RNCAsyncStorage:
511
     :path: "../node_modules/@react-native-community/async-storage"
513
     :path: "../node_modules/@react-native-community/async-storage"
514
+  RNDefaultPreference:
515
+    :path: "../node_modules/react-native-default-preference"
512
   RNGoogleSignin:
516
   RNGoogleSignin:
513
     :path: "../node_modules/@react-native-community/google-signin"
517
     :path: "../node_modules/@react-native-community/google-signin"
514
   RNSound:
518
   RNSound:
578
   React-RCTVibration: a1bcfcdc0b5a73a1b0829a34cee22bd0e95bacba
582
   React-RCTVibration: a1bcfcdc0b5a73a1b0829a34cee22bd0e95bacba
579
   ReactCommon: 675681aba4fecff5acbc0e440530cc422103c610
583
   ReactCommon: 675681aba4fecff5acbc0e440530cc422103c610
580
   RNCAsyncStorage: 8e31405a9f12fbf42c2bb330e4560bfd79c18323
584
   RNCAsyncStorage: 8e31405a9f12fbf42c2bb330e4560bfd79c18323
585
+  RNDefaultPreference: 56a405ce61033ac77b95004dccd7ac54c2eb50d1
581
   RNGoogleSignin: 39336070b35fc4cea6a98cf111e00480317be0ae
586
   RNGoogleSignin: 39336070b35fc4cea6a98cf111e00480317be0ae
582
   RNSound: c980916b596cc15c8dcd2f6ecd3b13c4881dbe20
587
   RNSound: c980916b596cc15c8dcd2f6ecd3b13c4881dbe20
583
   RNSVG: aac12785382e8fd4f28d072fe640612e34914631
588
   RNSVG: aac12785382e8fd4f28d072fe640612e34914631
584
   RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
589
   RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
585
   Yoga: 7b4209fda2441f99d54dd6cf4c82b094409bb68f
590
   Yoga: 7b4209fda2441f99d54dd6cf4c82b094409bb68f
586
 
591
 
587
-PODFILE CHECKSUM: f615794fb9184757b00cd16e534824ba6ee2fc98
592
+PODFILE CHECKSUM: 082858daebbe170e7a490de433e7f2a99e0c3701
588
 
593
 
589
 COCOAPODS: 1.9.1
594
 COCOAPODS: 1.9.1

+ 7
- 9
ios/app/src/AppDelegate.m 查看文件

24
 @import Firebase;
24
 @import Firebase;
25
 @import JitsiMeet;
25
 @import JitsiMeet;
26
 
26
 
27
-
28
 @implementation AppDelegate
27
 @implementation AppDelegate
29
 
28
 
30
 -             (BOOL)application:(UIApplication *)application
29
 -             (BOOL)application:(UIApplication *)application
31
   didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
30
   didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
32
-
33
-    // Initialize Crashlytics and Firebase if a valid GoogleService-Info.plist file was provided.
34
-    if ([FIRUtilities appContainsRealServiceInfoPlist]) {
35
-        NSLog(@"Enablign Crashlytics and Firebase");
36
-        [FIRApp configure];
37
-        [Fabric with:@[[Crashlytics class]]];
38
-    }
39
-
40
     JitsiMeet *jitsiMeet = [JitsiMeet sharedInstance];
31
     JitsiMeet *jitsiMeet = [JitsiMeet sharedInstance];
41
 
32
 
42
     jitsiMeet.conferenceActivityType = JitsiMeetConferenceActivityType;
33
     jitsiMeet.conferenceActivityType = JitsiMeetConferenceActivityType;
54
 #endif
45
 #endif
55
     }];
46
     }];
56
 
47
 
48
+    // Initialize Crashlytics and Firebase if a valid GoogleService-Info.plist file was provided.
49
+    if ([FIRUtilities appContainsRealServiceInfoPlist] && ![jitsiMeet isCrashReportingDisabled]) {
50
+        NSLog(@"Enabling Crashlytics and Firebase");
51
+        [FIRApp configure];
52
+        [Fabric with:@[[Crashlytics class]]];
53
+    }
54
+
57
     [jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
55
     [jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
58
 
56
 
59
     return YES;
57
     return YES;

+ 2
- 33
ios/app/src/FIRUtilities.m 查看文件

16
 
16
 
17
 #import "FIRUtilities.h"
17
 #import "FIRUtilities.h"
18
 
18
 
19
-// Plist file name.
20
-NSString *const kGoogleServiceInfoFileName = @"GoogleService-Info";
21
-// Plist file type.
22
-NSString *const kGoogleServiceInfoFileType = @"plist";
23
-NSString *const kGoogleAppIDPlistKey = @"GOOGLE_APP_ID";
24
-
19
+@import JitsiMeet;
25
 
20
 
26
 @implementation FIRUtilities
21
 @implementation FIRUtilities
27
 
22
 
30
   static dispatch_once_t onceToken;
25
   static dispatch_once_t onceToken;
31
   dispatch_once(&onceToken, ^{
26
   dispatch_once(&onceToken, ^{
32
     NSBundle *bundle = [NSBundle mainBundle];
27
     NSBundle *bundle = [NSBundle mainBundle];
33
-    containsRealServiceInfoPlist = [self containsRealServiceInfoPlistInBundle:bundle];
28
+    containsRealServiceInfoPlist = [InfoPlistUtil containsRealServiceInfoPlistInBundle:bundle];
34
   });
29
   });
35
   return containsRealServiceInfoPlist;
30
   return containsRealServiceInfoPlist;
36
 }
31
 }
37
 
32
 
38
-+ (BOOL)containsRealServiceInfoPlistInBundle:(NSBundle *)bundle {
39
-  NSString *bundlePath = bundle.bundlePath;
40
-  if (!bundlePath.length) {
41
-    return NO;
42
-  }
43
-
44
-  NSString *plistFilePath = [bundle pathForResource:kGoogleServiceInfoFileName
45
-                                             ofType:kGoogleServiceInfoFileType];
46
-  if (!plistFilePath.length) {
47
-    return NO;
48
-  }
49
-
50
-  NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
51
-  if (!plist) {
52
-    return NO;
53
-  }
54
-
55
-  // Perform a very naive validation by checking to see if the plist has the dummy google app id
56
-  NSString *googleAppID = plist[kGoogleAppIDPlistKey];
57
-  if (!googleAppID.length) {
58
-    return NO;
59
-  }
60
-
61
-  return YES;
62
-}
63
-
64
 + (NSURL *)extractURL: (FIRDynamicLink*)dynamicLink {
33
 + (NSURL *)extractURL: (FIRDynamicLink*)dynamicLink {
65
   NSURL *url = nil;
34
   NSURL *url = nil;
66
   if (dynamicLink != nil) {
35
   if (dynamicLink != nil) {

+ 8
- 0
ios/sdk/sdk.xcodeproj/project.pbxproj 查看文件

42
 		C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
42
 		C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
43
 		C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
43
 		C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
44
 		C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
44
 		C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
45
+		C8AFD27F2462C613000293D2 /* InfoPlistUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
46
+		C8AFD2802462C613000293D2 /* InfoPlistUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */; };
45
 		DE438CDA2350934700DD541D /* JavaScriptSandbox.m in Sources */ = {isa = PBXBuildFile; fileRef = DE438CD82350934700DD541D /* JavaScriptSandbox.m */; };
47
 		DE438CDA2350934700DD541D /* JavaScriptSandbox.m in Sources */ = {isa = PBXBuildFile; fileRef = DE438CD82350934700DD541D /* JavaScriptSandbox.m */; };
46
 		DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AAC92317FFCD00290BEC /* LogUtils.h */; };
48
 		DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AAC92317FFCD00290BEC /* LogUtils.h */; };
47
 		DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */; };
49
 		DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */; };
105
 		C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
107
 		C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
106
 		C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
108
 		C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
107
 		C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
109
 		C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
110
+		C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InfoPlistUtil.h; sourceTree = "<group>"; };
111
+		C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InfoPlistUtil.m; sourceTree = "<group>"; };
108
 		DE438CD82350934700DD541D /* JavaScriptSandbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JavaScriptSandbox.m; sourceTree = "<group>"; };
112
 		DE438CD82350934700DD541D /* JavaScriptSandbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JavaScriptSandbox.m; sourceTree = "<group>"; };
109
 		DE65AAC92317FFCD00290BEC /* LogUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogUtils.h; sourceTree = "<group>"; };
113
 		DE65AAC92317FFCD00290BEC /* LogUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogUtils.h; sourceTree = "<group>"; };
110
 		DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetBaseLogHandler+Private.h"; sourceTree = "<group>"; };
114
 		DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetBaseLogHandler+Private.h"; sourceTree = "<group>"; };
223
 				DEFE535521FB2E8300011A3A /* ReactUtils.m */,
227
 				DEFE535521FB2E8300011A3A /* ReactUtils.m */,
224
 				0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
228
 				0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
225
 				0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
229
 				0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
230
+				C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */,
231
+				C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */,
226
 			);
232
 			);
227
 			path = src;
233
 			path = src;
228
 			sourceTree = "<group>";
234
 			sourceTree = "<group>";
303
 				DE81A2D42316AC4D00AE1940 /* JitsiMeetLogger.h in Headers */,
309
 				DE81A2D42316AC4D00AE1940 /* JitsiMeetLogger.h in Headers */,
304
 				DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */,
310
 				DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */,
305
 				DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */,
311
 				DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */,
312
+				C8AFD27F2462C613000293D2 /* InfoPlistUtil.h in Headers */,
306
 			);
313
 			);
307
 			runOnlyForDeploymentPostprocessing = 0;
314
 			runOnlyForDeploymentPostprocessing = 0;
308
 		};
315
 		};
482
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
489
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
483
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
490
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
484
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
491
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
492
+				C8AFD2802462C613000293D2 /* InfoPlistUtil.m in Sources */,
485
 				C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */,
493
 				C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */,
486
 				DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */,
494
 				DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */,
487
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
495
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,

+ 6
- 2
ios/sdk/src/AppInfo.m 查看文件

19
 #import <React/RCTBridgeModule.h>
19
 #import <React/RCTBridgeModule.h>
20
 #import <React/RCTLog.h>
20
 #import <React/RCTLog.h>
21
 
21
 
22
+#import "InfoPlistUtil.h"
23
+
22
 @interface AppInfo : NSObject<RCTBridgeModule>
24
 @interface AppInfo : NSObject<RCTBridgeModule>
23
 @end
25
 @end
24
 
26
 
67
         buildNumber = @"";
69
         buildNumber = @"";
68
     }
70
     }
69
 
71
 
72
+    BOOL isGoogleServiceEnabled = [InfoPlistUtil containsRealServiceInfoPlistInBundle:[NSBundle mainBundle]];
73
+    
70
     return @{
74
     return @{
71
         @"calendarEnabled": [NSNumber numberWithBool:calendarEnabled],
75
         @"calendarEnabled": [NSNumber numberWithBool:calendarEnabled],
72
         @"buildNumber": buildNumber,
76
         @"buildNumber": buildNumber,
73
         @"name": name,
77
         @"name": name,
74
         @"sdkBundlePath": sdkBundlePath,
78
         @"sdkBundlePath": sdkBundlePath,
75
-        @"version": version
79
+        @"version": version,
80
+        @"GOOGLE_SERVICES_ENABLED": [NSNumber numberWithBool:isGoogleServiceEnabled]
76
     };
81
     };
77
 };
82
 };
78
-
79
 @end
83
 @end

+ 23
- 0
ios/sdk/src/InfoPlistUtil.h 查看文件

1
+/*
2
+* Copyright @ 2019-present 8x8, Inc.
3
+*
4
+* Licensed under the Apache License, Version 2.0 (the "License");
5
+* you may not use this file except in compliance with the License.
6
+* You may obtain a copy of the License at
7
+*
8
+*     http://www.apache.org/licenses/LICENSE-2.0
9
+*
10
+* Unless required by applicable law or agreed to in writing, software
11
+* distributed under the License is distributed on an "AS IS" BASIS,
12
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+* See the License for the specific language governing permissions and
14
+* limitations under the License.
15
+*/
16
+
17
+#import <Foundation/Foundation.h>
18
+
19
+@interface InfoPlistUtil : NSObject
20
+
21
++ (BOOL)containsRealServiceInfoPlistInBundle:(NSBundle *)bundle;
22
+
23
+@end

+ 52
- 0
ios/sdk/src/InfoPlistUtil.m 查看文件

1
+/*
2
+* Copyright @ 2019-present 8x8, Inc.
3
+*
4
+* Licensed under the Apache License, Version 2.0 (the "License");
5
+* you may not use this file except in compliance with the License.
6
+* You may obtain a copy of the License at
7
+*
8
+*     http://www.apache.org/licenses/LICENSE-2.0
9
+*
10
+* Unless required by applicable law or agreed to in writing, software
11
+* distributed under the License is distributed on an "AS IS" BASIS,
12
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+* See the License for the specific language governing permissions and
14
+* limitations under the License.
15
+*/
16
+
17
+#import "InfoPlistUtil.h"
18
+
19
+// Plist file name.
20
+NSString *const kGoogleServiceInfoFileName = @"GoogleService-Info";
21
+// Plist file type.
22
+NSString *const kGoogleServiceInfoFileType = @"plist";
23
+NSString *const kGoogleAppIDPlistKey = @"GOOGLE_APP_ID";
24
+
25
+@implementation InfoPlistUtil
26
+
27
++ (BOOL)containsRealServiceInfoPlistInBundle:(NSBundle *)bundle {
28
+  NSString *bundlePath = bundle.bundlePath;
29
+  if (!bundlePath.length) {
30
+    return NO;
31
+  }
32
+
33
+  NSString *plistFilePath = [bundle pathForResource:kGoogleServiceInfoFileName
34
+                                             ofType:kGoogleServiceInfoFileType];
35
+  if (!plistFilePath.length) {
36
+    return NO;
37
+  }
38
+
39
+  NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
40
+  if (!plist) {
41
+    return NO;
42
+  }
43
+
44
+  // Perform a very naive validation by checking to see if the plist has the dummy google app id
45
+  NSString *googleAppID = plist[kGoogleAppIDPlistKey];
46
+  if (!googleAppID.length) {
47
+    return NO;
48
+  }
49
+
50
+  return YES;
51
+}
52
+@end

+ 3
- 0
ios/sdk/src/JitsiMeet.h 查看文件

20
 #import <JitsiMeet/JitsiMeetConferenceOptions.h>
20
 #import <JitsiMeet/JitsiMeetConferenceOptions.h>
21
 #import <JitsiMeet/JitsiMeetLogger.h>
21
 #import <JitsiMeet/JitsiMeetLogger.h>
22
 #import <JitsiMeet/JitsiMeetBaseLogHandler.h>
22
 #import <JitsiMeet/JitsiMeetBaseLogHandler.h>
23
+#import <JitsiMeet/InfoPlistUtil.h>
23
 
24
 
24
 
25
 
25
 @interface JitsiMeet : NSObject
26
 @interface JitsiMeet : NSObject
64
 
65
 
65
 - (JitsiMeetConferenceOptions *_Nonnull)getInitialConferenceOptions;
66
 - (JitsiMeetConferenceOptions *_Nonnull)getInitialConferenceOptions;
66
 
67
 
68
+- (BOOL)isCrashReportingDisabled;
69
+
67
 @end
70
 @end

+ 6
- 1
ios/sdk/src/JitsiMeet.m 查看文件

107
     JitsiMeetConferenceOptions *conferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
107
     JitsiMeetConferenceOptions *conferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
108
         builder.room = [url absoluteString];
108
         builder.room = [url absoluteString];
109
     }];
109
     }];
110
-    
110
+
111
     return [JitsiMeetView setPropsInViews:[conferenceOptions asProps]];
111
     return [JitsiMeetView setPropsInViews:[conferenceOptions asProps]];
112
 }
112
 }
113
 
113
 
132
     return nil;
132
     return nil;
133
 }
133
 }
134
 
134
 
135
+- (BOOL)isCrashReportingDisabled {
136
+    NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"jitsi-default-preferences"];
137
+    return [userDefaults stringForKey:@"isCrashReportingDisabled"];
138
+}
139
+
135
 - (JitsiMeetConferenceOptions *)optionsFromUserActivity:(NSUserActivity *)userActivity {
140
 - (JitsiMeetConferenceOptions *)optionsFromUserActivity:(NSUserActivity *)userActivity {
136
     NSString *activityType = userActivity.activityType;
141
     NSString *activityType = userActivity.activityType;
137
 
142
 

+ 3
- 0
lang/main.json 查看文件

582
     "settingsView": {
582
     "settingsView": {
583
         "advanced": "Advanced",
583
         "advanced": "Advanced",
584
         "alertOk": "OK",
584
         "alertOk": "OK",
585
+        "alertCancel": "Cancel",
585
         "alertTitle": "Warning",
586
         "alertTitle": "Warning",
586
         "alertURLText": "The entered server URL is invalid",
587
         "alertURLText": "The entered server URL is invalid",
587
         "buildInfoSection": "Build Information",
588
         "buildInfoSection": "Build Information",
588
         "conferenceSection": "Conference",
589
         "conferenceSection": "Conference",
589
         "disableCallIntegration": "Disable native call integration",
590
         "disableCallIntegration": "Disable native call integration",
590
         "disableP2P": "Disable Peer-To-Peer mode",
591
         "disableP2P": "Disable Peer-To-Peer mode",
592
+        "disableCrashReporting": "Disable crash reporting",
593
+        "disableCrashReportingWarning": "Are you sure you want to disable crash reporting? The setting will be applied after you restart the app.",
591
         "displayName": "Display name",
594
         "displayName": "Display name",
592
         "email": "Email",
595
         "email": "Email",
593
         "header": "Settings",
596
         "header": "Settings",

+ 5
- 0
package-lock.json 查看文件

14827
         }
14827
         }
14828
       }
14828
       }
14829
     },
14829
     },
14830
+    "react-native-default-preference": {
14831
+      "version": "1.4.2",
14832
+      "resolved": "https://registry.npmjs.org/react-native-default-preference/-/react-native-default-preference-1.4.2.tgz",
14833
+      "integrity": "sha512-kNhBLv8s6kO2gJJFEKM7qew7oRvJnygjgG1CU2ZEY6SlG5qsRX8z1Ms7z1Oo/XB7fVfyXrAoZDGhIvy+uiByrg=="
14834
+    },
14830
     "react-native-immersive": {
14835
     "react-native-immersive": {
14831
       "version": "2.0.0",
14836
       "version": "2.0.0",
14832
       "resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
14837
       "resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",

+ 1
- 0
package.json 查看文件

72
     "react-native-calendar-events": "github:jitsi/react-native-calendar-events#902e6e92d6bae450a6052f76ba4d02f977ffd8f2",
72
     "react-native-calendar-events": "github:jitsi/react-native-calendar-events#902e6e92d6bae450a6052f76ba4d02f977ffd8f2",
73
     "react-native-callstats": "3.61.0",
73
     "react-native-callstats": "3.61.0",
74
     "react-native-collapsible": "1.5.1",
74
     "react-native-collapsible": "1.5.1",
75
+    "react-native-default-preference": "1.4.2",
75
     "react-native-immersive": "2.0.0",
76
     "react-native-immersive": "2.0.0",
76
     "react-native-keep-awake": "4.0.0",
77
     "react-native-keep-awake": "4.0.0",
77
     "react-native-linear-gradient": "2.5.6",
78
     "react-native-linear-gradient": "2.5.6",

+ 16
- 0
react/features/base/settings/functions.native.js 查看文件

2
 
2
 
3
 import { NativeModules } from 'react-native';
3
 import { NativeModules } from 'react-native';
4
 
4
 
5
+import DefaultPreference from 'react-native-default-preference';
6
+
5
 export * from './functions.any';
7
 export * from './functions.any';
6
 
8
 
7
 const { AudioMode } = NativeModules;
9
 const { AudioMode } = NativeModules;
19
         AudioMode.setUseConnectionService(!disabled);
21
         AudioMode.setUseConnectionService(!disabled);
20
     }
22
     }
21
 }
23
 }
24
+
25
+/**
26
+ * Handles changes to the `disableCrashReporting` setting.
27
+ * Stores the value into platform specific default preference file, so at app
28
+ * start-up time it is retrieved on the native side and the crash reporting
29
+ * is enabled/disabled.
30
+ *
31
+ * @param {boolean} disabled - Whether crash reporting is disabled or not.
32
+ * @returns {void}
33
+ */
34
+export function handleCrashReportingChange(disabled: boolean) {
35
+    DefaultPreference.setName('jitsi-default-preferences').then(
36
+        DefaultPreference.set('isCrashReportingDisabled', disabled.toString()));
37
+}

+ 10
- 0
react/features/base/settings/functions.web.js 查看文件

41
  */
41
  */
42
 export function handleCallIntegrationChange(disabled: boolean) { // eslint-disable-line no-unused-vars
42
 export function handleCallIntegrationChange(disabled: boolean) { // eslint-disable-line no-unused-vars
43
 }
43
 }
44
+
45
+/**
46
+ * Handles changes to the `disableCrashReporting` setting.
47
+ * Noop on web.
48
+ *
49
+ * @param {boolean} disabled - Whether crash reporting is disabled or not.
50
+ * @returns {void}
51
+ */
52
+export function handleCrashReportingChange(disabled: boolean) { // eslint-disable-line no-unused-vars
53
+}

+ 15
- 1
react/features/base/settings/middleware.js 查看文件

9
 import { MiddlewareRegistry } from '../redux';
9
 import { MiddlewareRegistry } from '../redux';
10
 
10
 
11
 import { SETTINGS_UPDATED } from './actionTypes';
11
 import { SETTINGS_UPDATED } from './actionTypes';
12
-import { handleCallIntegrationChange } from './functions';
12
+import { handleCallIntegrationChange, handleCrashReportingChange } from './functions';
13
 
13
 
14
 /**
14
 /**
15
  * The middleware of the feature base/settings. Distributes changes to the state
15
  * The middleware of the feature base/settings. Distributes changes to the state
30
         _maybeHandleCallIntegrationChange(action);
30
         _maybeHandleCallIntegrationChange(action);
31
         _maybeSetAudioOnly(store, action);
31
         _maybeSetAudioOnly(store, action);
32
         _updateLocalParticipant(store, action);
32
         _updateLocalParticipant(store, action);
33
+        _maybeCrashReportingChange(action);
33
         break;
34
         break;
34
     case SET_LOCATION_URL:
35
     case SET_LOCATION_URL:
35
         _updateLocalParticipantFromUrl(store);
36
         _updateLocalParticipantFromUrl(store);
84
     }
85
     }
85
 }
86
 }
86
 
87
 
88
+/**
89
+ * Handles a change in the `disableCrashReporting` setting.
90
+ *
91
+ * @param {Object} action - The redux action.
92
+ * @private
93
+ * @returns {void}
94
+ */
95
+function _maybeCrashReportingChange({ settings: { disableCrashReporting } }) {
96
+    if (typeof disableCrashReporting === 'boolean') {
97
+        handleCrashReportingChange(disableCrashReporting);
98
+    }
99
+}
100
+
87
 /**
101
 /**
88
  * Updates {@code startAudioOnly} flag if it's updated in the settings.
102
  * Updates {@code startAudioOnly} flag if it's updated in the settings.
89
  *
103
  *

+ 1
- 0
react/features/base/settings/reducer.js 查看文件

24
     avatarURL: undefined,
24
     avatarURL: undefined,
25
     cameraDeviceId: undefined,
25
     cameraDeviceId: undefined,
26
     disableCallIntegration: undefined,
26
     disableCallIntegration: undefined,
27
+    disableCrashReporting: undefined,
27
     disableP2P: undefined,
28
     disableP2P: undefined,
28
     displayName: undefined,
29
     displayName: undefined,
29
     email: undefined,
30
     email: undefined,

+ 70
- 1
react/features/settings/components/native/SettingsView.js 查看文件

36
      */
36
      */
37
     disableP2P: boolean,
37
     disableP2P: boolean,
38
 
38
 
39
+    /**
40
+     * State variable for the disable crash reporting switch.
41
+     */
42
+    disableCrashReporting: boolean,
43
+
39
     /**
44
     /**
40
      * State variable for the display name field.
45
      * State variable for the display name field.
41
      */
46
      */
84
         super(props);
89
         super(props);
85
         const {
90
         const {
86
             disableCallIntegration,
91
             disableCallIntegration,
92
+            disableCrashReporting,
87
             disableP2P,
93
             disableP2P,
88
             displayName,
94
             displayName,
89
             email,
95
             email,
94
 
100
 
95
         this.state = {
101
         this.state = {
96
             disableCallIntegration,
102
             disableCallIntegration,
103
+            disableCrashReporting,
97
             disableP2P,
104
             disableP2P,
98
             displayName,
105
             displayName,
99
             email,
106
             email,
107
         this._onBlurServerURL = this._onBlurServerURL.bind(this);
114
         this._onBlurServerURL = this._onBlurServerURL.bind(this);
108
         this._onClose = this._onClose.bind(this);
115
         this._onClose = this._onClose.bind(this);
109
         this._onDisableCallIntegration = this._onDisableCallIntegration.bind(this);
116
         this._onDisableCallIntegration = this._onDisableCallIntegration.bind(this);
117
+        this._onDisableCrashReporting = this._onDisableCrashReporting.bind(this);
110
         this._onDisableP2P = this._onDisableP2P.bind(this);
118
         this._onDisableP2P = this._onDisableP2P.bind(this);
111
         this._onShowAdvanced = this._onShowAdvanced.bind(this);
119
         this._onShowAdvanced = this._onShowAdvanced.bind(this);
112
         this._setURLFieldReference = this._setURLFieldReference.bind(this);
120
         this._setURLFieldReference = this._setURLFieldReference.bind(this);
285
         });
293
         });
286
     }
294
     }
287
 
295
 
296
+    _onDisableCrashReporting: (boolean) => void;
297
+
298
+    /**
299
+     * Handles the disable crash reporting change event.
300
+     *
301
+     * @param {boolean} disableCrashReporting - The new value
302
+     * option.
303
+     * @private
304
+     * @returns {void}
305
+     */
306
+    _onDisableCrashReporting(disableCrashReporting) {
307
+        if (disableCrashReporting) {
308
+            this._showCrashReportingDisableAlert();
309
+        } else {
310
+            this._disableCrashReporting(disableCrashReporting);
311
+        }
312
+    }
313
+
288
     _onClose: () => void;
314
     _onClose: () => void;
289
 
315
 
290
     /**
316
     /**
367
      * @returns {React$Element}
393
      * @returns {React$Element}
368
      */
394
      */
369
     _renderAdvancedSettings() {
395
     _renderAdvancedSettings() {
370
-        const { disableCallIntegration, disableP2P, showAdvanced } = this.state;
396
+        const { disableCallIntegration, disableP2P, disableCrashReporting, showAdvanced } = this.state;
371
 
397
 
372
         if (!showAdvanced) {
398
         if (!showAdvanced) {
373
             return (
399
             return (
397
                         onValueChange = { this._onDisableP2P }
423
                         onValueChange = { this._onDisableP2P }
398
                         value = { disableP2P } />
424
                         value = { disableP2P } />
399
                 </FormRow>
425
                 </FormRow>
426
+                {AppInfo.GOOGLE_SERVICES_ENABLED && (
427
+                    <FormRow
428
+                        fieldSeparator = { true }
429
+                        label = 'settingsView.disableCrashReporting'>
430
+                        <Switch
431
+                            onValueChange = { this._onDisableCrashReporting }
432
+                            value = { disableCrashReporting } />
433
+                    </FormRow>
434
+                )}
400
             </>
435
             </>
401
         );
436
         );
402
     }
437
     }
436
         );
471
         );
437
     }
472
     }
438
 
473
 
474
+    /**
475
+     * Shows an alert warning the user about disabling crash reporting.
476
+     *
477
+     * @returns {void}
478
+     */
479
+    _showCrashReportingDisableAlert() {
480
+        const { t } = this.props;
481
+
482
+        Alert.alert(
483
+            t('settingsView.alertTitle'),
484
+            t('settingsView.disableCrashReportingWarning'),
485
+            [
486
+                {
487
+                    onPress: () => this._disableCrashReporting(true),
488
+                    text: t('settingsView.alertOk')
489
+                },
490
+                {
491
+                    text: t('settingsView.alertCancel')
492
+                }
493
+            ]
494
+        );
495
+    }
496
+
439
     _updateSettings: (Object) => void;
497
     _updateSettings: (Object) => void;
498
+
499
+    /**
500
+     * Updates the settings and sets state for disableCrashReporting.
501
+     *
502
+     * @param {boolean} disableCrashReporting - Whether crash reporting is disabled or not.
503
+     * @returns {void}
504
+     */
505
+    _disableCrashReporting(disableCrashReporting) {
506
+        this._updateSettings({ disableCrashReporting });
507
+        this.setState({ disableCrashReporting });
508
+    }
440
 }
509
 }
441
 
510
 
442
 /**
511
 /**

Loading…
取消
儲存