Compare commits

..

10 Commits

Author SHA1 Message Date
6b9cbdef84 Import to intelij, started updating to use maps v2 2014-08-30 11:29:50 -04:00
ab75dd3bb1 Added files needed to build with ant 2012-08-09 13:21:37 -04:00
4de767f753 Cleaned up code 2012-07-22 09:36:07 -04:00
5e6e6c4dd9 I removed the compat library, and imported the NotificationConpat
classes. This will prevent compilation errors.
2012-07-18 15:00:38 -04:00
507d7901bf Updated notifications to use the compat notification builder
closes #124
2012-07-16 13:44:16 -04:00
9ec67079c0 Added .NOTEMPTY to the drawables file 2012-07-14 12:15:07 -04:00
42cb0b9f0e Cleaned Up code
Signed-off-by: Ricky Barrette <rickbarrette@gmail.com>
2012-07-01 11:21:18 -04:00
c4980a8295 Fixed: Multiple reports filed
closes #102
Signed-off-by: Ricky Barrette <rickbarrette@gmail.com>
2012-06-04 12:10:09 -04:00
824f58879c I moved the report filing work, to it's own thread
closes #96

Signed-off-by: Ricky Barrette <rickbarrette@gmail.com>
2012-06-03 11:52:49 -04:00
9b7c2594c9 Fixed some lint errors
Signed-off-by: Ricky Barrette <rickbarrette@gmail.com>
2012-06-03 11:35:36 -04:00
18 changed files with 1034 additions and 397 deletions

View File

@@ -4,5 +4,6 @@
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@@ -4,7 +4,9 @@
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="1" />
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.INTERNET" />
@@ -14,7 +16,9 @@
android:configChanges="keyboard|keyboardHidden|orientation" >
</activity>
<service android:name="ReportPostingService" >
<service
android:name="ReportPostingService"
android:process=":reportpostingservice" >
</service>
</application>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="EclipseModuleManager" forced_jdk="true">
<conelement value="com.android.ide.eclipse.adt.DEPENDENCIES" />
<src_description expected_position="1">
<src_folder value="file://$MODULE_DIR$/src" expected_position="0" />
<src_folder value="file://$MODULE_DIR$/gen" expected_position="1" />
<src_folder value="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK" expected_position="2" />
<src_folder value="com.android.ide.eclipse.adt.LIBRARIES" expected_position="3" />
</src_description>
</component>
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="LIBRARY_PROJECT" value="true" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/bin/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
</content>
<orderEntry type="jdk" jdkName="Android 4.3 Google APIs" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="ExceptionHandlerLib" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
</lint>

View File

@@ -0,0 +1,10 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/app/android-sdk-linux_86

View File

@@ -0,0 +1,20 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -9,4 +9,4 @@
android.library=true
# Project target.
target=android-15
target=android-17

View File

