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  package sk.baka.ambient.views;
19  
20  import java.util.concurrent.FutureTask;
21  
22  import sk.baka.ambient.AmbientApplication;
23  import sk.baka.ambient.R;
24  import android.app.ProgressDialog;
25  import android.content.Context;
26  import android.content.DialogInterface;
27  import android.content.DialogInterface.OnCancelListener;
28  import android.os.Handler;
29  import android.util.Log;
30  
31  /***
32   * <p>
33   * Shows the wait-for dialog and runs long running operation in the background.
34   * The {@link #start()} method starts the long running operation in new thread.
35   * </p>
36   * <p>
37   * Exceptions thrown by the runnable are reported using
38   * {@link AmbientApplication#error(Class, boolean, String, Throwable)}.
39   * </p>
40   * <p>
41   * The {@link Runnable} should periodically check for the interrupted state
42   * (using <code>Thread.currentThread().isInterrupted()</code>). If it is
43   * interrupted it should finish ASAP. It may do so by throwing an Exception -
44   * exceptions are not reported when the process is interrupted.
45   * </p>
46   * 
47   * @author Martin Vysny
48   */
49  public final class LongOperationExecutor implements Runnable, OnCancelListener {
50  	private final ProgressDialog dlg;
51  
52  	private final FutureTask<Object> longRunningOp;
53  
54  	private final Handler handler = AmbientApplication.getHandler();
55  
56  	/***
57  	 * Shown when the operation fails.
58  	 */
59  	private final int errorMsg;
60  
61  	/***
62  	 * Creates the long running operation runner. Must be called from an event
63  	 * thread.
64  	 * 
65  	 * @param context
66  	 *            the context
67  	 * @param text
68  	 *            the text to show on the dialog.
69  	 * @param errorMsg
70  	 *            shown when the operation fails.
71  	 * @param longRunningOp
72  	 *            the operation to run. It will be executed in its own thread.
73  	 */
74  	public LongOperationExecutor(final Context context, final int text,
75  			final int errorMsg,
76  			final Runnable longRunningOp) {
77  		super();
78  		this.errorMsg = errorMsg;
79  		dlg = new ProgressDialog(context);
80  		dlg.setIndeterminate(true);
81  		dlg.setTitle(context.getText(R.string.please_wait));
82  		dlg.setMessage(context.getText(text));
83  		dlg.setCancelable(true);
84  		dlg.setOnCancelListener(this);
85  		this.longRunningOp = new Runner(longRunningOp);
86  	}
87  
88  	private final class Runner extends FutureTask<Object> {
89  		@Override
90  		protected void done() {
91  			handler.post(LongOperationExecutor.this);
92  		}
93  
94  		/***
95  		 * @param runnable
96  		 */
97  		public Runner(Runnable runnable) {
98  			super(new ProtectedRunnable(runnable), null);
99  		}
100 	}
101 
102 	private class ProtectedRunnable implements Runnable {
103 		private final Runnable r;
104 
105 		/***
106 		 * Creates new instance of the object.
107 		 * 
108 		 * @param r
109 		 *            delegate runnable.
110 		 */
111 		public ProtectedRunnable(final Runnable r) {
112 			this.r = r;
113 		}
114 
115 		public void run() {
116 			try {
117 				r.run();
118 			} catch (final Exception e) {
119 				if (Thread.currentThread().isInterrupted()) {
120 					Log.i(r.getClass().getSimpleName(),
121 							"Interrupted: " + e.getMessage(), e);
122 				} else {
123 					AmbientApplication.getInstance().error(r.getClass(), true,
124 							dlg.getContext().getString(errorMsg), e);
125 				}
126 			}
127 		}
128 	}
129 	
130 	private boolean showDialog = true;
131 
132 	private boolean dialogCancelled = false;
133 
134 	/***
135 	 * Internal, do not call.
136 	 */
137 	public void run() {
138 		if (showDialog) {
139 			showDialog = false;
140 			dlg.show();
141 			new Thread(longRunningOp).start();
142 		} else {
143 			if (!dialogCancelled) {
144 				dlg.dismiss();
145 			}
146 		}
147 	}
148 
149 	public void onCancel(DialogInterface arg0) {
150 		dialogCancelled = true;
151 		longRunningOp.cancel(true);
152 	}
153 
154 	/***
155 	 * Shows the dialog and starts the long-running operation.
156 	 * 
157 	 */
158 	public void start() {
159 		handler.post(this);
160 	}
161 }