X-Git-Url: http://gitweb.fperrin.net/?p=Dictionary.git;a=blobdiff_plain;f=src%2Fcom%2Fhughes%2Fandroid%2Fdictionary%2FDictionaryManagerActivity.java;h=faf507da40dca60fd84a5ab53b1f551da1c4c5e1;hp=833429ad522c3fa480011d96b3dd958a1e400df4;hb=c76660b2772122109529d3616289980a7084eeeb;hpb=ca0becd6bc2bb0fa98925ffc6fa1b26964129c89 diff --git a/src/com/hughes/android/dictionary/DictionaryManagerActivity.java b/src/com/hughes/android/dictionary/DictionaryManagerActivity.java index 833429a..faf507d 100644 --- a/src/com/hughes/android/dictionary/DictionaryManagerActivity.java +++ b/src/com/hughes/android/dictionary/DictionaryManagerActivity.java @@ -19,7 +19,6 @@ import android.app.AlertDialog; import android.app.DownloadManager; import android.app.DownloadManager.Request; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -28,22 +27,23 @@ import android.content.SharedPreferences.Editor; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; -import android.preference.PreferenceManager; +import android.support.v7.preference.PreferenceManager; import android.provider.Settings; +import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.support.v7.widget.SearchView.OnQueryTextListener; import android.support.v7.widget.Toolbar; import android.text.InputType; import android.util.Log; -import android.util.TypedValue; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; @@ -53,13 +53,13 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.FrameLayout; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; @@ -76,7 +76,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; @@ -85,17 +84,17 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; // Right-click: // Delete, move to top. -public class DictionaryManagerActivity extends ActionBarActivity { +public class DictionaryManagerActivity extends AppCompatActivity { - static final String LOG = "QuickDic"; - static boolean blockAutoLaunch = false; + private static final String LOG = "QuickDic"; + private static boolean blockAutoLaunch = false; private ListView listView; private ListView getListView() { @@ -112,19 +111,19 @@ public class DictionaryManagerActivity extends ActionBarActivity { } // For DownloadManager bug workaround - private Set finishedDownloadIds = new HashSet(); + private final Set finishedDownloadIds = new HashSet<>(); - DictionaryApplication application; + private DictionaryApplication application; - SearchView filterSearchView; - ToggleButton showDownloadable; + private SearchView filterSearchView; + private ToggleButton showDownloadable; - LinearLayout dictionariesOnDeviceHeaderRow; - LinearLayout downloadableDictionariesHeaderRow; + private LinearLayout dictionariesOnDeviceHeaderRow; + private LinearLayout downloadableDictionariesHeaderRow; - Handler uiHandler; + private Handler uiHandler; - Runnable dictionaryUpdater = new Runnable() { + private final Runnable dictionaryUpdater = new Runnable() { @Override public void run() { if (uiHandler == null) { @@ -133,17 +132,20 @@ public class DictionaryManagerActivity extends ActionBarActivity { uiHandler.post(new Runnable() { @Override public void run() { - setMyListAdapater(); + setMyListAdapter(); } }); } }; - final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public synchronized void onReceive(Context context, Intent intent) { final String action = intent.getAction(); + if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(action)) { + startActivity(getLaunchIntent(getApplicationContext()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP)); + } if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) { final long downloadId = intent.getLongExtra( DownloadManager.EXTRA_DOWNLOAD_ID, 0); @@ -185,10 +187,11 @@ public class DictionaryManagerActivity extends ActionBarActivity { } Log.w(LOG, "Download finished: " + dest + " Id: " + downloadId); - Toast.makeText(context, getString(R.string.unzippingDictionary, dest), - Toast.LENGTH_LONG).show(); + if (!isFinishing()) + Toast.makeText(context, getString(R.string.unzippingDictionary, dest), + Toast.LENGTH_LONG).show(); - if (unzipInstall(context, Uri.parse(dest), dest)) { + if (unzipInstall(context, Uri.parse(dest), dest, true)) { finishedDownloadIds.add(downloadId); Log.w(LOG, "Unzipping finished: " + dest + " Id: " + downloadId); } @@ -196,7 +199,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { } }; - private boolean unzipInstall(Context context, Uri zipUri, String dest) { + private boolean unzipInstall(Context context, Uri zipUri, String dest, boolean delete) { File localZipFile = null; InputStream zipFileStream = null; ZipInputStream zipFile = null; @@ -208,26 +211,47 @@ public class DictionaryManagerActivity extends ActionBarActivity { localZipFile = null; } else { localZipFile = new File(zipUri.getPath()); - zipFileStream = new FileInputStream(localZipFile); + try { + zipFileStream = new FileInputStream(localZipFile); + } catch (Exception e) { + if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, + new String[] {Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }, 0); + return false; + } + throw e; + } } zipFile = new ZipInputStream(new BufferedInputStream(zipFileStream)); - final ZipEntry zipEntry = zipFile.getNextEntry(); - Log.d(LOG, "Unzipping entry: " + zipEntry.getName()); - File targetFile = new File(application.getDictDir(), zipEntry.getName()); - if (targetFile.exists()) { - targetFile.renameTo(new File(targetFile.getAbsolutePath().replace(".quickdic", ".bak.quickdic"))); - targetFile = new File(application.getDictDir(), zipEntry.getName()); + ZipEntry zipEntry; + while ((zipEntry = zipFile.getNextEntry()) != null) { + // Note: this check prevents security issues like accidental path + // traversal, which unfortunately ZipInputStream has no protection against. + // So take extra care when changing it. + if (!Pattern.matches("[-A-Za-z]+\\.quickdic", zipEntry.getName())) { + Log.w(LOG, "Invalid zip entry: " + zipEntry.getName()); + continue; + } + Log.d(LOG, "Unzipping entry: " + zipEntry.getName()); + File targetFile = new File(application.getDictDir(), zipEntry.getName()); + if (targetFile.exists()) { + targetFile.renameTo(new File(targetFile.getAbsolutePath().replace(".quickdic", ".bak.quickdic"))); + targetFile = new File(application.getDictDir(), zipEntry.getName()); + } + zipOut = new FileOutputStream(targetFile); + copyStream(zipFile, zipOut); } - zipOut = new FileOutputStream(targetFile); - copyStream(zipFile, zipOut); application.backgroundUpdateDictionaries(dictionaryUpdater); - Toast.makeText(context, getString(R.string.installationFinished, dest), - Toast.LENGTH_LONG).show(); + if (!isFinishing()) + Toast.makeText(context, getString(R.string.installationFinished, dest), + Toast.LENGTH_LONG).show(); result = true; } catch (Exception e) { - String msg = getString(R.string.unzippingFailed, dest); + String msg = getString(R.string.unzippingFailed, dest + ": " + e.getMessage()); File dir = application.getDictDir(); - if (!dir.canWrite() || !application.checkFileCreate(dir)) { + if (!dir.canWrite() || !DictionaryApplication.checkFileCreate(dir)) { msg = getString(R.string.notWritable, dir.getAbsolutePath()); } new AlertDialog.Builder(context).setTitle(getString(R.string.error)).setMessage(msg).setNeutralButton("Close", null).show(); @@ -235,14 +259,15 @@ public class DictionaryManagerActivity extends ActionBarActivity { } finally { try { if (zipOut != null) zipOut.close(); - } catch (IOException e) {} + } catch (IOException ignored) {} try { if (zipFile != null) zipFile.close(); - } catch (IOException e) {} + } catch (IOException ignored) {} try { if (zipFileStream != null) zipFileStream.close(); - } catch (IOException e) {} - if (localZipFile != null) localZipFile.delete(); + } catch (IOException ignored) {} + if (localZipFile != null && delete) //noinspection ResultOfMethodCallIgnored + localZipFile.delete(); } return result; } @@ -253,7 +278,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { return intent; } - public void readableCheckAndError(boolean requestPermission) { + private void readableCheckAndError(boolean requestPermission) { final File dictDir = application.getDictDir(); if (dictDir.canRead() && dictDir.canExecute()) return; blockAutoLaunch = true; @@ -278,12 +303,12 @@ public class DictionaryManagerActivity extends ActionBarActivity { } @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { readableCheckAndError(false); application.backgroundUpdateDictionaries(dictionaryUpdater); - setMyListAdapater(); + setMyListAdapter(); } @Override @@ -312,7 +337,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { getListView().getContext()).inflate( R.layout.dictionary_manager_header_row_downloadable, getListView(), false); - showDownloadable = (ToggleButton) downloadableDictionariesHeaderRow + showDownloadable = downloadableDictionariesHeaderRow .findViewById(R.id.hideDownloadable); showDownloadable.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override @@ -321,6 +346,9 @@ public class DictionaryManagerActivity extends ActionBarActivity { } }); + /* + Disable version update notification, I don't maintain the text really + and I don't think it is very useful. final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final String thanksForUpdatingLatestVersion = getString(R.string.thanksForUpdatingVersion); if (!prefs.getString(C.THANKS_FOR_UPDATING_VERSION, "").equals( @@ -330,11 +358,12 @@ public class DictionaryManagerActivity extends ActionBarActivity { prefs.edit().putString(C.THANKS_FOR_UPDATING_VERSION, thanksForUpdatingLatestVersion) .commit(); } + */ + IntentFilter downloadManagerIntents = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); + downloadManagerIntents.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED); + registerReceiver(broadcastReceiver, downloadManagerIntents); - registerReceiver(broadcastReceiver, new IntentFilter( - DownloadManager.ACTION_DOWNLOAD_COMPLETE)); - - setMyListAdapater(); + setMyListAdapter(); registerForContextMenu(getListView()); getListView().setItemsCanFocus(true); @@ -347,7 +376,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { intent.getAction().equals(Intent.ACTION_VIEW)) { blockAutoLaunch = true; Uri uri = intent.getData(); - unzipInstall(this, uri, uri.getLastPathSegment()); + unzipInstall(this, uri, uri.getLastPathSegment(), false); } } @@ -384,7 +413,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { @Override public boolean onQueryTextChange(String filterText) { - setMyListAdapater(); + setMyListAdapter(); return true; } }); @@ -457,22 +486,18 @@ public class DictionaryManagerActivity extends ActionBarActivity { startActivity(DictionaryActivity.getLaunchIntent(getApplicationContext(), new File(prefs.getString(C.DICT_FILE, "")), prefs.getString(C.INDEX_SHORT_NAME, ""), - prefs.getString(C.SEARCH_TOKEN, ""))); + "")); finish(); return; } // Remove the active dictionary from the prefs so we won't autolaunch // next time. - final Editor editor = prefs.edit(); - editor.remove(C.DICT_FILE); - editor.remove(C.INDEX_SHORT_NAME); - editor.remove(C.SEARCH_TOKEN); - editor.commit(); + prefs.edit().remove(C.DICT_FILE).remove(C.INDEX_SHORT_NAME).commit(); application.backgroundUpdateDictionaries(dictionaryUpdater); - setMyListAdapater(); + setMyListAdapter(); } @Override @@ -486,12 +511,24 @@ public class DictionaryManagerActivity extends ActionBarActivity { sort.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(final MenuItem menuItem) { application.sortDictionaries(); - setMyListAdapater(); + setMyListAdapter(); return true; } }); - application.onCreateGlobalOptionsMenu(this, menu); + final MenuItem browserDownload = menu.add(getString(R.string.browserDownload)); + MenuItemCompat.setShowAsAction(browserDownload, MenuItem.SHOW_AS_ACTION_NEVER); + browserDownload.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri + .parse("https://github.com/rdoeffinger/Dictionary/releases/v0.3-dictionaries")); + startActivity(intent); + return false; + } + }); + + DictionaryApplication.onCreateGlobalOptionsMenu(this, menu); return true; } @@ -518,7 +555,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { @Override public boolean onMenuItemClick(android.view.MenuItem item) { application.moveDictionaryToTop(row.dictionaryInfo); - setMyListAdapater(); + setMyListAdapter(); return true; } }); @@ -531,7 +568,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { @Override public boolean onMenuItemClick(android.view.MenuItem item) { application.deleteDictionary(row.dictionaryInfo); - setMyListAdapater(); + setMyListAdapter(); return true; } }); @@ -539,7 +576,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { } private void onShowDownloadableChanged() { - setMyListAdapater(); + setMyListAdapter(); Editor prefs = PreferenceManager.getDefaultSharedPreferences(this).edit(); prefs.putBoolean(C.SHOW_DOWNLOADABLE, showDownloadable.isChecked()); prefs.commit(); @@ -547,12 +584,12 @@ public class DictionaryManagerActivity extends ActionBarActivity { class MyListAdapter extends BaseAdapter { - List dictionariesOnDevice; - List downloadableDictionaries; + final List dictionariesOnDevice; + final List downloadableDictionaries; class Row { - DictionaryInfo dictionaryInfo; - boolean onDevice; + final DictionaryInfo dictionaryInfo; + final boolean onDevice; private Row(DictionaryInfo dictionaryInfo, boolean onDevice) { this.dictionaryInfo = dictionaryInfo; @@ -601,52 +638,98 @@ public class DictionaryManagerActivity extends ActionBarActivity { } @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView instanceof LinearLayout && - convertView != dictionariesOnDeviceHeaderRow && - convertView != downloadableDictionariesHeaderRow) { - /* - * This is done to try to avoid leaking memory that used to - * happen on Android 4.0.3 - */ - ((LinearLayout) convertView).removeAllViews(); - } + public int getViewTypeCount() { + return 3; + } + @Override + public int getItemViewType(int position) { final Row row = getItem(position); + if (row.dictionaryInfo == null) { + return row.onDevice ? 0 : 1; + } + assert row.dictionaryInfo.indexInfos.size() <= 2; + return 2; + } - if (row.onDevice) { - if (row.dictionaryInfo == null) { - return dictionariesOnDeviceHeaderRow; - } - return createDictionaryRow(row.dictionaryInfo, parent, true); + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == dictionariesOnDeviceHeaderRow || + convertView == downloadableDictionariesHeaderRow) { + return convertView; } + final Row row = getItem(position); + if (row.dictionaryInfo == null) { - return downloadableDictionariesHeaderRow; + assert convertView == null; + return row.onDevice ? dictionariesOnDeviceHeaderRow : downloadableDictionariesHeaderRow; } - return createDictionaryRow(row.dictionaryInfo, parent, false); + return createDictionaryRow(row.dictionaryInfo, parent, convertView, row.onDevice); } } - private void setMyListAdapater() { + private void setMyListAdapter() { final String filter = filterSearchView == null ? "" : filterSearchView.getQuery() .toString(); final String[] filters = filter.trim().toLowerCase().split("(\\s|-)+"); setListAdapter(new MyListAdapter(filters)); } + private boolean isDownloadActive(String downloadUrl, boolean cancel) { + DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + final DownloadManager.Query query = new DownloadManager.Query(); + query.setFilterByStatus(DownloadManager.STATUS_PAUSED | DownloadManager.STATUS_PENDING | DownloadManager.STATUS_RUNNING); + final Cursor cursor = downloadManager.query(query); + + // Due to a bug, cursor is null instead of empty when + // the download manager is disabled. + if (cursor == null) { + if (cancel) { + String msg = getString(R.string.downloadManagerQueryFailed); + new AlertDialog.Builder(this).setTitle(getString(R.string.error)) + .setMessage(getString(R.string.downloadFailed, msg)) + .setNeutralButton("Close", null).show(); + } + return cancel; + } + + String destFile; + try { + destFile = new File(new URL(downloadUrl).getPath()).getName(); + } catch (MalformedURLException e) { + throw new RuntimeException("Invalid download URL!", e); + } + while (cursor.moveToNext()) { + if (downloadUrl.equals(cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI)))) + break; + if (destFile.equals(cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE)))) + break; + } + boolean active = !cursor.isAfterLast(); + if (active && cancel) { + long downloadId = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)); + finishedDownloadIds.add(downloadId); + downloadManager.remove(downloadId); + } + cursor.close(); + return active; + } + private View createDictionaryRow(final DictionaryInfo dictionaryInfo, - final ViewGroup parent, boolean canLaunch) { + final ViewGroup parent, View row, boolean canLaunch) { - View row = LayoutInflater.from(parent.getContext()).inflate( + if (row == null) { + row = LayoutInflater.from(parent.getContext()).inflate( R.layout.dictionary_manager_row, parent, false); - final TextView name = (TextView) row.findViewById(R.id.dictionaryName); - final TextView details = (TextView) row.findViewById(R.id.dictionaryDetails); + } + final TextView name = row.findViewById(R.id.dictionaryName); + final TextView details = row.findViewById(R.id.dictionaryDetails); name.setText(application.getDictionaryName(dictionaryInfo.uncompressedFilename)); final boolean updateAvailable = application.updateAvailable(dictionaryInfo); - final Button downloadButton = (Button) row.findViewById(R.id.downloadButton); + final Button downloadButton = row.findViewById(R.id.downloadButton); final DictionaryInfo downloadable = application.getDownloadable(dictionaryInfo.uncompressedFilename); boolean broken = false; if (!dictionaryInfo.isValid()) { @@ -665,21 +748,34 @@ public class DictionaryManagerActivity extends ActionBarActivity { downloadDictionary(downloadable.downloadUrl, downloadable.zipBytes, downloadButton); } }); + downloadButton.setVisibility(View.VISIBLE); + + if (isDownloadActive(downloadable.downloadUrl, false)) + downloadButton.setText("X"); } else { - downloadButton.setVisibility(View.INVISIBLE); + downloadButton.setVisibility(View.GONE); } - LinearLayout buttons = (LinearLayout) row.findViewById(R.id.dictionaryLauncherButtons); + LinearLayout buttons = row.findViewById(R.id.dictionaryLauncherButtons); + final List sortedIndexInfos = application .sortedIndexInfos(dictionaryInfo.indexInfos); final StringBuilder builder = new StringBuilder(); if (updateAvailable) { builder.append(getString(R.string.updateAvailable)); } - for (IndexInfo indexInfo : sortedIndexInfos) { - final View button = IsoUtils.INSTANCE.createButton(buttons.getContext(), dictionaryInfo, - indexInfo, application.languageButtonPixels); - buttons.addView(button); + assert buttons.getChildCount() == 4; + for (int i = 0; i < 2; i++) { + final Button textButton = (Button)buttons.getChildAt(2*i); + final ImageButton imageButton = (ImageButton)buttons.getChildAt(2*i + 1); + if (i >= sortedIndexInfos.size()) { + textButton.setVisibility(View.GONE); + imageButton.setVisibility(View.GONE); + continue; + } + final IndexInfo indexInfo = sortedIndexInfos.get(i); + final View button = IsoUtils.INSTANCE.setupButton(textButton, imageButton, + indexInfo); if (canLaunch) { button.setOnClickListener( @@ -688,10 +784,9 @@ public class DictionaryManagerActivity extends ActionBarActivity { application.getPath(dictionaryInfo.uncompressedFilename), indexInfo.shortName, ""))); - } else { - button.setEnabled(false); - button.setFocusable(false); } + button.setEnabled(canLaunch); + button.setFocusable(canLaunch); if (builder.length() != 0) { builder.append("; "); } @@ -703,70 +798,48 @@ public class DictionaryManagerActivity extends ActionBarActivity { if (broken) { name.setText("Broken: " + application.getDictionaryName(dictionaryInfo.uncompressedFilename)); builder.append("; Cannot be used, redownload, check hardware/file system"); - // Allow deleting, but cannot open - row.setLongClickable(true); } details.setText(builder.toString()); if (canLaunch) { - row.setClickable(true); row.setOnClickListener(new IntentLauncher(parent.getContext(), DictionaryActivity.getLaunchIntent(getApplicationContext(), application.getPath(dictionaryInfo.uncompressedFilename), dictionaryInfo.indexInfos.get(0).shortName, ""))); // do not setFocusable, for keyboard navigation // offering only the index buttons is better. - row.setLongClickable(true); } + row.setClickable(canLaunch); + // Allow deleting, even if we cannot open + row.setLongClickable(broken || canLaunch); row.setBackgroundResource(android.R.drawable.menuitem_background); return row; } private synchronized void downloadDictionary(final String downloadUrl, long bytes, Button downloadButton) { - String destFile; - try { - destFile = new File(new URL(downloadUrl).getPath()).getName(); - } catch (MalformedURLException e) { - throw new RuntimeException("Invalid download URL!", e); - } - DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); - final DownloadManager.Query query = new DownloadManager.Query(); - query.setFilterByStatus(DownloadManager.STATUS_PAUSED | DownloadManager.STATUS_PENDING | DownloadManager.STATUS_RUNNING); - final Cursor cursor = downloadManager.query(query); - - // Due to a bug, cursor is null instead of empty when - // the download manager is disabled. - if (cursor == null) { - String msg = getString(R.string.downloadManagerQueryFailed); - new AlertDialog.Builder(DictionaryManagerActivity.this).setTitle(getString(R.string.error)) - .setMessage(getString(R.string.downloadFailed, msg)) - .setNeutralButton("Close", null).show(); - return; - } - - while (cursor.moveToNext()) { - if (downloadUrl.equals(cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI)))) - break; - if (destFile.equals(cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE)))) - break; - } - if (!cursor.isAfterLast()) { - downloadManager.remove(cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID))); + if (isDownloadActive(downloadUrl, true)) { downloadButton .setText(getString( R.string.downloadButton, bytes / 1024.0 / 1024.0)); - cursor.close(); return; } - cursor.close(); - Request request = new Request( - Uri.parse(downloadUrl)); + // API 19 and earlier have issues with github URLs, both http and https. + // Really old (~API 10) DownloadManager cannot handle https at all. + // Work around both with in one. + String altUrl = downloadUrl.replace("https://github.com/rdoeffinger/Dictionary/releases/download/v0.2-dictionaries/", "http://ffmpeg.org/~reimar/dict/"); + altUrl = altUrl.replace("https://github.com/rdoeffinger/Dictionary/releases/download/v0.3-dictionaries/", "http://ffmpeg.org/~reimar/dict/"); + Request request = new Request(Uri.parse(Build.VERSION.SDK_INT < 21 ? altUrl : downloadUrl)); + String destFile; + try { + destFile = new File(new URL(downloadUrl).getPath()).getName(); + } catch (MalformedURLException e) { + throw new RuntimeException("Invalid download URL!", e); + } Log.d(LOG, "Downloading to: " + destFile); request.setTitle(destFile); - File destFilePath = new File(application.getDictDir(), destFile); destFilePath.delete(); try { @@ -774,6 +847,16 @@ public class DictionaryManagerActivity extends ActionBarActivity { } catch (Exception e) { } + DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + + if (downloadManager == null) { + String msg = getString(R.string.downloadManagerQueryFailed); + new AlertDialog.Builder(this).setTitle(getString(R.string.error)) + .setMessage(getString(R.string.downloadFailed, msg)) + .setNeutralButton("Close", null).show(); + return; + } + try { downloadManager.enqueue(request); } catch (SecurityException e) {