Merge branch 'android_branch' into android_translate

Conflicts:
	xwords4/android/XWords4/res/values/strings.xml
This commit is contained in:
Eric House 2015-08-20 07:43:16 -07:00
commit 598ee7599b
12 changed files with 145 additions and 101 deletions

View file

@ -0,0 +1 @@
strings.xml

View file

@ -0,0 +1 @@
strings.xml

View file

@ -9,7 +9,8 @@
<!-- history is kept in a scrolling list of textview elems so <!-- history is kept in a scrolling list of textview elems so
different style can be applied based on whether they're local or different style can be applied based on whether they're local or
remote. Inserted at runtime.... --> remote. Inserted at runtime.... -->
<ScrollView android:orientation="vertical" <ScrollView android:id="@+id/scroll"
android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
@ -21,24 +22,11 @@
/> />
</ScrollView> </ScrollView>
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<EditText android:id="@+id/chat_edit" <EditText android:id="@+id/chat_edit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="false"
android:layout_weight="1"
/>
<Button android:id="@+id/send_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chat_send"
android:layout_weight="0" android:layout_weight="0"
/> />
</LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,7 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/chat_menu_send"
android:title="@string/chat_send"
android:showAsAction="ifRoom"
android:icon="@drawable/send__gen"
/>
<item android:id="@+id/chat_menu_clear" <item android:id="@+id/chat_menu_clear"
android:title="@string/chat_menu_clear" android:title="@string/chat_menu_clear"
android:showAsAction="ifRoom"
android:icon="@drawable/content_discard__gen"
/> />
</menu> </menu>

View file

@ -0,0 +1 @@
/strings.xml

View file

@ -2562,4 +2562,9 @@
<string name="not_again_clip_expl_fmt">The \"%1$s\" option copies an <string name="not_again_clip_expl_fmt">The \"%1$s\" option copies an
invitation URL to the clipboard. Paste it into the app of your invitation URL to the clipboard. Paste it into the app of your
choice and send it to your friend.</string> choice and send it to your friend.</string>
<string name="confirm_clear_chat">Are you sure you want to delete
all chat history for this game?\n\n(This action cannot be
undone.)</string>
</resources> </resources>

View file

@ -906,7 +906,7 @@ public class BoardDelegate extends DelegateBase
break; break;
case R.id.board_menu_game_resend: case R.id.board_menu_game_resend:
m_jniThread.handle( JNICmd.CMD_RESEND, true, false ); m_jniThread.handle( JNICmd.CMD_RESEND, true, false, true );
break; break;
case R.id.gamel_menu_checkmoves: case R.id.gamel_menu_checkmoves:
@ -2109,7 +2109,8 @@ public class BoardDelegate extends DelegateBase
} }
if ( 0 < m_connTypes.size() ) { if ( 0 < m_connTypes.size() ) {
m_jniThread.handle( JNIThread.JNICmd.CMD_RESEND, force, true ); m_jniThread.handle( JNIThread.JNICmd.CMD_RESEND, force, true,
false );
} }
} }

View file

