Add ability to open utf8-formatted dicts and to display bitmap tiles

(though poorly.)  Both required processing in the jni that isn't
possible (e.g. because no libiconv included) so I created callbacks
into java from the dict building code.  Add ability to download dicts,
to select them, to add and remove players and make them robots or
human.  Robot-vs-robot game doesn't work well (robots trade a lot and
server_do seems not to be getting called enough) but will soon.
Coalesce penMove events.  Implement game list menuitems like delete,
copy, etc.
This commit is contained in:
ehouse 2010-01-25 02:49:14 +00:00
parent 676fd73fc0
commit 68079d3590
21 changed files with 785 additions and 376 deletions

View file

@ -29,7 +29,6 @@
android:layout_marginRight="20dip"
android:text="@string/players_label"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<Button android:id="@+id/add_player"
@ -45,12 +44,27 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"/>
android:drawSelectorOnTop="false"
/>
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/dict_label"
/>
<Spinner android:id="@+id/dict_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="true"
/>
<Button android:id="@+id/game_config_done"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/game_config_done" />
android:text="@string/button_save"
/>
</LinearLayout>

View file

@ -14,64 +14,70 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/screen"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<CheckBox android:id="@+id/remote_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
android:text="@string/remote_label"
/>
<CheckBox android:id="@+id/remote_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/remote_label"
/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/player_label"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<EditText android:id="@+id/player_name_edit"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:gravity="fill_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/player_label"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<EditText android:id="@+id/player_edit"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:gravity="fill_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<CheckBox android:id="@+id/robot_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/robot_label"
/>
<CheckBox android:id="@+id/robot_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/robot_label"
/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/password_label"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<EditText android:id="@+id/password_edit"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:gravity="fill_horizontal"
android:password="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/password_label"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<EditText android:id="@+id/password_edit"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:gravity="fill_horizontal"
android:password="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -13,22 +13,22 @@
<item android:id="@+id/list_item_new_from"
android:title="@string/list_item_new_from"
/>
<item android:id="@+id/list_item_hide"
android:title="@string/list_item_hide"
/>
<!-- <item android:id="@+id/list_item_hide" -->
<!-- android:title="@string/list_item_hide" -->
<!-- /> -->
<item android:id="@+id/list_item_delete"
android:title="@string/list_item_delete"
/>
<item android:id="@+id/list_item_move_up"
android:title="@string/list_item_move_up"
/>
<item android:id="@+id/list_item_move_down"
android:title="@string/list_item_move_down"
/>
<item android:id="@+id/list_item_move_to_top"
android:title="@string/list_item_move_to_top"
/>
<item android:id="@+id/list_item_move_to_bottom"
android:title="@string/list_item_move_to_bottom"
/>
<!-- <item android:id="@+id/list_item_move_up" -->
<!-- android:title="@string/list_item_move_up" -->
<!-- /> -->
<!-- <item android:id="@+id/list_item_move_down" -->
<!-- android:title="@string/list_item_move_down" -->
<!-- /> -->
<!-- <item android:id="@+id/list_item_move_to_top" -->
<!-- android:title="@string/list_item_move_to_top" -->
<!-- /> -->
<!-- <item android:id="@+id/list_item_move_to_bottom" -->
<!-- android:title="@string/list_item_move_to_bottom" -->
<!-- /> -->
</menu>

View file

@ -22,7 +22,7 @@
<string name="menu_discard">Discard</string>
<string name="list_item_open">Open</string>
<string name="list_item_view">View me</string>
<string name="list_item_view">Configure</string>
<string name="list_item_hide">Hide</string>
<string name="list_item_delete">Delete</string>
<string name="list_item_copy">Copy</string>
@ -49,6 +49,7 @@
<string name="button_cancel">Cancel</string>
<string name="button_yes">Yes</string>
<string name="button_no">No</string>
<string name="button_save">Save</string>
<string name="player_label">Name:</string>
<string name="game_config_open">Open</string>
@ -115,11 +116,11 @@
<string name="strsd_summaryscored"></string>
<string name="strd_traded">%s:%d</string>
<string name="str_lostturn">Lost turn</string>
<string name="str_commit_confirm">Commit the current move?</string>
<string name="str_commit_confirm">Commit the current move?\n</string>
<string name="str_local_name">%s</string>
<string name="str_nonlocal_name">%s (remote)</string>
<string name="str_bonus_all">Bonus for using all tiles: 50</string>
<string name="strd_turn_score">Score for turn: %d</string>
<string name="str_bonus_all">Bonus for using all tiles: 50\n</string>
<string name="strd_turn_score">Score for turn: %d\n</string>
<!-- error messages -->
<string name="str_tiles_not_in_line">All tiles played must be in a line.</string>
@ -150,4 +151,9 @@
<string name="list_item_up">Up one</string>
<string name="list_item_down">Down one</string>
<string name="dict_label">Dictionary</string>
<string name="download_dicts">Download more...</string>
<string name="dict_url">http://eehouse.org/and_dicts</string>
</resources>

View file

@ -8,6 +8,7 @@ import android.content.Intent;
import android.app.Dialog;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.Configuration;
public class BlockingActivity extends Activity {
@ -32,37 +33,41 @@ public class BlockingActivity extends Activity {
.setCancelable( false );
if ( 0 != m_butPos ) {
ab.setPositiveButton( m_butPos,
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
Utils.logf( "Yes clicked" );
setResult( 1 );
finish();
}
});
DialogInterface.OnClickListener lstnr
= new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
Utils.logf( "Yes clicked" );
setResult( 1 );
finish();
}
};
ab.setPositiveButton( m_butPos, lstnr );
}
if ( 0 != m_butNeg ) {
ab.setNegativeButton( m_butNeg,
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
Utils.logf( "No clicked" );
setResult( 0 );
finish();
}
});
DialogInterface.OnClickListener lstnr =
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
Utils.logf( "No clicked" );
setResult( 0 );
finish();
}
};
ab.setNegativeButton( m_butNeg, lstnr );
}
if ( null != m_tiles ) {
Utils.logf( "adding m_tiles; len=" + m_tiles.length );
ab.setItems( m_tiles, new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog, int item ) {
DialogInterface.OnClickListener lstnr =
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
setResult( RESULT_FIRST_USER + item );
finish();
}
});
};
ab.setItems( m_tiles, lstnr );
} else if ( null != m_query ) {
ab.setMessage( m_query );
}

View file

@ -9,10 +9,12 @@ import android.view.MenuItem;
import android.view.MenuInflater;
import android.content.res.AssetManager;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.os.Handler;
import android.os.Message;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ByteArrayInputStream;
import android.content.res.Configuration;
import android.content.Intent;
import java.util.concurrent.Semaphore;
@ -20,6 +22,10 @@ import android.net.Uri;
import android.app.Dialog;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
import java.util.ArrayList;
import android.content.res.Resources;
import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.jni.JNIThread.*;
@ -100,7 +106,8 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
}
@Override
protected void onCreate( Bundle savedInstanceState ) {
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.board );
@ -122,60 +129,44 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
byte[] stream = Utils.savedGame( this, m_path );
XwJNI.gi_from_stream( m_gi, stream );
byte[] dictBytes = null;
InputStream dict = null;
AssetManager am = getAssets();
try {
dict = am.open( m_gi.dictName,
android.content.res.AssetManager.ACCESS_RANDOM );
Utils.logf( "opened dict" );
Utils.logf( "dict name: " + m_gi.dictName );
byte[] dictBytes = Utils.openDict( this, m_gi.dictName );
if ( null == dictBytes ) {
Utils.logf( "**** unable to open dict; warn user! ****" );
finish();
} else {
m_jniGamePtr = XwJNI.initJNI();
int len = dict.available();
Utils.logf( "dict size: " + len );
dictBytes = new byte[len];
int nRead = dict.read( dictBytes, 0, len );
if ( nRead != len ) {
Utils.logf( "**** warning ****; read only " + nRead + " of "
+ len + " bytes." );
if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
m_gi, dictBytes, this,
m_view, m_prefs,
null ) ) {
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, this, m_view, 0,
m_prefs, null, dictBytes );
}
} catch ( java.io.IOException ee ){
Utils.logf( "failed to open" );
}
m_jniGamePtr = XwJNI.initJNI();
if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
m_gi, dictBytes, this,
m_view, m_prefs,
null ) ) {
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, this, m_view, 0,
m_prefs, null, dictBytes );
}
m_jniThread = new
JNIThread( m_jniGamePtr,
new Handler() {
public void handleMessage( Message msg ) {
Utils.logf( "handleMessage() called" );
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;
m_jniThread = new
JNIThread( m_jniGamePtr,
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;
}
}
}
} );
m_jniThread.start();
} );
m_jniThread.start();
m_view.startHandling( m_jniThread, m_jniGamePtr, m_gi );
m_jniThread.handle( JNICmd.CMD_DO );
Utils.logf( "BoardActivity::onCreate() done" );
m_view.startHandling( m_jniThread, m_jniGamePtr, m_gi );
m_jniThread.handle( JNICmd.CMD_DO );
}
} // onCreate
// protected void onPause() {
@ -186,19 +177,21 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
protected void onDestroy()
{
// what if m_jniThread is null?
m_jniThread.waitToStop();
if ( null != m_jniThread ) {
m_jniThread.waitToStop();
Utils.logf( "onDestroy(): waitToStop() returned" );
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi );
Utils.saveGame( this, state, m_path );
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi );
Utils.saveGame( this, state, m_path );
XwJNI.game_dispose( m_jniGamePtr );
m_jniGamePtr = 0;
XwJNI.game_dispose( m_jniGamePtr );
m_jniGamePtr = 0;
}
super.onDestroy();
Utils.logf( "onDestroy done" );
}
protected void onActivityResult( int requestCode, int resultCode,
Intent result )
{
@ -287,11 +280,12 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
// gets called for orientation changes only if
// android:configChanges="orientation" set in AndroidManifest.xml
// public void onConfigurationChanged( Configuration newConfig )
// {
// super.onConfigurationChanged( newConfig );
// }
public void onConfigurationChanged( Configuration newConfig )
{
Utils.logf( "BoardActivity::onConfigurationChanged called" );
m_view.changeLayout();
super.onConfigurationChanged( newConfig );
}
//////////////////////////////////////////
// XW_UtilCtxt interface implementation //
@ -569,4 +563,62 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
R.string.finalscores_title );
}
public BitmapDrawable makeBitmap( int width, int height, boolean[] colors )
{
Bitmap bitmap = Bitmap.createBitmap( width, height,
Bitmap.Config.ARGB_8888 );
int indx = 0;
for ( int yy = 0; yy < height; ++yy ) {
for ( int xx = 0; xx < width; ++xx ) {
boolean pixelSet = colors[indx++];
bitmap.setPixel( xx, yy, pixelSet? 0xFF000000 : 0x00FFFFFF );
}
}
// Doesn't compile if pass getResources(). Maybe the
// "deprecated" API is really the only one?
return new BitmapDrawable( /*getResources(), */bitmap );
}
/** Working around lack of utf8 support on the JNI side: given a
* utf-8 string with embedded small number vals starting with 0,
* convert into individual strings. The 0 is the problem: it's
* not valid utf8. So turn it and the other nums into strings and
* catch them on the other side.
*/
public String[] splitFaces( byte[] chars )
{
ArrayList<String> al = new ArrayList<String>();
int ii = 0;
ByteArrayInputStream bais = new ByteArrayInputStream( chars );
InputStreamReader isr = new InputStreamReader( bais );
int[] codePoints = new int[1];
for ( ; ; ) {
int chr = -1;
try {
chr = isr.read();
} catch ( java.io.IOException ioe ) {
Utils.logf( ioe.toString() );
}
if ( -1 == chr ) {
break;
} else {
String letter;
if ( chr < 32 ) {
letter = String.format( "%d", chr );
} else {
codePoints[0] = chr;
letter = new String( codePoints, 0, 1 );
}
al.add( letter );
}
}
String[] result = al.toArray( new String[al.size()] );
return result;
}
} // class BoardActivity

View file

@ -12,6 +12,7 @@ import android.util.AttributeSet;
import org.eehouse.android.xw4.jni.*;
import android.view.MotionEvent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
import android.content.res.Resources;
public class BoardView extends View implements DrawCtx,
@ -163,23 +164,6 @@ public class BoardView extends View implements DrawCtx,
m_top = 0;
}
// XwJNI.board_setScoreboardLoc( m_jniGamePtr, 0, 0,
// nCells * cellSize, // width
// scoreHt, true );
// XwJNI.board_setPos( m_jniGamePtr, 0, scoreHt, false );
// XwJNI.board_setScale( m_jniGamePtr, cellSize, cellSize );
// XwJNI.board_setTrayLoc( m_jniGamePtr, 0,
// scoreHt + ((nCells-nToScroll) * cellSize),
// nCells * cellSize, // width
// trayHt, // height
// 4 );
// XwJNI.board_setShowColors( m_jniGamePtr, true ); // get from prefs!
// XwJNI.board_invalAll( m_jniGamePtr );
m_bitmap = Bitmap.createBitmap( 1 + (cellSize*nCells),
1 + trayHt + scoreHt
+ (cellSize *(nCells-nToScroll)),
@ -194,6 +178,11 @@ public class BoardView extends View implements DrawCtx,
return m_boardSet;
}
public void changeLayout()
{
m_boardSet = false;
}
public void startHandling( JNIThread thread, int gamePtr, CurGameInfo gi )
{
m_jniThread = thread;
@ -269,7 +258,7 @@ public class BoardView extends View implements DrawCtx,
drawCentered( text, rOuter );
}
public boolean drawCell( Rect rect, String text, Object[] bitmaps,
public boolean drawCell( Rect rect, String text, BitmapDrawable[] bitmaps,
int tile, int owner, int bonus, int hintAtts,
int flags )
{
@ -298,12 +287,14 @@ public class BoardView extends View implements DrawCtx,
m_origin.draw( m_canvas );
}
} else {
m_fillPaint.setTextSize( rect.bottom - rect.top );
if ( owner < 0 ) { // showColors option not turned on
owner = 0;
}
m_fillPaint.setColor( foreColor );
drawCentered( text, rect );
if ( null == bitmaps ) {
m_fillPaint.setTextSize( rect.bottom - rect.top );
drawCentered( text, rect );
} else {
bitmaps[0].setBounds( rect );
bitmaps[0].draw( m_canvas );
}
}
m_canvas.drawRect( rect, m_strokePaint );
@ -331,43 +322,21 @@ public class BoardView extends View implements DrawCtx,
return true;
}
public void drawTile( Rect rect, String text, Object[] bitmaps, int val,
public void drawTile( Rect rect, String text, BitmapDrawable[] bitmaps, int val,
int flags )
{
boolean valHidden = (flags & CELL_VALHIDDEN) != 0;
boolean notEmpty = (flags & CELL_ISEMPTY) == 0;
boolean isCursor = (flags & CELL_ISCURSOR) != 0;
m_canvas.save( Canvas.CLIP_SAVE_FLAG );
m_canvas.clipRect( rect );
clearToBack( rect );
if ( isCursor || notEmpty ) {
m_fillPaint.setColor( TILE_BACK );
m_canvas.drawRect( rect, m_fillPaint );
m_fillPaint.setColor( m_playerColors[m_trayOwner] );
positionDrawTile( rect, text, val );
m_canvas.drawRect( rect, m_tileStrokePaint); // frame
if ( 0 != (flags & CELL_HIGHLIGHT) ) {
rect.inset( 2, 2 );
m_canvas.drawRect( rect, m_tileStrokePaint ); // frame
}
}
m_canvas.restore();
drawTileImpl( rect, text, bitmaps, val, flags, true );
}
public void drawTileMidDrag ( Rect rect, String text, Object[] bitmaps,
public void drawTileMidDrag( Rect rect, String text, BitmapDrawable[] bitmaps,
int val, int owner, int flags )
{
drawTile( rect, text, bitmaps, val, flags );
drawTileImpl( rect, text, bitmaps, val, flags, false );
}
public void drawTileBack( Rect rect, int flags )
{
drawTile( rect, "?", null, -1, flags );
drawTileImpl( rect, "?", null, -1, flags, true );
}
public void drawTrayDivider( Rect rect, int flags )
@ -394,6 +363,40 @@ public class BoardView extends View implements DrawCtx,
}
}
private void drawTileImpl( Rect rect, String text,
BitmapDrawable[] bitmaps, int val,
int flags, boolean clearBack )
{
boolean valHidden = (flags & CELL_VALHIDDEN) != 0;
boolean notEmpty = (flags & CELL_ISEMPTY) == 0;
boolean isCursor = (flags & CELL_ISCURSOR) != 0;
m_canvas.save( Canvas.CLIP_SAVE_FLAG );
m_canvas.clipRect( rect );
if ( clearBack ) {
clearToBack( rect );
}
if ( isCursor || notEmpty ) {
if ( clearBack ) {
m_fillPaint.setColor( TILE_BACK );
m_canvas.drawRect( rect, m_fillPaint );
}
m_fillPaint.setColor( m_playerColors[m_trayOwner] );
positionDrawTile( rect, text, bitmaps, val );
m_canvas.drawRect( rect, m_tileStrokePaint); // frame
if ( 0 != (flags & CELL_HIGHLIGHT) ) {
rect.inset( 2, 2 );
m_canvas.drawRect( rect, m_tileStrokePaint ); // frame
}
}
m_canvas.restore();
}
private void drawCentered( String text, Rect rect )
{
int bottom = rect.bottom;
@ -401,17 +404,23 @@ public class BoardView extends View implements DrawCtx,
m_canvas.drawText( text, center, bottom, m_fillPaint );
}
private void positionDrawTile( Rect rect, String text, int val )
private void positionDrawTile( Rect rect, String text,
BitmapDrawable bitmaps[], int val )
{
if ( null != text ) {
if ( null != bitmaps || null != text ) {
if ( null == m_letterRect ) {
// assumes show values is on
m_letterRect = new Rect( 0, 0, rect.width() * 3 / 4,
rect.height() * 3 / 4 );
}
m_letterRect.offsetTo( rect.left, rect.top );
m_fillPaint.setTextSize( m_letterRect.height() );
drawCentered( text, m_letterRect );
if ( null != bitmaps ) {
bitmaps[0].setBounds( m_letterRect );
bitmaps[0].draw( m_canvas );
} else /*if ( null != text )*/ {
m_fillPaint.setTextSize( m_letterRect.height() );
drawCentered( text, m_letterRect );
}
}
if ( val >= 0 ) {

View file

@ -36,6 +36,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ListView;
import android.widget.ListAdapter;
import android.view.View;
@ -57,6 +58,10 @@ import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuInflater;
import android.widget.Spinner;
import android.widget.ArrayAdapter;
import android.webkit.WebView;
import java.io.File;
import org.eehouse.android.xw4.jni.*;
@ -76,6 +81,9 @@ public class GameConfig extends ListActivity implements View.OnClickListener {
private CurGameInfo m_gi;
private int m_whichPlayer;
private Dialog m_curDialog;
private Spinner m_dictSpinner;
private String[] m_dicts;
private int m_browsePosition;
private class PlayerListAdapter implements ListAdapter {
public boolean areAllItemsEnabled() {
@ -140,24 +148,20 @@ public class GameConfig extends ListActivity implements View.OnClickListener {
final View playerEditView
= factory.inflate( R.layout.player_edit, null );
DialogInterface.OnClickListener dlpos =
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
getPlayerSettings();
onContentChanged();
}
};
return new AlertDialog.Builder( this )
// .setIcon(R.drawable.alert_dialog_icon)
.setTitle(R.string.player_edit_title)
.setView(playerEditView)
.setPositiveButton(R.string.button_ok,
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
getPlayerSettings();
onContentChanged();
}
})
.setNegativeButton(R.string.button_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
}
})
.setPositiveButton(R.string.button_save, dlpos )
.create();
}
return null;
@ -174,36 +178,30 @@ public class GameConfig extends ListActivity implements View.OnClickListener {
private void setPlayerSettings()
{
LocalPlayer lp = m_gi.players[m_whichPlayer];
EditText player = (EditText)m_curDialog.findViewById( R.id.player_edit );
EditText player = (EditText)
m_curDialog.findViewById( R.id.player_name_edit );
player.setText( lp.name );
CheckBox isRobot = (CheckBox)m_curDialog.findViewById( R.id.robot_check );
CheckBox isRobot = (CheckBox)
m_curDialog.findViewById( R.id.robot_check );
isRobot.setChecked( lp.isRobot );
}
private void getPlayerSettings()
{
LocalPlayer lp = m_gi.players[m_whichPlayer];
EditText player = (EditText)m_curDialog.findViewById( R.id.player_edit );
EditText player = (EditText)
m_curDialog.findViewById( R.id.player_name_edit );
lp.name = player.getText().toString();
CheckBox isRobot = (CheckBox)m_curDialog.findViewById( R.id.robot_check );
CheckBox isRobot = (CheckBox)
m_curDialog.findViewById( R.id.robot_check );
lp.isRobot = isRobot.isChecked();
}
@Override
protected void onCreate( Bundle savedInstanceState )
public void onCreate( Bundle savedInstanceState )
{
super.onCreate(savedInstanceState);
setContentView(R.layout.game_config);
registerForContextMenu( getListView() );
mDoneB = (Button)findViewById(R.id.game_config_done);
mDoneB.setOnClickListener( this );
m_addPlayerButton = (Button)findViewById(R.id.add_player);
m_addPlayerButton.setOnClickListener( this );
// Do some setup based on the action being performed.
Intent intent = getIntent();
Uri uri = intent.getData();
m_path = uri.getPath();
@ -214,6 +212,47 @@ public class GameConfig extends ListActivity implements View.OnClickListener {
byte[] stream = Utils.savedGame( this, m_path );
m_gi = new CurGameInfo();
XwJNI.gi_from_stream( m_gi, stream );
int curSel = listAvailableDicts( m_gi.dictName );
Utils.logf( "listAvailableDicts done" );
setContentView(R.layout.game_config);
registerForContextMenu( getListView() );
mDoneB = (Button)findViewById(R.id.game_config_done);
mDoneB.setOnClickListener( this );
m_addPlayerButton = (Button)findViewById(R.id.add_player);
m_addPlayerButton.setOnClickListener( this );
m_dictSpinner = (Spinner)findViewById( R.id.dict_spinner );
ArrayAdapter<String> adapter =
new ArrayAdapter<String>( this,
android.R.layout.simple_spinner_item,
m_dicts );
int resID = android.R.layout.simple_spinner_dropdown_item;
adapter.setDropDownViewResource( resID );
m_dictSpinner.setAdapter( adapter );
if ( curSel >= 0 ) {
m_dictSpinner.setSelection( curSel );
}
m_dictSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView,
View selectedItemView, int position,
long id) {
if ( position == m_browsePosition ) {
launchDictBrowser();
} else {
m_gi.dictName = m_dicts[position];
Utils.logf( "assigned dictName: " + m_gi.dictName );
}
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
}
});
setListAdapter( new PlayerListAdapter() );
} // onCreate
@ -316,4 +355,32 @@ public class GameConfig extends ListActivity implements View.OnClickListener {
}
} // onClick
private int listAvailableDicts( String curDict )
{
int curSel = -1;
String[] list = Utils.listDicts( this );
m_browsePosition = list.length;
m_dicts = new String[m_browsePosition+1];
m_dicts[m_browsePosition] = getString( R.string.download_dicts );
for ( int ii = 0; ii < m_browsePosition; ++ii ) {
String dict = list[ii];
m_dicts[ii] = dict;
if ( dict.equals( curDict ) ) {
curSel = ii;
}
}
return curSel;
}
private void launchDictBrowser()
{
Intent intent = new Intent( this, DictActivity.class );
intent.setAction( Intent.ACTION_EDIT );
startActivity( intent );
}
}

View file

@ -91,7 +91,8 @@ public class GamesList extends ListActivity implements View.OnClickListener {
@Override
public boolean onContextItemSelected( MenuItem item )
{
boolean handled = false;
boolean handled = true;
byte[] stream;
AdapterView.AdapterContextMenuInfo info;
try {
@ -100,6 +101,8 @@ public class GamesList extends ListActivity implements View.OnClickListener {
Utils.logf( "bad menuInfo:" + e.toString() );
return false;
}
String path = fileList()[info.position];
switch (item.getItemId()) {
// case R.id.list_item_open:
@ -107,24 +110,47 @@ public class GamesList extends ListActivity implements View.OnClickListener {
// handled = true;
// break;
case R.id.list_item_view:
doView( info.position );
handled = true;
doView( path );
break;
case R.id.list_item_delete:
if ( ! deleteFile( path ) ) {
Utils.logf( "unable to delete " + path );
}
break;
case R.id.list_item_hide:
case R.id.list_item_delete:
case R.id.list_item_copy:
stream = Utils.savedGame( this, path );
Utils.saveGame( this, stream );
break;
case R.id.list_item_new_from:
case R.id.list_item_move_up:
case R.id.list_item_move_down:
case R.id.list_item_move_to_top:
case R.id.list_item_move_to_bottom:
handled = true;
Utils.notImpl( this );
stream = Utils.savedGame( this, path );
CurGameInfo gi = new CurGameInfo();
XwJNI.gi_from_stream( gi, stream );
stream = XwJNI.gi_to_stream( gi );
Utils.saveGame( this, stream );
break;
// These require some notion of predictable sort order.
// Maybe put off until I'm using a db?
// case R.id.list_item_hide:
// case R.id.list_item_move_up:
// case R.id.list_item_move_down:
// case R.id.list_item_move_to_top:
// case R.id.list_item_move_to_bottom:
// Utils.notImpl( this );
// break;
default:
handled = false;
break;
}
if ( handled ) {
onContentChanged();
}
return handled;
}
} // onContextItemSelected
public boolean onCreateOptionsMenu( Menu menu )
{
@ -139,8 +165,13 @@ public class GamesList extends ListActivity implements View.OnClickListener {
switch (item.getItemId()) {
case R.id.gamel_menu_delete_all:
for( String file : fileList() ) {
deleteFile( file );
String[] files = fileList();
for( String file : files ) {
if ( deleteFile( file ) ) {
Utils.logf( "deleted " + file );
} else {
Utils.logf( "unable to delete " + file );
}
}
m_adapter = new GameListAdapter( this );
setListAdapter( m_adapter );
@ -172,42 +203,20 @@ public class GamesList extends ListActivity implements View.OnClickListener {
startActivity( intent );
}
private void doView( int indx ) {
String path = fileList()[indx];
File file = new File( path );
Uri uri = Uri.fromFile( file );
private void doView( String path )
{
Uri uri = Uri.fromFile( new File(path) );
Intent intent = new Intent( Intent.ACTION_EDIT, uri,
GamesList.this, GameConfig.class );
startActivity( intent );
}
private String newName()
{
String name = null;
Integer num = 0;
int ii;
String[] files = fileList();
while ( name == null ) {
name = "game " + num.toString();
for ( ii = 0; ii < files.length; ++ii ) {
Utils.logf( "comparing " + name + " with " + files[ii] );
if ( files[ii].equals(name) ) {
++num;
name = null;
}
}
}
return name;
}
private void saveNew( CurGameInfo gi )
{
byte[] bytes = XwJNI.gi_to_stream( gi );
if ( null != bytes ) {
String name = newName();
Utils.saveGame( this, bytes, newName() );
Utils.saveGame( this, bytes );
} else {
Utils.logf( "gi_to_stream=>null" );
}

View file

@ -7,8 +7,13 @@ import java.lang.Thread;
import java.text.MessageFormat;
import android.widget.Toast;
import android.content.Context;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import android.content.res.AssetManager;
import android.os.Environment;
import java.io.InputStream;
public class Utils {
static final String TAG = "EJAVA";
@ -43,7 +48,6 @@ public class Utils {
try {
FileInputStream in = context.openFileInput( path );
int len = in.available();
Utils.logf( "savedGame: got " + len + " bytes." );
stream = new byte[len];
in.read( stream, 0, len );
in.close();
@ -69,5 +73,107 @@ public class Utils {
}
}
public static void saveGame( Context context, byte[] bytes )
{
saveGame( context, bytes, newName( context ) );
}
public static String newName( Context context )
{
String name = null;
Integer num = 0;
int ii;
String[] files = context.fileList();
while ( name == null ) {
name = "game " + num.toString();
for ( ii = 0; ii < files.length; ++ii ) {
if ( files[ii].equals(name) ) {
++num;
name = null;
}
}
}
return name;
}
private static void tryFile( ArrayList<String> al, String name )
{
if ( name.endsWith( ".xwd" ) ) {
al.add( name );
}
}
private static void tryDir( ArrayList<String> al, File dir )
{
for ( File file: dir.listFiles() ) {
tryFile( al, file.getAbsolutePath() );
}
}
public static String[] listDicts( Context context )
{
ArrayList<String> al = new ArrayList<String>();
try {
AssetManager am = context.getAssets();
String[] files = am.list("");
for ( String file : files ) {
tryFile( al, file );
}
} catch( java.io.IOException ioe ) {
Utils.logf( ioe.toString() );
}
File files[] = Environment.getExternalStorageDirectory().listFiles();
for ( File file : files ) {
if ( file.isDirectory() ) { // go down one level
tryDir( al, file );
} else {
tryFile( al, file.getAbsolutePath() );
}
}
return al.toArray( new String[al.size()] );
}
public static byte[] openDict( Context context, String name )
{
byte[] bytes = null;
InputStream dict = null;
AssetManager am = context.getAssets();
try {
dict = am.open( name,
android.content.res.AssetManager.ACCESS_RANDOM );
int len = dict.available();
bytes = new byte[len];
int nRead = dict.read( bytes, 0, len );
if ( nRead != len ) {
Utils.logf( "**** warning ****; read only " + nRead + " of "
+ len + " bytes." );
}
} catch ( java.io.IOException ee ){
Utils.logf( "failed to open" );
}
// not an asset? Try storage
if ( null == bytes ) {
try {
FileInputStream fis = new FileInputStream( new File(name) );
int len = fis.available();
bytes = new byte[len];
fis.read( bytes, 0, len );
fis.close();
} catch ( java.io.FileNotFoundException fnf ) {
Utils.logf( fnf.toString() );
} catch ( java.io.IOException ioe ) {
Utils.logf( ioe.toString() );
}
}
return bytes;
}
}

View file

@ -3,6 +3,7 @@
package org.eehouse.android.xw4.jni;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
public interface DrawCtx {
static final int CELL_NONE = 0x00;
@ -27,18 +28,18 @@ public interface DrawCtx {
int dfs );
void measureRemText( Rect r, int nTilesLeft, int[] width, int[] height );
void measureScoreText( Rect r, DrawScoreInfo dsi, int[] width, int[] height );
void drawRemText( Rect rInner,Rect rOuter, int nTilesLeft, boolean focussed );
void drawRemText( Rect rInner, Rect rOuter, int nTilesLeft, boolean focussed );
void score_drawPlayer( Rect rInner, Rect rOuter, DrawScoreInfo dsi );
boolean drawCell( Rect rect, String text, Object[] bitmaps, int tile,
boolean drawCell( Rect rect, String text, BitmapDrawable[] bitmaps, int tile,
int owner, int bonus, int hintAtts, int flags );
void drawBoardArrow ( Rect rect, int bonus, boolean vert, int hintAtts,
int flags );
boolean vertScrollBoard( Rect /*out*/ rect, int dist, int dfs );
boolean trayBegin ( Rect rect, int owner, int dfs );
void drawTile( Rect rect, String text, Object[] bitmaps, int val, int flags );
void drawTileMidDrag ( Rect rect, String text, Object[] bitmaps,
void drawTile( Rect rect, String text, BitmapDrawable[] bitmaps, int val, int flags );
void drawTileMidDrag ( Rect rect, String text, BitmapDrawable[] bitmaps,
int val, int owner, int flags );
void drawTileBack( Rect rect, int flags );
void drawTrayDivider( Rect rect, int flags );

View file

@ -66,7 +66,7 @@ public class JNIThread extends Thread {
m_stopped = true;
handle( JNICmd.CMD_NONE ); // tickle it
try {
join();
join(100); // wait up to 1/10 second
} catch ( java.lang.InterruptedException ie ) {
Utils.logf( "got InterruptedException: " + ie.toString() );
}
@ -176,6 +176,11 @@ public class JNIThread extends Thread {
barr );
break;
case CMD_PEN_MOVE:
QueueElem nextElem = m_queue.peek();
if ( null != nextElem && nextElem.m_cmd == JNICmd.CMD_PEN_MOVE ) {
Utils.logf( "dropping CMD_PEN_MOVE" );
continue;
}
draw = XwJNI.board_handlePenMove( m_jniGamePtr,
((Integer)args[0]).intValue(),
((Integer)args[1]).intValue() );

View file

@ -2,6 +2,8 @@
package org.eehouse.android.xw4.jni;
import android.graphics.drawable.BitmapDrawable;
public interface XW_UtilCtxt {
static final int BONUS_NONE = 0;
static final int BONUS_DOUBLE_LETTER = 1;
@ -75,6 +77,10 @@ public interface XW_UtilCtxt {
void notifyGameOver();
BitmapDrawable makeBitmap( int width, int height, boolean[] colors );
String[] splitFaces( byte[] chars );
// Don't need this unless we have a scroll thumb to indicate position
//void yOffsetChange( int oldOffset, int newOffset );

View file

@ -1,3 +1,4 @@
/* -*-mode: C; compile-command: "cd XWords4; ../scripts/ndkbuild.sh"; -*- */
#include <jni.h>
#include <stdio.h>
@ -7,9 +8,11 @@
#include "xptypes.h"
#include "dictnry.h"
#include "strutils.h"
#include "utilwrapper.h"
typedef struct _AndDictionaryCtxt {
DictionaryCtxt super;
XW_UtilCtxt* util;
JNIEnv *env;
XP_U8* bytes;
} AndDictionaryCtxt;
@ -77,36 +80,28 @@ andCountSpecials( AndDictionaryCtxt* ctxt )
} /* andCountSpecials */
static XP_Bitmap
andMakeBitmap( XP_U8** ptrp )
andMakeBitmap( AndDictionaryCtxt* ctxt, XP_U8** ptrp )
{
XP_U8* ptr = *ptrp;
XP_U8 nCols = *ptr++;
XP_Bitmap bitmap = NULL;
/* CEBitmapInfo* bitmap = (CEBitmapInfo*)NULL; */
jobject bitmap = NULL;
if ( nCols > 0 ) {
XP_ASSERT(0);
#if 0
XP_U8* dest;
XP_U8* savedDest;
XP_U8 nRows = *ptr++;
XP_U16 rowBytes = (nCols+7) / 8;
XP_U8 srcByte = 0;
XP_U8 destByte = 0;
XP_U8 nBits;
XP_U16 i;
XP_U16 ii;
bitmap = (CEBitmapInfo*)XP_CALLOC( ctxt->super.mpool,
sizeof(bitmap) );
bitmap->nCols = nCols;
bitmap->nRows = nRows;
dest = XP_MALLOC( ctxt->super.mpool, rowBytes * nRows );
bitmap->bits = savedDest = dest;
jboolean* colors = (jboolean*)XP_CALLOC( ctxt->super.mpool,
nCols * nRows * sizeof(*colors) );
jboolean* next = colors;
nBits = nRows * nCols;
for ( i = 0; i < nBits; ++i ) {
XP_U8 srcBitIndex = i % 8;
XP_U8 destBitIndex = (i % nCols) % 8;
for ( ii = 0; ii < nBits; ++ii ) {
XP_U8 srcBitIndex = ii % 8;
XP_U8 destBitIndex = (ii % nCols) % 8;
XP_U8 srcMask, bit;
if ( srcBitIndex == 0 ) {
@ -114,38 +109,22 @@ andMakeBitmap( XP_U8** ptrp )
}
srcMask = 1 << (7 - srcBitIndex);
bit = (srcByte & srcMask) != 0;
destByte |= bit << (7 - destBitIndex);
/* we need to put the byte if we've filled it or if we're done
with the row */
if ( (destBitIndex==7) || ((i%nCols) == (nCols-1)) ) {
*dest++ = destByte;
destByte = 0;
}
XP_ASSERT( next < (colors + (nRows * nCols)) );
*next++ = ((srcByte & srcMask) == 0) ? JNI_FALSE : JNI_TRUE;
}
/* printBitmapData1( nCols, nRows, savedDest ); */
/* printBitmapData2( nCols, nRows, savedDest ); */
#endif
JNIEnv* env = ctxt->env;
bitmap = and_util_makeJBitmap( ctxt->util, nCols, nRows, colors );
jobject tmp = (*env)->NewGlobalRef( env, bitmap );
XP_ASSERT( tmp == bitmap );
(*env)->DeleteLocalRef( env, bitmap );
XP_FREE( ctxt->super.mpool, colors );
}
*ptrp = ptr;
return (XP_Bitmap)bitmap;
} /* andMakeBitmap */
static void
andDeleteBitmap( const AndDictionaryCtxt* XP_UNUSED_DBG(ctxt),
XP_Bitmap* bitmap )
{
if ( !!bitmap ) {
XP_ASSERT(0);
/* CEBitmapInfo* bmi = (CEBitmapInfo*)bitmap; */
/* XP_FREE( ctxt->super.mpool, bmi->bits ); */
/* XP_FREE( ctxt->super.mpool, bmi ); */
}
}
static void
andLoadSpecialData( AndDictionaryCtxt* ctxt, XP_U8** ptrp )
{
@ -170,11 +149,11 @@ andLoadSpecialData( AndDictionaryCtxt* ctxt, XP_U8** ptrp )
XP_MEMCPY( text, ptr, txtlen );
ptr += txtlen;
text[txtlen] = '\0';
XP_ASSERT( *facep < nSpecials );
XP_ASSERT( *facep < nSpecials ); /* firing */
texts[(int)*facep] = text;
bitmaps[(int)*facep].largeBM = andMakeBitmap( &ptr );
bitmaps[(int)*facep].smallBM = andMakeBitmap( &ptr );
bitmaps[(int)*facep].largeBM = andMakeBitmap( ctxt, &ptr );
bitmaps[(int)*facep].smallBM = andMakeBitmap( ctxt, &ptr );
}
}
@ -184,12 +163,69 @@ andLoadSpecialData( AndDictionaryCtxt* ctxt, XP_U8** ptrp )
*ptrp = ptr;
} /* andLoadSpecialData */
/** Android doesn't include iconv for C code to use, so we'll have java do it.
* Cons up a string with all the tile faces (skipping the specials to make
* things easier) and have java return an array of strings. Then load one at
* a time into the expected null-separated format.
*/
static void
splitFaces_via_java( JNIEnv* env, AndDictionaryCtxt* ctxt, const XP_U8* ptr,
int nFaceBytes, int nFaces )
{
XP_UCHAR facesBuf[nFaces*4]; /* seems a reasonable upper bound... */
int indx = 0;
int offsets[nFaces];
int nBytes;
int ii;
jobject jstrarr = and_util_splitFaces( ctxt->util, ptr, nFaceBytes );
XP_ASSERT( (*env)->GetArrayLength( env, jstrarr ) == nFaces );
for ( ii = 0; ii < nFaces; ++ii ) {
jobject jstr = (*env)->GetObjectArrayElement( env, jstrarr, ii );
offsets[ii] = indx;
nBytes = (*env)->GetStringUTFLength( env, jstr );
const char* bytes = (*env)->GetStringUTFChars( env, jstr, NULL );
char* end;
long numval = strtol( bytes, &end, 10 );
if ( end > bytes ) {
XP_ASSERT( numval < 32 );
nBytes = 1;
facesBuf[indx] = (XP_UCHAR)numval;
} else {
XP_MEMCPY( &facesBuf[indx], bytes, nBytes );
}
(*env)->ReleaseStringUTFChars( env, jstr, bytes );
(*env)->DeleteLocalRef( env, jstr );
indx += nBytes;
facesBuf[indx++] = '\0';
XP_ASSERT( indx < VSIZE(facesBuf) );
}
(*env)->DeleteLocalRef( env, jstrarr );
XP_UCHAR* faces = (XP_UCHAR*)XP_CALLOC( ctxt->super.mpool, indx );
XP_UCHAR** ptrs = (XP_UCHAR**)XP_CALLOC( ctxt->super.mpool,
nFaces * sizeof(ptrs[0]));
XP_MEMCPY( faces, facesBuf, indx );
for ( ii = 0; ii < nFaces; ++ii ) {
ptrs[ii] = &faces[offsets[ii]];
}
XP_ASSERT( !ctxt->super.faces );
ctxt->super.faces = faces;
XP_ASSERT( !ctxt->super.facePtrs );
ctxt->super.facePtrs = ptrs;
} /* splitFaces_via_java */
static void
parseDict( AndDictionaryCtxt* ctxt, XP_U8* ptr, XP_U32 dictLength )
{
while( !!ptr ) { /* lets us break.... */
XP_U32 offset;
XP_U16 numFaces, numFaceBytes = 0;
XP_U16 nFaces, numFaceBytes = 0;
XP_U16 i;
XP_U16 flags;
void* mappedBase = (void*)ptr;
@ -198,7 +234,6 @@ parseDict( AndDictionaryCtxt* ctxt, XP_U8* ptr, XP_U32 dictLength )
flags = n_ptr_tohs( &ptr );
#ifdef NODE_CAN_4
if ( flags == 0x0002 ) {
nodeSize = 3;
} else if ( flags == 0x0003 ) {
@ -212,38 +247,33 @@ parseDict( AndDictionaryCtxt* ctxt, XP_U8* ptr, XP_U32 dictLength )
} else {
break; /* we want to return NULL */
}
#else
if( flags != 0x0001 ) {
break;
}
#endif
if ( isUTF8 ) {
numFaceBytes = (XP_U16)(*ptr++);
}
numFaces = (XP_U16)(*ptr++);
if ( numFaces > 64 ) {
nFaces = (XP_U16)(*ptr++);
if ( nFaces > 64 ) {
break;
}
ctxt->super.nodeSize = nodeSize;
if ( !isUTF8 ) {
numFaceBytes = numFaces * 2;
numFaceBytes = nFaces * 2;
}
ctxt->super.nFaces = (XP_U8)numFaces;
ctxt->super.nFaces = (XP_U8)nFaces;
ctxt->super.isUTF8 = isUTF8;
if ( isUTF8 ) {
XP_ASSERT(0);
dict_splitFaces( &ctxt->super, ptr, numFaceBytes, numFaces );
splitFaces_via_java( ctxt->env, ctxt, ptr, numFaceBytes, nFaces );
ptr += numFaceBytes;
} else {
XP_U8 tmp[numFaces*4]; /* should be enough... */
XP_U8 tmp[nFaces*4]; /* should be enough... */
XP_U16 nBytes = 0;
XP_U16 ii;
/* Need to translate from iso-8859-n to utf8 */
for ( ii = 0; ii < numFaces; ++ii ) {
for ( ii = 0; ii < nFaces; ++ii ) {
XP_UCHAR ch = ptr[1];
ptr += 2;
@ -251,16 +281,16 @@ parseDict( AndDictionaryCtxt* ctxt, XP_U8* ptr, XP_U32 dictLength )
tmp[nBytes] = ch;
nBytes += 1;
}
dict_splitFaces( &ctxt->super, tmp, nBytes, numFaces );
dict_splitFaces( &ctxt->super, tmp, nBytes, nFaces );
}
ctxt->super.is_4_byte = (ctxt->super.nodeSize == 4);
ctxt->super.countsAndValues =
(XP_U8*)XP_MALLOC(ctxt->super.mpool, numFaces*2);
(XP_U8*)XP_MALLOC(ctxt->super.mpool, nFaces*2);
ptr += 2; /* skip xloc header */
for ( i = 0; i < numFaces*2; i += 2 ) {
for ( i = 0; i < nFaces*2; i += 2 ) {
ctxt->super.countsAndValues[i] = *ptr++;
ctxt->super.countsAndValues[i+1] = *ptr++;
}
@ -324,11 +354,12 @@ and_dictionary_destroy( DictionaryCtxt* dict )
XP_FREE( ctxt->super.mpool, ctxt->super.chars );
}
if ( !!ctxt->super.bitmaps ) {
JNIEnv* env = ctxt->env;
for ( ii = 0; ii < nSpecials; ++ii ) {
XP_ASSERT( !ctxt->super.bitmaps[ii].largeBM );
XP_ASSERT( !ctxt->super.bitmaps[ii].smallBM );
andDeleteBitmap( ctxt, ctxt->super.bitmaps[ii].largeBM );
andDeleteBitmap( ctxt, ctxt->super.bitmaps[ii].smallBM );
jobject bitmap = ctxt->super.bitmaps[ii].largeBM;
if ( !!bitmap ) {
(*env)->DeleteGlobalRef( env, bitmap );
}
}
XP_FREE( ctxt->super.mpool, ctxt->super.bitmaps );
}
@ -345,7 +376,7 @@ and_dictionary_destroy( DictionaryCtxt* dict )
}
DictionaryCtxt*
makeDict( MPFORMAL JNIEnv *env, jbyteArray jbytes )
makeDict( MPFORMAL JNIEnv *env, XW_UtilCtxt* util, jbyteArray jbytes )
{
XP_Bool formatOk = XP_TRUE;
XP_Bool isUTF8 = XP_FALSE;
@ -368,6 +399,7 @@ makeDict( MPFORMAL JNIEnv *env, jbyteArray jbytes )
MPASSIGN(anddict->super.mpool, mpool);
anddict->bytes = localBytes;
anddict->env = env;
anddict->util = util;
parseDict( anddict, localBytes, len );
setBlankTile( &anddict->super );

View file

@ -9,7 +9,8 @@ void
dict_splitFaces( DictionaryCtxt* dict, const XP_U8* bytes,
XP_U16 nBytes, XP_U16 nFaces );
DictionaryCtxt* makeDict( MPFORMAL JNIEnv *env, jbyteArray bytes );
DictionaryCtxt* makeDict( MPFORMAL JNIEnv *env, XW_UtilCtxt* util,
jbyteArray bytes );
#endif

View file

@ -185,6 +185,20 @@ makeIntArray( JNIEnv *env, int siz, const jint* vals )
return array;
}
jbooleanArray
makeBooleanArray( JNIEnv *env, int siz, const jboolean* vals )
{
jbooleanArray array = (*env)->NewBooleanArray( env, siz );
XP_ASSERT( !!array );
if ( !!vals ) {
jboolean* elems = (*env)->GetBooleanArrayElements( env, array, NULL );
XP_ASSERT( !!elems );
XP_MEMCPY( elems, vals, siz * sizeof(*elems) );
(*env)->ReleaseBooleanArrayElements( env, array, elems, 0 );
}
return array;
}
int
getIntFromArray( JNIEnv* env, jintArray arr, bool del )
{
@ -241,3 +255,24 @@ getMethodID( JNIEnv* env, jobject obj, const char* proc, const char* sig )
(*env)->DeleteLocalRef( env, cls );
return mid;
}
jobjectArray
makeBitmapsArray( JNIEnv* env, const XP_Bitmaps* bitmaps )
{
jobjectArray result = NULL;
if ( !!bitmaps && bitmaps->nBitmaps > 0 ) {
jclass clazz = (*env)->FindClass( env,
"android/graphics/drawable/BitmapDrawable" );
XP_ASSERT( !!clazz );
result = (*env)->NewObjectArray( env, bitmaps->nBitmaps, clazz, NULL );
(*env)->DeleteLocalRef( env, clazz );
int ii;
for ( ii = 0; ii < bitmaps->nBitmaps; ++ii ) {
(*env)->SetObjectArrayElement( env, result, ii, bitmaps->bmps[ii] );
}
}
return result;
}

View file

@ -7,6 +7,7 @@
#include "comtypes.h"
#include "mempool.h"
#include "dictnry.h"
XP_U32 and_ntohl(XP_U32 l);
XP_U16 and_ntohs(XP_U16 l);
@ -26,9 +27,13 @@ bool getObject( JNIEnv* env, jobject obj, const char* name, const char* sig,
jintArray makeIntArray( JNIEnv *env, int size, const jint* vals );
int getIntFromArray( JNIEnv* env, jintArray arr, bool del );
jbooleanArray makeBooleanArray( JNIEnv *env, int size, const jboolean* vals );
jobjectArray makeStringArray( JNIEnv *env, int size, const XP_UCHAR** vals );
jstring streamToJString( MPFORMAL JNIEnv* env, XWStreamCtxt* stream );
jobjectArray makeBitmapsArray( JNIEnv* env, const XP_Bitmaps* bitmaps );
/* Note: jmethodID can be cached. Should not look up more than once. */
jmethodID getMethodID( JNIEnv* env, jobject obj, const char* proc,
const char* sig );

View file

@ -222,27 +222,28 @@ and_draw_drawCell( DrawCtx* dctx, const XP_Rect* rect, const XP_UCHAR* text,
const XP_Bitmaps* bitmaps, Tile tile, XP_S16 owner,
XWBonusType bonus, HintAtts hintAtts, CellFlags flags )
{
DRAW_CBK_HEADER("drawCell",
"(Landroid/graphics/Rect;Ljava/lang/String;"
"[Ljava/lang/Object;IIIII)Z" );
DRAW_CBK_HEADER("drawCell", "(Landroid/graphics/Rect;Ljava/lang/String;"
"[Landroid/graphics/drawable/BitmapDrawable;IIIII)Z" );
jobject jrect = makeJRect( env, rect );
jstring jtext = NULL;
if ( !!text ) {
jtext = (*env)->NewStringUTF( env, text );
}
jobjectArray jbitmaps = !!bitmaps ? makeBitmapsArray( env, bitmaps ) : NULL;
jboolean result = (*env)->CallBooleanMethod( env, draw->j_draw, mid,
jrect, jtext, NULL, tile,
jrect, jtext, jbitmaps, tile,
owner, bonus, hintAtts,
flags );
(*env)->DeleteLocalRef( env, jrect );
if ( !!jtext ) {
(*env)->DeleteLocalRef( env, jtext );
}
if ( !!jbitmaps ) {
(*env)->DeleteLocalRef( env, jbitmaps );
}
return XP_TRUE;
return result;
}
static void
@ -293,21 +294,24 @@ and_draw_drawTile( DrawCtx* dctx, const XP_Rect* rect, const XP_UCHAR* text,
const XP_Bitmaps* bitmaps, XP_U16 val, CellFlags flags )
{
DRAW_CBK_HEADER( "drawTile", "(Landroid/graphics/Rect;Ljava/lang/String;"
"[Ljava/lang/Object;II)V" );
"[Landroid/graphics/drawable/BitmapDrawable;II)V" );
jobject jrect = makeJRect( env, rect );
jstring jtext = NULL;
if ( !!text ) {
jtext = (*env)->NewStringUTF( env, text );
}
jobjectArray jbitmaps = makeBitmapsArray( env, bitmaps );
(*env)->CallVoidMethod( env, draw->j_draw, mid,
jrect, jtext, NULL, val, flags );
jrect, jtext, jbitmaps, val, flags );
(*env)->DeleteLocalRef( env, jrect );
if ( !!jtext ) {
(*env)->DeleteLocalRef( env, jtext );
}
if ( !!jbitmaps ) {
(*env)->DeleteLocalRef( env, jbitmaps );
}
}
static void
@ -316,7 +320,7 @@ and_draw_drawTileMidDrag( DrawCtx* dctx, const XP_Rect* rect,
XP_U16 val, XP_U16 owner, CellFlags flags )
{
DRAW_CBK_HEADER( "drawTileMidDrag", "(Landroid/graphics/Rect;Ljava/lang/String;"
"[Ljava/lang/Object;III)V" );
"[Landroid/graphics/drawable/BitmapDrawable;III)V" );
jobject jrect = makeJRect( env, rect );
jstring jtext = NULL;
@ -324,13 +328,17 @@ and_draw_drawTileMidDrag( DrawCtx* dctx, const XP_Rect* rect,
jtext = (*env)->NewStringUTF( env, text );
}
jobjectArray jbitmaps = makeBitmapsArray( env, bitmaps );
(*env)->CallVoidMethod( env, draw->j_draw, mid,
jrect, jtext, NULL, val, owner, flags );
jrect, jtext, jbitmaps, val, owner, flags );
(*env)->DeleteLocalRef( env, jrect );
if ( !!jtext ) {
(*env)->DeleteLocalRef( env, jtext );
}
if ( !!jbitmaps ) {
(*env)->DeleteLocalRef( env, jbitmaps );
}
}
static void

View file

@ -73,6 +73,11 @@ and_util_userError( XW_UtilCtxt* uc, UtilErrID id )
{
UTIL_CBK_HEADER( "userError", "(I)V" );
(*env)->CallVoidMethod( env, util->j_util, mid, id );
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
XP_LOGF( "exception found" );
}
}
static XP_Bool
@ -313,6 +318,40 @@ and_util_engineStopping( XW_UtilCtxt* uc )
}
#endif
/* These are called from anddict.c, not via vtable */
jobject
and_util_makeJBitmap( XW_UtilCtxt* uc, int nCols, int nRows,
const jboolean* colors )
{
jobject bitmap;
UTIL_CBK_HEADER( "makeBitmap",
"(II[Z)Landroid/graphics/drawable/BitmapDrawable;" );
jbooleanArray jcolors = makeBooleanArray( env, nCols*nRows, colors );
bitmap = (*env)->CallObjectMethod( env, util->j_util, mid,
nCols, nRows, jcolors );
(*env)->DeleteLocalRef( env, jcolors );
return bitmap;
}
jobject
and_util_splitFaces( XW_UtilCtxt* uc, const XP_U8* bytes, jsize len )
{
jobject strarray;
UTIL_CBK_HEADER( "splitFaces", "([B)[Ljava/lang/String;" );
jbyteArray jbytes = (*env)->NewByteArray( env, len );
jbyte* jp = (*env)->GetByteArrayElements( env, jbytes, NULL );
XP_MEMCPY( jp, bytes, len );
(*env)->ReleaseByteArrayElements( env, jbytes, jp, 0 );
strarray = (*env)->CallObjectMethod( env, util->j_util, mid, jbytes );
(*env)->DeleteLocalRef( env, jbytes );
return strarray;
}
XW_UtilCtxt*
makeUtil( MPFORMAL JNIEnv** envp, jobject j_util, CurGameInfo* gi,

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 76; c-basic-offset: 4; -*- */
/*
* Copyright 2001-2009 by Eric House (xwords@eehouse.org). All rights
* Copyright 2001-2010 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -33,5 +33,8 @@ void destroyUtil( XW_UtilCtxt* util );
bool utilTimerFired( XW_UtilCtxt* util, XWTimerReason why, int handle );
jobject and_util_makeJBitmap( XW_UtilCtxt* util, int nCols, int nRows,
const jboolean* colors );
jobject and_util_splitFaces( XW_UtilCtxt* uc, const XP_U8* bytes, int len );
#endif

View file

@ -271,7 +271,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
game_makeNewGame( MPPARM(mpool) &state->game, gi, util, dctx, gameID,
&cp, NULL );
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jDictBytes );
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, util, jDictBytes );
#ifdef STUBBED_DICT
if ( !dict ) {
XP_LOGF( "falling back to stubbed dict" );
@ -319,8 +319,8 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
XWJNI_START();
globals->gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*globals->gi) );
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jdict );
globals->util = makeUtil( MPPARM(mpool) &state->env, jutil, globals->gi, globals );
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, globals->util, jdict );
globals->dctx = makeDraw( MPPARM(mpool) &state->env, jdraw );
jbyte* jelems = (*env)->GetByteArrayElements( env, jstream, NULL );
@ -342,8 +342,8 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
setJGI( env, jgi, globals->gi );
} else {
destroyDraw( globals->dctx );
destroyUtil( globals->util );
dict_destroy( dict );
destroyUtil( globals->util );
destroyGI( MPPARM(mpool) globals->gi );
}