1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import com.android.internal.R;
20
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.drawable.Drawable;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Message;
27import android.text.Spannable;
28import android.text.SpannableString;
29import android.text.style.StyleSpan;
30import android.view.LayoutInflater;
31import android.view.View;
32import android.widget.ProgressBar;
33import android.widget.TextView;
34
35import java.text.NumberFormat;
36
37/**
38 * <p>A dialog showing a progress indicator and an optional text message or view.
39 * Only a text message or a view can be used at the same time.</p>
40 * <p>The dialog can be made cancelable on back key press.</p>
41 * <p>The progress range is 0..10000.</p>
42 */
43public class ProgressDialog extends AlertDialog {
44
45    /** Creates a ProgressDialog with a circular, spinning progress
46     * bar. This is the default.
47     */
48    public static final int STYLE_SPINNER = 0;
49
50    /** Creates a ProgressDialog with a horizontal progress bar.
51     */
52    public static final int STYLE_HORIZONTAL = 1;
53
54    private ProgressBar mProgress;
55    private TextView mMessageView;
56
57    private int mProgressStyle = STYLE_SPINNER;
58    private TextView mProgressNumber;
59    private String mProgressNumberFormat;
60    private TextView mProgressPercent;
61    private NumberFormat mProgressPercentFormat;
62
63    private int mMax;
64    private int mProgressVal;
65    private int mSecondaryProgressVal;
66    private int mIncrementBy;
67    private int mIncrementSecondaryBy;
68    private Drawable mProgressDrawable;
69    private Drawable mIndeterminateDrawable;
70    private CharSequence mMessage;
71    private boolean mIndeterminate;
72
73    private boolean mHasStarted;
74    private Handler mViewUpdateHandler;
75
76    public ProgressDialog(Context context) {
77        super(context);
78        initFormats();
79    }
80
81    public ProgressDialog(Context context, int theme) {
82        super(context, theme);
83        initFormats();
84    }
85
86    private void initFormats() {
87        mProgressNumberFormat = "%1d/%2d";
88        mProgressPercentFormat = NumberFormat.getPercentInstance();
89        mProgressPercentFormat.setMaximumFractionDigits(0);
90    }
91
92    public static ProgressDialog show(Context context, CharSequence title,
93            CharSequence message) {
94        return show(context, title, message, false);
95    }
96
97    public static ProgressDialog show(Context context, CharSequence title,
98            CharSequence message, boolean indeterminate) {
99        return show(context, title, message, indeterminate, false, null);
100    }
101
102    public static ProgressDialog show(Context context, CharSequence title,
103            CharSequence message, boolean indeterminate, boolean cancelable) {
104        return show(context, title, message, indeterminate, cancelable, null);
105    }
106
107    public static ProgressDialog show(Context context, CharSequence title,
108            CharSequence message, boolean indeterminate,
109            boolean cancelable, OnCancelListener cancelListener) {
110        ProgressDialog dialog = new ProgressDialog(context);
111        dialog.setTitle(title);
112        dialog.setMessage(message);
113        dialog.setIndeterminate(indeterminate);
114        dialog.setCancelable(cancelable);
115        dialog.setOnCancelListener(cancelListener);
116        dialog.show();
117        return dialog;
118    }
119
120    @Override
121    protected void onCreate(Bundle savedInstanceState) {
122        LayoutInflater inflater = LayoutInflater.from(mContext);
123        TypedArray a = mContext.obtainStyledAttributes(null,
124                com.android.internal.R.styleable.AlertDialog,
125                com.android.internal.R.attr.alertDialogStyle, 0);
126        if (mProgressStyle == STYLE_HORIZONTAL) {
127
128            /* Use a separate handler to update the text views as they
129             * must be updated on the same thread that created them.
130             */
131            mViewUpdateHandler = new Handler() {
132                @Override
133                public void handleMessage(Message msg) {
134                    super.handleMessage(msg);
135
136                    /* Update the number and percent */
137                    int progress = mProgress.getProgress();
138                    int max = mProgress.getMax();
139                    if (mProgressNumberFormat != null) {
140                        String format = mProgressNumberFormat;
141                        mProgressNumber.setText(String.format(format, progress, max));
142                    } else {
143                        mProgressNumber.setText("");
144                    }
145                    if (mProgressPercentFormat != null) {
146                        double percent = (double) progress / (double) max;
147                        SpannableString tmp = new SpannableString(mProgressPercentFormat.format(percent));
148                        tmp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
149                                0, tmp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
150                        mProgressPercent.setText(tmp);
151                    } else {
152                        mProgressPercent.setText("");
153                    }
154                }
155            };
156            View view = inflater.inflate(a.getResourceId(
157                    com.android.internal.R.styleable.AlertDialog_horizontalProgressLayout,
158                    R.layout.alert_dialog_progress), null);
159            mProgress = (ProgressBar) view.findViewById(R.id.progress);
160            mProgressNumber = (TextView) view.findViewById(R.id.progress_number);
161            mProgressPercent = (TextView) view.findViewById(R.id.progress_percent);
162            setView(view);
163        } else {
164            View view = inflater.inflate(a.getResourceId(
165                    com.android.internal.R.styleable.AlertDialog_progressLayout,
166                    R.layout.progress_dialog), null);
167            mProgress = (ProgressBar) view.findViewById(R.id.progress);
168            mMessageView = (TextView) view.findViewById(R.id.message);
169            setView(view);
170        }
171        a.recycle();
172        if (mMax > 0) {
173            setMax(mMax);
174        }
175        if (mProgressVal > 0) {
176            setProgress(mProgressVal);
177        }
178        if (mSecondaryProgressVal > 0) {
179            setSecondaryProgress(mSecondaryProgressVal);
180        }
181        if (mIncrementBy > 0) {
182            incrementProgressBy(mIncrementBy);
183        }
184        if (mIncrementSecondaryBy > 0) {
185            incrementSecondaryProgressBy(mIncrementSecondaryBy);
186        }
187        if (mProgressDrawable != null) {
188            setProgressDrawable(mProgressDrawable);
189        }
190        if (mIndeterminateDrawable != null) {
191            setIndeterminateDrawable(mIndeterminateDrawable);
192        }
193        if (mMessage != null) {
194            setMessage(mMessage);
195        }
196        setIndeterminate(mIndeterminate);
197        onProgressChanged();
198        super.onCreate(savedInstanceState);
199    }
200
201    @Override
202    public void onStart() {
203        super.onStart();
204        mHasStarted = true;
205    }
206
207    @Override
208    protected void onStop() {
209        super.onStop();
210        mHasStarted = false;
211    }
212
213    public void setProgress(int value) {
214        if (mHasStarted) {
215            mProgress.setProgress(value);
216            onProgressChanged();
217        } else {
218            mProgressVal = value;
219        }
220    }
221
222    public void setSecondaryProgress(int secondaryProgress) {
223        if (mProgress != null) {
224            mProgress.setSecondaryProgress(secondaryProgress);
225            onProgressChanged();
226        } else {
227            mSecondaryProgressVal = secondaryProgress;
228        }
229    }
230
231    public int getProgress() {
232        if (mProgress != null) {
233            return mProgress.getProgress();
234        }
235        return mProgressVal;
236    }
237
238    public int getSecondaryProgress() {
239        if (mProgress != null) {
240            return mProgress.getSecondaryProgress();
241        }
242        return mSecondaryProgressVal;
243    }
244
245    public int getMax() {
246        if (mProgress != null) {
247            return mProgress.getMax();
248        }
249        return mMax;
250    }
251
252    public void setMax(int max) {
253        if (mProgress != null) {
254            mProgress.setMax(max);
255            onProgressChanged();
256        } else {
257            mMax = max;
258        }
259    }
260
261    public void incrementProgressBy(int diff) {
262        if (mProgress != null) {
263            mProgress.incrementProgressBy(diff);
264            onProgressChanged();
265        } else {
266            mIncrementBy += diff;
267        }
268    }
269
270    public void incrementSecondaryProgressBy(int diff) {
271        if (mProgress != null) {
272            mProgress.incrementSecondaryProgressBy(diff);
273            onProgressChanged();
274        } else {
275            mIncrementSecondaryBy += diff;
276        }
277    }
278
279    public void setProgressDrawable(Drawable d) {
280        if (mProgress != null) {
281            mProgress.setProgressDrawable(d);
282        } else {
283            mProgressDrawable = d;
284        }
285    }
286
287    public void setIndeterminateDrawable(Drawable d) {
288        if (mProgress != null) {
289            mProgress.setIndeterminateDrawable(d);
290        } else {
291            mIndeterminateDrawable = d;
292        }
293    }
294
295    public void setIndeterminate(boolean indeterminate) {
296        if (mProgress != null) {
297            mProgress.setIndeterminate(indeterminate);
298        } else {
299            mIndeterminate = indeterminate;
300        }
301    }
302
303    public boolean isIndeterminate() {
304        if (mProgress != null) {
305            return mProgress.isIndeterminate();
306        }
307        return mIndeterminate;
308    }
309
310    @Override
311    public void setMessage(CharSequence message) {
312        if (mProgress != null) {
313            if (mProgressStyle == STYLE_HORIZONTAL) {
314                super.setMessage(message);
315            } else {
316                mMessageView.setText(message);
317            }
318        } else {
319            mMessage = message;
320        }
321    }
322
323    public void setProgressStyle(int style) {
324        mProgressStyle = style;
325    }
326
327    /**
328     * Change the format of the small text showing current and maximum units
329     * of progress.  The default is "%1d/%2d".
330     * Should not be called during the number is progressing.
331     * @param format A string passed to {@link String#format String.format()};
332     * use "%1d" for the current number and "%2d" for the maximum.  If null,
333     * nothing will be shown.
334     */
335    public void setProgressNumberFormat(String format) {
336        mProgressNumberFormat = format;
337        onProgressChanged();
338    }
339
340    /**
341     * Change the format of the small text showing the percentage of progress.
342     * The default is
343     * {@link NumberFormat#getPercentInstance() NumberFormat.getPercentageInstnace().}
344     * Should not be called during the number is progressing.
345     * @param format An instance of a {@link NumberFormat} to generate the
346     * percentage text.  If null, nothing will be shown.
347     */
348    public void setProgressPercentFormat(NumberFormat format) {
349        mProgressPercentFormat = format;
350        onProgressChanged();
351    }
352
353    private void onProgressChanged() {
354        if (mProgressStyle == STYLE_HORIZONTAL) {
355            if (mViewUpdateHandler != null && !mViewUpdateHandler.hasMessages(0)) {
356                mViewUpdateHandler.sendEmptyMessage(0);
357            }
358        }
359    }
360}
361