@ -21,19 +21,19 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MenuInflater; import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
public class ChatDelegate extends DelegateBase import org.eehouse.android.xw4.DlgDelegate.Action;
implements View.OnClickListener {
public class ChatDelegate extends DelegateBase {
private long m_rowid; private long m_rowid;
private Activity m_activity; private Activity m_activity;
@ -65,8 +65,13 @@ public class ChatDelegate extends DelegateBase
} }
} }
((Button)findViewById( R.id.send_button )) final ScrollView scroll = (ScrollView)findViewById( R.id.scroll );
.setOnClickListener( this ); scroll.post(new Runnable() {
@Override
public void run() {
scroll.fullScroll(View.FOCUS_DOWN);
}
});
String title = getString( R.string.chat_title_fmt, String title = getString( R.string.chat_title_fmt,
GameUtils.getName( m_activity, m_rowid ) ); GameUtils.getName( m_activity, m_rowid ) );
@ -80,18 +85,14 @@ public class ChatDelegate extends DelegateBase
@Override @Override
public boolean onOptionsItemSelected( MenuItem item ) public boolean onOptionsItemSelected( MenuItem item )
{ {
boolean handled = R.id.chat_menu_clear == item.getItemId(); boolean handled = true;
switch ( item.getItemId() ) {
case R.id.chat_menu_clear:
if ( handled ) { if ( handled ) {
DBUtils.clearChatHistory( m_activity, m_rowid ); showConfirmThen( R.string.confirm_clear_chat, Action.CLEAR_ACTION );
LinearLayout layout =
(LinearLayout)findViewById( R.id.chat_history );
layout.removeAllViews();
} }
return handled; break;
} case R.id.chat_menu_send:
public void onClick( View view )
{
EditText edit = (EditText)findViewById( R.id.chat_edit ); EditText edit = (EditText)findViewById( R.id.chat_edit );
String text = edit.getText().toString(); String text = edit.getText().toString();
if ( null == text || text.length() == 0 ) { if ( null == text || text.length() == 0 ) {
@ -104,5 +105,28 @@ public class ChatDelegate extends DelegateBase
setResult( Activity.RESULT_OK, result ); setResult( Activity.RESULT_OK, result );
} }
finish(); finish();
break;
default:
handled = false;
break;
}
return handled;
}
@Override
public void dlgButtonClicked( Action action, int which, Object[] params )
{
switch ( action ) {
case CLEAR_ACTION:
if ( AlertDialog.BUTTON_POSITIVE == which ) {
DBUtils.clearChatHistory( m_activity, m_rowid );
LinearLayout layout =
(LinearLayout)findViewById( R.id.chat_history );
layout.removeAllViews();
}
break;
default:
super.dlgButtonClicked( action, which, params );
}
} }
} }

View file