@@ -0,0 +1,388 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package anroid.v4.compat;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;
public class NotificationCompat {
/**
* Bit to be bitwise-ored into the {@link Notification#flags} field that should be set if
* this notification represents a high-priority event that may be shown to the user even
* if notifications are otherwise unavailable (that is, when the status bar is hidden).
* This flag is ideally used in conjunction with fullScreenIntent.
*
* <p>This will only be respected on API level 9 and above.</p>
*/
public static final int FLAG_HIGH_PRIORITY = 0x00000080;
private static final NotificationCompatImpl IMPL;
interface NotificationCompatImpl {
public Notification getNotification(Builder b);
}
static class NotificationCompatImplBase implements NotificationCompatImpl {
public Notification getNotification(Builder b) {
Notification result = (Notification) b.mNotification;
result.setLatestEventInfo(b.mContext, b.mContentTitle,
b.mContentText, b.mContentIntent);
return result;
}
}
static class NotificationCompatImplHoneycomb implements NotificationCompatImpl {
public Notification getNotification(Builder b) {
return NotificationCompatHoneycomb.add(b.mContext, b.mNotification,
b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon);
}
}
static {
if (Build.VERSION.SDK_INT >= 11) {
IMPL = new NotificationCompatImplHoneycomb();
} else {
IMPL = new NotificationCompatImplBase();
}
}
/**
* Builder class for {@link Notification} objects. Allows easier control over
* all the flags, as well as help constructing the typical notification layouts.
*/
public static class Builder {
Context mContext;
CharSequence mContentTitle;
CharSequence mContentText;
PendingIntent mContentIntent;
PendingIntent mFullScreenIntent;
RemoteViews mTickerView;
Bitmap mLargeIcon;
CharSequence mContentInfo;
int mNumber;
Notification mNotification = new Notification();
/**
* Constructor.
*
* Automatically sets the when field to {@link System#currentTimeMillis()
* System.currentTimeMillis()} and the audio stream to the
* {@link Notification#STREAM_DEFAULT}.
*
* @param context A {@link Context} that will be used to construct the
* RemoteViews. The Context will not be held past the lifetime of this
* Builder object.
*/
public Builder(Context context) {
mContext = context;
// Set defaults to match the defaults of a Notification
mNotification.when = System.currentTimeMillis();
mNotification.audioStreamType = Notification.STREAM_DEFAULT;
}
/**
* Set the time that the event occurred. Notifications in the panel are
* sorted by this time.
*/
public Builder setWhen(long when) {
mNotification.when = when;
return this;
}
/**
* Set the small icon to use in the notification layouts. Different classes of devices
* may return different sizes. See the UX guidelines for more information on how to
* design these icons.
*
* @param icon A resource ID in the application's package of the drawble to use.
*/
public Builder setSmallIcon(int icon) {
mNotification.icon = icon;
return this;
}
/**
* A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
* level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
* LevelListDrawable}.
*
* @param icon A resource ID in the application's package of the drawble to use.
* @param level The level to use for the icon.
*
* @see android.graphics.drawable.LevelListDrawable
*/
public Builder setSmallIcon(int icon, int level) {
mNotification.icon = icon;
mNotification.iconLevel = level;
return this;
}
/**
* Set the title (first row) of the notification, in a standard notification.
*/
public Builder setContentTitle(CharSequence title) {
mContentTitle = title;
return this;
}
/**
* Set the text (second row) of the notification, in a standard notification.
*/
public Builder setContentText(CharSequence text) {
mContentText = text;
return this;
}
/**
* Set the large number at the right-hand side of the notification. This is
* equivalent to setContentInfo, although it might show the number in a different
* font size for readability.
*/
public Builder setNumber(int number) {
mNumber = number;
return this;
}
/**
* Set the large text at the right-hand side of the notification.
*/
public Builder setContentInfo(CharSequence info) {
mContentInfo = info;
return this;
}
/**
* Set the progress this notification represents, which may be
* represented as a {@link ProgressBar}.
*/
/* TODO
public Builder setProgress(int max, int progress, boolean indeterminate) {
mProgressMax = max;
mProgress = progress;
mProgressIndeterminate = indeterminate;
return this;
}*/
/**
* Supply a custom RemoteViews to use instead of the standard one.
*/
public Builder setContent(RemoteViews views) {
mNotification.contentView = views;
return this;
}
/**
* Supply a {@link PendingIntent} to send when the notification is clicked.
* If you do not supply an intent, you can now add PendingIntents to individual
* views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent
* RemoteViews.setOnClickPendingIntent(int,PendingIntent)}. Be sure to
* read {@link Notification#contentIntent Notification.contentIntent} for
* how to correctly use this.
*/
public Builder setContentIntent(PendingIntent intent) {
mContentIntent = intent;
return this;
}
/**
* Supply a {@link PendingIntent} to send when the notification is cleared by the user
* directly from the notification panel. For example, this intent is sent when the user
* clicks the "Clear all" button, or the individual "X" buttons on notifications. This
* intent is not sent when the application calls {@link NotificationManager#cancel
* NotificationManager.cancel(int)}.
*/
public Builder setDeleteIntent(PendingIntent intent) {
mNotification.deleteIntent = intent;
return this;
}
/**
* An intent to launch instead of posting the notification to the status bar.
* Only for use with extremely high-priority notifications demanding the user's
* <strong>immediate</strong> attention, such as an incoming phone call or
* alarm clock that the user has explicitly set to a particular time.
* If this facility is used for something else, please give the user an option
* to turn it off and use a normal notification, as this can be extremely
* disruptive.
*
* @param intent The pending intent to launch.
* @param highPriority Passing true will cause this notification to be sent
* even if other notifications are suppressed.
*/
public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
mFullScreenIntent = intent;
setFlag(FLAG_HIGH_PRIORITY, highPriority);
return this;
}
/**
* Set the text that is displayed in the status bar when the notification first
* arrives.
*/
public Builder setTicker(CharSequence tickerText) {
mNotification.tickerText = tickerText;
return this;
}
/**
* Set the text that is displayed in the status bar when the notification first
* arrives, and also a RemoteViews object that may be displayed instead on some
* devices.
*/
public Builder setTicker(CharSequence tickerText, RemoteViews views) {
mNotification.tickerText = tickerText;
mTickerView = views;
return this;
}
/**
* Set the large icon that is shown in the ticker and notification.
*/
public Builder setLargeIcon(Bitmap icon) {
mLargeIcon = icon;
return this;
}
/**
* Set the sound to play. It will play on the default stream.
*/
public Builder setSound(Uri sound) {
mNotification.sound = sound;
mNotification.audioStreamType = Notification.STREAM_DEFAULT;
return this;
}
/**
* Set the sound to play. It will play on the stream you supply.
*
* @see #STREAM_DEFAULT
* @see AudioManager for the <code>STREAM_</code> constants.
*/
public Builder setSound(Uri sound, int streamType) {
mNotification.sound = sound;
mNotification.audioStreamType = streamType;
return this;
}
/**
* Set the vibration pattern to use.
*
* @see android.os.Vibrator for a discussion of the <code>pattern</code>
* parameter.
*/
public Builder setVibrate(long[] pattern) {
mNotification.vibrate = pattern;
return this;
}
/**
* Set the argb value that you would like the LED on the device to blnk, as well as the
* rate. The rate is specified in terms of the number of milliseconds to be on
* and then the number of milliseconds to be off.
*/
public Builder setLights(int argb, int onMs, int offMs) {
mNotification.ledARGB = argb;
mNotification.ledOnMS = onMs;
mNotification.ledOffMS = offMs;
boolean showLights = mNotification.ledOnMS != 0 && mNotification.ledOffMS != 0;
mNotification.flags = (mNotification.flags & ~Notification.FLAG_SHOW_LIGHTS) |
(showLights ? Notification.FLAG_SHOW_LIGHTS : 0);
return this;
}
/**
* Set whether this is an ongoing notification.
*
* <p>Ongoing notifications differ from regular notifications in the following ways:
* <ul>
* <li>Ongoing notifications are sorted above the regular notifications in the
* notification panel.</li>
* <li>Ongoing notifications do not have an 'X' close button, and are not affected
* by the "Clear all" button.
* </ul>
*/
public Builder setOngoing(boolean ongoing) {
setFlag(Notification.FLAG_ONGOING_EVENT, ongoing);
return this;
}
/**
* Set this flag if you would only like the sound, vibrate
* and ticker to be played if the notification is not already showing.
*/
public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
setFlag(Notification.FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
return this;
}
/**
* Setting this flag will make it so the notification is automatically
* canceled when the user clicks it in the panel. The PendingIntent
* set with {@link #setDeleteIntent} will be broadcast when the notification
* is canceled.
*/
public Builder setAutoCancel(boolean autoCancel) {
setFlag(Notification.FLAG_AUTO_CANCEL, autoCancel);
return this;
}
/**
* Set the default notification options that will be used.
* <p>
* The value should be one or more of the following fields combined with
* bitwise-or:
* {@link Notification#DEFAULT_SOUND}, {@link Notification#DEFAULT_VIBRATE},
* {@link Notification#DEFAULT_LIGHTS}.
* <p>
* For all default values, use {@link Notification#DEFAULT_ALL}.
*/
public Builder setDefaults(int defaults) {
mNotification.defaults = defaults;
if ((defaults & Notification.DEFAULT_LIGHTS) != 0) {
mNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
}
return this;
}
private void setFlag(int mask, boolean value) {
if (value) {
mNotification.flags |= mask;
} else {
mNotification.flags &= ~mask;
}
}
/**
* Combine all of the options that have been set and return a new {@link Notification}
* object.
*/
public Notification getNotification() {
return (Notification) IMPL.getNotification(this);
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package anroid.v4.compat;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.RemoteViews;
class NotificationCompatHoneycomb {
static Notification add(Context context, Notification n,
CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
RemoteViews tickerView, int number,
PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon) {
Notification.Builder b = new Notification.Builder(context)
.setWhen(n.when)
.setSmallIcon(n.icon, n.iconLevel)
.setContent(n.contentView)
.setTicker(n.tickerText, tickerView)
.setSound(n.sound, n.audioStreamType)
.setVibrate(n.vibrate)
.setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
.setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
.setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
.setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
.setDefaults(n.defaults)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setContentInfo(contentInfo)
.setContentIntent(contentIntent)
.setDeleteIntent(n.deleteIntent)
.setFullScreenIntent(fullScreenIntent,
(n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
.setLargeIcon(largeIcon)
.setNumber(number);
return b.getNotification();
}
}

View File

@@ -14,7 +14,6 @@ import java.util.Date;
import java.util.Properties;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -28,23 +27,26 @@ import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Build;
import android.util.Log;
import anroid.v4.compat.NotificationCompat;
/**
* This is Twenty Codes, LLC Exception Handler of Awesomeness!
* This class will be used to generate reports that will be emailed to us via the users email client after the users approval
* This is Twenty Codes, LLC Exception Handler of Awesomeness! This class will
* be used to generate reports that will be emailed to us via the users email
* client after the users approval
*
* @author ricky barrette
*/
public class ExceptionHandler implements UncaughtExceptionHandler, Runnable {
private static final String MSG_SUBJECT_TAG = "Exception Report";
private static final String MSG_BODY = "Just click send to help make this application better. "+
"No personal information is being sent (you can check by reading the rest of the email).";
private static final String MSG_BODY = "Just click send to help make this application better. "
+ "No personal information is being sent (you can check by reading the rest of the email).";
protected static final int SIMPLE_NOTFICATION_ID = 45684645;
private Thread.UncaughtExceptionHandler mDefaultUEH;
private Activity mApp= null;
private final Thread.UncaughtExceptionHandler mDefaultUEH;
private Activity mApp = null;
private Service mService = null;
private BroadcastReceiver mBroadcastReceiver = null;
private Context mContext;
private final Context mContext;
private Report mReport;
private static final String TAG = "ExceptionHandler";
private String mURL = null;
@@ -54,61 +56,65 @@ public class ExceptionHandler implements UncaughtExceptionHandler, Runnable {
/**
* Creates a new ExceptionHandler
*
* @param app
* @author ricky barrette
*/
public ExceptionHandler(Activity app) {
this.mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
this.mApp = app;
this.mContext = app;
parseProperties();
}
/**
* Creates a new ExceptionHandler
* @param broadcastReceiver
* @author ricky barrette
*/
public ExceptionHandler(BroadcastReceiver broadcastReceiver, Context context){
this.mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
this.mBroadcastReceiver = broadcastReceiver;
this.mContext = context;
public ExceptionHandler(final Activity app) {
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
mApp = app;
mContext = app;
parseProperties();
}
/**
* Creates a new ExceptionHandler
*
* @param broadcastReceiver
* @author ricky barrette
*/
public ExceptionHandler(final BroadcastReceiver broadcastReceiver, final Context context) {
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
mBroadcastReceiver = broadcastReceiver;
mContext = context;
parseProperties();
}
/**
* Creates a new ExceptionHandler
*
* @param service
* @author ricky barrette
*/
public ExceptionHandler(Service service){
this.mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
this.mService = service;
this.mContext = service;
public ExceptionHandler(final Service service) {
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
mService = service;
mContext = service;
parseProperties();
}
/**
* Generates an email from the report
*
* @author ricky barrette
*/
private void displayEmailNotification(){
private void displayEmailNotification() {
Log.i(TAG, "displayEmailNotification");
CharSequence title = null;
if(mApp != null)
if (mApp != null)
title = mApp.getTitle();
if(mService != null)
if (mService != null)
title = mService.getClass().getName();
if(mBroadcastReceiver != null)
if (mBroadcastReceiver != null)
title = mBroadcastReceiver.getClass().getName();
Intent intent = new Intent(Intent.ACTION_SEND);
String theSubject = title + " " + MSG_SUBJECT_TAG;
String theBody = "\n\n"+MSG_BODY+this.mReport.toString();
intent.putExtra(Intent.EXTRA_EMAIL,new String[] {this.mEmail});
final Intent intent = new Intent(Intent.ACTION_SEND);
final String theSubject = title + " " + MSG_SUBJECT_TAG;
final String theBody = "\n\n" + MSG_BODY + mReport.toString();
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { mEmail });
intent.putExtra(Intent.EXTRA_TEXT, theBody);
intent.putExtra(Intent.EXTRA_SUBJECT, theSubject);
intent.setType("message/rfc822");
@@ -117,114 +123,116 @@ public class ExceptionHandler implements UncaughtExceptionHandler, Runnable {
}
/**
* displays an notification in the status bar, letting the user know that there was an issue
* displays an notification in the status bar, letting the user know that
* there was an issue
*
* @param generatedReportIntent
*/
@SuppressWarnings("deprecation")
private void displayNotification(Intent generatedReportIntent) {
private void displayNotification(final Intent generatedReportIntent) {
Log.i(TAG, "displayNotification");
Context context = mContext.getApplicationContext();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notifyDetails = new Notification(android.R.drawable.stat_notify_error, context.getString(R.string.sorry), System.currentTimeMillis());
PendingIntent intent = PendingIntent.getActivity(context, 0, generatedReportIntent, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
notifyDetails.setLatestEventInfo(context, context.getString(R.string.crash), context.getString(R.string.sorry), intent);
notifyDetails.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(SIMPLE_NOTFICATION_ID, notifyDetails);
final Context context = mContext.getApplicationContext();
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
final PendingIntent intent = PendingIntent.getActivity(context, 0, generatedReportIntent, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setContentTitle(context.getString(R.string.crash))
.setContentText(context.getString(R.string.sorry)).setTicker(context.getString(R.string.crash)).setSmallIcon(android.R.drawable.stat_notify_error)
.setWhen(System.currentTimeMillis()).setAutoCancel(true).setContentIntent(intent);
notificationManager.notify(SIMPLE_NOTFICATION_ID, builder.getNotification());
}
/**
* parses in the exception handler options from the client application's assets folder. /assets/exceptionhandler.properties
* parses in the exception handler options from the client application's
* assets folder. /assets/exceptionhandler.properties
*
* @author ricky barrette
*/
private void parseProperties() {
Resources resources = this.mContext.getResources();
AssetManager assetManager = resources.getAssets();
final Resources resources = mContext.getResources();
final AssetManager assetManager = resources.getAssets();
// Read from the /assets directory
try {
InputStream inputStream = assetManager.open("exceptionhandler.properties");
Properties properties = new Properties();
properties.load(inputStream);
this.mURL = properties.getProperty("server");
this.mEmail = properties.getProperty("email");
this.mAppName = properties.getProperty("app");
this.mTracker = properties.getProperty("tracker");
} catch (IOException e) {
Log.e(TAG, "Failed to open exceptionhandler.properties");
e.printStackTrace();
final InputStream inputStream = assetManager.open("exceptionhandler.properties");
final Properties properties = new Properties();
properties.load(inputStream);
mURL = properties.getProperty("server");
mEmail = properties.getProperty("email");
mAppName = properties.getProperty("app");
mTracker = properties.getProperty("tracker");
} catch (final IOException e) {
Log.e(TAG, "Failed to open exceptionhandler.properties");
e.printStackTrace();
}
}
@Override
public void run() {
if(this.mEmail == null)
displayNotification(new Intent(this.mContext, ExceptionReportActivity.class).putExtra("report", this.mReport));
if (mEmail == null)
displayNotification(new Intent(mContext, ExceptionReportActivity.class).putExtra("report", mReport));
else
displayEmailNotification();
}
/**
* Called when there is an uncaught exception.
* (non-Javadoc)
* @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, java.lang.Throwable)
* Called when there is an uncaught exception. (non-Javadoc)
*
* @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread,
* java.lang.Throwable)
* @author ricky barrette
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
public void uncaughtException(final Thread t, final Throwable e) {
Log.d(TAG, "uncaughtException()");
// Log.d(TAG,"mURL = "+ this.mURL);
// Log.d(TAG,"mEmail = "+ this.mEmail);
// Log.d(TAG,"mURL = "+ this.mURL);
// Log.d(TAG,"mEmail = "+ this.mEmail);
Date theDate = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss_zzz");
PackageManager pm = mContext.getPackageManager();
final Date theDate = new Date();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss_zzz");
final PackageManager pm = mContext.getPackageManager();
//app environment;
// app environment;
PackageInfo pi;
try {
pi = pm.getPackageInfo(mContext.getPackageName(), 0);
} catch (NameNotFoundException eNnf) {
//doubt this will ever run since we want info about our own package
} catch (final NameNotFoundException eNnf) {
// doubt this will ever run since we want info about our own package
pi = new PackageInfo();
pi.versionName = "unknown";
pi.versionCode = 69;
}
StringBuffer report = new StringBuffer();
for (StackTraceElement item : e.getStackTrace())
report.append("at "+item.toString() + "\n");
final StringBuffer report = new StringBuffer();
for (final StackTraceElement item : e.getStackTrace())
report.append("at " + item.toString() + "\n");
StringBuffer causereport = new StringBuffer();
Throwable cause = e.getCause();
final StringBuffer causereport = new StringBuffer();
final Throwable cause = e.getCause();
if (cause != null) {
causereport.append(cause.toString() + "\n \n");
for (StackTraceElement item : cause.getStackTrace())
causereport.append("at "+item.toString() + "\n");
for (final StackTraceElement item : cause.getStackTrace())
causereport.append("at " + item.toString() + "\n");
}
//generate the report
this.mReport = new Report(mURL).generateReport(e.toString(), report.toString(), causereport.toString(), sdf.format(theDate), Build.FINGERPRINT, pi.versionName+"b"+pi.versionCode, mAppName != null ? mAppName : mContext.getPackageName(), this.mTracker, mContext.getPackageName());
// generate the report
mReport = new Report(mURL).generateReport(e.toString(), report.toString(), causereport.toString(), sdf.format(theDate), Build.FINGERPRINT, pi.versionName + "b"
+ pi.versionCode, mAppName != null ? mAppName : mContext.getPackageName(), mTracker, mContext.getPackageName());
//try to send file contents via email (need to do so via the UI thread)
if(this.mApp != null){
this.mApp.runOnUiThread(this);
}
// try to send file contents via email (need to do so via the UI thread)
if (mApp != null)
mApp.runOnUiThread(this);
if(this.mService != null){
if(this.mEmail == null)
displayNotification(new Intent(this.mContext, ExceptionReportActivity.class).putExtra("report", this.mReport));
if (mService != null)
if (mEmail == null)
displayNotification(new Intent(mContext, ExceptionReportActivity.class).putExtra("report", mReport));
else
displayEmailNotification();
}
if(this.mBroadcastReceiver != null){
if(this.mEmail == null)
displayNotification(new Intent(this.mContext, ExceptionReportActivity.class).putExtra("report", this.mReport));
if (mBroadcastReceiver != null)
if (mEmail == null)
displayNotification(new Intent(mContext, ExceptionReportActivity.class).putExtra("report", mReport));
else
displayEmailNotification();
}
//do not forget to pass this exception through up the chain
mDefaultUEH.uncaughtException(t,e);
// do not forget to pass this exception through up the chain
mDefaultUEH.uncaughtException(t, e);
}
}

View File

@@ -16,7 +16,9 @@ import android.widget.EditText;
import android.widget.ListView;
/**
* This activity will be used to present the user with the exception report, and allows them to send it, or not
* This activity will be used to present the user with the exception report, and
* allows them to send it, or not
*
* @author ricky barrette
*/
public class ExceptionReportActivity extends Activity implements OnClickListener {
@@ -26,37 +28,39 @@ public class ExceptionReportActivity extends Activity implements OnClickListener
/**
* (non-Javadoc)
* @see android.app.Activity#onCreate(android.os.Bundle)
* @author ricky barrette
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
this.mReport = (Report) this.getIntent().getParcelableExtra("report");
if(this.getIntent().hasExtra("display"))
this.setContentView(R.layout.list);
else {
this.setContentView(R.layout.exception_activity);
this.findViewById(R.id.send).setOnClickListener(this);
}
ListView list = (ListView) this.findViewById(android.R.id.list);
list.setAdapter(new ReportAdapter(this, this.mReport.getReport()));
list.setClickable(false);
}
/**
* (non-Javadoc)
*
* @see android.view.View.OnClickListener#onClick(android.view.View)
* @author ricky barrette
*/
@Override
public void onClick(View v) {
EditText description = (EditText) findViewById(R.id.description);
this.mReport.setDescription(description.getText().toString());
public void onClick(final View v) {
final EditText description = (EditText) findViewById(R.id.description);
mReport.setDescription(description.getText().toString());
v.setEnabled(false);
this.startService(new Intent(this, ReportPostingService.class).putExtra("report", this.mReport));
this.finish();
startService(new Intent(this, ReportPostingService.class).putExtra("report", mReport));
finish();
}
/**
* (non-Javadoc)
*
* @see android.app.Activity#onCreate(android.os.Bundle)
* @author ricky barrette
*/
@Override
protected void onCreate(final Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
mReport = (Report) getIntent().getParcelableExtra("report");
if (getIntent().hasExtra("display"))
this.setContentView(R.layout.list);
else {
this.setContentView(R.layout.exception_activity);
findViewById(R.id.send).setOnClickListener(this);
}
final ListView list = (ListView) findViewById(android.R.id.list);
list.setAdapter(new ReportAdapter(this, mReport.getReport()));
list.setClickable(false);
}
}

View File

@@ -29,42 +29,48 @@ import android.os.Parcel;
import android.os.Parcelable;
/**
* This class will be used to generate a report, and insert it into our exception report database
* This class will be used to generate a report, and insert it into our
* exception report database
*
* @author ricky barrette
*/
public class Report implements Parcelable{
public class Report implements Parcelable {
private final String mUrl;
private ArrayList<ReportItem> mReport;
public static final Parcelable.Creator<Report> CREATOR = new Parcelable.Creator<Report>() {
public Report createFromParcel(Parcel in) {
@Override
public Report createFromParcel(final Parcel in) {
return new Report(in);
}
public Report[] newArray(int size) {
@Override
public Report[] newArray(final int size) {
return new Report[size];
}
};
/**
* Creates a new Report
*
* @param in
* @author ricky barrette
*/
public Report(Parcel in){
this.mUrl = in.readString();
this.mReport = new ArrayList<ReportItem>();
in.readTypedList(this.mReport, ReportItem.CREATOR);
public Report(final Parcel in) {
mUrl = in.readString();
mReport = new ArrayList<ReportItem>();
in.readTypedList(mReport, ReportItem.CREATOR);
}
/**
* Creates a new Report
*
* @author ricky barrette
*/
public Report(String url) {
// Log.d(TAG, url);
this.mUrl = url;
public Report(final String url) {
// Log.d(TAG, url);
mUrl = url;
}
@Override
@@ -74,51 +80,54 @@ public class Report implements Parcelable{
/**
* Files the report with the remote database
*
* @author ricky barrette
* @throws IOException
* @throws ClientProtocolException
* @return String result
*/
public String file() throws ClientProtocolException, IOException{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(mUrl);
public String file() throws ClientProtocolException, IOException {
final HttpClient httpclient = new DefaultHttpClient();
final HttpPost httppost = new HttpPost(mUrl);
httppost.setEntity(new UrlEncodedFormEntity(getNameValuePairs()));
//return the results
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
// return the results
final HttpResponse response = httpclient.execute(httppost);
final HttpEntity entity = response.getEntity();
final InputStream is = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
StringBuilder sb = new StringBuilder();
sb.append(reader.readLine() + "\n");
String line="0";
while ((line = reader.readLine()) != null)
sb.append(line + "\n");
is.close();
reader.close();
return sb.toString();
final BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
final StringBuilder sb = new StringBuilder();
sb.append(reader.readLine() + "\n");
String line = "0";
while ((line = reader.readLine()) != null)
sb.append(line + "\n");
is.close();
reader.close();
return sb.toString();
}
/**
* Generates a report to be displayed form a downloaded JSON object
*
* @param report
* @return
* @author ricky barrette
* @throws JSONException
*/
@SuppressWarnings("rawtypes")
public Report generateReport(JSONObject report) throws JSONException{
this.mReport = new ArrayList<ReportItem>();
Iterator iter = report.keys();
while(iter.hasNext()){
String key = (String)iter.next();
this.mReport.add(new ReportItem(key , report.getString(key)));
}
public Report generateReport(final JSONObject report) throws JSONException {
mReport = new ArrayList<ReportItem>();
final Iterator iter = report.keys();
while (iter.hasNext()) {
final String key = (String) iter.next();
mReport.add(new ReportItem(key, report.getString(key)));
}
return this;
}
/**
* Generates a report to be sent.
*
* @param msg
* @param stackTrace
* @param cause
@@ -128,29 +137,31 @@ public class Report implements Parcelable{
* @return this
* @author ricky barrette
*/
public Report generateReport(String msg, String stackTrace, String cause, String date, String device, String appVersion, String app, String tracker, String packageName){
this.mReport = new ArrayList<ReportItem>();
this.mReport.add(new ReportItem("app",app));
this.mReport.add(new ReportItem("version",appVersion));
this.mReport.add(new ReportItem("date",date));
this.mReport.add(new ReportItem("msg",msg));
this.mReport.add(new ReportItem("stackTrace",stackTrace));
this.mReport.add(new ReportItem("cause",cause));
this.mReport.add(new ReportItem("device",device));
this.mReport.add(new ReportItem("tracker",tracker));
this.mReport.add(new ReportItem("package",packageName));
public Report generateReport(final String msg, final String stackTrace, final String cause, final String date, final String device, final String appVersion,
final String app, final String tracker, final String packageName) {
mReport = new ArrayList<ReportItem>();
mReport.add(new ReportItem("app", app));
mReport.add(new ReportItem("version", appVersion));
mReport.add(new ReportItem("date", date));
mReport.add(new ReportItem("msg", msg));
mReport.add(new ReportItem("stackTrace", stackTrace));
mReport.add(new ReportItem("cause", cause));
mReport.add(new ReportItem("device", device));
mReport.add(new ReportItem("tracker", tracker));
mReport.add(new ReportItem("package", packageName));
return this;
}
/**
* Extracts the name value pairs from the report bundle
*
* @return
* @author ricky barrette
*/
private ArrayList<NameValuePair> getNameValuePairs() {
ArrayList<NameValuePair> list = new ArrayList<NameValuePair>();
for(ReportItem entry : this.mReport)
list.add(new BasicNameValuePair(entry.getKey(), (String) entry.getValue()));
final ArrayList<NameValuePair> list = new ArrayList<NameValuePair>();
for (final ReportItem entry : mReport)
list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
return list;
}
@@ -158,17 +169,18 @@ public class Report implements Parcelable{
* @return the generated exception report
* @author ricky barrette
*/
public ArrayList<NameValuePair> getReport(){
public ArrayList<NameValuePair> getReport() {
return getNameValuePairs();
}
/**
* Sets the optional users description of what happened
*
* @param string
* @author ricky barrette
*/
public Report setDescription(String description) {
this.mReport.add(new ReportItem("description", description));
public Report setDescription(final String description) {
mReport.add(new ReportItem("description", description));
return this;
}
@@ -177,18 +189,18 @@ public class Report implements Parcelable{
* @author ricky barrette
*/
@Override
public String toString(){
StringBuilder s = new StringBuilder();
for(ReportItem item : this.mReport){
s.append("\n\n-----"+ item.getKey()+"-----");
s.append("\n"+item.getValue());
public String toString() {
final StringBuilder s = new StringBuilder();
for (final ReportItem item : mReport) {
s.append("\n\n-----" + item.getKey() + "-----");
s.append("\n" + item.getValue());
}
return s.toString();
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(this.mUrl);
out.writeTypedList(this.mReport);
public void writeToParcel(final Parcel out, final int flags) {
out.writeString(mUrl);
out.writeTypedList(mReport);
}
}

View File

@@ -17,55 +17,65 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.TwentyCodes.android.exception.R;
/**
* This class will be used to populate a custom Listview used to display the Generated exception report
* This class will be used to populate a custom Listview used to display the
* Generated exception report
*
* @author ricky barrette
*/
public class ReportAdapter extends BaseAdapter {
private ArrayList<NameValuePair> mReport;
private LayoutInflater mInflater;
class ViewHolder {
TextView title;
TextView body;
}
private final ArrayList<NameValuePair> mReport;
private final LayoutInflater mInflater;
/**
* Creates a new ReportAdapter
*
* @author ricky barrette
*/
public ReportAdapter(Context context, ArrayList<NameValuePair> report) {
public ReportAdapter(final Context context, final ArrayList<NameValuePair> report) {
super();
// Cache the LayoutInflate to avoid asking for a new one each time.
this.mInflater = LayoutInflater.from(context);
this.mReport = report;
mInflater = LayoutInflater.from(context);
mReport = report;
}
/**
* (non-Javadoc)
*
* @see android.widget.Adapter#getCount()
* @author ricky barrette
*/
@Override
public int getCount() {
return this.mReport.size();
return mReport.size();
}
/**
* (non-Javadoc)
*
* @see android.widget.Adapter#getItem(int)
* @author ricky barrette
*/
@Override
public NameValuePair getItem(int index) {
return this.mReport.get(index);
public NameValuePair getItem(final int index) {
return mReport.get(index);
}
/**
* (non-Javadoc)
*
* @see android.widget.Adapter#getItemId(int)
* @author ricky barrette
*/
@Override
public long getItemId(int index) {
public long getItemId(final int index) {
return index;
}
@@ -73,40 +83,38 @@ public class ReportAdapter extends BaseAdapter {
*
*/
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
public View getView(final int position, View convertView, final ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unnecessary calls to findViewById() on each row.
ViewHolder holder;
// A ViewHolder keeps references to children views to avoid unnecessary
// calls to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.exception_list_item, null);
// When convertView is not null, we can reuse it directly, there is no
// need
// to reinflate it. We only inflate a new View when the convertView
// supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.exception_list_item, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.exception_title);
holder.body = (TextView) convertView.findViewById(R.id.exception_text);
// Creates a ViewHolder and store references to the two children
// views
// we want to bind data to.
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.exception_title);
holder.body = (TextView) convertView.findViewById(R.id.exception_text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
/*
* Bind the data efficiently with the holder.
*/
holder.title.setText(getItem(position).getName());
holder.body.setText(getItem(position).getValue());
/*
* Bind the data efficiently with the holder.
*/
holder.title.setText(getItem(position).getName());
holder.body.setText(getItem(position).getValue());
return convertView;
return convertView;
}
class ViewHolder {
TextView title;
TextView body;
}
}

View File

@@ -10,18 +10,22 @@ import android.os.Parcel;
import android.os.Parcelable;
/**
* This class will represent an individual report item. The ReportItems will be used in an array list that will be passed via intent.
* This will allow for our report items to stay in the proper order.
* This class will represent an individual report item. The ReportItems will be
* used in an array list that will be passed via intent. This will allow for our
* report items to stay in the proper order.
*
* @author ricky
*/
public final class ReportItem implements Parcelable {
public static final Parcelable.Creator<ReportItem> CREATOR = new Parcelable.Creator<ReportItem>() {
public ReportItem createFromParcel(Parcel in) {
@Override
public ReportItem createFromParcel(final Parcel in) {
return new ReportItem(in);
}
public ReportItem[] newArray(int size) {
@Override
public ReportItem[] newArray(final int size) {
return new ReportItem[size];
}
};
@@ -30,24 +34,28 @@ public final class ReportItem implements Parcelable {
private final String mValue;
/**
* Creates a new ReportItem
* @author ricky barrette
* Creates a new ReportItem from a parcel
*
* @param in
*/
public ReportItem(String key, String value) {
this.mKey = key;
this.mValue = value;
public ReportItem(final Parcel in) {
mKey = in.readString();
mValue = in.readString();
}
/**
* Creates a new ReportItem from a parcel
* @param in
* Creates a new ReportItem
*
* @author ricky barrette
*/
public ReportItem(Parcel in){
this.mKey = in.readString();
this.mValue = in.readString();
public ReportItem(final String key, final String value) {
mKey = key;
mValue = value;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see android.os.Parcelable#describeContents()
*/
@Override
@@ -56,27 +64,29 @@ public final class ReportItem implements Parcelable {
return 0;
}
/* (non-Javadoc)
* @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.mKey);
dest.writeString(this.mValue);
}
/**
* @return the key
*/
public String getKey() {
return this.mKey;
return mKey;
}
/**
* @return the value
*/
public String getValue() {
return this.mValue;
return mValue;
}
/*
* (non-Javadoc)
*
* @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
*/
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(mKey);
dest.writeString(mValue);
}
}

View File

@@ -10,9 +10,7 @@ import java.io.IOException;
import org.apache.http.client.ClientProtocolException;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.Notification.Builder;
import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -20,10 +18,12 @@ import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import anroid.v4.compat.NotificationCompat;
/**
* This service will allow the exception handler to post reports in the backgound,
* allowing the user to do what ever they want
* This service will allow the exception handler to post reports in the
* backgound, allowing the user to do what ever they want
*
* @author ricky barrette
*/
public class ReportPostingService extends Service {
@@ -39,6 +39,7 @@ public class ReportPostingService extends Service {
/**
* Fires of a notification based upon api level
*
* @param title
* @param contentText
* @param ticker
@@ -47,105 +48,83 @@ public class ReportPostingService extends Service {
* @param isOngoing
* @author ricky barrette
*/
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private void fireNotification(String title, String contentText, String ticker, int icon, Intent intent, boolean isOngoing) {
private void fireNotification(final String title, final String contentText, final String ticker, final int icon, final Intent intent, final boolean isOngoing) {
PendingIntent pendingIntent = null;
if(intent != null)
pendingIntent = PendingIntent.getService(this.getApplicationContext(), 0, intent, 0);
/*
* Use the appropriate notificafation methods
*/
if(Integer.valueOf(android.os.Build.VERSION.SDK_INT) > 11){
Builder builder = new Notification.Builder(this.getApplicationContext())
.setContentTitle(title)
.setContentText(contentText)
.setTicker(ticker)
.setSmallIcon(icon)
.setWhen(System.currentTimeMillis());
if(isOngoing)
builder.setOngoing(true);
else
builder.setAutoCancel(true);
if (intent != null)
builder.setContentIntent(pendingIntent);
mNotificationManager.notify(NOTIFICATION_ID, builder.getNotification());
} else {
Notification notification = new Notification(icon, title , System.currentTimeMillis());
if(isOngoing)
notification.flags |= Notification.FLAG_ONGOING_EVENT;
else
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(this.getApplicationContext(), title, contentText, pendingIntent);
mNotificationManager.notify(NOTIFICATION_ID, notification);
}
if (intent != null)
pendingIntent = PendingIntent.getService(getApplicationContext(), 0, intent, 0);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()).setContentTitle(title).setContentText(contentText)
.setTicker(ticker).setSmallIcon(icon).setWhen(System.currentTimeMillis());
if (isOngoing)
builder.setOngoing(true);
else
builder.setAutoCancel(true);
if (intent != null)
builder.setContentIntent(pendingIntent);
mNotificationManager.notify(NOTIFICATION_ID, builder.getNotification());
}
/**
* Extracts the report object from the intent
*
* @param intent
* @author ricky barrette
*/
private void getReport(Intent intent) {
private void getReport(final Intent intent) {
mReport = (Report) intent.getParcelableExtra("report");
}
/**
* notifiys the user that we are sending a report
*
* @author ricky barrette
*/
private void notifyError() {
fireNotification(getString(R.string.reporting_error),
getString(R.string.reporting_error_msg),
getString(R.string.reporting_error_msg),
android.R.drawable.stat_notify_error,
new Intent(this.getApplicationContext(), ReportPostingService.class).putExtras(mIntent),
false);
fireNotification(getString(R.string.reporting_error), getString(R.string.reporting_error_msg), getString(R.string.reporting_error_msg),
android.R.drawable.stat_notify_error, new Intent(getApplicationContext(), ReportPostingService.class).putExtras(mIntent), false);
}
/**
* notifiys the user that we are sending a report
*
* @author ricky barrette
*/
private void notifySending() {
fireNotification(getString(R.string.sending),
getString(R.string.sending_report),
getString(R.string.sending),
android.R.drawable.stat_sys_upload,
null,
true);
fireNotification(getString(R.string.sending), getString(R.string.sending_report), getString(R.string.sending), android.R.drawable.stat_sys_upload, null, true);
}
/**
* (non-Javadoc)
*
* @see android.app.Service#onBind(android.content.Intent)
*/
@Override
public IBinder onBind(Intent intent) {
public IBinder onBind(final Intent intent) {
// Unused
return null;
}
/**
* Called when the service is being created
* Here we want to display a notifcation,
* to inform the user what we are doing.
* (non-Javadoc)
* Called when the service is being created Here we want to display a
* notifcation, to inform the user what we are doing. (non-Javadoc)
*
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate() {
Context context = this.getApplicationContext();
final Context context = getApplicationContext();
mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notifySending();
super.onCreate();
}
/**
* Called when the service is being destroyed
* Here we want to dismiss the notifications
* (non-Javadoc)
* Called when the service is being destroyed Here we want to dismiss the
* notifications (non-Javadoc)
*
* @see android.app.Service#onDestroy()
*/
@Override
@@ -158,11 +137,12 @@ public class ReportPostingService extends Service {
/**
* (non-Javadoc)
*
* @see android.app.Service#onStart(android.content.Intent, int)
*/
@SuppressWarnings("deprecation")
@Override
public void onStart(Intent intent, int startId) {
public void onStart(final Intent intent, final int startId) {
mStartId = startId;
getReport(intent);
postReport();
@@ -172,10 +152,12 @@ public class ReportPostingService extends Service {
/**
* (non-Javadoc)
*
* @see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
@TargetApi(5)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
public int onStartCommand(final Intent intent, final int flags, final int startId) {
mStartId = startId;
getReport(intent);
postReport();
@@ -185,22 +167,28 @@ public class ReportPostingService extends Service {
/**
* Posts a copy of the report to the report handing server
*
* @author ricky barrette
*/
private void postReport(){
if(!isStarted){
private void postReport() {
if (!isStarted) {
isStarted = true;
try {
Log.d(TAG, mReport.file());
} catch (ClientProtocolException e) {
e.printStackTrace();
hasErrored = true;
} catch (IOException e) {
e.printStackTrace();
hasErrored = true;
} finally {
ReportPostingService.this.stopSelf(mStartId);
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.d(TAG, mReport.file());
} catch (final ClientProtocolException e) {
e.printStackTrace();
hasErrored = true;
} catch (final IOException e) {
e.printStackTrace();
hasErrored = true;
} finally {
ReportPostingService.this.stopSelf(mStartId);
}
}
}).start();
}
}
}

View File

@@ -15,91 +15,96 @@ import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* this class will be a simple preference that contains only a text view that will display the application build information
* this class will be a simple preference that contains only a text view that
* will display the application build information
*
* @author ricky barrette
*/
public class VersionInformationPreference extends Preference {
private Context mContext;
private final Context mContext;
/**
* creates a preference that is nothing but a text view
*
* @param context
*/
public VersionInformationPreference(Context context) {
public VersionInformationPreference(final Context context) {
super(context);
mContext = context;
}
/**
* creates a preference that is nothing but a text view
*
* @param context
* @param attrs
*/
public VersionInformationPreference(Context context, AttributeSet attrs) {
public VersionInformationPreference(final Context context, final AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
/**
* creates a preference that is nothing but a text view
*
* @param context
* @param attrs
* @param defStyle
*/
public VersionInformationPreference(Context context, AttributeSet attrs, int defStyle) {
public VersionInformationPreference(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
mContext = context;
}
/**
* creates a linear layout the contains only a textview.
* (non-Javadoc)
* creates a linear layout the contains only a textview. (non-Javadoc)
*
* @see android.preference.Preference#onCreateView(android.view.ViewGroup)
* @param parent
* @return
* @author ricky barrette
*/
@Override
protected View onCreateView(ViewGroup parent){
protected View onCreateView(final ViewGroup parent) {
/*
* get the build information, and build the string
*/
PackageManager pm = mContext.getPackageManager();
final PackageManager pm = mContext.getPackageManager();
PackageInfo pi;
try {
pi = pm.getPackageInfo(mContext.getPackageName(), 0);
} catch (NameNotFoundException eNnf) {
//doubt this will ever run since we want info about our own package
} catch (final NameNotFoundException eNnf) {
// doubt this will ever run since we want info about our own package
pi = new PackageInfo();
pi.versionName = "unknown";
pi.versionCode = 1;
}
/*
* create a vertical linear layout that width and height that wraps content
* create a vertical linear layout that width and height that wraps
* content
*/
LinearLayout layout = new LinearLayout(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
// params.gravity = Gravity.CENTER;
final LinearLayout layout = new LinearLayout(getContext());
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
// params.gravity = Gravity.CENTER;
layout.setPadding(15, 5, 10, 5);
layout.setOrientation(LinearLayout.VERTICAL);
layout.removeAllViews();
/*
* create a textview that will be used to display the application's name and build information
* and add it to the layout
* create a textview that will be used to display the application's name
* and build information and add it to the layout
*/
TextView title = new TextView(getContext());
title.setText(mContext.getString(R.string.version)+" "+pi.versionName+" bulid "+pi.versionCode);
final TextView title = new TextView(getContext());
title.setText(mContext.getString(R.string.version) + " " + pi.versionName + " bulid " + pi.versionCode);
title.setTextSize(16);
title.setTypeface(Typeface.SANS_SERIF);
title.setGravity(Gravity.LEFT);