View Javadoc

1   /***
2    *     Ambient - A music player for the Android platform
3    Copyright (C) 2007 Martin Vysny
4    
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9    
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14  
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package sk.baka.ambient;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Queue;
24  import java.util.concurrent.ConcurrentLinkedQueue;
25  
26  import sk.baka.ambient.commons.MiscUtils;
27  import android.os.Handler;
28  import android.util.Log;
29  import android.widget.Toast;
30  
31  /***
32   * Handles errors that occurs in the application. Thread-safe.
33   * 
34   * @author Martin Vysny
35   * 
36   */
37  public final class ErrorHandler {
38  
39  	private final AmbientApplication app;
40  
41  	/***
42  	 * Creates new handler instance.
43  	 * 
44  	 * @param app
45  	 *            owner application.
46  	 */
47  	ErrorHandler(final AmbientApplication app) {
48  		this.app = app;
49  	}
50  
51  	/***
52  	 * An error occurred. A notification will be shown to the user.
53  	 * 
54  	 * @param sender
55  	 *            the sender.
56  	 * @param error
57  	 *            error if <code>true</code>, warning if <code>false</code>.
58  	 * @param message
59  	 *            the message to show. The message will be followed by a new
60  	 *            line, the <code>Cause: </code> string and the
61  	 *            {@link Throwable#getMessage()} if the cause is not
62  	 *            <code>null</code>.
63  	 * @param cause
64  	 *            optional cause.
65  	 */
66  	public void error(final Class<?> sender, boolean error, String message,
67  			Throwable cause) {
68  		// log the error
69  		if (error) {
70  			if (cause != null) {
71  				Log.e(sender.getSimpleName(), message, cause);
72  			} else {
73  				Log.e(sender.getSimpleName(), message);
74  			}
75  		} else {
76  			if (cause != null) {
77  				Log.w(sender.getSimpleName(), message, cause);
78  			} else {
79  				Log.w(sender.getSimpleName(), message);
80  			}
81  		}
82  		// show notification
83  		final StringBuilder text = new StringBuilder();
84  		text.append(app.getString(error ? R.string.error : R.string.warning));
85  		text.append(": ");
86  		text.append(message);
87  		if (cause != null) {
88  			text.append('\n');
89  			text.append(app.getString(R.string.cause));
90  			text.append(": ");
91  			text.append(cause.getClass().getSimpleName());
92  			text.append(": ");
93  			text.append(cause.getLocalizedMessage());
94  		}
95  		final String errorText = text.toString();
96  		errors.add(new ErrorInfo(error, errorText, cause));
97  		AmbientApplication.getHandler().post(new Runnable() {
98  			public void run() {
99  				final Toast toast = Toast.makeText(app, errorText,
100 						Toast.LENGTH_LONG);
101 				toast.show();
102 				updateNotification();
103 			}
104 		});
105 	}
106 
107 	/***
108 	 * Contains information about an error.
109 	 * 
110 	 * @author Martin Vysny
111 	 */
112 	public static class ErrorInfo {
113 		/***
114 		 * The time the error occurred.
115 		 */
116 		public final long timestamp;
117 		/***
118 		 * <code>true</code> on error, <code>false</code> on warning.
119 		 */
120 		public final boolean error;
121 		/***
122 		 * The message.
123 		 */
124 		public final String message;
125 		/***
126 		 * The stack trace.
127 		 */
128 		public final String stacktrace;
129 
130 		/***
131 		 * Creates new error information object.
132 		 * 
133 		 * @param error
134 		 *            <code>true</code> on error, <code>false</code> on warning.
135 		 * @param message
136 		 *            The message.
137 		 * @param t
138 		 *            the cause.
139 		 */
140 		public ErrorInfo(final boolean error, final String message,
141 				final Throwable t) {
142 			super();
143 			timestamp = System.currentTimeMillis();
144 			this.error = error;
145 			this.message = message;
146 			this.stacktrace = t == null ? null : MiscUtils.getStackTrace(t);
147 		}
148 	}
149 
150 	private final Queue<ErrorInfo> errors = new ConcurrentLinkedQueue<ErrorInfo>();
151 
152 	/***
153 	 * Returns a new copy of the recorded error list.
154 	 * 
155 	 * @return non-<code>null</code> list of errors.
156 	 */
157 	public List<ErrorInfo> getErrors() {
158 		return new ArrayList<ErrorInfo>(errors);
159 	}
160 
161 	/***
162 	 * Clears all recorded error messages. Must be invoked from the
163 	 * {@link Handler} thread.
164 	 */
165 	public void clear() {
166 		errors.clear();
167 		updateNotification();
168 	}
169 
170 	private void updateNotification() {
171 		// get the last item
172 		ErrorInfo last = null;
173 		int size = 0;
174 		for (final ErrorInfo ei : errors) {
175 			last = ei;
176 			size++;
177 		}
178 		app.getNotificator().updateErrorNotification(size, last);
179 	}
180 }