LatinIME/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressB...

179 lines
7.6 KiB
Java

/**
* Copyright (C) 2013 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 com.android.inputmethod.dictionarypack;
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
public class DictionaryDownloadProgressBar extends ProgressBar {
@SuppressWarnings("unused")
private static final String TAG = DictionaryDownloadProgressBar.class.getSimpleName();
private static final int NOT_A_DOWNLOADMANAGER_PENDING_ID = 0;
private String mClientId;
private String mWordlistId;
private boolean mIsCurrentlyAttachedToWindow = false;
private Thread mReporterThread = null;
public DictionaryDownloadProgressBar(final Context context) {
super(context);
}
public DictionaryDownloadProgressBar(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public void setIds(final String clientId, final String wordlistId) {
mClientId = clientId;
mWordlistId = wordlistId;
}
static private int getDownloadManagerPendingIdFromWordlistId(final Context context,
final String clientId, final String wordlistId) {
final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId);
final ContentValues wordlistValues =
MetadataDbHelper.getContentValuesOfLatestAvailableWordlistById(db, wordlistId);
if (null == wordlistValues) {
// We don't know anything about a word list with this id. Bug? This should never
// happen, but still return to prevent a crash.
Log.e(TAG, "Unexpected word list ID: " + wordlistId);
return NOT_A_DOWNLOADMANAGER_PENDING_ID;
}
return wordlistValues.getAsInteger(MetadataDbHelper.PENDINGID_COLUMN);
}
/*
* This method will stop any running updater thread for this progress bar and create and run
* a new one only if the progress bar is visible.
* Hence, as a result of calling this method, the progress bar will have an updater thread
* running if and only if the progress bar is visible.
*/
private void updateReporterThreadRunningStatusAccordingToVisibility() {
if (null != mReporterThread) mReporterThread.interrupt();
if (mIsCurrentlyAttachedToWindow && View.VISIBLE == getVisibility()) {
final int downloadManagerPendingId =
getDownloadManagerPendingIdFromWordlistId(getContext(), mClientId, mWordlistId);
if (NOT_A_DOWNLOADMANAGER_PENDING_ID == downloadManagerPendingId) {
// Can't get the ID. This is never supposed to happen, but still clear the updater
// thread and return to avoid a crash.
mReporterThread = null;
return;
}
final UpdaterThread updaterThread =
new UpdaterThread(getContext(), downloadManagerPendingId);
updaterThread.start();
mReporterThread = updaterThread;
} else {
// We're not going to restart the thread anyway, so we may as well garbage collect it.
mReporterThread = null;
}
}
@Override
protected void onAttachedToWindow() {
mIsCurrentlyAttachedToWindow = true;
updateReporterThreadRunningStatusAccordingToVisibility();
}
@Override
protected void onDetachedFromWindow() {
mIsCurrentlyAttachedToWindow = false;
updateReporterThreadRunningStatusAccordingToVisibility();
}
private class UpdaterThread extends Thread {
private final static int REPORT_PERIOD = 150; // how often to report progress, in ms
final DownloadManager mDownloadManager;
final int mId;
public UpdaterThread(final Context context, final int id) {
super();
mDownloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
mId = id;
}
@Override
public void run() {
try {
// It's almost impossible that mDownloadManager is null (it would mean it has been
// disabled between pressing the 'install' button and displaying the progress
// bar), but just in case.
if (null == mDownloadManager) return;
final UpdateHelper updateHelper = new UpdateHelper();
final Query query = new Query().setFilterById(mId);
int lastProgress = 0;
setIndeterminate(true);
while (!isInterrupted()) {
final Cursor cursor = mDownloadManager.query(query);
if (null == cursor) {
// Can't contact DownloadManager: this should never happen.
return;
}
try {
if (cursor.moveToNext()) {
final int columnBytesDownloadedSoFar = cursor.getColumnIndex(
DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
final int bytesDownloadedSoFar =
cursor.getInt(columnBytesDownloadedSoFar);
updateHelper.setProgressFromAnotherThread(bytesDownloadedSoFar);
} else {
// Download has finished and DownloadManager has already been asked to
// clean up the db entry.
updateHelper.setProgressFromAnotherThread(getMax());
return;
}
} finally {
cursor.close();
}
Thread.sleep(REPORT_PERIOD);
}
} catch (InterruptedException e) {
// Do nothing and terminate normally.
}
}
private class UpdateHelper implements Runnable {
private int mProgress;
@Override
public void run() {
setIndeterminate(false);
setProgress(mProgress);
}
public void setProgressFromAnotherThread(final int progress) {
if (mProgress != progress) {
mProgress = progress;
// For some unknown reason, setProgress just does not work from a separate
// thread, although the code in ProgressBar looks like it should. Thus, we
// resort to a runnable posted to the handler of the view.
final Handler handler = getHandler();
// It's possible to come here before this view has been laid out. If so,
// just ignore the call - it will be updated again later.
if (null == handler) return;
handler.post(this);
}
}
}
}
}