@ -528,10 +528,9 @@ public class DelegateBase implements DlgClickNotify,
m_dlgDelegate.eventOccurred( event, args ); m_dlgDelegate.eventOccurred( event, args );
break; break;
default: default:
if ( BuildConfig.DEBUG ) { DbgUtils.logdf( "DelegateBase.eventOccurred(event=%s) (DROPPED)",
DbgUtils.logf( "DelegateBase.eventOccurred(event=%s) (DROPPED)",
event.toString() ); event.toString() );
} break;
} }
if ( 0 != fmtId ) { if ( 0 != fmtId ) {

View file

@ -523,11 +523,11 @@ public class JNIThread extends Thread {
break; break;
case CMD_RESEND: case CMD_RESEND:
boolean force = ((Boolean)args[0]).booleanValue();
int nSent = int nSent =
XwJNI.comms_resendAll( m_jniGamePtr, force, XwJNI.comms_resendAll( m_jniGamePtr,
((Boolean)args[0]).booleanValue(),
((Boolean)args[1]).booleanValue() ); ((Boolean)args[1]).booleanValue() );
if ( force ) { if ( ((Boolean)args[2]).booleanValue() ) {
Message.obtain(m_handler, MSGS_SENT, nSent).sendToTarget(); Message.obtain(m_handler, MSGS_SENT, nSent).sendToTarget();
} }
break; break;

View file

@ -345,9 +345,6 @@ chooseMove( EngineCtxt* engine, PossibleMove** move )
result = (NULL != chosen) && (chosen->score > 0); result = (NULL != chosen) && (chosen->score > 0);
if ( !result ) {
engine_reset( engine );
}
return result; return result;
} /* chooseMove */ } /* chooseMove */
@ -381,7 +378,7 @@ normalizeIQ( EngineCtxt* engine, XP_U16 iq )
XP_Bool XP_Bool
engine_findMove( EngineCtxt* engine, const ModelCtxt* model, engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
XP_U16 turn, const Tile* tiles, XP_U16 turn, const Tile* tiles,
XP_U16 nTiles, XP_Bool usePrev, const XP_U16 nTiles, XP_Bool usePrev,
#ifdef XWFEATURE_BONUSALL #ifdef XWFEATURE_BONUSALL
XP_U16 allTilesBonus, XP_U16 allTilesBonus,
#endif #endif
@ -394,7 +391,9 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
XP_Bool result = XP_TRUE; XP_Bool result = XP_TRUE;
XP_U16 star_row; XP_U16 star_row;
XP_Bool canMove = XP_FALSE; XP_Bool canMove = XP_FALSE;
XP_Bool isRetry = XP_FALSE;
retry:
engine->nTilesMax = XP_MIN( MAX_TRAY_TILES, nTiles ); engine->nTilesMax = XP_MIN( MAX_TRAY_TILES, nTiles );
#ifdef XWFEATURE_BONUSALL #ifdef XWFEATURE_BONUSALL
engine->allTilesBonus = allTilesBonus; engine->allTilesBonus = allTilesBonus;
@ -542,6 +541,22 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
newMove->nTiles = 0; newMove->nTiles = 0;
} }
/* Gross hack alert: there's an elusive bug in move cacheing that means
when we move forward or back from the highest-scoring move to the
lowest (or vice-versa) no move is found. But the next try succeeds,
because an engine_reset clears the state that makes that happen. So as
a workaround, try doing that when no moves are found. If none is found
for some other reason, e.g. no tiles, at least the search should be
quick. */
if ( !canMove ) {
engine_reset( engine );
if ( !isRetry ) {
isRetry = XP_TRUE;
XP_LOGF( "%s: no moves found so retrying", __func__ );
goto retry;
}
}
*canMoveP = canMove; *canMoveP = canMove;
return result; return result;
} /* engine_findMove */ } /* engine_findMove */
@ -1146,23 +1161,24 @@ considerScoreWordHasBlanks( EngineCtxt* engine, XP_U16 blanksLeft,
static void static void
saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove ) saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove )
{ {
XP_S16 mostest = 0; XP_S16 mostest;
XP_S16 cmpVal; XP_S16 cmpVal;
XP_Bool usePrev = engine->usePrev; XP_Bool usePrev = engine->usePrev;
XP_Bool foundEmpty = XP_FALSE; XP_Bool foundEmpty = XP_FALSE;
MoveIterationData* miData = &engine->miData;
if ( 1 == engine->nMovesToSave ) { /* only saving one */ if ( 1 == engine->nMovesToSave ) { /* only saving one */
mostest = 0; mostest = 0;
} else { } else {
mostest = -1; mostest = -1;
/* we're not interested if we've seen this */ /* we're not interested if we've seen this */
cmpVal = CMPMOVES( posmove, &engine->miData.lastSeenMove ); cmpVal = CMPMOVES( posmove, &miData->lastSeenMove );
if ( !usePrev && cmpVal >= 0 ) { if ( !usePrev && cmpVal >= 0 ) {
/* XP_LOGF( "%s: dropping %d: higher than %d", __func__, */ /* XP_LOGF( "%s: dropping %d: >= %d", __func__, */
/* posmove->score, engine->miData.lastSeenMove.score ); */ /* posmove->score, miData->lastSeenMove.score ); */
} else if ( usePrev && cmpVal <= 0 ) { } else if ( usePrev && cmpVal <= 0 ) {
/* XP_LOGF( "%s: dropping %d: lower than %d", __func__, */ /* XP_LOGF( "%s: dropping %d: <= %d", __func__, */
/* posmove->score, engine->miData.lastSeenMove.score ); */ /* posmove->score, miData->lastSeenMove.score ); */
} else { } else {
XP_S16 ii; XP_S16 ii;
/* terminate i at 1 because mostest starts at 0 */ /* terminate i at 1 because mostest starts at 0 */
@ -1178,19 +1194,19 @@ saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove )
/* 1/20/2001 I don't see that this assertion is valid. I /* 1/20/2001 I don't see that this assertion is valid. I
simply don't understand why it isn't tripped all the time simply don't understand why it isn't tripped all the time
in the old crosswords. */ in the old crosswords. */
/* XP_ASSERT( (engine->miData.lastSeenMove.score == 0x7fff) */ /* XP_ASSERT( (miData->lastSeenMove.score == 0x7fff) */
/* || (engine->miData.savedMoves[i].score */ /* || (miData->savedMoves[i].score */
/* <= posmove->score) ); */ /* <= posmove->score) ); */
if ( 0 == engine->miData.savedMoves[ii].score ) { if ( 0 == miData->savedMoves[ii].score ) {
foundEmpty = XP_TRUE; foundEmpty = XP_TRUE;
mostest = ii; mostest = ii;
break; break;
} else if ( -1 == mostest ) { } else if ( -1 == mostest ) {
mostest = ii; mostest = ii;
} else { } else {
cmpVal = CMPMOVES( &engine->miData.savedMoves[mostest], cmpVal = CMPMOVES( &miData->savedMoves[mostest],
&engine->miData.savedMoves[ii] ); &miData->savedMoves[ii] );
if ( !usePrev && cmpVal > 0 ) { if ( !usePrev && cmpVal > 0 ) {
mostest = ii; mostest = ii;
} else if ( usePrev && cmpVal < 0 ) { } else if ( usePrev && cmpVal < 0 ) {
@ -1204,14 +1220,14 @@ saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove )
while ( mostest >= 0 ) { /* while: so we can break */ while ( mostest >= 0 ) { /* while: so we can break */
/* record the score we're dumping. No point in considering any scores /* record the score we're dumping. No point in considering any scores
lower than this for the rest of this round. */ lower than this for the rest of this round. */
/* engine->miData.lowestSavedScore = */ /* miData->lowestSavedScore = */
/* engine->miData.savedMoves[lowest].score; */ /* miData->savedMoves[lowest].score; */
/* XP_DEBUGF( "lowestSavedScore now %d\n", */ /* XP_DEBUGF( "lowestSavedScore now %d\n", */
/* engine->miData.lowestSavedScore ); */ /* miData->lowestSavedScore ); */
if ( foundEmpty ) { if ( foundEmpty ) {
/* we're good */ /* we're good */
} else { } else {
cmpVal = CMPMOVES( posmove, &engine->miData.savedMoves[mostest]); cmpVal = CMPMOVES( posmove, &miData->savedMoves[mostest]);
if ( !usePrev && cmpVal <= 0 ) { if ( !usePrev && cmpVal <= 0 ) {
break; break;
} else if ( usePrev && cmpVal >= 0 ) { } else if ( usePrev && cmpVal >= 0 ) {
@ -1220,9 +1236,9 @@ saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove )
} }
/* XP_LOGF( "saving move with score %d at %d (replacing %d)\n", */ /* XP_LOGF( "saving move with score %d at %d (replacing %d)\n", */
/* posmove->score, mostest, */ /* posmove->score, mostest, */
/* engine->miData.savedMoves[mostest].score ); */ /* miData->savedMoves[mostest].score ); */
XP_MEMCPY( &engine->miData.savedMoves[mostest], posmove, XP_MEMCPY( &miData->savedMoves[mostest], posmove,
sizeof(engine->miData.savedMoves[mostest]) ); sizeof(miData->savedMoves[mostest]) );
break; break;
} }
} /* saveMoveIfQualifies */ } /* saveMoveIfQualifies */
@ -1230,15 +1246,16 @@ saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove )
static void static void
set_search_limits( EngineCtxt* engine ) set_search_limits( EngineCtxt* engine )
{ {
MoveIterationData* miData = &engine->miData;
/* If we're going to be searching backwards we want our highest cached /* If we're going to be searching backwards we want our highest cached
move as the limit; otherwise the lowest */ move as the limit; otherwise the lowest */
if ( 0 < engine->miData.nInMoveCache ) { if ( 0 < miData->nInMoveCache ) {
XP_U16 srcIndx = engine->usePrev XP_U16 srcIndx = engine->usePrev
? engine->nMovesToSave-1 : engine->miData.bottom; ? engine->nMovesToSave-1 : miData->bottom;
XP_MEMCPY( &engine->miData.lastSeenMove, XP_MEMCPY( &miData->lastSeenMove,
&engine->miData.savedMoves[srcIndx], &miData->savedMoves[srcIndx],
sizeof(engine->miData.lastSeenMove) ); sizeof(miData->lastSeenMove) );
//engine->miData.lowestSavedScore = 0; //miData->lowestSavedScore = 0;
} else { } else {
/* we're doing this for first time */ /* we're doing this for first time */
engine_reset( engine ); engine_reset( engine );
@ -1249,41 +1266,40 @@ static void
init_move_cache( EngineCtxt* engine ) init_move_cache( EngineCtxt* engine )
{ {
XP_U16 nInMoveCache = engine->nMovesToSave; XP_U16 nInMoveCache = engine->nMovesToSave;
MoveIterationData* miData = &engine->miData;
XP_U16 ii; XP_U16 ii;
XP_ASSERT( engine->nMovesToSave == NUM_SAVED_ENGINE_MOVES ); XP_ASSERT( engine->nMovesToSave == NUM_SAVED_ENGINE_MOVES );
for ( ii = 0; ii < NUM_SAVED_ENGINE_MOVES; ++ii ) { for ( ii = 0; ii < NUM_SAVED_ENGINE_MOVES; ++ii ) {
if ( 0 == engine->miData.savedMoves[ii].score ) { if ( 0 == miData->savedMoves[ii].score ) {
--nInMoveCache; --nInMoveCache;
} else { } else {
break; break;
} }
} }
engine->miData.nInMoveCache = nInMoveCache; miData->nInMoveCache = nInMoveCache;
engine->miData.bottom = NUM_SAVED_ENGINE_MOVES - nInMoveCache; miData->bottom = NUM_SAVED_ENGINE_MOVES - nInMoveCache;
if ( engine->usePrev ) { miData->curCacheIndex = engine->usePrev
engine->miData.curCacheIndex = ? NUM_SAVED_ENGINE_MOVES - nInMoveCache - 1
NUM_SAVED_ENGINE_MOVES - nInMoveCache - 1; : NUM_SAVED_ENGINE_MOVES;
} else {
engine->miData.curCacheIndex = NUM_SAVED_ENGINE_MOVES;
}
} }
static PossibleMove* static PossibleMove*
next_from_cache( EngineCtxt* engine ) next_from_cache( EngineCtxt* engine )
{ {
MoveIterationData* miData = &engine->miData;
PossibleMove* move; PossibleMove* move;
if ( move_cache_empty( engine ) ) { if ( move_cache_empty( engine ) ) {
move = NULL; move = NULL;
} else { } else {
if ( engine->usePrev ) { if ( engine->usePrev ) {
++engine->miData.curCacheIndex; ++miData->curCacheIndex;
} else { } else {
--engine->miData.curCacheIndex; --miData->curCacheIndex;
} }
move = &engine->miData.savedMoves[engine->miData.curCacheIndex]; move = &miData->savedMoves[miData->curCacheIndex];
} }
return move; return move;
} }
@ -1309,21 +1325,22 @@ scoreQualifies( EngineCtxt* engine, XP_U16 score )
{ {
XP_Bool qualifies = XP_FALSE; XP_Bool qualifies = XP_FALSE;
XP_Bool usePrev = engine->usePrev; XP_Bool usePrev = engine->usePrev;
MoveIterationData* miData = &engine->miData;
if ( usePrev && score < engine->miData.lastSeenMove.score ) { if ( usePrev && score < miData->lastSeenMove.score ) {
/* drop it */ /* drop it */
} else if ( !usePrev && score > engine->miData.lastSeenMove.score } else if ( !usePrev && score > miData->lastSeenMove.score
/* || (score < engine->miData.lowestSavedScore) */ ) { /* || (score < miData->lowestSavedScore) */ ) {
/* drop it */ /* drop it */
} else { } else {
XP_S16 ii; XP_S16 ii;
PossibleMove* savedMoves = engine->miData.savedMoves; PossibleMove* savedMoves = miData->savedMoves;
/* Look at each saved score, and return true as soon as one's found /* Look at each saved score, and return true as soon as one's found
with a lower or equal score to this. <eeh> As an optimization, with a lower or equal score to this. <eeh> As an optimization,
consider remembering what the lowest score is *once there are consider remembering what the lowest score is *once there are
NUM_SAVED_ENGINE_MOVES moves in here* and doing a quick test on NUM_SAVED_ENGINE_MOVES moves in here* and doing a quick test on
that. Or better, keeping the list in sorted order. */ that. Or better, keeping the list in sorted order. */
for ( ii = 0, savedMoves = engine->miData.savedMoves; for ( ii = 0, savedMoves = miData->savedMoves;
ii < engine->nMovesToSave; ++ii, ++savedMoves ) { ii < engine->nMovesToSave; ++ii, ++savedMoves ) {
if ( savedMoves->score == 0 ) { /* empty slot */ if ( savedMoves->score == 0 ) { /* empty slot */
qualifies = XP_TRUE; qualifies = XP_TRUE;

View file

@ -2119,7 +2119,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
/* send any events that need to get off before the event loop begins */ /* send any events that need to get off before the event loop begins */
if ( !isServer ) { if ( !!cGlobals->game.comms && !isServer ) {
(void)server_initClientConnection( cGlobals->game.server, (void)server_initClientConnection( cGlobals->game.server,
mem_stream_make( MEMPOOL mem_stream_make( MEMPOOL
params->vtMgr, params->vtMgr,