├── .classpath ├── .project ├── .settings └── org.eclipse.core.resources.prefs ├── AndroidManifest.xml ├── LICENCE.txt ├── README.mkdn ├── README_images ├── dialog-message.png ├── dialog.png └── notification.png ├── android-error-reporter.jar ├── build.jardesc ├── default.properties ├── res └── .gitignore └── src └── de └── quist └── app └── errorreporter ├── ExceptionReportActivity.java ├── ExceptionReportService.java ├── ExceptionReporter.java ├── ReportingActivity.java ├── ReportingActivityGroup.java ├── ReportingAliasActivity.java ├── ReportingExpandableListActivity.java ├── ReportingIntentService.java ├── ReportingLauncherActivity.java ├── ReportingListActivity.java ├── ReportingPreferenceActivity.java ├── ReportingService.java └── ReportingTabActivity.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ErrorReporter 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | #Sun Dec 19 01:39:37 CET 2010 2 | eclipse.preferences.version=1 3 | encoding/LICENCE.txt=UTF-8 4 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 2 | 3 | 4 | 1. Definitions. 5 | 6 | 1.1. "Contributor" means each individual or entity that 7 | creates or contributes to the creation of Modifications. 8 | 9 | 1.2. "Contributor Version" means the combination of the 10 | Original Software, prior Modifications used by a 11 | Contributor (if any), and the Modifications made by that 12 | particular Contributor. 13 | 14 | 1.3. "Covered Software" means (a) the Original Software, or 15 | (b) Modifications, or (c) the combination of files 16 | containing Original Software with files containing 17 | Modifications, in each case including portions thereof. 18 | 19 | 1.4. "Executable" means the Covered Software in any form 20 | other than Source Code. 21 | 22 | 1.5. "Initial Developer" means the individual or entity 23 | that first makes Original Software available under this 24 | License. 25 | 26 | 1.6. "Larger Work" means a work which combines Covered 27 | Software or portions thereof with code not governed by the 28 | terms of this License. 29 | 30 | 1.7. "License" means this document. 31 | 32 | 1.8. "Licensable" means having the right to grant, to the 33 | maximum extent possible, whether at the time of the initial 34 | grant or subsequently acquired, any and all of the rights 35 | conveyed herein. 36 | 37 | 1.9. "Modifications" means the Source Code and Executable 38 | form of any of the following: 39 | 40 | A. Any file that results from an addition to, 41 | deletion from or modification of the contents of a 42 | file containing Original Software or previous 43 | Modifications; 44 | 45 | B. Any new file that contains any part of the 46 | Original Software or previous Modification; or 47 | 48 | C. Any new file that is contributed or otherwise made 49 | available under the terms of this License. 50 | 51 | 1.10. "Original Software" means the Source Code and 52 | Executable form of computer software code that is 53 | originally released under this License. 54 | 55 | 1.11. "Patent Claims" means any patent claim(s), now owned 56 | or hereafter acquired, including without limitation, 57 | method, process, and apparatus claims, in any patent 58 | Licensable by grantor. 59 | 60 | 1.12. "Source Code" means (a) the common form of computer 61 | software code in which modifications are made and (b) 62 | associated documentation included in or with such code. 63 | 64 | 1.13. "You" (or "Your") means an individual or a legal 65 | entity exercising rights under, and complying with all of 66 | the terms of, this License. For legal entities, "You" 67 | includes any entity which controls, is controlled by, or is 68 | under common control with You. For purposes of this 69 | definition, "control" means (a) the power, direct or 70 | indirect, to cause the direction or management of such 71 | entity, whether by contract or otherwise, or (b) ownership 72 | of more than fifty percent (50%) of the outstanding shares 73 | or beneficial ownership of such entity. 74 | 75 | 2. License Grants. 76 | 77 | 2.1. The Initial Developer Grant. 78 | 79 | Conditioned upon Your compliance with Section 3.1 below and 80 | subject to third party intellectual property claims, the 81 | Initial Developer hereby grants You a world-wide, 82 | royalty-free, non-exclusive license: 83 | 84 | (a) under intellectual property rights (other than 85 | patent or trademark) Licensable by Initial Developer, 86 | to use, reproduce, modify, display, perform, 87 | sublicense and distribute the Original Software (or 88 | portions thereof), with or without Modifications, 89 | and/or as part of a Larger Work; and 90 | 91 | (b) under Patent Claims infringed by the making, 92 | using or selling of Original Software, to make, have 93 | made, use, practice, sell, and offer for sale, and/or 94 | otherwise dispose of the Original Software (or 95 | portions thereof). 96 | 97 | (c) The licenses granted in Sections 2.1(a) and (b) 98 | are effective on the date Initial Developer first 99 | distributes or otherwise makes the Original Software 100 | available to a third party under the terms of this 101 | License. 102 | 103 | (d) Notwithstanding Section 2.1(b) above, no patent 104 | license is granted: (1) for code that You delete from 105 | the Original Software, or (2) for infringements 106 | caused by: (i) the modification of the Original 107 | Software, or (ii) the combination of the Original 108 | Software with other software or devices. 109 | 110 | 2.2. Contributor Grant. 111 | 112 | Conditioned upon Your compliance with Section 3.1 below and 113 | subject to third party intellectual property claims, each 114 | Contributor hereby grants You a world-wide, royalty-free, 115 | non-exclusive license: 116 | 117 | (a) under intellectual property rights (other than 118 | patent or trademark) Licensable by Contributor to 119 | use, reproduce, modify, display, perform, sublicense 120 | and distribute the Modifications created by such 121 | Contributor (or portions thereof), either on an 122 | unmodified basis, with other Modifications, as 123 | Covered Software and/or as part of a Larger Work; and 124 | 125 | 126 | (b) under Patent Claims infringed by the making, 127 | using, or selling of Modifications made by that 128 | Contributor either alone and/or in combination with 129 | its Contributor Version (or portions of such 130 | combination), to make, use, sell, offer for sale, 131 | have made, and/or otherwise dispose of: (1) 132 | Modifications made by that Contributor (or portions 133 | thereof); and (2) the combination of Modifications 134 | made by that Contributor with its Contributor Version 135 | (or portions of such combination). 136 | 137 | (c) The licenses granted in Sections 2.2(a) and 138 | 2.2(b) are effective on the date Contributor first 139 | distributes or otherwise makes the Modifications 140 | available to a third party. 141 | 142 | (d) Notwithstanding Section 2.2(b) above, no patent 143 | license is granted: (1) for any code that Contributor 144 | has deleted from the Contributor Version; (2) for 145 | infringements caused by: (i) third party 146 | modifications of Contributor Version, or (ii) the 147 | combination of Modifications made by that Contributor 148 | with other software (except as part of the 149 | Contributor Version) or other devices; or (3) under 150 | Patent Claims infringed by Covered Software in the 151 | absence of Modifications made by that Contributor. 152 | 153 | 3. Distribution Obligations. 154 | 155 | 3.1. Availability of Source Code. 156 | 157 | Any Covered Software that You distribute or otherwise make 158 | available in Executable form must also be made available in 159 | Source Code form and that Source Code form must be 160 | distributed only under the terms of this License. You must 161 | include a copy of this License with every copy of the 162 | Source Code form of the Covered Software You distribute or 163 | otherwise make available. You must inform recipients of any 164 | such Covered Software in Executable form as to how they can 165 | obtain such Covered Software in Source Code form in a 166 | reasonable manner on or through a medium customarily used 167 | for software exchange. 168 | 169 | 3.2. Modifications. 170 | 171 | The Modifications that You create or to which You 172 | contribute are governed by the terms of this License. You 173 | represent that You believe Your Modifications are Your 174 | original creation(s) and/or You have sufficient rights to 175 | grant the rights conveyed by this License. 176 | 177 | 3.3. Required Notices. 178 | 179 | You must include a notice in each of Your Modifications 180 | that identifies You as the Contributor of the Modification. 181 | You may not remove or alter any copyright, patent or 182 | trademark notices contained within the Covered Software, or 183 | any notices of licensing or any descriptive text giving 184 | attribution to any Contributor or the Initial Developer. 185 | 186 | 3.4. Application of Additional Terms. 187 | 188 | You may not offer or impose any terms on any Covered 189 | Software in Source Code form that alters or restricts the 190 | applicable version of this License or the recipients' 191 | rights hereunder. You may choose to offer, and to charge a 192 | fee for, warranty, support, indemnity or liability 193 | obligations to one or more recipients of Covered Software. 194 | However, you may do so only on Your own behalf, and not on 195 | behalf of the Initial Developer or any Contributor. You 196 | must make it absolutely clear that any such warranty, 197 | support, indemnity or liability obligation is offered by 198 | You alone, and You hereby agree to indemnify the Initial 199 | Developer and every Contributor for any liability incurred 200 | by the Initial Developer or such Contributor as a result of 201 | warranty, support, indemnity or liability terms You offer. 202 | 203 | 204 | 3.5. Distribution of Executable Versions. 205 | 206 | You may distribute the Executable form of the Covered 207 | Software under the terms of this License or under the terms 208 | of a license of Your choice, which may contain terms 209 | different from this License, provided that You are in 210 | compliance with the terms of this License and that the 211 | license for the Executable form does not attempt to limit 212 | or alter the recipient's rights in the Source Code form 213 | from the rights set forth in this License. If You 214 | distribute the Covered Software in Executable form under a 215 | different license, You must make it absolutely clear that 216 | any terms which differ from this License are offered by You 217 | alone, not by the Initial Developer or Contributor. You 218 | hereby agree to indemnify the Initial Developer and every 219 | Contributor for any liability incurred by the Initial 220 | Developer or such Contributor as a result of any such terms 221 | You offer. 222 | 223 | 3.6. Larger Works. 224 | 225 | You may create a Larger Work by combining Covered Software 226 | with other code not governed by the terms of this License 227 | and distribute the Larger Work as a single product. In such 228 | a case, You must make sure the requirements of this License 229 | are fulfilled for the Covered Software. 230 | 231 | 4. Versions of the License. 232 | 233 | 4.1. New Versions. 234 | 235 | Sun Microsystems, Inc. is the initial license steward and 236 | may publish revised and/or new versions of this License 237 | from time to time. Each version will be given a 238 | distinguishing version number. Except as provided in 239 | Section 4.3, no one other than the license steward has the 240 | right to modify this License. 241 | 242 | 4.2. Effect of New Versions. 243 | 244 | You may always continue to use, distribute or otherwise 245 | make the Covered Software available under the terms of the 246 | version of the License under which You originally received 247 | the Covered Software. If the Initial Developer includes a 248 | notice in the Original Software prohibiting it from being 249 | distributed or otherwise made available under any 250 | subsequent version of the License, You must distribute and 251 | make the Covered Software available under the terms of the 252 | version of the License under which You originally received 253 | the Covered Software. Otherwise, You may also choose to 254 | use, distribute or otherwise make the Covered Software 255 | available under the terms of any subsequent version of the 256 | License published by the license steward. 257 | 258 | 4.3. Modified Versions. 259 | 260 | When You are an Initial Developer and You want to create a 261 | new license for Your Original Software, You may create and 262 | use a modified version of this License if You: (a) rename 263 | the license and remove any references to the name of the 264 | license steward (except to note that the license differs 265 | from this License); and (b) otherwise make it clear that 266 | the license contains terms which differ from this License. 267 | 268 | 269 | 5. DISCLAIMER OF WARRANTY. 270 | 271 | COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" 272 | BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 273 | INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED 274 | SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR 275 | PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND 276 | PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY 277 | COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE 278 | INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF 279 | ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF 280 | WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF 281 | ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS 282 | DISCLAIMER. 283 | 284 | 6. TERMINATION. 285 | 286 | 6.1. This License and the rights granted hereunder will 287 | terminate automatically if You fail to comply with terms 288 | herein and fail to cure such breach within 30 days of 289 | becoming aware of the breach. Provisions which, by their 290 | nature, must remain in effect beyond the termination of 291 | this License shall survive. 292 | 293 | 6.2. If You assert a patent infringement claim (excluding 294 | declaratory judgment actions) against Initial Developer or 295 | a Contributor (the Initial Developer or Contributor against 296 | whom You assert such claim is referred to as "Participant") 297 | alleging that the Participant Software (meaning the 298 | Contributor Version where the Participant is a Contributor 299 | or the Original Software where the Participant is the 300 | Initial Developer) directly or indirectly infringes any 301 | patent, then any and all rights granted directly or 302 | indirectly to You by such Participant, the Initial 303 | Developer (if the Initial Developer is not the Participant) 304 | and all Contributors under Sections 2.1 and/or 2.2 of this 305 | License shall, upon 60 days notice from Participant 306 | terminate prospectively and automatically at the expiration 307 | of such 60 day notice period, unless if within such 60 day 308 | period You withdraw Your claim with respect to the 309 | Participant Software against such Participant either 310 | unilaterally or pursuant to a written agreement with 311 | Participant. 312 | 313 | 6.3. In the event of termination under Sections 6.1 or 6.2 314 | above, all end user licenses that have been validly granted 315 | by You or any distributor hereunder prior to termination 316 | (excluding licenses granted to You by any distributor) 317 | shall survive termination. 318 | 319 | 7. LIMITATION OF LIABILITY. 320 | 321 | UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT 322 | (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE 323 | INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF 324 | COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE 325 | LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR 326 | CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT 327 | LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK 328 | STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER 329 | COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN 330 | INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF 331 | LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL 332 | INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT 333 | APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO 334 | NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR 335 | CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT 336 | APPLY TO YOU. 337 | 338 | 8. U.S. GOVERNMENT END USERS. 339 | 340 | The Covered Software is a "commercial item," as that term is 341 | defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial 342 | computer software" (as that term is defined at 48 C.F.R. § 343 | 252.227-7014(a)(1)) and "commercial computer software 344 | documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 345 | 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 346 | through 227.7202-4 (June 1995), all U.S. Government End Users 347 | acquire Covered Software with only those rights set forth herein. 348 | This U.S. Government Rights clause is in lieu of, and supersedes, 349 | any other FAR, DFAR, or other clause or provision that addresses 350 | Government rights in computer software under this License. 351 | 352 | 9. MISCELLANEOUS. 353 | 354 | This License represents the complete agreement concerning subject 355 | matter hereof. If any provision of this License is held to be 356 | unenforceable, such provision shall be reformed only to the 357 | extent necessary to make it enforceable. This License shall be 358 | governed by the law of the jurisdiction specified in a notice 359 | contained within the Original Software (except to the extent 360 | applicable law, if any, provides otherwise), excluding such 361 | jurisdiction's conflict-of-law provisions. Any litigation 362 | relating to this License shall be subject to the jurisdiction of 363 | the courts located in the jurisdiction and venue specified in a 364 | notice contained within the Original Software, with the losing 365 | party responsible for costs, including, without limitation, court 366 | costs and reasonable attorneys' fees and expenses. The 367 | application of the United Nations Convention on Contracts for the 368 | International Sale of Goods is expressly excluded. Any law or 369 | regulation which provides that the language of a contract shall 370 | be construed against the drafter shall not apply to this License. 371 | You agree that You alone are responsible for compliance with the 372 | United States export administration regulations (and the export 373 | control laws and regulation of any other countries) when You use, 374 | distribute or otherwise make available any Covered Software. 375 | 376 | 10. RESPONSIBILITY FOR CLAIMS. 377 | 378 | As between Initial Developer and the Contributors, each party is 379 | responsible for claims and damages arising, directly or 380 | indirectly, out of its utilization of rights under this License 381 | and You agree to work with Initial Developer and Contributors to 382 | distribute such responsibility on an equitable basis. Nothing 383 | herein is intended or shall be deemed to constitute any admission 384 | of liability. -------------------------------------------------------------------------------- /README.mkdn: -------------------------------------------------------------------------------- 1 | Android Error-Reporter 2 | ====================== 3 | 4 | Integration 5 | ------------ 6 | To integrate **Android Error-Reporter** just follow four simple steps: 7 | 8 | 1. Download android-error-reporter.jar and add it to your build path 9 | 10 | 2. Inherit from one of the base classes `Reporting*` (e.g. `ReportingActivity`, `ReportingService` or `ReportingIntentService`) or register your context in every `onCreate()` of your activity or context, e.g.: 11 | 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | ExceptionReporter reporter = ExceptionReporter.register(this); 16 | super.onCreate(savedInstanceState); 17 | } 18 | 19 | 20 | 3. Add the `ExceptionReportService` to your `AndroidManifest.xml` (within the `` node): 21 | 22 | 25 | 26 | 4. Configure the target URL where to send the errors by adding the following line to your `AndroidManifest.xml` (within the `` node): 27 | 28 | 31 | 32 | 33 | Usage 34 | ----- 35 | 36 | ### Report catched exceptions 37 | 38 | Unhandled exceptions are automatically handled by the ExceptionReportService. You can also report 39 | catched exceptions by calling `exceptionHandler.reportException(thread, e)` or 40 | `exceptionHandler.reportException(thread, e, extraMessage)` on the ExceptionReporter 41 | object returned by `ExceptionReporter.register(context)`. 42 | If your activity/service inherits from one of the supplied base classes (`de.quist.app.errorreporter.Reporting*`), you can access the 43 | ExceptionReporter via `getExceptionReporter()`. 44 | 45 | ### User approved error reporting 46 | 47 | By default errors are sent to the server automatically without asking the user for permission. This can be 48 | changed by adding the `ExceptionReportActivity` to your `AndroidManifest.xml`. To do so, add the following line: 49 | 50 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | The exception reporter will automatically detect the defined activity and instead of automatically sending the 64 | report a notification is created which will ask the user for permission to send the notification when clicked. 65 | You have full control over the texts and icons of both, the notification and the report-dialog. All resources 66 | can be specified by adding meta-data tags to your `AndroidManifest.xml` (within the `` node) which reference to a string/drawable in 67 | their `android:resource`-attribute, e.g.: 68 | 69 | 72 | 73 | See attributes `de.quist.app.errorreporter.dialog*` and `de.quist.app.errorreporter.notification*` in the section __Configuration__ for more information abouth how to control the notification and dialog. 74 | 75 | ### Retry-Rules 76 | 77 | The ExceptionReportService tries to send the error to the URL specified in your `AndroidManifest.xml`. 78 | If it fails it retries with an exponential back-off. The default configuration will increase the back-off 79 | up to 2^12 sec (about 1h8m) and will retry it 17 times until it gives up (this will result in a total 80 | time span of 1s+2s+4s+8s+...2^12 s*5=8h). You can change these values by adding specific meta-data nodes to your `AndroidManifest.xml` (see __Configuration__) 81 | 82 | Configuration 83 | ------------- 84 | You can add the following name/value pairs as a meta-data node to your `AndroidManifest.xml` (within the `` node). 85 | 86 | __All names need to have `de.quist.app.errorreporter.` as a prefix__!!!: 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 |
NameTypeDefaultDescription
maximumRetryCountint17Maximum number of tries to send an error report
maximumBackoffExponentint12Maximum exponent for the back-off
reportOnFroyobooleanfalseDefines whether unhandled exception are reported on Android 2.2 (Froyo) and above or not, since Froyo has its own error-reporting system
includeFieldsStringallComma-separated list of fields to send (field names as of section Server). If the list contains all, all available fields will be included
dialogIconint@android:drawable/ic_dialog_alertThe icon of the dialog for user approved error reports. (Use the android:resource attribute and reference to an existing resource)
dialogTitleint^1 crashedThe dialog title for user approved error reports. You can use a template style text (^1) where the first placeholder will be replaced by the application name. (Use the android:resource attribute and reference to an existing resource)
dialogTextint^1 crashed because of an unexpected error. Please help fixing the error by sending an error report.The text shown in the dialog for user approved error reports. You can use a template style text (^1) where the first placeholder will be replaced by the application name. (Use the android:resource attribute and reference to an existing resource)
dialogMessageHintintUndefinedIf you specify this value, an additional text-input field will be shown in the dialog with the specified message as hint. The content will be sent as additional information in the extraMessage field (see section Server). (Use the android:resource attribute and reference to an existing resource)
dialogSendButtonint@android:string/okText on the send-button of the dialog for user approved error reports. (Use the android:resource attribute and reference to an existing resource)
dialogCancelButtonint@android:string/cancelText on the cancel-button of the dialog for user approved error reports. (Use the android:resource attribute and reference to an existing resource)
notificationIconint@android:drawable/stat_notify_errorIcon of the notification for user approved error reports. (Use the android:resource attribute and reference to an existing resource)
notificationTitleint^1 crashedThe notification title for user approved error reports. You can use a template style text (^1) where the first placeholder will be replaced by the application name. (Use the android:resource attribute and reference to an existing resource)
notificationTextintClick here to help fixing the issueThe notification text for user approved error reports. You can use a template style text (^1) where the first placeholder will be replaced by the application name.(Use the android:resource attribute and reference to an existing resource)
notificationTickerTextintEmptyThe notification ticker text for user approved error reports. You can use a template style text (^1) where the first placeholder will be replaced by the application name.(Use the android:resource attribute and reference to an existing resource)
180 | 181 | 182 | 183 | Server 184 | ------ 185 | The server will receive error reports via HTTP-post requests. They will contain the following fields: 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 |
exStackTraceThe stack trace
exClassThe exception class
exMessageThe exceptions message
exDateTimeThe date and time when the exception happend in the format "yyyy-MM-dd HH:mm:ssZ" (SimpleDateFormat)
extraMessageA custom message which can be added to manual error reports
exThreadNameThe name of the thread the error has been thrown in
appVersionCodeThe version code (as defined in your AndroidManifest.xml)
appVersionNameThe version name (as defined in your AndroidManifest.xml)
appPackageNameThe package name (as defined in your AndroidManifest.xml)
devAvailableMemoryThe devices available memory in bytes
devTotalMemoryThe devices total memory in bytes
devModelThe phones model name (android.os.Build.MODEL)
devSdkThe phones sdk version (android.os.Build.VERSION.SDK)
devReleaseVersionThe phones release version (android.os.Build.VERSION.RELEASE)
-------------------------------------------------------------------------------- /README_images/dialog-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomquist/Android-Error-Reporter/334987f7669373704eb60421e1579fc1afac839b/README_images/dialog-message.png -------------------------------------------------------------------------------- /README_images/dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomquist/Android-Error-Reporter/334987f7669373704eb60421e1579fc1afac839b/README_images/dialog.png -------------------------------------------------------------------------------- /README_images/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomquist/Android-Error-Reporter/334987f7669373704eb60421e1579fc1afac839b/README_images/notification.png -------------------------------------------------------------------------------- /android-error-reporter.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomquist/Android-Error-Reporter/334987f7669373704eb60421e1579fc1afac839b/android-error-reporter.jar -------------------------------------------------------------------------------- /build.jardesc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-3 12 | android.library=true 13 | -------------------------------------------------------------------------------- /res/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomquist/Android-Error-Reporter/334987f7669373704eb60421e1579fc1afac839b/res/.gitignore -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ExceptionReportActivity.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.content.pm.ApplicationInfo; 8 | import android.content.pm.PackageManager; 9 | import android.content.pm.PackageManager.NameNotFoundException; 10 | import android.graphics.drawable.Drawable; 11 | import android.os.Bundle; 12 | import android.text.TextUtils; 13 | import android.util.Log; 14 | import android.view.ViewGroup.LayoutParams; 15 | import android.widget.EditText; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | 19 | public class ExceptionReportActivity extends Activity { 20 | 21 | private static final String META_DATA_DIALOG_TITLE = ExceptionReportActivity.class.getPackage().getName().concat(".dialogTitle"); 22 | private static final String META_DATA_DIALOG_TEXT = ExceptionReportActivity.class.getPackage().getName().concat(".dialogText"); 23 | private static final String META_DATA_DIALOG_ICON = ExceptionReportActivity.class.getPackage().getName().concat(".dialogIcon"); 24 | private static final String META_DATA_DIALOG_MESSAGE_HINT = ExceptionReportActivity.class.getPackage().getName().concat(".dialogMessageHint"); 25 | private static final String META_DATA_DIALOG_SEND_BUTTON = ExceptionReportActivity.class.getPackage().getName().concat(".dialogSendButton"); 26 | private static final String META_DATA_DIALOG_CANCEL_BUTTON = ExceptionReportActivity.class.getPackage().getName().concat(".dialogCancelButton"); 27 | 28 | private static final CharSequence DEFAULT_DIALOG_TITLE = "^1 crashed"; 29 | private static final CharSequence DEFAULT_DIALOG_TEXT = "^1 crashed because of an unexpected error. Please help fixing the error by sending an error report to the developer."; 30 | private static final int DEFAULT_DIALOG_ICON = android.R.drawable.ic_dialog_alert; 31 | private static final int DEFAULT_POSITIVE_BUTTON_TEXT = android.R.string.ok; 32 | private static final int DEFAULT_NEGATIVE_BUTTON_TEXT = android.R.string.cancel; 33 | private static final String TAG = ExceptionReportActivity.class.getSimpleName(); 34 | 35 | private ApplicationInfo info; 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setTheme(android.R.style.Theme_NoDisplay); 41 | AlertDialog.Builder dialog = new AlertDialog.Builder(this); 42 | dialog.setTitle(getDialogTitle()); 43 | dialog.setIcon(getDialogIcon()); 44 | CharSequence messageHint = getDialogMessageHint(); 45 | EditText textEdit = null; 46 | if (messageHint == null) { 47 | dialog.setMessage(getDialogText()); 48 | } else { 49 | LinearLayout layout = new LinearLayout(this); 50 | float scale = getResources().getDisplayMetrics().density; 51 | int padding = (int) (10f * scale + 0.5f); 52 | layout.setPadding(padding, padding, padding, padding); 53 | layout.setOrientation(LinearLayout.VERTICAL); 54 | TextView textView = new TextView(this); 55 | textView.setText(getDialogText()); 56 | textView.setTextAppearance(this, android.R.style.TextAppearance_Medium); 57 | textView.setPadding(textView.getPaddingLeft() + padding, textView.getPaddingTop(), textView.getPaddingRight() + padding, textView.getPaddingBottom() + padding); 58 | layout.addView(textView, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); 59 | textEdit = new EditText(this); 60 | textEdit.setHint(getDialogMessageHint()); 61 | layout.addView(textEdit, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); 62 | dialog.setView(layout); 63 | } 64 | final EditText text = textEdit; 65 | dialog.setPositiveButton(getDialogPositiveButtonText(), new DialogInterface.OnClickListener() { 66 | 67 | @Override 68 | public void onClick(DialogInterface dialog, int which) { 69 | Intent i = getIntent(); 70 | i.setClass(ExceptionReportActivity.this, ExceptionReportService.class); 71 | if (text != null && !TextUtils.isEmpty(text.getText())) { 72 | i.putExtra(ExceptionReportService.EXTRA_EXTRA_MESSAGE, text.getText().toString()); 73 | } 74 | i.putExtra(ExceptionReportService.EXTRA_MANUAL_REPORT, true); 75 | startService(i); 76 | dialog.dismiss(); 77 | finish(); 78 | } 79 | }); 80 | dialog.setNegativeButton(getDialogNegativeButtonText(), new DialogInterface.OnClickListener() { 81 | 82 | @Override 83 | public void onClick(DialogInterface dialog, int which) { 84 | dialog.cancel(); 85 | finish(); 86 | } 87 | }); 88 | dialog.show(); 89 | } 90 | 91 | private CharSequence getDialogPositiveButtonText() { 92 | int resId = DEFAULT_POSITIVE_BUTTON_TEXT; 93 | 94 | ApplicationInfo info = getPackageApplicationInfo(); 95 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_DIALOG_SEND_BUTTON)) { 96 | try { 97 | resId = info.metaData.getInt(META_DATA_DIALOG_SEND_BUTTON); 98 | } catch (Exception e) {Log.e(TAG, "Attribute " + META_DATA_DIALOG_SEND_BUTTON + " must be specified using the attribute android:resource", e);} 99 | } 100 | return getText(resId); 101 | } 102 | 103 | private CharSequence getDialogNegativeButtonText() { 104 | int resId = DEFAULT_NEGATIVE_BUTTON_TEXT; 105 | 106 | ApplicationInfo info = getPackageApplicationInfo(); 107 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_DIALOG_CANCEL_BUTTON)) { 108 | try { 109 | resId = info.metaData.getInt(META_DATA_DIALOG_CANCEL_BUTTON); 110 | } catch (Exception e) {Log.e(TAG, "Attribute " + META_DATA_DIALOG_CANCEL_BUTTON + " must be specified using the attribute android:resource", e);} 111 | } 112 | return getText(resId); 113 | } 114 | 115 | private CharSequence getDialogTitle() { 116 | CharSequence dialogTitle = DEFAULT_DIALOG_TITLE; 117 | 118 | PackageManager pm = getPackageManager(); 119 | ApplicationInfo info = getPackageApplicationInfo(); 120 | if (info != null) { 121 | if (info.metaData != null && info.metaData.containsKey(META_DATA_DIALOG_TITLE)) { 122 | try { 123 | int resId = info.metaData.getInt(META_DATA_DIALOG_TITLE); 124 | dialogTitle = getText(resId); 125 | } catch (Exception e) {Log.e(TAG, "Attribute " + META_DATA_DIALOG_TITLE + " must be specified using the attribute android:resource", e);} 126 | 127 | } 128 | return TextUtils.expandTemplate(dialogTitle, pm.getApplicationLabel(info)); 129 | } else return dialogTitle; 130 | } 131 | 132 | private CharSequence getDialogText() { 133 | CharSequence dialogText = DEFAULT_DIALOG_TEXT; 134 | 135 | PackageManager pm = getPackageManager(); 136 | ApplicationInfo info = getPackageApplicationInfo(); 137 | if (info != null) { 138 | if (info.metaData != null && info.metaData.containsKey(META_DATA_DIALOG_TEXT)) { 139 | try { 140 | int resId = info.metaData.getInt(META_DATA_DIALOG_TEXT); 141 | dialogText = getText(resId); 142 | } catch (Exception e) {Log.e(TAG, "Attribute " + META_DATA_DIALOG_TEXT + " must be specified using the attribute android:resource", e);} 143 | 144 | } 145 | return TextUtils.expandTemplate(dialogText, pm.getApplicationLabel(info)); 146 | } else return dialogText; 147 | } 148 | 149 | private CharSequence getDialogMessageHint() { 150 | CharSequence dialogMessageHint = null; 151 | 152 | ApplicationInfo info = getPackageApplicationInfo(); 153 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_DIALOG_MESSAGE_HINT)) { 154 | try { 155 | int resId = info.metaData.getInt(META_DATA_DIALOG_MESSAGE_HINT); 156 | dialogMessageHint = getText(resId); 157 | } catch (Exception e) {Log.e(TAG, "Attribute " + META_DATA_DIALOG_MESSAGE_HINT + " must be specified using the attribute android:resource", e);} 158 | 159 | } 160 | return dialogMessageHint; 161 | } 162 | 163 | private Drawable getDialogIcon() { 164 | int dialogIcon = DEFAULT_DIALOG_ICON; 165 | 166 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_DIALOG_ICON)) { 167 | try { 168 | dialogIcon = info.metaData.getInt(META_DATA_DIALOG_ICON); 169 | } catch (Exception e) {Log.e(TAG, "Attribute " + META_DATA_DIALOG_ICON + " must be specified using the attribute android:resource", e);} 170 | } 171 | return getResources().getDrawable(dialogIcon); 172 | } 173 | 174 | private ApplicationInfo getPackageApplicationInfo() { 175 | if (info != null) return info; 176 | else { 177 | PackageManager pm = getPackageManager(); 178 | ApplicationInfo info; 179 | try { 180 | info = pm.getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 181 | this.info = info; 182 | return info; 183 | } catch (NameNotFoundException e) { 184 | return null; 185 | } 186 | } 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ExceptionReportService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The contents of this file are subject to the terms of the 3 | * Common Development and Distribution License (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * 6 | * You can obtain a copy of the license at LICENSE.txt 7 | * or http://www.opensource.org/licenses/cddl1.php. 8 | * See the License for the specific language governing permissions 9 | * and limitations under the License. 10 | * 11 | * When distributing Covered Code, include this CDDL HEADER in each 12 | * file and include the License file at LICENSE.txt. 13 | * If applicable, add the following below this CDDL HEADER, with the 14 | * fields enclosed by brackets "[]" replaced with your own identifying 15 | * information: Portions Copyright [yyyy] [name of copyright owner] 16 | * 17 | * 18 | * Copyright 2010 Tom Quist 19 | * All rights reserved Use is subject to license terms. 20 | */ 21 | package de.quist.app.errorreporter; 22 | 23 | import java.io.IOException; 24 | import java.io.UnsupportedEncodingException; 25 | import java.net.SocketException; 26 | import java.util.ArrayList; 27 | import java.util.HashSet; 28 | import java.util.List; 29 | import java.util.Set; 30 | import java.util.StringTokenizer; 31 | 32 | import javax.net.ssl.SSLException; 33 | 34 | import org.apache.http.NameValuePair; 35 | import org.apache.http.client.ClientProtocolException; 36 | import org.apache.http.client.HttpClient; 37 | import org.apache.http.client.entity.UrlEncodedFormEntity; 38 | import org.apache.http.client.methods.HttpPost; 39 | import org.apache.http.impl.client.DefaultHttpClient; 40 | import org.apache.http.message.BasicNameValuePair; 41 | import org.apache.http.protocol.HTTP; 42 | 43 | import android.app.AlarmManager; 44 | import android.app.PendingIntent; 45 | import android.content.Intent; 46 | import android.content.pm.ApplicationInfo; 47 | import android.content.pm.PackageInfo; 48 | import android.content.pm.PackageManager; 49 | import android.content.pm.PackageManager.NameNotFoundException; 50 | import android.net.Uri; 51 | import android.os.SystemClock; 52 | import android.util.Log; 53 | 54 | public class ExceptionReportService extends ReportingIntentService { 55 | 56 | static final String ACTION_SEND_REPORT = ExceptionReportService.class.getPackage().getName().concat(".actionSendReport"); 57 | 58 | static final String EXTRA_STACK_TRACE = ExceptionReportService.class.getPackage().getName().concat(".extraStackTrace"); 59 | static final String EXTRA_EXCEPTION_CLASS = ExceptionReportService.class.getPackage().getName().concat(".extraExceptionClass"); 60 | static final String EXTRA_MESSAGE = ExceptionReportService.class.getPackage().getName().concat(".extraMessage"); 61 | static final String EXTRA_EXCEPTION_TIME = ExceptionReportService.class.getPackage().getName().concat(".extraExceptionTime"); 62 | static final String EXTRA_THREAD_NAME = ExceptionReportService.class.getPackage().getName().concat(".extraThreadName"); 63 | static final String EXTRA_EXTRA_MESSAGE = ExceptionReportService.class.getPackage().getName().concat(".extraCustomMessage"); 64 | static final String EXTRA_MANUAL_REPORT = ExceptionReportService.class.getPackage().getName().concat(".extraManualReport"); 65 | static final String EXTRA_AVAILABLE_MEMORY = ExceptionReportService.class.getPackage().getName().concat(".extraAvailableMemory"); 66 | static final String EXTRA_TOTAL_MEMORY = ExceptionReportService.class.getPackage().getName().concat(".extraTotalMemory"); 67 | 68 | /** 69 | * Used internally to count retries. 70 | */ 71 | private static final String EXTRA_CURRENT_RETRY_COUNT = ExceptionReportService.class.getPackage().getName().concat(".extraCurrentRetryCount"); 72 | 73 | /** 74 | * The default maximum backoff exponent. 75 | */ 76 | static final int DEFAULT_MAXIMUM_BACKOFF_EXPONENT = 12; 77 | 78 | /** 79 | * The default maximum number of tries to send a report. This value results in a retry 80 | * time of about 8 hours with an unchanged retry count. 81 | */ 82 | static final int DEFAULT_MAXIMUM_RETRY_COUNT = DEFAULT_MAXIMUM_BACKOFF_EXPONENT + 5; 83 | 84 | /** 85 | * The default value whether to report on Android 2.2 and above. 86 | */ 87 | static final boolean DEFAULT_REPORT_ON_FROYO = false; 88 | 89 | private static final String TAG = ExceptionReportService.class.getSimpleName(); 90 | 91 | /** 92 | * Maximum number of tries to send a report. Default is {@value #DEFAULT_MAXIMUM_RETRY_COUNT}. 93 | */ 94 | private static final String META_DATA_MAXIMUM_RETRY_COUNT = ExceptionReportService.class.getPackage().getName().concat(".maximumRetryCount"); 95 | private static final String META_DATA_MAXIMUM_BACKOFF_EXPONENT = ExceptionReportService.class.getPackage().getName().concat(".maximumBackoffExponent"); 96 | private static final String META_DATA_REPORT_ON_FROYO = ExceptionReportService.class.getPackage().getName().concat(".reportOnFroyo"); 97 | private static final String META_DATA_FIELDS_TO_SEND = ExceptionReportService.class.getPackage().getName().concat(".includeFields"); 98 | 99 | private static final String DEFAULT_FIELDS_TO_SEND = "all"; 100 | 101 | public ExceptionReportService() { 102 | super(ExceptionReportService.class.getSimpleName()); 103 | } 104 | 105 | @Override 106 | protected void onHandleIntent(Intent intent) { 107 | try { 108 | if (intent.getAction().equals(ACTION_SEND_REPORT)) { 109 | sendReport(intent); 110 | } 111 | } catch (Exception e) { 112 | // Catch all other exceptions as otherwise they would create an endless loop 113 | Log.e(TAG, "Error while sending an error report", e); 114 | } 115 | } 116 | 117 | private void sendReport(Intent intent) throws UnsupportedEncodingException, NameNotFoundException { 118 | Log.v(TAG, "Got request to report error: " + intent.toString()); 119 | Uri server = getTargetUrl(); 120 | 121 | boolean isManualReport = intent.getBooleanExtra(EXTRA_MANUAL_REPORT, false); 122 | boolean isReportOnFroyo = isReportOnFroyo(); 123 | boolean isFroyoOrAbove = isFroyoOrAbove(); 124 | if (isFroyoOrAbove && !isManualReport && !isReportOnFroyo) { 125 | // We don't send automatic reports on froyo or above 126 | Log.d(TAG, "Don't send automatic report on froyo"); 127 | return; 128 | } 129 | 130 | Set fieldsToSend = getFieldsToSend(); 131 | 132 | String stacktrace = intent.getStringExtra(EXTRA_STACK_TRACE); 133 | String exception = intent.getStringExtra(EXTRA_EXCEPTION_CLASS); 134 | String message = intent.getStringExtra(EXTRA_MESSAGE); 135 | long availableMemory = intent.getLongExtra(EXTRA_AVAILABLE_MEMORY, -1l); 136 | long totalMemory = intent.getLongExtra(EXTRA_TOTAL_MEMORY, -1l); 137 | String dateTime = intent.getStringExtra(EXTRA_EXCEPTION_TIME); 138 | String threadName = intent.getStringExtra(EXTRA_THREAD_NAME); 139 | String extraMessage = intent.getStringExtra(EXTRA_EXTRA_MESSAGE); 140 | List params = new ArrayList(); 141 | addNameValuePair(params, fieldsToSend, "exStackTrace", stacktrace); 142 | addNameValuePair(params, fieldsToSend, "exClass", exception); 143 | addNameValuePair(params, fieldsToSend, "exDateTime", dateTime); 144 | addNameValuePair(params, fieldsToSend, "exMessage", message); 145 | addNameValuePair(params, fieldsToSend, "exThreadName", threadName); 146 | if (extraMessage != null) addNameValuePair(params, fieldsToSend, "extraMessage", extraMessage); 147 | if (availableMemory >= 0) addNameValuePair(params, fieldsToSend, "devAvailableMemory", availableMemory+""); 148 | if (totalMemory >= 0) addNameValuePair(params, fieldsToSend, "devTotalMemory", totalMemory+""); 149 | 150 | PackageManager pm = getPackageManager(); 151 | try { 152 | PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0); 153 | addNameValuePair(params, fieldsToSend, "appVersionCode", packageInfo.versionCode+""); 154 | addNameValuePair(params, fieldsToSend, "appVersionName", packageInfo.versionName); 155 | addNameValuePair(params, fieldsToSend, "appPackageName", packageInfo.packageName); 156 | } catch (NameNotFoundException e) {} 157 | addNameValuePair(params, fieldsToSend, "devModel", android.os.Build.MODEL); 158 | addNameValuePair(params, fieldsToSend, "devSdk", android.os.Build.VERSION.SDK); 159 | addNameValuePair(params, fieldsToSend, "devReleaseVersion", android.os.Build.VERSION.RELEASE); 160 | 161 | HttpClient httpClient = new DefaultHttpClient(); 162 | HttpPost post = new HttpPost(server.toString()); 163 | post.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); 164 | Log.d(TAG, "Created post request"); 165 | 166 | try { 167 | httpClient.execute(post); 168 | Log.v(TAG, "Reported error: " + intent.toString()); 169 | } catch (ClientProtocolException e) { 170 | // Ignore this kind of error 171 | Log.e(TAG, "Error while sending an error report", e); 172 | } catch (SSLException e) { 173 | Log.e(TAG, "Error while sending an error report", e); 174 | } catch (IOException e) { 175 | if (e instanceof SocketException && e.getMessage().contains("Permission denied")) { 176 | Log.e(TAG, "You don't have internet permission", e); 177 | } else { 178 | int maximumRetryCount = getMaximumRetryCount(); 179 | int maximumExponent = getMaximumBackoffExponent(); 180 | // Retry at a later point in time 181 | AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE); 182 | int exponent = intent.getIntExtra(EXTRA_CURRENT_RETRY_COUNT, 0); 183 | intent.putExtra(EXTRA_CURRENT_RETRY_COUNT, exponent + 1); 184 | PendingIntent operation = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 185 | if (exponent >= maximumRetryCount) { 186 | // Discard error 187 | Log.w(TAG, "Error report reached the maximum retry count and will be discarded.\nStacktrace:\n"+stacktrace); 188 | return; 189 | } 190 | if (exponent > maximumExponent) { 191 | exponent = maximumExponent; 192 | } 193 | long backoff = (1 << exponent) * 1000; // backoff in ms 194 | alarmMgr.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + backoff, operation); 195 | } 196 | } 197 | } 198 | 199 | private void addNameValuePair(List list, Set fieldsToSend, String name, String value) { 200 | if (fieldsToSend.contains("all") || fieldsToSend.contains(name)) { 201 | list.add(new BasicNameValuePair(name, value)); 202 | } 203 | 204 | } 205 | 206 | private boolean isFroyoOrAbove() { 207 | int sdk = getSdkInt(); 208 | return sdk >= 8; 209 | } 210 | 211 | private int getSdkInt() { 212 | String sdk = android.os.Build.VERSION.SDK; 213 | try { 214 | return Integer.parseInt(sdk); 215 | } catch (NumberFormatException e) { 216 | return 1000; // Development version 217 | } 218 | } 219 | 220 | public Uri getTargetUrl() throws NameNotFoundException { 221 | ApplicationInfo info = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 222 | 223 | ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 224 | if (ai.metaData == null) throw new IllegalArgumentException(ExceptionReportService.class.getPackage().getName().concat("targetUrl is undefined")); 225 | String key = ExceptionReportService.class.getPackage().getName().concat(".targetUrl"); 226 | String urlString = null; 227 | if (info.metaData.containsKey(key)) { 228 | Object url = info.metaData.get(key); 229 | if (url instanceof String) { 230 | urlString = (String) url; 231 | } else if (url instanceof Integer) { 232 | int urlResId = info.metaData.getInt(key); 233 | urlString = getString(urlResId); 234 | } 235 | } 236 | if (urlString == null) { 237 | throw new IllegalArgumentException(ExceptionReportService.class.getPackage().getName().concat("targetUrl is undefined")); 238 | } 239 | return Uri.parse(urlString); 240 | } 241 | 242 | public int getMaximumRetryCount() throws NameNotFoundException { 243 | try { 244 | ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 245 | return ai.metaData.getInt(META_DATA_MAXIMUM_RETRY_COUNT, DEFAULT_MAXIMUM_RETRY_COUNT); 246 | } catch (NameNotFoundException e) { 247 | // Should never happen 248 | throw e; 249 | } 250 | } 251 | 252 | public int getMaximumBackoffExponent() throws NameNotFoundException { 253 | try { 254 | ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 255 | return ai.metaData.getInt(META_DATA_MAXIMUM_BACKOFF_EXPONENT, DEFAULT_MAXIMUM_BACKOFF_EXPONENT); 256 | } catch (NameNotFoundException e) { 257 | // Should never happen 258 | throw e; 259 | } 260 | } 261 | 262 | public Set getFieldsToSend() throws NameNotFoundException { 263 | try { 264 | HashSet result = new HashSet(); 265 | ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 266 | String fields = ai.metaData.getString(META_DATA_FIELDS_TO_SEND); 267 | if (fields == null) fields = DEFAULT_FIELDS_TO_SEND; 268 | StringTokenizer st = new StringTokenizer(fields, ","); 269 | while (st.hasMoreTokens()) { 270 | result.add(st.nextToken()); 271 | } 272 | return result; 273 | } catch (NameNotFoundException e) { 274 | // Should never happen 275 | throw e; 276 | } 277 | } 278 | 279 | public boolean isReportOnFroyo() throws NameNotFoundException { 280 | try { 281 | ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 282 | return ai.metaData.getBoolean(META_DATA_REPORT_ON_FROYO, DEFAULT_REPORT_ON_FROYO); 283 | } catch (NameNotFoundException e) { 284 | // Should never happen 285 | throw e; 286 | } 287 | } 288 | 289 | } 290 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ExceptionReporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The contents of this file are subject to the terms of the 3 | * Common Development and Distribution License (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * 6 | * You can obtain a copy of the license at LICENSE.txt 7 | * or http://www.opensource.org/licenses/cddl1.php. 8 | * See the License for the specific language governing permissions 9 | * and limitations under the License. 10 | * 11 | * When distributing Covered Code, include this CDDL HEADER in each 12 | * file and include the License file at LICENSE.txt. 13 | * If applicable, add the following below this CDDL HEADER, with the 14 | * fields enclosed by brackets "[]" replaced with your own identifying 15 | * information: Portions Copyright [yyyy] [name of copyright owner] 16 | * 17 | * 18 | * Copyright 2010 Tom Quist 19 | * All rights reserved Use is subject to license terms. 20 | */ 21 | package de.quist.app.errorreporter; 22 | 23 | import java.io.PrintWriter; 24 | import java.io.StringWriter; 25 | import java.io.Writer; 26 | import java.lang.Thread.UncaughtExceptionHandler; 27 | import java.text.SimpleDateFormat; 28 | import java.util.Date; 29 | import java.util.List; 30 | import java.util.Random; 31 | 32 | import android.app.Notification; 33 | import android.app.NotificationManager; 34 | import android.app.PendingIntent; 35 | import android.content.ComponentName; 36 | import android.content.Context; 37 | import android.content.Intent; 38 | import android.content.pm.ApplicationInfo; 39 | import android.content.pm.PackageManager; 40 | import android.content.pm.ResolveInfo; 41 | import android.content.pm.PackageManager.NameNotFoundException; 42 | import android.net.Uri; 43 | import android.os.Environment; 44 | import android.os.StatFs; 45 | import android.os.SystemClock; 46 | import android.text.TextUtils; 47 | import android.util.Log; 48 | 49 | public final class ExceptionReporter { 50 | 51 | private static final String TAG = ExceptionReporter.class.getSimpleName(); 52 | private static final String META_DATA_NOTIFICATION_ICON = ExceptionReporter.class.getPackage().getName().concat(".notificationIcon"); 53 | private static final String META_DATA_NOTIFICATION_TITLE = ExceptionReporter.class.getPackage().getName().concat(".notificationTitle"); 54 | private static final String META_DATA_NOTIFICATION_TEXT = ExceptionReporter.class.getPackage().getName().concat(".notificationText"); 55 | private static final String META_DATA_NOTIFICATION_TICKER_TEXT = ExceptionReporter.class.getPackage().getName().concat(".notificationTickerText"); 56 | 57 | private static final int DEFAULT_NOTIFICATION_ICON = android.R.drawable.stat_notify_error; 58 | private static final CharSequence DEFAULT_NOTIFICATION_TITLE = "^1 crashed"; 59 | private static final CharSequence DEFAULT_NOTIFICATION_TEXT = "Click here to help fixing the issue"; 60 | private static final CharSequence DEFAULT_NOTIFICATION_TICKER_TEXT = ""; 61 | 62 | /** 63 | * Registers this context and returns an error handler object 64 | * to be able to manually report errors. 65 | * 66 | * @param context The context 67 | * @return The error handler which can be used to manually report errors 68 | */ 69 | public static ExceptionReporter register(Context context) { 70 | UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); 71 | if (handler instanceof Handler) { 72 | Handler errHandler = (Handler) handler; 73 | errHandler.errorHandler.setContext(context); 74 | return errHandler.errorHandler; 75 | } else { 76 | ExceptionReporter errHandler = new ExceptionReporter(handler, context); 77 | Thread.setDefaultUncaughtExceptionHandler(errHandler.handler); 78 | return errHandler; 79 | } 80 | } 81 | 82 | private void setContext(Context context) { 83 | if (context.getApplicationContext() != null) { 84 | this.context = context.getApplicationContext(); 85 | } else { 86 | this.context = context; 87 | } 88 | } 89 | 90 | private Context context; 91 | private Handler handler; 92 | private ApplicationInfo applicationInfo; 93 | 94 | private ExceptionReporter(UncaughtExceptionHandler defaultHandler, Context context) { 95 | this.handler = new Handler(defaultHandler); 96 | this.setContext(context); 97 | } 98 | 99 | private class Handler implements UncaughtExceptionHandler { 100 | 101 | private UncaughtExceptionHandler subject; 102 | private ExceptionReporter errorHandler; 103 | 104 | private Handler(UncaughtExceptionHandler subject) { 105 | this.subject = subject; 106 | this.errorHandler = ExceptionReporter.this; 107 | } 108 | 109 | @Override 110 | public void uncaughtException(Thread thread, Throwable ex) { 111 | try { 112 | reportException(thread, ex, null, false); 113 | } catch (Exception e) { 114 | Log.e(TAG, "Error while reporting exception", e); 115 | } 116 | subject.uncaughtException(thread, ex); 117 | } 118 | 119 | } 120 | 121 | /** 122 | * Sends an error report. 123 | * 124 | * @param thread The thread where the exception occurred (e.g. {@link java.lang.Thread#currentThread()}) 125 | * @param ex The exception 126 | */ 127 | public void reportException(Thread thread, Throwable ex) { 128 | reportException(thread, ex, null, true); 129 | } 130 | 131 | /** 132 | * Sends an error report with an extra message. 133 | * 134 | * @param thread The thread where the exception occurred (e.g. {@link java.lang.Thread#currentThread()}) 135 | * @param ex The exception 136 | */ 137 | public void reportException(Thread thread, Throwable ex, String extraMessage) { 138 | reportException(thread, ex, extraMessage, true); 139 | } 140 | 141 | private void reportException(Thread thread, Throwable ex, String extraMessage, boolean manual) { 142 | final Writer writer = new StringWriter(); 143 | final PrintWriter pWriter = new PrintWriter(writer); 144 | ex.printStackTrace(pWriter); 145 | String stackTrace = writer.toString(); 146 | 147 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ"); 148 | 149 | Intent intent = new Intent(); 150 | intent.setData((Uri.parse("custom://" + SystemClock.elapsedRealtime()))); // Makes the intent unique 151 | intent.setAction(ExceptionReportService.ACTION_SEND_REPORT); 152 | intent.putExtra(ExceptionReportService.EXTRA_THREAD_NAME, thread.getName()); 153 | intent.putExtra(ExceptionReportService.EXTRA_EXCEPTION_CLASS, ex.getClass().getName()); 154 | intent.putExtra(ExceptionReportService.EXTRA_EXCEPTION_TIME, format.format(new Date())); 155 | intent.putExtra(ExceptionReportService.EXTRA_STACK_TRACE, stackTrace); 156 | intent.putExtra(ExceptionReportService.EXTRA_MESSAGE, ex.getMessage()); 157 | intent.putExtra(ExceptionReportService.EXTRA_MANUAL_REPORT, manual); 158 | intent.putExtra(ExceptionReportService.EXTRA_AVAILABLE_MEMORY, getAvailableInternalMemorySize()); 159 | intent.putExtra(ExceptionReportService.EXTRA_TOTAL_MEMORY, getTotalInternalMemorySize()); 160 | if (extraMessage != null) intent.putExtra(ExceptionReportService.EXTRA_EXTRA_MESSAGE, extraMessage); 161 | 162 | intent.setClass(context, ExceptionReportActivity.class); 163 | List resolvedActivities = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); 164 | if (!resolvedActivities.isEmpty()) { 165 | Log.v(TAG, ExceptionReportActivity.class.getSimpleName() + " is registered. Generating notification..."); 166 | Notification notification = new Notification(); 167 | notification.icon = getNotificationIcon(); 168 | notification.tickerText = getNotificationTickerText(); 169 | notification.flags |= Notification.FLAG_AUTO_CANCEL; 170 | notification.setLatestEventInfo(context, getNotificationTitle(), getNotificationMessage(), PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)); 171 | NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); 172 | nm.notify(new Random().nextInt(), notification); 173 | } else { 174 | intent.setClass(context, ExceptionReportService.class); 175 | ComponentName service = context.startService(intent); 176 | if (service == null) { 177 | Log.e(TAG, "Service has not be added to your AndroidManifest.xml\n" + 178 | "Add the following line to your manifest:\n" + 179 | ""); 180 | } 181 | } 182 | } 183 | 184 | private CharSequence getNotificationTickerText() { 185 | CharSequence result = DEFAULT_NOTIFICATION_TICKER_TEXT; 186 | ApplicationInfo info = getApplicationInfo(); 187 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_NOTIFICATION_TICKER_TEXT)) { 188 | int resId = info.metaData.getInt(META_DATA_NOTIFICATION_TICKER_TEXT); 189 | result = context.getText(resId); 190 | } 191 | return TextUtils.expandTemplate(result, context.getPackageManager().getApplicationLabel(info)); 192 | } 193 | 194 | private int getNotificationIcon() { 195 | int result = DEFAULT_NOTIFICATION_ICON; 196 | ApplicationInfo info = getApplicationInfo(); 197 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_NOTIFICATION_ICON)) { 198 | result = info.metaData.getInt(META_DATA_NOTIFICATION_ICON); 199 | } 200 | return result; 201 | } 202 | 203 | private CharSequence getNotificationTitle() { 204 | CharSequence result = DEFAULT_NOTIFICATION_TITLE; 205 | ApplicationInfo info = getApplicationInfo(); 206 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_NOTIFICATION_TITLE)) { 207 | int resId = info.metaData.getInt(META_DATA_NOTIFICATION_TITLE); 208 | result = context.getText(resId); 209 | } 210 | return TextUtils.expandTemplate(result, context.getPackageManager().getApplicationLabel(info)); 211 | } 212 | 213 | private CharSequence getNotificationMessage() { 214 | CharSequence result = DEFAULT_NOTIFICATION_TEXT; 215 | ApplicationInfo info = getApplicationInfo(); 216 | if (info != null && info.metaData != null && info.metaData.containsKey(META_DATA_NOTIFICATION_TEXT)) { 217 | int resId = info.metaData.getInt(META_DATA_NOTIFICATION_TEXT); 218 | result = context.getText(resId); 219 | } 220 | return TextUtils.expandTemplate(result, context.getPackageManager().getApplicationLabel(info)); 221 | } 222 | 223 | private ApplicationInfo getApplicationInfo() { 224 | if (this.applicationInfo != null) return this.applicationInfo; 225 | ApplicationInfo info; 226 | try { 227 | info = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); 228 | this.applicationInfo = info; 229 | return info; 230 | } catch (NameNotFoundException e) { 231 | return null; 232 | } 233 | } 234 | 235 | private long getAvailableInternalMemorySize() { 236 | StatFs stat = new StatFs(Environment.getDataDirectory().getPath()); 237 | return stat.getAvailableBlocks() * stat.getBlockSize(); 238 | } 239 | 240 | private long getTotalInternalMemorySize() { 241 | StatFs stat = new StatFs(Environment.getDataDirectory().getPath()); 242 | return stat.getBlockCount() * stat.getBlockSize(); 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The contents of this file are subject to the terms of the 3 | * Common Development and Distribution License (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * 6 | * You can obtain a copy of the license at LICENSE.txt 7 | * or http://www.opensource.org/licenses/cddl1.php. 8 | * See the License for the specific language governing permissions 9 | * and limitations under the License. 10 | * 11 | * When distributing Covered Code, include this CDDL HEADER in each 12 | * file and include the License file at LICENSE.txt. 13 | * If applicable, add the following below this CDDL HEADER, with the 14 | * fields enclosed by brackets "[]" replaced with your own identifying 15 | * information: Portions Copyright [yyyy] [name of copyright owner] 16 | * 17 | * 18 | * Copyright 2010 Tom Quist 19 | * All rights reserved Use is subject to license terms. 20 | */ 21 | package de.quist.app.errorreporter; 22 | 23 | import android.app.Activity; 24 | import android.os.Bundle; 25 | 26 | public class ReportingActivity extends Activity { 27 | 28 | private ExceptionReporter exceptionReporter; 29 | 30 | protected ExceptionReporter getExceptionReporter() { 31 | return exceptionReporter; 32 | } 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | exceptionReporter = ExceptionReporter.register(this); 37 | super.onCreate(savedInstanceState); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingActivityGroup.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.app.ActivityGroup; 4 | import android.os.Bundle; 5 | 6 | public class ReportingActivityGroup extends ActivityGroup { 7 | 8 | private ExceptionReporter exceptionReporter; 9 | 10 | protected ExceptionReporter getExceptionReporter() { 11 | return exceptionReporter; 12 | } 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | exceptionReporter = ExceptionReporter.register(this); 17 | super.onCreate(savedInstanceState); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingAliasActivity.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.app.AliasActivity; 4 | import android.os.Bundle; 5 | 6 | public class ReportingAliasActivity extends AliasActivity { 7 | 8 | private ExceptionReporter exceptionReporter; 9 | 10 | protected ExceptionReporter getExceptionReporter() { 11 | return exceptionReporter; 12 | } 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | exceptionReporter = ExceptionReporter.register(this); 17 | super.onCreate(savedInstanceState); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingExpandableListActivity.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.app.ExpandableListActivity; 4 | import android.os.Bundle; 5 | 6 | public class ReportingExpandableListActivity extends ExpandableListActivity { 7 | 8 | private ExceptionReporter exceptionReporter; 9 | 10 | protected ExceptionReporter getExceptionReporter() { 11 | return exceptionReporter; 12 | } 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | exceptionReporter = ExceptionReporter.register(this); 17 | super.onCreate(savedInstanceState); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingIntentService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The contents of this file are subject to the terms of the 3 | * Common Development and Distribution License (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * 6 | * You can obtain a copy of the license at LICENSE.txt 7 | * or http://www.opensource.org/licenses/cddl1.php. 8 | * See the License for the specific language governing permissions 9 | * and limitations under the License. 10 | * 11 | * When distributing Covered Code, include this CDDL HEADER in each 12 | * file and include the License file at LICENSE.txt. 13 | * If applicable, add the following below this CDDL HEADER, with the 14 | * fields enclosed by brackets "[]" replaced with your own identifying 15 | * information: Portions Copyright [yyyy] [name of copyright owner] 16 | * 17 | * 18 | * Copyright 2010 Tom Quist 19 | * All rights reserved Use is subject to license terms. 20 | */ 21 | package de.quist.app.errorreporter; 22 | 23 | import android.app.IntentService; 24 | 25 | public abstract class ReportingIntentService extends IntentService { 26 | 27 | public ReportingIntentService(String name) { 28 | super(name); 29 | } 30 | 31 | private ExceptionReporter exceptionReporter; 32 | 33 | protected ExceptionReporter getExceptionReporter() { 34 | return exceptionReporter; 35 | } 36 | 37 | @Override 38 | public void onCreate() { 39 | exceptionReporter = ExceptionReporter.register(this); 40 | super.onCreate(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingLauncherActivity.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.app.LauncherActivity; 4 | import android.os.Bundle; 5 | 6 | public abstract class ReportingLauncherActivity extends LauncherActivity { 7 | 8 | private ExceptionReporter exceptionReporter; 9 | 10 | protected ExceptionReporter getExceptionReporter() { 11 | return exceptionReporter; 12 | } 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | exceptionReporter = ExceptionReporter.register(this); 17 | super.onCreate(savedInstanceState); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingListActivity.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.app.ListActivity; 4 | import android.os.Bundle; 5 | 6 | public class ReportingListActivity extends ListActivity { 7 | 8 | private ExceptionReporter exceptionReporter; 9 | 10 | protected ExceptionReporter getExceptionReporter() { 11 | return exceptionReporter; 12 | } 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | exceptionReporter = ExceptionReporter.register(this); 17 | super.onCreate(savedInstanceState); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingPreferenceActivity.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.os.Bundle; 4 | import android.preference.PreferenceActivity; 5 | 6 | public class ReportingPreferenceActivity extends PreferenceActivity { 7 | 8 | private ExceptionReporter exceptionReporter; 9 | 10 | protected ExceptionReporter getExceptionReporter() { 11 | return exceptionReporter; 12 | } 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | exceptionReporter = ExceptionReporter.register(this); 17 | super.onCreate(savedInstanceState); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The contents of this file are subject to the terms of the 3 | * Common Development and Distribution License (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * 6 | * You can obtain a copy of the license at LICENSE.txt 7 | * or http://www.opensource.org/licenses/cddl1.php. 8 | * See the License for the specific language governing permissions 9 | * and limitations under the License. 10 | * 11 | * When distributing Covered Code, include this CDDL HEADER in each 12 | * file and include the License file at LICENSE.txt. 13 | * If applicable, add the following below this CDDL HEADER, with the 14 | * fields enclosed by brackets "[]" replaced with your own identifying 15 | * information: Portions Copyright [yyyy] [name of copyright owner] 16 | * 17 | * 18 | * Copyright 2010 Tom Quist 19 | * All rights reserved Use is subject to license terms. 20 | */ 21 | package de.quist.app.errorreporter; 22 | 23 | import android.app.Service; 24 | 25 | public abstract class ReportingService extends Service { 26 | 27 | private ExceptionReporter exceptionReporter; 28 | 29 | protected ExceptionReporter getExceptionReporter() { 30 | return exceptionReporter; 31 | } 32 | 33 | @Override 34 | public void onCreate() { 35 | exceptionReporter = ExceptionReporter.register(this); 36 | super.onCreate(); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/de/quist/app/errorreporter/ReportingTabActivity.java: -------------------------------------------------------------------------------- 1 | package de.quist.app.errorreporter; 2 | 3 | import android.app.TabActivity; 4 | import android.os.Bundle; 5 | 6 | public class ReportingTabActivity extends TabActivity { 7 | 8 | private ExceptionReporter exceptionReporter; 9 | 10 | protected ExceptionReporter getExceptionReporter() { 11 | return exceptionReporter; 12 | } 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | exceptionReporter = ExceptionReporter.register(this); 17 | super.onCreate(savedInstanceState); 18 | } 19 | 20 | } 21 | --------------------------------------------------------------------------------