X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=src%2Fcom%2Fhughes%2Fandroid%2Fdictionary%2FDictionaryManagerActivity.java;h=0374794d82a84b48d65b70041d64341e53758384;hb=14d18d24c26cdb8d270c8a0220f23f8e969adccb;hp=a1bc375823f8954db8fb41e4d3ba0500fb4ff7eb;hpb=50e3589564c609cd41c430aa10b45dc9db06623b;p=Dictionary.git diff --git a/src/com/hughes/android/dictionary/DictionaryManagerActivity.java b/src/com/hughes/android/dictionary/DictionaryManagerActivity.java index a1bc375..0374794 100644 --- a/src/com/hughes/android/dictionary/DictionaryManagerActivity.java +++ b/src/com/hughes/android/dictionary/DictionaryManagerActivity.java @@ -14,6 +14,7 @@ package com.hughes.android.dictionary; +import android.Manifest; import android.app.AlertDialog; import android.app.DownloadManager; import android.app.DownloadManager.Request; @@ -24,12 +25,17 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; +import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.preference.PreferenceManager; +import android.provider.Settings; +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.widget.SearchView; @@ -42,6 +48,7 @@ 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.ViewGroup; @@ -63,6 +70,7 @@ import android.widget.ToggleButton; import com.hughes.android.dictionary.DictionaryInfo.IndexInfo; import com.hughes.android.util.IntentLauncher; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -71,6 +79,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -136,7 +146,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) { final long downloadId = intent.getLongExtra( - DownloadManager.EXTRA_DOWNLOAD_ID, 0); + DownloadManager.EXTRA_DOWNLOAD_ID, 0); if (finishedDownloadIds.contains(downloadId)) return; // ignore double notifications final DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadId); @@ -149,20 +159,26 @@ public class DictionaryManagerActivity extends ActionBarActivity { } final String dest = cursor - .getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); + .getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); final int status = cursor - .getInt(cursor - .getColumnIndex(DownloadManager.COLUMN_STATUS)); + .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=" + reason); + "Download failed: status=" + status + + ", 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; + 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; @@ -170,14 +186,14 @@ 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(); + Toast.LENGTH_LONG).show(); final Uri zipUri = Uri.parse(dest); File localZipFile = null; InputStream zipFileStream = null; ZipInputStream zipFile = null; - OutputStream zipOut = null; + FileOutputStream zipOut = null; try { if (zipUri.getScheme().equals("content")) { zipFileStream = context.getContentResolver().openInputStream(zipUri); @@ -186,7 +202,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { localZipFile = new File(zipUri.getPath()); zipFileStream = new FileInputStream(localZipFile); } - zipFile = new ZipInputStream(zipFileStream); + 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()); @@ -198,7 +214,7 @@ public class DictionaryManagerActivity extends ActionBarActivity { copyStream(zipFile, zipOut); application.backgroundUpdateDictionaries(dictionaryUpdater); Toast.makeText(context, getString(R.string.installationFinished, dest), - Toast.LENGTH_LONG).show(); + Toast.LENGTH_LONG).show(); finishedDownloadIds.add(downloadId); Log.w(LOG, "Unzipping finished: " + dest + " Id: " + downloadId); } catch (Exception e) { @@ -210,9 +226,15 @@ public class DictionaryManagerActivity extends ActionBarActivity { new AlertDialog.Builder(context).setTitle(getString(R.string.error)).setMessage(msg).setNeutralButton("Close", null).show(); Log.e(LOG, "Failed to unzip.", e); } finally { - 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) {} + 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(); } } @@ -225,16 +247,58 @@ public class DictionaryManagerActivity extends ActionBarActivity { return intent; } + public void readableCheckAndError(boolean requestPermission) { + final File dictDir = application.getDictDir(); + if (dictDir.canRead() && dictDir.canExecute()) return; + blockAutoLaunch = true; + if (requestPermission && + 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; + } + blockAutoLaunch = true; + + AlertDialog.Builder builder = new AlertDialog.Builder(getListView().getContext()); + builder.setTitle(getString(R.string.error)); + builder.setMessage(getString( + R.string.unableToReadDictionaryDir, + dictDir.getAbsolutePath(), + Environment.getExternalStorageDirectory())); + builder.setNeutralButton("Close", null); + builder.create().show(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + readableCheckAndError(false); + + application.backgroundUpdateDictionaries(dictionaryUpdater); + + setMyListAdapater(); + } + @Override public void onCreate(Bundle savedInstanceState) { - // This must be first, otherwise the actiona bar doesn't get + // This must be first, otherwise the action bar doesn't get // styled properly. - setTheme(((DictionaryApplication) getApplication()).getSelectedTheme().themeId); + // Unfortunately on some (Samsung?) Android versions this + // results in a ClassCastException... + boolean themeSet = true; + try { + setTheme(((DictionaryApplication) getApplication()).getSelectedTheme().themeId); + } catch (ClassCastException e) { + themeSet = false; + } super.onCreate(savedInstanceState); Log.d(LOG, "onCreate:" + this); application = (DictionaryApplication) getApplication(); + if (!themeSet) + setTheme(application.getSelectedTheme().themeId); blockAutoLaunch = false; @@ -242,15 +306,15 @@ public class DictionaryManagerActivity extends ActionBarActivity { setContentView(R.layout.dictionary_manager_activity); dictionariesOnDeviceHeaderRow = (LinearLayout) LayoutInflater.from( - getListView().getContext()).inflate( - R.layout.dictionary_manager_header_row_on_device, getListView(), false); + getListView().getContext()).inflate( + R.layout.dictionary_manager_header_row_on_device, getListView(), false); downloadableDictionariesHeaderRow = (LinearLayout) LayoutInflater.from( - getListView().getContext()).inflate( - R.layout.dictionary_manager_header_row_downloadable, getListView(), false); + getListView().getContext()).inflate( + R.layout.dictionary_manager_header_row_downloadable, getListView(), false); showDownloadable = (ToggleButton) downloadableDictionariesHeaderRow - .findViewById(R.id.hideDownloadable); + .findViewById(R.id.hideDownloadable); showDownloadable.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @@ -261,32 +325,21 @@ public class DictionaryManagerActivity extends ActionBarActivity { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final String thanksForUpdatingLatestVersion = getString(R.string.thanksForUpdatingVersion); if (!prefs.getString(C.THANKS_FOR_UPDATING_VERSION, "").equals( - thanksForUpdatingLatestVersion)) { + thanksForUpdatingLatestVersion)) { blockAutoLaunch = true; startActivity(HtmlDisplayActivity.getWhatsNewLaunchIntent(getApplicationContext())); prefs.edit().putString(C.THANKS_FOR_UPDATING_VERSION, thanksForUpdatingLatestVersion) - .commit(); + .commit(); } registerReceiver(broadcastReceiver, new IntentFilter( - DownloadManager.ACTION_DOWNLOAD_COMPLETE)); + DownloadManager.ACTION_DOWNLOAD_COMPLETE)); setMyListAdapater(); registerForContextMenu(getListView()); + getListView().setItemsCanFocus(true); - final File dictDir = application.getDictDir(); - if (!dictDir.canRead() || !dictDir.canExecute()) { - blockAutoLaunch = true; - - AlertDialog.Builder builder = new AlertDialog.Builder(getListView().getContext()); - builder.setTitle(getString(R.string.error)); - builder.setMessage(getString( - R.string.unableToReadDictionaryDir, - dictDir.getAbsolutePath(), - Environment.getExternalStorageDirectory())); - builder.setNeutralButton("Close", null); - builder.create().show(); - } + readableCheckAndError(true); onCreateSetupActionBar(); } @@ -309,11 +362,11 @@ public class DictionaryManagerActivity extends ActionBarActivity { filterSearchView.setLayoutParams(lp); filterSearchView.setInputType(InputType.TYPE_CLASS_TEXT); filterSearchView.setImeOptions( - EditorInfo.IME_ACTION_DONE | - EditorInfo.IME_FLAG_NO_EXTRACT_UI | - // EditorInfo.IME_FLAG_NO_FULLSCREEN | // Requires API - // 11 - EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + EditorInfo.IME_ACTION_DONE | + EditorInfo.IME_FLAG_NO_EXTRACT_UI | + // EditorInfo.IME_FLAG_NO_FULLSCREEN | // Requires API + // 11 + EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS); filterSearchView.setOnQueryTextListener(new OnQueryTextListener() { @Override @@ -344,16 +397,24 @@ public class DictionaryManagerActivity extends ActionBarActivity { unregisterReceiver(broadcastReceiver); } - private static int copyStream(final InputStream in, final OutputStream out) - throws IOException { + private static void copyStream(final InputStream ins, final FileOutputStream outs) + throws IOException { + ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 64); + FileChannel out = outs.getChannel(); int bytesRead; - final byte[] bytes = new byte[1024 * 16]; - while ((bytesRead = in.read(bytes)) != -1) { - out.write(bytes, 0, bytesRead); - } - in.close(); - out.close(); - return bytesRead; + int pos = 0; + final byte[] bytes = new byte[1024 * 64]; + do { + bytesRead = ins.read(bytes, pos, bytes.length - pos); + if (bytesRead != -1) pos += bytesRead; + if (bytesRead == -1 ? pos != 0 : 2*pos >= bytes.length) { + buf.put(bytes, 0, pos); + pos = 0; + buf.flip(); + while (buf.hasRemaining()) out.write(buf); + buf.clear(); + } + } while (bytesRead != -1); } @Override @@ -387,9 +448,9 @@ public class DictionaryManagerActivity extends ActionBarActivity { prefs.contains(C.INDEX_SHORT_NAME)) { Log.d(LOG, "Skipping DictionaryManager, going straight to dictionary."); startActivity(DictionaryActivity.getLaunchIntent(getApplicationContext(), - new File(prefs.getString(C.DICT_FILE, "")), - prefs.getString(C.INDEX_SHORT_NAME, ""), - prefs.getString(C.SEARCH_TOKEN, ""))); + new File(prefs.getString(C.DICT_FILE, "")), + prefs.getString(C.INDEX_SHORT_NAME, ""), + prefs.getString(C.SEARCH_TOKEN, ""))); finish(); return; } @@ -409,18 +470,32 @@ public class DictionaryManagerActivity extends ActionBarActivity { @Override public boolean onCreateOptionsMenu(final Menu menu) { + if ("true".equals(Settings.System.getString(getContentResolver(), "firebase.test.lab"))) + { + return false; // testing the menu is not very interesting + } + 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; } @Override public void onCreateContextMenu(final ContextMenu menu, final View view, - final ContextMenuInfo menuInfo) { + final ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); Log.d(LOG, "onCreateContextMenu, " + menuInfo); final AdapterContextMenuInfo adapterContextMenuInfo = - (AdapterContextMenuInfo) menuInfo; + (AdapterContextMenuInfo) menuInfo; final int position = adapterContextMenuInfo.position; final MyListAdapter.Row row = (MyListAdapter.Row) getListAdapter().getItem(position); @@ -430,29 +505,29 @@ public class DictionaryManagerActivity extends ActionBarActivity { if (position > 0 && row.onDevice) { final android.view.MenuItem moveToTopMenuItem = - menu.add(R.string.moveToTop); + menu.add(R.string.moveToTop); moveToTopMenuItem.setOnMenuItemClickListener(new - android.view.MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(android.view.MenuItem item) { - application.moveDictionaryToTop(row.dictionaryInfo); - setMyListAdapater(); - return true; - } - }); + android.view.MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(android.view.MenuItem item) { + application.moveDictionaryToTop(row.dictionaryInfo); + setMyListAdapater(); + return true; + } + }); } if (row.onDevice) { final android.view.MenuItem deleteMenuItem = menu.add(R.string.deleteDictionary); deleteMenuItem - .setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(android.view.MenuItem item) { - application.deleteDictionary(row.dictionaryInfo); - setMyListAdapater(); - return true; - } - }); + .setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(android.view.MenuItem item) { + application.deleteDictionary(row.dictionaryInfo); + setMyListAdapater(); + return true; + } + }); } } @@ -472,8 +547,8 @@ public class DictionaryManagerActivity extends ActionBarActivity { 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; } } @@ -549,16 +624,16 @@ public class DictionaryManagerActivity extends ActionBarActivity { private void setMyListAdapater() { final String filter = filterSearchView == null ? "" : filterSearchView.getQuery() - .toString(); + .toString(); final String[] filters = filter.trim().toLowerCase().split("(\\s|-)+"); setListAdapter(new MyListAdapter(filters)); } private View createDictionaryRow(final DictionaryInfo dictionaryInfo, - final ViewGroup parent, boolean canLaunch) { + final ViewGroup parent, boolean canLaunch) { View row = LayoutInflater.from(parent.getContext()).inflate( - R.layout.dictionary_manager_row, parent, false); + 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); name.setText(application.getDictionaryName(dictionaryInfo.uncompressedFilename)); @@ -573,9 +648,9 @@ public class DictionaryManagerActivity extends ActionBarActivity { } if (downloadable != null && (!canLaunch || updateAvailable)) { downloadButton - .setText(getString( - R.string.downloadButton, - downloadable.zipBytes / 1024.0 / 1024.0)); + .setText(getString( + R.string.downloadButton, + downloadable.zipBytes / 1024.0 / 1024.0)); downloadButton.setMinWidth(application.languageButtonPixels * 3 / 2); downloadButton.setOnClickListener(new OnClickListener() { @Override @@ -596,24 +671,25 @@ public class DictionaryManagerActivity extends ActionBarActivity { } for (IndexInfo indexInfo : sortedIndexInfos) { final View button = application.createButton(buttons.getContext(), dictionaryInfo, - indexInfo); + indexInfo); buttons.addView(button); if (canLaunch) { button.setOnClickListener( - new IntentLauncher(buttons.getContext(), - DictionaryActivity.getLaunchIntent(getApplicationContext(), - application.getPath(dictionaryInfo.uncompressedFilename), - indexInfo.shortName, ""))); + new IntentLauncher(buttons.getContext(), + DictionaryActivity.getLaunchIntent(getApplicationContext(), + application.getPath(dictionaryInfo.uncompressedFilename), + indexInfo.shortName, ""))); } else { button.setEnabled(false); + button.setFocusable(false); } if (builder.length() != 0) { builder.append("; "); } builder.append(getString(R.string.indexInfo, indexInfo.shortName, - indexInfo.mainTokenCount)); + indexInfo.mainTokenCount)); } builder.append("; "); builder.append(getString(R.string.downloadButton, dictionaryInfo.uncompressedBytes / 1024.0 / 1024.0)); @@ -628,10 +704,11 @@ public class DictionaryManagerActivity extends ActionBarActivity { if (canLaunch) { row.setClickable(true); row.setOnClickListener(new IntentLauncher(parent.getContext(), - DictionaryActivity.getLaunchIntent(getApplicationContext(), - application.getPath(dictionaryInfo.uncompressedFilename), - dictionaryInfo.indexInfos.get(0).shortName, ""))); - row.setFocusable(true); + 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.setBackgroundResource(android.R.drawable.menuitem_background); @@ -655,8 +732,8 @@ public class DictionaryManagerActivity extends ActionBarActivity { // 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(); + .setMessage(getString(R.string.downloadFailed, R.string.downloadManagerQueryFailed)) + .setNeutralButton("Close", null).show(); return; } @@ -669,15 +746,15 @@ public class DictionaryManagerActivity extends ActionBarActivity { if (!cursor.isAfterLast()) { downloadManager.remove(cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID))); downloadButton - .setText(getString( - R.string.downloadButton, - bytes / 1024.0 / 1024.0)); + .setText(getString( + R.string.downloadButton, + bytes / 1024.0 / 1024.0)); cursor.close(); return; } cursor.close(); Request request = new Request( - Uri.parse(downloadUrl)); + Uri.parse(downloadUrl)); Log.d(LOG, "Downloading to: " + destFile); request.setTitle(destFile);