X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=src%2Fcom%2Fhughes%2Fandroid%2Fdictionary%2FDictionaryManagerActivity.java;h=0139c474b18aa142ac6e0c6eab96ee5154bab59a;hb=51adf3a1cb11c9be99558e27162f2eb51c6c62ea;hp=fa0bf8a5415438076eb36fde61baf87ca656f34d;hpb=3d72bc11d5ef9d58e62b5beb4e33a437da28d4b8;p=Dictionary.git diff --git a/src/com/hughes/android/dictionary/DictionaryManagerActivity.java b/src/com/hughes/android/dictionary/DictionaryManagerActivity.java index fa0bf8a..0139c47 100644 --- a/src/com/hughes/android/dictionary/DictionaryManagerActivity.java +++ b/src/com/hughes/android/dictionary/DictionaryManagerActivity.java @@ -15,10 +15,10 @@ package com.hughes.android.dictionary; import android.app.AlertDialog; -import android.app.Dialog; 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; @@ -30,39 +30,43 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.preference.PreferenceManager; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +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; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; -import android.view.View.OnLongClickListener; -import android.view.inputmethod.EditorInfo; 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.FrameLayout; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.Toast; import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.FrameLayout; import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ListView; import android.widget.TextView; +import android.widget.Toast; import android.widget.ToggleButton; -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockListActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuInflater; -import com.actionbarsherlock.widget.SearchView; -import com.actionbarsherlock.widget.SearchView.OnQueryTextListener; import com.hughes.android.dictionary.DictionaryInfo.IndexInfo; import com.hughes.android.util.IntentLauncher; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -70,18 +74,38 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; 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 SherlockListActivity { +public class DictionaryManagerActivity extends ActionBarActivity { static final String LOG = "QuickDic"; static boolean blockAutoLaunch = false; + private ListView listView; + private ListView getListView() { + if (listView == null) { + listView = (ListView)findViewById(android.R.id.list); + } + return listView; + } + private void setListAdapter(ListAdapter adapter) { + getListView().setAdapter(adapter); + } + private ListAdapter getListAdapter() { + return getListView().getAdapter(); + } + + // For DownloadManager bug workaround + private Set finishedDownloadIds = new HashSet(); + DictionaryApplication application; SearchView filterSearchView; @@ -101,7 +125,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { uiHandler.post(new Runnable() { @Override public void run() { - setListAdapater(); + setMyListAdapater(); } }); } @@ -109,18 +133,19 @@ public class DictionaryManagerActivity extends SherlockListActivity { final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { + public synchronized void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) { final long downloadId = intent.getLongExtra( DownloadManager.EXTRA_DOWNLOAD_ID, 0); + if (finishedDownloadIds.contains(downloadId)) return; // ignore double notifications final DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadId); final DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); final Cursor cursor = downloadManager.query(query); - if (!cursor.moveToFirst()) { + if (cursor == null || !cursor.moveToFirst()) { Log.e(LOG, "Couldn't find download."); return; } @@ -131,51 +156,81 @@ public class DictionaryManagerActivity extends SherlockListActivity { .getInt(cursor .getColumnIndex(DownloadManager.COLUMN_STATUS)); if (DownloadManager.STATUS_SUCCESSFUL != status) { + final int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON)); Log.w(LOG, "Download failed: status=" + status + - ", reason=" + cursor.getString(cursor - .getColumnIndex(DownloadManager.COLUMN_REASON))); - Toast.makeText(context, getString(R.string.downloadFailed, dest), - Toast.LENGTH_LONG).show(); + ", reason=" + reason); + String msg = Integer.toString(reason); + switch (reason) { + case DownloadManager.ERROR_FILE_ALREADY_EXISTS: msg = "File exists"; break; + case DownloadManager.ERROR_FILE_ERROR: msg = "File error"; break; + case DownloadManager.ERROR_INSUFFICIENT_SPACE: msg = "Not enough space"; break; + } + new AlertDialog.Builder(context).setTitle(getString(R.string.error)).setMessage(getString(R.string.downloadFailed, msg)).setNeutralButton("Close", null).show(); return; } - Log.w(LOG, "Download finished: " + dest); - final File localZipFile = new File(Uri.parse(dest).getPath()); + Log.w(LOG, "Download finished: " + dest + " Id: " + downloadId); + Toast.makeText(context, getString(R.string.unzippingDictionary, dest), + Toast.LENGTH_LONG).show(); + + final Uri zipUri = Uri.parse(dest); + File localZipFile = null; + InputStream zipFileStream = null; + ZipInputStream zipFile = null; + OutputStream zipOut = null; try { - ZipFile zipFile = new ZipFile(localZipFile); - final ZipEntry zipEntry = zipFile.entries().nextElement(); + if (zipUri.getScheme().equals("content")) { + zipFileStream = context.getContentResolver().openInputStream(zipUri); + localZipFile = null; + } else { + localZipFile = new File(zipUri.getPath()); + zipFileStream = new FileInputStream(localZipFile); + } + zipFile = new ZipInputStream(zipFileStream); + final ZipEntry zipEntry = zipFile.getNextEntry(); Log.d(LOG, "Unzipping entry: " + zipEntry.getName()); - final InputStream zipIn = zipFile.getInputStream(zipEntry); - final OutputStream zipOut = new FileOutputStream( - new File(application.getDictDir(), zipEntry.getName())); - copyStream(zipIn, zipOut); - zipFile.close(); + 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); application.backgroundUpdateDictionaries(dictionaryUpdater); - Toast.makeText(context, getString(R.string.downloadFinished, dest), + Toast.makeText(context, getString(R.string.installationFinished, dest), Toast.LENGTH_LONG).show(); + finishedDownloadIds.add(downloadId); + Log.w(LOG, "Unzipping finished: " + dest + " Id: " + downloadId); } catch (Exception e) { - Toast.makeText(context, getString(R.string.downloadFailed, dest), - Toast.LENGTH_LONG).show(); + String msg = getString(R.string.unzippingFailed, dest); + File dir = application.getDictDir(); + if (!dir.canWrite() || !application.checkFileCreate(dir)) { + msg = getString(R.string.notWritable, dir.getAbsolutePath()); + } + new AlertDialog.Builder(context).setTitle(getString(R.string.error)).setMessage(msg).setNeutralButton("Close", null).show(); Log.e(LOG, "Failed to unzip.", e); } finally { - localZipFile.delete(); + try { if (zipOut != null) zipOut.close(); } catch (IOException e) {} + try { if (zipFile != null) zipFile.close(); } catch (IOException e) {} + try { if (zipFileStream != null) zipFileStream.close(); } catch (IOException e) {} + if (localZipFile != null) localZipFile.delete(); } } } }; - public static Intent getLaunchIntent() { - final Intent intent = new Intent(); - intent.setClassName(DictionaryManagerActivity.class.getPackage().getName(), - DictionaryManagerActivity.class.getName()); + public static Intent getLaunchIntent(Context c) { + final Intent intent = new Intent(c, DictionaryManagerActivity.class); intent.putExtra(C.CAN_AUTO_LAUNCH_DICT, false); return intent; } @Override public void onCreate(Bundle savedInstanceState) { + // This must be first, otherwise the actiona bar doesn't get + // styled properly. setTheme(((DictionaryApplication) getApplication()).getSelectedTheme().themeId); super.onCreate(savedInstanceState); @@ -201,7 +256,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { showDownloadable.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onShowLocalChanged(); + onShowDownloadableChanged(); } }); @@ -210,7 +265,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { if (!prefs.getString(C.THANKS_FOR_UPDATING_VERSION, "").equals( thanksForUpdatingLatestVersion)) { blockAutoLaunch = true; - startActivity(HtmlDisplayActivity.getWhatsNewLaunchIntent()); + startActivity(HtmlDisplayActivity.getWhatsNewLaunchIntent(getApplicationContext())); prefs.edit().putString(C.THANKS_FOR_UPDATING_VERSION, thanksForUpdatingLatestVersion) .commit(); } @@ -218,7 +273,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { registerReceiver(broadcastReceiver, new IntentFilter( DownloadManager.ACTION_DOWNLOAD_COMPLETE)); - setListAdapater(); + setMyListAdapater(); registerForContextMenu(getListView()); final File dictDir = application.getDictDir(); @@ -231,6 +286,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { R.string.unableToReadDictionaryDir, dictDir.getAbsolutePath(), Environment.getExternalStorageDirectory())); + builder.setNeutralButton("Close", null); builder.create().show(); } @@ -240,6 +296,8 @@ public class DictionaryManagerActivity extends SherlockListActivity { private void onCreateSetupActionBar() { ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayShowTitleEnabled(false); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(false); filterSearchView = new SearchView(getSupportActionBar().getThemedContext()); filterSearchView.setIconifiedByDefault(false); @@ -248,29 +306,27 @@ public class DictionaryManagerActivity extends SherlockListActivity { // wrong place. filterSearchView.setQueryHint(getString(R.string.searchText)); filterSearchView.setSubmitButtonEnabled(false); - final int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, - getResources().getDisplayMetrics()); - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(width, + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); filterSearchView.setLayoutParams(lp); + filterSearchView.setInputType(InputType.TYPE_CLASS_TEXT); filterSearchView.setImeOptions( - EditorInfo.IME_ACTION_SEARCH | + EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI | - EditorInfo.IME_FLAG_NO_ENTER_ACTION | // EditorInfo.IME_FLAG_NO_FULLSCREEN | // Requires API // 11 - EditorInfo.IME_MASK_ACTION | EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS); filterSearchView.setOnQueryTextListener(new OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { - return true; + filterSearchView.clearFocus(); + return false; } @Override public boolean onQueryTextChange(String filterText) { - setListAdapater(); + setMyListAdapater(); return true; } }); @@ -278,6 +334,10 @@ public class DictionaryManagerActivity extends SherlockListActivity { actionBar.setCustomView(filterSearchView); actionBar.setDisplayShowCustomEnabled(true); + + // Avoid wasting space on large left inset + Toolbar tb = (Toolbar)filterSearchView.getParent(); + tb.setContentInsetsRelative(0, 0); } @Override @@ -321,14 +381,14 @@ public class DictionaryManagerActivity extends SherlockListActivity { } final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - showDownloadable.setChecked(prefs.getBoolean(C.SHOW_DOWNLOADABLE, false)); + showDownloadable.setChecked(prefs.getBoolean(C.SHOW_DOWNLOADABLE, true)); if (!blockAutoLaunch && getIntent().getBooleanExtra(C.CAN_AUTO_LAUNCH_DICT, true) && prefs.contains(C.DICT_FILE) && prefs.contains(C.INDEX_SHORT_NAME)) { Log.d(LOG, "Skipping DictionaryManager, going straight to dictionary."); - startActivity(DictionaryActivity.getLaunchIntent( + startActivity(DictionaryActivity.getLaunchIntent(getApplicationContext(), new File(prefs.getString(C.DICT_FILE, "")), prefs.getString(C.INDEX_SHORT_NAME, ""), prefs.getString(C.SEARCH_TOKEN, ""))); @@ -346,11 +406,21 @@ public class DictionaryManagerActivity extends SherlockListActivity { application.backgroundUpdateDictionaries(dictionaryUpdater); - setListAdapater(); + setMyListAdapater(); } @Override public boolean onCreateOptionsMenu(final Menu menu) { + final MenuItem sort = menu.add(getString(R.string.sortDicts)); + MenuItemCompat.setShowAsAction(sort, MenuItem.SHOW_AS_ACTION_NEVER); + sort.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + application.sortDictionaries(); + setMyListAdapater(); + return true; + } + }); + application.onCreateGlobalOptionsMenu(this, menu); return true; } @@ -378,7 +448,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { @Override public boolean onMenuItemClick(android.view.MenuItem item) { application.moveDictionaryToTop(row.dictionaryInfo); - setListAdapater(); + setMyListAdapater(); return true; } }); @@ -391,15 +461,15 @@ public class DictionaryManagerActivity extends SherlockListActivity { @Override public boolean onMenuItemClick(android.view.MenuItem item) { application.deleteDictionary(row.dictionaryInfo); - setListAdapater(); + setMyListAdapater(); return true; } }); } } - private void onShowLocalChanged() { - setListAdapater(); + private void onShowDownloadableChanged() { + setMyListAdapater(); Editor prefs = PreferenceManager.getDefaultSharedPreferences(this).edit(); prefs.putBoolean(C.SHOW_DOWNLOADABLE, showDownloadable.isChecked()); prefs.commit(); @@ -414,8 +484,8 @@ public class DictionaryManagerActivity extends SherlockListActivity { DictionaryInfo dictionaryInfo; boolean onDevice; - private Row(DictionaryInfo dictinoaryInfo, boolean onDevice) { - this.dictionaryInfo = dictinoaryInfo; + private Row(DictionaryInfo dictionaryInfo, boolean onDevice) { + this.dictionaryInfo = dictionaryInfo; this.onDevice = onDevice; } } @@ -489,7 +559,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { } - private void setListAdapater() { + private void setMyListAdapater() { final String filter = filterSearchView == null ? "" : filterSearchView.getQuery() .toString(); final String[] filters = filter.trim().toLowerCase().split("(\\s|-)+"); @@ -497,7 +567,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { } private View createDictionaryRow(final DictionaryInfo dictionaryInfo, - final ViewGroup parent, final boolean canLaunch) { + final ViewGroup parent, boolean canLaunch) { View row = LayoutInflater.from(parent.getContext()).inflate( R.layout.dictionary_manager_row, parent, false); @@ -507,16 +577,22 @@ public class DictionaryManagerActivity extends SherlockListActivity { final boolean updateAvailable = application.updateAvailable(dictionaryInfo); final Button downloadButton = (Button) row.findViewById(R.id.downloadButton); - if (!canLaunch || updateAvailable) { + final DictionaryInfo downloadable = application.getDownloadable(dictionaryInfo.uncompressedFilename); + boolean broken = false; + if (!dictionaryInfo.isValid()) { + broken = true; + canLaunch = false; + } + if (downloadable != null && (!canLaunch || updateAvailable)) { downloadButton .setText(getString( R.string.downloadButton, - application.getDownloadable(dictionaryInfo.uncompressedFilename).zipBytes / 1024.0 / 1024.0)); + downloadable.zipBytes / 1024.0 / 1024.0)); downloadButton.setMinWidth(application.languageButtonPixels * 3 / 2); downloadButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { - downloadDictionary(dictionaryInfo); + downloadDictionary(downloadable.downloadUrl, downloadable.zipBytes, downloadButton); } }); } else { @@ -528,7 +604,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { .sortedIndexInfos(dictionaryInfo.indexInfos); final StringBuilder builder = new StringBuilder(); if (updateAvailable) { - builder.append(getString(R.string.updateButton)); + builder.append(getString(R.string.updateAvailable)); } for (IndexInfo indexInfo : sortedIndexInfos) { final View button = application.createButton(buttons.getContext(), dictionaryInfo, @@ -538,7 +614,7 @@ public class DictionaryManagerActivity extends SherlockListActivity { if (canLaunch) { button.setOnClickListener( new IntentLauncher(buttons.getContext(), - DictionaryActivity.getLaunchIntent( + DictionaryActivity.getLaunchIntent(getApplicationContext(), application.getPath(dictionaryInfo.uncompressedFilename), indexInfo.shortName, ""))); @@ -551,12 +627,20 @@ public class DictionaryManagerActivity extends SherlockListActivity { builder.append(getString(R.string.indexInfo, indexInfo.shortName, indexInfo.mainTokenCount)); } + builder.append("; "); + builder.append(getString(R.string.downloadButton, dictionaryInfo.uncompressedBytes / 1024.0 / 1024.0)); + 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( + DictionaryActivity.getLaunchIntent(getApplicationContext(), application.getPath(dictionaryInfo.uncompressedFilename), dictionaryInfo.indexInfos.get(0).shortName, ""))); row.setFocusable(true); @@ -567,21 +651,65 @@ public class DictionaryManagerActivity extends SherlockListActivity { return row; } - private void downloadDictionary(final DictionaryInfo dictionaryInfo) { + 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) { + new AlertDialog.Builder(DictionaryManagerActivity.this).setTitle(getString(R.string.error)) + .setMessage(getString(R.string.downloadFailed, R.string.downloadManagerQueryFailed)) + .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))); + downloadButton + .setText(getString( + R.string.downloadButton, + bytes / 1024.0 / 1024.0)); + cursor.close(); + return; + } + cursor.close(); Request request = new Request( - Uri.parse(dictionaryInfo.downloadUrl)); + Uri.parse(downloadUrl)); + + Log.d(LOG, "Downloading to: " + destFile); + request.setTitle(destFile); + + File destFilePath = new File(application.getDictDir(), destFile); + destFilePath.delete(); try { - final String destFile = new File(new URL(dictionaryInfo.downloadUrl).getFile()) - .getName(); - Log.d(LOG, "Downloading to: " + destFile); + request.setDestinationUri(Uri.fromFile(destFilePath)); + } catch (Exception e) { + } - request.setDestinationUri(Uri.fromFile(new File(Environment - .getExternalStorageDirectory(), destFile))); - } catch (MalformedURLException e) { - throw new RuntimeException(e); + try { + downloadManager.enqueue(request); + } catch (SecurityException e) { + request = new Request(Uri.parse(downloadUrl)); + request.setTitle(destFile); + downloadManager.enqueue(request); } - downloadManager.enqueue(request); + Log.w(LOG, "Download started: " + destFile); + downloadButton.setText("X"); } }