From e920fbcaff57567019588ac4f33e7454df2268b2 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 1 Dec 2012 11:55:16 -0800 Subject: [PATCH 01/50] unregister (flag in db) devids google says are unregistered --- xwords4/relay/scripts/gcm_loop.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/xwords4/relay/scripts/gcm_loop.py b/xwords4/relay/scripts/gcm_loop.py index 0e9b48015..666df85dd 100755 --- a/xwords4/relay/scripts/gcm_loop.py +++ b/xwords4/relay/scripts/gcm_loop.py @@ -55,13 +55,19 @@ def init(): def getPendingMsgs( con, typ ): cur = con.cursor() query = """SELECT id, devid FROM msgs - WHERE devid IN (SELECT id FROM devices WHERE devtype=%d) + WHERE devid IN (SELECT id FROM devices WHERE devtype=%d and NOT unreg) AND NOT connname IN (SELECT connname FROM games WHERE dead); """ cur.execute(query % typ) result = cur.fetchall() if g_debug: print "getPendingMsgs=>", result return result +def unregister( gcmid ): + global g_con + print "unregister(", gcmid, ")" + query = "UPDATE devices SET unreg=TRUE WHERE id = '%s'" % gcmid + g_con.cursor().execute( query ) + def asGCMIds(con, devids, typ): cur = con.cursor() query = "SELECT devid FROM devices WHERE devtype = %d AND id IN (%s)" \ @@ -72,20 +78,15 @@ def asGCMIds(con, devids, typ): def notifyGCM( devids, typ ): if typ == DEVTYPE_GCM: instance = gcm.GCM( mykey.myKey ) - data = { 'getMoves': True, - # 'title' : 'Msg from Darth', - # 'msg' : "I am your father, Luke.", - } + data = { 'getMoves': True, } response = instance.json_request( registration_ids = devids, - # restricted_package_name = 'org.eehouse.android.xw4', data = data, - # collapse_key = 'NewMove', ) if 'errors' in response: response = response['errors'] if 'NotRegistered' in response: - for id in response['NotRegistered']: - print 'need to remove "', id, '" from db' + for gcmid in response['NotRegistered']: + unregister( gcmid ) else: print "got some kind of error" else: @@ -184,6 +185,7 @@ def main(): print "devices needing notification:", targets notifyGCM( asGCMIds( g_con, targets, typ ), typ ) pruneSent( devids ) + elif g_debug: print "no targets after backoff" else: emptyCount += 1 if (0 == (emptyCount%5)) and not g_debug: From 9007331fb0798001471bf305daf0607ad24a9954 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 1 Dec 2012 21:13:04 -0800 Subject: [PATCH 02/50] cleanup, and suggest accepting Android's make-default offer --- xwords4/android/scripts/and_index.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/xwords4/android/scripts/and_index.php b/xwords4/android/scripts/and_index.php index baee28084..01dac83c2 100644 --- a/xwords4/android/scripts/and_index.php +++ b/xwords4/android/scripts/and_index.php @@ -30,16 +30,17 @@ function printNonAndroid($agent) { $subject = "Android device not identified"; $body = htmlentities("My browser is running on an android device but" - . " says its user agent is: \"$agent\". Please fix your script to recognize" + . " says its user agent is: \"$agent\"." + . " Please fix your website to recognize" . " this as an Android browser."); print <<

This page is meant to be viewed on an Android device.


-

(If you are viewing this on an Android device, you've - found a bug! Please email me (and be - sure to leave the user agent string in the email body.) +

(If you are viewing this on an Android device, + you've found a bug! Please email me + (and be sure to leave the user agent string in the email body.)

@@ -73,6 +74,11 @@ invite email (or text) and tap the link again.

link in your invite email (or text) again, and this time let Crosswords handle it.

+

(If you get tired of having to having to make that choice, Android +will allow you to make Crosswords the default. What you're saying is +that Crosswords will be giving control of all URLs that start with +"http://eehouse.org/and/" -- not all URLs of any type.)

+

Have fun. And as always, let me know if you have problems or suggestions.

From e5bb360537b032fcc7206bd03d94b121f8e501a9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 2 Dec 2012 18:02:27 -0800 Subject: [PATCH 03/50] When a game is over, show "Game over" dialog containing score summary every time it's opened. This is a quick way to get around it being a bit hard to tell a board's in that state. --- .../org/eehouse/android/xw4/BoardActivity.java | 17 ++++++++++++++--- .../org/eehouse/android/xw4/jni/JNIThread.java | 6 +++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index cdbd49cab..5e24b8c08 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -165,6 +165,7 @@ public class BoardActivity extends XWActivity private int m_missing; private boolean m_haveInvited = false; + private boolean m_overNotShown; private static BoardActivity s_this = null; private static Class s_thisLocker = BoardActivity.class; @@ -500,6 +501,7 @@ public class BoardActivity extends XWActivity Intent intent = getIntent(); m_rowid = intent.getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 ); m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); + m_overNotShown = true; setBackgroundColor(); setKeepScreenOn(); @@ -692,7 +694,7 @@ public class BoardActivity extends XWActivity } return true; - } + } // onPrepareOptionsMenu public boolean onOptionsItemSelected( MenuItem item ) { @@ -1722,8 +1724,17 @@ public class BoardActivity extends XWActivity if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) { startChatActivity(); } - if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { - m_jniThread.handle( JNICmd.CMD_POST_OVER ); + if ( m_overNotShown ) { + Boolean auto = null; + if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { + auto = new Boolean( false ); + } else if ( DBUtils.gameOver( this, m_rowid ) ) { + auto = new Boolean( true ); + } + if ( null != auto ) { + m_overNotShown = false; + m_jniThread.handle( JNICmd.CMD_POST_OVER, auto ); + } } if ( 0 != flags ) { DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java index 24b82e392..3cc42187c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -524,7 +524,11 @@ public class JNIThread extends Thread { case CMD_POST_OVER: if ( XwJNI.server_getGameIsOver( m_jniGamePtr ) ) { - sendForDialog( R.string.finalscores_title, + boolean auto = 0 < args.length && + ((Boolean)args[0]).booleanValue(); + int titleID = auto? R.string.summary_gameover + : R.string.finalscores_title; + sendForDialog( titleID, XwJNI.server_writeFinalScores( m_jniGamePtr ) ); } break; From 9eea15185c4b8e2f62c4d128d6efe50b77a16c3d Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 2 Dec 2012 18:09:15 -0800 Subject: [PATCH 04/50] set m_gameOver when it's first known. --- .../src/org/eehouse/android/xw4/BoardActivity.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 5e24b8c08..0e8b1e4a0 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -1725,13 +1725,14 @@ public class BoardActivity extends XWActivity startChatActivity(); } if ( m_overNotShown ) { - Boolean auto = null; + boolean auto = false; if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { - auto = new Boolean( false ); + m_gameOver = true; } else if ( DBUtils.gameOver( this, m_rowid ) ) { - auto = new Boolean( true ); + m_gameOver = true; + auto = true; } - if ( null != auto ) { + if ( m_gameOver ) { m_overNotShown = false; m_jniThread.handle( JNICmd.CMD_POST_OVER, auto ); } From 7621b909c3a88ede0adeff3424b715a9ee6e1339 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 2 Dec 2012 18:19:52 -0800 Subject: [PATCH 05/50] hide a couple of message-related menuitems for standalone games --- .../src/org/eehouse/android/xw4/BoardActivity.java | 5 +++++ .../XWords4/src/org/eehouse/android/xw4/Utils.java | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 0e8b1e4a0..dcbd08151 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -693,6 +693,11 @@ public class BoardActivity extends XWActivity item.setTitle( R.string.board_menu_game_final ); } + if ( DeviceRole.SERVER_STANDALONE == m_gi.serverRole ) { + Utils.setItemVisible( menu, R.id.board_menu_game_resend, false ); + Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, false ); + } + return true; } // onPrepareOptionsMenu diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java index 4744e48ab..6d74998aa 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java @@ -40,6 +40,8 @@ import android.net.Uri; import android.provider.ContactsContract.PhoneLookup; import android.telephony.TelephonyManager; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.widget.CheckBox; import android.widget.EditText; @@ -345,6 +347,12 @@ public class Utils { } } + public static void setItemVisible( Menu menu, int id, boolean enabled ) + { + MenuItem item = menu.findItem( id ); + item.setVisible( enabled ); + } + public static boolean hasSmallScreen( Context context ) { if ( null == s_hasSmallScreen ) { From bda0d51c7f52b247f7046ad666dcec0ae2700329 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 2 Dec 2012 18:45:35 -0800 Subject: [PATCH 06/50] add Rematch button to game over dialog -- which only puts up notImpl toast so far. --- xwords4/android/XWords4/res/values/strings.xml | 5 +++++ .../org/eehouse/android/xw4/BoardActivity.java | 17 ++++++++++++++++- .../org/eehouse/android/xw4/jni/JNIThread.java | 7 +++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 956715178..55f904e56 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -2130,4 +2130,9 @@ My games New games + + + Rematch diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index dcbd08151..7c01d39f2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -75,7 +75,7 @@ public class BoardActivity extends XWActivity private static final int PICK_TILE_REQUESTTRAY_BLK = DLG_OKONLY + 11; private static final int DLG_USEDICT = DLG_OKONLY + 12; private static final int DLG_GETDICT = DLG_OKONLY + 13; - + private static final int GAME_OVER = DLG_OKONLY + 14; private static final int CHAT_REQUEST = 1; private static final int BT_INVITE_RESULT = 2; @@ -235,6 +235,7 @@ public class BoardActivity extends XWActivity case DLG_OKONLY: case DLG_BADWORDS: case DLG_RETRY: + case GAME_OVER: ab = new AlertDialog.Builder( this ) .setTitle( m_dlgTitle ) .setMessage( m_dlgBytes ) @@ -247,6 +248,15 @@ public class BoardActivity extends XWActivity } }; ab.setNegativeButton( R.string.button_retry, lstnr ); + } else if ( GAME_OVER == id ) { + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, + int whichButton ) { + Utils.notImpl( BoardActivity.this ); + finish(); + } + }; + ab.setNegativeButton( R.string.button_rematch, lstnr ); } dialog = ab.create(); Utils.setRemoveOnDismiss( this, dialog, id ); @@ -1700,6 +1710,11 @@ public class BoardActivity extends XWActivity launchLookup( wordsToArray((String)msg.obj), m_gi.dictLang ); break; + case JNIThread.GAME_OVER: + m_dlgBytes = (String)msg.obj; + m_dlgTitle = msg.arg1; + showDialog( GAME_OVER ); + break; } } }; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java index 3cc42187c..71577dc6a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -94,6 +94,7 @@ public class JNIThread extends Thread { public static final int QUERY_ENDGAME = 4; public static final int TOOLBAR_STATES = 5; public static final int GOT_WORDS = 6; + public static final int GAME_OVER = 7; public class GameStateInfo implements Cloneable { public int visTileCount; @@ -528,8 +529,10 @@ public class JNIThread extends Thread { ((Boolean)args[0]).booleanValue(); int titleID = auto? R.string.summary_gameover : R.string.finalscores_title; - sendForDialog( titleID, - XwJNI.server_writeFinalScores( m_jniGamePtr ) ); + + String text = XwJNI.server_writeFinalScores( m_jniGamePtr ); + Message.obtain( m_handler, GAME_OVER, titleID, 0, text ) + .sendToTarget(); } break; From da9c7a1afd2a39c05e723932a5c1aacc1f0a7090 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 2 Dec 2012 21:39:33 -0800 Subject: [PATCH 07/50] comment out unused column --- .../android/XWords4/src/org/eehouse/android/xw4/DBHelper.java | 4 ++-- .../android/XWords4/src/org/eehouse/android/xw4/DBUtils.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java index b44021f29..cf6a4d6bd 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBHelper.java @@ -60,7 +60,7 @@ public class DBHelper extends SQLiteOpenHelper { public static final String INVITEID = "INVITEID"; public static final String RELAYID = "RELAYID"; public static final String SEED = "SEED"; - public static final String SMSPHONE = "SMSPHONE"; + public static final String SMSPHONE = "SMSPHONE"; // unused -- so far public static final String LASTMOVE = "LASTMOVE"; public static final String GROUPID = "GROUPID"; @@ -100,7 +100,7 @@ public class DBHelper extends SQLiteOpenHelper { ,SEED, "INTEGER" ,DICTLANG, "INTEGER" ,DICTLIST, "TEXT" - ,SMSPHONE, "TEXT" + ,SMSPHONE, "TEXT" // unused ,SCORES, "TEXT" ,CHAT_HISTORY, "TEXT" ,GAMEID, "INTEGER" diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index f3504932f..303b09af9 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -129,7 +129,7 @@ public class DBUtils { DBHelper.TURN, DBHelper.GIFLAGS, DBHelper.CONTYPE, DBHelper.SERVERROLE, DBHelper.ROOMNAME, DBHelper.RELAYID, - DBHelper.SMSPHONE, DBHelper.SEED, + /*DBHelper.SMSPHONE,*/ DBHelper.SEED, DBHelper.DICTLANG, DBHelper.GAMEID, DBHelper.SCORES, DBHelper.HASMSGS, DBHelper.LASTPLAY_TIME, DBHelper.REMOTEDEVS, From a640f9d97b816454ccb7195deb8cbfa17ecfc419 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 3 Dec 2012 06:51:50 -0800 Subject: [PATCH 08/50] up version number and string --- xwords4/android/XWords4/AndroidManifest.xml | 2 +- xwords4/android/XWords4/res/raw/changes | 15 ++++++--------- xwords4/android/XWords4/res/values/app_name.xml | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 640303a36..9524eab68 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -22,7 +22,7 @@ to come from a domain that you own or have control over. --> diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index 20c716fa5..c1165a0bd 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -5,17 +5,14 @@ -Crosswords 4.4 beta 56 release -
    New with this release -
  • Improve invitations: no more redirection through a website, and - confirm before creating duplicate games
  • -
  • (For sideloading users only) No more browser involvement in - updates: app can launch the installer directly to update - itself
  • -
  • Remove notifications when their games are deleted
  • +Crosswords 4.4 beta 57 release + +

    New with this release

    +
    -
      Next up +

      Next up

      +
      • One more idea for improving invitations
      • Allow grouping of games in collapsible user-defined categores: "Games with Kati", "Finished games", etc.
      • diff --git a/xwords4/android/XWords4/res/values/app_name.xml b/xwords4/android/XWords4/res/values/app_name.xml index 844bb12a6..095652a34 100644 --- a/xwords4/android/XWords4/res/values/app_name.xml +++ b/xwords4/android/XWords4/res/values/app_name.xml @@ -1,5 +1,5 @@ - 4.4 beta 56 + 4.4 beta 57 From ad606cdec073e6898584f115590d5f6abe7084cb Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 3 Dec 2012 06:53:15 -0800 Subject: [PATCH 09/50] trying to figure out why games list is occasionally redrawing itself all the time: add logging that activated only when XWApp's DEBUG flag is set, and turn that flag on. The latter change should be reversed before the next release. --- .../eehouse/android/xw4/ExpiringDelegate.java | 8 +++++++ .../eehouse/android/xw4/GameListAdapter.java | 23 ++++++++++++++----- .../src/org/eehouse/android/xw4/XWApp.java | 2 +- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java index 7d1b13968..13083266d 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java @@ -194,12 +194,20 @@ public class ExpiringDelegate { if ( null == m_runnable ) { m_runnable = new Runnable() { public void run() { + if ( XWApp.DEBUG ) { + DbgUtils.logf( "ExpiringDelegate: timer fired" + + " for %H", this ); + } if ( m_active ) { figurePct(); if ( m_haveTurnLocal ) { m_back = null; setBackground(); } + if ( XWApp.DEBUG ) { + DbgUtils.logf( "ExpiringDelegate: invalidating" + + " view %H", m_view ); + } m_view.invalidate(); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index c234cb09a..ec28a52f0 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -1,7 +1,7 @@ /* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */ /* - * Copyright 2009-2010 by Eric House (xwords@eehouse.org). All - * rights reserved. + * Copyright 2009-2012 by Eric House (xwords@eehouse.org). All rights + * reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -249,7 +249,9 @@ public class GameListAdapter extends XWListAdapter { ViewInfo vi = new ViewInfo( layout, m_rowid, expanded, summary.lastMoveTime, haveATurn, haveALocalTurn ); - + if ( XWApp.DEBUG ) { + DbgUtils.logf( "created new view for rowid %d", m_rowid ); + } synchronized( m_viewsCache ) { m_viewsCache.put( m_rowid, vi ); } @@ -329,7 +331,7 @@ public class GameListAdapter extends XWListAdapter { } } - public void setField( String field ) + public void setField( String fieldName ) { int[] ids = { R.string.game_summary_field_empty @@ -339,12 +341,21 @@ public class GameListAdapter extends XWListAdapter { }; int result = -1; for ( int id : ids ) { - if ( m_context.getString( id ).equals( field ) ) { + if ( m_context.getString( id ).equals( fieldName ) ) { result = id; break; } } - if ( m_fieldID != result ) { + if ( -1 == result ) { + if ( XWApp.DEBUG ) { + DbgUtils.logf( "GameListAdapter.setField(): unable to match" + + " fieldName %s", fieldName ); + } + } else if ( m_fieldID != result ) { + if ( XWApp.DEBUG ) { + DbgUtils.logf( "setField: clearing views cache for change" + + " from %d to %d", m_fieldID, result ); + } m_viewsCache.clear(); m_fieldID = result; } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index 0525faf7d..06bec14f1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -33,7 +33,7 @@ public class XWApp extends Application { public static final boolean SMSSUPPORTED = true; public static final boolean GCMSUPPORTED = true; public static final boolean ATTACH_SUPPORTED = false; - public static final boolean DEBUG = false; + public static final boolean DEBUG = true; // DON'T SHIP THIS WAY public static final String SMS_PUBLIC_HEADER = "-XW4"; From c041792f837f87d419632bd585241725fb04c8f6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 3 Dec 2012 08:03:00 -0800 Subject: [PATCH 10/50] move methods toward better encapsualtion (no behavior change) --- .../eehouse/android/xw4/BTInviteActivity.java | 8 ++++++++ .../org/eehouse/android/xw4/BoardActivity.java | 8 ++++---- .../src/org/eehouse/android/xw4/GameUtils.java | 16 ---------------- .../org/eehouse/android/xw4/InviteActivity.java | 2 +- .../org/eehouse/android/xw4/NewGameActivity.java | 4 ++-- .../eehouse/android/xw4/SMSInviteActivity.java | 10 +++++++++- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java index c6a3297d3..fcfcbd51a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java @@ -45,6 +45,14 @@ public class BTInviteActivity extends InviteActivity private boolean m_firstScan; private int m_checkCount; + public static void launchForResult( Activity activity, int nMissing, + int requestCode ) + { + Intent intent = new Intent( activity, BTInviteActivity.class ); + intent.putExtra( INTENT_KEY_NMISSING, nMissing ); + activity.startActivityForResult( intent, requestCode ); + } + @Override protected void onCreate( Bundle savedInstanceState ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 7c01d39f2..64d1c775e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -847,12 +847,12 @@ public class BoardActivity extends XWActivity doSyncMenuitem(); break; case BT_PICK_ACTION: - GameUtils.launchBTInviter( this, m_nMissingPlayers, - BT_INVITE_RESULT ); + BTInviteActivity.launchForResult( this, m_nMissingPlayers, + BT_INVITE_RESULT ); break; case SMS_PICK_ACTION: - GameUtils.launchSMSInviter( this, m_nMissingPlayers, - SMS_INVITE_RESULT ); + SMSInviteActivity.launchForResult( this, m_nMissingPlayers, + SMS_INVITE_RESULT ); break; case SMS_CONFIG_ACTION: Utils.launchSettings( this ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 19317bf77..9a291d1b8 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -508,22 +508,6 @@ public class GameUtils { nPlayersH, null, gameID, isHost ); } - public static void launchBTInviter( Activity activity, int nMissing, - int requestCode ) - { - Intent intent = new Intent( activity, BTInviteActivity.class ); - intent.putExtra( BTInviteActivity.INTENT_KEY_NMISSING, nMissing ); - activity.startActivityForResult( intent, requestCode ); - } - - public static void launchSMSInviter( Activity activity, int nMissing, - int requestCode ) - { - Intent intent = new Intent( activity, SMSInviteActivity.class ); - intent.putExtra( SMSInviteActivity.INTENT_KEY_NMISSING, nMissing ); - activity.startActivityForResult( intent, requestCode ); - } - public static void launchInviteActivity( Context context, boolean choseEmail, String room, String inviteID, diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/InviteActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/InviteActivity.java index 1a5c803f0..41dd2ae7c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/InviteActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/InviteActivity.java @@ -43,7 +43,7 @@ abstract class InviteActivity extends XWListActivity implements View.OnClickListener { public static final String DEVS = "DEVS"; - public static final String INTENT_KEY_NMISSING = "NMISSING"; + protected static final String INTENT_KEY_NMISSING = "NMISSING"; protected int m_nMissing; protected Button m_okButton; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java index 039aaa23d..85bbccd63 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java @@ -357,7 +357,7 @@ public class NewGameActivity extends XWActivity { intent.putExtra( GameUtils.INTENT_FORRESULT_ROWID, true ); startActivityForResult( intent, CONFIG_FOR_BT ); } else { - GameUtils.launchBTInviter( this, 1, INVITE_FOR_BT ); + BTInviteActivity.launchForResult( this, 1, INVITE_FOR_BT ); } } @@ -378,7 +378,7 @@ public class NewGameActivity extends XWActivity { intent.putExtra( GameUtils.INTENT_FORRESULT_ROWID, true ); startActivityForResult( intent, CONFIG_FOR_SMS ); } else { - GameUtils.launchSMSInviter( this, 1, INVITE_FOR_SMS ); + SMSInviteActivity.launchForResult( this, 1, INVITE_FOR_SMS ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSInviteActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSInviteActivity.java index 60a7da3dd..aa955374b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSInviteActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSInviteActivity.java @@ -32,9 +32,9 @@ import android.os.Bundle; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract; -import android.text.method.DialerKeyListener; import android.text.Editable; import android.text.TextWatcher; +import android.text.method.DialerKeyListener; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; @@ -64,6 +64,14 @@ public class SMSInviteActivity extends InviteActivity { private String m_pendingNumber; private boolean m_immobileConfirmed; + public static void launchForResult( Activity activity, int nMissing, + int requestCode ) + { + Intent intent = new Intent( activity, SMSInviteActivity.class ); + intent.putExtra( INTENT_KEY_NMISSING, nMissing ); + activity.startActivityForResult( intent, requestCode ); + } + @Override protected void onCreate( Bundle savedInstanceState ) { From 2c71c8425fa5e2addda9b8ad4be5392413c92a4c Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 3 Dec 2012 08:30:29 -0800 Subject: [PATCH 11/50] pass the rematch request off to GamesList where it can create new games without interference from locked current game. Handle standalone case by cloning and launching: easy. Networked games will be harder. --- .../eehouse/android/xw4/BoardActivity.java | 10 +++++-- .../org/eehouse/android/xw4/GamesList.java | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 64d1c775e..9998ba315 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -252,8 +252,7 @@ public class BoardActivity extends XWActivity lstnr = new DialogInterface.OnClickListener() { public void onClick( DialogInterface dlg, int whichButton ) { - Utils.notImpl( BoardActivity.this ); - finish(); + doRematch(); } }; ab.setNegativeButton( R.string.button_rematch, lstnr ); @@ -2106,4 +2105,11 @@ public class BoardActivity extends XWActivity m_passwdEdit = (EditText)m_passwdLyt.findViewById( R.id.edit ); } + private void doRematch() + { + Intent intent = GamesList.makeRematchIntent( this, m_gi, m_rowid ); + startActivity( intent ); + finish(); + } + } // class BoardActivity diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 2fb7f724f..4129c1cbf 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -68,6 +68,7 @@ public class GamesList extends XWListActivity private static final String RELAYIDS_EXTRA = "relayids"; private static final String GAMEID_EXTRA = "gameid"; + private static final String REMATCH_ROWID_EXTRA = "rowid"; private static final int NEW_NET_GAME_ACTION = 1; private static final int RESET_GAME_ACTION = 2; @@ -291,6 +292,7 @@ public class GamesList extends XWListActivity startFirstHasDict( intent ); startNewNetGame( intent ); startHasGameID( intent ); + startHasRowID( intent ); askDefaultNameIf(); } // onCreate @@ -305,6 +307,7 @@ public class GamesList extends XWListActivity startFirstHasDict( intent ); startNewNetGame( intent ); startHasGameID( intent ); + startHasRowID( intent ); } @Override @@ -826,6 +829,16 @@ public class GamesList extends XWListActivity } } + private void startHasRowID( Intent intent ) + { + long rowid = intent.getLongExtra( REMATCH_ROWID_EXTRA, -1 ); + if ( -1 != rowid ) { + // this will juggle if the preference is set + long newid = GameUtils.dupeGame( this, rowid ); + GameUtils.launchGame( this, newid ); + } + } + private void askDefaultNameIf() { if ( null == CommonPrefs.getDefaultPlayerName( this, 0, false ) ) { @@ -900,6 +913,20 @@ public class GamesList extends XWListActivity return intent; } + public static Intent makeRematchIntent( Context context, CurGameInfo gi, + long rowid ) + { + Intent intent = makeSelfIntent( context ); + + if ( CurGameInfo.DeviceRole.SERVER_STANDALONE == gi.serverRole ) { + intent.putExtra( REMATCH_ROWID_EXTRA, rowid ); + } else { + Utils.notImpl( context ); + } + + return intent; + } + public static void openGame( Context context, Uri data ) { Intent intent = makeSelfIntent( context ); From 922f4a22f4a4b05fdba35c2644f1e745a2f58498 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 3 Dec 2012 23:07:14 -0800 Subject: [PATCH 12/50] remove unneeded variable --- .../src/org/eehouse/android/xw4/GameListAdapter.java | 5 ++++- .../XWords4/src/org/eehouse/android/xw4/GamesList.java | 7 ++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index ec28a52f0..bcdfbefe3 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -331,8 +331,9 @@ public class GameListAdapter extends XWListAdapter { } } - public void setField( String fieldName ) + public boolean setField( String fieldName ) { + boolean changed = false; int[] ids = { R.string.game_summary_field_empty ,R.string.game_summary_field_language @@ -358,7 +359,9 @@ public class GameListAdapter extends XWListAdapter { } m_viewsCache.clear(); m_fieldID = result; + changed = true; } + return changed; } } \ No newline at end of file diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 4129c1cbf..fbed5b9d7 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -90,7 +90,6 @@ public class GamesList extends XWListActivity private String[] m_sameLangDicts; private int m_missingDictLang; private long m_rowid; - private String m_nameField; private NetLaunchInfo m_netLaunchInfo; // private String m_smsPhone; @@ -851,10 +850,8 @@ public class GamesList extends XWListActivity private void updateField() { String newField = CommonPrefs.getSummaryField( this ); - if ( ! newField.equals( m_nameField ) ) { - m_nameField = newField; - m_adapter.setField( newField ); - onContentChanged(); + if ( m_adapter.setField( newField ) ) { + onContentChanged(); } } From ac8c229e75c22fa322273ed50c6891a889866e8e Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 3 Dec 2012 23:10:31 -0800 Subject: [PATCH 13/50] Fix attachments, at least for the one phone I've tested on. Requires K-9 mail (Gmail doesn't allow opening attachments). Trick was to drop the file extension, since if present that seems to come ahead of mime type for filtering. --- xwords4/android/XWords4/AndroidManifest.xml | 14 ++++++-------- .../src/org/eehouse/android/xw4/GameUtils.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/XWApp.java | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 9524eab68..ba77b8886 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -134,14 +134,12 @@ /> - - - - - - - - + + + + + + diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 9a291d1b8..52b274823 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -920,7 +920,7 @@ public class GameUtils { byte[] data = json.toString().getBytes(); File file = new File( dir, - String.format("invite_%s.json", room ) ); + String.format("invite_%s", room ) ); FileOutputStream fos = new FileOutputStream( file ); fos.write( data, 0, data.length ); fos.close(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index 06bec14f1..57927412b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -32,7 +32,7 @@ public class XWApp extends Application { public static final boolean BTSUPPORTED = false; public static final boolean SMSSUPPORTED = true; public static final boolean GCMSUPPORTED = true; - public static final boolean ATTACH_SUPPORTED = false; + public static final boolean ATTACH_SUPPORTED = true; public static final boolean DEBUG = true; // DON'T SHIP THIS WAY public static final String SMS_PUBLIC_HEADER = "-XW4"; From a035ef5623af299019c3ba9c5e6e40a423a8f52e Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Dec 2012 06:52:10 -0800 Subject: [PATCH 14/50] attachment can come in as file or content scheme --- .../XWords4/src/org/eehouse/android/xw4/NetLaunchInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetLaunchInfo.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetLaunchInfo.java index e2f79b4ed..878316cf1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetLaunchInfo.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetLaunchInfo.java @@ -73,7 +73,7 @@ public class NetLaunchInfo { if ( null != data ) { String scheme = data.getScheme(); try { - if ( "content".equals(scheme) ) { + if ( "content".equals(scheme) || "file".equals(scheme) ) { Assert.assertNotNull( context ); ContentResolver resolver = context.getContentResolver(); InputStream is = resolver.openInputStream( data ); From af853098f2bd8f634673c08389d63f7a4277818b Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Dec 2012 07:32:11 -0800 Subject: [PATCH 15/50] use preferred SDK-8 API to look for downloads directory, and wrap it in an interface loaded only when SDK>=8 to avoid load-time crash on older devices. --- xwords4/android/XWords4/AndroidManifest.xml | 2 +- xwords4/android/XWords4/project.properties | 2 +- .../org/eehouse/android/xw4/DictUtils.java | 45 ++++++++++++++++++- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index ba77b8886..1b634d862 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -26,7 +26,7 @@ android:versionName="@string/app_version" > - + diff --git a/xwords4/android/XWords4/project.properties b/xwords4/android/XWords4/project.properties index 797fb4fc3..c1fd41ab1 100644 --- a/xwords4/android/XWords4/project.properties +++ b/xwords4/android/XWords4/project.properties @@ -10,4 +10,4 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=android-7 +target=android-8 diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java index 126410cfd..6adcc5e6e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java @@ -47,6 +47,20 @@ import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; public class DictUtils { + // Standard hack for using APIs from an SDK in code to ship on + // older devices that don't support it: prevent class loader from + // seeing something it'll barf on by loading it manually + private static interface SafeDirGetter { + public File getDownloadDir(); + } + private static SafeDirGetter s_dirGetter = null; + static { + int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK ); + if ( 8 <= sdkVersion ) { + s_dirGetter = new DirGetter(); + } + } + // keep in sync with loc_names string-array public enum DictLoc { UNKNOWN, BUILT_IN, INTERNAL, EXTERNAL, DOWNLOAD }; public static final String INVITED = "invited"; @@ -566,10 +580,21 @@ public class DictUtils { return null != getDownloadDir( context ); } + // The approved way to get this is to call Environment. + // getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), + // but at least on my Samsung Galaxy Blaze 4G it returns a + // directory that does not exist! (FU, Samsung...) So in that + // case fall back to using getExternalStorageDirectory and + // appending "download" public static File getDownloadDir( Context context ) { File result = null; - if ( haveWriteableSD() ) { + if ( null != s_dirGetter ) { + result = s_dirGetter.getDownloadDir(); + } + if ( null != result ) { + // we're done + } else if ( haveWriteableSD() ) { File file = null; String myPath = XWPrefs.getMyDownloadDir( context ); if ( null != myPath && 0 < myPath.length() ) { @@ -577,7 +602,11 @@ public class DictUtils { } else { file = Environment.getExternalStorageDirectory(); if ( null != file ) { - file = new File( file, "download/" ); + File child = new File( file, "download/" ); + if ( child.exists() && child.isDirectory() + && child.canWrite() ) { + file = child; + } } } if ( null != file && file.exists() && file.isDirectory() ) { @@ -596,4 +625,16 @@ public class DictUtils { } return result; } + + private static class DirGetter implements SafeDirGetter { + public File getDownloadDir() + { + File path = Environment. + getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + if ( null != path && !path.canWrite() ) { + path = null; + } + return path; + } + } } From 6789a64b68060bebe230334a80c0b6864225d4cf Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Dec 2012 08:14:55 -0800 Subject: [PATCH 16/50] reduce redundant code (hopefully without adding too much confusion) --- .../org/eehouse/android/xw4/DictUtils.java | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java index 6adcc5e6e..05fff08d0 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java @@ -580,37 +580,45 @@ public class DictUtils { return null != getDownloadDir( context ); } - // The approved way to get this is to call Environment. - // getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), - // but at least on my Samsung Galaxy Blaze 4G it returns a - // directory that does not exist! (FU, Samsung...) So in that - // case fall back to using getExternalStorageDirectory and - // appending "download" + // Loop through three ways of getting the directory until one + // produces a directory I can write to. public static File getDownloadDir( Context context ) { File result = null; - if ( null != s_dirGetter ) { - result = s_dirGetter.getDownloadDir(); - } - if ( null != result ) { - // we're done - } else if ( haveWriteableSD() ) { - File file = null; - String myPath = XWPrefs.getMyDownloadDir( context ); - if ( null != myPath && 0 < myPath.length() ) { - file = new File( myPath ); - } else { - file = Environment.getExternalStorageDirectory(); - if ( null != file ) { - File child = new File( file, "download/" ); - if ( child.exists() && child.isDirectory() - && child.canWrite() ) { - file = child; - } + outer: + for ( int attempt = 0; attempt < 4; ++attempt ) { + switch ( attempt ) { + case 0: + String myPath = XWPrefs.getMyDownloadDir( context ); + if ( null == myPath || 0 == myPath.length() ) { + continue; } + result = new File( myPath ); + break; + case 1: + if ( null == s_dirGetter ) { + continue; + } + result = s_dirGetter.getDownloadDir(); + break; + case 2: + case 3: + if ( !haveWriteableSD() ) { + continue; + } + result = Environment.getExternalStorageDirectory(); + if ( 2 == attempt && null != result ) { + // the old way... + result = new File( result, "download/" ); + } + break; } - if ( null != file && file.exists() && file.isDirectory() ) { - result = file; + + // Exit test for loop + if ( null != result ) { + if ( result.exists() && result.isDirectory() && result.canWrite() ) { + break outer; + } } } return result; @@ -631,9 +639,6 @@ public class DictUtils { { File path = Environment. getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); - if ( null != path && !path.canWrite() ) { - path = null; - } return path; } } From e719c6e61d0cf0b5e760d222b7b29823571d6090 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Dec 2012 08:15:48 -0800 Subject: [PATCH 17/50] don't crash if for some reason json file can't be written --- .../src/org/eehouse/android/xw4/GameUtils.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 52b274823..420819c46 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -533,18 +533,22 @@ public class GameUtils { intent.putExtra( Intent.EXTRA_SUBJECT, subject ); intent.putExtra( Intent.EXTRA_TEXT, Html.fromHtml(message) ); + File attach = null; File tmpdir = XWApp.ATTACH_SUPPORTED ? DictUtils.getDownloadDir( context ) : null; - if ( null == tmpdir ) { // no attachment + if ( null != tmpdir ) { // no attachment + attach = makeJsonFor( tmpdir, room, inviteID, lang, + dict, nPlayers ); + } + + if ( null == attach ) { // no attachment intent.setType( "message/rfc822"); } else { - intent.setType( context.getString( R.string.invite_mime ) ); - - File attach = makeJsonFor( tmpdir, room, inviteID, lang, - dict, nPlayers ); + String mime = context.getString( R.string.invite_mime ); + intent.setType( mime ); Uri uri = Uri.fromFile( attach ); - DbgUtils.logf( "using file uri for attachment: %s", - uri.toString() ); + DbgUtils.logf( "using file uri %s, type %s for attachment", + uri.toString(), mime ); intent.putExtra( Intent.EXTRA_STREAM, uri ); } From 93bf2a1b3ec2c7e23aa7051acc97df49699319b3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Dec 2012 19:15:20 -0800 Subject: [PATCH 18/50] hard-code tiles drawn nearly-square, just to see what it looks like. Making them exactly square would probably take a new jni call. And if I ship this it needs to be optional, controlled by a preference. --- .../XWords4/src/org/eehouse/android/xw4/BoardView.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java index eea07b086..d05909c99 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -53,6 +53,8 @@ public class BoardView extends View implements DrawCtx, BoardHandler, private static final int PINCH_THRESHOLD = 40; private static final int SCORE_HT_DROP = 2; private static final boolean DEBUG_DRAWFRAMES = false; + // this can be a preference + private static final boolean MAKETILESSQUARE = true; private Context m_context; private Paint m_drawPaint; @@ -379,8 +381,12 @@ public class BoardView extends View implements DrawCtx, BoardHandler, heightLeft = cellSize * 3 / 2; } heightLeft /= 3; - trayHt += heightLeft * 2; scoreHt += heightLeft; + + trayHt += heightLeft * 2; + if ( MAKETILESSQUARE && trayHt > width / 7 ) { + trayHt = width / 7; + } heightUsed = trayHt + scoreHt + ((nCells - nToScroll) * cellSize); } From 83b1d4c364f4c76c0ececde36873d7b480c3f004 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 8 Dec 2012 06:40:21 -0800 Subject: [PATCH 19/50] fix compiler warnings --- xwords4/common/game.c | 14 +++++++++----- xwords4/common/server.c | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/xwords4/common/game.c b/xwords4/common/game.c index f61e25ab2..ae73da301 100644 --- a/xwords4/common/game.c +++ b/xwords4/common/game.c @@ -93,10 +93,12 @@ game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi, #endif ) { - XP_U16 nPlayersHere, nPlayersTotal; - - assertUtilOK( util ); +#ifndef XWFEATURE_STANDALONE_ONLY + XP_U16 nPlayersHere = 0; + XP_U16 nPlayersTotal = 0; checkServerRole( gi, &nPlayersHere, &nPlayersTotal ); +#endif + assertUtilOK( util ); gi->gameID = makeGameID( util ); @@ -137,15 +139,17 @@ game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, CommonPrefs* cp, const TransportProcs* procs ) { XP_U16 ii; - XP_U16 nPlayersHere, nPlayersTotal; XP_ASSERT( !!game->model ); XP_ASSERT( !!gi ); - checkServerRole( gi, &nPlayersHere, &nPlayersTotal ); gi->gameID = makeGameID( util ); #ifndef XWFEATURE_STANDALONE_ONLY + XP_U16 nPlayersHere = 0; + XP_U16 nPlayersTotal = 0; + checkServerRole( gi, &nPlayersHere, &nPlayersTotal ); + if ( !!game->comms ) { if ( gi->serverRole == SERVER_STANDALONE ) { comms_destroy( game->comms ); diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 95e4c0759..aebb41ab2 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -686,7 +686,7 @@ handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ) { XP_Bool success = XP_TRUE; XP_U16 playersInMsg; - XP_S8 clientIndex; + XP_S8 clientIndex = 0; /* quiet compiler */ XP_U16 ii = 0; LOG_FUNC(); From 7efbd2697d3ab8a55e9d131d8a64c7266a98df24 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 8 Dec 2012 08:47:53 -0800 Subject: [PATCH 20/50] rewrite list item logic. Use a single custom LinearLayout subclass for both the loading and loaded phases, toggling its state once the data's available. Reuse it: pay attention to what's passed into getView and only allocate when there's no existing View to reuse. Stop caching Views, as that defeats Android list logic that might limit in-memory representation to the subset that's visible on-screen, instead tracking a set of rowids whose data is known to be good as a way of quickly drawing when there's a refresh. --- .../XWords4/res/layout/game_list_item.xml | 185 ++++---- .../eehouse/android/xw4/GameListAdapter.java | 431 ++++++++---------- .../org/eehouse/android/xw4/GameListItem.java | 115 +++++ .../org/eehouse/android/xw4/GamesList.java | 8 +- .../eehouse/android/xw4/XWListAdapter.java | 5 +- 5 files changed, 420 insertions(+), 324 deletions(-) create mode 100644 xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java diff --git a/xwords4/android/XWords4/res/layout/game_list_item.xml b/xwords4/android/XWords4/res/layout/game_list_item.xml index 1a6ab2cb1..771af6471 100644 --- a/xwords4/android/XWords4/res/layout/game_list_item.xml +++ b/xwords4/android/XWords4/res/layout/game_list_item.xml @@ -3,95 +3,114 @@ - + - + - - - - - - - - - - - - - - + android:visibility="gone" + > - - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index bcdfbefe3..87b65e852 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -35,7 +35,7 @@ import android.widget.TextView; import java.io.FileInputStream; import java.text.DateFormat; import java.util.Date; -import java.util.HashMap; // class is not synchronized +import java.util.HashSet; import java.util.Random; import junit.framework.Assert; @@ -46,10 +46,6 @@ import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; public class GameListAdapter extends XWListAdapter { - private Context m_context; - private LayoutInflater m_factory; - private int m_fieldID; - private Handler m_handler; private static final boolean s_isFire; private static Random s_random; static { @@ -59,79 +55,35 @@ public class GameListAdapter extends XWListAdapter { } } - private class ViewInfo implements View.OnClickListener { - private View m_view; - private View m_hideable; - private ExpiringTextView m_name; - private boolean m_expanded, m_haveTurn, m_haveTurnLocal; - private long m_rowid; - private long m_lastMoveTime; - private ImageButton m_expandButton; - - public ViewInfo( View view, long rowid ) - { - m_view = view; - m_rowid = rowid; - m_lastMoveTime = 0; - } - - public ViewInfo( View view, long rowid, boolean expanded, - long lastMoveTime, boolean haveTurn, - boolean haveTurnLocal ) { - this( view, rowid ); - m_expanded = expanded; - m_lastMoveTime = lastMoveTime; - m_haveTurn = haveTurn; - m_haveTurnLocal = haveTurnLocal; - m_hideable = (LinearLayout)view.findViewById( R.id.hideable ); - m_name = (ExpiringTextView)m_view.findViewById( R.id.game_name ); - m_expandButton = (ImageButton)view.findViewById( R.id.expander ); - m_expandButton.setOnClickListener( this ); - showHide(); - } - - private void showHide() - { - m_expandButton.setImageResource( m_expanded ? - R.drawable.expander_ic_maximized : - R.drawable.expander_ic_minimized); - m_hideable.setVisibility( m_expanded? View.VISIBLE : View.GONE ); - - m_name.setBackgroundColor( android.R.color.transparent ); - m_name.setPct( m_handler, m_haveTurn && !m_expanded, - m_haveTurnLocal, m_lastMoveTime ); - } - - public void onClick( View view ) { - m_expanded = !m_expanded; - DBUtils.setExpanded( m_rowid, m_expanded ); - showHide(); - } - } - - private HashMap m_viewsCache; + private Context m_context; + private LayoutInflater m_factory; + private int m_fieldID; + private Handler m_handler; private DateFormat m_df; private LoadItemCB m_cb; - + // Track those rows known to be good. If a rowid is not in this + // set, assume it must be loaded. Add rowids to this set as + // they're loaded, and remove one when when it must be redrawn. + private HashSet m_loadedRows; public interface LoadItemCB { - public void itemLoaded( long rowid ); public void itemClicked( long rowid ); } - private class LoadItemTask extends AsyncTask { - private long m_rowid; + private class LoadItemTask extends AsyncTask { + private GameListItem m_view; private Context m_context; // private int m_id; - public LoadItemTask( Context context, long rowid/*, int id*/ ) + public LoadItemTask( Context context, GameListItem view ) { + DbgUtils.logf( "Creating LoadItemTask for row %d", + view.getRowID() ); m_context = context; - m_rowid = rowid; - // m_id = id; + m_view = view; } @Override - protected Void doInBackground( Void... unused ) + protected GameSummary doInBackground( Void... unused ) { // Without this, on the Fire only the last item in the // list it tappable. Likely my fault, but this seems to @@ -143,133 +95,26 @@ public class GameListAdapter extends XWListAdapter { } catch ( Exception e ) { } } - View layout = m_factory.inflate( R.layout.game_list_item, null ); - boolean hideTitle = false;//CommonPrefs.getHideTitleBar(m_context); - GameSummary summary = DBUtils.getSummary( m_context, m_rowid, 1500 ); - if ( null == summary ) { - m_rowid = -1; - } else { - String state = summary.summarizeState(); - TextView view = (TextView)layout.findViewById( R.id.game_name ); - if ( hideTitle ) { - view.setVisibility( View.GONE ); - } else { - String value = null; - switch ( m_fieldID ) { - case R.string.game_summary_field_empty: - break; - case R.string.game_summary_field_language: - value = - DictLangCache.getLangName( m_context, - summary.dictLang ); - break; - case R.string.game_summary_field_opponents: - value = summary.playerNames(); - break; - case R.string.game_summary_field_state: - value = state; - break; - } - - String name = GameUtils.getName( m_context, m_rowid ); - - if ( null != value ) { - value = m_context.getString( R.string.str_game_namef, - name, value ); - } else { - value = name; - } - - view.setText( value ); - } - - layout.setOnClickListener( new View.OnClickListener() { - @Override - public void onClick( View v ) { - m_cb.itemClicked( m_rowid ); - } - } ); - - LinearLayout list = - (LinearLayout)layout.findViewById( R.id.player_list ); - boolean haveATurn = false; - boolean haveALocalTurn = false; - boolean[] isLocal = new boolean[1]; - for ( int ii = 0; ii < summary.nPlayers; ++ii ) { - ExpiringLinearLayout tmp = (ExpiringLinearLayout) - m_factory.inflate( R.layout.player_list_elem, null ); - view = (TextView)tmp.findViewById( R.id.item_name ); - view.setText( summary.summarizePlayer( ii ) ); - view = (TextView)tmp.findViewById( R.id.item_score ); - view.setText( String.format( " %d", summary.scores[ii] ) ); - boolean thisHasTurn = summary.isNextToPlay( ii, isLocal ); - if ( thisHasTurn ) { - haveATurn = true; - if ( isLocal[0] ) { - haveALocalTurn = true; - } - } - tmp.setPct( m_handler, thisHasTurn, isLocal[0], - summary.lastMoveTime ); - list.addView( tmp, ii ); - } - - view = (TextView)layout.findViewById( R.id.state ); - view.setText( state ); - view = (TextView)layout.findViewById( R.id.modtime ); - long lastMoveTime = summary.lastMoveTime; - lastMoveTime *= 1000; - view.setText( m_df.format( new Date( lastMoveTime ) ) ); - - int iconID; - ImageView marker = - (ImageView)layout.findViewById( R.id.msg_marker ); - CommsConnType conType = summary.conType; - if ( CommsConnType.COMMS_CONN_RELAY == conType ) { - iconID = R.drawable.relaygame; - } else if ( CommsConnType.COMMS_CONN_BT == conType ) { - iconID = android.R.drawable.stat_sys_data_bluetooth; - } else if ( CommsConnType.COMMS_CONN_SMS == conType ) { - iconID = android.R.drawable.sym_action_chat; - } else { - iconID = R.drawable.sologame; - } - marker.setImageResource( iconID ); - - view = (TextView)layout.findViewById( R.id.role ); - String roleSummary = summary.summarizeRole(); - if ( null != roleSummary ) { - view.setText( roleSummary ); - } else { - view.setVisibility( View.GONE ); - } - - boolean expanded = DBUtils.getExpanded( m_context, m_rowid ); - ViewInfo vi = new ViewInfo( layout, m_rowid, expanded, - summary.lastMoveTime, haveATurn, - haveALocalTurn ); - if ( XWApp.DEBUG ) { - DbgUtils.logf( "created new view for rowid %d", m_rowid ); - } - synchronized( m_viewsCache ) { - m_viewsCache.put( m_rowid, vi ); - } - } - return null; + long rowid = m_view.getRowID(); + GameSummary summary = DBUtils.getSummary( m_context, rowid, 1500 ); + return summary; } // doInBackground @Override - protected void onPostExecute( Void unused ) + protected void onPostExecute( GameSummary summary ) { - // DbgUtils.logf( "onPostExecute(rowid=%d)", m_rowid ); - if ( -1 != m_rowid ) { - m_cb.itemLoaded( m_rowid ); - } + setData( m_view, summary ); + setLoaded( m_view.getRowID() ); + m_view.setLoaded( true ); + + DbgUtils.logf( "LoadItemTask for row %d finished", + m_view.getRowID() ); } } // class LoadItemTask - public GameListAdapter( Context context, Handler handler, LoadItemCB cb ) { + public GameListAdapter( Context context, Handler handler, LoadItemCB cb, + String fieldName ) { super( DBUtils.gamesList(context).length ); m_context = context; m_handler = handler; @@ -278,62 +123,92 @@ public class GameListAdapter extends XWListAdapter { m_df = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT ); - m_viewsCache = new HashMap(); + m_loadedRows = new HashSet(); + m_fieldID = fieldToID( fieldName ); } - + + @Override public int getCount() { return DBUtils.gamesList(m_context).length; } - - public Object getItem( int position ) + + // Views. A view depends on a summary, which takes time to load. + // When one needs loading it's done via an async task. + public View getView( int position, View convertView, ViewGroup parent ) { - final long rowid = DBUtils.gamesList(m_context)[position]; - View layout; - boolean haveLayout = false; - synchronized( m_viewsCache ) { - ViewInfo vi = m_viewsCache.get( rowid ); - haveLayout = null != vi; - if ( haveLayout ) { - layout = vi.m_view; - } else { - layout = m_factory.inflate( R.layout.game_list_tmp, null ); - vi = new ViewInfo( layout, rowid ); - m_viewsCache.put( rowid, vi ); + GameListItem result; + boolean mustLoad = false; + if ( null == convertView ) { + result = (GameListItem) + m_factory.inflate( R.layout.game_list_item, null ); + result.setRowID( DBUtils.gamesList(m_context)[position] ); + mustLoad = true; + } else { + result = (GameListItem)convertView; + long rowid = result.getRowID(); + if ( isDirty(rowid) || !result.isLoaded() ) { + mustLoad = true; } } - if ( !haveLayout ) { - new LoadItemTask( m_context, rowid/*, ++m_taskCounter*/ ).execute(); + if ( mustLoad ) { + new LoadItemTask( m_context, result ).execute(); } - // this doesn't work. Rather, it breaks highlighting because - // the background, if we don't set it, is a more complicated - // object like @android:drawable/list_selector_background. I - // tried calling getBackground(), expecting to get a Drawable - // I could then clone and modify, but null comes back. So - // layout must be inheriting its background from elsewhere or - // it gets set later, during layout. - // if ( (position%2) == 0 ) { - // layout.setBackgroundColor( 0xFF3F3F3F ); - // } - - return layout; - } // getItem - - public View getView( int position, View convertView, ViewGroup parent ) { - return (View)getItem( position ); + return result; } public void inval( long rowid ) { - synchronized( m_viewsCache ) { - m_viewsCache.remove( rowid ); + synchronized( m_loadedRows ) { + m_loadedRows.remove( rowid ); + } + } + + private void dirtyAll() + { + synchronized( m_loadedRows ) { + m_loadedRows.clear(); + } + } + + private boolean isDirty( long rowid ) + { + synchronized( m_loadedRows ) { + return ! m_loadedRows.contains( rowid ); + } + } + + private void setLoaded( long rowid ) + { + synchronized( m_loadedRows ) { + m_loadedRows.add( rowid ); } } public boolean setField( String fieldName ) { boolean changed = false; + int newID = fieldToID( fieldName ); + if ( -1 == newID ) { + if ( XWApp.DEBUG ) { + DbgUtils.logf( "GameListAdapter.setField(): unable to match" + + " fieldName %s", fieldName ); + } + } else if ( m_fieldID != newID ) { + if ( XWApp.DEBUG ) { + DbgUtils.logf( "setField: clearing views cache for change" + + " from %d to %d", m_fieldID, newID ); + } + m_fieldID = newID; + dirtyAll(); + changed = true; + } + return changed; + } + + private int fieldToID( String fieldName ) + { int[] ids = { R.string.game_summary_field_empty ,R.string.game_summary_field_language @@ -347,21 +222,109 @@ public class GameListAdapter extends XWListAdapter { break; } } - if ( -1 == result ) { - if ( XWApp.DEBUG ) { - DbgUtils.logf( "GameListAdapter.setField(): unable to match" - + " fieldName %s", fieldName ); - } - } else if ( m_fieldID != result ) { - if ( XWApp.DEBUG ) { - DbgUtils.logf( "setField: clearing views cache for change" - + " from %d to %d", m_fieldID, result ); - } - m_viewsCache.clear(); - m_fieldID = result; - changed = true; - } - return changed; + return result; } + private void setData( GameListItem layout, GameSummary summary ) + { + if ( null != summary ) { + final long rowid = layout.getRowID(); + String state = summary.summarizeState(); + + TextView view = (TextView)layout.findViewById( R.id.game_name ); + String value = null; + switch ( m_fieldID ) { + case R.string.game_summary_field_empty: + break; + case R.string.game_summary_field_language: + value = + DictLangCache.getLangName( m_context, + summary.dictLang ); + break; + case R.string.game_summary_field_opponents: + value = summary.playerNames(); + break; + case R.string.game_summary_field_state: + value = state; + break; + } + + String name = GameUtils.getName( m_context, rowid ); + + if ( null != value ) { + value = m_context.getString( R.string.str_game_namef, + name, value ); + } else { + value = name; + } + + view.setText( value ); + + layout.setOnClickListener( new View.OnClickListener() { + @Override + public void onClick( View v ) { + m_cb.itemClicked( rowid ); + } + } ); + + LinearLayout list = + (LinearLayout)layout.findViewById( R.id.player_list ); + boolean haveATurn = false; + boolean haveALocalTurn = false; + boolean[] isLocal = new boolean[1]; + for ( int ii = 0; ii < summary.nPlayers; ++ii ) { + ExpiringLinearLayout tmp = (ExpiringLinearLayout) + m_factory.inflate( R.layout.player_list_elem, null ); + view = (TextView)tmp.findViewById( R.id.item_name ); + view.setText( summary.summarizePlayer( ii ) ); + view = (TextView)tmp.findViewById( R.id.item_score ); + view.setText( String.format( " %d", summary.scores[ii] ) ); + boolean thisHasTurn = summary.isNextToPlay( ii, isLocal ); + if ( thisHasTurn ) { + haveATurn = true; + if ( isLocal[0] ) { + haveALocalTurn = true; + } + } + tmp.setPct( m_handler, thisHasTurn, isLocal[0], + summary.lastMoveTime ); + list.addView( tmp, ii ); + } + + view = (TextView)layout.findViewById( R.id.state ); + view.setText( state ); + view = (TextView)layout.findViewById( R.id.modtime ); + long lastMoveTime = summary.lastMoveTime; + lastMoveTime *= 1000; + view.setText( m_df.format( new Date( lastMoveTime ) ) ); + + int iconID; + ImageView marker = + (ImageView)layout.findViewById( R.id.msg_marker ); + CommsConnType conType = summary.conType; + if ( CommsConnType.COMMS_CONN_RELAY == conType ) { + iconID = R.drawable.relaygame; + } else if ( CommsConnType.COMMS_CONN_BT == conType ) { + iconID = android.R.drawable.stat_sys_data_bluetooth; + } else if ( CommsConnType.COMMS_CONN_SMS == conType ) { + iconID = android.R.drawable.sym_action_chat; + } else { + iconID = R.drawable.sologame; + } + marker.setImageResource( iconID ); + + view = (TextView)layout.findViewById( R.id.role ); + String roleSummary = summary.summarizeRole(); + if ( null != roleSummary ) { + view.setText( roleSummary ); + } else { + view.setVisibility( View.GONE ); + } + + boolean expanded = DBUtils.getExpanded( m_context, rowid ); + + layout.update( m_handler, expanded, summary.lastMoveTime, + haveATurn, haveALocalTurn ); + } + } } \ No newline at end of file diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java new file mode 100644 index 000000000..fb03482d2 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java @@ -0,0 +1,115 @@ +/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */ +/* + * Copyright 2009-2012 by Eric House (xwords@eehouse.org). All rights + * reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.eehouse.android.xw4; + +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; + +public class GameListItem extends LinearLayout + implements View.OnClickListener { + + private Context m_context; + private boolean m_loaded; + private long m_rowid; + private View m_hideable; + private ExpiringTextView m_name; + private boolean m_expanded, m_haveTurn, m_haveTurnLocal; + private long m_lastMoveTime; + private ImageButton m_expandButton; + private Handler m_handler; + + public GameListItem( Context cx, AttributeSet as ) + { + super( cx, as ); + m_context = cx; + m_loaded = false; + m_rowid = DBUtils.ROWID_NOTFOUND; + m_lastMoveTime = 0; + } + + public void update( Handler handler, boolean expanded, + long lastMoveTime, boolean haveTurn, + boolean haveTurnLocal ) + { + m_handler = handler; + m_expanded = expanded; + m_lastMoveTime = lastMoveTime; + m_haveTurn = haveTurn; + m_haveTurnLocal = haveTurnLocal; + m_hideable = (LinearLayout)findViewById( R.id.hideable ); + m_name = (ExpiringTextView)findViewById( R.id.game_name ); + m_expandButton = (ImageButton)findViewById( R.id.expander ); + m_expandButton.setOnClickListener( this ); + showHide(); + } + + public void setLoaded( boolean loaded ) + { + if ( m_loaded != loaded ) { + m_loaded = loaded; + // This should be enough to invalidate + findViewById( R.id.view_unloaded ) + .setVisibility( loaded ? View.GONE : View.VISIBLE ); + findViewById( R.id.view_loaded ) + .setVisibility( loaded ? View.VISIBLE : View.GONE ); + } + } + + public boolean isLoaded() + { + return m_loaded; + } + + public void setRowID( long rowid ) + { + m_rowid = rowid; + } + + public long getRowID() + { + return m_rowid; + } + + // View.OnClickListener interface + public void onClick( View view ) { + m_expanded = !m_expanded; + DBUtils.setExpanded( m_rowid, m_expanded ); + showHide(); + } + + private void showHide() + { + m_expandButton.setImageResource( m_expanded ? + R.drawable.expander_ic_maximized : + R.drawable.expander_ic_minimized); + m_hideable.setVisibility( m_expanded? View.VISIBLE : View.GONE ); + + m_name.setBackgroundColor( android.R.color.transparent ); + m_name.setPct( m_handler, m_haveTurn && !m_expanded, + m_haveTurnLocal, m_lastMoveTime ); + } + + +} diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index fbed5b9d7..3bb68c661 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -282,7 +282,8 @@ public class GamesList extends XWListActivity } }); - m_adapter = new GameListAdapter( this, new Handler(), this ); + String field = CommonPrefs.getSummaryField( this ); + m_adapter = new GameListAdapter( this, new Handler(), this, field ); setListAdapter( m_adapter ); NetUtils.informOfDeaths( this ); @@ -391,11 +392,6 @@ public class GamesList extends XWListActivity } // GameListAdapter.LoadItemCB interface - public void itemLoaded( long rowid ) - { - onContentChanged(); - } - public void itemClicked( long rowid ) { // We need a way to let the user get back to the basic-config diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java index 069191e81..52306419b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java @@ -41,8 +41,11 @@ public abstract class XWListAdapter implements ListAdapter { public boolean areAllItemsEnabled() { return true; } public boolean isEnabled( int position ) { return true; } public int getCount() { return m_count; } + public Object getItem( int position ) { return null; } public long getItemId(int position) { return position; } - public int getItemViewType(int position) { return 0; } + public int getItemViewType( int position ) { + return ListAdapter.IGNORE_ITEM_VIEW_TYPE; + } public int getViewTypeCount() { return 1; } public boolean hasStableIds() { return true; } public boolean isEmpty() { return getCount() == 0; } From ce803a928cdcc3f50ef475b5ba8c2f3613f9e601 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 8 Dec 2012 08:55:45 -0800 Subject: [PATCH 21/50] pass summary rather than have callee refetch it --- .../src/org/eehouse/android/xw4/GameListAdapter.java | 6 +++--- .../XWords4/src/org/eehouse/android/xw4/GamesList.java | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index 87b65e852..9693a0eba 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -67,7 +67,7 @@ public class GameListAdapter extends XWListAdapter { private HashSet m_loadedRows; public interface LoadItemCB { - public void itemClicked( long rowid ); + public void itemClicked( long rowid, GameSummary summary ); } private class LoadItemTask extends AsyncTask { @@ -225,7 +225,7 @@ public class GameListAdapter extends XWListAdapter { return result; } - private void setData( GameListItem layout, GameSummary summary ) + private void setData( GameListItem layout, final GameSummary summary ) { if ( null != summary ) { final long rowid = layout.getRowID(); @@ -263,7 +263,7 @@ public class GameListAdapter extends XWListAdapter { layout.setOnClickListener( new View.OnClickListener() { @Override public void onClick( View v ) { - m_cb.itemClicked( rowid ); + m_cb.itemClicked( rowid, summary ); } } ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 3bb68c661..99697680b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -392,12 +392,11 @@ public class GamesList extends XWListActivity } // GameListAdapter.LoadItemCB interface - public void itemClicked( long rowid ) + public void itemClicked( long rowid, GameSummary summary ) { // We need a way to let the user get back to the basic-config // dialog in case it was dismissed. That way it to check for // an empty room name. - GameSummary summary = DBUtils.getSummary( this, rowid ); if ( summary.conType == CommsAddrRec.CommsConnType.COMMS_CONN_RELAY && summary.roomName.length() == 0 ) { // If it's unconfigured and of the type RelayGameActivity From 3a45db66cf6a3b50580445959b811704b2e1b0a4 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 8 Dec 2012 20:28:11 -0800 Subject: [PATCH 22/50] cache array of rowids, and clear cache appropriately, rather than query DB for all games every time. --- .../src/org/eehouse/android/xw4/DBUtils.java | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 303b09af9..e656377e7 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -60,6 +60,7 @@ public class DBUtils { private static long s_cachedRowID = -1; private static byte[] s_cachedBytes = null; + private static long[] s_cachedRowIDs = null; public static interface DBChangeListener { public void gameSaved( long rowid ); @@ -314,6 +315,9 @@ public class DBUtils { long result = db.update( DBHelper.TABLE_NAME_SUM, values, selection, null ); Assert.assertTrue( result >= 0 ); + if ( result != rowid ) { // new row added + clearRowIDsCache(); + } } notifyListeners( rowid ); db.close(); @@ -675,7 +679,8 @@ public class DBUtils { } } - public static GameUtils.GameLock saveNewGame( Context context, byte[] bytes ) + public static GameUtils.GameLock saveNewGame( Context context, + byte[] bytes ) { GameUtils.GameLock lock = null; @@ -695,9 +700,9 @@ public class DBUtils { long rowid = db.insert( DBHelper.TABLE_NAME_SUM, null, values ); setCached( rowid, null ); // force reread + clearRowIDsCache(); lock = new GameUtils.GameLock( rowid, true ).lock(); - notifyListeners( rowid ); } @@ -771,34 +776,47 @@ public class DBUtils { db.delete( DBHelper.TABLE_NAME_SUM, selection, null ); db.close(); } + clearRowIDsCache(); notifyListeners( lock.getRowid() ); } public static long[] gamesList( Context context ) { - long[] result = null; + long[] result; + synchronized( DBUtils.class ) { + if ( null == s_cachedRowIDs ) { + initDB( context ); + synchronized( s_dbHelper ) { + SQLiteDatabase db = s_dbHelper.getReadableDatabase(); - initDB( context ); - synchronized( s_dbHelper ) { - SQLiteDatabase db = s_dbHelper.getReadableDatabase(); - - String[] columns = { ROW_ID }; - String orderBy = DBHelper.CREATE_TIME + " DESC"; - Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, - null, null, null, null, orderBy ); - int count = cursor.getCount(); - result = new long[count]; - int index = cursor.getColumnIndex( ROW_ID ); - for ( int ii = 0; cursor.moveToNext(); ++ii ) { - result[ii] = cursor.getLong( index ); + String[] columns = { ROW_ID }; + String orderBy = DBHelper.CREATE_TIME + " DESC"; + Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, + columns, null, null, null, + null, orderBy ); + int count = cursor.getCount(); + s_cachedRowIDs = new long[count]; + int index = cursor.getColumnIndex( ROW_ID ); + for ( int ii = 0; cursor.moveToNext(); ++ii ) { + s_cachedRowIDs[ii] = cursor.getLong( index ); + } + cursor.close(); + db.close(); + } } - cursor.close(); - db.close(); + result = s_cachedRowIDs; } - return result; } + private static void clearRowIDsCache() + { + DbgUtils.logf( "DBUtils.clearRowIDsCache()" ); + synchronized( DBUtils.class ) { + s_cachedRowIDs = null; + } + } + // Get either the file name or game name, preferring the latter. public static String getName( Context context, long rowid ) { From 1bc8070bb1bb3c0141e9e289d277550201404bc6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 10 Dec 2012 07:11:13 -0800 Subject: [PATCH 23/50] disable Rematch button (for now) --- .../XWords4/src/org/eehouse/android/xw4/BoardActivity.java | 2 +- xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 9998ba315..e638df480 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -248,7 +248,7 @@ public class BoardActivity extends XWActivity } }; ab.setNegativeButton( R.string.button_retry, lstnr ); - } else if ( GAME_OVER == id ) { + } else if ( XWApp.REMATCH_SUPPORTED && GAME_OVER == id ) { lstnr = new DialogInterface.OnClickListener() { public void onClick( DialogInterface dlg, int whichButton ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index 57927412b..24edc0066 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -33,6 +33,7 @@ public class XWApp extends Application { public static final boolean SMSSUPPORTED = true; public static final boolean GCMSUPPORTED = true; public static final boolean ATTACH_SUPPORTED = true; + public static final boolean REMATCH_SUPPORTED = false; public static final boolean DEBUG = true; // DON'T SHIP THIS WAY public static final String SMS_PUBLIC_HEADER = "-XW4"; From d820554ffba3ae463a4105809b2123ee1936440b Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 10 Dec 2012 07:48:15 -0800 Subject: [PATCH 24/50] change game list item strategy since it turns out adapter's findView() doesn't pass in the previous representation of a given item for recycling: move async loading of summary into GameListItem class, and use getChildAt() to invalidate a single list (rather than reloading the whole list) whereever possible. Still need to dump the list whenever the number of items changes since we're depending on DBUtils to determine the order and have no way to reshuffle existing items. --- .../src/org/eehouse/android/xw4/DBUtils.java | 18 +- .../eehouse/android/xw4/GameListAdapter.java | 236 +++--------------- .../org/eehouse/android/xw4/GameListItem.java | 203 +++++++++++++-- .../org/eehouse/android/xw4/GamesList.java | 21 +- .../eehouse/android/xw4/XWListAdapter.java | 6 +- 5 files changed, 241 insertions(+), 243 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index e656377e7..8deebeeaa 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -63,7 +63,7 @@ public class DBUtils { private static long[] s_cachedRowIDs = null; public static interface DBChangeListener { - public void gameSaved( long rowid ); + public void gameSaved( long rowid, boolean countChanged ); } private static HashSet s_listeners = new HashSet(); @@ -319,8 +319,8 @@ public class DBUtils { clearRowIDsCache(); } } - notifyListeners( rowid ); db.close(); + notifyListeners( rowid, false ); } } // saveSummary @@ -376,7 +376,7 @@ public class DBUtils { public static void setMsgFlags( long rowid, int flags ) { setInt( rowid, DBHelper.HASMSGS, flags ); - notifyListeners( rowid ); + notifyListeners( rowid, false ); } public static void setExpanded( long rowid, boolean expanded ) @@ -703,7 +703,7 @@ public class DBUtils { clearRowIDsCache(); lock = new GameUtils.GameLock( rowid, true ).lock(); - notifyListeners( rowid ); + notifyListeners( rowid, true ); } return lock; @@ -727,8 +727,8 @@ public class DBUtils { updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values ); setCached( rowid, null ); // force reread - if ( -1 != rowid ) { // Is this possible? PENDING - notifyListeners( rowid ); + if ( -1 != rowid ) { // Means new game? + notifyListeners( rowid, false ); } return rowid; } @@ -777,7 +777,7 @@ public class DBUtils { db.close(); } clearRowIDsCache(); - notifyListeners( lock.getRowid() ); + notifyListeners( lock.getRowid(), true ); } public static long[] gamesList( Context context ) @@ -1223,12 +1223,12 @@ public class DBUtils { } } - private static void notifyListeners( long rowid ) + private static void notifyListeners( long rowid, boolean countChanged ) { synchronized( s_listeners ) { Iterator iter = s_listeners.iterator(); while ( iter.hasNext() ) { - iter.next().gameSaved( rowid ); + iter.next().gameSaved( rowid, countChanged ); } } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index 9693a0eba..26405c0a2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -21,7 +21,6 @@ package org.eehouse.android.xw4; import android.content.Context; import android.database.DataSetObserver; -import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.view.LayoutInflater; @@ -31,11 +30,11 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListAdapter; +import android.widget.ListView; import android.widget.TextView; import java.io.FileInputStream; import java.text.DateFormat; import java.util.Date; -import java.util.HashSet; import java.util.Random; import junit.framework.Assert; @@ -46,84 +45,29 @@ import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; public class GameListAdapter extends XWListAdapter { - private static final boolean s_isFire; - private static Random s_random; - static { - s_isFire = Build.MANUFACTURER.equals( "Amazon" ); - if ( s_isFire ) { - s_random = new Random(); - } - } - private Context m_context; + private ListView m_list; private LayoutInflater m_factory; private int m_fieldID; private Handler m_handler; private DateFormat m_df; private LoadItemCB m_cb; - // Track those rows known to be good. If a rowid is not in this - // set, assume it must be loaded. Add rowids to this set as - // they're loaded, and remove one when when it must be redrawn. - private HashSet m_loadedRows; public interface LoadItemCB { public void itemClicked( long rowid, GameSummary summary ); } - private class LoadItemTask extends AsyncTask { - private GameListItem m_view; - private Context m_context; - // private int m_id; - public LoadItemTask( Context context, GameListItem view ) - { - DbgUtils.logf( "Creating LoadItemTask for row %d", - view.getRowID() ); - m_context = context; - m_view = view; - } - - @Override - protected GameSummary doInBackground( Void... unused ) - { - // Without this, on the Fire only the last item in the - // list it tappable. Likely my fault, but this seems to - // work around it. - if ( s_isFire ) { - try { - int sleepTime = 500 + (s_random.nextInt() % 500); - Thread.sleep( sleepTime ); - } catch ( Exception e ) { - } - } - - long rowid = m_view.getRowID(); - GameSummary summary = DBUtils.getSummary( m_context, rowid, 1500 ); - return summary; - } // doInBackground - - @Override - protected void onPostExecute( GameSummary summary ) - { - setData( m_view, summary ); - setLoaded( m_view.getRowID() ); - m_view.setLoaded( true ); - - DbgUtils.logf( "LoadItemTask for row %d finished", - m_view.getRowID() ); - } - } // class LoadItemTask - - public GameListAdapter( Context context, Handler handler, LoadItemCB cb, - String fieldName ) { + public GameListAdapter( Context context, ListView list, + Handler handler, LoadItemCB cb, String fieldName ) { super( DBUtils.gamesList(context).length ); m_context = context; + m_list = list; m_handler = handler; m_cb = cb; m_factory = LayoutInflater.from( context ); m_df = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT ); - m_loadedRows = new HashSet(); m_fieldID = fieldToID( fieldName ); } @@ -136,53 +80,29 @@ public class GameListAdapter extends XWListAdapter { // When one needs loading it's done via an async task. public View getView( int position, View convertView, ViewGroup parent ) { - GameListItem result; - boolean mustLoad = false; - if ( null == convertView ) { - result = (GameListItem) - m_factory.inflate( R.layout.game_list_item, null ); - result.setRowID( DBUtils.gamesList(m_context)[position] ); - mustLoad = true; - } else { - result = (GameListItem)convertView; - long rowid = result.getRowID(); - if ( isDirty(rowid) || !result.isLoaded() ) { - mustLoad = true; - } - } - - if ( mustLoad ) { - new LoadItemTask( m_context, result ).execute(); - } - + GameListItem result = (GameListItem) + m_factory.inflate( R.layout.game_list_item, null ); + result.init( m_handler, DBUtils.gamesList(m_context)[position], + m_fieldID, m_cb ); return result; } public void inval( long rowid ) { - synchronized( m_loadedRows ) { - m_loadedRows.remove( rowid ); + GameListItem child = getItemFor( rowid ); + if ( null != child ) { + child.forceReload(); + } else { + DbgUtils.logf( "no child for rowid %d", rowid ); + m_list.invalidate(); } } - private void dirtyAll() + public void invalName( long rowid ) { - synchronized( m_loadedRows ) { - m_loadedRows.clear(); - } - } - - private boolean isDirty( long rowid ) - { - synchronized( m_loadedRows ) { - return ! m_loadedRows.contains( rowid ); - } - } - - private void setLoaded( long rowid ) - { - synchronized( m_loadedRows ) { - m_loadedRows.add( rowid ); + GameListItem item = getItemFor( rowid ); + if ( null != item ) { + item.invalName(); } } @@ -201,12 +121,24 @@ public class GameListAdapter extends XWListAdapter { + " from %d to %d", m_fieldID, newID ); } m_fieldID = newID; - dirtyAll(); + // return true so caller will do onContentChanged. + // There's no other way to signal GameListItem instances + // since we don't maintain a list of them. changed = true; } return changed; } + private GameListItem getItemFor( long rowid ) + { + GameListItem result = null; + int position = positionFor( rowid ); + if ( 0 <= position ) { + result = (GameListItem)m_list.getChildAt( position ); + } + return result; + } + private int fieldToID( String fieldName ) { int[] ids = { @@ -225,106 +157,16 @@ public class GameListAdapter extends XWListAdapter { return result; } - private void setData( GameListItem layout, final GameSummary summary ) + private int positionFor( long rowid ) { - if ( null != summary ) { - final long rowid = layout.getRowID(); - String state = summary.summarizeState(); - - TextView view = (TextView)layout.findViewById( R.id.game_name ); - String value = null; - switch ( m_fieldID ) { - case R.string.game_summary_field_empty: - break; - case R.string.game_summary_field_language: - value = - DictLangCache.getLangName( m_context, - summary.dictLang ); - break; - case R.string.game_summary_field_opponents: - value = summary.playerNames(); - break; - case R.string.game_summary_field_state: - value = state; + int position = -1; + long[] rowids = DBUtils.gamesList( m_context ); + for ( int ii = 0; ii < rowids.length; ++ii ) { + if ( rowids[ii] == rowid ) { + position = ii; break; } - - String name = GameUtils.getName( m_context, rowid ); - - if ( null != value ) { - value = m_context.getString( R.string.str_game_namef, - name, value ); - } else { - value = name; - } - - view.setText( value ); - - layout.setOnClickListener( new View.OnClickListener() { - @Override - public void onClick( View v ) { - m_cb.itemClicked( rowid, summary ); - } - } ); - - LinearLayout list = - (LinearLayout)layout.findViewById( R.id.player_list ); - boolean haveATurn = false; - boolean haveALocalTurn = false; - boolean[] isLocal = new boolean[1]; - for ( int ii = 0; ii < summary.nPlayers; ++ii ) { - ExpiringLinearLayout tmp = (ExpiringLinearLayout) - m_factory.inflate( R.layout.player_list_elem, null ); - view = (TextView)tmp.findViewById( R.id.item_name ); - view.setText( summary.summarizePlayer( ii ) ); - view = (TextView)tmp.findViewById( R.id.item_score ); - view.setText( String.format( " %d", summary.scores[ii] ) ); - boolean thisHasTurn = summary.isNextToPlay( ii, isLocal ); - if ( thisHasTurn ) { - haveATurn = true; - if ( isLocal[0] ) { - haveALocalTurn = true; - } - } - tmp.setPct( m_handler, thisHasTurn, isLocal[0], - summary.lastMoveTime ); - list.addView( tmp, ii ); - } - - view = (TextView)layout.findViewById( R.id.state ); - view.setText( state ); - view = (TextView)layout.findViewById( R.id.modtime ); - long lastMoveTime = summary.lastMoveTime; - lastMoveTime *= 1000; - view.setText( m_df.format( new Date( lastMoveTime ) ) ); - - int iconID; - ImageView marker = - (ImageView)layout.findViewById( R.id.msg_marker ); - CommsConnType conType = summary.conType; - if ( CommsConnType.COMMS_CONN_RELAY == conType ) { - iconID = R.drawable.relaygame; - } else if ( CommsConnType.COMMS_CONN_BT == conType ) { - iconID = android.R.drawable.stat_sys_data_bluetooth; - } else if ( CommsConnType.COMMS_CONN_SMS == conType ) { - iconID = android.R.drawable.sym_action_chat; - } else { - iconID = R.drawable.sologame; - } - marker.setImageResource( iconID ); - - view = (TextView)layout.findViewById( R.id.role ); - String roleSummary = summary.summarizeRole(); - if ( null != roleSummary ) { - view.setText( roleSummary ); - } else { - view.setVisibility( View.GONE ); - } - - boolean expanded = DBUtils.getExpanded( m_context, rowid ); - - layout.update( m_handler, expanded, summary.lastMoveTime, - haveATurn, haveALocalTurn ); } + return position; } } \ No newline at end of file diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java index fb03482d2..154ffb4c3 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java @@ -21,11 +21,19 @@ package org.eehouse.android.xw4; import android.content.Context; +import android.os.AsyncTask; import android.os.Handler; import android.util.AttributeSet; import android.view.View; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.TextView; +import java.text.DateFormat; +import java.util.Date; + +import org.eehouse.android.xw4.jni.GameSummary; +import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; public class GameListItem extends LinearLayout implements View.OnClickListener { @@ -39,6 +47,9 @@ public class GameListItem extends LinearLayout private long m_lastMoveTime; private ImageButton m_expandButton; private Handler m_handler; + private GameSummary m_summary; + private GameListAdapter.LoadItemCB m_cb; + private int m_fieldID; public GameListItem( Context cx, AttributeSet as ) { @@ -49,11 +60,31 @@ public class GameListItem extends LinearLayout m_lastMoveTime = 0; } - public void update( Handler handler, boolean expanded, - long lastMoveTime, boolean haveTurn, - boolean haveTurnLocal ) + public void init( Handler handler, long rowid, int fieldID, + GameListAdapter.LoadItemCB cb ) { m_handler = handler; + m_rowid = rowid; + m_fieldID = fieldID; + m_cb = cb; + + forceReload(); + } + + public void forceReload() + { + m_summary = null; + new LoadItemTask().execute(); + } + + public void invalName() + { + setName(); + } + + private void update( boolean expanded, long lastMoveTime, boolean haveTurn, + boolean haveTurnLocal ) + { m_expanded = expanded; m_lastMoveTime = lastMoveTime; m_haveTurn = haveTurn; @@ -65,28 +96,6 @@ public class GameListItem extends LinearLayout showHide(); } - public void setLoaded( boolean loaded ) - { - if ( m_loaded != loaded ) { - m_loaded = loaded; - // This should be enough to invalidate - findViewById( R.id.view_unloaded ) - .setVisibility( loaded ? View.GONE : View.VISIBLE ); - findViewById( R.id.view_loaded ) - .setVisibility( loaded ? View.VISIBLE : View.GONE ); - } - } - - public boolean isLoaded() - { - return m_loaded; - } - - public void setRowID( long rowid ) - { - m_rowid = rowid; - } - public long getRowID() { return m_rowid; @@ -99,6 +108,18 @@ public class GameListItem extends LinearLayout showHide(); } + private void setLoaded() + { + if ( !m_loaded ) { + m_loaded = true; + // This should be enough to invalidate + findViewById( R.id.view_unloaded ) + .setVisibility( m_loaded ? View.GONE : View.VISIBLE ); + findViewById( R.id.view_loaded ) + .setVisibility( m_loaded ? View.VISIBLE : View.GONE ); + } + } + private void showHide() { m_expandButton.setImageResource( m_expanded ? @@ -111,5 +132,137 @@ public class GameListItem extends LinearLayout m_haveTurnLocal, m_lastMoveTime ); } + private String setName() + { + String state = null; // hack to avoid calling summarizeState twice + if ( null != m_summary ) { + state = m_summary.summarizeState(); + TextView view = (TextView)findViewById( R.id.game_name ); + String value = null; + switch ( m_fieldID ) { + case R.string.game_summary_field_empty: + break; + case R.string.game_summary_field_language: + value = + DictLangCache.getLangName( m_context, + m_summary.dictLang ); + break; + case R.string.game_summary_field_opponents: + value = m_summary.playerNames(); + break; + case R.string.game_summary_field_state: + value = state; + break; + } + + if ( null != value ) { + String name = GameUtils.getName( m_context, m_rowid ); + value = m_context.getString( R.string.str_game_namef, + name, value ); + } else { + value = GameUtils.getName( m_context, m_rowid ); + } + + view.setText( value ); + } + return state; + } + + private void setData() + { + if ( null != m_summary ) { + TextView view; + String state = setName(); + + setOnClickListener( new View.OnClickListener() { + @Override + public void onClick( View v ) { + m_cb.itemClicked( m_rowid, m_summary ); + } + } ); + + LinearLayout list = + (LinearLayout)findViewById( R.id.player_list ); + list.removeAllViews(); + boolean haveATurn = false; + boolean haveALocalTurn = false; + boolean[] isLocal = new boolean[1]; + for ( int ii = 0; ii < m_summary.nPlayers; ++ii ) { + ExpiringLinearLayout tmp = (ExpiringLinearLayout) + Utils.inflate( m_context, R.layout.player_list_elem ); + view = (TextView)tmp.findViewById( R.id.item_name ); + view.setText( m_summary.summarizePlayer( ii ) ); + view = (TextView)tmp.findViewById( R.id.item_score ); + view.setText( String.format( " %d", m_summary.scores[ii] ) ); + boolean thisHasTurn = m_summary.isNextToPlay( ii, isLocal ); + if ( thisHasTurn ) { + haveATurn = true; + if ( isLocal[0] ) { + haveALocalTurn = true; + } + } + tmp.setPct( m_handler, thisHasTurn, isLocal[0], + m_summary.lastMoveTime ); + list.addView( tmp, ii ); + } + + view = (TextView)findViewById( R.id.state ); + view.setText( state ); + view = (TextView)findViewById( R.id.modtime ); + long lastMoveTime = m_summary.lastMoveTime; + lastMoveTime *= 1000; + + DateFormat df = DateFormat.getDateTimeInstance( DateFormat.SHORT, + DateFormat.SHORT ); + view.setText( df.format( new Date( lastMoveTime ) ) ); + + int iconID; + ImageView marker = + (ImageView)findViewById( R.id.msg_marker ); + CommsConnType conType = m_summary.conType; + if ( CommsConnType.COMMS_CONN_RELAY == conType ) { + iconID = R.drawable.relaygame; + } else if ( CommsConnType.COMMS_CONN_BT == conType ) { + iconID = android.R.drawable.stat_sys_data_bluetooth; + } else if ( CommsConnType.COMMS_CONN_SMS == conType ) { + iconID = android.R.drawable.sym_action_chat; + } else { + iconID = R.drawable.sologame; + } + marker.setImageResource( iconID ); + + view = (TextView)findViewById( R.id.role ); + String roleSummary = m_summary.summarizeRole(); + if ( null != roleSummary ) { + view.setText( roleSummary ); + } else { + view.setVisibility( View.GONE ); + } + + boolean expanded = DBUtils.getExpanded( m_context, m_rowid ); + + update( expanded, m_summary.lastMoveTime, haveATurn, + haveALocalTurn ); + } + } + + private class LoadItemTask extends AsyncTask { + @Override + protected GameSummary doInBackground( Void... unused ) + { + return DBUtils.getSummary( m_context, m_rowid, 1500 ); + } // doInBackground + + @Override + protected void onPostExecute( GameSummary summary ) + { + m_summary = summary; + setData(); + // setLoaded( m_view.getRowID() ); + setLoaded(); + + DbgUtils.logf( "LoadItemTask for row %d finished", m_rowid ); + } + } // class LoadItemTask } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 99697680b..301f153da 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -200,8 +200,7 @@ public class GamesList extends XWListActivity public void onClick( DialogInterface dlg, int item ) { String name = namerView.getName(); DBUtils.setName( GamesList.this, m_rowid, name ); - m_adapter.inval( m_rowid ); - onContentChanged(); + m_adapter.invalName( m_rowid ); } }; dialog = new AlertDialog.Builder( this ) @@ -283,7 +282,8 @@ public class GamesList extends XWListActivity }); String field = CommonPrefs.getSummaryField( this ); - m_adapter = new GameListAdapter( this, new Handler(), this, field ); + m_adapter = new GameListAdapter( this, getListView(), new Handler(), + this, field ); setListAdapter( m_adapter ); NetUtils.informOfDeaths( this ); @@ -381,12 +381,15 @@ public class GamesList extends XWListActivity } // DBUtils.DBChangeListener interface - public void gameSaved( final long rowid ) + public void gameSaved( final long rowid, final boolean countChanged ) { post( new Runnable() { public void run() { - m_adapter.inval( rowid ); - onContentChanged(); + if ( countChanged ) { + onContentChanged(); + } else { + m_adapter.inval( rowid ); + } } } ); } @@ -457,7 +460,6 @@ public class GamesList extends XWListActivity long[] games = DBUtils.gamesList( this ); for ( int ii = games.length - 1; ii >= 0; --ii ) { GameUtils.deleteGame( this, games[ii], ii == 0 ); - m_adapter.inval( games[ii] ); } break; case SYNC_MENU_ACTION: @@ -738,7 +740,6 @@ public class GamesList extends XWListActivity } } } - onContentChanged(); } } @@ -846,7 +847,9 @@ public class GamesList extends XWListActivity { String newField = CommonPrefs.getSummaryField( this ); if ( m_adapter.setField( newField ) ) { - onContentChanged(); + // The adapter should be able to decide whether full + // content change is required. PENDING + onContentChanged(); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java index 52306419b..decc6fde6 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java @@ -42,13 +42,13 @@ public abstract class XWListAdapter implements ListAdapter { public boolean isEnabled( int position ) { return true; } public int getCount() { return m_count; } public Object getItem( int position ) { return null; } - public long getItemId(int position) { return position; } + public long getItemId( int position ) { return position; } public int getItemViewType( int position ) { return ListAdapter.IGNORE_ITEM_VIEW_TYPE; } public int getViewTypeCount() { return 1; } public boolean hasStableIds() { return true; } public boolean isEmpty() { return getCount() == 0; } - public void registerDataSetObserver(DataSetObserver observer) {} - public void unregisterDataSetObserver(DataSetObserver observer) {} + public void registerDataSetObserver( DataSetObserver observer ) {} + public void unregisterDataSetObserver( DataSetObserver observer ) {} } \ No newline at end of file From f599ca8be42b9aed181e0bb703b3d8dcdc5ec33e Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 10 Dec 2012 07:54:42 -0800 Subject: [PATCH 25/50] make tiles square based on rowid so people can see both --- .../src/org/eehouse/android/xw4/BoardActivity.java | 1 + .../XWords4/src/org/eehouse/android/xw4/BoardView.java | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index e638df480..c4003c5fa 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -509,6 +509,7 @@ public class BoardActivity extends XWActivity Intent intent = getIntent(); m_rowid = intent.getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 ); + m_view.setRowID( m_rowid ); m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); m_overNotShown = true; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java index d05909c99..9c4b62c67 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -53,8 +53,6 @@ public class BoardView extends View implements DrawCtx, BoardHandler, private static final int PINCH_THRESHOLD = 40; private static final int SCORE_HT_DROP = 2; private static final boolean DEBUG_DRAWFRAMES = false; - // this can be a preference - private static final boolean MAKETILESSQUARE = true; private Context m_context; private Paint m_drawPaint; @@ -77,6 +75,7 @@ public class BoardView extends View implements DrawCtx, BoardHandler, private boolean m_blackArrow; private boolean m_inTrade = false; private boolean m_hasSmallScreen; + private long m_rowid; // m_backgroundUsed: alpha not set ensures inequality private int m_backgroundUsed = 0x00000000; private boolean m_darkOnLight; @@ -239,6 +238,11 @@ public class BoardView extends View implements DrawCtx, BoardHandler, return true; // required to get subsequent events } + public void setRowID( long rowid ) + { + m_rowid = rowid; + } + // private void printMode( String comment, int mode ) // { // comment += ": "; @@ -384,7 +388,7 @@ public class BoardView extends View implements DrawCtx, BoardHandler, scoreHt += heightLeft; trayHt += heightLeft * 2; - if ( MAKETILESSQUARE && trayHt > width / 7 ) { + if ( (1 == (m_rowid % 2)) && trayHt > width / 7 ) { trayHt = width / 7; } heightUsed = trayHt + scoreHt + ((nCells - nToScroll) * cellSize); From 877225f59dc3b6248a6a4190c14886a93079395d Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 10 Dec 2012 18:20:44 -0800 Subject: [PATCH 26/50] rename interface; no code change --- .../src/org/eehouse/android/xw4/BTInviteActivity.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/BTService.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/BoardActivity.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/GamesList.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/MultiService.java | 6 +++--- .../src/org/eehouse/android/xw4/NewGameActivity.java | 4 ++-- .../XWords4/src/org/eehouse/android/xw4/SMSService.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/XWActivity.java | 4 ++-- .../XWords4/src/org/eehouse/android/xw4/XWListActivity.java | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java index fcfcbd51a..bcc4c325c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTInviteActivity.java @@ -65,7 +65,7 @@ public class BTInviteActivity extends InviteActivity BTService.clearDevices( this, null ); // will return names } - // BTService.BTEventListener interface + // MultiService.MultiEventListener interface @Override public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java index f62100c4e..4d6f8fd6d 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java @@ -147,7 +147,7 @@ public class BTService extends Service { } } - public static void setListener( MultiService.BTEventListener li ) + public static void setListener( MultiService.MultiEventListener li ) { if ( XWApp.BTSUPPORTED ) { if ( null == s_srcMgr ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index c4003c5fa..f8a5f802c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -924,7 +924,7 @@ public class BoardActivity extends XWActivity } ////////////////////////////////////////////////// - // BTService.BTEventListener interface + // MultiService.MultiEventListener interface ////////////////////////////////////////////////// @Override @SuppressWarnings("fallthrough") diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 301f153da..306a7a4aa 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -419,7 +419,7 @@ public class GamesList extends XWListActivity } } - // BTService.BTEventListener interface + // BTService.MultiEventListener interface @Override public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiService.java index 4e4d21686..5d2a776eb 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiService.java @@ -42,7 +42,7 @@ public class MultiService { public static final int OWNER_SMS = 1; public static final int OWNER_RELAY = 2; - private BTEventListener m_li; + private MultiEventListener m_li; public enum MultiEvent { BAD_PROTO , BT_ENABLED @@ -64,14 +64,14 @@ public class MultiService { , SMS_SEND_FAILED_NORADIO }; - public interface BTEventListener { + public interface MultiEventListener { public void eventOccurred( MultiEvent event, Object ... args ); } // public interface MultiEventSrc { // public void setBTEventListener( BTEventListener li ); // } - public void setListener( BTEventListener li ) + public void setListener( MultiEventListener li ) { synchronized( this ) { m_li = li; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java index 85bbccd63..d1b90148a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NewGameActivity.java @@ -256,7 +256,7 @@ public class NewGameActivity extends XWActivity { return dialog; } - // BTService.BTEventListener interface + // MultiService.MultiEventListener interface @Override public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) @@ -299,7 +299,7 @@ public class NewGameActivity extends XWActivity { super.eventOccurred( event, args ); break; } - } // BTService.BTEventListener.eventOccurred + } // MultiService.MultiEventListener.eventOccurred private void makeNewGame( boolean networked, boolean launch ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java index 6734e1220..be0a95ef2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java @@ -189,7 +189,7 @@ public class SMSService extends Service { return result; } - public static void setListener( MultiService.BTEventListener li ) + public static void setListener( MultiService.MultiEventListener li ) { if ( XWApp.SMSSUPPORTED ) { if ( null == s_srcMgr ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java index 18670d379..71e8a922d 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java @@ -31,7 +31,7 @@ import android.widget.TextView; import junit.framework.Assert; public class XWActivity extends Activity - implements DlgDelegate.DlgClickNotify, MultiService.BTEventListener { + implements DlgDelegate.DlgClickNotify, MultiService.MultiEventListener { private DlgDelegate m_delegate; @@ -192,7 +192,7 @@ public class XWActivity extends Activity Assert.fail(); } - // BTService.BTEventListener interface + // BTService.MultiEventListener interface public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java index 9f10da3d4..927b73f78 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java @@ -28,7 +28,7 @@ import android.os.Bundle; import junit.framework.Assert; public class XWListActivity extends ListActivity - implements DlgDelegate.DlgClickNotify, MultiService.BTEventListener { + implements DlgDelegate.DlgClickNotify, MultiService.MultiEventListener { private DlgDelegate m_delegate; @@ -193,7 +193,7 @@ public class XWListActivity extends ListActivity m_delegate.launchLookup( words, lang, forceList ); } - // BTService.BTEventListener interface + // MultiService.MultiEventListener interface public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) { From b2bd6ce662b382d8c59f8e95342733d42ac50ec9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 11 Dec 2012 07:25:43 -0800 Subject: [PATCH 27/50] add and use preference for square rack tiles, removing code that alternated for testing purposes. --- xwords4/android/XWords4/res/values/common_rsrc.xml | 1 + xwords4/android/XWords4/res/values/strings.xml | 4 ++++ xwords4/android/XWords4/res/xml/xwprefs.xml | 5 +++++ .../src/org/eehouse/android/xw4/BoardActivity.java | 1 - .../XWords4/src/org/eehouse/android/xw4/BoardView.java | 9 ++------- .../XWords4/src/org/eehouse/android/xw4/XWPrefs.java | 5 +++++ 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml index 860b43b11..759d74e00 100644 --- a/xwords4/android/XWords4/res/values/common_rsrc.xml +++ b/xwords4/android/XWords4/res/values/common_rsrc.xml @@ -6,6 +6,7 @@ key_color_tiles key_show_arrow + key_square_tiles key_explain_robot key_skip_confirm key_sort_tiles diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 55f904e56..cdd796f20 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -2135,4 +2135,8 @@ game with the same players and parameters as the one that just ended. --> Rematch + + Square rack tiles + Even if they can be taller + diff --git a/xwords4/android/XWords4/res/xml/xwprefs.xml b/xwords4/android/XWords4/res/xml/xwprefs.xml index 86fdece79..2cb350166 100644 --- a/xwords4/android/XWords4/res/xml/xwprefs.xml +++ b/xwords4/android/XWords4/res/xml/xwprefs.xml @@ -132,6 +132,11 @@ android:summary="@string/show_arrow_summary" android:defaultValue="true" /> + width / 7 ) { + if ( XWPrefs.getSquareTiles( m_context ) + && trayHt > (width / 7) ) { trayHt = width / 7; } heightUsed = trayHt + scoreHt + ((nCells - nToScroll) * cellSize); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java index dce8adae8..b966a051e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java @@ -83,6 +83,11 @@ public class XWPrefs { return getPrefsBoolean( context, R.string.key_ringer_zoom, false ); } + public static boolean getSquareTiles( Context context ) + { + return getPrefsBoolean( context, R.string.key_square_tiles, false ); + } + public static int getDefaultPlayerMinutes( Context context ) { String value = From 7246ae28c697043c1b6475610c9546f1de97aab1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 11 Dec 2012 19:09:33 -0800 Subject: [PATCH 28/50] clear the cache after loading a new DB so will redraw correctly in list --- xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 8deebeeaa..679b62a80 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -924,6 +924,7 @@ public class DBUtils { public static void loadDB( Context context ) { + clearRowIDsCache(); copyGameDB( context, false ); } From 01d17fe0c50783bfb3033d420e5466099a4b7f5c Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 11 Dec 2012 19:10:05 -0800 Subject: [PATCH 29/50] redraw full list after resetting a game in case position changes --- .../android/XWords4/src/org/eehouse/android/xw4/GamesList.java | 1 + 1 file changed, 1 insertion(+) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 306a7a4aa..833c4eac4 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -452,6 +452,7 @@ public class GamesList extends XWListActivity break; case RESET_GAME_ACTION: GameUtils.resetGame( this, m_rowid ); + onContentChanged(); // required because position may change break; case DELETE_GAME_ACTION: GameUtils.deleteGame( this, m_rowid, true ); From 575d5e97453693041825686a6acfcb6838a14839 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 11 Dec 2012 19:11:23 -0800 Subject: [PATCH 30/50] reduce time we'll wait for a summary to unlock when loading it for games list. --- .../XWords4/src/org/eehouse/android/xw4/GameListItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java index 154ffb4c3..89f9e4b7a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java @@ -250,7 +250,7 @@ public class GameListItem extends LinearLayout @Override protected GameSummary doInBackground( Void... unused ) { - return DBUtils.getSummary( m_context, m_rowid, 1500 ); + return DBUtils.getSummary( m_context, m_rowid, 150 ); } // doInBackground @Override From eee954e705101556a414571dbe7b8f0419e42c4a Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 11 Dec 2012 19:15:15 -0800 Subject: [PATCH 31/50] list changes for next release --- xwords4/android/XWords4/res/raw/changes | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index c1165a0bd..d87b350f7 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -9,11 +9,17 @@

        New with this release

          +
        • Include attachment in email invites for devices that don't dispatch + URLs correctly (e.g. some by HTC)
        • +
        • Fix flickering in main screen (games list)
        • +
        • Add option, off by default, to keep rack tiles square even when + the screen is large enough that they can be taller
        • +
        • Show final scores alert whenever a finished game is opened -- to + make it more clear that it's finished

        Next up

          -
        • One more idea for improving invitations
        • Allow grouping of games in collapsible user-defined categores: "Games with Kati", "Finished games", etc.
        From 69f868722f54e754a1ac2a8da9282cb831e1192c Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 12 Dec 2012 06:41:56 -0800 Subject: [PATCH 32/50] cancel any notification for game when resetting it --- .../android/XWords4/src/org/eehouse/android/xw4/GameUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 420819c46..a5c442cc1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -246,6 +246,8 @@ public class GameUtils { tellDied( context, lock, true ); resetGame( context, lock, lock, false ); lock.unlock(); + + Utils.cancelNotification( context, (int)rowidIn ); } private static GameSummary summarizeAndClose( Context context, From db8364c28551c19bbc5a0481f4ece10333417342 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 12 Dec 2012 06:43:18 -0800 Subject: [PATCH 33/50] return empty array rather than null when query succeeds but produces no result. --- .../XWords4/src/org/eehouse/android/xw4/DBUtils.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 679b62a80..b584fcc8e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -451,10 +451,8 @@ public class DBUtils { String selection = DBHelper.RELAYID + "='" + relayID + "'"; Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, selection, null, null, null, null ); + result = new long[cursor.getCount()]; for ( int ii = 0; cursor.moveToNext(); ++ii ) { - if ( null == result ) { - result = new long[cursor.getCount()]; - } result[ii] = cursor.getLong( cursor.getColumnIndex(ROW_ID) ); } cursor.close(); @@ -473,11 +471,8 @@ public class DBUtils { String selection = String.format( DBHelper.GAMEID + "=%d", gameID ); Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, selection, null, null, null, null ); - + result = new long[cursor.getCount()]; for ( int ii = 0; cursor.moveToNext(); ++ii ) { - if ( null == result ) { - result = new long[cursor.getCount()]; - } result[ii] = cursor.getLong( cursor.getColumnIndex(ROW_ID) ); } cursor.close(); From b71046e5aac08f1f920c5245b4c958c187a7327d Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 12 Dec 2012 07:13:25 -0800 Subject: [PATCH 34/50] lock, rather than tryLock, game when feeding it messages. Otherwise messages are dropped e.g. when UI's loading a summary in GameListItem. --- .../org/eehouse/android/xw4/GameUtils.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index a5c442cc1..a1837bd7a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -713,35 +713,35 @@ public class GameUtils { { boolean draw = false; Assert.assertTrue( -1 != rowid ); - GameLock lock = new GameLock( rowid, true ); - if ( lock.tryLock() ) { - CurGameInfo gi = new CurGameInfo( context ); - FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid ); - int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock ); - if ( 0 != gamePtr ) { - XwJNI.comms_resendAll( gamePtr, false, false ); + GameLock lock = new GameLock( rowid, true ).lock(); - if ( null != msgs ) { - for ( byte[] msg : msgs ) { - draw = XwJNI.game_receiveMessage( gamePtr, msg, ret ) - || draw; - } - } - XwJNI.comms_ackAny( gamePtr ); + CurGameInfo gi = new CurGameInfo( context ); + FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid ); + int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock ); + if ( 0 != gamePtr ) { + XwJNI.comms_resendAll( gamePtr, false, false ); - // update gi to reflect changes due to messages - XwJNI.game_getGi( gamePtr, gi ); - saveGame( context, gamePtr, gi, lock, false ); - summarizeAndClose( context, lock, gamePtr, gi, feedImpl ); - - int flags = setFromFeedImpl( feedImpl ); - if ( GameSummary.MSG_FLAGS_NONE != flags ) { - draw = true; - DBUtils.setMsgFlags( rowid, flags ); + if ( null != msgs ) { + for ( byte[] msg : msgs ) { + draw = XwJNI.game_receiveMessage( gamePtr, msg, ret ) + || draw; } } - lock.unlock(); + XwJNI.comms_ackAny( gamePtr ); + + // update gi to reflect changes due to messages + XwJNI.game_getGi( gamePtr, gi ); + saveGame( context, gamePtr, gi, lock, false ); + summarizeAndClose( context, lock, gamePtr, gi, feedImpl ); + + int flags = setFromFeedImpl( feedImpl ); + if ( GameSummary.MSG_FLAGS_NONE != flags ) { + draw = true; + DBUtils.setMsgFlags( rowid, flags ); + } } + lock.unlock(); + return draw; } // feedMessages From 91ac04b8965825778d697bf3d4b9fa5f9b519dce Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 13 Dec 2012 06:32:57 -0800 Subject: [PATCH 35/50] remove logging --- .../XWords4/src/org/eehouse/android/xw4/GameListItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java index 89f9e4b7a..d5d727b08 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java @@ -261,7 +261,7 @@ public class GameListItem extends LinearLayout // setLoaded( m_view.getRowID() ); setLoaded(); - DbgUtils.logf( "LoadItemTask for row %d finished", m_rowid ); + // DbgUtils.logf( "LoadItemTask for row %d finished", m_rowid ); } } // class LoadItemTask From 6060d5e8bdd04e497c58cf932b9329864a3a9fdf Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 13 Dec 2012 06:57:12 -0800 Subject: [PATCH 36/50] Fix hangs when receiving relay messages in background for open game by adding a static feedMessages method like the one used by SMS and BT games. For that to work, rowid and relayid need to be fetched and tracked together -- so do that in RelayService. --- .../eehouse/android/xw4/BoardActivity.java | 22 ++++ .../src/org/eehouse/android/xw4/DBUtils.java | 27 +++-- .../org/eehouse/android/xw4/DlgDelegate.java | 2 +- .../org/eehouse/android/xw4/GameUtils.java | 102 +++++++++--------- .../src/org/eehouse/android/xw4/NetUtils.java | 16 ++- .../org/eehouse/android/xw4/RelayService.java | 47 ++++---- .../src/org/eehouse/android/xw4/XWApp.java | 4 +- 7 files changed, 129 insertions(+), 91 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 396270e30..ff5f5a104 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -189,6 +189,27 @@ public class BoardActivity extends XWActivity return delivered; } + public static boolean feedMessages( long rowid, byte[][] msgs ) + { + boolean delivered = false; + Assert.assertNotNull( msgs ); + synchronized( s_thisLocker ) { + if ( null != s_this ) { + Assert.assertNotNull( s_this.m_gi ); + Assert.assertNotNull( s_this.m_gameLock ); + Assert.assertNotNull( s_this.m_jniThread ); + if ( rowid == s_this.m_rowid ) { + delivered = true; // even if no messages! + for ( byte[] msg : msgs ) { + s_this.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, + null ); + } + } + } + } + return delivered; + } + private static void setThis( BoardActivity self ) { synchronized( s_thisLocker ) { @@ -509,6 +530,7 @@ public class BoardActivity extends XWActivity Intent intent = getIntent(); m_rowid = intent.getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 ); + DbgUtils.logf( "BoardActivity: opening rowid %d", m_rowid ); m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); m_overNotShown = true; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index b584fcc8e..cc6529755 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -573,7 +573,7 @@ public class DBUtils { return result; } - public static String[] getRelayIDs( Context context, boolean noMsgs ) + public static String[] getRelayIDs( Context context, long[][] rowIDs ) { String[] result = null; initDB( context ); @@ -581,26 +581,31 @@ public class DBUtils { synchronized( s_dbHelper ) { SQLiteDatabase db = s_dbHelper.getReadableDatabase(); - String[] columns = { DBHelper.RELAYID }; + String[] columns = { ROW_ID, DBHelper.RELAYID }; String selection = DBHelper.RELAYID + " NOT null"; - if ( noMsgs ) { - selection += " AND NOT " + DBHelper.HASMSGS; - } Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, selection, null, null, null, null ); + int count = cursor.getCount(); + if ( 0 < count ) { + result = new String[count]; + if ( null != rowIDs ) { + rowIDs[0] = new long[count]; + } - while ( cursor.moveToNext() ) { - ids.add( cursor.getString( cursor. - getColumnIndex(DBHelper.RELAYID)) ); + int idIndex = cursor.getColumnIndex(DBHelper.RELAYID); + int rowIndex = cursor.getColumnIndex(ROW_ID); + for ( int ii = 0; cursor.moveToNext(); ++ii ) { + result[ii] = cursor.getString( idIndex ); + if ( null != rowIDs ) { + rowIDs[0][ii] = cursor.getLong( rowIndex ); + } + } } cursor.close(); db.close(); } - if ( 0 < ids.size() ) { - result = ids.toArray( new String[ids.size()] ); - } return result; } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java index b0e642bf1..05ace973a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java @@ -242,7 +242,7 @@ public class DlgDelegate { public void doSyncMenuitem() { - if ( null == DBUtils.getRelayIDs( m_activity, false ) ) { + if ( null == DBUtils.getRelayIDs( m_activity, null ) ) { showOKOnlyDialog( R.string.no_games_to_refresh ); } else { RelayReceiver.RestartTimer( m_activity, true ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index a1837bd7a..e9cc53ce7 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -111,16 +111,21 @@ public class GameUtils { final long assertTime = 2000; Assert.assertTrue( maxMillis < assertTime ); long sleptTime = 0; - // DbgUtils.logf( "GameLock.lock(%s)", m_path ); - // Utils.printStack(); + + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", + this, m_rowid, maxMillis ); + } + for ( ; ; ) { if ( tryLock() ) { result = this; break; } if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "GameLock.lock() %H failed; sleeping", this ); - DbgUtils.printStack(); + DbgUtils.logf( "GameLock.lock() %H failed (rowid:%d); sleeping", + this, m_rowid ); + // DbgUtils.printStack(); } try { Thread.sleep( 25 ); // milliseconds @@ -130,12 +135,17 @@ public class GameUtils { break; } + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "GameLock.lock() %H awake; " + + "sleptTime now %d millis", this, sleptTime ); + } + if ( 0 < maxMillis && sleptTime >= maxMillis ) { break; } else if ( sleptTime >= assertTime ) { if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "lock %H overlocked. lock holding stack:", - this ); + DbgUtils.logf( "lock %H overlocked waiting for rowid:%d. " + + "lock holding stack:", m_rowid, this ); DbgUtils.printStack( m_lockTrace ); DbgUtils.logf( "lock %H seeking stack:", this ); DbgUtils.printStack(); @@ -158,8 +168,12 @@ public class GameUtils { Assert.assertTrue( !m_isForWrite ); } --m_lockCount; + + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) " + + "unlocked", this, m_rowid ); + } } - // DbgUtils.logf( "GameLock.unlock(%s) done", m_path ); } public long getRowid() @@ -707,41 +721,46 @@ public class GameUtils { } } - private static boolean feedMessages( Context context, long rowid, - byte[][] msgs, CommsAddrRec ret, - MultiMsgSink sink ) + public static boolean feedMessages( Context context, long rowid, + byte[][] msgs, CommsAddrRec ret, + MultiMsgSink sink ) { boolean draw = false; Assert.assertTrue( -1 != rowid ); - GameLock lock = new GameLock( rowid, true ).lock(); + if ( null != msgs ) { + // timed lock: If a game is opened by BoardActivity just + // as we're trying to deliver this message to it it'll + // have the lock and we'll never get it. Better to drop + // the message than fire the hung-lock assert. Messages + // belong in local pre-delivery storage anyway. + GameLock lock = new GameLock( rowid, true ).lock( 150 ); + if ( null != lock ) { + CurGameInfo gi = new CurGameInfo( context ); + FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid ); + int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock ); + if ( 0 != gamePtr ) { + XwJNI.comms_resendAll( gamePtr, false, false ); - CurGameInfo gi = new CurGameInfo( context ); - FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid ); - int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock ); - if ( 0 != gamePtr ) { - XwJNI.comms_resendAll( gamePtr, false, false ); + for ( byte[] msg : msgs ) { + draw = XwJNI.game_receiveMessage( gamePtr, msg, ret ) + || draw; + } + XwJNI.comms_ackAny( gamePtr ); - if ( null != msgs ) { - for ( byte[] msg : msgs ) { - draw = XwJNI.game_receiveMessage( gamePtr, msg, ret ) - || draw; + // update gi to reflect changes due to messages + XwJNI.game_getGi( gamePtr, gi ); + saveGame( context, gamePtr, gi, lock, false ); + summarizeAndClose( context, lock, gamePtr, gi, feedImpl ); + + int flags = setFromFeedImpl( feedImpl ); + if ( GameSummary.MSG_FLAGS_NONE != flags ) { + draw = true; + DBUtils.setMsgFlags( rowid, flags ); + } } - } - XwJNI.comms_ackAny( gamePtr ); - - // update gi to reflect changes due to messages - XwJNI.game_getGi( gamePtr, gi ); - saveGame( context, gamePtr, gi, lock, false ); - summarizeAndClose( context, lock, gamePtr, gi, feedImpl ); - - int flags = setFromFeedImpl( feedImpl ); - if ( GameSummary.MSG_FLAGS_NONE != flags ) { - draw = true; - DBUtils.setMsgFlags( rowid, flags ); + lock.unlock(); } } - lock.unlock(); - return draw; } // feedMessages @@ -754,21 +773,6 @@ public class GameUtils { return feedMessages( context, rowid, msgs, ret, sink ); } - // Current assumption: this is the relay case where return address - // can be null. - public static boolean feedMessages( Context context, String relayID, - byte[][] msgs, MultiMsgSink sink ) - { - boolean draw = false; - long[] rowids = DBUtils.getRowIDsFor( context, relayID ); - if ( null != rowids ) { - for ( long rowid : rowids ) { - draw = feedMessages( context, rowid, msgs, null, sink ) || draw; - } - } - return draw; - } - // This *must* involve a reset if the language is changing!!! // Which isn't possible right now, so make sure the old and new // dict have the same langauge code. diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetUtils.java index 4f900a76d..75d3d11b0 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetUtils.java @@ -130,8 +130,7 @@ public class NetUtils { } } - public static byte[][][] queryRelay( Context context, String[] ids, - int nBytes ) + public static byte[][][] queryRelay( Context context, String[] ids ) { byte[][][] msgs = null; try { @@ -141,6 +140,7 @@ public class NetUtils { new DataOutputStream( socket.getOutputStream() ); // total packet size + int nBytes = sumStrings( ids ); outStream.writeShort( 2 + nBytes + ids.length + 1 ); outStream.writeByte( NetUtils.PROTOCOL_VERSION ); @@ -263,4 +263,16 @@ public class NetUtils { DbgUtils.logf( "sendToRelay: null msgs" ); } } // sendToRelay + + private static int sumStrings( final String[] strs ) + { + int len = 0; + if ( null != strs ) { + for ( String str : strs ) { + len += str.length(); + } + } + return len; + } + } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java index c312a8fc8..5da48cd30 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java @@ -74,45 +74,40 @@ public class RelayService extends Service { } } } - - private String[] collectIDs( int[] nBytes ) - { - String[] ids = DBUtils.getRelayIDs( this, false ); - int len = 0; - if ( null != ids ) { - for ( String id : ids ) { - len += id.length(); - } - } - nBytes[0] = len; - return ids; - } private void fetchAndProcess() { - int[] nBytes = new int[1]; - String[] ids = collectIDs( nBytes ); - if ( null != ids && 0 < ids.length ) { - RelayMsgSink sink = new RelayMsgSink(); - byte[][][] msgs = - NetUtils.queryRelay( this, ids, nBytes[0] ); + long[][] rowIDss = new long[1][]; + String[] relayIDs = DBUtils.getRelayIDs( this, rowIDss ); + if ( null != relayIDs && 0 < relayIDs.length ) { + long[] rowIDs = rowIDss[0]; + byte[][][] msgs = NetUtils.queryRelay( this, relayIDs ); if ( null != msgs ) { - int nameCount = ids.length; + RelayMsgSink sink = new RelayMsgSink(); + int nameCount = relayIDs.length; ArrayList idsWMsgs = new ArrayList( nameCount ); for ( int ii = 0; ii < nameCount; ++ii ) { + byte[][] forOne = msgs[ii]; // if game has messages, open it and feed 'em // to it. - if ( GameUtils.feedMessages( this, ids[ii], - msgs[ii], sink ) ) { - idsWMsgs.add( ids[ii] ); + if ( null == forOne ) { + // Nothing for this relayID + } else if ( BoardActivity.feedMessages( rowIDs[ii], forOne ) + || GameUtils.feedMessages( this, rowIDs[ii], + forOne, null, + sink ) ) { + idsWMsgs.add( relayIDs[ii] ); + } else { + DbgUtils.logf( "dropping message for %s (rowid %d)", + relayIDs[ii], rowIDs[ii] ); } } if ( 0 < idsWMsgs.size() ) { - String[] relayIDs = new String[idsWMsgs.size()]; - idsWMsgs.toArray( relayIDs ); - setupNotification( relayIDs ); + String[] tmp = new String[idsWMsgs.size()]; + idsWMsgs.toArray( tmp ); + setupNotification( tmp ); } sink.send( this ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index 24edc0066..3d157d54f 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -28,13 +28,13 @@ import java.util.UUID; import org.eehouse.android.xw4.jni.XwJNI; public class XWApp extends Application { - public static final boolean DEBUG_LOCKS = false; public static final boolean BTSUPPORTED = false; public static final boolean SMSSUPPORTED = true; public static final boolean GCMSUPPORTED = true; public static final boolean ATTACH_SUPPORTED = true; public static final boolean REMATCH_SUPPORTED = false; - public static final boolean DEBUG = true; // DON'T SHIP THIS WAY + public static final boolean DEBUG = true; // DON'T SHIP THIS WAY + public static final boolean DEBUG_LOCKS = false && DEBUG; public static final String SMS_PUBLIC_HEADER = "-XW4"; From f989dad63c47e3d3efc6d56a3fe5385fadd32e55 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 13 Dec 2012 07:11:51 -0800 Subject: [PATCH 37/50] tweak changed descriptions --- xwords4/android/XWords4/res/raw/changes | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index d87b350f7..b9613677d 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -9,13 +9,14 @@

        New with this release

          -
        • Include attachment in email invites for devices that don't dispatch - URLs correctly (e.g. some by HTC)
        • +
        • Include new game information as an attachment in email invites + for use on devices that don't dispatch URLs correctly in received + email (e.g. some by HTC)
        • +
        • Show final scores alert whenever a finished game is opened -- to + make it more clear that it's finished
        • Fix flickering in main screen (games list)
        • Add option, off by default, to keep rack tiles square even when the screen is large enough that they can be taller
        • -
        • Show final scores alert whenever a finished game is opened -- to - make it more clear that it's finished

        Next up

        From 85953c64dd5d789272474939b14e8adbb75492d7 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 13 Dec 2012 18:47:55 -0800 Subject: [PATCH 38/50] move GameLock into its own file --- .../eehouse/android/xw4/BoardActivity.java | 4 +- .../src/org/eehouse/android/xw4/DBUtils.java | 25 ++- .../org/eehouse/android/xw4/GameConfig.java | 4 +- .../src/org/eehouse/android/xw4/GameLock.java | 165 ++++++++++++++++++ .../org/eehouse/android/xw4/GameUtils.java | 142 --------------- .../org/eehouse/android/xw4/GamesList.java | 3 +- .../android/xw4/RelayGameActivity.java | 4 +- .../eehouse/android/xw4/jni/JNIThread.java | 5 +- 8 files changed, 187 insertions(+), 165 deletions(-) create mode 100644 xwords4/android/XWords4/src/org/eehouse/android/xw4/GameLock.java diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index ff5f5a104..c0bb10d0b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -114,7 +114,7 @@ public class BoardActivity extends XWActivity private BoardView m_view; private int m_jniGamePtr; - private GameUtils.GameLock m_gameLock; + private GameLock m_gameLock; private CurGameInfo m_gi; private CommsTransport m_xport; private Handler m_handler = null; @@ -1673,7 +1673,7 @@ public class BoardActivity extends XWActivity showDictGoneFinish(); } else { Assert.assertNull( m_gameLock ); - m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock(); + m_gameLock = new GameLock( m_rowid, true ).lock(); byte[] stream = GameUtils.savedGame( this, m_gameLock ); m_gi = new CurGameInfo( this ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index cc6529755..698c9b905 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -101,8 +101,7 @@ public class DBUtils { long maxMillis ) { GameSummary result = null; - GameUtils.GameLock lock = - new GameUtils.GameLock( rowid, false ).lock( maxMillis ); + GameLock lock = new GameLock( rowid, false ).lock( maxMillis ); if ( null != lock ) { result = getSummary( context, lock ); lock.unlock(); @@ -116,7 +115,7 @@ public class DBUtils { } public static GameSummary getSummary( Context context, - GameUtils.GameLock lock ) + GameLock lock ) { initDB( context ); GameSummary summary = null; @@ -248,13 +247,13 @@ public class DBUtils { return summary; } // getSummary - public static void saveSummary( Context context, GameUtils.GameLock lock, + public static void saveSummary( Context context, GameLock lock, GameSummary summary ) { saveSummary( context, lock, summary, null ); } - public static void saveSummary( Context context, GameUtils.GameLock lock, + public static void saveSummary( Context context, GameLock lock, GameSummary summary, String inviteID ) { Assert.assertTrue( lock.canWrite() ); @@ -679,10 +678,10 @@ public class DBUtils { } } - public static GameUtils.GameLock saveNewGame( Context context, - byte[] bytes ) + public static GameLock saveNewGame( Context context, + byte[] bytes ) { - GameUtils.GameLock lock = null; + GameLock lock = null; initDB( context ); synchronized( s_dbHelper ) { @@ -702,14 +701,14 @@ public class DBUtils { setCached( rowid, null ); // force reread clearRowIDsCache(); - lock = new GameUtils.GameLock( rowid, true ).lock(); + lock = new GameLock( rowid, true ).lock(); notifyListeners( rowid, true ); } return lock; } - public static long saveGame( Context context, GameUtils.GameLock lock, + public static long saveGame( Context context, GameLock lock, byte[] bytes, boolean setCreate ) { Assert.assertTrue( lock.canWrite() ); @@ -733,7 +732,7 @@ public class DBUtils { return rowid; } - public static byte[] loadGame( Context context, GameUtils.GameLock lock ) + public static byte[] loadGame( Context context, GameLock lock ) { long rowid = lock.getRowid(); Assert.assertTrue( -1 != rowid ); @@ -761,12 +760,12 @@ public class DBUtils { public static void deleteGame( Context context, long rowid ) { - GameUtils.GameLock lock = new GameUtils.GameLock( rowid, true ).lock(); + GameLock lock = new GameLock( rowid, true ).lock(); deleteGame( context, lock ); lock.unlock(); } - public static void deleteGame( Context context, GameUtils.GameLock lock ) + public static void deleteGame( Context context, GameLock lock ) { Assert.assertTrue( lock.canWrite() ); initDB( context ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java index 5fcf33251..c35139e61 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java @@ -92,7 +92,7 @@ public class GameConfig extends XWActivity private boolean m_forResult; private CurGameInfo m_gi; private CurGameInfo m_giOrig; - private GameUtils.GameLock m_gameLock; + private GameLock m_gameLock; private int m_whichPlayer; // private Spinner m_roleSpinner; // private Spinner m_connectSpinner; @@ -473,7 +473,7 @@ public class GameConfig extends XWActivity // Lock in case we're going to config. We *could* re-get the // lock once the user decides to make changes. PENDING. - m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock(); + m_gameLock = new GameLock( m_rowid, true ).lock(); int gamePtr = GameUtils.loadMakeGame( this, m_giOrig, m_gameLock ); if ( 0 == gamePtr ) { showDictGoneFinish(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameLock.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameLock.java new file mode 100644 index 000000000..12c080c23 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameLock.java @@ -0,0 +1,165 @@ +/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */ +/* + * Copyright 2009-2010 by Eric House (xwords@eehouse.org). All + * rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.eehouse.android.xw4; + +import java.util.HashMap; + +import junit.framework.Assert; + +// Implements read-locks and write-locks per game. A read lock is +// obtainable when other read locks are granted but not when a +// write lock is. Write-locks are exclusive. +public class GameLock { + private long m_rowid; + private boolean m_isForWrite; + private int m_lockCount; + StackTraceElement[] m_lockTrace; + + private static HashMap + s_locks = new HashMap(); + + public GameLock( long rowid, boolean isForWrite ) + { + m_rowid = rowid; + m_isForWrite = isForWrite; + m_lockCount = 0; + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "GameLock.GameLock(rowid:%d,isForWrite:%b)=>" + + "this: %H", rowid, isForWrite, this ); + DbgUtils.printStack(); + } + } + + // This could be written to allow multiple read locks. Let's + // see if not doing that causes problems. + public boolean tryLock() + { + boolean gotIt = false; + synchronized( s_locks ) { + GameLock owner = s_locks.get( m_rowid ); + if ( null == owner ) { // unowned + Assert.assertTrue( 0 == m_lockCount ); + s_locks.put( m_rowid, this ); + ++m_lockCount; + gotIt = true; + + if ( XWApp.DEBUG_LOCKS ) { + StackTraceElement[] trace = Thread.currentThread(). + getStackTrace(); + m_lockTrace = new StackTraceElement[trace.length]; + System.arraycopy( trace, 0, m_lockTrace, 0, trace.length ); + } + } else if ( this == owner && ! m_isForWrite ) { + Assert.assertTrue( 0 == m_lockCount ); + ++m_lockCount; + gotIt = true; + } + } + return gotIt; + } + + // Wait forever (but may assert if too long) + public GameLock lock() + { + return this.lock( 0 ); + } + + // Version that's allowed to return null -- if maxMillis > 0 + public GameLock lock( long maxMillis ) + { + GameLock result = null; + final long assertTime = 2000; + Assert.assertTrue( maxMillis < assertTime ); + long sleptTime = 0; + + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid, maxMillis ); + } + + for ( ; ; ) { + if ( tryLock() ) { + result = this; + break; + } + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "GameLock.lock() %H failed; sleeping", this ); + DbgUtils.printStack(); + } + try { + Thread.sleep( 25 ); // milliseconds + sleptTime += 25; + } catch( InterruptedException ie ) { + DbgUtils.loge( ie ); + break; + } + + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "GameLock.lock() %H awake; " + + "sleptTime now %d millis", this, sleptTime ); + } + + if ( 0 < maxMillis && sleptTime >= maxMillis ) { + break; + } else if ( sleptTime >= assertTime ) { + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "lock %H overlocked. lock holding stack:", + this ); + DbgUtils.printStack( m_lockTrace ); + DbgUtils.logf( "lock %H seeking stack:", this ); + DbgUtils.printStack(); + } + Assert.fail(); + } + } + // DbgUtils.logf( "GameLock.lock(%s) done", m_path ); + return result; + } + + public void unlock() + { + // DbgUtils.logf( "GameLock.unlock(%s)", m_path ); + synchronized( s_locks ) { + Assert.assertTrue( this == s_locks.get(m_rowid) ); + if ( 1 == m_lockCount ) { + s_locks.remove( m_rowid ); + } else { + Assert.assertTrue( !m_isForWrite ); + } + --m_lockCount; + + if ( XWApp.DEBUG_LOCKS ) { + DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) unlocked", + this, m_rowid ); + } + } + } + + public long getRowid() + { + return m_rowid; + } + + // used only for asserts + public boolean canWrite() + { + return m_isForWrite && 1 == m_lockCount; + } +} diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index e9cc53ce7..e03101184 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -46,148 +46,6 @@ public class GameUtils { public static final String INTENT_KEY_ROWID = "rowid"; public static final String INTENT_FORRESULT_ROWID = "forresult"; - // Implements read-locks and write-locks per game. A read lock is - // obtainable when other read locks are granted but not when a - // write lock is. Write-locks are exclusive. - public static class GameLock { - private long m_rowid; - private boolean m_isForWrite; - private int m_lockCount; - StackTraceElement[] m_lockTrace; - - private static HashMap - s_locks = new HashMap(); - - public GameLock( long rowid, boolean isForWrite ) - { - m_rowid = rowid; - m_isForWrite = isForWrite; - m_lockCount = 0; - if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "GameLock.GameLock(rowid:%d,isForWrite:%b)=>" - + "this: %H", rowid, isForWrite, this ); - DbgUtils.printStack(); - } - } - - // This could be written to allow multiple read locks. Let's - // see if not doing that causes problems. - public boolean tryLock() - { - boolean gotIt = false; - synchronized( s_locks ) { - GameLock owner = s_locks.get( m_rowid ); - if ( null == owner ) { // unowned - Assert.assertTrue( 0 == m_lockCount ); - s_locks.put( m_rowid, this ); - ++m_lockCount; - gotIt = true; - - if ( XWApp.DEBUG_LOCKS ) { - StackTraceElement[] trace = Thread.currentThread(). - getStackTrace(); - m_lockTrace = new StackTraceElement[trace.length]; - System.arraycopy( trace, 0, m_lockTrace, 0, trace.length ); - } - } else if ( this == owner && ! m_isForWrite ) { - Assert.assertTrue( 0 == m_lockCount ); - ++m_lockCount; - gotIt = true; - } - } - return gotIt; - } - - // Wait forever (but may assert if too long) - public GameLock lock() - { - return this.lock( 0 ); - } - - // Version that's allowed to return null -- if maxMillis > 0 - public GameLock lock( long maxMillis ) - { - GameLock result = null; - final long assertTime = 2000; - Assert.assertTrue( maxMillis < assertTime ); - long sleptTime = 0; - - if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", - this, m_rowid, maxMillis ); - } - - for ( ; ; ) { - if ( tryLock() ) { - result = this; - break; - } - if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "GameLock.lock() %H failed (rowid:%d); sleeping", - this, m_rowid ); - // DbgUtils.printStack(); - } - try { - Thread.sleep( 25 ); // milliseconds - sleptTime += 25; - } catch( InterruptedException ie ) { - DbgUtils.loge( ie ); - break; - } - - if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "GameLock.lock() %H awake; " - + "sleptTime now %d millis", this, sleptTime ); - } - - if ( 0 < maxMillis && sleptTime >= maxMillis ) { - break; - } else if ( sleptTime >= assertTime ) { - if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "lock %H overlocked waiting for rowid:%d. " - + "lock holding stack:", m_rowid, this ); - DbgUtils.printStack( m_lockTrace ); - DbgUtils.logf( "lock %H seeking stack:", this ); - DbgUtils.printStack(); - } - Assert.fail(); - } - } - // DbgUtils.logf( "GameLock.lock(%s) done", m_path ); - return result; - } - - public void unlock() - { - // DbgUtils.logf( "GameLock.unlock(%s)", m_path ); - synchronized( s_locks ) { - Assert.assertTrue( this == s_locks.get(m_rowid) ); - if ( 1 == m_lockCount ) { - s_locks.remove( m_rowid ); - } else { - Assert.assertTrue( !m_isForWrite ); - } - --m_lockCount; - - if ( XWApp.DEBUG_LOCKS ) { - DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) " - + "unlocked", this, m_rowid ); - } - } - } - - public long getRowid() - { - return m_rowid; - } - - // used only for asserts - public boolean canWrite() - { - return m_isForWrite && 1 == m_lockCount; - } - } - private static Object s_syncObj = new Object(); public static byte[] savedGame( Context context, long rowid ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 833c4eac4..2d3ca4f37 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -651,8 +651,7 @@ public class GamesList extends XWListActivity showOKOnlyDialog( R.string.no_copy_network ); } else { byte[] stream = GameUtils.savedGame( this, m_rowid ); - GameUtils.GameLock lock = - GameUtils.saveNewGame( this, stream ); + GameLock lock = GameUtils.saveNewGame( this, stream ); DBUtils.saveSummary( this, lock, summary ); lock.unlock(); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java index e5571a6a1..d3f4a6795 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java @@ -41,7 +41,7 @@ public class RelayGameActivity extends XWActivity private long m_rowid; private CurGameInfo m_gi; - private GameUtils.GameLock m_gameLock; + private GameLock m_gameLock; private CommsAddrRec m_car; private Button m_playButton; private Button m_configButton; @@ -68,7 +68,7 @@ public class RelayGameActivity extends XWActivity super.onStart(); m_gi = new CurGameInfo( this ); - m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock(); + m_gameLock = new GameLock( m_rowid, true ).lock(); int gamePtr = GameUtils.loadMakeGame( this, m_gi, m_gameLock ); m_car = new CommsAddrRec(); if ( XwJNI.game_hasComms( gamePtr ) ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java index 71577dc6a..e39adc389 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -34,6 +34,7 @@ import org.eehouse.android.xw4.R; import org.eehouse.android.xw4.DbgUtils; import org.eehouse.android.xw4.ConnStatusHandler; import org.eehouse.android.xw4.BoardDims; +import org.eehouse.android.xw4.GameLock; import org.eehouse.android.xw4.GameUtils; import org.eehouse.android.xw4.DBUtils; import org.eehouse.android.xw4.Toolbar; @@ -121,7 +122,7 @@ public class JNIThread extends Thread { private boolean m_stopped = false; private boolean m_saveOnStop = false; private int m_jniGamePtr; - private GameUtils.GameLock m_lock; + private GameLock m_lock; private Context m_context; private CurGameInfo m_gi; private Handler m_handler; @@ -143,7 +144,7 @@ public class JNIThread extends Thread { } public JNIThread( int gamePtr, CurGameInfo gi, SyncedDraw drawer, - GameUtils.GameLock lock, Context context, Handler handler ) + GameLock lock, Context context, Handler handler ) { m_jniGamePtr = gamePtr; m_gi = gi; From 7a1de73fb79b71ab5c876934f8ad41cd155ec6a9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 13 Dec 2012 20:18:36 -0800 Subject: [PATCH 39/50] remove unused file --- xwords4/android/XWords4/res/layout/game_list_tmp.xml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 xwords4/android/XWords4/res/layout/game_list_tmp.xml diff --git a/xwords4/android/XWords4/res/layout/game_list_tmp.xml b/xwords4/android/XWords4/res/layout/game_list_tmp.xml deleted file mode 100644 index 00ec641a9..000000000 --- a/xwords4/android/XWords4/res/layout/game_list_tmp.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - From 322a65ee412817f0a396fd4032179b137cae47e3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 13 Dec 2012 20:22:11 -0800 Subject: [PATCH 40/50] in several places where lock() was being called without a timeout, add one, and fail gracefully when a timeout occurs. --- .../src/org/eehouse/android/xw4/DBUtils.java | 13 +-- .../org/eehouse/android/xw4/GameUtils.java | 81 +++++++++++-------- .../org/eehouse/android/xw4/GamesList.java | 20 +++-- .../android/xw4/RelayGameActivity.java | 34 ++++---- 4 files changed, 89 insertions(+), 59 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 698c9b905..235dc108f 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -678,8 +678,7 @@ public class DBUtils { } } - public static GameLock saveNewGame( Context context, - byte[] bytes ) + public static GameLock saveNewGame( Context context, byte[] bytes ) { GameLock lock = null; @@ -760,9 +759,13 @@ public class DBUtils { public static void deleteGame( Context context, long rowid ) { - GameLock lock = new GameLock( rowid, true ).lock(); - deleteGame( context, lock ); - lock.unlock(); + GameLock lock = new GameLock( rowid, true ).lock( 300 ); + if ( null != lock ) { + deleteGame( context, lock ); + lock.unlock(); + } else { + DbgUtils.logf( "deleteGame: unable to lock rowid %d", rowid ); + } } public static void deleteGame( Context context, GameLock lock ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index e03101184..eb9f18303 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -114,12 +114,16 @@ public class GameUtils { public static void resetGame( Context context, long rowidIn ) { - GameLock lock = new GameLock( rowidIn, true ).lock(); - tellDied( context, lock, true ); - resetGame( context, lock, lock, false ); - lock.unlock(); + GameLock lock = new GameLock( rowidIn, true ).lock( 500 ); + if ( null != lock ) { + tellDied( context, lock, true ); + resetGame( context, lock, lock, false ); + lock.unlock(); - Utils.cancelNotification( context, (int)rowidIn ); + Utils.cancelNotification( context, (int)rowidIn ); + } else { + DbgUtils.logf( "resetGame: unable to open rowid %d", rowidIn ); + } } private static GameSummary summarizeAndClose( Context context, @@ -172,12 +176,17 @@ public class GameUtils { public static long dupeGame( Context context, long rowidIn ) { - boolean juggle = CommonPrefs.getAutoJuggle( context ); - GameLock lockSrc = new GameLock( rowidIn, false ).lock(); - GameLock lockDest = resetGame( context, lockSrc, null, juggle ); - long rowid = lockDest.getRowid(); - lockDest.unlock(); - lockSrc.unlock(); + long rowid = DBUtils.ROWID_NOTFOUND; + GameLock lockSrc = new GameLock( rowidIn, false ).lock( 300 ); + if ( null != lockSrc ) { + boolean juggle = CommonPrefs.getAutoJuggle( context ); + GameLock lockDest = resetGame( context, lockSrc, null, juggle ); + rowid = lockDest.getRowid(); + lockDest.unlock(); + lockSrc.unlock(); + } else { + DbgUtils.logf( "dupeGame: unable to open rowid %d", rowidIn ); + } return rowid; } @@ -634,34 +643,42 @@ public class GameUtils { // This *must* involve a reset if the language is changing!!! // Which isn't possible right now, so make sure the old and new // dict have the same langauge code. - public static void replaceDicts( Context context, long rowid, - String oldDict, String newDict ) + public static boolean replaceDicts( Context context, long rowid, + String oldDict, String newDict ) { - GameLock lock = new GameLock( rowid, true ).lock(); - byte[] stream = savedGame( context, lock ); - CurGameInfo gi = new CurGameInfo( context ); - XwJNI.gi_from_stream( gi, stream ); + GameLock lock = new GameLock( rowid, true ).lock(300); + boolean success = null != lock; + if ( success ) { + byte[] stream = savedGame( context, lock ); + CurGameInfo gi = new CurGameInfo( context ); + XwJNI.gi_from_stream( gi, stream ); - // first time required so dictNames() will work - gi.replaceDicts( newDict ); + // first time required so dictNames() will work + gi.replaceDicts( newDict ); - String[] dictNames = gi.dictNames(); - DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); + String[] dictNames = gi.dictNames(); + DictUtils.DictPairs pairs = DictUtils.openDicts( context, + dictNames ); - int gamePtr = XwJNI.initJNI(); - XwJNI.game_makeFromStream( gamePtr, stream, gi, dictNames, - pairs.m_bytes, pairs.m_paths, - gi.langName(), JNIUtilsImpl.get(context), - CommonPrefs.get( context ) ); - // second time required as game_makeFromStream can overwrite - gi.replaceDicts( newDict ); + int gamePtr = XwJNI.initJNI(); + XwJNI.game_makeFromStream( gamePtr, stream, gi, dictNames, + pairs.m_bytes, pairs.m_paths, + gi.langName(), + JNIUtilsImpl.get(context), + CommonPrefs.get( context ) ); + // second time required as game_makeFromStream can overwrite + gi.replaceDicts( newDict ); - saveGame( context, gamePtr, gi, lock, false ); + saveGame( context, gamePtr, gi, lock, false ); - summarizeAndClose( context, lock, gamePtr, gi ); + summarizeAndClose( context, lock, gamePtr, gi ); - lock.unlock(); - } + lock.unlock(); + } else { + DbgUtils.logf( "replaceDicts: unable to open rowid %d", rowid ); + } + return success; + } // replaceDicts public static void applyChanges( Context context, CurGameInfo gi, CommsAddrRec car, GameLock lock, diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 2d3ca4f37..83be99efb 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -165,11 +165,12 @@ public class GamesList extends XWListActivity getCheckedItemPosition(); String dict = m_sameLangDicts[pos]; dict = DictLangCache.stripCount( dict ); - GameUtils.replaceDicts( GamesList.this, - m_missingDictRowId, - m_missingDictName, - dict ); - launchGameIf(); + if ( GameUtils.replaceDicts( GamesList.this, + m_missingDictRowId, + m_missingDictName, + dict ) ) { + launchGameIf(); + } } }; dialog = new AlertDialog.Builder( this ) @@ -721,9 +722,12 @@ public class GamesList extends XWListActivity } else if ( null != m_missingDictName ) { showDialog( WARN_NODICT_SUBST ); } else { - String dict = DictLangCache.getHaveLang( this, m_missingDictLang)[0]; - GameUtils.replaceDicts( this, m_missingDictRowId, null, dict ); - launchGameIf(); + String dict = + DictLangCache.getHaveLang( this, m_missingDictLang)[0]; + if ( GameUtils.replaceDicts( this, m_missingDictRowId, + null, dict ) ) { + launchGameIf(); + } } } return hasDicts; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java index d3f4a6795..16ead9288 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java @@ -68,22 +68,28 @@ public class RelayGameActivity extends XWActivity super.onStart(); m_gi = new CurGameInfo( this ); - m_gameLock = new GameLock( m_rowid, true ).lock(); - int gamePtr = GameUtils.loadMakeGame( this, m_gi, m_gameLock ); - m_car = new CommsAddrRec(); - if ( XwJNI.game_hasComms( gamePtr ) ) { - XwJNI.comms_getAddr( gamePtr, m_car ); + m_gameLock = new GameLock( m_rowid, true ).lock( 300 ); + if ( null == m_gameLock ) { + DbgUtils.logf( "RelayGameActivity.onStart(): unable to lock rowid %d", + m_rowid ); + finish(); } else { - Assert.fail(); - // String relayName = CommonPrefs.getDefaultRelayHost( this ); - // int relayPort = CommonPrefs.getDefaultRelayPort( this ); - // XwJNI.comms_getInitialAddr( m_carOrig, relayName, relayPort ); - } - XwJNI.game_dispose( gamePtr ); + int gamePtr = GameUtils.loadMakeGame( this, m_gi, m_gameLock ); + m_car = new CommsAddrRec(); + if ( XwJNI.game_hasComms( gamePtr ) ) { + XwJNI.comms_getAddr( gamePtr, m_car ); + } else { + Assert.fail(); + // String relayName = CommonPrefs.getDefaultRelayHost( this ); + // int relayPort = CommonPrefs.getDefaultRelayPort( this ); + // XwJNI.comms_getInitialAddr( m_carOrig, relayName, relayPort ); + } + XwJNI.game_dispose( gamePtr ); - String lang = DictLangCache.getLangName( this, m_gi.dictLang ); - TextView text = (TextView)findViewById( R.id.explain ); - text.setText( getString( R.string.relay_game_explainf, lang ) ); + String lang = DictLangCache.getLangName( this, m_gi.dictLang ); + TextView text = (TextView)findViewById( R.id.explain ); + text.setText( getString( R.string.relay_game_explainf, lang ) ); + } } @Override From 028899a9c453bb29da9041362466b0d48f146806 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 14 Dec 2012 07:24:26 -0800 Subject: [PATCH 41/50] no need to set action on local intent --- .../android/XWords4/src/org/eehouse/android/xw4/GameUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index eb9f18303..1e203a90c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -538,7 +538,6 @@ public class GameUtils { boolean invited ) { Intent intent = new Intent( activity, BoardActivity.class ); - intent.setAction( Intent.ACTION_EDIT ); intent.putExtra( INTENT_KEY_ROWID, rowid ); if ( invited ) { intent.putExtra( INVITED, true ); From 8125c451cd994d90e0376c31cc53af5bff3593d5 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 14 Dec 2012 07:26:13 -0800 Subject: [PATCH 42/50] in GamesList, save rowid of launched game and inval it afterwards so scores etc. get updated. --- .../org/eehouse/android/xw4/GamesList.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 83be99efb..2fa53e3b1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -91,7 +91,7 @@ public class GamesList extends XWListActivity private int m_missingDictLang; private long m_rowid; private NetLaunchInfo m_netLaunchInfo; - // private String m_smsPhone; + private long m_invalRowID = DBUtils.ROWID_NOTFOUND; @Override protected Dialog onCreateDialog( int id ) @@ -379,6 +379,10 @@ public class GamesList extends XWListActivity if ( hasFocus ) { updateField(); } + if ( hasFocus && DBUtils.ROWID_NOTFOUND != m_invalRowID ) { + m_adapter.inval( m_invalRowID ); + m_invalRowID = DBUtils.ROWID_NOTFOUND; + } } // DBUtils.DBChangeListener interface @@ -415,7 +419,7 @@ public class GamesList extends XWListActivity GameUtils.doConfig( this, rowid, clazz ); } else { if ( checkWarnNoDict( rowid ) ) { - GameUtils.launchGame( this, rowid ); + launchGame( rowid ); } } } @@ -758,7 +762,7 @@ public class GamesList extends XWListActivity if ( null != rowids ) { for ( long rowid : rowids ) { if ( GameUtils.gameDictsHere( this, rowid ) ) { - GameUtils.launchGame( this, rowid ); + launchGame( rowid ); break outer; } } @@ -816,7 +820,7 @@ public class GamesList extends XWListActivity { long[] rowids = DBUtils.getRowIDsFor( this, gameID ); if ( null != rowids && 0 < rowids.length ) { - GameUtils.launchGame( this, rowids[0] ); + launchGame( rowids[0] ); } } @@ -834,7 +838,7 @@ public class GamesList extends XWListActivity if ( -1 != rowid ) { // this will juggle if the preference is set long newid = GameUtils.dupeGame( this, rowid ); - GameUtils.launchGame( this, newid ); + launchGame( newid ); } } @@ -877,10 +881,21 @@ public class GamesList extends XWListActivity return madeGame; } + private void launchGame( long rowid, boolean invited ) + { + m_invalRowID = rowid; + GameUtils.launchGame( this, rowid, invited ); + } + + private void launchGame( long rowid ) + { + launchGame( rowid, false ); + } + private void makeNewNetGame( NetLaunchInfo info ) { long rowid = GameUtils.makeNewNetGame( this, info ); - GameUtils.launchGame( this, rowid, true ); + launchGame( rowid, true ); } public static void onGameDictDownload( Context context, Intent intent ) From 646ec65d6645e95f35e1ae91f8d3c33193563d7b Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 14 Dec 2012 18:23:33 -0800 Subject: [PATCH 43/50] wrap elapsed time logging in its own debug flag and turn off. --- .../XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java | 4 ++-- .../android/XWords4/src/org/eehouse/android/xw4/XWApp.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java index 13083266d..e1cedab05 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java @@ -194,7 +194,7 @@ public class ExpiringDelegate { if ( null == m_runnable ) { m_runnable = new Runnable() { public void run() { - if ( XWApp.DEBUG ) { + if ( XWApp.DEBUG_EXP_TIMERS ) { DbgUtils.logf( "ExpiringDelegate: timer fired" + " for %H", this ); } @@ -204,7 +204,7 @@ public class ExpiringDelegate { m_back = null; setBackground(); } - if ( XWApp.DEBUG ) { + if ( XWApp.DEBUG_EXP_TIMERS ) { DbgUtils.logf( "ExpiringDelegate: invalidating" + " view %H", m_view ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index 3d157d54f..51d7db703 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -35,6 +35,7 @@ public class XWApp extends Application { public static final boolean REMATCH_SUPPORTED = false; public static final boolean DEBUG = true; // DON'T SHIP THIS WAY public static final boolean DEBUG_LOCKS = false && DEBUG; + public static final boolean DEBUG_EXP_TIMERS = false && DEBUG; public static final String SMS_PUBLIC_HEADER = "-XW4"; From 3c4f266b8ffece2f53110e3ffc2311ec1fe06251 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 14 Dec 2012 18:46:54 -0800 Subject: [PATCH 44/50] work around problems locating GameListItems when it's time to invalidate them by adding static list of those needing invalidating and checking it in a new onDraw override. --- .../eehouse/android/xw4/GameListAdapter.java | 3 +- .../org/eehouse/android/xw4/GameListItem.java | 100 ++++++++++++++---- 2 files changed, 79 insertions(+), 24 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index 26405c0a2..47b398964 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -90,10 +90,11 @@ public class GameListAdapter extends XWListAdapter { public void inval( long rowid ) { GameListItem child = getItemFor( rowid ); - if ( null != child ) { + if ( null != child && child.getRowID() == rowid ) { child.forceReload(); } else { DbgUtils.logf( "no child for rowid %d", rowid ); + GameListItem.inval( rowid ); m_list.invalidate(); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java index d5d727b08..9cd376d18 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java @@ -21,8 +21,10 @@ package org.eehouse.android.xw4; import android.content.Context; +import android.graphics.Canvas; import android.os.AsyncTask; import android.os.Handler; +// import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.widget.ImageButton; @@ -31,6 +33,8 @@ import android.widget.LinearLayout; import android.widget.TextView; import java.text.DateFormat; import java.util.Date; +import java.util.HashSet; +// import java.util.Iterator; import org.eehouse.android.xw4.jni.GameSummary; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; @@ -38,6 +42,8 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; public class GameListItem extends LinearLayout implements View.OnClickListener { + private static HashSet s_invalRows = new HashSet(); + private Context m_context; private boolean m_loaded; private long m_rowid; @@ -50,6 +56,7 @@ public class GameListItem extends LinearLayout private GameSummary m_summary; private GameListAdapter.LoadItemCB m_cb; private int m_fieldID; + private int m_loadingCount; public GameListItem( Context cx, AttributeSet as ) { @@ -58,6 +65,7 @@ public class GameListItem extends LinearLayout m_loaded = false; m_rowid = DBUtils.ROWID_NOTFOUND; m_lastMoveTime = 0; + m_loadingCount = 0; } public void init( Handler handler, long rowid, int fieldID, @@ -73,7 +81,13 @@ public class GameListItem extends LinearLayout public void forceReload() { + // DbgUtils.logf( "GameListItem.forceReload: rowid=%d", m_rowid ); m_summary = null; + setLoaded( false ); + // Apparently it's impossible to reliably cancel an existing + // AsyncTask, so let it complete, but drop the results as soon + // as we're back on the UI thread. + ++m_loadingCount; new LoadItemTask().execute(); } @@ -82,6 +96,19 @@ public class GameListItem extends LinearLayout setName(); } + @Override + protected void onDraw( Canvas canvas ) + { + super.onDraw( canvas ); + if ( DBUtils.ROWID_NOTFOUND != m_rowid ) { + synchronized( s_invalRows ) { + if ( s_invalRows.contains( m_rowid ) ) { + forceReload(); + } + } + } + } + private void update( boolean expanded, long lastMoveTime, boolean haveTurn, boolean haveTurnLocal ) { @@ -108,15 +135,15 @@ public class GameListItem extends LinearLayout showHide(); } - private void setLoaded() + private void setLoaded( boolean loaded ) { - if ( !m_loaded ) { - m_loaded = true; + if ( loaded != m_loaded ) { + m_loaded = loaded; // This should be enough to invalidate findViewById( R.id.view_unloaded ) - .setVisibility( m_loaded ? View.GONE : View.VISIBLE ); + .setVisibility( loaded ? View.GONE : View.VISIBLE ); findViewById( R.id.view_loaded ) - .setVisibility( m_loaded ? View.VISIBLE : View.GONE ); + .setVisibility( loaded ? View.VISIBLE : View.GONE ); } } @@ -168,16 +195,16 @@ public class GameListItem extends LinearLayout return state; } - private void setData() + private void setData( final GameSummary summary ) { - if ( null != m_summary ) { + if ( null != summary ) { TextView view; String state = setName(); setOnClickListener( new View.OnClickListener() { @Override public void onClick( View v ) { - m_cb.itemClicked( m_rowid, m_summary ); + m_cb.itemClicked( m_rowid, summary ); } } ); @@ -187,14 +214,14 @@ public class GameListItem extends LinearLayout boolean haveATurn = false; boolean haveALocalTurn = false; boolean[] isLocal = new boolean[1]; - for ( int ii = 0; ii < m_summary.nPlayers; ++ii ) { + for ( int ii = 0; ii < summary.nPlayers; ++ii ) { ExpiringLinearLayout tmp = (ExpiringLinearLayout) Utils.inflate( m_context, R.layout.player_list_elem ); view = (TextView)tmp.findViewById( R.id.item_name ); - view.setText( m_summary.summarizePlayer( ii ) ); + view.setText( summary.summarizePlayer( ii ) ); view = (TextView)tmp.findViewById( R.id.item_score ); - view.setText( String.format( " %d", m_summary.scores[ii] ) ); - boolean thisHasTurn = m_summary.isNextToPlay( ii, isLocal ); + view.setText( String.format( " %d", summary.scores[ii] ) ); + boolean thisHasTurn = summary.isNextToPlay( ii, isLocal ); if ( thisHasTurn ) { haveATurn = true; if ( isLocal[0] ) { @@ -202,14 +229,14 @@ public class GameListItem extends LinearLayout } } tmp.setPct( m_handler, thisHasTurn, isLocal[0], - m_summary.lastMoveTime ); + summary.lastMoveTime ); list.addView( tmp, ii ); } view = (TextView)findViewById( R.id.state ); view.setText( state ); view = (TextView)findViewById( R.id.modtime ); - long lastMoveTime = m_summary.lastMoveTime; + long lastMoveTime = summary.lastMoveTime; lastMoveTime *= 1000; DateFormat df = DateFormat.getDateTimeInstance( DateFormat.SHORT, @@ -219,7 +246,7 @@ public class GameListItem extends LinearLayout int iconID; ImageView marker = (ImageView)findViewById( R.id.msg_marker ); - CommsConnType conType = m_summary.conType; + CommsConnType conType = summary.conType; if ( CommsConnType.COMMS_CONN_RELAY == conType ) { iconID = R.drawable.relaygame; } else if ( CommsConnType.COMMS_CONN_BT == conType ) { @@ -232,7 +259,7 @@ public class GameListItem extends LinearLayout marker.setImageResource( iconID ); view = (TextView)findViewById( R.id.role ); - String roleSummary = m_summary.summarizeRole(); + String roleSummary = summary.summarizeRole(); if ( null != roleSummary ) { view.setText( roleSummary ); } else { @@ -241,7 +268,7 @@ public class GameListItem extends LinearLayout boolean expanded = DBUtils.getExpanded( m_context, m_rowid ); - update( expanded, m_summary.lastMoveTime, haveATurn, + update( expanded, summary.lastMoveTime, haveATurn, haveALocalTurn ); } } @@ -256,13 +283,40 @@ public class GameListItem extends LinearLayout @Override protected void onPostExecute( GameSummary summary ) { - m_summary = summary; - setData(); - // setLoaded( m_view.getRowID() ); - setLoaded(); - - // DbgUtils.logf( "LoadItemTask for row %d finished", m_rowid ); + if ( 0 == --m_loadingCount ) { + m_summary = summary; + setData( summary ); + setLoaded( null != m_summary ); + synchronized( s_invalRows ) { + s_invalRows.remove( m_rowid ); + } + } + // DbgUtils.logf( "LoadItemTask for row %d finished; " + // + "inval rows now %s", + // m_rowid, invalRowsToString() ); } } // class LoadItemTask + public static void inval( long rowid ) + { + synchronized( s_invalRows ) { + s_invalRows.add( rowid ); + } + // DbgUtils.logf( "GameListItem.inval(rowid=%d); inval rows now %s", + // rowid, invalRowsToString() ); + } + + // private static String invalRowsToString() + // { + // String[] strs; + // synchronized( s_invalRows ) { + // strs = new String[s_invalRows.size()]; + // Iterator iter = s_invalRows.iterator(); + // for ( int ii = 0; iter.hasNext(); ++ii ) { + // strs[ii] = String.format("%d", iter.next() ); + // } + // } + // return TextUtils.join(",", strs ); + // } + } From 51a5e80a267f487acdc7691d8ab55aa106711ba7 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 16 Dec 2012 15:13:36 -0800 Subject: [PATCH 45/50] mention attachment in invite email text --- xwords4/android/XWords4/res/values/strings.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index cdd796f20..e9288214b 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -1229,8 +1229,10 @@ encodings for the greater-than and less-than symbols which are not legal in xml strings.)--> \u003ca href=\"%1$s\"\u003ETap - here\u003c/a\u003E (or the full link below) to accept my invitation and - join this game. + here\u003c/a\u003E (or tap the full link below, or, if you already + have Crosswords installed, open the attachment) to accept my + invitation and join this game. + \u003cbr \\\u003E \u003cbr \\\u003E (full link: %1$s) From e38b99c0c8feedf2aa06030d15c1988aa1497bcf Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 16 Dec 2012 15:14:04 -0800 Subject: [PATCH 46/50] set DEBUG to false for release --- xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index 51d7db703..1f0790181 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -33,7 +33,7 @@ public class XWApp extends Application { public static final boolean GCMSUPPORTED = true; public static final boolean ATTACH_SUPPORTED = true; public static final boolean REMATCH_SUPPORTED = false; - public static final boolean DEBUG = true; // DON'T SHIP THIS WAY + public static final boolean DEBUG = false; public static final boolean DEBUG_LOCKS = false && DEBUG; public static final boolean DEBUG_EXP_TIMERS = false && DEBUG; From 60adf36718116aeabe67e65e78accbced74101fc Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 16 Dec 2012 15:14:33 -0800 Subject: [PATCH 47/50] fix typo --- xwords4/android/scripts/and_index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xwords4/android/scripts/and_index.php b/xwords4/android/scripts/and_index.php index 01dac83c2..7d0a53cc1 100644 --- a/xwords4/android/scripts/and_index.php +++ b/xwords4/android/scripts/and_index.php @@ -75,8 +75,8 @@ link in your invite email (or text) again, and this time let Crosswords handle it.

        (If you get tired of having to having to make that choice, Android -will allow you to make Crosswords the default. What you're saying is -that Crosswords will be giving control of all URLs that start with +will allow you to make Crosswords the default. If you do that +Crosswords will be given control of all URLs that start with "http://eehouse.org/and/" -- not all URLs of any type.)

        Have fun. And as always, let From 9d7d0aca2bf21652047613e5ccac3bbfd70c1580 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 16 Dec 2012 20:14:15 -0800 Subject: [PATCH 48/50] cleanup: logging and unused imports --- .../src/org/eehouse/android/xw4/DBUtils.java | 1 - .../org/eehouse/android/xw4/GameListAdapter.java | 14 -------------- .../src/org/eehouse/android/xw4/GameUtils.java | 2 -- 3 files changed, 17 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 235dc108f..84a8bcae5 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -813,7 +813,6 @@ public class DBUtils { private static void clearRowIDsCache() { - DbgUtils.logf( "DBUtils.clearRowIDsCache()" ); synchronized( DBUtils.class ) { s_cachedRowIDs = null; } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index 47b398964..4a6290d7e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -20,26 +20,15 @@ package org.eehouse.android.xw4; import android.content.Context; -import android.database.DataSetObserver; -import android.os.Build; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; -import android.widget.TextView; -import java.io.FileInputStream; -import java.text.DateFormat; -import java.util.Date; -import java.util.Random; import junit.framework.Assert; - import org.eehouse.android.xw4.jni.*; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; @@ -50,7 +39,6 @@ public class GameListAdapter extends XWListAdapter { private LayoutInflater m_factory; private int m_fieldID; private Handler m_handler; - private DateFormat m_df; private LoadItemCB m_cb; public interface LoadItemCB { @@ -65,8 +53,6 @@ public class GameListAdapter extends XWListAdapter { m_handler = handler; m_cb = cb; m_factory = LayoutInflater.from( context ); - m_df = DateFormat.getDateTimeInstance( DateFormat.SHORT, - DateFormat.SHORT ); m_fieldID = fieldToID( fieldName ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 1e203a90c..37b765f6a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -430,8 +430,6 @@ public class GameUtils { String mime = context.getString( R.string.invite_mime ); intent.setType( mime ); Uri uri = Uri.fromFile( attach ); - DbgUtils.logf( "using file uri %s, type %s for attachment", - uri.toString(), mime ); intent.putExtra( Intent.EXTRA_STREAM, uri ); } From 6988b1424737d9ea4d9aa9efc9d6e56935a03102 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 18 Dec 2012 05:40:25 -0800 Subject: [PATCH 49/50] no need for redraw in onWindowFocusChanged --- .../XWords4/src/org/eehouse/android/xw4/GamesList.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 2fa53e3b1..84bfba2ae 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -91,7 +91,6 @@ public class GamesList extends XWListActivity private int m_missingDictLang; private long m_rowid; private NetLaunchInfo m_netLaunchInfo; - private long m_invalRowID = DBUtils.ROWID_NOTFOUND; @Override protected Dialog onCreateDialog( int id ) @@ -379,10 +378,6 @@ public class GamesList extends XWListActivity if ( hasFocus ) { updateField(); } - if ( hasFocus && DBUtils.ROWID_NOTFOUND != m_invalRowID ) { - m_adapter.inval( m_invalRowID ); - m_invalRowID = DBUtils.ROWID_NOTFOUND; - } } // DBUtils.DBChangeListener interface @@ -883,7 +878,6 @@ public class GamesList extends XWListActivity private void launchGame( long rowid, boolean invited ) { - m_invalRowID = rowid; GameUtils.launchGame( this, rowid, invited ); } From 8cb9d3d66b61a0f2fdd451572d3610523c969b01 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 18 Dec 2012 05:56:14 -0800 Subject: [PATCH 50/50] turn debug back on --- xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index 1f0790181..d3d02f534 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -33,7 +33,7 @@ public class XWApp extends Application { public static final boolean GCMSUPPORTED = true; public static final boolean ATTACH_SUPPORTED = true; public static final boolean REMATCH_SUPPORTED = false; - public static final boolean DEBUG = false; + public static final boolean DEBUG = true; public static final boolean DEBUG_LOCKS = false && DEBUG; public static final boolean DEBUG_EXP_TIMERS = false && DEBUG;