From ce8104f679b2ba8d760905c5e6f32597199a2331 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Tue, 6 Sep 2011 07:18:45 -0700 Subject: [PATCH 1/4] move dict-related methods from GameUtils.java to new DictUtils.java --- .../eehouse/android/xw4/BoardActivity.java | 2 +- .../android/xw4/DictImportActivity.java | 2 +- .../eehouse/android/xw4/DictLangCache.java | 12 +- .../android/xw4/DictListPreference.java | 2 +- .../org/eehouse/android/xw4/DictUtils.java | 426 ++++++++++++++++++ .../eehouse/android/xw4/DictsActivity.java | 40 +- .../org/eehouse/android/xw4/GameUtils.java | 378 +--------------- .../eehouse/android/xw4/jni/CommonPrefs.java | 8 +- .../eehouse/android/xw4/jni/CurGameInfo.java | 4 +- 9 files changed, 466 insertions(+), 408 deletions(-) create mode 100644 xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 9d207ba5b..cc3da1713 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -1170,7 +1170,7 @@ public class BoardActivity extends XWActivity { if ( 0 == m_jniGamePtr ) { String[] dictNames = GameUtils.dictNames( this, m_rowid ); - GameUtils.DictPairs pairs = GameUtils.openDicts( this, dictNames ); + DictUtils.DictPairs pairs = DictUtils.openDicts( this, dictNames ); if ( pairs.anyMissing( dictNames ) ) { showDictGoneFinish(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java index 34155b7ec..eaf5a8141 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java @@ -121,7 +121,7 @@ public class DictImportActivity extends XWActivity { private String saveDict( InputStream inputStream, String path ) { String name = basename( path ); - if ( GameUtils.saveDict( this, inputStream, name, s_useSD ) ) { + if ( DictUtils.saveDict( this, inputStream, name, s_useSD ) ) { return name; } else { return null; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java index 3f293bdc0..d36733642 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java @@ -95,7 +95,7 @@ public class DictLangCache { public static int getLangCount( Context context, int code ) { int count = 0; - String[] dicts = GameUtils.dictList( context ); + String[] dicts = DictUtils.dictList( context ); for ( String dict : dicts ) { if ( code == getDictLangCode( context, dict ) ) { ++count; @@ -107,7 +107,7 @@ public class DictLangCache { private static DictInfo[] getInfosHaveLang( Context context, int code ) { ArrayList al = new ArrayList(); - String[] dicts = GameUtils.dictList( context ); + String[] dicts = DictUtils.dictList( context ); for ( String dict : dicts ) { DictInfo info = getInfo( context, dict ); if ( code == info.langCode ) { @@ -200,7 +200,7 @@ public class DictLangCache { public static void inval( final Context context, String name, boolean added ) { - name = GameUtils.removeDictExtn( name ); + name = DictUtils.removeDictExtn( name ); s_nameToLang.remove( name ); if ( added ) { getInfo( context, name ); @@ -226,7 +226,7 @@ public class DictLangCache { public static String[] listLangs( Context context ) { - return listLangs( context, GameUtils.dictList( context ) ); + return listLangs( context, DictUtils.dictList( context ) ); } public static String[] listLangs( Context context, final String[] names ) @@ -315,8 +315,8 @@ public class DictLangCache { if ( s_nameToLang.containsKey( name ) ) { info = s_nameToLang.get( name ); } else { - byte[] dict = GameUtils.openDict( context, name ); - String path = GameUtils.getDictPath( context, name ); + byte[] dict = DictUtils.openDict( context, name ); + String path = DictUtils.getDictPath( context, name ); info = new DictInfo(); XwJNI.dict_getInfo( dict, path, JNIUtilsImpl.get(), info ); info.name = name; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java index 072268dae..418798085 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java @@ -32,7 +32,7 @@ public class DictListPreference extends XWListPreference { { super( context, attrs ); - String[] dicts = GameUtils.dictList( context ); + String[] dicts = DictUtils.dictList( context ); String[] dictEntries = new String[dicts.length]; for ( int ii = 0; ii < dicts.length; ++ii ) { dictEntries[ii] = diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java new file mode 100644 index 000000000..f8dd99050 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java @@ -0,0 +1,426 @@ +/* -*- compile-command: "cd ../../../../../; ant install"; -*- */ +/* + * Copyright 2009-2010 by Eric House (xwords@eehouse.org). All + * rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.eehouse.android.xw4; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Environment; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Arrays; +import android.content.res.AssetManager; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.Lock; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Random; +import android.text.Html; + +import junit.framework.Assert; + +import org.eehouse.android.xw4.jni.*; +import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; + +public class DictUtils { + + public enum DictLoc { BUILT_IN, INTERNAL, EXTERNAL, DOWNLOAD }; + public static final String INVITED = "invited"; + + public static class DictPairs { + public byte[][] m_bytes; + public String[] m_paths; + public DictPairs( byte[][] bytes, String[] paths ) { + m_bytes = bytes; m_paths = paths; + } + + public boolean anyMissing( final String[] names ) + { + boolean missing = false; + for ( int ii = 0; ii < m_paths.length; ++ii ) { + if ( names[ii] != null ) { + // It's ok for there to be no dict IFF there's no + // name. That's a player using the default dict. + if ( null == m_paths[ii] && null == m_bytes[ii] ) { + missing = true; + break; + } + } + } + return missing; + } + } // DictPairs + + + public static String[] dictList( Context context ) + { + ArrayList al = new ArrayList(); + + for ( String file : getAssets( context ) ) { + if ( isDict( file ) ) { + al.add( removeDictExtn( file ) ); + } + } + + for ( String file : context.fileList() ) { + if ( isDict( file ) ) { + al.add( removeDictExtn( file ) ); + } + } + + File sdDir = getSDDir( context ); + if ( null != sdDir ) { + for ( String file : sdDir.list() ) { + if ( isDict( file ) ) { + al.add( removeDictExtn( file ) ); + } + } + } + + return al.toArray( new String[al.size()] ); + } + + public static DictLoc getDictLoc( Context context, String name ) + { + DictLoc loc = null; + name = addDictExtn( name ); + + for ( String file : getAssets( context ) ) { + if ( file.equals( name ) ) { + loc = DictLoc.BUILT_IN; + break; + } + } + + if ( null == loc ) { + try { + FileInputStream fis = context.openFileInput( name ); + fis.close(); + loc = DictLoc.INTERNAL; + } catch ( java.io.FileNotFoundException fnf ) { + } catch ( java.io.IOException ioe ) { + } + } + + if ( null == loc ) { + File file = getSDPathFor( context, name ); + if ( null != file && file.exists() ) { + loc = DictLoc.EXTERNAL; + } + } + + return loc; + } + + public static boolean dictExists( Context context, String name ) + { + return null != getDictLoc( context, name ); + } + + public static boolean dictIsBuiltin( Context context, String name ) + { + return DictLoc.BUILT_IN == getDictLoc( context, name ); + } + + public static boolean moveDict( Context context, String name, + DictLoc from, DictLoc to ) + { + name = addDictExtn( name ); + boolean success = copyDict( context, name, from, to ); + if ( success ) { + deleteDict( context, name, from ); + } + return success; + } + + private static boolean copyDict( Context context, String name, + DictLoc from, DictLoc to ) + { + boolean success = false; + + File file = getSDPathFor( context, name ); + if ( null != file ) { + FileChannel channelIn = null; + FileChannel channelOut = null; + + try { + FileInputStream fis; + FileOutputStream fos; + if ( DictLoc.INTERNAL == from ) { + fis = context.openFileInput( name ); + fos = new FileOutputStream( file ); + } else { + fis = new FileInputStream( file ); + fos = context.openFileOutput( name, Context.MODE_PRIVATE ); + } + + channelIn = fis.getChannel(); + channelOut = fos.getChannel(); + + channelIn.transferTo( 0, channelIn.size(), channelOut ); + success = true; + + } catch ( java.io.FileNotFoundException fnfe ) { + Utils.logf( "%s", fnfe.toString() ); + } catch ( java.io.IOException ioe ) { + Utils.logf( "%s", ioe.toString() ); + } finally { + try { + // Order should match assignment order to above in + // case one or both null + channelIn.close(); + channelOut.close(); + } catch ( Exception e ) { + Utils.logf( "%s", e.toString() ); + } + } + } + return success; + } // copyDict + + private static void deleteDict( Context context, String name, DictLoc loc ) + { + if ( DictLoc.EXTERNAL == loc ) { + File onSD = getSDPathFor( context, name ); + if ( null != onSD ) { + onSD.delete(); + } // otherwise what? + } else { + Assert.assertTrue( DictLoc.INTERNAL == loc ); + context.deleteFile( name ); + } + } + + public static void deleteDict( Context context, String name ) + { + name = addDictExtn( name ); + deleteDict( context, name, getDictLoc( context, name ) ); + } + + public static byte[] openDict( Context context, String name ) + { + byte[] bytes = null; + + name = addDictExtn( name ); + + try { + AssetManager am = context.getAssets(); + InputStream dict = am.open( name, + android.content.res.AssetManager.ACCESS_RANDOM ); + + int len = dict.available(); // this may not be the full length! + bytes = new byte[len]; + int nRead = dict.read( bytes, 0, len ); + if ( nRead != len ) { + Utils.logf( "**** warning ****; read only %d of %d bytes.", + nRead, len ); + } + // check that with len bytes we've read the whole file + Assert.assertTrue( -1 == dict.read() ); + } catch ( java.io.IOException ee ){ + Utils.logf( "%s failed to open; likely not built-in", name ); + } + + // not an asset? Try external and internal storage + if ( null == bytes ) { + try { + File sdFile = getSDPathFor( context, name ); + FileInputStream fis; + if ( null != sdFile && sdFile.exists() ) { + Utils.logf( "loading %s from SD", name ); + fis = new FileInputStream( sdFile ); + } else { + Utils.logf( "loading %s from private storage", name ); + fis = context.openFileInput( name ); + } + int len = (int)fis.getChannel().size(); + bytes = new byte[len]; + fis.read( bytes, 0, len ); + fis.close(); + Utils.logf( "Successfully loaded %s", name ); + } catch ( java.io.FileNotFoundException fnf ) { + Utils.logf( fnf.toString() ); + } catch ( java.io.IOException ioe ) { + Utils.logf( ioe.toString() ); + } + } + + return bytes; + } + + public static String getDictPath( Context context, String name ) + { + name = addDictExtn( name ); + + File file = context.getFileStreamPath( name ); + if ( !file.exists() ) { + file = getSDPathFor( context, name ); + if ( null != file && !file.exists() ) { + file = null; + } + } + String path = null == file? null : file.getPath(); + return path; + } + + public static DictPairs openDicts( Context context, String[] names ) + { + byte[][] dictBytes = new byte[names.length][]; + String[] dictPaths = new String[names.length]; + + HashMap seen = new HashMap(); + for ( int ii = 0; ii < names.length; ++ii ) { + byte[] bytes = null; + String path = null; + String name = names[ii]; + if ( null != name ) { + path = getDictPath( context, name ); + if ( null == path ) { + bytes = seen.get( name ); + if ( null == bytes ) { + bytes = openDict( context, name ); + seen.put( name, bytes ); + } + } + } + dictBytes[ii] = bytes; + dictPaths[ii] = path; + } + return new DictPairs( dictBytes, dictPaths ); + } + + public static boolean saveDict( Context context, InputStream in, + String name, boolean useSD ) + { + boolean success = false; + File sdFile = null; + if ( useSD ) { + sdFile = getSDPathFor( context, name ); + } + + if ( null != sdFile || !useSD ) { + try { + FileOutputStream fos; + if ( null != sdFile ) { + fos = new FileOutputStream( sdFile ); + } else { + fos = context.openFileOutput( name, Context.MODE_PRIVATE ); + } + byte[] buf = new byte[1024]; + int nRead; + while( 0 <= (nRead = in.read( buf, 0, buf.length )) ) { + fos.write( buf, 0, nRead ); + } + fos.close(); + success = true; + } catch ( java.io.FileNotFoundException fnf ) { + Utils.logf( "saveDict: FileNotFoundException: %s", + fnf.toString() ); + } catch ( java.io.IOException ioe ) { + Utils.logf( "saveDict: IOException: %s", ioe.toString() ); + deleteDict( context, name ); + } + } + return success; + } + + private static boolean isGame( String file ) + { + return file.endsWith( XWConstants.GAME_EXTN ); + } + + private static boolean isDict( String file ) + { + return file.endsWith( XWConstants.DICT_EXTN ); + } + + public static String removeDictExtn( String str ) + { + if ( str.endsWith( XWConstants.DICT_EXTN ) ) { + int indx = str.lastIndexOf( XWConstants.DICT_EXTN ); + str = str.substring( 0, indx ); + } + return str; + } + + private static String addDictExtn( String str ) + { + if ( ! str.endsWith( XWConstants.DICT_EXTN ) ) { + str += XWConstants.DICT_EXTN; + } + return str; + } + + private static String[] getAssets( Context context ) + { + try { + AssetManager am = context.getAssets(); + return am.list(""); + } catch( java.io.IOException ioe ) { + Utils.logf( ioe.toString() ); + return new String[0]; + } + } + + public static boolean haveWriteableSD() + { + String state = Environment.getExternalStorageState(); + + return state.equals( Environment.MEDIA_MOUNTED ); + // want this later? Environment.MEDIA_MOUNTED_READ_ONLY + } + + private static File getSDDir( Context context ) + { + File result = null; + if ( haveWriteableSD() ) { + File storage = Environment.getExternalStorageDirectory(); + if ( null != storage ) { + String packdir = String.format( "Android/data/%s/files/", + context.getPackageName() ); + result = new File( storage.getPath(), packdir ); + if ( !result.exists() ) { + result.mkdirs(); + if ( !result.exists() ) { + Utils.logf( "unable to create sd dir %s", packdir ); + result = null; + } + } + } + } + return result; + } + + private static File getSDPathFor( Context context, String name ) + { + File result = null; + File dir = getSDDir( context ); + if ( dir != null ) { + result = new File( dir, name ); + } + return result; + } +} diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index 7475222d1..a47ba3d69 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -85,8 +85,8 @@ public class DictsActivity extends ExpandableListActivity private DictListAdapter m_adapter; private long m_packedPosition; - private GameUtils.DictLoc m_moveFromLoc; - private GameUtils.DictLoc m_moveToLoc; + private DictUtils.DictLoc m_moveFromLoc; + private DictUtils.DictLoc m_moveToLoc; private SDCardWatcher m_cardWatcher; private LayoutInflater m_factory; @@ -133,7 +133,7 @@ public class DictsActivity extends ExpandableListActivity boolean canDelete = false; if ( null != dicts && childPosition < dicts.length ) { text = dicts[childPosition]; - canDelete = !GameUtils.dictIsBuiltin( DictsActivity.this, + canDelete = !DictUtils.dictIsBuiltin( DictsActivity.this, text ); } else { text = m_download; @@ -144,8 +144,8 @@ public class DictsActivity extends ExpandableListActivity view.setDeleteCallback( DictsActivity.this ); } - GameUtils.DictLoc loc = - GameUtils.getDictLoc( DictsActivity.this, text ); + DictUtils.DictLoc loc = + DictUtils.getDictLoc( DictsActivity.this, text ); view.setComment( m_locNames[loc.ordinal()] ); view.cache( loc ); @@ -263,10 +263,10 @@ public class DictsActivity extends ExpandableListActivity lstnr = new DialogInterface.OnClickListener() { public void onClick( DialogInterface dlg, int item ) { XWListItem rowView = m_adapter.getSelChildView(); - if ( GameUtils.moveDict( DictsActivity.this, - rowView.getText(), - m_moveFromLoc, - m_moveToLoc ) ) { + if ( DictUtils.moveDict( DictsActivity.this, + rowView.getText(), + m_moveFromLoc, + m_moveToLoc ) ) { rowView. setComment( m_locNames[m_moveToLoc.ordinal()]); rowView.cache( m_moveToLoc ); @@ -408,11 +408,11 @@ public class DictsActivity extends ExpandableListActivity int tmp = savedInstanceState.getInt( MOVEFROMLOC, -1 ); if ( -1 != tmp ) { - m_moveFromLoc = GameUtils.DictLoc.values()[tmp]; + m_moveFromLoc = DictUtils.DictLoc.values()[tmp]; } tmp = savedInstanceState.getInt( MOVETOLOC, -1 ); if ( -1 != tmp ) { - m_moveToLoc = GameUtils.DictLoc.values()[tmp]; + m_moveToLoc = DictUtils.DictLoc.values()[tmp]; } } } @@ -452,9 +452,9 @@ public class DictsActivity extends ExpandableListActivity inflater.inflate( R.menu.dicts_item_menu, menu ); XWListItem row = (XWListItem)info.targetView; - GameUtils.DictLoc loc = (GameUtils.DictLoc)row.getCached(); - if ( loc == GameUtils.DictLoc.BUILT_IN - || ! GameUtils.haveWriteableSD() ) { + DictUtils.DictLoc loc = (DictUtils.DictLoc)row.getCached(); + if ( loc == DictUtils.DictLoc.BUILT_IN + || ! DictUtils.haveWriteableSD() ) { menu.removeItem( R.id.dicts_item_move ); } @@ -510,11 +510,11 @@ public class DictsActivity extends ExpandableListActivity // options for YY? private void askMoveDict( XWListItem item ) { - m_moveFromLoc = (GameUtils.DictLoc)item.getCached(); - if ( m_moveFromLoc == GameUtils.DictLoc.INTERNAL ) { - m_moveToLoc = GameUtils.DictLoc.EXTERNAL; + m_moveFromLoc = (DictUtils.DictLoc)item.getCached(); + if ( m_moveFromLoc == DictUtils.DictLoc.INTERNAL ) { + m_moveToLoc = DictUtils.DictLoc.EXTERNAL; } else { - m_moveToLoc = GameUtils.DictLoc.INTERNAL; + m_moveToLoc = DictUtils.DictLoc.INTERNAL; } showDialog( MOVE_DICT ); @@ -566,14 +566,14 @@ public class DictsActivity extends ExpandableListActivity private void deleteDict( String dict ) { - GameUtils.deleteDict( this, dict ); + DictUtils.deleteDict( this, dict ); DictLangCache.inval( this, dict, false ); mkListAdapter(); } private void askStartDownload( int lang, String name ) { - if ( GameUtils.haveWriteableSD() ) { + if ( DictUtils.haveWriteableSD() ) { m_lang = lang; m_name = name; showDialog( PICK_STORAGE ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index b3b79dd1f..abcc21946 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -47,7 +47,6 @@ import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; public class GameUtils { - public enum DictLoc { BUILT_IN, INTERNAL, EXTERNAL, DOWNLOAD }; public static final String INVITED = "invited"; // Implements read-locks and write-locks per game. A read lock is @@ -152,30 +151,6 @@ public class GameUtils { } } - public static class DictPairs { - public byte[][] m_bytes; - public String[] m_paths; - public DictPairs( byte[][] bytes, String[] paths ) { - m_bytes = bytes; m_paths = paths; - } - - public boolean anyMissing( final String[] names ) - { - boolean missing = false; - for ( int ii = 0; ii < m_paths.length; ++ii ) { - if ( names[ii] != null ) { - // It's ok for there to be no dict IFF there's no - // name. That's a player using the default dict. - if ( null == m_paths[ii] && null == m_bytes[ii] ) { - missing = true; - break; - } - } - } - return missing; - } - } // DictPairs - private static Object s_syncObj = new Object(); public static byte[] savedGame( Context context, long rowid ) @@ -205,7 +180,7 @@ public class GameUtils { // as DeviceRole.SERVER_STANDALONE != gi.serverRole int gamePtr = loadMakeGame( context, gi, lockSrc ); String[] dictNames = gi.dictNames(); - DictPairs pairs = openDicts( context, dictNames ); + DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); if ( XwJNI.game_hasComms( gamePtr ) ) { addr = new CommsAddrRec( context ); @@ -333,7 +308,7 @@ public class GameUtils { byte[] stream = savedGame( context, lock ); XwJNI.gi_from_stream( gi, stream ); String[] dictNames = gi.dictNames(); - DictPairs pairs = openDicts( context, dictNames ); + DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); if ( pairs.anyMissing( dictNames ) ) { Utils.logf( "loadMakeGame() failing: dict unavailable" ); } else { @@ -490,7 +465,7 @@ public class GameUtils { { String[] dictNames = dictNames( context, rowid, missingLang ); HashSet missingSet; - String[] installed = dictList( context ); + String[] installed = DictUtils.dictList( context ); missingSet = new HashSet( Arrays.asList( dictNames ) ); missingSet.remove( null ); @@ -532,288 +507,11 @@ public class GameUtils { // return name; } - public static String[] dictList( Context context ) - { - ArrayList al = new ArrayList(); - - for ( String file : getAssets( context ) ) { - if ( isDict( file ) ) { - al.add( removeDictExtn( file ) ); - } - } - - for ( String file : context.fileList() ) { - if ( isDict( file ) ) { - al.add( removeDictExtn( file ) ); - } - } - - File sdDir = getSDDir( context ); - if ( null != sdDir ) { - for ( String file : sdDir.list() ) { - if ( isDict( file ) ) { - al.add( removeDictExtn( file ) ); - } - } - } - - return al.toArray( new String[al.size()] ); - } - - public static DictLoc getDictLoc( Context context, String name ) - { - DictLoc loc = null; - name = addDictExtn( name ); - - for ( String file : getAssets( context ) ) { - if ( file.equals( name ) ) { - loc = DictLoc.BUILT_IN; - break; - } - } - - if ( null == loc ) { - try { - FileInputStream fis = context.openFileInput( name ); - fis.close(); - loc = DictLoc.INTERNAL; - } catch ( java.io.FileNotFoundException fnf ) { - } catch ( java.io.IOException ioe ) { - } - } - - if ( null == loc ) { - File file = getSDPathFor( context, name ); - if ( null != file && file.exists() ) { - loc = DictLoc.EXTERNAL; - } - } - - return loc; - } - - public static boolean dictExists( Context context, String name ) - { - return null != getDictLoc( context, name ); - } - - public static boolean dictIsBuiltin( Context context, String name ) - { - return DictLoc.BUILT_IN == getDictLoc( context, name ); - } - - public static boolean moveDict( Context context, String name, - DictLoc from, DictLoc to ) - { - name = addDictExtn( name ); - boolean success = copyDict( context, name, from, to ); - if ( success ) { - deleteDict( context, name, from ); - } - return success; - } - - private static boolean copyDict( Context context, String name, - DictLoc from, DictLoc to ) - { - boolean success = false; - - File file = getSDPathFor( context, name ); - if ( null != file ) { - FileChannel channelIn = null; - FileChannel channelOut = null; - - try { - FileInputStream fis; - FileOutputStream fos; - if ( DictLoc.INTERNAL == from ) { - fis = context.openFileInput( name ); - fos = new FileOutputStream( file ); - } else { - fis = new FileInputStream( file ); - fos = context.openFileOutput( name, Context.MODE_PRIVATE ); - } - - channelIn = fis.getChannel(); - channelOut = fos.getChannel(); - - channelIn.transferTo( 0, channelIn.size(), channelOut ); - success = true; - - } catch ( java.io.FileNotFoundException fnfe ) { - Utils.logf( "%s", fnfe.toString() ); - } catch ( java.io.IOException ioe ) { - Utils.logf( "%s", ioe.toString() ); - } finally { - try { - // Order should match assignment order to above in - // case one or both null - channelIn.close(); - channelOut.close(); - } catch ( Exception e ) { - Utils.logf( "%s", e.toString() ); - } - } - } - return success; - } // copyDict - - private static void deleteDict( Context context, String name, DictLoc loc ) - { - if ( DictLoc.EXTERNAL == loc ) { - File onSD = getSDPathFor( context, name ); - if ( null != onSD ) { - onSD.delete(); - } // otherwise what? - } else { - Assert.assertTrue( DictLoc.INTERNAL == loc ); - context.deleteFile( name ); - } - } - - public static void deleteDict( Context context, String name ) - { - name = addDictExtn( name ); - deleteDict( context, name, getDictLoc( context, name ) ); - } - - public static byte[] openDict( Context context, String name ) - { - byte[] bytes = null; - - name = addDictExtn( name ); - - try { - AssetManager am = context.getAssets(); - InputStream dict = am.open( name, - android.content.res.AssetManager.ACCESS_RANDOM ); - - int len = dict.available(); // this may not be the full length! - bytes = new byte[len]; - int nRead = dict.read( bytes, 0, len ); - if ( nRead != len ) { - Utils.logf( "**** warning ****; read only %d of %d bytes.", - nRead, len ); - } - // check that with len bytes we've read the whole file - Assert.assertTrue( -1 == dict.read() ); - } catch ( java.io.IOException ee ){ - Utils.logf( "%s failed to open; likely not built-in", name ); - } - - // not an asset? Try external and internal storage - if ( null == bytes ) { - try { - File sdFile = getSDPathFor( context, name ); - FileInputStream fis; - if ( null != sdFile && sdFile.exists() ) { - Utils.logf( "loading %s from SD", name ); - fis = new FileInputStream( sdFile ); - } else { - Utils.logf( "loading %s from private storage", name ); - fis = context.openFileInput( name ); - } - int len = (int)fis.getChannel().size(); - bytes = new byte[len]; - fis.read( bytes, 0, len ); - fis.close(); - Utils.logf( "Successfully loaded %s", name ); - } catch ( java.io.FileNotFoundException fnf ) { - Utils.logf( fnf.toString() ); - } catch ( java.io.IOException ioe ) { - Utils.logf( ioe.toString() ); - } - } - - return bytes; - } - - public static String getDictPath( Context context, String name ) - { - name = addDictExtn( name ); - - File file = context.getFileStreamPath( name ); - if ( !file.exists() ) { - file = getSDPathFor( context, name ); - if ( null != file && !file.exists() ) { - file = null; - } - } - String path = null == file? null : file.getPath(); - return path; - } - - public static DictPairs openDicts( Context context, String[] names ) - { - byte[][] dictBytes = new byte[names.length][]; - String[] dictPaths = new String[names.length]; - - HashMap seen = new HashMap(); - for ( int ii = 0; ii < names.length; ++ii ) { - byte[] bytes = null; - String path = null; - String name = names[ii]; - if ( null != name ) { - path = getDictPath( context, name ); - if ( null == path ) { - bytes = seen.get( name ); - if ( null == bytes ) { - bytes = openDict( context, name ); - seen.put( name, bytes ); - } - } - } - dictBytes[ii] = bytes; - dictPaths[ii] = path; - } - return new DictPairs( dictBytes, dictPaths ); - } - - public static boolean saveDict( Context context, InputStream in, - String name, boolean useSD ) - { - boolean success = false; - File sdFile = null; - if ( useSD ) { - sdFile = getSDPathFor( context, name ); - } - - if ( null != sdFile || !useSD ) { - try { - FileOutputStream fos; - if ( null != sdFile ) { - fos = new FileOutputStream( sdFile ); - } else { - fos = context.openFileOutput( name, Context.MODE_PRIVATE ); - } - byte[] buf = new byte[1024]; - int nRead; - while( 0 <= (nRead = in.read( buf, 0, buf.length )) ) { - fos.write( buf, 0, nRead ); - } - fos.close(); - success = true; - } catch ( java.io.FileNotFoundException fnf ) { - Utils.logf( "saveDict: FileNotFoundException: %s", - fnf.toString() ); - } catch ( java.io.IOException ioe ) { - Utils.logf( "saveDict: IOException: %s", ioe.toString() ); - deleteDict( context, name ); - } - } - return success; - } - private static boolean isGame( String file ) { return file.endsWith( XWConstants.GAME_EXTN ); } - private static boolean isDict( String file ) - { - return file.endsWith( XWConstants.DICT_EXTN ); - } - public static void launchGame( Activity activity, long rowid, boolean invited ) { @@ -925,7 +623,7 @@ public class GameUtils { gi.replaceDicts( newDict ); String[] dictNames = gi.dictNames(); - DictPairs pairs = openDicts( context, dictNames ); + DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); int gamePtr = XwJNI.initJNI(); XwJNI.game_makeFromStream( gamePtr, stream, JNIUtilsImpl.get(), gi, @@ -950,7 +648,7 @@ public class GameUtils { // that don't reset the game, e.g. player name for standalone // games? String[] dictNames = gi.dictNames(); - DictPairs pairs = openDicts( context, dictNames ); + DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); String langName = gi.langName(); int gamePtr = XwJNI.initJNI(); boolean madeGame = false; @@ -996,34 +694,6 @@ public class GameUtils { activity.startActivity( intent ); } - public static String removeDictExtn( String str ) - { - if ( str.endsWith( XWConstants.DICT_EXTN ) ) { - int indx = str.lastIndexOf( XWConstants.DICT_EXTN ); - str = str.substring( 0, indx ); - } - return str; - } - - private static String addDictExtn( String str ) - { - if ( ! str.endsWith( XWConstants.DICT_EXTN ) ) { - str += XWConstants.DICT_EXTN; - } - return str; - } - - private static String[] getAssets( Context context ) - { - try { - AssetManager am = context.getAssets(); - return am.list(""); - } catch( java.io.IOException ioe ) { - Utils.logf( ioe.toString() ); - return new String[0]; - } - } - private static void tellRelayDied( Context context, GameLock lock, boolean informNow ) { @@ -1036,43 +706,5 @@ public class GameUtils { } } - public static boolean haveWriteableSD() - { - String state = Environment.getExternalStorageState(); - - return state.equals( Environment.MEDIA_MOUNTED ); - // want this later? Environment.MEDIA_MOUNTED_READ_ONLY - } - - private static File getSDDir( Context context ) - { - File result = null; - if ( haveWriteableSD() ) { - File storage = Environment.getExternalStorageDirectory(); - if ( null != storage ) { - String packdir = String.format( "Android/data/%s/files/", - context.getPackageName() ); - result = new File( storage.getPath(), packdir ); - if ( !result.exists() ) { - result.mkdirs(); - if ( !result.exists() ) { - Utils.logf( "unable to create sd dir %s", packdir ); - result = null; - } - } - } - } - return result; - } - - private static File getSDPathFor( Context context, String name ) - { - File result = null; - File dir = getSDDir( context ); - if ( dir != null ) { - result = new File( dir, name ); - } - return result; - } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java index e5c972cef..6e2bcc22e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java @@ -29,7 +29,7 @@ import junit.framework.Assert; import org.eehouse.android.xw4.Utils; import org.eehouse.android.xw4.R; -import org.eehouse.android.xw4.GameUtils; +import org.eehouse.android.xw4.DictUtils; public class CommonPrefs { public static final int COLOR_TILE_BACK = 0; @@ -211,8 +211,8 @@ public class CommonPrefs { public static String getDefaultHumanDict( Context context ) { String value = getString( context, R.string.key_default_dict ); - if ( value.equals("") || !GameUtils.dictExists( context, value ) ) { - value = GameUtils.dictList( context )[0]; + if ( value.equals("") || !DictUtils.dictExists( context, value ) ) { + value = DictUtils.dictList( context )[0]; } return value; } @@ -220,7 +220,7 @@ public class CommonPrefs { public static String getDefaultRobotDict( Context context ) { String value = getString( context, R.string.key_default_robodict ); - if ( value.equals("") || !GameUtils.dictExists( context, value ) ) { + if ( value.equals("") || !DictUtils.dictExists( context, value ) ) { value = getDefaultHumanDict( context ); } return value; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java index 19f5f1e9b..b49dc41c9 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java @@ -27,7 +27,7 @@ import java.util.Arrays; import junit.framework.Assert; import org.eehouse.android.xw4.Utils; -import org.eehouse.android.xw4.GameUtils; +import org.eehouse.android.xw4.DictUtils; import org.eehouse.android.xw4.R; import org.eehouse.android.xw4.DictLangCache; @@ -403,7 +403,7 @@ public class CurGameInfo { DictLangCache.getBestDefault( m_context, dictLang, false ); if ( null == dictName - || ! GameUtils.dictExists( m_context, dictName ) + || ! DictUtils.dictExists( m_context, dictName ) || dictLang != DictLangCache.getDictLangCode( m_context, dictName ) ) { dictName = humanDict; From 2aa544730157254d45657af8fde7abb6d9db36f1 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Thu, 8 Sep 2011 19:03:08 -0700 Subject: [PATCH 2/4] remove some logging --- xwords4/android/XWords4/jni/anddict.c | 1 - 1 file changed, 1 deletion(-) diff --git a/xwords4/android/XWords4/jni/anddict.c b/xwords4/android/XWords4/jni/anddict.c index 4b93fc10c..84ce56000 100644 --- a/xwords4/android/XWords4/jni/anddict.c +++ b/xwords4/android/XWords4/jni/anddict.c @@ -513,7 +513,6 @@ makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, jstring jname, /* copy the name */ anddict->super.name = getStringCopy( MPPARM(mpool) env, jname ); - XP_LOGF( "%s: setting dict name: %s", __func__, anddict->super.name ); anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname ); return (DictionaryCtxt*)anddict; From 528db479b5c58e8385364148b99e458723f5a3e2 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Thu, 8 Sep 2011 19:10:06 -0700 Subject: [PATCH 3/4] bunch of changes whose original purpose was to allow me to deal correctly with having the same dict stored in more than one place. Added DictAndLoc with members name and loc to support this, and used in a bunch of places in place of mere strings, including DictsActivity. Also removed code warning when you're deleting a dict that's not the last in its lang, which incorrectly warned that the dict was in use when in fact I can't tell that. The warning "might be in use" sounds dumb so it's gone for now. --- .../XWords4/res/values/common_rsrc.xml | 2 + .../android/XWords4/res/values/strings.xml | 6 +- .../src/org/eehouse/android/xw4/DBUtils.java | 2 +- .../android/xw4/DictImportActivity.java | 10 +- .../eehouse/android/xw4/DictLangCache.java | 100 +++++++--- .../android/xw4/DictListPreference.java | 12 +- .../org/eehouse/android/xw4/DictUtils.java | 173 ++++++++++++------ .../eehouse/android/xw4/DictsActivity.java | 86 ++++----- .../org/eehouse/android/xw4/GameConfig.java | 4 +- .../org/eehouse/android/xw4/GameUtils.java | 6 +- .../org/eehouse/android/xw4/XWListItem.java | 4 +- .../eehouse/android/xw4/jni/CommonPrefs.java | 2 +- 12 files changed, 266 insertions(+), 141 deletions(-) diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml index dbf89e1b8..f2a274d72 100644 --- a/xwords4/android/XWords4/res/values/common_rsrc.xml +++ b/xwords4/android/XWords4/res/values/common_rsrc.xml @@ -125,7 +125,9 @@ @string/connect_daily + + @string/loc_builtin @string/loc_internal @string/loc_external diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index b76829a9b..dc3d9e51a 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -215,9 +215,9 @@ \u0020It is the only %s wordlist installed. One or more games will be unopenable without it. - \u0020One game (at least) - is using it (but there is another %s wordlist installed that - can replace it.) + + + Allow hints Enable the hint feature diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 55c31a787..af6736c28 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -253,7 +253,7 @@ public class DBUtils { } } // saveSummary - public static int countGamesUsing( Context context, int lang ) + public static int countGamesUsingLang( Context context, int lang ) { int result = 0; initDB( context ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java index eaf5a8141..a11ff4090 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java @@ -36,11 +36,12 @@ import junit.framework.Assert; public class DictImportActivity extends XWActivity { - private static boolean s_useSD = false; + private static DictUtils.DictLoc s_saveWhere = DictUtils.DictLoc.INTERNAL; public static void setUseSD( boolean useSD ) { - s_useSD = useSD; + s_saveWhere = useSD ? + DictUtils.DictLoc.EXTERNAL : DictUtils.DictLoc.INTERNAL; } private class DownloadFilesTask extends AsyncTask { @@ -80,7 +81,8 @@ public class DictImportActivity extends XWActivity { { Utils.logf( "onPostExecute passed %d", result ); if ( null != m_saved ) { - DictLangCache.inval( DictImportActivity.this, m_saved, true ); + DictLangCache.inval( DictImportActivity.this, m_saved, + s_saveWhere, true ); } finish(); } @@ -121,7 +123,7 @@ public class DictImportActivity extends XWActivity { private String saveDict( InputStream inputStream, String path ) { String name = basename( path ); - if ( DictUtils.saveDict( this, inputStream, name, s_useSD ) ) { + if ( DictUtils.saveDict( this, inputStream, name, s_saveWhere ) ) { return name; } else { return null; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java index d36733642..b62ecef96 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java @@ -27,17 +27,19 @@ import android.widget.ArrayAdapter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Set; import java.util.Arrays; import java.util.Comparator; +import org.eehouse.android.xw4.DictUtils.DictAndLoc; import org.eehouse.android.xw4.jni.JNIUtilsImpl; import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.DictInfo; import org.eehouse.android.xw4.jni.CommonPrefs; public class DictLangCache { - private static final HashMap s_nameToLang = - new HashMap(); + private static final HashMap s_nameToLang = + new HashMap(); private static String[] s_langNames; private static int m_adaptedLang = -1; @@ -59,17 +61,18 @@ public class DictLangCache { } }; - public static String annotatedDictName( Context context, String name ) + public static String annotatedDictName( Context context, DictAndLoc dal ) { - DictInfo info = getInfo( context, name ); + DictInfo info = getInfo( context, dal ); int wordCount = info.wordCount; - String langName = getLangName( context, name ); + String langName = getLangName( context, dal.name ); String result; if ( 0 == wordCount ) { - result = String.format( "%s (%s)", name, langName ); + result = String.format( "%s (%s)", dal.name, langName ); } else { - result = String.format( "%s (%s/%d)", name, langName, wordCount ); + result = String.format( "%s (%s/%d)", dal.name, langName, + wordCount ); } return result; @@ -95,9 +98,9 @@ public class DictLangCache { public static int getLangCount( Context context, int code ) { int count = 0; - String[] dicts = DictUtils.dictList( context ); - for ( String dict : dicts ) { - if ( code == getDictLangCode( context, dict ) ) { + DictAndLoc[] dals = DictUtils.dictList( context ); + for ( DictAndLoc dal : dals ) { + if ( code == getDictLangCode( context, dal ) ) { ++count; } } @@ -107,9 +110,9 @@ public class DictLangCache { private static DictInfo[] getInfosHaveLang( Context context, int code ) { ArrayList al = new ArrayList(); - String[] dicts = DictUtils.dictList( context ); - for ( String dict : dicts ) { - DictInfo info = getInfo( context, dict ); + DictAndLoc[] dals = DictUtils.dictList( context ); + for ( DictAndLoc dal : dals ) { + DictInfo info = getInfo( context, dal ); if ( code == info.langCode ) { al.add( info ); } @@ -149,6 +152,20 @@ public class DictLangCache { return getHaveLang( context, code, null, false ); } + public static DictAndLoc[] getDALsHaveLang( Context context, int code ) + { + ArrayList al = new ArrayList(); + DictAndLoc[] dals = DictUtils.dictList( context ); + for ( DictAndLoc dal : dals ) { + DictInfo info = getInfo( context, dal ); + if ( code == info.langCode ) { + al.add( dal ); + } + } + DictAndLoc[] result = al.toArray( new DictAndLoc[al.size()] ); + return result; + } + private static Comparator s_ByCount = new Comparator() { public int compare( DictInfo di1, DictInfo di2 ) @@ -173,6 +190,11 @@ public class DictLangCache { return nameWithCount.substring( 0, indx ); } + public static int getDictLangCode( Context context, DictAndLoc dal ) + { + return getInfo( context, dal ).langCode; + } + public static int getDictLangCode( Context context, String dict ) { return getInfo( context, dict ).langCode; @@ -197,13 +219,13 @@ public class DictLangCache { return getLangName( context, code ); } - public static void inval( final Context context, String name, - boolean added ) + public static void inval( final Context context, String name, + DictUtils.DictLoc loc, boolean added ) { - name = DictUtils.removeDictExtn( name ); - s_nameToLang.remove( name ); + DictAndLoc dal = new DictAndLoc( name, loc ); + s_nameToLang.remove( dal ); if ( added ) { - getInfo( context, name ); + getInfo( context, dal ); } if ( null != s_handler ) { @@ -229,11 +251,11 @@ public class DictLangCache { return listLangs( context, DictUtils.dictList( context ) ); } - public static String[] listLangs( Context context, final String[] names ) + public static String[] listLangs( Context context, DictAndLoc[] dals ) { HashSet langs = new HashSet(); - for ( String name:names ) { - langs.add( getLangName( context, name ) ); + for ( DictAndLoc dal : dals ) { + langs.add( getLangName( context, dal.name ) ); } String[] result = new String[langs.size()]; return langs.toArray( result ); @@ -310,17 +332,39 @@ public class DictLangCache { } private static DictInfo getInfo( Context context, String name ) + { + DictInfo result = null; + Set keys = s_nameToLang.keySet(); + for ( DictAndLoc key : keys ) { + if ( key.name.equals(name) ) { + result = s_nameToLang.get( key ); + break; + } + } + + if ( null == result ) { + DictUtils.DictLoc loc = DictUtils.getDictLoc( context, name ); + result = getInfo( context, new DictAndLoc( name, loc ) ); + } + return result; + } + + private static DictInfo getInfo( Context context, DictAndLoc dal ) { DictInfo info; - if ( s_nameToLang.containsKey( name ) ) { - info = s_nameToLang.get( name ); + if ( s_nameToLang.containsKey( dal ) ) { + info = s_nameToLang.get( dal ); } else { - byte[] dict = DictUtils.openDict( context, name ); - String path = DictUtils.getDictPath( context, name ); + String[] names = { dal.name }; + DictUtils.DictPairs pairs = DictUtils.openDicts( context, names ); + info = new DictInfo(); - XwJNI.dict_getInfo( dict, path, JNIUtilsImpl.get(), info ); - info.name = name; - s_nameToLang.put( name, info ); + + XwJNI.dict_getInfo( pairs.m_bytes[0], pairs.m_paths[0], + JNIUtilsImpl.get(), info ); + + info.name = dal.name; + s_nameToLang.put( dal, info ); } return info; } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java index 418798085..572bb1962 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictListPreference.java @@ -32,13 +32,15 @@ public class DictListPreference extends XWListPreference { { super( context, attrs ); - String[] dicts = DictUtils.dictList( context ); - String[] dictEntries = new String[dicts.length]; - for ( int ii = 0; ii < dicts.length; ++ii ) { + DictUtils.DictAndLoc[] dals = DictUtils.dictList( context ); + String[] dictEntries = new String[dals.length]; + String[] values = new String[dals.length]; + for ( int ii = 0; ii < dals.length; ++ii ) { + values[ii] = dals[ii].name; dictEntries[ii] = - DictLangCache.annotatedDictName( context, dicts[ii] ); + DictLangCache.annotatedDictName( context, dals[ii] ); } setEntries( dictEntries ); - setEntryValues( dicts ); + setEntryValues( values ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java index f8dd99050..683ef0efb 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java @@ -47,8 +47,11 @@ import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; public class DictUtils { - public enum DictLoc { BUILT_IN, INTERNAL, EXTERNAL, DOWNLOAD }; + // keep in sync with loc_names string-array + public enum DictLoc { UNKNOWN, BUILT_IN, INTERNAL, EXTERNAL, DOWNLOAD }; public static final String INVITED = "invited"; + + private static DictAndLoc[] s_dictListCache = null; public static class DictPairs { public byte[][] m_bytes; @@ -74,33 +77,60 @@ public class DictUtils { } } // DictPairs + public static class DictAndLoc { + public DictAndLoc( String pname, DictLoc ploc ) { + name = removeDictExtn(pname); loc = ploc; + } + public String name; + public DictLoc loc; + + @Override + public boolean equals( Object obj ) + { + boolean result = false; + if ( obj instanceof DictAndLoc ) { + DictAndLoc other = (DictAndLoc)obj; + + result = name.equals( other.name ) + && loc.equals( other.loc ); + } + return result; + } + } - public static String[] dictList( Context context ) + public static DictAndLoc[] dictList( Context context ) { - ArrayList al = new ArrayList(); + if ( null == s_dictListCache ) { + ArrayList al = new ArrayList(); - for ( String file : getAssets( context ) ) { - if ( isDict( file ) ) { - al.add( removeDictExtn( file ) ); - } - } - - for ( String file : context.fileList() ) { - if ( isDict( file ) ) { - al.add( removeDictExtn( file ) ); - } - } - - File sdDir = getSDDir( context ); - if ( null != sdDir ) { - for ( String file : sdDir.list() ) { + for ( String file : getAssets( context ) ) { if ( isDict( file ) ) { - al.add( removeDictExtn( file ) ); + al.add( new DictAndLoc( removeDictExtn( file ), + DictLoc.BUILT_IN ) ); } } - } - return al.toArray( new String[al.size()] ); + for ( String file : context.fileList() ) { + if ( isDict( file ) ) { + al.add( new DictAndLoc( removeDictExtn( file ), + DictLoc.INTERNAL ) ); + } + } + + File sdDir = getSDDir( context ); + if ( null != sdDir ) { + for ( String file : sdDir.list() ) { + if ( isDict( file ) ) { + al.add( new DictAndLoc( removeDictExtn( file ), + DictLoc.EXTERNAL ) ); + } + } + } + + s_dictListCache = + al.toArray( new DictUtils.DictAndLoc[al.size()] ); + } + return s_dictListCache; } public static DictLoc getDictLoc( Context context, String name ) @@ -148,10 +178,18 @@ public class DictUtils { public static boolean moveDict( Context context, String name, DictLoc from, DictLoc to ) { + boolean success; name = addDictExtn( name ); - boolean success = copyDict( context, name, from, to ); - if ( success ) { - deleteDict( context, name, from ); + + File toPath = getDictFile( context, name, to ); + if ( null != toPath && toPath.exists() ) { + success = false; + } else { + success = copyDict( context, name, from, to ); + if ( success ) { + deleteDict( context, name, from ); + s_dictListCache = null; + } } return success; } @@ -201,8 +239,9 @@ public class DictUtils { return success; } // copyDict - private static void deleteDict( Context context, String name, DictLoc loc ) + public static void deleteDict( Context context, String name, DictLoc loc ) { + name = addDictExtn( name ); if ( DictLoc.EXTERNAL == loc ) { File onSD = getSDPathFor( context, name ); if ( null != onSD ) { @@ -212,49 +251,57 @@ public class DictUtils { Assert.assertTrue( DictLoc.INTERNAL == loc ); context.deleteFile( name ); } + s_dictListCache = null; } public static void deleteDict( Context context, String name ) { - name = addDictExtn( name ); deleteDict( context, name, getDictLoc( context, name ) ); } - public static byte[] openDict( Context context, String name ) + private static byte[] openDict( Context context, String name, DictLoc loc ) { byte[] bytes = null; name = addDictExtn( name ); - try { - AssetManager am = context.getAssets(); - InputStream dict = am.open( name, - android.content.res.AssetManager.ACCESS_RANDOM ); + if ( loc == DictLoc.UNKNOWN || loc == DictLoc.BUILT_IN ) { + try { + AssetManager am = context.getAssets(); + InputStream dict = am.open( name, android.content.res. + AssetManager.ACCESS_RANDOM ); - int len = dict.available(); // this may not be the full length! - bytes = new byte[len]; - int nRead = dict.read( bytes, 0, len ); - if ( nRead != len ) { - Utils.logf( "**** warning ****; read only %d of %d bytes.", - nRead, len ); + int len = dict.available(); // this may not be the + // full length! + bytes = new byte[len]; + int nRead = dict.read( bytes, 0, len ); + if ( nRead != len ) { + Utils.logf( "**** warning ****; read only %d of %d bytes.", + nRead, len ); + } + // check that with len bytes we've read the whole file + Assert.assertTrue( -1 == dict.read() ); + } catch ( java.io.IOException ee ){ + Utils.logf( "%s failed to open; likely not built-in", name ); } - // check that with len bytes we've read the whole file - Assert.assertTrue( -1 == dict.read() ); - } catch ( java.io.IOException ee ){ - Utils.logf( "%s failed to open; likely not built-in", name ); } // not an asset? Try external and internal storage if ( null == bytes ) { try { - File sdFile = getSDPathFor( context, name ); - FileInputStream fis; - if ( null != sdFile && sdFile.exists() ) { - Utils.logf( "loading %s from SD", name ); - fis = new FileInputStream( sdFile ); - } else { - Utils.logf( "loading %s from private storage", name ); - fis = context.openFileInput( name ); + FileInputStream fis = null; + if ( loc == DictLoc.UNKNOWN || loc == DictLoc.EXTERNAL ) { + File sdFile = getSDPathFor( context, name ); + if ( null != sdFile && sdFile.exists() ) { + Utils.logf( "loading %s from SD", name ); + fis = new FileInputStream( sdFile ); + } + } + if ( null == fis ) { + if ( loc == DictLoc.UNKNOWN || loc == DictLoc.INTERNAL ) { + Utils.logf( "loading %s from private storage", name ); + fis = context.openFileInput( name ); + } } int len = (int)fis.getChannel().size(); bytes = new byte[len]; @@ -269,9 +316,14 @@ public class DictUtils { } return bytes; + } // openDict + + private static byte[] openDict( Context context, String name ) + { + return openDict( context, name, DictLoc.UNKNOWN ); } - public static String getDictPath( Context context, String name ) + private static String getDictPath( Context context, String name ) { name = addDictExtn( name ); @@ -286,6 +338,23 @@ public class DictUtils { return path; } + private static File getDictFile( Context context, String name, DictLoc to ) + { + File path; + switch ( to ) { + case EXTERNAL: + path = getSDPathFor( context, name ); + break; + case INTERNAL: + path = context.getFileStreamPath( name ); + break; + default: + Assert.fail(); + path = null; + } + return path; + } + public static DictPairs openDicts( Context context, String[] names ) { byte[][] dictBytes = new byte[names.length][]; @@ -313,10 +382,11 @@ public class DictUtils { } public static boolean saveDict( Context context, InputStream in, - String name, boolean useSD ) + String name, DictLoc loc ) { boolean success = false; File sdFile = null; + boolean useSD = DictLoc.EXTERNAL == loc; if ( useSD ) { sdFile = getSDPathFor( context, name ); } @@ -335,6 +405,7 @@ public class DictUtils { fos.write( buf, 0, nRead ); } fos.close(); + s_dictListCache = null; success = true; } catch ( java.io.FileNotFoundException fnf ) { Utils.logf( "saveDict: FileNotFoundException: %s", diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index a47ba3d69..41e26a24e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -50,6 +50,7 @@ import android.preference.PreferenceManager; import android.net.Uri; import junit.framework.Assert; +import org.eehouse.android.xw4.DictUtils.DictAndLoc; import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.JNIUtilsImpl; import org.eehouse.android.xw4.jni.CommonPrefs; @@ -96,13 +97,12 @@ public class DictsActivity extends ExpandableListActivity private XWListItem[][] m_cache; public DictListAdapter( Context context ) { - //super( context, m_dicts.length ); m_context = context; } public boolean areAllItemsEnabled() { return false; } - public Object getChild(int groupPosition, int childPosition) + public Object getChild( int groupPosition, int childPosition ) { return null; } @@ -127,27 +127,27 @@ public class DictsActivity extends ExpandableListActivity } if ( null == view ) { - int lang = (int)getGroupId( groupPosition ); - String[] dicts = DictLangCache.getHaveLang( m_context, lang ); - String text; - boolean canDelete = false; - if ( null != dicts && childPosition < dicts.length ) { - text = dicts[childPosition]; - canDelete = !DictUtils.dictIsBuiltin( DictsActivity.this, - text ); - } else { - text = m_download; - } - view = (XWListItem)m_factory.inflate( R.layout.list_item, null ); - view.setText( text ); - if ( canDelete ) { - view.setDeleteCallback( DictsActivity.this ); - } + view = (XWListItem) + m_factory.inflate( R.layout.list_item, null ); - DictUtils.DictLoc loc = - DictUtils.getDictLoc( DictsActivity.this, text ); - view.setComment( m_locNames[loc.ordinal()] ); - view.cache( loc ); + int lang = (int)getGroupId( groupPosition ); + DictAndLoc[] dals = DictLangCache.getDALsHaveLang( m_context, + lang ); + + if ( null != dals && childPosition < dals.length ) { + DictAndLoc dal; + dal = dals[childPosition]; + view.setText( dal.name ); + + DictUtils.DictLoc loc = dal.loc; + view.setComment( m_locNames[loc.ordinal()] ); + view.cache( loc ); + if ( DictUtils.DictLoc.BUILT_IN != loc ) { + view.setDeleteCallback( DictsActivity.this ); + } + } else { + view.setText( m_download ); + } addToCache( groupPosition, childPosition, view ); } @@ -157,10 +157,10 @@ public class DictsActivity extends ExpandableListActivity public int getChildrenCount( int groupPosition ) { int lang = (int)getGroupId( groupPosition ); - String[] dicts = DictLangCache.getHaveLang( m_context, lang ); + DictAndLoc[] dals = DictLangCache.getDALsHaveLang( m_context, lang ); int result = 0; // 1; // 1 for the download option - if ( null != dicts ) { - result += dicts.length; + if ( null != dals ) { + result += dals.length; } return result; } @@ -263,10 +263,10 @@ public class DictsActivity extends ExpandableListActivity lstnr = new DialogInterface.OnClickListener() { public void onClick( DialogInterface dlg, int item ) { XWListItem rowView = m_adapter.getSelChildView(); - if ( DictUtils.moveDict( DictsActivity.this, - rowView.getText(), - m_moveFromLoc, - m_moveToLoc ) ) { + if ( DictUtils.moveDict( DictsActivity.this, + rowView.getText(), + m_moveFromLoc, + m_moveToLoc ) ) { rowView. setComment( m_locNames[m_moveToLoc.ordinal()]); rowView.cache( m_moveToLoc ); @@ -521,23 +521,25 @@ public class DictsActivity extends ExpandableListActivity } // XWListItem.DeleteCallback interface - public void deleteCalled( int myPosition, String dict ) + public void deleteCalled( XWListItem item ) { + String dict = item.getText(); int code = DictLangCache.getDictLangCode( this, dict ); - String lang = DictLangCache.getLangName( this, code ); - int nGames = DBUtils.countGamesUsing( this, code ); + int nGames = DBUtils.countGamesUsingLang( this, code ); String msg = String.format( getString( R.string.confirm_delete_dictf ), dict ); + m_deleteDict = dict; + m_moveFromLoc = (DictUtils.DictLoc)item.getCached(); if ( nGames > 0 ) { - int fmt; - if ( 1 == DictLangCache.getHaveLang( this, code ).length ) { - fmt = R.string.confirm_deleteonly_dictf; - } else { - fmt = R.string.confirm_deletemore_dictf; + DictAndLoc[] dal = DictLangCache.getDALsHaveLang( this, code ); + if ( 1 == dal.length ) { + Assert.assertTrue( dict.equals(dal[0].name) ); + String fmt = getString( R.string.confirm_deleteonly_dictf ); + msg += String.format( fmt, DictLangCache. + getLangName( this, code ) ); } - msg += String.format( getString(fmt), lang ); } m_delegate.showConfirmThen( msg, DELETE_DICT_ACTION ); @@ -556,7 +558,7 @@ public class DictsActivity extends ExpandableListActivity switch( id ) { case DELETE_DICT_ACTION: if ( DialogInterface.BUTTON_POSITIVE == which ) { - deleteDict( m_deleteDict ); + deleteDict( m_deleteDict, m_moveFromLoc ); } break; default: @@ -564,10 +566,10 @@ public class DictsActivity extends ExpandableListActivity } } - private void deleteDict( String dict ) + private void deleteDict( String dict, DictUtils.DictLoc loc ) { - DictUtils.deleteDict( this, dict ); - DictLangCache.inval( this, dict, false ); + DictUtils.deleteDict( this, dict, loc ); + DictLangCache.inval( this, dict, loc, false ); mkListAdapter(); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java index d65bf9e17..49e161dfc 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java @@ -533,9 +533,9 @@ public class GameConfig extends XWActivity } // DeleteCallback interface - public void deleteCalled( int myPosition, final String name ) + public void deleteCalled( XWListItem item ) { - if ( m_gi.delete( myPosition ) ) { + if ( m_gi.delete( item.getPosition() ) ) { loadPlayersList(); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index abcc21946..ebe284fc5 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -465,11 +465,13 @@ public class GameUtils { { String[] dictNames = dictNames( context, rowid, missingLang ); HashSet missingSet; - String[] installed = DictUtils.dictList( context ); + DictUtils.DictAndLoc[] installed = DictUtils.dictList( context ); missingSet = new HashSet( Arrays.asList( dictNames ) ); missingSet.remove( null ); - missingSet.removeAll( Arrays.asList(installed) ); + for ( DictUtils.DictAndLoc dal : installed ) { + missingSet.remove( dal.name ); + } boolean allHere = 0 == missingSet.size(); if ( null != missingNames ) { missingNames[0] = diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java index 95d4ccf1b..ec4e3a8ce 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java @@ -35,7 +35,7 @@ public class XWListItem extends LinearLayout { DeleteCallback m_cb; public interface DeleteCallback { - void deleteCalled( int myPosition, String name ); + void deleteCalled( XWListItem item ); } public XWListItem( Context cx, AttributeSet as ) { @@ -72,7 +72,7 @@ public class XWListItem extends LinearLayout { button.setOnClickListener( new View.OnClickListener() { @Override public void onClick( View view ) { - m_cb.deleteCalled( m_position, getText() ); + m_cb.deleteCalled( XWListItem.this ); } } ); button.setVisibility( View.VISIBLE ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java index 6e2bcc22e..db5b0cdda 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommonPrefs.java @@ -212,7 +212,7 @@ public class CommonPrefs { { String value = getString( context, R.string.key_default_dict ); if ( value.equals("") || !DictUtils.dictExists( context, value ) ) { - value = DictUtils.dictList( context )[0]; + value = DictUtils.dictList( context )[0].name; } return value; } From fda3207b783ce1e27c5168a401583e54a5876202 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Thu, 8 Sep 2011 21:36:15 -0700 Subject: [PATCH 4/4] fix inability to tell whether a dict being deleted is in use: add to summaries DB list of dicts in the game and a method that queries that. Pick delete-confirmation message based on the language of dict, whether there are others in the same language, and whether games are using that language or that dict. --- .../android/XWords4/res/values/strings.xml | 6 ++-- .../src/org/eehouse/android/xw4/DBHelper.java | 7 +++- .../src/org/eehouse/android/xw4/DBUtils.java | 25 ++++++++++++++ .../eehouse/android/xw4/DictLangCache.java | 13 +++++++ .../eehouse/android/xw4/DictsActivity.java | 34 ++++++++++++++----- .../eehouse/android/xw4/jni/GameSummary.java | 10 ++++++ 6 files changed, 83 insertions(+), 12 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index dc3d9e51a..517f9c336 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -215,9 +215,9 @@ \u0020It is the only %s wordlist installed. One or more games will be unopenable without it. - - - + \u0020One game (at least) + is using it, but there is another %s wordlist installed that can + replace it. Allow hints Enable the hint feature diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java index f6fbbee82..7e835f829 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java @@ -29,7 +29,7 @@ public class DBHelper extends SQLiteOpenHelper { public static final String TABLE_NAME_SUM = "summaries"; public static final String TABLE_NAME_OBITS = "obits"; private static final String DB_NAME = "xwdb"; - private static final int DB_VERSION = 9; + private static final int DB_VERSION = 10; public static final String GAME_NAME = "GAME_NAME"; public static final String NUM_MOVES = "NUM_MOVES"; @@ -48,6 +48,7 @@ public class DBHelper extends SQLiteOpenHelper { // format public static final String GAMEID = "GAMEID"; public static final String DICTLANG = "DICTLANG"; + public static final String DICTLIST = "DICTLIST"; public static final String HASMSGS = "HASMSGS"; public static final String CONTRACTED = "CONTRACTED"; public static final String SNAPSHOT = "SNAPSHOT"; @@ -87,6 +88,7 @@ public class DBHelper extends SQLiteOpenHelper { + RELAYID + " TEXT," + SEED + " INTEGER," + DICTLANG + " INTEGER," + + DICTLIST + " TEXT," + SMSPHONE + " TEXT," + SCORES + " TEXT," @@ -142,6 +144,9 @@ public class DBHelper extends SQLiteOpenHelper { db.execSQL( "ALTER TABLE " + TABLE_NAME_SUM + " ADD COLUMN " + CONTRACTED + " INTEGER;" ); case 9: + db.execSQL( "ALTER TABLE " + TABLE_NAME_SUM + + " ADD COLUMN " + DICTLIST + " TEXT;" ); + case 10: // nothing yet break; default: diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index af6736c28..b56bb3704 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -38,6 +38,8 @@ import org.eehouse.android.xw4.jni.*; public class DBUtils { + private static final String DICTS_SEP = ","; + private static final String ROW_ID = "rowid"; private static final String ROW_ID_FMT = "rowid=%d"; @@ -226,6 +228,7 @@ public class DBUtils { summary.summarizePlayers() ); values.put( DBHelper.DICTLANG, summary.dictLang ); values.put( DBHelper.GAME_OVER, summary.gameOver ); + values.put( DBHelper.DICTLIST, summary.dictNames(DICTS_SEP) ); if ( null != summary.scores ) { StringBuffer sb = new StringBuffer(); @@ -273,6 +276,28 @@ public class DBUtils { return result; } + public static int countGamesUsingDict( Context context, String dict ) + { + int result = 0; + initDB( context ); + synchronized( s_dbHelper ) { + SQLiteDatabase db = s_dbHelper.getReadableDatabase(); + String pattern = String.format( "%%%s%s%s%%", + DICTS_SEP, dict, DICTS_SEP ); + String selection = String.format( "%s LIKE '%s'", + DBHelper.DICTLIST, pattern ); + // null for columns will return whole rows: bad. But + // might as well make it an int for speed + String[] columns = { DBHelper.DICTLANG }; + Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, + selection, null, null, null, null ); + result = cursor.getCount(); + cursor.close(); + db.close(); + } + return result; + } + private static void setInt( long rowid, String column, int value ) { synchronized( s_dbHelper ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java index b62ecef96..1a29eac2e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.Set; import java.util.Arrays; import java.util.Comparator; +import junit.framework.Assert; import org.eehouse.android.xw4.DictUtils.DictAndLoc; import org.eehouse.android.xw4.jni.JNIUtilsImpl; @@ -331,6 +332,18 @@ public class DictLangCache { return s_langNames; } + public static int getDictCount( Context context, String name ) + { + int result = 0; + for ( DictAndLoc dal : DictUtils.dictList( context ) ) { + if ( name.equals( dal.name ) ) { + ++result; + } + } + Assert.assertTrue( result > 0 ); + return result; + } + private static DictInfo getInfo( Context context, String name ) { DictInfo result = null; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index 41e26a24e..fd4c6c59a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -524,21 +524,39 @@ public class DictsActivity extends ExpandableListActivity public void deleteCalled( XWListItem item ) { String dict = item.getText(); - int code = DictLangCache.getDictLangCode( this, dict ); - int nGames = DBUtils.countGamesUsingLang( this, code ); String msg = String.format( getString( R.string.confirm_delete_dictf ), dict ); m_deleteDict = dict; m_moveFromLoc = (DictUtils.DictLoc)item.getCached(); - if ( nGames > 0 ) { - DictAndLoc[] dal = DictLangCache.getDALsHaveLang( this, code ); - if ( 1 == dal.length ) { - Assert.assertTrue( dict.equals(dal[0].name) ); - String fmt = getString( R.string.confirm_deleteonly_dictf ); + // When and what to warn about. First off, if there's another + // identical dict, simply confirm. Or if nobody's using this + // dict *and* it's not the last of a language that somebody's + // using, simply confirm. If somebody is using it, then we + // want different warnings depending on whether it's the last + // available dict in its language. + + if ( 1 < DictLangCache.getDictCount( this, dict ) ) { + // there's another; do nothing + } else { + int fmtid = 0; + int langcode = DictLangCache.getDictLangCode( this, dict ); + DictAndLoc[] langDals = DictLangCache.getDALsHaveLang( this, + langcode ); + int nUsingLang = DBUtils.countGamesUsingLang( this, langcode ); + + if ( 1 == langDals.length ) { // last in this language? + if ( 0 < nUsingLang ) { + fmtid = R.string.confirm_deleteonly_dictf; + } + } else if ( 0 < DBUtils.countGamesUsingDict( this, dict ) ) { + fmtid = R.string.confirm_deletemore_dictf; + } + if ( 0 != fmtid ) { + String fmt = getString( fmtid ); msg += String.format( fmt, DictLangCache. - getLangName( this, code ) ); + getLangName( this, langcode ) ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java index 0244bc355..e1abd23c6 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java @@ -258,4 +258,14 @@ public class GameSummary { return indx == turn && isLocal(indx); } + public String dictNames( String separator ) + { + String list = null; + if ( null != m_gi ) { + String[] names = m_gi.dictNames(); + list = TextUtils.join( separator, names ); + } + return String.format( "%s%s%s", separator, list, separator ); + } + }