Rewrite to actually cache lang names and codes we encounter

This commit is contained in:
Eric House 2022-07-11 17:46:47 -07:00
parent 041a9f3c3e
commit ac7e918718
6 changed files with 205 additions and 70 deletions

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */ /* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
/* /*
* Copyright 2010 by Eric House (xwords@eehouse.org). All rights * Copyright 2010 - 2022 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -36,6 +36,7 @@ import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.Utils.ISOCode; import org.eehouse.android.xw4.Utils.ISOCode;
import java.io.File; import java.io.File;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@ -48,8 +49,6 @@ import java.util.Set;
public class DictLangCache { public class DictLangCache {
private static final String TAG = DictLangCache.class.getSimpleName(); private static final String TAG = DictLangCache.class.getSimpleName();
private static Map<ISOCode, String> s_langNames;
private static Map<String, ISOCode> s_langCodes;
private static ISOCode s_adaptedLang = null; private static ISOCode s_adaptedLang = null;
private static LangsArrayAdapter s_langsAdapter; private static LangsArrayAdapter s_langsAdapter;
@ -213,15 +212,10 @@ public class DictLangCache {
List<DictAndLoc> al = new ArrayList<>(); List<DictAndLoc> al = new ArrayList<>();
DictAndLoc[] dals = DictUtils.dictList( context ); DictAndLoc[] dals = DictUtils.dictList( context );
makeMaps( context );
for ( DictAndLoc dal : dals ) { for ( DictAndLoc dal : dals ) {
DictInfo info = getInfo( context, dal ); DictInfo info = getInfo( context, dal );
if ( null != info ) { if ( null != info && isoCode.equals( info.isoCode() ) ) {
Assert.assertTrueNR( s_langNames.containsKey( info.isoCode() ) ); al.add( dal );
if ( isoCode.equals( info.isoCode() ) ) {
al.add( dal );
}
} }
} }
DictAndLoc[] result = al.toArray( new DictAndLoc[al.size()] ); DictAndLoc[] result = al.toArray( new DictAndLoc[al.size()] );
@ -255,33 +249,60 @@ public class DictLangCache {
public static ISOCode getDictISOCode( Context context, DictAndLoc dal ) public static ISOCode getDictISOCode( Context context, DictAndLoc dal )
{ {
return getInfo( context, dal ).isoCode(); ISOCode result = getInfo( context, dal ).isoCode();
Assert.assertTrueNR( null != result );
return result;
} }
public static ISOCode getDictISOCode( Context context, String dictName ) public static ISOCode getDictISOCode( Context context, String dictName )
{ {
DictInfo info = getInfo( context, dictName ); DictInfo info = getInfo( context, dictName );
return info.isoCode(); ISOCode result = info.isoCode();
Assert.assertTrueNR( null != result );
return result;
} }
public static String getLangNameForISOCode( Context context, ISOCode isoCode ) public static String getLangNameForISOCode( Context context, ISOCode isoCode )
{ {
makeMaps( context ); String langName;
return s_langNames.get( isoCode ); try ( DLCache cache = DLCache.get( context ) ) {
langName = cache.get( isoCode );
if ( null == langName ) {
// Any chance we have a installed dict providing this? How to
// search given we can't read an ISOCode from a dict without
// opening it.
}
}
Log.d( TAG, "getLangNameForISOCode(%s) => %s", isoCode, langName );
return langName;
} }
public static void setLangNameForISOCode( Context context, ISOCode isoCode, public static void setLangNameForISOCode( Context context, ISOCode isoCode,
String langName ) String langName )
{ {
makeMaps( context ); Log.d( TAG, "setLangNameForISOCode(%s=>%s)", isoCode, langName );
putTwo( isoCode, langName ); try ( DLCache cache = DLCache.get( context ) ) {
cache.put( isoCode, langName );
}
} }
public static ISOCode getLangIsoCode( Context context, String langName ) public static ISOCode getLangIsoCode( Context context, String langName )
{ {
makeMaps( context ); Log.d( TAG, "getLangIsoCode(%s)", langName );
ISOCode result = s_langCodes.get( langName ); ISOCode result;
// Log.d( TAG, "getLangIsoCode(%s) => %s", langName, result ); try ( DLCache cache = DLCache.get( context ) ) {
Log.d( TAG, "looking for %s in %H", langName, cache );
result = cache.get( langName );
}
if ( null == result ) {
Assert.failDbg();
// getinfo
}
Log.d( TAG, "getLangIsoCode(%s) => %s", langName, result );
// Assert.assertTrueNR( null != result );
return result; return result;
} }
@ -311,7 +332,8 @@ public class DictLangCache {
public static String getLangName( Context context, String dictName ) public static String getLangName( Context context, String dictName )
{ {
ISOCode isoCode = getDictISOCode( context, dictName ); ISOCode isoCode = getDictISOCode( context, dictName );
return getLangNameForISOCode( context, isoCode ); String langName = getLangNameForISOCode( context, isoCode );
return langName;
} }
// May be called from background thread // May be called from background thread
@ -354,9 +376,18 @@ public class DictLangCache {
String name = getLangName( context, dal.name ); String name = getLangName( context, dal.name );
if ( null == name || 0 == name.length() ) { if ( null == name || 0 == name.length() ) {
Log.w( TAG, "bad lang name for dal name %s", dal.name ); Log.w( TAG, "bad lang name for dal name %s", dal.name );
// Assert.fail();
DictInfo di = getInfo( context, dal );
if ( null != di ) {
name = di.langName;
try ( DLCache cache = DLCache.get( context ) ) {
cache.put( di.isoCode(), name );
}
}
}
if ( null != name && 0 < name.length() ) {
langs.add( name );
} }
langs.add( name );
} }
String[] result = new String[langs.size()]; String[] result = new String[langs.size()];
return langs.toArray( result ); return langs.toArray( result );
@ -422,45 +453,6 @@ public class DictLangCache {
return s_dictsAdapter; return s_dictsAdapter;
} }
private static void putTwo( ISOCode isoCode, String langName )
{
// Log.d( TAG, "putTwo(): adding %s => %s", langName, isoCode );
Assert.assertTrueNR( null != isoCode
&& !TextUtils.isEmpty(langName) );
s_langCodes.put( langName, isoCode );
s_langNames.put( isoCode, langName );
}
private static void makeMaps( Context context )
{
if ( null == s_langNames ) {
s_langCodes = new HashMap<>();
s_langNames = new HashMap<>();
Resources res = context.getResources();
String[] entries = res.getStringArray( R.array.language_names );
for ( int ii = 0; ii < entries.length; ii += 2 ) {
ISOCode isoCode = new ISOCode(entries[ii]);
String langName = entries[ii+1];
putTwo( isoCode, langName );
}
// Now deal with any dicts too new for their isoCodes to be in
// language_names
DictAndLoc[] dals = DictUtils.dictList( context ) ;
for ( DictAndLoc dal : dals ) {
DictInfo info = getInfo( context, dal );
ISOCode isoCode = info.isoCode();
Assert.assertTrueNR( null != isoCode );
if ( !s_langNames.containsKey( isoCode ) ) {
// Log.d( TAG, "looking at info %s", info );
Assert.assertTrueNR( null != info.langName );
putTwo( isoCode, info.langName );
}
}
}
}
private static DictInfo getInfo( Context context, String name ) private static DictInfo getInfo( Context context, String name )
{ {
DictInfo result = DBUtils.dictsGetInfo( context, name ); DictInfo result = DBUtils.dictsGetInfo( context, name );
@ -502,4 +494,133 @@ public class DictLangCache {
} }
return info; return info;
} }
private static class DLCache implements Serializable, AutoCloseable {
private static final String CACHE_KEY = TAG + "/cache";
private static DLCache[] sCache = {null};
private HashMap<ISOCode, String> mLangNames = new HashMap<>();
private int mCurRev;
private transient boolean mDirty = false;
private transient Context mContext;
static DLCache get( Context context )
{
DLCache result;
synchronized ( sCache ) {
result = sCache[0];
if ( null == result ) {
result = (DLCache)DBUtils.getSerializableFor( context, CACHE_KEY );
if ( null != result ) {
Log.d( TAG, "loaded cache: %s", result );
}
}
if ( null == result ) {
result = new DLCache();
}
result.update( context );
sCache[0] = result;
try {
while ( !result.tryLock( context ) ) {
sCache.wait();
}
} catch ( InterruptedException ioe ) {
Log.ex( TAG, ioe );
Assert.failDbg();
}
}
// Log.d( TAG, "getCache() => %H", sCache[0] );
return sCache[0];
}
ISOCode get( String langName )
{
ISOCode result = null;
for ( ISOCode code : mLangNames.keySet() ) {
if ( langName.equals( mLangNames.get(code) ) ) {
result = code;
break;
}
}
if ( null == result ) {
Log.d( TAG, "langName '%s' not in %s", langName, this );
}
return result;
}
String get( ISOCode code )
{
String result = mLangNames.get(code);
if ( null == result ) {
Log.d( TAG, "code '%s' not in %s", code, this );
}
return result;
}
void put( ISOCode code, String langName )
{
if ( !langName.equals( mLangNames.get( code ) ) ) {
mDirty = true;
mLangNames.put( code, langName );
}
}
// @Override
// public String toString()
// {
// ArrayList<String> pairs = new ArrayList<>();
// for ( ISOCode code : mLangNames.keySet() ) {
// pairs.add(String.format("%s<=>%s", code, mLangNames.get(code) ) );
// }
// return TextUtils.join( ", ", pairs );
// }
@Override
public void close()
{
if ( mDirty ) {
DBUtils.setSerializableFor( mContext, CACHE_KEY, this );
Log.d( TAG, "saveCache(%H) stored %s", this, this );
mDirty = false;
}
unlock();
synchronized ( sCache ) {
sCache.notifyAll();
}
}
private void update( Context context )
{
if ( mCurRev < BuildConfig.VERSION_CODE ) {
Resources res = context.getResources();
String[] entries = res.getStringArray( R.array.language_names );
for ( int ii = 0; ii < entries.length; ii += 2 ) {
ISOCode isoCode = new ISOCode(entries[ii]);
String langName = entries[ii+1];
put( isoCode, langName );
}
mCurRev = BuildConfig.VERSION_CODE;
if ( mDirty ) {
Log.d( TAG, "updated cache; now %s", this );
}
}
}
private boolean tryLock( Context context )
{
boolean canLock = null == mContext;
if ( canLock ) {
mContext = context;
}
return canLock;
}
private void unlock()
{
Assert.assertTrueNR( null != mContext );
mContext = null;
}
}
} }

View file

@ -1206,6 +1206,7 @@ public class DictsDelegate extends ListDelegateBase
if ( m_launchedForMissing ) { if ( m_launchedForMissing ) {
post( new Runnable() { post( new Runnable() {
@Override
public void run() { public void run() {
if ( success ) { if ( success ) {
Intent intent = getIntent(); Intent intent = getIntent();

View file

@ -445,7 +445,7 @@ public class DwnldDelegate extends ListDelegateBase {
return new File(path).getName(); return new File(path).getName();
} }
private static ISOCode langFromUri( Uri uri ) private static ISOCode isoCodeFromUri( Uri uri )
{ {
List<String> segs = uri.getPathSegments(); List<String> segs = uri.getPathSegments();
ISOCode result = new ISOCode(segs.get( segs.size() - 2 )); ISOCode result = new ISOCode(segs.get( segs.size() - 2 ));
@ -461,7 +461,7 @@ public class DwnldDelegate extends ListDelegateBase {
} }
} }
private static void callListener( Uri uri, boolean success ) private void callListener( Uri uri, boolean success )
{ {
if ( null != uri ) { if ( null != uri ) {
ListenerData ld; ListenerData ld;
@ -470,11 +470,11 @@ public class DwnldDelegate extends ListDelegateBase {
} }
if ( null != ld ) { if ( null != ld ) {
String name = ld.m_name; String name = ld.m_name;
ISOCode lang = langFromUri( uri ); ISOCode isoCode = isoCodeFromUri( uri );
if ( null == name ) { if ( null == name ) {
name = uri.toString(); name = uri.toString();
} }
ld.m_lstnr.downloadFinished( lang, name, success ); ld.m_lstnr.downloadFinished( isoCode, name, success );
} }
} }
} }

View file

@ -551,7 +551,7 @@ public class GameConfigDelegate extends DelegateBase
break; break;
case REQUEST_LANG_GC: case REQUEST_LANG_GC:
ISOCode isoCode = cancelled ? m_gi.isoCode() ISOCode isoCode = cancelled ? m_gi.isoCode()
: (ISOCode)data.getSerializableExtra( DictsDelegate.RESULT_LAST_LANG ); : ISOCode.newIf(data.getStringExtra( DictsDelegate.RESULT_LAST_LANG ) );
String langName = DictLangCache.getLangNameForISOCode( m_activity, isoCode ); String langName = DictLangCache.getLangNameForISOCode( m_activity, isoCode );
selLangChanged( langName ); selLangChanged( langName );
setLangSpinnerSelection( langName ); setLangSpinnerSelection( langName );

View file

@ -274,8 +274,7 @@ public class GameListItem extends LinearLayout
value = String.format( "%d", m_summary.nPacketsPending ); value = String.format( "%d", m_summary.nPacketsPending );
break; break;
case R.string.game_summary_field_language: case R.string.game_summary_field_language:
value = value = getDictLang();
DictLangCache.getLangNameForISOCode( m_context, m_summary.isoCode );
break; break;
case R.string.game_summary_field_opponents: case R.string.game_summary_field_opponents:
value = m_summary.playerNames( m_context ); value = m_summary.playerNames( m_context );
@ -304,6 +303,17 @@ public class GameListItem extends LinearLayout
return state; return state;
} }
private String getDictLang()
{
String langName = DictLangCache
.getLangNameForISOCode( m_context, m_summary.isoCode );
if ( null == langName ) {
langName = LocUtils.getString( m_context, R.string.langUnknownFmt,
m_summary.isoCode );
}
return langName;
}
private void setData( GameSummary summary, boolean expanded ) private void setData( GameSummary summary, boolean expanded )
{ {
if ( null != summary ) { if ( null != summary ) {
@ -465,8 +475,7 @@ public class GameListItem extends LinearLayout
synchronized( s_invalRows ) { synchronized( s_invalRows ) {
s_invalRows.add( rowid ); s_invalRows.add( rowid );
} }
// DbgUtils.logf( "GameListItem.inval(rowid=%d); inval rows now %s", // Log.d( TAG, "GameListItem.inval(rowid=%d)", rowid );
// rowid, invalRowsToString() );
} }
// private static String invalRowsToString() // private static String invalRowsToString()

View file

@ -269,6 +269,10 @@
<string name="lang_label">Game language</string> <string name="lang_label">Game language</string>
<string name="langdict_label">Game language/wordlist</string> <string name="langdict_label">Game language/wordlist</string>
<!-- text of separator marking out the connection area of the dialog --> <!-- text of separator marking out the connection area of the dialog -->
<!-- Used as name of missing language, usually when a game's been
created and the wordlist then deleted. -->
<string name="langUnknownFmt">Unknown (%1$s)</string>
<string name="connect_label_fmt">Connection (via %1$s)</string> <string name="connect_label_fmt">Connection (via %1$s)</string>
<!-- text of separator marking out other-setting area of the dialog --> <!-- text of separator marking out other-setting area of the dialog -->