From 5a43b837ae7d4e72542c3beac027af466c335db8 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Thu, 14 Mar 2019 12:40:43 +0100 Subject: [PATCH] disable crashlythics by default --- Classes/LinphoneAppDelegate.m | 5 ++- Classes/Utils/Log.m | 28 ++++++------- GoogleService-Info.plist | 24 +++++++++++ Podfile | 28 +++++++++++-- README.md | 16 +++++++ linphone.xcodeproj/project.pbxproj | 67 +++++++++++++----------------- 6 files changed, 110 insertions(+), 58 deletions(-) create mode 100644 GoogleService-Info.plist diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 38027be37..2940b9515 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -30,7 +30,10 @@ #include "LinphoneManager.h" #include "linphone/linphonecore.h" + +#ifdef USE_CRASHLYTHICSS #include "FIRApp.h" +#endif @implementation LinphoneAppDelegate @@ -234,7 +237,7 @@ #pragma deploymate pop - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { -#ifdef USE_FIREBASE +#ifdef USE_CRASHLYTHICSS NSString *pathForFile=[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"GoogleService-Info.plist"]; if ([[NSFileManager defaultManager] fileExistsAtPath:pathForFile]){ // If GoogleService-Info.plist doesn't exist, not call this function avoiding a crash. diff --git a/Classes/Utils/Log.m b/Classes/Utils/Log.m index 3484bfd08..eb996cb8b 100644 --- a/Classes/Utils/Log.m +++ b/Classes/Utils/Log.m @@ -20,17 +20,15 @@ #import "Log.h" #import #import + +#ifdef USE_CRASHLYTHICSS #import +#endif @implementation Log #define FILE_SIZE 17 #define DOMAIN_SIZE 3 -#ifdef USE_FIREBASE -#define USE_CRASHLYTICS TRUE -#else -#define USE_CRASHLYTICS FALSE -#endif + (NSString *)cacheDirectory { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); @@ -120,20 +118,20 @@ void linphone_iphone_log_handler(const char *domain, OrtpLogLevel lev, const cha for (int i = 0; i < myWords.count; i++) { NSString *tab = i > 0 ? @"\t" : @""; if (((NSString *)myWords[i]).length > 0) { - if (USE_CRASHLYTICS) { - CLSNSLog(@"[%@] %@%@", lvl, tab, (NSString *)myWords[i]); - } else { - NSLog(@"[%@] %@%@", lvl, tab, (NSString *)myWords[i]); - } +#ifdef USE_CRASHLYTHICSS + CLSNSLog(@"[%@] %@%@", lvl, tab, (NSString *)myWords[i]); +#else + NSLog(@"[%@] %@%@", lvl, tab, (NSString *)myWords[i]); +#endif } } } else { - if (USE_CRASHLYTICS) { - CLSNSLog(@"[%@] %@", lvl, [formatedString stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"]); - } else { - NSLog(@"[%@] %@", lvl, [formatedString stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"]); - } +#ifdef USE_CRASHLYTHICSS + CLSNSLog(@"[%@] %@", lvl, [formatedString stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"]); +#else + NSLog(@"[%@] %@", lvl, [formatedString stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"]); +#endif } } diff --git a/GoogleService-Info.plist b/GoogleService-Info.plist new file mode 100644 index 000000000..e3fe76c08 --- /dev/null +++ b/GoogleService-Info.plist @@ -0,0 +1,24 @@ + + + + + AD_UNIT_ID_FOR_BANNER_TEST + + AD_UNIT_ID_FOR_INTERSTITIAL_TEST + + CLIENT_ID + + REVERSED_CLIENT_ID + + API_KEY + + GCM_SENDER_ID + + BUNDLE_ID + + PROJECT_ID + + STORAGE_BUCKET + + + diff --git a/Podfile b/Podfile index 0ee94ca65..ac77885aa 100644 --- a/Podfile +++ b/Podfile @@ -10,10 +10,13 @@ def basic_pods pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # loacl sdk end - pod 'Firebase/Core' - pod 'Fabric', '~> 1.9.0' - pod 'Crashlytics', '~> 3.12.0' - pod 'Firebase/Performance' + if not ENV['USE_CRASHLYTHICS'].nil? + # activate crashlythics + pod 'Firebase/Core' + pod 'Fabric', '~> 1.9.0' + pod 'Crashlytics', '~> 3.12.0' + pod 'Firebase/Performance' + end end target 'latestCallsWidget' do @@ -75,3 +78,20 @@ target 'richNotifications' do # Pods for richNotifications end + +post_install do |installer| + app_project = Xcodeproj::Project.open(Dir.glob("*.xcodeproj")[0]) + app_project.native_targets.each do |target| + if target.name == 'linphone' + target.build_configurations.each do |config| + if ENV['USE_CRASHLYTHICS'].nil? + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) DEBUG=1' + else + # activate crashlythics + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) DEBUG=1 USE_CRASHLYTHICSS=1' + end + app_project.save + end + end + end +end diff --git a/README.md b/README.md index f2c677ccb..d13ee4c11 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,22 @@ See: https://developer.apple.com/library/archive/documentation/DeveloperTools/Co -Then open linphone.xcworkspace (instead of linphone.xcodeproj) to install the app. +# Use crashlythics + +We've integrated the crashlythics into liphone-iphone, which can automatically send us a crash report. It is disabled by default. +To activate it : + +-Download GoogleService-Info.plist from : + https://console.firebase.google.com/project/linphone-iphone/settings/general/ios:org.linphone.phone + You may not have access to this website because it is restricted to certain developers. + +-Replace GoogleService-Info.plist for this project with the file you downloaded. + +-Rebuild the project: +* USE_CRASHLYTHICS=true pod install + +-Then open linphone.xcworkspace (instead of linphone.xcodeproj) to install the app. + # Quick UI reference - The app is contained in a window, which resides in the MainStoryboard file. diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 2f57c21f9..2f2531557 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -125,7 +125,7 @@ 61AE364F20C00B370089D9D3 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AE364E20C00B370089D9D3 /* ShareViewController.m */; }; 61AE365220C00B370089D9D3 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61AE365020C00B370089D9D3 /* MainInterface.storyboard */; }; 61AE365620C00B370089D9D3 /* linphoneExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 61AE364B20C00B370089D9D3 /* linphoneExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 61AEBEA321906AFC00F35E7F /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + 61AEBEA321906AFC00F35E7F /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; 61AEBEBD2191990A00F35E7F /* DevicesListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AEBEBC2191990A00F35E7F /* DevicesListView.m */; }; 61AEBEBF2191991F00F35E7F /* DevicesListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 61AEBEBE2191991F00F35E7F /* DevicesListView.xib */; }; 61AEBEC22191D7C800F35E7F /* UIDevicesDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AEBEC12191D7C800F35E7F /* UIDevicesDetails.m */; }; @@ -665,7 +665,7 @@ 63E27A321C4FECD000D332AE /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63E27A311C4FECD000D332AE /* LaunchScreen.xib */; }; 63E27A521C50EDB000D332AE /* hold.mkv in Resources */ = {isa = PBXBuildFile; fileRef = 63E27A511C50EB2700D332AE /* hold.mkv */; }; 63E59A3F1ADE70D900646FB3 /* InAppProductsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E59A3E1ADE70D900646FB3 /* InAppProductsManager.m */; }; - 63E802DB1C625AEF000D5509 /* (null) in Resources */ = {isa = PBXBuildFile; }; + 63E802DB1C625AEF000D5509 /* BuildFile in Resources */ = {isa = PBXBuildFile; }; 63EC8D391D7438660066547B /* AssistantLinkView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63EC8D3B1D7438660066547B /* AssistantLinkView.xib */; }; 63F1DF441BCE618E00EDED90 /* UIAddressTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF431BCE618E00EDED90 /* UIAddressTextField.m */; }; 63F1DF4B1BCE983200EDED90 /* CallConferenceTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF4A1BCE983200EDED90 /* CallConferenceTableView.m */; }; @@ -2058,7 +2058,7 @@ buildActionMask = 2147483647; files = ( 6180D6FE21EE41A800AD9CB6 /* QuickLook.framework in Frameworks */, - 61AEBEA321906AFC00F35E7F /* (null) in Frameworks */, + 61AEBEA321906AFC00F35E7F /* BuildFile in Frameworks */, D37DC7181594AF3400B2A5EB /* MessageUI.framework in Frameworks */, 61F1997520C6B1D5006B069A /* AVKit.framework in Frameworks */, 249660951FD6A35F001D55AA /* Photos.framework in Frameworks */, @@ -2449,7 +2449,7 @@ path = LinphoneUI; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + 29B97314FDCFA39411CA2CEA = { isa = PBXGroup; children = ( 8C23BCB71D82AAC3005F19BB /* linphone.entitlements */, @@ -3707,7 +3707,7 @@ zh_CN, fr, ); - mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + mainGroup = 29B97314FDCFA39411CA2CEA; productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */; projectDirPath = ""; projectReferences = ( @@ -3822,7 +3822,7 @@ 633FEED41D3CD55A0014B822 /* numpad_7_default@2x.png in Resources */, 633FEEE01D3CD55A0014B822 /* numpad_8_over~ipad@2x.png in Resources */, 633FEDDC1D3CD5590014B822 /* call_start_body_disabled~ipad.png in Resources */, - 63E802DB1C625AEF000D5509 /* (null) in Resources */, + 63E802DB1C625AEF000D5509 /* BuildFile in Resources */, 633FEE2E1D3CD5590014B822 /* color_F.png in Resources */, 633FEDC51D3CD5590014B822 /* call_hangup_disabled@2x.png in Resources */, 633FEEDF1D3CD55A0014B822 /* numpad_8_over~ipad.png in Resources */, @@ -4481,11 +4481,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/bctoolbox-tester.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/bctoolbox.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/belcard.framework", @@ -4502,14 +4498,9 @@ "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/mswebrtc.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/msx264.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/ortp.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox-tester.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belcard.framework", @@ -4526,11 +4517,10 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mswebrtc.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msx264.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ortp.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 4E500A814E95DC4FBA8F1F8D /* [CP] Check Pods Manifest.lock */ = { @@ -4584,7 +4574,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Fabric/run\"\n"; + shellScript = "if USE_CRASHLYTHICS; then\n ${PODS_ROOT}/Fabric/run\nfi\n"; }; 61C84B4200D58AABECCE326E /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; @@ -4594,7 +4584,7 @@ inputFileListPaths = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-resources.sh", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Resources/liblinphone_tester", ); name = "[CP] Copy Pods Resources"; @@ -4605,7 +4595,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-resources.sh\"\n"; showEnvVarsInLog = 0; }; 63DCC71D1A07B08E00916627 /* Run Script */ = { @@ -4620,7 +4610,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = $SRCROOT/Tools/git_version.sh; + shellScript = "$SRCROOT/Tools/git_version.sh\n"; }; 7FF8A712621CDB927700B916 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -4651,7 +4641,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = $SRCROOT/Tools/deploy.sh; + shellScript = "$SRCROOT/Tools/deploy.sh\n"; }; 97C36AF90B8C564FC115304D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -4695,11 +4685,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${PODS_ROOT}/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-frameworks.sh", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/bctoolbox-tester.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/bctoolbox.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/belcard.framework", @@ -4716,14 +4702,9 @@ "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/mswebrtc.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/msx264.framework", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/ortp.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox-tester.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belcard.framework", @@ -4740,11 +4721,10 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mswebrtc.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msx264.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ortp.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; A73A24C4B99B11012FDAA7CF /* [CP] Copy Pods Resources */ = { @@ -4755,7 +4735,7 @@ inputFileListPaths = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-linphone/Pods-linphone-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-resources.sh", "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Resources/liblinphone_tester", ); name = "[CP] Copy Pods Resources"; @@ -4766,7 +4746,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-linphone/Pods-linphone-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-resources.sh\"\n"; showEnvVarsInLog = 0; }; F43E063274A630F02AB2AAEC /* [CP] Check Pods Manifest.lock */ = { @@ -5596,7 +5576,6 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "DEBUG=1", - "USE_FIREBASE=1", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; @@ -5696,6 +5675,10 @@ GCC_OPTIMIZATION_LEVEL = s; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = linphone_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES; @@ -5794,6 +5777,10 @@ GCC_OPTIMIZATION_LEVEL = s; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = linphone_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES; @@ -5892,6 +5879,10 @@ GCC_OPTIMIZATION_LEVEL = s; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = linphone_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES;