when sd card is umounted, android sends a message to apps. I can't

figure out how to get that so I ignore it and am killed then
relaunched if I have a dict file open on the SD.  On relauch, don't
crash.  Instead, check if any dicts used by the game is unreachable
and put up an alert with only one choice: Close game.  Thought about
using the missing-dicts stuff from GamesList, but this is a special
case that should be seen only when user umounts while a BoardActivity
instance is frontmost.
This commit is contained in:
Andy2 2011-08-18 07:54:00 -07:00
parent 7a1346e530
commit fd4e627628
3 changed files with 125 additions and 80 deletions

View file

@ -389,13 +389,18 @@
<string name="subst_dict_title">Substitute dictionary (wordcount)</string> <string name="subst_dict_title">Substitute dictionary (wordcount)</string>
<string name="button_substdict">Substitute</string> <string name="button_substdict">Substitute</string>
<string name="button_close_game">Close game</string>
<string name="no_dict_finish">A dictionary this game is using has
disappeared. (Usually this means it\'s on an external card that
is no longer available.)</string>
<string name="no_dictf">Unable to open game \"%1$s\" because no <string name="no_dictf">Unable to open game \"%1$s\" because no
%2$s dictionary found. (It may have been deleted, or stored on %2$s dictionary found. (It may have been deleted, or stored on
an an SD card that\'s been unmounted.)</string> an external card that is no longer available.)</string>
<string name="no_dict_substf">Unable to open game \"%1$s\" because <string name="no_dict_substf">Unable to open game \"%1$s\" because
dictionary %2$s not found. (It may have been deleted, or stored dictionary %2$s not found. (It may have been deleted, or stored
on an an SD card that\'s been unmounted.) You can download a on an external card that is no longer available.) You can
replacement or substitute another %3$s dictionary.</string> download a replacement or substitute another %3$s
dictionary.</string>
<string name="msg_ask_password">Password for \"%s\":</string> <string name="msg_ask_password">Password for \"%s\":</string>

View file

@ -38,6 +38,7 @@ import android.app.Dialog;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.EditText; import android.widget.EditText;
@ -68,6 +69,7 @@ public class BoardActivity extends XWActivity
private static final int QUERY_ENDGAME = DLG_OKONLY + 7; private static final int QUERY_ENDGAME = DLG_OKONLY + 7;
private static final int DLG_DELETED = DLG_OKONLY + 8; private static final int DLG_DELETED = DLG_OKONLY + 8;
private static final int DLG_INVITE = DLG_OKONLY + 9; private static final int DLG_INVITE = DLG_OKONLY + 9;
private static final int DLG_NODICT = DLG_OKONLY + 10;
private static final int CHAT_REQUEST = 1; private static final int CHAT_REQUEST = 1;
private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins
@ -80,7 +82,7 @@ public class BoardActivity extends XWActivity
private int m_jniGamePtr; private int m_jniGamePtr;
private GameUtils.GameLock m_gameLock; private GameUtils.GameLock m_gameLock;
private CurGameInfo m_gi; private CurGameInfo m_gi;
CommsTransport m_xport; private CommsTransport m_xport;
private Handler m_handler = null; private Handler m_handler = null;
private TimerRunnable[] m_timers; private TimerRunnable[] m_timers;
private Runnable m_screenTimer; private Runnable m_screenTimer;
@ -286,6 +288,21 @@ public class BoardActivity extends XWActivity
.create(); .create();
} }
break; break;
case DLG_NODICT:
dialog = new AlertDialog.Builder( this )
.setTitle( R.string.no_dict_title )
.setMessage( R.string.no_dict_finish )
.setPositiveButton( R.string.button_close_game, null )
.create();
OnDismissListener dlstnr;
dlstnr = new OnDismissListener() {
public void onDismiss( DialogInterface di ) {
// removeDialog( DLG_NODICT );
finish();
}
};
dialog.setOnDismissListener( dlstnr );
break;
default: default:
// just drop it; super.onCreateDialog likely failed // just drop it; super.onCreateDialog likely failed
break; break;
@ -1044,89 +1061,96 @@ public class BoardActivity extends XWActivity
private void loadGame() private void loadGame()
{ {
if ( 0 == m_jniGamePtr ) { if ( 0 == m_jniGamePtr ) {
Assert.assertNull( m_gameLock );
m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock();
byte[] stream = GameUtils.savedGame( this, m_gameLock );
XwJNI.gi_from_stream( m_gi, stream );
String[] dictNames = m_gi.dictNames(); String[] dictNames = m_gi.dictNames();
GameUtils.DictPairs pairs = GameUtils.openDicts( this, dictNames ); GameUtils.DictPairs pairs = GameUtils.openDicts( this, dictNames );
String langName = m_gi.langName();
m_jniGamePtr = XwJNI.initJNI();
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { if ( pairs.anyMissing( dictNames ) ) {
m_xport = new CommsTransport( m_jniGamePtr, this, this, showDialog( DLG_NODICT );
m_gi.serverRole ); } else {
}
CommonPrefs cp = CommonPrefs.get( this ); String langName = m_gi.langName();
if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
m_gi, dictNames, pairs.m_bytes,
pairs.m_paths, langName, m_utils,
m_jniu, m_view, cp, m_xport ) ) {
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, m_jniu,
m_view, cp, m_xport, dictNames,
pairs.m_bytes, pairs.m_paths,
langName );
}
m_jniThread = new Assert.assertNull( m_gameLock );
JNIThread( m_jniGamePtr, m_gi, m_view, m_gameLock, this, m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock();
new Handler() {
public void handleMessage( Message msg ) { byte[] stream = GameUtils.savedGame( this, m_gameLock );
switch( msg.what ) { XwJNI.gi_from_stream( m_gi, stream );
case JNIThread.DRAW:
m_view.invalidate(); m_jniGamePtr = XwJNI.initJNI();
break;
case JNIThread.DIALOG: if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
m_dlgBytes = (String)msg.obj; m_xport = new CommsTransport( m_jniGamePtr, this, this,
m_dlgTitle = msg.arg1; m_gi.serverRole );
showDialog( DLG_OKONLY ); }
break;
case JNIThread.QUERY_ENDGAME: CommonPrefs cp = CommonPrefs.get( this );
showDialog( QUERY_ENDGAME ); if ( null == stream ||
break; ! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
case JNIThread.TOOLBAR_STATES: m_gi, dictNames, pairs.m_bytes,
if ( null != m_jniThread ) { pairs.m_paths, langName, m_utils,
m_gsi = m_jniThread.getGameStateInfo(); m_jniu, m_view, cp, m_xport ) ) {
updateToolbar(); XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, m_jniu,
m_view, cp, m_xport, dictNames,
pairs.m_bytes, pairs.m_paths,
langName );
}
m_jniThread = new
JNIThread( m_jniGamePtr, m_gi, m_view, m_gameLock, this,
new Handler() {
public void handleMessage( Message msg ) {
switch( msg.what ) {
case JNIThread.DRAW:
m_view.invalidate();
break;
case JNIThread.DIALOG:
m_dlgBytes = (String)msg.obj;
m_dlgTitle = msg.arg1;
showDialog( DLG_OKONLY );
break;
case JNIThread.QUERY_ENDGAME:
showDialog( QUERY_ENDGAME );
break;
case JNIThread.TOOLBAR_STATES:
if ( null != m_jniThread ) {
m_gsi = m_jniThread.getGameStateInfo();
updateToolbar();
}
break;
} }
break;
} }
} } );
} ); // see http://stackoverflow.com/questions/680180/where-to-stop-\
// see http://stackoverflow.com/questions/680180/where-to-stop-\ // destroy-threads-in-android-service-class
// destroy-threads-in-android-service-class m_jniThread.setDaemon( true );
m_jniThread.setDaemon( true ); m_jniThread.start();
m_jniThread.start();
m_view.startHandling( this, m_jniThread, m_jniGamePtr, m_gi ); m_view.startHandling( this, m_jniThread, m_jniGamePtr, m_gi );
if ( null != m_xport ) { if ( null != m_xport ) {
m_xport.setReceiver( m_jniThread ); m_xport.setReceiver( m_jniThread );
} }
m_jniThread.handle( JNICmd.CMD_START ); m_jniThread.handle( JNICmd.CMD_START );
if ( !CommonPrefs.getHideTitleBar( this ) ) { if ( !CommonPrefs.getHideTitleBar( this ) ) {
setTitle( GameUtils.getName( this, m_rowid ) ); setTitle( GameUtils.getName( this, m_rowid ) );
} }
m_toolbar = new Toolbar( this ); m_toolbar = new Toolbar( this );
populateToolbar(); populateToolbar();
int flags = DBUtils.getMsgFlags( this, m_rowid ); int flags = DBUtils.getMsgFlags( this, m_rowid );
if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) { if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) {
startChatActivity(); startChatActivity();
} }
if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) {
m_jniThread.handle( JNIThread.JNICmd.CMD_POST_OVER ); m_jniThread.handle( JNIThread.JNICmd.CMD_POST_OVER );
} }
if ( 0 != flags ) { if ( 0 != flags ) {
DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE ); DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE );
} }
trySendChats(); trySendChats();
}
} }
} // loadGame } // loadGame
@ -1209,9 +1233,9 @@ public class BoardActivity extends XWActivity
}); });
} // populateToolbar } // populateToolbar
private DialogInterface.OnDismissListener makeODLforBlocking( final int id ) private OnDismissListener makeODLforBlocking( final int id )
{ {
return new DialogInterface.OnDismissListener() { return new OnDismissListener() {
public void onDismiss( DialogInterface di ) { public void onDismiss( DialogInterface di ) {
releaseIfBlocking(); releaseIfBlocking();
removeDialog( id ); removeDialog( id );

View file

@ -158,7 +158,23 @@ public class GameUtils {
public DictPairs( byte[][] bytes, String[] paths ) { public DictPairs( byte[][] bytes, String[] paths ) {
m_bytes = bytes; m_paths = 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(); private static Object s_syncObj = new Object();
@ -180,7 +196,7 @@ public class GameUtils {
* basis for a new one. * basis for a new one.
*/ */
public static GameLock resetGame( Context context, GameLock lockSrc, public static GameLock resetGame( Context context, GameLock lockSrc,
GameLock lockDest ) GameLock lockDest )
{ {
int gamePtr = XwJNI.initJNI(); int gamePtr = XwJNI.initJNI();
CurGameInfo gi = new CurGameInfo( context ); CurGameInfo gi = new CurGameInfo( context );
@ -897,7 +913,7 @@ public class GameUtils {
// Which isn't possible right now, so make sure the old and new // Which isn't possible right now, so make sure the old and new
// dict have the same langauge code. // dict have the same langauge code.
public static void replaceDicts( Context context, long rowid, public static void replaceDicts( Context context, long rowid,
String oldDict, String newDict ) String oldDict, String newDict )
{ {
GameLock lock = new GameLock( rowid, true ).lock(); GameLock lock = new GameLock( rowid, true ).lock();
byte[] stream = savedGame( context, lock ); byte[] stream = savedGame( context, lock );