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"; -*- */
/*
* Copyright 2010 by Eric House (xwords@eehouse.org). All rights
* Copyright 2010 - 2022 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* 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 java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@ -48,8 +49,6 @@ import java.util.Set;
public class DictLangCache {
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 LangsArrayAdapter s_langsAdapter;
@ -213,15 +212,10 @@ public class DictLangCache {
List<DictAndLoc> al = new ArrayList<>();
DictAndLoc[] dals = DictUtils.dictList( context );
makeMaps( context );
for ( DictAndLoc dal : dals ) {
DictInfo info = getInfo( context, dal );
if ( null != info ) {
Assert.assertTrueNR( s_langNames.containsKey( info.isoCode() ) );
if ( isoCode.equals( info.isoCode() ) ) {
al.add( dal );
}
if ( null != info && isoCode.equals( info.isoCode() ) ) {
al.add( dal );
}
}
DictAndLoc[] result = al.toArray( new DictAndLoc[al.size()] );
@ -255,33 +249,60 @@ public class DictLangCache {
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 )
{
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 )
{
makeMaps( context );
return s_langNames.get( isoCode );
String langName;
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,
String langName )
{
makeMaps( context );
putTwo( isoCode, langName );
Log.d( TAG, "setLangNameForISOCode(%s=>%s)", isoCode, langName );
try ( DLCache cache = DLCache.get( context ) ) {
cache.put( isoCode, langName );
}
}
public static ISOCode getLangIsoCode( Context context, String langName )
{
makeMaps( context );
ISOCode result = s_langCodes.get( langName );
// Log.d( TAG, "getLangIsoCode(%s) => %s", langName, result );
Log.d( TAG, "getLangIsoCode(%s)", langName );
ISOCode 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;
}
@ -311,7 +332,8 @@ public class DictLangCache {
public static String getLangName( Context context, String dictName )
{
ISOCode isoCode = getDictISOCode( context, dictName );
return getLangNameForISOCode( context, isoCode );
String langName = getLangNameForISOCode( context, isoCode );
return langName;
}
// May be called from background thread
@ -354,9 +376,18 @@ public class DictLangCache {
String name = getLangName( context, dal.name );
if ( null == name || 0 == name.length() ) {
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()];
return langs.toArray( result );
@ -422,45 +453,6 @@ public class DictLangCache {
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 )
{
DictInfo result = DBUtils.dictsGetInfo( context, name );
@ -502,4 +494,133 @@ public class DictLangCache {
}
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 ) {
post( new Runnable() {
@Override
public void run() {
if ( success ) {
Intent intent = getIntent();

View file

@ -445,7 +445,7 @@ public class DwnldDelegate extends ListDelegateBase {
return new File(path).getName();
}
private static ISOCode langFromUri( Uri uri )
private static ISOCode isoCodeFromUri( Uri uri )
{
List<String> segs = uri.getPathSegments();
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 ) {
ListenerData ld;
@ -470,11 +470,11 @@ public class DwnldDelegate extends ListDelegateBase {
}
if ( null != ld ) {
String name = ld.m_name;
ISOCode lang = langFromUri( uri );
ISOCode isoCode = isoCodeFromUri( uri );
if ( null == name ) {
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;
case REQUEST_LANG_GC:
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 );
selLangChanged( langName );
setLangSpinnerSelection( langName );

View file

@ -274,8 +274,7 @@ public class GameListItem extends LinearLayout
value = String.format( "%d", m_summary.nPacketsPending );
break;
case R.string.game_summary_field_language:
value =
DictLangCache.getLangNameForISOCode( m_context, m_summary.isoCode );
value = getDictLang();
break;
case R.string.game_summary_field_opponents:
value = m_summary.playerNames( m_context );
@ -304,6 +303,17 @@ public class GameListItem extends LinearLayout
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 )
{
if ( null != summary ) {
@ -465,8 +475,7 @@ public class GameListItem extends LinearLayout
synchronized( s_invalRows ) {
s_invalRows.add( rowid );
}
// DbgUtils.logf( "GameListItem.inval(rowid=%d); inval rows now %s",
// rowid, invalRowsToString() );
// Log.d( TAG, "GameListItem.inval(rowid=%d)", rowid );
}
// private static String invalRowsToString()

View file

@ -269,6 +269,10 @@
<string name="lang_label">Game language</string>
<string name="langdict_label">Game language/wordlist</string>
<!-- 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>
<!-- text of separator marking out other-setting area of the dialog -->