├── .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 | Name |
91 | Type |
92 | Default |
93 | Description |
94 |
95 |
96 | maximumRetryCount |
97 | int |
98 | 17 |
99 | Maximum number of tries to send an error report |
100 |
101 |
102 | maximumBackoffExponent |
103 | int |
104 | 12 |
105 | Maximum exponent for the back-off |
106 |
107 |
108 | reportOnFroyo |
109 | boolean |
110 | false |
111 | Defines whether unhandled exception are reported on Android 2.2 (Froyo) and above or not, since Froyo has its own error-reporting system |
112 |
113 |
114 | includeFields |
115 | String |
116 | all |
117 | Comma-separated list of fields to send (field names as of section Server). If the list contains all, all available fields will be included |
118 |
119 |
120 | dialogIcon |
121 | int |
122 | @android:drawable/ic_dialog_alert |
123 | The icon of the dialog for user approved error reports. (Use the android:resource attribute and reference to an existing resource) |
124 |
125 |
126 | dialogTitle |
127 | int |
128 | ^1 crashed |
129 | The 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) |
130 |
131 |
132 | dialogText |
133 | int |
134 | ^1 crashed because of an unexpected error. Please help fixing the error by sending an error report. |
135 | 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) |
136 |
137 |
138 | dialogMessageHint |
139 | int |
140 | Undefined |
141 | If 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) |
142 |
143 |
144 | dialogSendButton |
145 | int |
146 | @android:string/ok |
147 | Text on the send-button of the dialog for user approved error reports. (Use the android:resource attribute and reference to an existing resource) |
148 |
149 |
150 | dialogCancelButton |
151 | int |
152 | @android:string/cancel |
153 | Text on the cancel-button of the dialog for user approved error reports. (Use the android:resource attribute and reference to an existing resource)> |
154 |
155 |
156 | notificationIcon |
157 | int |
158 | @android:drawable/stat_notify_error |
159 | Icon of the notification for user approved error reports. (Use the android:resource attribute and reference to an existing resource) |
160 |
161 |
162 | notificationTitle |
163 | int |
164 | ^1 crashed |
165 | The 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) |
166 |
167 |
168 | notificationText |
169 | int |
170 | Click here to help fixing the issue |
171 | The 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) |
172 |
173 |
174 | notificationTickerText |
175 | int |
176 | Empty |
177 | The 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) |
178 |
179 |
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 | exStackTrace |
190 | The stack trace |
191 |
192 |
193 | exClass |
194 | The exception class |
195 |
196 |
197 | exMessage |
198 | The exceptions message |
199 |
200 |
201 | exDateTime |
202 | The date and time when the exception happend in the format "yyyy-MM-dd HH:mm:ssZ" (SimpleDateFormat) |
203 |
204 |
205 | extraMessage |
206 | A custom message which can be added to manual error reports |
207 |
208 |
209 | exThreadName |
210 | The name of the thread the error has been thrown in |
211 |
212 |
213 | appVersionCode |
214 | The version code (as defined in your AndroidManifest.xml) |
215 |
216 |
217 | appVersionName |
218 | The version name (as defined in your AndroidManifest.xml) |
219 |
220 |
221 | appPackageName |
222 | The package name (as defined in your AndroidManifest.xml) |
223 |
224 |
225 | devAvailableMemory |
226 | The devices available memory in bytes |
227 |
228 |
229 | devTotalMemory |
230 | The devices total memory in bytes |
231 |
232 |
233 | devModel |
234 | The phones model name (android.os.Build.MODEL) |
235 |
236 |
237 | devSdk |
238 | The phones sdk version (android.os.Build.VERSION.SDK) |
239 |
240 |
241 | devReleaseVersion |
242 | The phones release version (android.os.Build.VERSION.RELEASE) |
243 |
244 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------