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 }