mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-01 06:19:57 +01:00
Merge branch 'android_branch' into android_groups
Conflicts: xwords4/android/XWords4/res/values/strings.xml xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java
This commit is contained in:
commit
8a58492389
44 changed files with 1333 additions and 828 deletions
|
@ -22,11 +22,11 @@
|
|||
to come from a domain that you own or have control over. -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.eehouse.android.xw4"
|
||||
android:versionCode="48"
|
||||
android:versionCode="49"
|
||||
android:versionName="@string/app_version"
|
||||
>
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" />
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
@ -134,14 +134,12 @@
|
|||
/>
|
||||
</intent-filter>
|
||||
|
||||
<!-- <intent-filter> -->
|
||||
<!-- <action android:name="android.intent.action.VIEW" /> -->
|
||||
<!-- <category android:name="android.intent.category.DEFAULT" /> -->
|
||||
<!-- <category android:name="android.intent.category.BROWSABLE" /> -->
|
||||
<!-- <data android:mimeType="*/*" -->
|
||||
<!-- android:scheme="content" -->
|
||||
<!-- /> -->
|
||||
<!-- </intent-filter> -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:mimeType="@string/invite_mime" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- downloading dicts -->
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,95 +3,114 @@
|
|||
|
||||
|
||||
<!-- top-level layout is hozontal, with an image and another layout -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:longClickable="true"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:background="@android:drawable/list_selector_background"
|
||||
>
|
||||
<org.eehouse.android.xw4.GameListItem
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:longClickable="true"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:background="@android:drawable/list_selector_background"
|
||||
>
|
||||
|
||||
<ImageView android:id="@+id/msg_marker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:layout_weight="0"
|
||||
/>
|
||||
<TextView android:id="@+id/view_unloaded"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:gravity="center"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="@string/game_list_tmp"
|
||||
/>
|
||||
|
||||
<!-- this layout is vertical, holds everything but the status
|
||||
icon[s] (plural later) -->
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<!-- This is the game name and expander -->
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<org.eehouse.android.xw4.ExpiringTextView
|
||||
android:id="@+id/game_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
|
||||
<ImageButton android:id="@+id/expander"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@drawable/expander_ic_maximized"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- This is everything below the name (which can be hidden) -->
|
||||
<LinearLayout android:id="@+id/hideable"
|
||||
<LinearLayout android:id="@+id/view_loaded"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4sp">
|
||||
android:visibility="gone"
|
||||
>
|
||||
|
||||
<!-- Player list plus connection status -->
|
||||
<LinearLayout android:id="@+id/player_list"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginRight="4dip"
|
||||
/> <!-- end players column -->
|
||||
<ImageView android:id="@+id/msg_marker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:layout_weight="0"
|
||||
/>
|
||||
|
||||
<!-- holds right column. Could hold more... -->
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
>
|
||||
<TextView android:id="@+id/modtime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
/>
|
||||
<TextView android:id="@+id/state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<!-- this layout is vertical, holds everything but the status
|
||||
icon[s] (plural later) -->
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<!-- This is the game name and expander -->
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<org.eehouse.android.xw4.ExpiringTextView
|
||||
android:id="@+id/game_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
|
||||
<ImageButton android:id="@+id/expander"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@drawable/expander_ic_maximized"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- This is everything below the name (which can be hidden) -->
|
||||
<LinearLayout android:id="@+id/hideable"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4sp">
|
||||
|
||||
<!-- Player list plus connection status -->
|
||||
<LinearLayout android:id="@+id/player_list"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginRight="4dip"
|
||||
/> <!-- end players column -->
|
||||
|
||||
<!-- holds right column. Could hold more... -->
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
>
|
||||
<TextView android:id="@+id/modtime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
/>
|
||||
<TextView android:id="@+id/state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<TextView android:id="@+id/role"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<TextView android:id="@+id/role"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</org.eehouse.android.xw4.GameListItem>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:gravity="center"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="@string/game_list_tmp"
|
||||
/>
|
|
@ -5,18 +5,22 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<b>Crosswords 4.4 beta 56 release</b>
|
||||
<ul>New with this release
|
||||
<li>Improve invitations: no more redirection through a website, and
|
||||
confirm before creating duplicate games</li>
|
||||
<li>(For sideloading users only) No more browser involvement in
|
||||
updates: app can launch the installer directly to update
|
||||
itself</li>
|
||||
<li>Remove notifications when their games are deleted</li>
|
||||
<b>Crosswords 4.4 beta 57 release</b>
|
||||
|
||||
<h3>New with this release</h3>
|
||||
<ul>
|
||||
<li>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) </li>
|
||||
<li>Show final scores alert whenever a finished game is opened -- to
|
||||
make it more clear that it's finished</li>
|
||||
<li>Fix flickering in main screen (games list)</li>
|
||||
<li>Add option, off by default, to keep rack tiles square even when
|
||||
the screen is large enough that they can be taller</li>
|
||||
</ul>
|
||||
|
||||
<ul>Next up
|
||||
<li>One more idea for improving invitations</li>
|
||||
<h3>Next up</h3>
|
||||
<ul>
|
||||
<li>Allow grouping of games in collapsible user-defined categores: "Games with
|
||||
Kati", "Finished games", etc.</li>
|
||||
</ul>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="app_version">4.4 beta 56</string>
|
||||
<string name="app_version">4.4 beta 57</string>
|
||||
</resources>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<!-- prefs keys -->
|
||||
<string name="key_color_tiles">key_color_tiles</string>
|
||||
<string name="key_show_arrow">key_show_arrow</string>
|
||||
<string name="key_square_tiles">key_square_tiles</string>
|
||||
<string name="key_explain_robot">key_explain_robot</string>
|
||||
<string name="key_skip_confirm">key_skip_confirm</string>
|
||||
<string name="key_sort_tiles">key_sort_tiles</string>
|
||||
|
|
|
@ -1233,8 +1233,10 @@
|
|||
encodings for the greater-than and less-than symbols which
|
||||
are not legal in xml strings.)-->
|
||||
<string name="invite_htmf">\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)
|
||||
|
@ -2154,4 +2156,13 @@
|
|||
|
||||
<string name="no_move_onegroup">Moving is impossible until there
|
||||
is more than one group.</string>
|
||||
|
||||
<!-- Button shown in game over dialog triggering creation of new
|
||||
game with the same players and parameters as the one that
|
||||
just ended. -->
|
||||
<string name="button_rematch">Rematch</string>
|
||||
|
||||
<string name="square_tiles">Square rack tiles</string>
|
||||
<string name="square_tiles_summary">Even if they can be taller</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -132,6 +132,11 @@
|
|||
android:summary="@string/show_arrow_summary"
|
||||
android:defaultValue="true"
|
||||
/>
|
||||
<CheckBoxPreference android:key="@string/key_square_tiles"
|
||||
android:title="@string/square_tiles"
|
||||
android:summary="@string/square_tiles_summary"
|
||||
android:defaultValue="false"
|
||||
/>
|
||||
<CheckBoxPreference android:key="@string/key_keep_screenon"
|
||||
android:title="@string/keep_screenon"
|
||||
android:summary="@string/keep_screenon_summary"
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
@ -57,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 )
|
||||
{
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -188,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 ) {
|
||||
|
@ -234,6 +256,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 )
|
||||
|
@ -246,6 +269,14 @@ public class BoardActivity extends XWActivity
|
|||
}
|
||||
};
|
||||
ab.setNegativeButton( R.string.button_retry, lstnr );
|
||||
} else if ( XWApp.REMATCH_SUPPORTED && GAME_OVER == id ) {
|
||||
lstnr = new DialogInterface.OnClickListener() {
|
||||
public void onClick( DialogInterface dlg,
|
||||
int whichButton ) {
|
||||
doRematch();
|
||||
}
|
||||
};
|
||||
ab.setNegativeButton( R.string.button_rematch, lstnr );
|
||||
}
|
||||
dialog = ab.create();
|
||||
Utils.setRemoveOnDismiss( this, dialog, id );
|
||||
|
@ -499,7 +530,9 @@ 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;
|
||||
|
||||
setBackgroundColor();
|
||||
setKeepScreenOn();
|
||||
|
@ -691,8 +724,13 @@ 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
|
||||
|
||||
public boolean onOptionsItemSelected( MenuItem item )
|
||||
{
|
||||
|
@ -830,12 +868,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 );
|
||||
|
@ -907,7 +945,7 @@ public class BoardActivity extends XWActivity
|
|||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// BTService.BTEventListener interface
|
||||
// MultiService.MultiEventListener interface
|
||||
//////////////////////////////////////////////////
|
||||
@Override
|
||||
@SuppressWarnings("fallthrough")
|
||||
|
@ -1635,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 );
|
||||
|
@ -1693,6 +1731,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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1722,8 +1765,18 @@ 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 = false;
|
||||
if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) {
|
||||
m_gameOver = true;
|
||||
} else if ( DBUtils.gameOver( this, m_rowid ) ) {
|
||||
m_gameOver = true;
|
||||
auto = true;
|
||||
}
|
||||
if ( m_gameOver ) {
|
||||
m_overNotShown = false;
|
||||
m_jniThread.handle( JNICmd.CMD_POST_OVER, auto );
|
||||
}
|
||||
}
|
||||
if ( 0 != flags ) {
|
||||
DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE );
|
||||
|
@ -2074,4 +2127,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
|
||||
|
|
|
@ -379,8 +379,13 @@ public class BoardView extends View implements DrawCtx, BoardHandler,
|
|||
heightLeft = cellSize * 3 / 2;
|
||||
}
|
||||
heightLeft /= 3;
|
||||
trayHt += heightLeft * 2;
|
||||
scoreHt += heightLeft;
|
||||
|
||||
trayHt += heightLeft * 2;
|
||||
if ( XWPrefs.getSquareTiles( m_context )
|
||||
&& trayHt > (width / 7) ) {
|
||||
trayHt = width / 7;
|
||||
}
|
||||
heightUsed = trayHt + scoreHt + ((nCells - nToScroll) * cellSize);
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -60,9 +60,10 @@ 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 );
|
||||
public void gameSaved( long rowid, boolean countChanged );
|
||||
}
|
||||
private static HashSet<DBChangeListener> s_listeners =
|
||||
new HashSet<DBChangeListener>();
|
||||
|
@ -100,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();
|
||||
|
@ -115,7 +115,7 @@ public class DBUtils {
|
|||
}
|
||||
|
||||
public static GameSummary getSummary( Context context,
|
||||
GameUtils.GameLock lock )
|
||||
GameLock lock )
|
||||
{
|
||||
initDB( context );
|
||||
GameSummary summary = null;
|
||||
|
@ -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,
|
||||
|
@ -247,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() );
|
||||
|
@ -314,9 +314,12 @@ 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();
|
||||
notifyListeners( rowid, false );
|
||||
}
|
||||
} // saveSummary
|
||||
|
||||
|
@ -372,7 +375,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 )
|
||||
|
@ -447,10 +450,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();
|
||||
|
@ -469,11 +470,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();
|
||||
|
@ -574,7 +572,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 );
|
||||
|
@ -582,26 +580,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;
|
||||
}
|
||||
|
||||
|
@ -675,9 +678,9 @@ 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 ) {
|
||||
|
@ -695,16 +698,16 @@ 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 );
|
||||
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() );
|
||||
|
@ -722,13 +725,13 @@ 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;
|
||||
}
|
||||
|
||||
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 );
|
||||
|
@ -756,12 +759,16 @@ public class DBUtils {
|
|||
|
||||
public static void deleteGame( Context context, long rowid )
|
||||
{
|
||||
GameUtils.GameLock lock = new GameUtils.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, GameUtils.GameLock lock )
|
||||
public static void deleteGame( Context context, GameLock lock )
|
||||
{
|
||||
Assert.assertTrue( lock.canWrite() );
|
||||
initDB( context );
|
||||
|
@ -771,34 +778,46 @@ public class DBUtils {
|
|||
db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
|
||||
db.close();
|
||||
}
|
||||
notifyListeners( lock.getRowid() );
|
||||
clearRowIDsCache();
|
||||
notifyListeners( lock.getRowid(), true );
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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 )
|
||||
{
|
||||
|
@ -1099,6 +1118,7 @@ public class DBUtils {
|
|||
|
||||
public static void loadDB( Context context )
|
||||
{
|
||||
clearRowIDsCache();
|
||||
copyGameDB( context, false );
|
||||
}
|
||||
|
||||
|
@ -1381,12 +1401,29 @@ public class DBUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static void notifyListeners( long rowid )
|
||||
private static void updateRow( Context context, String table,
|
||||
long rowid, ContentValues values )
|
||||
{
|
||||
initDB( context );
|
||||
synchronized( s_dbHelper ) {
|
||||
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
|
||||
|
||||
String selection = String.format( ROW_ID_FMT, rowid );
|
||||
|
||||
int result = db.update( table, values, selection, null );
|
||||
db.close();
|
||||
if ( 0 == result ) {
|
||||
DbgUtils.logf( "updateRow failed" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyListeners( long rowid, boolean countChanged )
|
||||
{
|
||||
synchronized( s_listeners ) {
|
||||
Iterator<DBChangeListener> iter = s_listeners.iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
iter.next().gameSaved( rowid );
|
||||
iter.next().gameSaved( rowid, countChanged );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,22 +580,45 @@ public class DictUtils {
|
|||
return null != getDownloadDir( context );
|
||||
}
|
||||
|
||||
// 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 ( 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 = new File( file, "download/" );
|
||||
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;
|
||||
|
@ -596,4 +633,13 @@ public class DictUtils {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class DirGetter implements SafeDirGetter {
|
||||
public File getDownloadDir()
|
||||
{
|
||||
File path = Environment.
|
||||
getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -194,12 +194,20 @@ public class ExpiringDelegate {
|
|||
if ( null == m_runnable ) {
|
||||
m_runnable = new Runnable() {
|
||||
public void run() {
|
||||
if ( XWApp.DEBUG_EXP_TIMERS ) {
|
||||
DbgUtils.logf( "ExpiringDelegate: timer fired"
|
||||
+ " for %H", this );
|
||||
}
|
||||
if ( m_active ) {
|
||||
figurePct();
|
||||
if ( m_haveTurnLocal ) {
|
||||
m_back = null;
|
||||
setBackground();
|
||||
}
|
||||
if ( XWApp.DEBUG_EXP_TIMERS ) {
|
||||
DbgUtils.logf( "ExpiringDelegate: invalidating"
|
||||
+ " view %H", m_view );
|
||||
}
|
||||
m_view.invalidate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
@ -21,8 +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;
|
||||
import android.view.View;
|
||||
|
@ -44,7 +42,6 @@ import java.util.Set;
|
|||
|
||||
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;
|
||||
|
@ -52,242 +49,35 @@ import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
|
|||
|
||||
public class GameListAdapter implements ExpandableListAdapter {
|
||||
private Context m_context;
|
||||
private ExpandableListView m_list;
|
||||
private LayoutInflater m_factory;
|
||||
private int m_fieldID;
|
||||
private Handler m_handler;
|
||||
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 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<Long,ViewInfo> m_viewsCache;
|
||||
private DateFormat m_df;
|
||||
private LoadItemCB m_cb;
|
||||
|
||||
public interface LoadItemCB {
|
||||
public void itemLoaded( long rowid );
|
||||
public void itemClicked( long rowid );
|
||||
public void itemClicked( long rowid, GameSummary summary );
|
||||
}
|
||||
|
||||
private class LoadItemTask extends AsyncTask<Void, Void, Void> {
|
||||
private long m_rowid;
|
||||
private Context m_context;
|
||||
// private int m_id;
|
||||
public LoadItemTask( Context context, long rowid/*, int id*/ )
|
||||
{
|
||||
m_context = context;
|
||||
m_rowid = rowid;
|
||||
// m_id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void 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 ) {
|
||||
}
|
||||
}
|
||||
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 );
|
||||
|
||||
synchronized( m_viewsCache ) {
|
||||
m_viewsCache.put( m_rowid, vi );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} // doInBackground
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( Void unused )
|
||||
{
|
||||
if ( -1 != m_rowid ) {
|
||||
m_cb.itemLoaded( m_rowid );
|
||||
}
|
||||
}
|
||||
} // class LoadItemTask
|
||||
|
||||
public GameListAdapter( Context context, Handler handler, LoadItemCB cb ) {
|
||||
public GameListAdapter( Context context, ExpandableListView 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_viewsCache = new HashMap<Long,ViewInfo>();
|
||||
m_fieldID = fieldToID( fieldName );
|
||||
}
|
||||
|
||||
public void inval( long rowid )
|
||||
{
|
||||
synchronized( m_viewsCache ) {
|
||||
m_viewsCache.remove( rowid );
|
||||
}
|
||||
}
|
||||
// public void inval( long rowid )
|
||||
// {
|
||||
// synchronized( m_viewsCache ) {
|
||||
// m_viewsCache.remove( rowid );
|
||||
// }
|
||||
// }
|
||||
|
||||
public void expandGroups( ExpandableListView view )
|
||||
{
|
||||
|
@ -301,26 +91,26 @@ public class GameListAdapter implements ExpandableListAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
public void setField( String field )
|
||||
{
|
||||
int[] ids = {
|
||||
R.string.game_summary_field_empty
|
||||
,R.string.game_summary_field_language
|
||||
,R.string.game_summary_field_opponents
|
||||
,R.string.game_summary_field_state
|
||||
};
|
||||
int result = -1;
|
||||
for ( int id : ids ) {
|
||||
if ( m_context.getString( id ).equals( field ) ) {
|
||||
result = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( m_fieldID != result ) {
|
||||
m_viewsCache.clear();
|
||||
m_fieldID = result;
|
||||
}
|
||||
}
|
||||
// public void setField( String field )
|
||||
// {
|
||||
// int[] ids = {
|
||||
// R.string.game_summary_field_empty
|
||||
// ,R.string.game_summary_field_language
|
||||
// ,R.string.game_summary_field_opponents
|
||||
// ,R.string.game_summary_field_state
|
||||
// };
|
||||
// int result = -1;
|
||||
// for ( int id : ids ) {
|
||||
// if ( m_context.getString( id ).equals( field ) ) {
|
||||
// result = id;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if ( m_fieldID != result ) {
|
||||
// m_viewsCache.clear();
|
||||
// m_fieldID = result;
|
||||
// }
|
||||
// }
|
||||
|
||||
public long getRowIDFor( int group, int child )
|
||||
{
|
||||
|
@ -473,39 +263,57 @@ public class GameListAdapter implements ExpandableListAdapter {
|
|||
public void registerDataSetObserver( DataSetObserver obs ){}
|
||||
public void unregisterDataSetObserver( DataSetObserver obs ){}
|
||||
|
||||
private View getItem( final long rowid )
|
||||
// private View getItem( final long rowid )
|
||||
// {
|
||||
// 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 );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// @Override
|
||||
// public int getCount() {
|
||||
// return DBUtils.gamesList(m_context).length;
|
||||
// }
|
||||
|
||||
// // 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 )
|
||||
// {
|
||||
// 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 )
|
||||
{
|
||||
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 child = getItemFor( rowid );
|
||||
if ( null != child && child.getRowID() == rowid ) {
|
||||
child.forceReload();
|
||||
} else {
|
||||
DbgUtils.logf( "no child for rowid %d", rowid );
|
||||
GameListItem.inval( rowid );
|
||||
m_list.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
if ( !haveLayout ) {
|
||||
new LoadItemTask( m_context, rowid/*, ++m_taskCounter*/ ).execute();
|
||||
public void invalName( long rowid )
|
||||
{
|
||||
GameListItem item = getItemFor( rowid );
|
||||
if ( null != item ) {
|
||||
item.invalName();
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
private long[] getRows( String group )
|
||||
{
|
||||
|
@ -538,9 +346,67 @@ public class GameListAdapter implements ExpandableListAdapter {
|
|||
return pos;
|
||||
}
|
||||
|
||||
private HashMap<String,GameGroupInfo> gameInfo()
|
||||
public boolean setField( String fieldName )
|
||||
{
|
||||
return DBUtils.getGroups( m_context );
|
||||
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;
|
||||
// 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 = {
|
||||
R.string.game_summary_field_empty
|
||||
,R.string.game_summary_field_language
|
||||
,R.string.game_summary_field_opponents
|
||||
,R.string.game_summary_field_state
|
||||
};
|
||||
int result = -1;
|
||||
for ( int id : ids ) {
|
||||
if ( m_context.getString( id ).equals( fieldName ) ) {
|
||||
result = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int positionFor( long rowid )
|
||||
{
|
||||
int position = -1;
|
||||
long[] rowids = DBUtils.gamesList( m_context );
|
||||
for ( int ii = 0; ii < rowids.length; ++ii ) {
|
||||
if ( rowids[ii] == rowid ) {
|
||||
position = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
/* -*- 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.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;
|
||||
import android.widget.ImageView;
|
||||
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;
|
||||
|
||||
public class GameListItem extends LinearLayout
|
||||
implements View.OnClickListener {
|
||||
|
||||
private static HashSet<Long> s_invalRows = new HashSet<Long>();
|
||||
|
||||
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;
|
||||
private GameSummary m_summary;
|
||||
private GameListAdapter.LoadItemCB m_cb;
|
||||
private int m_fieldID;
|
||||
private int m_loadingCount;
|
||||
|
||||
public GameListItem( Context cx, AttributeSet as )
|
||||
{
|
||||
super( cx, as );
|
||||
m_context = cx;
|
||||
m_loaded = false;
|
||||
m_rowid = DBUtils.ROWID_NOTFOUND;
|
||||
m_lastMoveTime = 0;
|
||||
m_loadingCount = 0;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
|
||||
public void invalName()
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 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 setLoaded( boolean loaded )
|
||||
{
|
||||
if ( loaded != m_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 );
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
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( final GameSummary summary )
|
||||
{
|
||||
if ( null != summary ) {
|
||||
TextView view;
|
||||
String state = setName();
|
||||
|
||||
setOnClickListener( new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick( View v ) {
|
||||
m_cb.itemClicked( m_rowid, 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 < 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( 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)findViewById( R.id.state );
|
||||
view.setText( state );
|
||||
view = (TextView)findViewById( R.id.modtime );
|
||||
long lastMoveTime = 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 = 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 = summary.summarizeRole();
|
||||
if ( null != roleSummary ) {
|
||||
view.setText( roleSummary );
|
||||
} else {
|
||||
view.setVisibility( View.GONE );
|
||||
}
|
||||
|
||||
boolean expanded = DBUtils.getExpanded( m_context, m_rowid );
|
||||
|
||||
update( expanded, summary.lastMoveTime, haveATurn,
|
||||
haveALocalTurn );
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadItemTask extends AsyncTask<Void, Void, GameSummary> {
|
||||
@Override
|
||||
protected GameSummary doInBackground( Void... unused )
|
||||
{
|
||||
return DBUtils.getSummary( m_context, m_rowid, 150 );
|
||||
} // doInBackground
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( GameSummary summary )
|
||||
{
|
||||
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<Long> iter = s_invalRows.iterator();
|
||||
// for ( int ii = 0; iter.hasNext(); ++ii ) {
|
||||
// strs[ii] = String.format("%d", iter.next() );
|
||||
// }
|
||||
// }
|
||||
// return TextUtils.join(",", strs );
|
||||
// }
|
||||
|
||||
}
|
|
@ -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<Long, GameLock>
|
||||
s_locks = new HashMap<Long,GameLock>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -46,134 +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<Long, GameLock>
|
||||
s_locks = new HashMap<Long,GameLock>();
|
||||
|
||||
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;
|
||||
// DbgUtils.logf( "GameLock.lock(%s)", m_path );
|
||||
// Utils.printStack();
|
||||
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 ( 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;
|
||||
}
|
||||
// DbgUtils.logf( "GameLock.unlock(%s) done", m_path );
|
||||
}
|
||||
|
||||
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 )
|
||||
|
@ -242,10 +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 );
|
||||
} else {
|
||||
DbgUtils.logf( "resetGame: unable to open rowid %d", rowidIn );
|
||||
}
|
||||
}
|
||||
|
||||
private static GameSummary summarizeAndClose( Context context,
|
||||
|
@ -298,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;
|
||||
}
|
||||
|
||||
|
@ -525,22 +408,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,
|
||||
|
@ -566,18 +433,20 @@ 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() );
|
||||
intent.putExtra( Intent.EXTRA_STREAM, uri );
|
||||
}
|
||||
|
||||
|
@ -684,7 +553,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 );
|
||||
|
@ -734,40 +602,45 @@ 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 );
|
||||
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 );
|
||||
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 );
|
||||
|
||||
if ( null != msgs ) {
|
||||
for ( byte[] msg : msgs ) {
|
||||
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret )
|
||||
|| draw;
|
||||
}
|
||||
}
|
||||
XwJNI.comms_ackAny( gamePtr );
|
||||
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 );
|
||||
// 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 );
|
||||
int flags = setFromFeedImpl( feedImpl );
|
||||
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
|
||||
draw = true;
|
||||
DBUtils.setMsgFlags( rowid, flags );
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
return draw;
|
||||
} // feedMessages
|
||||
|
@ -781,52 +654,45 @@ 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.
|
||||
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,
|
||||
|
@ -953,7 +819,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();
|
||||
|
|
|
@ -75,6 +75,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
|
||||
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;
|
||||
|
@ -102,7 +103,6 @@ public class GamesList extends XWExpandableListActivity
|
|||
private String m_nameField;
|
||||
private NetLaunchInfo m_netLaunchInfo;
|
||||
private GameNamer m_namer;
|
||||
// private String m_smsPhone;
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog( int id )
|
||||
|
@ -177,11 +177,12 @@ public class GamesList extends XWExpandableListActivity
|
|||
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 )
|
||||
|
@ -202,8 +203,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
public void onClick( DialogInterface dlg, int item ) {
|
||||
String name = m_namer.getName();
|
||||
DBUtils.setName( GamesList.this, m_rowid, name );
|
||||
m_adapter.inval( m_rowid );
|
||||
onContentChanged();
|
||||
m_adapter.invalName( m_rowid );
|
||||
}
|
||||
};
|
||||
dialog = buildNamerDlg( GameUtils.getName( this, m_rowid ),
|
||||
|
@ -346,7 +346,9 @@ public class GamesList extends XWExpandableListActivity
|
|||
}
|
||||
});
|
||||
|
||||
m_adapter = new GameListAdapter( this, new Handler(), this );
|
||||
String field = CommonPrefs.getSummaryField( this );
|
||||
m_adapter = new GameListAdapter( this, getListView(), new Handler(),
|
||||
this, field );
|
||||
setListAdapter( m_adapter );
|
||||
|
||||
NetUtils.informOfDeaths( this );
|
||||
|
@ -355,6 +357,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
startFirstHasDict( intent );
|
||||
startNewNetGame( intent );
|
||||
startHasGameID( intent );
|
||||
startHasRowID( intent );
|
||||
askDefaultNameIf();
|
||||
} // onCreate
|
||||
|
||||
|
@ -369,6 +372,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
startFirstHasDict( intent );
|
||||
startNewNetGame( intent );
|
||||
startHasGameID( intent );
|
||||
startHasRowID( intent );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -444,28 +448,25 @@ public class GamesList extends XWExpandableListActivity
|
|||
}
|
||||
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
// GameListAdapter.LoadItemCB interface
|
||||
public void itemLoaded( long rowid )
|
||||
{
|
||||
onContentChanged();
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -480,12 +481,12 @@ public class GamesList extends XWExpandableListActivity
|
|||
GameUtils.doConfig( this, rowid, clazz );
|
||||
} else {
|
||||
if ( checkWarnNoDict( rowid ) ) {
|
||||
GameUtils.launchGame( this, rowid );
|
||||
launchGame( rowid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BTService.BTEventListener interface
|
||||
// BTService.MultiEventListener interface
|
||||
@Override
|
||||
public void eventOccurred( MultiService.MultiEvent event,
|
||||
final Object ... args )
|
||||
|
@ -518,6 +519,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
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 );
|
||||
|
@ -526,7 +528,6 @@ public class GamesList extends XWExpandableListActivity
|
|||
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:
|
||||
|
@ -759,8 +760,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
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();
|
||||
}
|
||||
|
@ -859,9 +859,12 @@ public class GamesList extends XWExpandableListActivity
|
|||
} 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;
|
||||
|
@ -878,7 +881,6 @@ public class GamesList extends XWExpandableListActivity
|
|||
}
|
||||
}
|
||||
}
|
||||
onContentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -893,7 +895,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
if ( null != rowids ) {
|
||||
for ( long rowid : rowids ) {
|
||||
if ( GameUtils.gameDictsHere( this, rowid ) ) {
|
||||
GameUtils.launchGame( this, rowid );
|
||||
launchGame( rowid );
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
|
@ -951,7 +953,7 @@ public class GamesList extends XWExpandableListActivity
|
|||
{
|
||||
long[] rowids = DBUtils.getRowIDsFor( this, gameID );
|
||||
if ( null != rowids && 0 < rowids.length ) {
|
||||
GameUtils.launchGame( this, rowids[0] );
|
||||
launchGame( rowids[0] );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -963,6 +965,16 @@ public class GamesList extends XWExpandableListActivity
|
|||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
launchGame( newid );
|
||||
}
|
||||
}
|
||||
|
||||
private void askDefaultNameIf()
|
||||
{
|
||||
if ( null == CommonPrefs.getDefaultPlayerName( this, 0, false ) ) {
|
||||
|
@ -975,9 +987,9 @@ public class GamesList extends XWExpandableListActivity
|
|||
private void updateField()
|
||||
{
|
||||
String newField = CommonPrefs.getSummaryField( this );
|
||||
if ( ! newField.equals( m_nameField ) ) {
|
||||
m_nameField = newField;
|
||||
m_adapter.setField( newField );
|
||||
if ( m_adapter.setField( newField ) ) {
|
||||
// The adapter should be able to decide whether full
|
||||
// content change is required. PENDING
|
||||
onContentChanged();
|
||||
}
|
||||
}
|
||||
|
@ -1019,10 +1031,20 @@ public class GamesList extends XWExpandableListActivity
|
|||
return madeGame;
|
||||
}
|
||||
|
||||
private void launchGame( long rowid, boolean invited )
|
||||
{
|
||||
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 )
|
||||
|
@ -1054,6 +1076,20 @@ public class GamesList extends XWExpandableListActivity
|
|||
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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,22 +68,28 @@ public class RelayGameActivity extends XWActivity
|
|||
super.onStart();
|
||||
|
||||
m_gi = new CurGameInfo( this );
|
||||
m_gameLock = new GameUtils.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
|
||||
|
|
|
@ -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<String> idsWMsgs =
|
||||
new ArrayList<String>( 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 );
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -28,12 +28,14 @@ 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 = false;
|
||||
public static final boolean ATTACH_SUPPORTED = true;
|
||||
public static final boolean REMATCH_SUPPORTED = false;
|
||||
public static final boolean DEBUG = true;
|
||||
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";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -187,7 +187,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 )
|
||||
{
|
||||
|
|
|
@ -41,11 +41,14 @@ 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 long getItemId(int position) { return position; }
|
||||
public int getItemViewType(int position) { return 0; }
|
||||
public Object getItem( int position ) { return null; }
|
||||
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 ) {}
|
||||
}
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
@ -94,6 +95,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;
|
||||
|
@ -120,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;
|
||||
|
@ -142,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;
|
||||
|
@ -524,8 +526,14 @@ public class JNIThread extends Thread {
|
|||
|
||||
case CMD_POST_OVER:
|
||||
if ( XwJNI.server_getGameIsOver( m_jniGamePtr ) ) {
|
||||
sendForDialog( R.string.finalscores_title,
|
||||
XwJNI.server_writeFinalScores( m_jniGamePtr ) );
|
||||
boolean auto = 0 < args.length &&
|
||||
((Boolean)args[0]).booleanValue();
|
||||
int titleID = auto? R.string.summary_gameover
|
||||
: R.string.finalscores_title;
|
||||
|
||||
String text = XwJNI.server_writeFinalScores( m_jniGamePtr );
|
||||
Message.obtain( m_handler, GAME_OVER, titleID, 0, text )
|
||||
.sendToTarget();
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -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 <<<EOF
|
||||
<div class="center">
|
||||
<p>This page is meant to be viewed on an Android device.</p>
|
||||
<hr>
|
||||
<p>(If you <em>are</em> viewing this on an Android device, you've
|
||||
found a bug! Please <a href="mailto:
|
||||
xwords@eehouse.org?subject=$subject&body=$body">email me</a> (and be
|
||||
sure to leave the user agent string in the email body.)
|
||||
<p>(If you <em>are</em> viewing this on an Android device,
|
||||
you've found a bug! Please <a href="mailto:
|
||||
xwords@eehouse.org?subject=$subject&body=$body">email me</a>
|
||||
(and be sure to leave the user agent string in the email body.)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -73,6 +74,11 @@ invite email (or text) and tap the link again.</p>
|
|||
link in your invite email (or text) again, and this time let
|
||||
Crosswords handle it.</p>
|
||||
|
||||
<p>(If you get tired of having to having to make that choice, Android
|
||||
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.)</p>
|
||||
|
||||
<p>Have fun. And as always, <a href="mailto:xwords@eehouse.org">let
|
||||
me know</a> if you have problems or suggestions.</p>
|
||||
</div>
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue