take
@@ -26,10 +25,12 @@
New with this release
- - Fix delays bringing up the Invite dialog for new games
- - Explicitly specify application "theme" to fix a Samsung
- "upgrade" turning the titlebar white and so making menu
- icons disappear
+ - Offer to "Archive" finished games
+ - Make tap on thumbnail toggle whether game's selected rather
+ than open it. (Tap to the right still opens)
+ - Bug fix: don't allow duplicate group names
+ - Fix battery-hogging behavior on non-Google-play
+ installs
(The full changelog
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java
index 70df610ca..1fb0a9c3f 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java
@@ -550,7 +550,7 @@ public class BTService extends XWService {
} else {
short len = is.readShort();
byte[] nliData = new byte[len];
- is.read( nliData );
+ is.readFully( nliData );
nli = XwJNI.nliFromStream( nliData );
}
@@ -573,26 +573,20 @@ public class BTService extends XWService {
int gameID = dis.readInt();
switch ( cmd ) {
case MESG_SEND:
- short len = dis.readShort();
- byte[] buffer = new byte[len];
- int nRead = dis.read( buffer, 0, len );
- if ( nRead == len ) {
- BluetoothDevice host = socket.getRemoteDevice();
- addAddr( host );
+ byte[] buffer = new byte[dis.readShort()];
+ dis.readFully( buffer );
+ BluetoothDevice host = socket.getRemoteDevice();
+ addAddr( host );
- CommsAddrRec addr = new CommsAddrRec( host.getName(),
- host.getAddress() );
- ReceiveResult rslt
- = BTService.this.receiveMessage( BTService.this,
- gameID, m_btMsgSink,
- buffer, addr );
+ CommsAddrRec addr = new CommsAddrRec( host.getName(),
+ host.getAddress() );
+ ReceiveResult rslt
+ = BTService.this.receiveMessage( BTService.this,
+ gameID, m_btMsgSink,
+ buffer, addr );
- result = rslt == ReceiveResult.GAME_GONE ?
- BTCmd.MESG_GAMEGONE : BTCmd.MESG_ACCPT;
- } else {
- Log.e( TAG, "receiveMessage(): read only %d of %d bytes",
- nRead, len );
- }
+ result = rslt == ReceiveResult.GAME_GONE ?
+ BTCmd.MESG_GAMEGONE : BTCmd.MESG_ACCPT;
break;
case MESG_GAMEGONE:
postEvent( MultiEvent.MESSAGE_NOGAME, gameID );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BiDiSockWrap.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BiDiSockWrap.java
index 9b97432d5..e2744c9f5 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BiDiSockWrap.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BiDiSockWrap.java
@@ -188,10 +188,8 @@ public class BiDiSockWrap {
DataInputStream inStream
= new DataInputStream( mSocket.getInputStream() );
while ( mRunThreads ) {
- short len = inStream.readShort();
- Log.d( TAG, "got len: %d", len );
- byte[] packet = new byte[len];
- inStream.read( packet );
+ byte[] packet = new byte[inStream.readShort()];
+ inStream.readFully( packet );
mIface.gotPacket( BiDiSockWrap.this, packet );
}
} catch( IOException ioe ) {
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
index 176f6d7cc..5b0dcd7a3 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
@@ -322,26 +322,35 @@ public class BoardCanvas extends Canvas implements DrawCtx {
}
}
- public void drawTimer( Rect rect, int player, int secondsLeft )
+ public void drawTimer( Rect rect, final int player,
+ int secondsLeft )
{
- if ( null != m_jniThread &&
- (m_lastSecsLeft != secondsLeft || m_lastTimerPlayer != player) ) {
- m_lastSecsLeft = secondsLeft;
- m_lastTimerPlayer = player;
+ if ( m_lastSecsLeft != secondsLeft || m_lastTimerPlayer != player ) {
+ final Rect rectCopy = new Rect(rect);
+ final int secondsLeftCopy = secondsLeft;
+ m_activity.runOnUiThread( new Runnable() {
+ @Override
+ public void run() {
+ if ( null != m_jniThread ) {
+ m_lastSecsLeft = secondsLeftCopy;
+ m_lastTimerPlayer = player;
- String negSign = secondsLeft < 0? "-":"";
- secondsLeft = Math.abs( secondsLeft );
- String time = String.format( "%s%d:%02d", negSign, secondsLeft/60,
- secondsLeft%60 );
+ String negSign = secondsLeftCopy < 0? "-":"";
+ int secondsLeft = Math.abs( secondsLeftCopy );
+ String time =
+ String.format( "%s%d:%02d", negSign,
+ secondsLeft/60, secondsLeft%60 );
- fillRectOther( rect, CommonPrefs.COLOR_BACKGRND );
- m_fillPaint.setColor( m_playerColors[player] );
+ fillRectOther( rectCopy, CommonPrefs.COLOR_BACKGRND );
+ m_fillPaint.setColor( m_playerColors[player] );
- Rect shorter = new Rect( rect );
- shorter.inset( 0, shorter.height() / 5 );
- drawCentered( time, shorter, null );
+ rectCopy.inset( 0, rectCopy.height() / 5 );
+ drawCentered( time, rectCopy, null );
- m_jniThread.handle( JNIThread.JNICmd.CMD_DRAW );
+ m_jniThread.handle( JNIThread.JNICmd.CMD_DRAW );
+ }
+ }
+ } );
}
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java
index 78e963232..b90febc40 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java
@@ -104,7 +104,6 @@ public class BoardDelegate extends DelegateBase
private Button m_exchCancelButton;
private SentInvitesInfo m_sentInfo;
private Perms23.PermCbck m_permCbck;
- private ArrayList m_pendingChats;
private CommsConnTypeSet m_connTypes = null;
private String[] m_missingDevs;
@@ -205,6 +204,25 @@ public class BoardDelegate extends DelegateBase
}
};
ab.setNegativeButton( R.string.button_rematch, lstnr );
+
+ // If we're not already in the "archive" group, offer to move
+ final String archiveName = LocUtils
+ .getString( m_activity, R.string.group_name_archive );
+ final long archiveGroup = DBUtils.getGroup( m_activity, archiveName );
+ long curGroup = DBUtils.getGroupForGame( m_activity, m_rowid );
+ if ( curGroup != archiveGroup ) {
+ lstnr = new OnClickListener() {
+ public void onClick( DialogInterface dlg,
+ int whichButton ) {
+ makeNotAgainBuilder( R.string.not_again_archive,
+ R.string.key_na_archive,
+ Action.ARCHIVE_ACTION )
+ .setParams( archiveName, archiveGroup )
+ .show();
+ }
+ };
+ ab.setNeutralButton( R.string.button_archive, lstnr );
+ }
} else if ( DlgID.DLG_CONNSTAT == dlgID
&& BuildConfig.DEBUG && null != m_connTypes
&& (m_connTypes.contains( CommsConnType.COMMS_CONN_RELAY )
@@ -553,8 +571,6 @@ public class BoardDelegate extends DelegateBase
m_isFirstLaunch = null == savedInstanceState;
getBundledData( savedInstanceState );
- m_pendingChats = new ArrayList();
-
m_utils = new BoardUtilCtxt();
m_timers = new TimerRunnable[4]; // needs to be in sync with
// XWTimerReason
@@ -843,7 +859,7 @@ public class BoardDelegate extends DelegateBase
Utils.setItemVisible( menu, R.id.board_menu_game_invites, enable );
enable = XWPrefs.getStudyEnabled( m_activity );
- Utils.setItemVisible( menu, R.id.games_menu_study, enable );
+ Utils.setItemVisible( menu, R.id.board_menu_study, enable );
return true;
} // onPrepareOptionsMenu
@@ -913,7 +929,7 @@ public class BoardDelegate extends DelegateBase
case R.id.board_menu_tray:
cmd = JNICmd.CMD_TOGGLE_TRAY;
break;
- case R.id.games_menu_study:
+ case R.id.board_menu_study:
StudyListDelegate.launchOrAlert( getDelegator(), m_gi.dictLang, this );
break;
case R.id.board_menu_game_netstats:
@@ -1095,6 +1111,12 @@ public class BoardDelegate extends DelegateBase
makeOkOnlyBuilder( R.string.after_restart ).show();
break;
+ case ARCHIVE_ACTION:
+ String archiveName = (String)params[0];
+ long archiveGroup = (Long)params[1];
+ archiveAndClose( archiveName, archiveGroup );
+ break;
+
case ENABLE_SMS_DO:
post( new Runnable() {
public void run() {
@@ -2144,7 +2166,6 @@ public class BoardDelegate extends DelegateBase
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
warnIfNoTransport();
- trySendChats();
tickle( isStart );
tryInvites();
}
@@ -2407,15 +2428,6 @@ public class BoardDelegate extends DelegateBase
}
}
- private void trySendChats()
- {
- Iterator iter = m_pendingChats.iterator();
- while ( iter.hasNext() ) {
- handleViaThread( JNICmd.CMD_SENDCHAT, iter.next() );
- }
- m_pendingChats.clear();
- }
-
private void tryInvites()
{
if ( 0 < m_mySIS.nMissing && m_summary.hasRematchInfo() ) {
@@ -2588,6 +2600,16 @@ public class BoardDelegate extends DelegateBase
return wordsArray;
}
+ private void archiveAndClose( String archiveName, long groupID )
+ {
+ if ( DBUtils.GROUPID_UNSPEC == groupID ) {
+ groupID = DBUtils.addGroup( m_activity, archiveName );
+ }
+ DBUtils.moveGame( m_activity, m_rowid, groupID );
+ waitCloseGame( false );
+ finish();
+ }
+
// For now, supported if standalone or either BT or SMS used for transport
private boolean rematchSupported( boolean showMulti )
{
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java
index 25b804311..d0c9f33de 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java
@@ -158,8 +158,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
if ( null != m_dims ) {
if ( BoardContainer.getIsPortrait() != (m_dims.height > m_dims.width) ) {
- // square possible; will break above!
- Assert.assertTrue( m_dims.height != m_dims.width );
+ // square possible; will break above! No. tested by forceing square
Log.d( TAG, "onMeasure: discarding m_dims" );
if ( ++m_dimsTossCount < 4 ) {
m_dims = null;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnStatusHandler.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnStatusHandler.java
index c2d0b9795..3a544a429 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnStatusHandler.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnStatusHandler.java
@@ -52,9 +52,6 @@ public class ConnStatusHandler {
public Handler getHandler();
}
- private static final int GREEN = 0xFF00AF00;
- private static final int RED = 0xFFAF0000;
- private static final int BLACK = 0xFF000000;
private static final int SUCCESS_IN = 0;
private static final int SUCCESS_OUT = 1;
private static final int SHOW_SUCCESS_INTERVAL = 1000;
@@ -340,7 +337,7 @@ public class ConnStatusHandler {
boolean isIn )
{
enabled = enabled && null != newestSuccess( connTypes, isIn );
- s_fillPaint.setColor( enabled ? GREEN : RED );
+ s_fillPaint.setColor( enabled ? XWApp.GREEN : XWApp.RED );
canvas.drawRect( rect, s_fillPaint );
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java
index 5ac2fc34c..9f287b36b 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java
@@ -81,7 +81,9 @@ public class DBUtils {
private static long s_cachedRowID = ROWID_NOTFOUND;
private static byte[] s_cachedBytes = null;
- public static enum GameChangeType { GAME_CHANGED, GAME_CREATED, GAME_DELETED };
+ public static enum GameChangeType { GAME_CHANGED, GAME_CREATED,
+ GAME_DELETED, GAME_MOVED,
+ };
public static interface DBChangeListener {
public void gameSaved( long rowid, GameChangeType change );
@@ -1616,21 +1618,34 @@ public class DBUtils {
return result;
}
+ // ORDER BY clause that governs display of games in main GamesList view
+ private static final String s_getGroupGamesOrderBy =
+ TextUtils.join(",", new String[] {
+ // Ended games at bottom
+ DBHelper.GAME_OVER,
+ // games with unread chat messages at top
+ "(" + DBHelper.HASMSGS + " & " + GameSummary.MSG_FLAGS_CHAT + ") IS NOT 0 DESC",
+ // Games not yet connected at top
+ DBHelper.TURN + " is -1 DESC",
+ // Games where it's a local player's turn at top
+ DBHelper.TURN_LOCAL + " DESC",
+ // finally, sort by timestamp of last-made move
+ DBHelper.LASTMOVE,
+ });
+
public static long[] getGroupGames( Context context, long groupID )
{
long[] result = null;
initDB( context );
- String[] columns = { ROW_ID };
+ String[] columns = { ROW_ID, DBHelper.HASMSGS };
String selection = String.format( "%s=%d", DBHelper.GROUPID, groupID );
- String orderBy = String.format( "%s,%s DESC,%s", DBHelper.GAME_OVER,
- DBHelper.TURN_LOCAL, DBHelper.LASTMOVE );
synchronized( s_dbHelper ) {
Cursor cursor = s_db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, // selection
null, // args
null, // groupBy
null, // having
- orderBy
+ s_getGroupGamesOrderBy
);
int index = cursor.getColumnIndex( ROW_ID );
result = new long[ cursor.getCount() ];
@@ -1688,6 +1703,29 @@ public class DBUtils {
return result;
}
+ public static long getGroup( Context context, String name )
+ {
+ long result = GROUPID_UNSPEC;
+ String[] columns = { ROW_ID };
+ String selection = DBHelper.GROUPNAME + " = ?";
+ String[] selArgs = { name };
+
+ initDB( context );
+ synchronized( s_dbHelper ) {
+ Cursor cursor = s_db.query( DBHelper.TABLE_NAME_GROUPS, columns,
+ selection, selArgs,
+ null, // groupBy
+ null, // having
+ null // orderby
+ );
+ if ( cursor.moveToNext() ) {
+ result = cursor.getLong( cursor.getColumnIndex( ROW_ID ) );
+ }
+ cursor.close();
+ }
+ return result;
+ }
+
public static long addGroup( Context context, String name )
{
long rowid = GROUPID_UNSPEC;
@@ -1746,13 +1784,14 @@ public class DBUtils {
}
// Change group id of a game
- public static void moveGame( Context context, long gameid, long groupID )
+ public static void moveGame( Context context, long rowid, long groupID )
{
Assert.assertTrue( GROUPID_UNSPEC != groupID );
ContentValues values = new ContentValues();
values.put( DBHelper.GROUPID, groupID );
- updateRow( context, DBHelper.TABLE_NAME_SUM, gameid, values );
+ updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values );
invalGroupsCache();
+ notifyListeners( rowid, GameChangeType.GAME_MOVED );
}
private static String getChatHistoryStr( Context context, long rowid )
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
index 5b9f4da1b..144c61b91 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
@@ -163,6 +163,7 @@ public class DelegateBase implements DlgClickNotify,
}
if ( this != result ) {
Log.d( TAG, "%s.curThis() => " + result, this.toString() );
+ Assert.fail();
}
return result;
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
index 265e4b7d6..8b13e3b91 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
@@ -84,6 +84,7 @@ public class DlgDelegate {
TRAY_PICKED,
INVITE_INFO,
DISABLE_DUALPANE,
+ ARCHIVE_ACTION,
// Dict Browser
FINISH_ACTION,
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ExpiringDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ExpiringDelegate.java
index 72607dfb1..3987291eb 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ExpiringDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ExpiringDelegate.java
@@ -22,7 +22,6 @@ package org.eehouse.android.xw4;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
@@ -214,13 +213,13 @@ public class ExpiringDelegate {
int offset = 0;
int count = s_points.length;
if ( 0 < redWidth ) {
- s_paint.setColor( Color.RED );
+ s_paint.setColor( XWApp.RED );
canvas.drawLines( s_points, offset, count / 2, s_paint );
count /= 2;
offset += count;
}
if ( redWidth < width ) {
- s_paint.setColor( Color.GREEN );
+ s_paint.setColor( XWApp.GREEN );
}
canvas.drawLines( s_points, offset, count, s_paint );
}
@@ -256,7 +255,7 @@ public class ExpiringDelegate {
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
- paint.setColor( Color.RED );
+ paint.setColor( XWApp.RED );
canvas.drawRect( 0, 0, pct, 1, paint );
paint.setColor( Utils.TURN_COLOR );
canvas.drawRect( pct, 0, 100, 1, paint );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java
index efeeaa513..f3cd41e91 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java
@@ -64,7 +64,7 @@ public class GameListItem extends LinearLayout
private LinearLayout m_list;
private TextView m_state;
private TextView m_modTime;
- private ImageView m_marker;
+ private ImageView m_gameTypeImage;
private TextView m_role;
private boolean m_expanded, m_haveTurn, m_haveTurnLocal;
@@ -90,16 +90,6 @@ public class GameListItem extends LinearLayout
m_lastMoveTime = 0;
m_loadingCount = 0;
m_dsdel = new DrawSelDelegate( this );
-
- setOnClickListener( new View.OnClickListener() {
- @Override
- public void onClick( View v ) {
- // if selected, just un-select
- if ( null != m_summary ) {
- m_cb.itemClicked( GameListItem.this, m_summary );
- }
- }
- } );
}
public GameSummary getSummary()
@@ -174,13 +164,32 @@ public class GameListItem extends LinearLayout
}
// View.OnClickListener interface
- public void onClick( View view ) {
- m_expanded = !m_expanded;
- DBUtils.setExpanded( m_rowid, m_expanded );
+ public void onClick( View view )
+ {
+ int id = view.getId();
+ switch ( id ) {
+ case R.id.expander:
+ m_expanded = !m_expanded;
+ DBUtils.setExpanded( m_rowid, m_expanded );
- makeThumbnailIf( m_expanded );
+ makeThumbnailIf( m_expanded );
- showHide();
+ showHide();
+ break;
+
+ case R.id.view_loaded:
+ toggleSelected();
+ break;
+
+ case R.id.right_side:
+ if ( null != m_summary ) {
+ m_cb.itemClicked( GameListItem.this, m_summary );
+ }
+ break;
+ default:
+ Assert.assertFalse(BuildConfig.DEBUG);
+ break;
+ }
}
private void findViews()
@@ -191,12 +200,15 @@ public class GameListItem extends LinearLayout
m_expandButton.setOnClickListener( this );
m_viewUnloaded = (TextView)findViewById( R.id.view_unloaded );
m_viewLoaded = findViewById( R.id.view_loaded );
+ m_viewLoaded.setOnClickListener( this );
m_list = (LinearLayout)findViewById( R.id.player_list );
m_state = (TextView)findViewById( R.id.state );
m_modTime = (TextView)findViewById( R.id.modtime );
- m_marker = (ImageView)findViewById( R.id.msg_marker );
+ m_gameTypeImage = (ImageView)findViewById( R.id.game_type_marker );
m_thumb = (ImageView)findViewById( R.id.thumbnail );
m_role = (TextView)findViewById( R.id.role );
+
+ findViewById( R.id.right_side ).setOnClickListener( this );
}
private void setLoaded( boolean loaded )
@@ -316,19 +328,20 @@ public class GameListItem extends LinearLayout
int iconID = summary.isMultiGame() ?
R.drawable.multigame__gen : R.drawable.sologame__gen;
- m_marker.setImageResource( iconID );
- m_marker.setOnClickListener( new View.OnClickListener() {
- @Override
- public void onClick( View view ) {
- toggleSelected();
- }
- } );
+ m_gameTypeImage.setImageResource( iconID );
+
+ boolean hasChat = summary.isMultiGame();
+ if ( hasChat ) {
+ int flags = DBUtils.getMsgFlags( m_context, m_rowid );
+ hasChat = 0 != (flags & GameSummary.MSG_FLAGS_CHAT);
+ }
+ findViewById( R.id.has_chat_marker )
+ .setVisibility( hasChat ? View.VISIBLE : View.GONE );
String roleSummary = summary.summarizeRole( m_context, m_rowid );
+ m_role.setVisibility( null == roleSummary ? View.GONE : View.VISIBLE );
if ( null != roleSummary ) {
m_role.setText( roleSummary );
- } else {
- m_role.setVisibility( View.GONE );
}
update( expanded, summary.lastMoveTime, haveATurn,
@@ -420,6 +433,7 @@ public class GameListItem extends LinearLayout
// }
// GameListAdapter.ClickHandler interface
+ @Override
public void longClicked()
{
toggleSelected();
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java
index dba4c57de..92c476792 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java
@@ -1196,7 +1196,7 @@ public class GameUtils {
for ( CommsConnType typ : conTypes ) {
switch ( typ ) {
case COMMS_CONN_RELAY:
- tellRelayDied( context, summary, informNow );
+ // see below
break;
case COMMS_CONN_BT:
BTService.gameDied( context, addr.bt_btAddr, gameID );
@@ -1210,6 +1210,14 @@ public class GameUtils {
}
}
}
+
+ // comms doesn't have a relay address for us until the game's
+ // in play (all devices registered, at least.) To enable
+ // deleting on relay half-games that we created but nobody
+ // joined, special-case this one.
+ if ( summary.inRelayGame() ) {
+ tellRelayDied( context, summary, informNow );
+ }
gamePtr.release();
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
index e98aecd9f..41ffeeaea 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
@@ -564,6 +564,7 @@ public class GamesListDelegate extends ListDelegateBase
private static final int[] DEBUG_ITEMS = {
// R.id.games_menu_loaddb,
R.id.games_menu_storedb,
+ R.id.games_menu_writegit,
};
private static final int[] NOSEL_ITEMS = {
R.id.games_menu_newgroup,
@@ -754,9 +755,18 @@ public class GamesListDelegate extends ListDelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = namer.getName();
- DBUtils.addGroup( m_activity, name );
- mkListAdapter();
- showNewGroupIf();
+ long hasName = DBUtils.getGroup( m_activity, name );
+ if ( DBUtils.GROUPID_UNSPEC == hasName ) {
+ DBUtils.addGroup( m_activity, name );
+ mkListAdapter();
+ showNewGroupIf();
+ } else {
+ String msg = LocUtils
+ .getString( m_activity,
+ R.string.duplicate_group_name_fmt,
+ name );
+ makeOkOnlyBuilder( msg ).show();
+ }
}
};
lstnr2 = new OnClickListener() {
@@ -1059,8 +1069,6 @@ public class GamesListDelegate extends ListDelegateBase
invalidateOptionsMenuIf();
setTitle();
}
-
- mkListAdapter();
}
public void invalidateOptionsMenuIf()
@@ -1132,6 +1140,9 @@ public class GamesListDelegate extends ListDelegateBase
mkListAdapter();
setSelGame( rowid );
break;
+ case GAME_MOVED:
+ mkListAdapter();
+ break;
default:
Assert.fail();
break;
@@ -1539,10 +1550,10 @@ public class GamesListDelegate extends ListDelegateBase
GameUtils.resendAllIf( m_activity, null, true, true );
break;
case R.id.games_menu_newgame_solo:
- handleNewGame( true );
+ handleNewGameButton( true );
break;
case R.id.games_menu_newgame_net:
- handleNewGame( false );
+ handleNewGameButton( false );
break;
case R.id.games_menu_newgroup:
@@ -1597,6 +1608,10 @@ public class GamesListDelegate extends ListDelegateBase
Action.STORAGE_CONFIRMED, itemID );
break;
+ case R.id.games_menu_writegit:
+ Utils.gitInfoToClip( m_activity );
+ break;
+
default:
handled = handleSelGamesItem( itemID, selRowIDs )
|| handleSelGroupsItem( itemID, getSelGroupIDs() );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java
index 62ff478da..78c43b68e 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java
@@ -184,7 +184,7 @@ public class NetUtils {
short len = dis.readShort();
if ( len > 0 ) {
byte[] packet = new byte[len];
- dis.read( packet );
+ dis.readFully( packet );
msgs[ii][jj] = packet;
}
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RefreshNamesTask.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RefreshNamesTask.java
index f94f04725..9844be6d8 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RefreshNamesTask.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RefreshNamesTask.java
@@ -94,7 +94,7 @@ public class RefreshNamesTask extends AsyncTask {
// Can't figure out how to read a null-terminated string
// from DataInputStream so parse it myself.
byte[] bytes = new byte[len];
- dis.read( bytes );
+ dis.readFully( bytes );
int index = -1;
for ( int ii = 0; ii < nRooms; ++ii ) {
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
index 740aa945d..89c24ebb4 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
@@ -736,7 +736,7 @@ public class RelayService extends XWService
case XWPDEV_MSG:
int token = dis.readInt();
byte[] msg = new byte[dis.available()];
- dis.read( msg );
+ dis.readFully( msg );
postData( this, token, msg );
// game-related packets only count
@@ -756,9 +756,8 @@ public class RelayService extends XWService
resetBackoff = true;
intent = getIntentTo( this, MsgCmds.GOT_INVITE );
int srcDevID = dis.readInt();
- short len = dis.readShort();
- byte[] nliData = new byte[len];
- dis.read( nliData );
+ byte[] nliData = new byte[dis.readShort()];
+ dis.readFully( nliData );
NetLaunchInfo nli = XwJNI.nliFromStream( nliData );
intent.putExtra( INVITE_FROM, srcDevID );
String asStr = nli.toString();
@@ -995,9 +994,8 @@ public class RelayService extends XWService
private String getVLIString( DataInputStream dis )
throws java.io.IOException
{
- int len = vli2un( dis );
- byte[] tmp = new byte[len];
- dis.read( tmp );
+ byte[] tmp = new byte[vli2un( dis )];
+ dis.readFully( tmp );
String result = new String( tmp );
return result;
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java
index 38c1bef61..c5a226eec 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java
@@ -522,7 +522,7 @@ public class SMSService extends XWService {
case DATA:
int gameID = dis.readInt();
byte[] rest = new byte[dis.available()];
- dis.read( rest );
+ dis.readFully( rest );
if ( feedMessage( gameID, rest, new CommsAddrRec( phone ) ) ) {
SMSResendReceiver.resetTimer( this );
}
@@ -618,7 +618,7 @@ public class SMSService extends XWService {
} else {
SMS_CMD cmd = SMS_CMD.values()[dis.readByte()];
byte[] rest = new byte[dis.available()];
- dis.read( rest );
+ dis.readFully( rest );
receive( cmd, rest, senderPhone );
success = true;
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/StudyListDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/StudyListDelegate.java
index eed42cdee..db51df1f4 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/StudyListDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/StudyListDelegate.java
@@ -50,6 +50,7 @@ public class StudyListDelegate extends ListDelegateBase
implements OnItemSelectedListener, SelectableItem,
View.OnLongClickListener, View.OnClickListener,
DBUtils.StudyListListener {
+ private static final String TAG = StudyListDelegate.class.getSimpleName();
protected static final int NO_LANG = -1;
@@ -220,7 +221,8 @@ public class StudyListDelegate extends ListDelegateBase
showToast( msg );
break;
default:
- Assert.assertFalse( BuildConfig.DEBUG );
+ Log.d( TAG, "not handling: %s", action );
+ handled = false;
break;
}
return handled;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
index e76107259..de49a2c2f 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
@@ -32,7 +32,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
+import android.text.ClipboardManager;
import android.database.Cursor;
import android.media.Ringtone;
@@ -55,9 +57,12 @@ import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@@ -187,6 +192,33 @@ public class Utils {
context.startActivity( Intent.createChooser( intent, chooserMsg ) );
}
+ static void gitInfoToClip( Context context )
+ {
+ StringBuilder sb;
+ try {
+ InputStream is = context.getAssets().open( BuildConfig.BUILD_INFO_NAME,
+ AssetManager.ACCESS_BUFFER );
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ sb = new StringBuilder();
+ for ( ; ; ) {
+ String line = reader.readLine();
+ if ( null == line ) {
+ break;
+ }
+ sb.append( line ).append( "\n" );
+ }
+ reader.close();
+ } catch ( Exception ex ) {
+ sb = null;
+ }
+
+ if ( null != sb ) {
+ ClipboardManager clipboard = (ClipboardManager)
+ context.getSystemService(Context.CLIPBOARD_SERVICE);
+ clipboard.setText( sb.toString() );
+ }
+ }
+
public static void postNotification( Context context, Intent intent,
int titleID, int bodyID, int id )
{
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
index f37658925..dbf740b99 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
@@ -36,7 +36,6 @@ public class XWApp extends Application {
private static final String TAG = XWApp.class.getSimpleName();
public static final boolean BTSUPPORTED = true;
- public static final boolean GCMSUPPORTED = true;
public static final boolean ATTACH_SUPPORTED = false;
public static final boolean LOG_LIFECYLE = false;
public static final boolean DEBUG_EXP_TIMERS = false;
@@ -53,6 +52,9 @@ public class XWApp extends Application {
public static final int MAX_TRAY_TILES = 7; // comtypes.h
public static final int SEL_COLOR = Color.argb( 0xFF, 0x09, 0x70, 0x93 );
+ public static final int GREEN = 0xFF00AF00;
+ public static final int RED = 0xFFAF0000;
+
private static UUID s_UUID = null;
private static Boolean s_onEmulator = null;
private static Context s_context = null;
diff --git a/xwords4/android/app/src/main/res/.gitignore b/xwords4/android/app/src/main/res/.gitignore
index 1e3315a64..e19e5a0cb 100644
--- a/xwords4/android/app/src/main/res/.gitignore
+++ b/xwords4/android/app/src/main/res/.gitignore
@@ -1 +1,2 @@
values-??/strings.xml
+**/*__gen.png
diff --git a/xwords4/android/app/src/main/res/layout/chat.xml b/xwords4/android/app/src/main/res/layout/chat.xml
index cdaa787cd..b0ac5d6eb 100644
--- a/xwords4/android/app/src/main/res/layout/chat.xml
+++ b/xwords4/android/app/src/main/res/layout/chat.xml
@@ -30,7 +30,6 @@
diff --git a/xwords4/android/app/src/main/res/layout/dict_browser.xml b/xwords4/android/app/src/main/res/layout/dict_browser.xml
index 15ea2f71c..f83534566 100644
--- a/xwords4/android/app/src/main/res/layout/dict_browser.xml
+++ b/xwords4/android/app/src/main/res/layout/dict_browser.xml
@@ -26,7 +26,8 @@
-
+
+
+
+
-
@@ -66,7 +81,7 @@
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
- android:singleLine="true"
+ android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
diff --git a/xwords4/android/app/src/main/res/layout/get_relay.xml b/xwords4/android/app/src/main/res/layout/get_relay.xml
index 186f92600..6c37c233e 100644
--- a/xwords4/android/app/src/main/res/layout/get_relay.xml
+++ b/xwords4/android/app/src/main/res/layout/get_relay.xml
@@ -19,7 +19,8 @@
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:autoText="false"
- android:singleLine="true"
+ android:maxLines="1"
+ android:inputType="text"
android:selectAllOnFocus="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
@@ -37,7 +38,8 @@
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:autoText="false"
- android:singleLine="true"
+ android:maxLines="1"
+ android:inputType="text"
android:selectAllOnFocus="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
diff --git a/xwords4/android/app/src/main/res/layout/get_sms.xml b/xwords4/android/app/src/main/res/layout/get_sms.xml
index 118fddcf4..e19a11073 100644
--- a/xwords4/android/app/src/main/res/layout/get_sms.xml
+++ b/xwords4/android/app/src/main/res/layout/get_sms.xml
@@ -19,7 +19,8 @@
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:autoText="false"
- android:singleLine="true"
+ android:maxLines="1"
+ android:inputType="text"
android:selectAllOnFocus="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
@@ -37,7 +38,8 @@
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:autoText="false"
- android:singleLine="true"
+ android:maxLines="1"
+ android:inputType="text"
android:selectAllOnFocus="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
diff --git a/xwords4/android/app/src/main/res/layout/list_item.xml b/xwords4/android/app/src/main/res/layout/list_item.xml
index 5b8b1f555..4aa520d2b 100644
--- a/xwords4/android/app/src/main/res/layout/list_item.xml
+++ b/xwords4/android/app/src/main/res/layout/list_item.xml
@@ -31,7 +31,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:singleLine="true"
+ android:maxLines="1"
/>
diff --git a/xwords4/android/app/src/main/res/layout/loc_list_item.xml b/xwords4/android/app/src/main/res/layout/loc_list_item.xml
index e3d1d9f8b..48326a1a6 100644
--- a/xwords4/android/app/src/main/res/layout/loc_list_item.xml
+++ b/xwords4/android/app/src/main/res/layout/loc_list_item.xml
@@ -9,11 +9,11 @@
diff --git a/xwords4/android/app/src/main/res/layout/loc_main.xml b/xwords4/android/app/src/main/res/layout/loc_main.xml
index 8fad9f96a..210f54c4b 100644
--- a/xwords4/android/app/src/main/res/layout/loc_main.xml
+++ b/xwords4/android/app/src/main/res/layout/loc_main.xml
@@ -37,7 +37,8 @@
diff --git a/xwords4/android/app/src/main/res/layout/msg_label_and_edit.xml b/xwords4/android/app/src/main/res/layout/msg_label_and_edit.xml
index 1e4327dda..68c381039 100644
--- a/xwords4/android/app/src/main/res/layout/msg_label_and_edit.xml
+++ b/xwords4/android/app/src/main/res/layout/msg_label_and_edit.xml
@@ -18,8 +18,9 @@
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:scrollHorizontally="true"
- android:autoText="false"
- android:capitalize="none"
+ android:maxLines="1"
+ android:inputType="textCapWords"
+ android:maxLength="32"
android:textAppearance="?android:attr/textAppearanceMedium"
android:selectAllOnFocus="true"
/>
diff --git a/xwords4/android/app/src/main/res/layout/player_edit.xml b/xwords4/android/app/src/main/res/layout/player_edit.xml
index cf5427b14..84679f3a8 100644
--- a/xwords4/android/app/src/main/res/layout/player_edit.xml
+++ b/xwords4/android/app/src/main/res/layout/player_edit.xml
@@ -51,11 +51,11 @@
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
- android:autoText="false"
- android:capitalize="words"
android:selectAllOnFocus="true"
android:gravity="fill_horizontal"
- android:singleLine="true"
+ android:maxLines="1"
+ android:maxLength="32"
+ android:inputType="textCapWords"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
@@ -101,11 +101,9 @@
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:singleLine="true"
+ android:maxLines="1"
+ android:inputType="textPassword"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
diff --git a/xwords4/android/app/src/main/res/layout/player_list_elem.xml b/xwords4/android/app/src/main/res/layout/player_list_elem.xml
index f8c865a1d..a2092b6d2 100644
--- a/xwords4/android/app/src/main/res/layout/player_list_elem.xml
+++ b/xwords4/android/app/src/main/res/layout/player_list_elem.xml
@@ -8,14 +8,14 @@
>
diff --git a/xwords4/android/app/src/main/res/layout/rename_game.xml b/xwords4/android/app/src/main/res/layout/rename_game.xml
index f6dca767b..fbeb59890 100644
--- a/xwords4/android/app/src/main/res/layout/rename_game.xml
+++ b/xwords4/android/app/src/main/res/layout/rename_game.xml
@@ -20,7 +20,8 @@
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:autoText="false"
- android:singleLine="true"
+ android:maxLines="1"
+ android:inputType="text"
android:selectAllOnFocus="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
diff --git a/xwords4/android/app/src/main/res/menu/board_menu.xml b/xwords4/android/app/src/main/res/menu/board_menu.xml
index 35e3997ef..f49b85f19 100644
--- a/xwords4/android/app/src/main/res/menu/board_menu.xml
+++ b/xwords4/android/app/src/main/res/menu/board_menu.xml
@@ -57,7 +57,7 @@
-
diff --git a/xwords4/android/app/src/main/res/menu/games_list_menu.xml b/xwords4/android/app/src/main/res/menu/games_list_menu.xml
index fb964160a..078a860ad 100644
--- a/xwords4/android/app/src/main/res/menu/games_list_menu.xml
+++ b/xwords4/android/app/src/main/res/menu/games_list_menu.xml
@@ -120,5 +120,8 @@
+
diff --git a/xwords4/android/app/src/main/res/values-v11/themes.xml b/xwords4/android/app/src/main/res/values-v11/themes.xml
new file mode 100644
index 000000000..0d55c6559
--- /dev/null
+++ b/xwords4/android/app/src/main/res/values-v11/themes.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/xwords4/android/app/src/main/res/values-v21/themes.xml b/xwords4/android/app/src/main/res/values-v21/themes.xml
new file mode 100644
index 000000000..d60f79a3c
--- /dev/null
+++ b/xwords4/android/app/src/main/res/values-v21/themes.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/xwords4/android/app/src/main/res/values/common_rsrc.xml b/xwords4/android/app/src/main/res/values/common_rsrc.xml
index 2ef326001..d029cb00f 100644
--- a/xwords4/android/app/src/main/res/values/common_rsrc.xml
+++ b/xwords4/android/app/src/main/res/values/common_rsrc.xml
@@ -111,6 +111,7 @@
key_notagain_trading
key_notagain_hidenewgamebuttons
key_na_lookup
+ key_na_archive
key_na_browse
key_na_browseall
key_na_values
diff --git a/xwords4/android/app/src/main/res/values/strings.xml b/xwords4/android/app/src/main/res/values/strings.xml
index 8d8fdbfa5..713fd3211 100644
--- a/xwords4/android/app/src/main/res/values/strings.xml
+++ b/xwords4/android/app/src/main/res/values/strings.xml
@@ -1743,6 +1743,14 @@
This button lets you look up,
online, the words just played.
+ Archiving uses a special group
+ called \"Archive\" to store finished games you want to keep. And,
+ since deleting an entire archive is easy, archiving is also a
+ great way to mark games for deletion – if that\'s what you prefer
+ to do.\n\n(Deleting the Archive group is safe because it will be
+ created anew when needed.)
+
+
Move
New group
@@ -2123,7 +2131,7 @@
Name your new group:
Delete group
- Rename
+ Rename group
Put new games here
Move up
Move down
@@ -2158,6 +2166,10 @@
game with the same players and parameters as the one that
just ended. -->
Rematch
+ Archive\u200C
+ Archive
+
+ The group \"%1$s\" already exists.
Reconnect
@@ -2419,11 +2431,11 @@
Hide buttons
- These two buttons do
- the same thing as the first two items in this window\'s Action Bar
- (or menu). If you like you can hide the buttons to make more games
- visible.\n\n(If you later want to unhide them go to the Appearance
- section of App settings).
+ The two buttons at the
+ bottom of this screen and the first two items in its Action Bar
+ (or menu) do the same thing. If you like you can hide the buttons
+ to make more games visible.\n\n(If you later want to unhide the
+ buttons go to the Appearance section of App settings).
Waiting for players
@@ -2491,6 +2503,7 @@
%1$s/%2$s
Write games to SD card
Load games from SD card
+ Copy git info to clipboard
Accept duplicate invites
Fake locale for translation
Accept invitations more than once
diff --git a/xwords4/android/app/src/main/res/values/styles.xml b/xwords4/android/app/src/main/res/values/styles.xml
index 8ffaf87a8..48152edba 100644
--- a/xwords4/android/app/src/main/res/values/styles.xml
+++ b/xwords4/android/app/src/main/res/values/styles.xml
@@ -1,7 +1,5 @@
-
-