├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── ISSUE_TEMPLATE.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── firebase-crashlytics.gradle ├── package.json ├── plugin.xml ├── src ├── android │ └── uk │ │ └── co │ │ └── reallysmall │ │ └── cordova │ │ └── plugin │ │ └── firebase │ │ └── crashlytics │ │ ├── ActionHandler.java │ │ ├── CrashHandler.java │ │ ├── FirebaseCrashlyticsPlugin.java │ │ ├── InitialiseHandler.java │ │ ├── LogErrorHandler.java │ │ ├── LogExceptionHandler.java │ │ ├── LogHandler.java │ │ ├── LogPriorityHandler.java │ │ ├── SetBoolHandler.java │ │ ├── SetDoubleHandler.java │ │ ├── SetFloatHandler.java │ │ ├── SetIntHandler.java │ │ ├── SetStringHandler.java │ │ └── SetUserIdentifierHandler.java └── ios │ ├── FirebaseCrashlyticsPlugin.h │ └── FirebaseCrashlyticsPlugin.m ├── types └── index.d.ts └── www ├── browser └── crashlytics.js └── crashlytics.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /test-reports/jshint.xml 3 | /package-lock.json 4 | .idea 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at richard.windley@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | We want to make contributing to this project as easy and transparent as possible, whether it's: 3 | 4 | - Reporting a bug 5 | - Discussing the current state of the code 6 | - Submitting a fix 7 | - Proposing new features 8 | 9 | ## We Develop with Github 10 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 11 | 12 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests 13 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: 14 | 15 | 1. Fork the repo and create your branch from `master`. 16 | 2. If you've added code that should be tested, add tests. 17 | 3. If you've changed APIs, update the documentation. 18 | 4. Ensure the test suite passes. 19 | 5. Make sure your code lints. 20 | 6. Issue that pull request! 21 | 22 | ## Any contributions you make will be under the licence specified in the LICENSE file 23 | In short, when you submit code changes, your submissions are understood to be under the same License that covers the project. Feel free to contact the maintainers if that's a concern. 24 | 25 | ## Report bugs using Github's [issues](https://github.com/ReallySmallSoftware/cordova-plugin-firebase-crashlytics/issues) 26 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/ReallySmallSoftware/cordova-plugin-firebase-crashlytics/issues/new); it's that easy! 27 | 28 | ## Write bug reports with detail, background, and sample code where appropriate 29 | Make sure at the very least you provide the information in the [issue template](https://github.com/ReallySmallSoftware/cordova-plugin-firebase-crashlytics/blob/master/ISSUE_TEMPLATE.md) 30 | 31 | ## Use a Consistent Coding Style 32 | The coding style should match what is present in the code currently. Do not reformat entire files. 33 | 34 | ## References 35 | This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md) 36 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | jshint: { 6 | options: { 7 | reporter: require("jshint-junit-reporter"), 8 | reporterOutput: "test-reports/jshint.xml", 9 | curly: true, 10 | eqeqeq: true, 11 | immed: true, 12 | latedef: true, 13 | newcap: true, 14 | noarg: true, 15 | sub: true, 16 | undef: true, 17 | boss: true, 18 | eqnull: true, 19 | node: true, 20 | es5: false, 21 | globals: { 22 | jasmine: false, 23 | describe: false, 24 | beforeEach: false, 25 | afterEach: false, 26 | expect: false, 27 | it: false, 28 | spyOn: false, 29 | $: false, 30 | cordova: false, 31 | launchnavigator: false, 32 | window: false, 33 | document: false, 34 | ons: false, 35 | navigator: false, 36 | google: false, 37 | FCMPlugin: false, 38 | device: false, 39 | plugins: false, 40 | addFixture: false, 41 | truncateSql: false 42 | } 43 | }, 44 | all: ['Gruntfile.js', 'www/**/*.js'] 45 | } 46 | }); 47 | 48 | grunt.loadNpmTasks('grunt-contrib-jshint'); 49 | }; 50 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 10 | ## Specifications 11 | 12 | - Plugin version: 13 | - Framework: 14 | - Framework version: 15 | - Operating system: 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Richard Windley 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | ## Proposed Changes 4 | 5 | - 6 | - 7 | - 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cordova Firebase Crashlytics Plugin 2 | 3 | A Google Firebase Crashlytics plugin to enable capture of crash reports. 4 | 5 | # What is Crashlytics? 6 | 7 | From the Google documentation (https://firebase.google.com/products/crashlytics/): 8 | 9 | > Firebase Crashlytics helps you track, prioritize, and fix stability issues that erode app quality, in realtime. Spend less time triaging and troubleshooting crashes and more time building app features that delight users 10 | 11 | # Supported platforms 12 | This plugin supports the following platforms: 13 | 14 | - Android 15 | - iOS (untested) 16 | - Browser (for testing only) 17 | 18 | # Installation 19 | 20 | `cordova plugin add cordova-plugin-firebase-crashlytics --variable ANDROID_FIREBASE_CORE_VERSION=16.0.0 --save` 21 | 22 | or 23 | 24 | `phonegap plugin add cordova-plugin-firebase-crashlytics --variable ANDROID_FIREBASE_CORE_VERSION=16.0.0` 25 | 26 | Omitting `FIREBASE_VERSION` will use a default value. 27 | 28 | ## Firebase configuration 29 | ### Android 30 | 31 | You must ensure that `google-services.json` is put in the correct location. This can be achieved using the following in your `config.xml`: 32 | 33 | ``` 34 | 35 | 36 | 37 | ``` 38 | 39 | #### Dependencies 40 | ##### cordova-support-google-services 41 | 42 | In order to ensure Firebase initialises correctly on Android this plugin can be used. This is not automatically added as a dependency to allow for the configuration it performs to be done manually if desired. 43 | 44 | ### iOS 45 | iOS requires `GoogleService-Info.plist` is put in the correct location. Similarly this can be done as follows: 46 | ``` 47 | 48 | 49 | 50 | ``` 51 | 52 | #### Podfile configuration 53 | At this time it is necessary to manually add the `use_frameworks!` directive to this file. 54 | 55 | #### Keychain Sharing Capability 56 | If using multiple Firebase plugins it may be necessary to enable this. 57 | 58 | # How to use it 59 | Simply add the plugin to get the default Crashlytics functionality. Note that crashes and logged exceptions will only be reported when the application restarts. 60 | 61 | In order to log caught exceptions the following can be used: 62 | 63 | ``` 64 | var crashlytics = FirebaseCrashlytics.initialise(); 65 | crashlytics.logException("my caught exception"); 66 | ``` 67 | 68 | ## Methods 69 | ### crash() 70 | Generate a forced crash. Visible in console after restart of application. 71 | 72 | ### logPriority(priority, tag, message) 73 | Log a priority message. Will only be logged in the event of a crash. 74 | 75 | Available priorities are compatible with most Android [Log constants](https://developer.android.com/reference/android/util/Log#constants_2): 76 | - FirebaseCrashlytics.LOG.VERBOSE 77 | - FirebaseCrashlytics.LOG.DEBUG 78 | - FirebaseCrashlytics.LOG.INFO 79 | - FirebaseCrashlytics.LOG.WARN 80 | - FirebaseCrashlytics.LOG.ERROR 81 | 82 | Example usage 83 | ```js 84 | crashlytics.logPriority(FirebaseCrashlytics.LOG.WARN, 'dashboard', 'This should not happened') 85 | ``` 86 | 87 | ### initialise(hasConsent):Promise 88 | Initialise Crashlytics and send any logs files if the user has given consent, otherwise delete them. 89 | 90 | Returns a true if there was a previous crash. 91 | 92 | ### log(message) 93 | Log a message. Will only be logged in the event of a crash. 94 | 95 | ### logException(message) 96 | Log when a handled exception has happened. Visible in console after restart of application. 97 | 98 | ### setString(key, value) 99 | Set extra key/value string value. Will only be logged in the event of a crash. 100 | 101 | ### setBool(key, value) 102 | Set extra key/value bool value. Will only be logged in the event of a crash. 103 | 104 | ### setDouble(key, value) 105 | Set extra key/value double value. Will only be logged in the event of a crash. 106 | 107 | ### setFloat(key, value) 108 | Set extra key/value float value. Will only be logged in the event of a crash. 109 | 110 | ### setInt(key, value) 111 | Set extra key/value integer value. Will only be logged in the event of a crash. 112 | 113 | ### setUserIdentifier(identifier) 114 | Set the identifier for the user. Take care when using this method and ensure you privacy policy is updated accordingly. 115 | 116 | ## Typescript 117 | Support is now included for typescript. Use the following to reference the typescript definitions: 118 | 119 | ``` 120 | /// 121 | 122 | private static crashlytics: FirebaseCrashlytics.FirebaseCrashlytics = FirebaseCrashlytics.initialise(); 123 | crashlytics.logException("my message"); 124 | ``` 125 | 126 | You may also need to add an external to webpack.config.ls: 127 | 128 | ``` 129 | externals: { 130 | 'cordova-plugin-firebase-crashlytics': "cordova-plugin-firebase-crashlytics" 131 | '/exec':"cordova/exec" 132 | }, 133 | ``` 134 | 135 | ## 1.1.0 136 | - Update dependencies 137 | - Refactor Android and iOS code 138 | - Add initialise method to give consent for uploading logs 139 | 140 | ## 1.0.0 141 | - Added types 142 | - Updated dependencies for iOS 143 | - Updated dependencies for Android 144 | 145 | ## 0.1.0 146 | - Dependency updates 147 | - Addition of log priority constants 148 | 149 | ## 0.0.9 150 | - Update Android Firebase dependency 151 | - Fix podspec for cordova-ios 5 152 | - Fix xcode issue 153 | 154 | ## 0.0.8 155 | - Remove podspec version for firebase 156 | 157 | ## 0.0.7 158 | - Update Android dependency versions 159 | - Update iOS dependency versions 160 | - Update plugin dependencies 161 | - WARNING: The Android update may require you to update com.google.gms:google-services to 4.0.0, com.android.tools.build:gradle to 3.1.2 and gradle to 4.4.4 (look in platforms/android/cordova/lib/builders/GradleBuilder.js) 162 | 163 | ## 0.0.6 164 | - Add SetUserIdentifier() 165 | - Fix iOS set() methods with wrong types 166 | 167 | ## 0.0.5 168 | - Merge back in some iOS updates (https://github.com/kanodeveloper/cordova-plugin-firebase-crashlytics-ka) 169 | - Add xcode npm dependency 170 | 171 | ## 0.0.4 172 | - Updated gradle dependencies 173 | 174 | ## 0.0.3 175 | - Fix typo in README 176 | - Added embarrassing fix for logException() 177 | 178 | ## 0.0.2 179 | - Add grunt to run jshint 180 | - Fix some grunt warnings 181 | 182 | ## 0.0.1 183 | - Initial release 184 | -------------------------------------------------------------------------------- /firebase-crashlytics.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath 'com.google.gms:google-services:4.3.3' 8 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | } 16 | } 17 | 18 | ext.postBuildExtras = { 19 | apply plugin: com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-firebase-crashlytics", 3 | "version": "1.2.0", 4 | "cordova": { 5 | "id": "cordova-plugin-firebase-crashlytics", 6 | "platforms": [ 7 | "android", 8 | "ios", 9 | "browser" 10 | ] 11 | }, 12 | "types": "./types/index.d.ts", 13 | "engines": { 14 | "cordovaDependencies": { 15 | "0.0.7": { 16 | "cordova": ">=7.0.0", 17 | "cordova-android": ">=8.0.0", 18 | "cordova-ios": ">=5.0.0" 19 | } 20 | } 21 | }, 22 | "description": "A Google Firebase Crashlytics plugin", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/ReallySmallSoftware/cordova-plugin-firebase-crashlytics.git" 26 | }, 27 | "author": "Richard Windley (http://www.reallysmall.co.uk)", 28 | "license": "Apache-2.0", 29 | "bugs": { 30 | "url": "https://github.com/ReallySmallSoftware/cordova-plugin-firebase-crashlytics/issues" 31 | }, 32 | "homepage": "https://github.com/ReallySmallSoftware/cordova-plugin-firebase-crashlytics", 33 | "keywords": [ 34 | "ecosystem:cordova", 35 | "cordova-android", 36 | "cordova-ios", 37 | "cordova-browser" 38 | ], 39 | "dependencies": { 40 | "xcode": "^1.0.0" 41 | }, 42 | "devDependencies": { 43 | "grunt": "^1.0.2", 44 | "grunt-contrib-jshint": "^1.1.0", 45 | "jshint-junit-reporter": "^0.2.3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cordova Firebase Crashlytics Plugin 5 | Google Firebase Crashlytics 6 | MIT 7 | cloud, crash, reporting 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/ActionHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import org.apache.cordova.CallbackContext; 4 | import org.apache.cordova.CordovaInterface; 5 | import org.json.JSONArray; 6 | 7 | public interface ActionHandler { 8 | boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext); 9 | } 10 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/CrashHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 4 | 5 | import org.apache.cordova.CallbackContext; 6 | import org.apache.cordova.CordovaInterface; 7 | import org.json.JSONArray; 8 | 9 | public class CrashHandler implements ActionHandler { 10 | @Override 11 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 12 | 13 | cordova.getActivity().runOnUiThread(new Runnable() { 14 | @Override 15 | public void run() { 16 | throw new RuntimeException(); 17 | } 18 | }); 19 | 20 | return true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/FirebaseCrashlyticsPlugin.java: -------------------------------------------------------------------------------- 1 | /** 2 | */ 3 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 4 | 5 | import android.util.Log; 6 | 7 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 8 | 9 | import org.apache.cordova.CallbackContext; 10 | import org.apache.cordova.CordovaInterface; 11 | import org.apache.cordova.CordovaPlugin; 12 | import org.apache.cordova.CordovaWebView; 13 | import org.json.JSONArray; 14 | import org.json.JSONException; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | public class FirebaseCrashlyticsPlugin extends CordovaPlugin { 20 | static final String TAG = "FBCrashlyticsPlugin"; 21 | private Map handlers = new HashMap(); 22 | 23 | public void initialize(CordovaInterface cordova, CordovaWebView webView) { 24 | super.initialize(cordova, webView); 25 | 26 | handlers.put("crash", new CrashHandler()); 27 | handlers.put("logPriority", new LogPriorityHandler()); 28 | handlers.put("log", new LogHandler()); 29 | handlers.put("setString", new SetStringHandler()); 30 | handlers.put("setBool", new SetBoolHandler()); 31 | handlers.put("setDouble", new SetDoubleHandler()); 32 | handlers.put("setFloat", new SetFloatHandler()); 33 | handlers.put("setInt", new SetIntHandler()); 34 | handlers.put("logException", new LogExceptionHandler()); 35 | handlers.put("setUserIdentifier", new SetUserIdentifierHandler()); 36 | handlers.put("logError", new LogErrorHandler()); 37 | handlers.put("initialise", new InitialiseHandler()); 38 | 39 | Log.d(TAG, "Initializing FBFirebaseCrashlyticsPlugin"); 40 | } 41 | 42 | public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { 43 | Log.d(TAG, action); 44 | 45 | if (handlers.containsKey(action)) { 46 | return handlers.get(action).handle(args, this.cordova, callbackContext); 47 | } 48 | 49 | return false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/InitialiseHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 4 | 5 | import org.apache.cordova.CallbackContext; 6 | import org.apache.cordova.CordovaInterface; 7 | import org.json.JSONArray; 8 | import org.apache.cordova.PluginResult; 9 | 10 | public class InitialiseHandler implements ActionHandler { 11 | @Override 12 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 13 | 14 | boolean result = false; 15 | 16 | if (FirebaseCrashlytics.getInstance().didCrashOnPreviousExecution()) { 17 | result = true; 18 | } 19 | 20 | callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); 21 | 22 | return true; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/LogErrorHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONObject; 11 | import org.json.JSONException; 12 | 13 | public class LogErrorHandler implements ActionHandler { 14 | @Override 15 | public boolean handle(final JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 16 | cordova.getActivity().runOnUiThread(new Runnable() { 17 | @Override 18 | public void run() { 19 | JavascriptException exception = null; 20 | 21 | try { 22 | final String msg = args.getString(0); 23 | final JSONArray stl = args.getJSONArray(1); 24 | final StackTraceLine[] stackTraceLines = getStackTraceLines(stl); 25 | 26 | exception = new JavascriptException(msg, stackTraceLines); 27 | } 28 | catch(JSONException e) { 29 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Unable to convert args to Exception object", e); 30 | } 31 | 32 | if(exception != null) { 33 | FirebaseCrashlytics.getInstance().recordException(exception); 34 | } 35 | } 36 | }); 37 | 38 | return true; 39 | } 40 | 41 | private StackTraceLine[] getStackTraceLines(JSONArray array) throws JSONException { 42 | final int length = array.length(); 43 | 44 | StackTraceLine[] stackTraceLines = new StackTraceLine[length]; 45 | for(int i = 0; i < length; i ++) { 46 | JSONObject json = array.getJSONObject(i); 47 | stackTraceLines[i] = StackTraceLine.fromJSONObject(json); 48 | } 49 | 50 | return stackTraceLines; 51 | } 52 | 53 | private static class JavascriptException extends Exception { 54 | public JavascriptException(String message, StackTraceLine[] stackTraceLines) { 55 | super(message); 56 | StackTraceElement[] stackTrace = new StackTraceElement[stackTraceLines.length]; 57 | for(int i = 0; i < stackTraceLines.length; i++) { 58 | stackTrace[i] = new StackTraceElement( 59 | stackTraceLines[i].className, 60 | stackTraceLines[i].functionName, 61 | stackTraceLines[i].fileName, 62 | stackTraceLines[i].lineNumber 63 | ); 64 | } 65 | 66 | setStackTrace(stackTrace); 67 | } 68 | } 69 | 70 | private static class StackTraceLine { 71 | public String className; 72 | public String functionName; 73 | public String fileName; 74 | public int lineNumber; 75 | 76 | private StackTraceLine() { 77 | } 78 | 79 | public static StackTraceLine fromJSONObject(JSONObject json) throws JSONException { 80 | StackTraceLine sl = new StackTraceLine(); 81 | 82 | sl.className = json.optString("className", "<>"); 83 | sl.functionName = json.optString("functionName", "<>"); 84 | sl.fileName = json.getString("fileName"); 85 | sl.lineNumber = json.getInt("lineNumber"); 86 | 87 | return sl; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/LogExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class LogExceptionHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(final JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | cordova.getActivity().runOnUiThread(new Runnable() { 16 | @Override 17 | public void run() { 18 | try { 19 | final String msg = args.getString(0); 20 | 21 | Exception exception = new Exception(msg); 22 | 23 | FirebaseCrashlytics.getInstance().recordException(exception); 24 | } catch (JSONException e) { 25 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error logging exception", e); 26 | } 27 | } 28 | }); 29 | 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/LogHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class LogHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(final JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | cordova.getActivity().runOnUiThread(new Runnable() { 16 | @Override 17 | public void run() { 18 | try { 19 | final String msg = args.getString(0); 20 | 21 | FirebaseCrashlytics.getInstance().log(msg); 22 | } catch (JSONException e) { 23 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error logging", e); 24 | } 25 | } 26 | }); 27 | 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/LogPriorityHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class LogPriorityHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(final JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | cordova.getActivity().runOnUiThread(new Runnable() { 16 | @Override 17 | public void run() { 18 | try { 19 | final Integer priority = args.getInt(0); 20 | final String tag = args.getString(1); 21 | final String msg = args.getString(2); 22 | 23 | FirebaseCrashlytics.getInstance().log(msg); 24 | } catch (JSONException e) { 25 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error logging with priority", e); 26 | } 27 | } 28 | }); 29 | 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/SetBoolHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class SetBoolHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | try { 16 | final String key = args.getString(0); 17 | final Boolean value = args.getBoolean(1); 18 | 19 | FirebaseCrashlytics.getInstance().setCustomKey(key, value); 20 | } catch (JSONException e) { 21 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error setting bool", e); 22 | } 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/SetDoubleHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class SetDoubleHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | try { 16 | final String key = args.getString(0); 17 | final Double value = args.getDouble(1); 18 | 19 | FirebaseCrashlytics.getInstance().setCustomKey(key, value); 20 | } catch (JSONException e) { 21 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error setting double", e); 22 | } 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/SetFloatHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class SetFloatHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | try { 16 | final String key = args.getString(0); 17 | final Double value = args.getDouble(1); 18 | 19 | FirebaseCrashlytics.getInstance().setCustomKey(key, value); 20 | } catch (JSONException e) { 21 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error setting float", e); 22 | } 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/SetIntHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class SetIntHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | try { 16 | final String key = args.getString(0); 17 | final Integer value = args.getInt(1); 18 | 19 | FirebaseCrashlytics.getInstance().setCustomKey(key, value); 20 | } catch (JSONException e) { 21 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error setting int", e); 22 | } 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/SetStringHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class SetStringHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | 16 | try { 17 | final String key = args.getString(0); 18 | final String value = args.getString(1); 19 | 20 | FirebaseCrashlytics.getInstance().setCustomKey(key, value); 21 | } catch (JSONException e) { 22 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error setting string", e); 23 | } 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/android/uk/co/reallysmall/cordova/plugin/firebase/crashlytics/SetUserIdentifierHandler.java: -------------------------------------------------------------------------------- 1 | package uk.co.reallysmall.cordova.plugin.firebase.crashlytics; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.crashlytics.FirebaseCrashlytics; 6 | 7 | import org.apache.cordova.CallbackContext; 8 | import org.apache.cordova.CordovaInterface; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | public class SetUserIdentifierHandler implements ActionHandler { 13 | @Override 14 | public boolean handle(JSONArray args, CordovaInterface cordova, final CallbackContext callbackContext) { 15 | try { 16 | final String identifier = args.getString(0); 17 | 18 | FirebaseCrashlytics.getInstance().setUserId(identifier); 19 | } catch (JSONException e) { 20 | Log.e(FirebaseCrashlyticsPlugin.TAG, "Error setting user identifier", e); 21 | } 22 | return true; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ios/FirebaseCrashlyticsPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FirebaseCrashlyticsPlugin : CDVPlugin 4 | 5 | - (void)crash:(CDVInvokedUrlCommand *)command; 6 | - (void)logPriority:(CDVInvokedUrlCommand *)command; 7 | - (void)logException:(CDVInvokedUrlCommand *)command; 8 | - (void)log:(CDVInvokedUrlCommand *)command; 9 | - (void)setString:(CDVInvokedUrlCommand *)command; 10 | - (void)setInt:(CDVInvokedUrlCommand *)command; 11 | - (void)setBool:(CDVInvokedUrlCommand *)command; 12 | - (void)setDouble:(CDVInvokedUrlCommand *)command; 13 | - (void)setFloat:(CDVInvokedUrlCommand *)command; 14 | - (void)setUserIdentifier:(CDVInvokedUrlCommand *)command; 15 | - (void)initialise:(CDVInvokedUrlCommand *)command; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /src/ios/FirebaseCrashlyticsPlugin.m: -------------------------------------------------------------------------------- 1 | #import "FirebaseCrashlyticsPlugin.h" 2 | 3 | #import 4 | 5 | #import 6 | @import FirebaseCrashlytics; 7 | 8 | @implementation FirebaseCrashlyticsPlugin 9 | 10 | - (void)pluginInitialize { 11 | if(![FIRApp defaultApp]) { 12 | [FIRApp configure]; 13 | } 14 | } 15 | 16 | - (void)initialise:(CDVInvokedUrlCommand *)command { 17 | NSNumber *hasConsent = [command argumentAtIndex:0]; 18 | 19 | NSNumber *result = [NSNumber numberWithInt:0]; 20 | 21 | [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:false]; 22 | 23 | [[FIRCrashlytics crashlytics] checkForUnsentReportsWithCompletion:^(BOOL hasUnsentReports) { 24 | 25 | if (hasConsent && hasUnsentReports) { 26 | [[FIRCrashlytics crashlytics] sendUnsentReports]; 27 | } else { 28 | [[FIRCrashlytics crashlytics] deleteUnsentReports]; 29 | } 30 | }]; 31 | 32 | if ([[FIRCrashlytics crashlytics] didCrashDuringPreviousExecution]) { 33 | result = [NSNumber numberWithInt:1]; 34 | } 35 | 36 | CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; 37 | 38 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; 39 | } 40 | 41 | - (void)crash:(CDVInvokedUrlCommand *)command { 42 | assert(NO); 43 | } 44 | 45 | - (void)logPriority:(CDVInvokedUrlCommand *)command { 46 | NSString *message = [command argumentAtIndex:2]; 47 | [[FIRCrashlytics crashlytics] logWithFormat:@"%@", message]; 48 | } 49 | 50 | - (void)logException:(CDVInvokedUrlCommand *)command { 51 | NSString *message = [command argumentAtIndex:0]; 52 | 53 | NSDictionary *userInfo = @{ 54 | NSLocalizedDescriptionKey: NSLocalizedString(@"Unexpected excerption", nil), 55 | NSLocalizedFailureReasonErrorKey: NSLocalizedString(message, nil), 56 | NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"", nil)}; 57 | 58 | NSError *error = [NSError errorWithDomain:@"uk.co.trssc" code:-1 userInfo:userInfo]; 59 | [[FIRCrashlytics crashlytics] recordError:error]; 60 | 61 | } 62 | 63 | - (void)log:(CDVInvokedUrlCommand *)command { 64 | NSString *message = [command argumentAtIndex:0]; 65 | [[FIRCrashlytics crashlytics] logWithFormat:@"%@", message]; 66 | } 67 | 68 | - (void)setString:(CDVInvokedUrlCommand *)command { 69 | NSString *key = [command argumentAtIndex:0]; 70 | NSString *value = [command argumentAtIndex:1]; 71 | 72 | [[FIRCrashlytics crashlytics] setCustomValue:value forKey:key]; 73 | } 74 | 75 | - (void)setInt:(CDVInvokedUrlCommand *)command { 76 | NSString *key = [command argumentAtIndex:0]; 77 | NSNumber *value = [command argumentAtIndex:1]; 78 | 79 | [[FIRCrashlytics crashlytics] setCustomValue:value forKey:key]; 80 | } 81 | 82 | - (void)setBool:(CDVInvokedUrlCommand *)command { 83 | NSString *key = [command argumentAtIndex:0]; 84 | NSNumber *value = [command argumentAtIndex:1]; 85 | 86 | [[FIRCrashlytics crashlytics] setCustomValue:value forKey:key]; 87 | } 88 | 89 | - (void)setDouble:(CDVInvokedUrlCommand *)command { 90 | NSString *key = [command argumentAtIndex:0]; 91 | NSNumber *value = [command argumentAtIndex:1]; 92 | 93 | [[FIRCrashlytics crashlytics] setCustomValue:value forKey:key]; 94 | } 95 | 96 | - (void)setFloat:(CDVInvokedUrlCommand *)command { 97 | NSString *key = [command argumentAtIndex:0]; 98 | NSNumber *value = [command argumentAtIndex:1]; 99 | 100 | [[FIRCrashlytics crashlytics] setCustomValue:value forKey:key]; 101 | } 102 | 103 | - (void)setUserIdentifier:(CDVInvokedUrlCommand *)command { 104 | NSString *identifier = [command argumentAtIndex:0]; 105 | 106 | [[FIRCrashlytics crashlytics] setUserID:identifier]; 107 | } 108 | 109 | @end 110 | 111 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace FirebaseCrashlytics { 2 | export function initialise(): FirebaseCrashlytics; 3 | 4 | export interface FirebaseCrashlytics { 5 | 6 | crash(): void; 7 | logPriority(priority: string, tag: string, message: string): void; 8 | log(message: string): void; 9 | logException(message: string): void; 10 | setString(key: string, value: string): void; 11 | setBool(key: string, value: boolean): void; 12 | setDouble(key: string, value: number): void; 13 | setFloat(key: string, value: number): void; 14 | setInt(key: string, value: number): void; 15 | setUserIdentifier(identifier: string): void; 16 | logError(message: string, stackTrace: StackTraceLine[]): void; 17 | initialise(hasConsent: boolean): Promise; 18 | } 19 | 20 | export interface StackTraceLine { 21 | className: string; 22 | functionName: string; 23 | fileName: string; 24 | lineNumber: number; 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /www/browser/crashlytics.js: -------------------------------------------------------------------------------- 1 | /*global alert */ 2 | var PLUGIN_NAME = 'Crashlytics'; 3 | 4 | function Crashlytics() { 5 | } 6 | 7 | Crashlytics.prototype = { 8 | initialise: function(hasConsent) { 9 | if (hasConsent) { 10 | console.log("Sending previous crash reports"); 11 | } 12 | 13 | return Promise.resolve(false); 14 | }, 15 | crash: function() { 16 | alert("Crash! Bang!"); 17 | }, 18 | logPriority: function(priority, tag, message) { 19 | console.debug("P: " + priority + " T: " + tag + " M: " + message); 20 | }, 21 | log: function(message) { 22 | console.debug(message); 23 | }, 24 | logException: function(message) { 25 | console.debug(message); 26 | }, 27 | setString: function(key, value) { 28 | console.debug(key + ":" + value); 29 | }, 30 | setBool: function(key, value) { 31 | console.debug(key + ":" + value); 32 | }, 33 | setDouble: function(key, value) { 34 | console.debug(key + ":" + value); 35 | }, 36 | setFloat: function(key, value) { 37 | console.debug(key + ":" + value); 38 | }, 39 | setInt: function(key, value) { 40 | console.debug(key + ":" + value); 41 | }, 42 | setUserIdentifier: function(identifier) { 43 | console.debug(identifier); 44 | } 45 | }; 46 | 47 | // Log levels 48 | Crashlytics.LOG = { 49 | VERBOSE: 2, 50 | DEBUG: 3, 51 | INFO: 4, 52 | WARN: 5, 53 | ERROR: 6 54 | } 55 | 56 | // Backward compatibility instantiation 57 | Crashlytics.initialise = function() { 58 | return new Crashlytics(); 59 | }; 60 | 61 | module.exports = Crashlytics; 62 | -------------------------------------------------------------------------------- /www/crashlytics.js: -------------------------------------------------------------------------------- 1 | var exec = require('cordova/exec'); 2 | 3 | var PLUGIN_NAME = 'FirebaseCrashlytics'; 4 | 5 | function Crashlytics() { 6 | } 7 | 8 | Crashlytics.prototype = { 9 | initialise: function(hasConsent) { 10 | return new Promise(function (resolve, reject) { 11 | exec(resolve, reject, PLUGIN_NAME, 'initialise', [hasConsent]); 12 | }) 13 | }, 14 | crash: function() { 15 | exec(null, null, PLUGIN_NAME, 'crash', []); 16 | }, 17 | logPriority: function(priority, tag, message) { 18 | exec(null, null, PLUGIN_NAME, 'logPriority', [priority, tag, message]); 19 | }, 20 | log: function(message) { 21 | exec(null, null, PLUGIN_NAME, 'log', [message]); 22 | }, 23 | logException: function(message) { 24 | exec(null, null, PLUGIN_NAME, 'logException', [message]); 25 | }, 26 | setString: function(key, value) { 27 | exec(null, null, PLUGIN_NAME, 'setString', [key, value]); 28 | }, 29 | setBool: function(key, value) { 30 | exec(null, null, PLUGIN_NAME, 'setBool', [key, value]); 31 | }, 32 | setDouble: function(key, value) { 33 | exec(null, null, PLUGIN_NAME, 'setDouble', [key, value]); 34 | }, 35 | setFloat: function(key, value) { 36 | exec(null, null, PLUGIN_NAME, 'setFloat', [key, value]); 37 | }, 38 | setInt: function(key, value) { 39 | exec(null, null, PLUGIN_NAME, 'setInt', [key, value]); 40 | }, 41 | setUserIdentifier: function(identifier) { 42 | exec(null, null, PLUGIN_NAME, 'setUserIdentifier', [identifier]); 43 | }, 44 | logError: function(message, stackTrace) { 45 | exec(null, null, PLUGIN_NAME, 'logError', [message, stackTrace]); 46 | }, 47 | }; 48 | 49 | // Log levels 50 | // See https://developer.android.com/reference/android/util/Log 51 | Crashlytics.LOG = { 52 | VERBOSE: 2, 53 | DEBUG: 3, 54 | INFO: 4, 55 | WARN: 5, 56 | ERROR: 6 57 | } 58 | 59 | // Backward compatibility instantiation 60 | Crashlytics.initialise = function() { 61 | return new Crashlytics(); 62 | }; 63 | 64 | module.exports = Crashlytics; 65 | --------------------------------------------------------------------------------