Merge branch 'android_branch' into android_translate

This commit is contained in:
Eric House 2016-01-23 22:03:40 -08:00
commit e6fa514982
39 changed files with 927 additions and 373 deletions

View file

@ -22,7 +22,7 @@
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.xw4dbg"
android:versionCode="95"
android:versionCode="96"
android:versionName="@string/app_version"
>
@ -98,18 +98,15 @@
<activity android:name="BTInviteActivity"
android:label="@string/bt_invite_title"
android:theme="@android:style/Theme.Dialog"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="SMSInviteActivity"
android:label="@string/sms_invite_title"
android:theme="@android:style/Theme.Dialog"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="sensor"
/>
<activity android:name="RelayInviteActivity"
android:label="@string/relay_invite_title"
android:theme="@android:style/Theme.Dialog"
android:configChanges="keyboardHidden|orientation|screenSize"
/>

View file

@ -63,9 +63,7 @@
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_KEEP_LINE_BREAKS" value="false" />
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
@ -79,7 +77,7 @@
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE />
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
@ -89,7 +87,7 @@
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE />
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>

View file

@ -22,7 +22,7 @@
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="95"
android:versionCode="96"
android:versionName="@string/app_version"
>
@ -94,18 +94,15 @@
<activity android:name="BTInviteActivity"
android:label="@string/bt_invite_title"
android:theme="@android:style/Theme.Dialog"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="SMSInviteActivity"
android:label="@string/sms_invite_title"
android:theme="@android:style/Theme.Dialog"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="sensor"
/>
<activity android:name="RelayInviteActivity"
android:label="@string/relay_invite_title"
android:theme="@android:style/Theme.Dialog"
android:configChanges="keyboardHidden|orientation|screenSize"
/>

View file

@ -66,24 +66,17 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes-proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/debug" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/mockable-android-14.jar" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard-rules" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/release" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/tmp" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/reports" />
<excludeFolder url="file://$MODULE_DIR$/build/test-results" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 14 Platform" jdkType="Android SDK" />

View file

@ -21,9 +21,9 @@ android {
}
// Rename all output artifacts to include version information
applicationVariants.all { variant ->
renameArtifact(variant)
}
// applicationVariants.all { variant ->
// renameArtifact(variant)
// }
// flavorDimensions "variant", "abi"
// productFlavors {
@ -185,25 +185,25 @@ tasks.whenTaskAdded { theTask ->
}
}
def getVersionName() {
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'describe', '--dirty'
standardOutput = stdout
}
return stdout.toString().trim()
}
catch (ignored) {
return null;
}
}
// def getVersionName() {
// try {
// def stdout = new ByteArrayOutputStream()
// exec {
// commandLine 'git', 'describe', '--dirty'
// standardOutput = stdout
// }
// return stdout.toString().trim()
// }
// catch (ignored) {
// return null;
// }
// }
def renameArtifact(variant) {
variant.outputs.each { output ->
def name = String.format( "XWords4-%s-%s.apk", variant.name,
getVersionName() )
output.outputFile = new File( (String)output.outputFile.parent,
(String)name )
}
}
// def renameArtifact(variant) {
// variant.outputs.each { output ->
// def name = String.format( "XWords4-%s-%s.apk", variant.name,
// getVersionName() )
// output.outputFile = new File( (String)output.outputFile.parent,
// (String)name )
// }
// }

View file

@ -13,10 +13,15 @@
</style>
</head>
<body>
<h2>Crosswords 4.4 beta 101 release</h2>
<h2>Crosswords 4.4 beta 102 release</h2>
<p>This is the second of two releases that together fix stalling
issues in network games.</p>
<p>This release introduces "Rematch", a way to create new games from
existing ones that short-circuits all the configuration and
invitation nonsense. (Nonsense, anyway, after the first time.)
Invitations are cleaned up. And it is the second of two releases
that together fix stalling issues in network games. </p>
<p>I hope you like it!</p>
<div id="survey">
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
@ -26,8 +31,40 @@
<h3>New with this release</h3>
<ul>
<li>Fix to send correctly calculated identifier for game
state.</li>
<li>Rematch option allows creating a new game with the same
opponents, and takes care of sending invitations to the
remote device</li>
<li>Improve how invitations are created and sent, including
making it easier to resend when necessary</li>
<li>Finish fixing problem with moves being dropped</li>
<li>Make it easier to "discover" and enable play-via-SMS (GSM
phones only)</li>
<li>Fix memory leak that caused crashes if you opened a lot of
games in a short period</li>
<li>Ask player for name the first time it's needed rather than
when newly installed game first opened</li>
<li>Fix occasional failure to refresh thumbnails</li>
<li>Don't allow removing players when game is locked (in Game
Config screen)</li>
<li>Put back long-tap (a.k.a. "context") menus in Games List
screen. Because I like them</li>
<li>No reminders in robot games by default</li>
<li>Tweaks to Chat screen, and confirm before deleting chat
history</li>
<li>Tweak notifications to make them more useful</li>
<li>Other minor bug fixes</li>
</ul>
<p>(The full changelog
@ -35,8 +72,6 @@
<h3>Next up</h3>
<ul>
<li>Offer &quot;Rematch&quot; when game&apos;s over (Easy via
SMS and Bluetooth; harder via the internet/relay)</li>
<li>Take advantage of Marshmallow's new permissions model (where
the app only asks for them when it needs them.)
</ul>

View file

@ -52,7 +52,6 @@
android:text="@string/button_invite"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dip"
/>
</LinearLayout>

View file

@ -95,5 +95,7 @@
/>
<item android:id="@+id/board_menu_game_netstats"
android:title="@string/board_menu_game_netstats" />
<item android:id="@+id/board_menu_game_invites"
android:title="@string/board_menu_game_showInvites" />
</menu>

View file

@ -72,5 +72,7 @@
<item android:id="@+id/board_menu_game_netstats"
android:title="@string/board_menu_game_netstats" />
<item android:id="@+id/board_menu_game_invites"
android:title="@string/board_menu_game_showInvites" />
</menu>

View file

@ -31,4 +31,9 @@
<item android:id="@+id/games_game_deselect"
android:title="@string/list_item_deselect"
/>
<!-- Debug only -->
<item android:id="@+id/games_game_invites"
android:title="@string/board_menu_game_showInvites"
/>
</menu>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_version">4.4 beta 101</string>
<string name="app_version">4.4 beta 102</string>
</resources>

View file

@ -59,6 +59,8 @@
<!-- Final state: game is over. -->
<string name="summary_relay_gameover_fmt">Game over in room \"%1$s\"</string>
<string name="summary_invites_out">Players invited</string>
<!-- Games that have ended are listed with this string -->
<string name="gameOver">Game over</string>
<!-- Otherwise they're listed with this to give some indication of
@ -545,6 +547,20 @@
players. Would you like to invite someone to join -- assuming
you haven\'t already?</item>
</plurals>
<plurals name="invite_sent_fmt">
<item quantity="one">You have already invited a remote player to
this game. We are waiting for him/her to respond. Please
use the re-invite button if you think the invitation did not go
out.</item>
<item quantity="other">You have already sent %1$d unique
invitations for this game. We are waiting for %2$d of the
recipients to respond. Please use the re-invite button if you
think the invitations did not go out.</item>
</plurals>
<string name="invited_msg">This game was created from an
invitation you received. As soon as it is able to connect to the
sender and any other invitees have arrived play will
begin.</string>
<!-- Appended to message above if local device has NFC available -->
<string name="invite_if_nfc">Or just Tap to Invite -- if the other
@ -567,6 +583,15 @@
this alert will not be dismissed until everybody has been invited
and all invitations have been accepted.)</string>
<string name="invit_expl_sms_fmt">Invite sent via SMS to phone
number %1$s on %2$s</string>
<string name="invit_expl_bt_fmt">Invite sent via Bluetooth to
paired device \"%1$s\" on %2$s</string>
<string name="invit_expl_relay_fmt">Invite forwarded by the relay
to another device on %1$s</string>
<string name="invit_expl_notarget_fmt">Invite sent via %1$s on
%2$s. Recipient unknown.</string>
<!-- Short for "points", this is shown at the right end of the
tray in place of the first tile placed along with the points
the current move would earn if committed. -->
@ -1173,6 +1198,7 @@
it immediately because an email or messaging app will be
launched to send your invitation. -->
<string name="newgame_invite">Invite now</string>
<string name="newgame_invite_more">More info</string>
<string name="newgame_drop_relay">Drop Relay</string>
<!-- section separator (white-on-gray bar) for third section:
@ -1470,6 +1496,7 @@
device, and the body that appears when you pull the notifications
down. -->
<string name="notify_title_fmt">Move in game %1$s</string>
<string name="notify_title_turn_fmt">Your turn in game %1$s</string>
<string name="notify_chat_title_fmt">Chat message in game %1$s</string>
<string name="notify_chat_body_fmt">%1$s: %2$s</string>
<!--
@ -2016,6 +2043,9 @@
<!-- -->
<string name="connstat_net_fmt">Network status for game connected via
%1$s:</string>
<!-- First line of debug-only Invites list dialog -->
<string name="invites_net_fmt">Invitations sent for game connected via
%1$s:</string>
<!-- -->
<string name="connstat_succ">successful</string>
<!-- -->
@ -2439,12 +2469,16 @@
<string name="waiting_title">Waiting for players</string>
<!-- Button for alert with title above -->
<string name="waiting_invite_title">Waiting for response</string>
<string name="waiting_rematch_title">Rematch in progress</string>
<!-- Button for alert with title above -->
<string name="button_wait">Wait</string>
<string name="button_reinvite">Re-invite</string>
<string name="invite_stays">(This dialog will stay up until all
remote players have connected. You can close the game if you
expect it to take a while. They will still be able to
connect.)</string>
expect it to take a while. Remote players will still be able to
connect, and you will be notified when they do.)</string>
<string name="nfc_just_tap">To invite via NFC just touch the back
of this device against the one you want to invite—any time the
@ -2461,10 +2495,10 @@
<string name="button_edit">Edit</string>
<string name="button_discard_changes">Discard changes</string>
<string name="rematch_msg">Issuing rematch invitation. You will
see this message until it has been accepted.\n\nYou do not need to
keep this game open while waiting. You will be notified when the
game is ready to play.</string>
<string name="rematch_sent_toast">Rematch invitations sent</string>
<string name="rematch_msg">This game is hosting a rematch, and has
sent an invitation. You will see this message until it has been
accepted.</string>
<string name="not_again_enablepublic">Public rooms have been made
an \"advanced\" feature in this release. If you were using them
@ -2496,6 +2530,7 @@
<string name="title_send_data_sms">Send SMS as data</string>
<string name="summary_send_data_sms">(GSM phones only)</string>
<string name="board_menu_game_netstats">Network stats</string>
<string name="board_menu_game_showInvites">Show invites</string>
<string name="netstats_title">Game network stats</string>
<string name="git_rev_title">Source version id</string>
@ -2586,10 +2621,10 @@
<string name="str_no_hint_found">Cannot find any moves</string>
<string name="not_again_rematch_two_only">The Rematch button is
disabled because, for now anyway, rematch is limited to two-person
games. I think it\'s rare that people play with more than two. Let
me know if I\'m wrong and I\'ll try harder to make it work.</string>
<string name="not_again_rematch_two_only">Rematch is limited to
two-person games, at least for now, because it\'s harder with more
devices and I think it\'s rare that people play with more than
two. Let me know if I\'m wrong and I\'ll up the priority.</string>
<string name="enable_relay_toself_title">Enable relay invites to self</string>
<string name="enable_relay_toself_summary">(To aid testing and debugging)</string>

View file

@ -52,6 +52,7 @@
<string name="summary_relay_conn_fmt">Emag ni yalp ni moor \"%1$s\"</string>
<!-- Final state: game is over. -->
<string name="summary_relay_gameover_fmt">Emag revo ni moor \"%1$s\"</string>
<string name="summary_invites_out">Sreyalp detivni</string>
<!-- Games that have ended are listed with this string -->
<string name="gameOver">Emag revo</string>
<!-- Otherwise they're listed with this to give some indication of
@ -464,6 +465,20 @@
sreyalp. Dluow uoy ekil ot etivni enoemos ot nioj -- gnimussa
uoy nevah\'t ?ydaerla</item>
</plurals>
<plurals name="invite_sent_fmt">
<item quantity="one">Uoy evah ydaerla detivni a etomer reyalp ot
siht emag. Ew era gnitiaw rof reh/mih ot dnopser. Esaelp
esu eht etivni-er nottub fi uoy kniht eht noitativni did ton og
tuo.</item>
<item quantity="other">Uoy evah ydaerla tnes %1$d euqinu
snoitativni rof siht emag. Ew era gnitiaw rof %2$d fo eht
stneipicer ot dnopser. Esaelp esu eht etivni-er nottub fi uoy
kniht eht snoitativni did ton og tuo.</item>
</plurals>
<string name="invited_msg">Siht emag saw detaerc morf na
noitativni uoy deviecer. Sa noos sa ti si elba ot tcennoc ot eht
rednes dna yna rehto seetivni evah devirra yalp lliw
nigeb.</string>
<!-- Appended to message above if local device has NFC available -->
<string name="invite_if_nfc">Ro tsuj Pat ot Etivni -- fi eht rehto
ecived osla sah Diordna Gnimaeb dna si ybraen.</string>
@ -482,6 +497,14 @@
etomer sreyalp. Uoy nod\'t evah ot etivni meht lla ta ,ecno tub
siht trela lliw ton eb dessimsid litnu ydobyreve sah neeb detivni
dna lla snoitativni evah neeb detpecca.)</string>
<string name="invit_expl_sms_fmt">Etivni tnes aiv SMS ot enohp
rebmun %1$s no %2$s</string>
<string name="invit_expl_bt_fmt">Etivni tnes aiv Htooteulb ot
deriap ecived \"%1$s\" no %2$s</string>
<string name="invit_expl_relay_fmt">Etivni dedrawrof yb eht yaler
ot rehtona ecived no %1$s</string>
<string name="invit_expl_notarget_fmt">Etivni tnes aiv %1$s no
%2$s. Tneipicer nwonknu.</string>
<!-- Short for "points", this is shown at the right end of the
tray in place of the first tile placed along with the points
the current move would earn if committed. -->
@ -1009,6 +1032,7 @@
it immediately because an email or messaging app will be
launched to send your invitation. -->
<string name="newgame_invite">Etivni won</string>
<string name="newgame_invite_more">Erom ofni</string>
<string name="newgame_drop_relay">Pord Yaler</string>
<!-- section separator (white-on-gray bar) for third section:
bluetooth games -->
@ -1259,6 +1283,7 @@
device, and the body that appears when you pull the notifications
down. -->
<string name="notify_title_fmt">Evom ni emag %1$s</string>
<string name="notify_title_turn_fmt">Ruoy nrut ni emag %1$s</string>
<string name="notify_chat_title_fmt">Tahc egassem ni emag %1$s</string>
<string name="notify_chat_body_fmt">%1$s: %2$s</string>
<!--
@ -1741,6 +1766,9 @@
<!-- -->
<string name="connstat_net_fmt">Krowten sutats rof emag detcennoc aiv
%1$s:</string>
<!-- First line of debug-only Invites list dialog -->
<string name="invites_net_fmt">Snoitativni tnes rof emag detcennoc aiv
%1$s:</string>
<!-- -->
<string name="connstat_succ">lufsseccus</string>
<!-- -->
@ -2091,11 +2119,15 @@
</string>
<string name="waiting_title">Gnitiaw rof sreyalp</string>
<!-- Button for alert with title above -->
<string name="waiting_invite_title">Gnitiaw rof esnopser</string>
<string name="waiting_rematch_title">Hctamer ni ssergorp</string>
<!-- Button for alert with title above -->
<string name="button_wait">Tiaw</string>
<string name="button_reinvite">Etivni-er</string>
<string name="invite_stays">sIht( golaid lliw yats pu litnu lla
etomer sreyalp evah detcennoc. Uoy nac esolc eht emag fi uoy
tcepxe ti ot ekat a elihw. Yeht lliw llits eb elba ot
tcennoc.)</string>
tcepxe ti ot ekat a elihw. Etomer sreyalp lliw llits eb elba ot
,tcennoc dna uoy lliw eb deifiton nehw yeht od.)</string>
<string name="nfc_just_tap">Ot etivni aiv CFN tsuj hcuot eht kcab
fo siht ecived tsniaga eht eno uoy tnaw ot yna—etivni emit eht
emag si nepo.</string>
@ -2108,10 +2140,10 @@
tide ot evig ti a yaw ot ?tcennoc</string>
<string name="button_edit">Tide</string>
<string name="button_discard_changes">Dracsid segnahc</string>
<string name="rematch_msg">Gniussi hctamer noitativni. Uoy lliw
ees siht egassem litnu ti sah neeb detpecca.\n\nUoy od ton deen ot
peek siht emag nepo elihw gnitiaw. Uoy lliw eb deifiton nehw eht
emag si ydaer ot yalp.</string>
<string name="rematch_sent_toast">Hctamer snoitativni tnes</string>
<string name="rematch_msg">Siht emag si gnitsoh a ,hctamer dna sah
tnes na noitativni. Uoy lliw ees siht egassem litnu ti sah neeb
detpecca.</string>
<string name="not_again_enablepublic">Cilbup smoor evah neeb edam
na \"decnavda\" erutaef ni siht esaeler. Fi uoy erew gnisu meht
dna tnaw meht ,kcab elbane meht won. Uoy nac nrut meht ffo niaga
@ -2139,6 +2171,7 @@
<string name="title_send_data_sms">Dnes SMS sa atad</string>
<string name="summary_send_data_sms">mSG( senohp )ylno</string>
<string name="board_menu_game_netstats">Krowten stats</string>
<string name="board_menu_game_showInvites">Wohs setivni</string>
<string name="netstats_title">Emag krowten stats</string>
<string name="git_rev_title">Ecruos noisrev di</string>
<string name="devid_title">Ecived DI no( )yaler</string>
@ -2210,10 +2243,10 @@
<string name="not_again_comms_bt">Esu Htooteulb ot yalp tsniaga a
ybraen ecived taht\'s \"deriap\" htiw sruoy.</string>
<string name="str_no_hint_found">Tonnac dnif yna sevom</string>
<string name="not_again_rematch_two_only">Eht Hctamer nottub si
delbasid ,esuaceb rof won ,yawyna hctamer si detimil ot nosrep-owt
semag. I kniht ti\'s erar taht elpoep yalp htiw erom naht owt. Tel
em wonk fi I\'m gnorw dna I\'ll yrt redrah ot ekam ti krow.</string>
<string name="not_again_rematch_two_only">Hctamer si detimil ot
nosrep-owt ,semag ta tsael rof ,won esuaceb ti\'s redrah htiw erom
secived dna I kniht ti\'s erar taht elpoep yalp htiw erom naht
owt. Tel em wonk fi I\'m gnorw dna I\'ll pu eht ytiroirp.</string>
<string name="enable_relay_toself_title">Elbane yaler setivni ot fles</string>
<string name="enable_relay_toself_summary">oT( dia gnitset dna )gniggubed</string>
<!-- Shown after "resend messages" menuitem chosen -->

View file

@ -52,6 +52,7 @@
<string name="summary_relay_conn_fmt">GAME IN PLAY IN ROOM \"%1$s\"</string>
<!-- Final state: game is over. -->
<string name="summary_relay_gameover_fmt">GAME OVER IN ROOM \"%1$s\"</string>
<string name="summary_invites_out">PLAYERS INVITED</string>
<!-- Games that have ended are listed with this string -->
<string name="gameOver">GAME OVER</string>
<!-- Otherwise they're listed with this to give some indication of
@ -464,6 +465,20 @@
PLAYERS. WOULD YOU LIKE TO INVITE SOMEONE TO JOIN -- ASSUMING
YOU HAVEN\'T ALREADY?</item>
</plurals>
<plurals name="invite_sent_fmt">
<item quantity="one">YOU HAVE ALREADY INVITED A REMOTE PLAYER TO
THIS GAME. WE ARE WAITING FOR HIM/HER TO RESPOND. PLEASE
USE THE RE-INVITE BUTTON IF YOU THINK THE INVITATION DID NOT GO
OUT.</item>
<item quantity="other">YOU HAVE ALREADY SENT %1$d UNIQUE
INVITATIONS FOR THIS GAME. WE ARE WAITING FOR %2$d OF THE
RECIPIENTS TO RESPOND. PLEASE USE THE RE-INVITE BUTTON IF YOU
THINK THE INVITATIONS DID NOT GO OUT.</item>
</plurals>
<string name="invited_msg">THIS GAME WAS CREATED FROM AN
INVITATION YOU RECEIVED. AS SOON AS IT IS ABLE TO CONNECT TO THE
SENDER AND ANY OTHER INVITEES HAVE ARRIVED PLAY WILL
BEGIN.</string>
<!-- Appended to message above if local device has NFC available -->
<string name="invite_if_nfc">OR JUST TAP TO INVITE -- IF THE OTHER
DEVICE ALSO HAS ANDROID BEAMING AND IS NEARBY.</string>
@ -482,6 +497,14 @@
REMOTE PLAYERS. YOU DON\'T HAVE TO INVITE THEM ALL AT ONCE, BUT
THIS ALERT WILL NOT BE DISMISSED UNTIL EVERYBODY HAS BEEN INVITED
AND ALL INVITATIONS HAVE BEEN ACCEPTED.)</string>
<string name="invit_expl_sms_fmt">INVITE SENT VIA SMS TO PHONE
NUMBER %1$s ON %2$s</string>
<string name="invit_expl_bt_fmt">INVITE SENT VIA BLUETOOTH TO
PAIRED DEVICE \"%1$s\" ON %2$s</string>
<string name="invit_expl_relay_fmt">INVITE FORWARDED BY THE RELAY
TO ANOTHER DEVICE ON %1$s</string>
<string name="invit_expl_notarget_fmt">INVITE SENT VIA %1$s ON
%2$s. RECIPIENT UNKNOWN.</string>
<!-- Short for "points", this is shown at the right end of the
tray in place of the first tile placed along with the points
the current move would earn if committed. -->
@ -1009,6 +1032,7 @@
it immediately because an email or messaging app will be
launched to send your invitation. -->
<string name="newgame_invite">INVITE NOW</string>
<string name="newgame_invite_more">MORE INFO</string>
<string name="newgame_drop_relay">DROP RELAY</string>
<!-- section separator (white-on-gray bar) for third section:
bluetooth games -->
@ -1259,6 +1283,7 @@
device, and the body that appears when you pull the notifications
down. -->
<string name="notify_title_fmt">MOVE IN GAME %1$s</string>
<string name="notify_title_turn_fmt">YOUR TURN IN GAME %1$s</string>
<string name="notify_chat_title_fmt">CHAT MESSAGE IN GAME %1$s</string>
<string name="notify_chat_body_fmt">%1$s: %2$s</string>
<!--
@ -1741,6 +1766,9 @@
<!-- -->
<string name="connstat_net_fmt">NETWORK STATUS FOR GAME CONNECTED VIA
%1$s:</string>
<!-- First line of debug-only Invites list dialog -->
<string name="invites_net_fmt">INVITATIONS SENT FOR GAME CONNECTED VIA
%1$s:</string>
<!-- -->
<string name="connstat_succ">SUCCESSFUL</string>
<!-- -->
@ -2091,11 +2119,15 @@
</string>
<string name="waiting_title">WAITING FOR PLAYERS</string>
<!-- Button for alert with title above -->
<string name="waiting_invite_title">WAITING FOR RESPONSE</string>
<string name="waiting_rematch_title">REMATCH IN PROGRESS</string>
<!-- Button for alert with title above -->
<string name="button_wait">WAIT</string>
<string name="button_reinvite">RE-INVITE</string>
<string name="invite_stays">(THIS DIALOG WILL STAY UP UNTIL ALL
REMOTE PLAYERS HAVE CONNECTED. YOU CAN CLOSE THE GAME IF YOU
EXPECT IT TO TAKE A WHILE. THEY WILL STILL BE ABLE TO
CONNECT.)</string>
EXPECT IT TO TAKE A WHILE. REMOTE PLAYERS WILL STILL BE ABLE TO
CONNECT, AND YOU WILL BE NOTIFIED WHEN THEY DO.)</string>
<string name="nfc_just_tap">TO INVITE VIA NFC JUST TOUCH THE BACK
OF THIS DEVICE AGAINST THE ONE YOU WANT TO INVITE—ANY TIME THE
GAME IS OPEN.</string>
@ -2108,10 +2140,10 @@
EDIT TO GIVE IT A WAY TO CONNECT?</string>
<string name="button_edit">EDIT</string>
<string name="button_discard_changes">DISCARD CHANGES</string>
<string name="rematch_msg">ISSUING REMATCH INVITATION. YOU WILL
SEE THIS MESSAGE UNTIL IT HAS BEEN ACCEPTED.\n\nYOU DO NOT NEED TO
KEEP THIS GAME OPEN WHILE WAITING. YOU WILL BE NOTIFIED WHEN THE
GAME IS READY TO PLAY.</string>
<string name="rematch_sent_toast">REMATCH INVITATIONS SENT</string>
<string name="rematch_msg">THIS GAME IS HOSTING A REMATCH, AND HAS
SENT AN INVITATION. YOU WILL SEE THIS MESSAGE UNTIL IT HAS BEEN
ACCEPTED.</string>
<string name="not_again_enablepublic">PUBLIC ROOMS HAVE BEEN MADE
AN \"ADVANCED\" FEATURE IN THIS RELEASE. IF YOU WERE USING THEM
AND WANT THEM BACK, ENABLE THEM NOW. YOU CAN TURN THEM OFF AGAIN
@ -2139,6 +2171,7 @@
<string name="title_send_data_sms">SEND SMS AS DATA</string>
<string name="summary_send_data_sms">(GSM PHONES ONLY)</string>
<string name="board_menu_game_netstats">NETWORK STATS</string>
<string name="board_menu_game_showInvites">SHOW INVITES</string>
<string name="netstats_title">GAME NETWORK STATS</string>
<string name="git_rev_title">SOURCE VERSION ID</string>
<string name="devid_title">DEVICE ID (ON RELAY)</string>
@ -2210,10 +2243,10 @@
<string name="not_again_comms_bt">USE BLUETOOTH TO PLAY AGAINST A
NEARBY DEVICE THAT\'S \"PAIRED\" WITH YOURS.</string>
<string name="str_no_hint_found">CANNOT FIND ANY MOVES</string>
<string name="not_again_rematch_two_only">THE REMATCH BUTTON IS
DISABLED BECAUSE, FOR NOW ANYWAY, REMATCH IS LIMITED TO TWO-PERSON
GAMES. I THINK IT\'S RARE THAT PEOPLE PLAY WITH MORE THAN TWO. LET
ME KNOW IF I\'M WRONG AND I\'LL TRY HARDER TO MAKE IT WORK.</string>
<string name="not_again_rematch_two_only">REMATCH IS LIMITED TO
TWO-PERSON GAMES, AT LEAST FOR NOW, BECAUSE IT\'S HARDER WITH MORE
DEVICES AND I THINK IT\'S RARE THAT PEOPLE PLAY WITH MORE THAN
TWO. LET ME KNOW IF I\'M WRONG AND I\'LL UP THE PRIORITY.</string>
<string name="enable_relay_toself_title">ENABLE RELAY INVITES TO SELF</string>
<string name="enable_relay_toself_summary">(TO AID TESTING AND DEBUGGING)</string>
<!-- Shown after "resend messages" menuitem chosen -->

View file

@ -3503,4 +3503,4 @@ pour la langue</string>
<string name="not_again_dfltname_fmt">Vous utilisez le nom par défaut de joueur \"%1$s\". Souhaitez-vous le personnaliser avec votre propre nom avant de créer cette partie ?</string>
</resources>
</resources>

View file

@ -44,21 +44,28 @@ import java.util.Iterator;
import junit.framework.Assert;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
public class BTInviteDelegate extends InviteDelegate {
private Activity m_activity;
private Set<String> m_checked;
private Set<LinearLayout> m_checked;
private Map<String, Integer> m_counts;
private boolean m_setChecked;
private BTDevsAdapter m_adapter;
public static void launchForResult( Activity activity, int nMissing,
public static void launchForResult( Activity activity, int nMissing,
SentInvitesInfo info,
RequestCode requestCode )
{
Assert.assertTrue( 0 < nMissing ); // don't call if nMissing == 0
Intent intent = new Intent( activity, BTInviteActivity.class );
intent.putExtra( INTENT_KEY_NMISSING, nMissing );
if ( null != info ) {
String lastDev = info.getLastDev( InviteMeans.BLUETOOTH );
intent.putExtra( INTENT_KEY_LASTDEV, lastDev );
}
activity.startActivityForResult( intent, requestCode.ordinal() );
}
@ -71,7 +78,7 @@ public class BTInviteDelegate extends InviteDelegate {
@Override
protected void init( Bundle savedInstanceState )
{
m_checked = new HashSet<String>();
m_checked = new HashSet<LinearLayout>();
m_counts = new HashMap<String, Integer>();
String msg = getString( R.string.bt_pick_addall_button );
@ -149,9 +156,11 @@ public class BTInviteDelegate extends InviteDelegate {
}
int nxt = 0;
for ( Iterator<String> iter = m_checked.iterator();
for ( Iterator<LinearLayout> iter = m_checked.iterator();
iter.hasNext(); ) {
String btAddr = iter.next();
LinearLayout layout = iter.next();
CheckBox box = (CheckBox)layout.findViewById( R.id.inviter_check );
String btAddr = (String)box.getTag();
devs[nxt] = btAddr;
if ( null != counts ) {
counts[nxt] = m_counts.get( btAddr );
@ -178,6 +187,7 @@ public class BTInviteDelegate extends InviteDelegate {
private class BTDevsAdapter extends XWListAdapter {
private String[] m_devAddrs;
private String[] m_devNames;
public BTDevsAdapter( String[] btAddrs, String[] btNames )
{
super( null == btAddrs? 0 : btAddrs.length );
@ -189,7 +199,7 @@ public class BTInviteDelegate extends InviteDelegate {
public View getView( int position, View convertView, ViewGroup parent ) {
final String btAddr = m_devAddrs[position];
LinearLayout layout = (LinearLayout)inflate( R.layout.btinviter_item );
final LinearLayout layout = (LinearLayout)inflate( R.layout.btinviter_item );
CheckBox box = (CheckBox)layout.findViewById( R.id.inviter_check );
box.setText( m_devNames[position] );
box.setTag( btAddr );
@ -223,12 +233,19 @@ public class BTInviteDelegate extends InviteDelegate {
CompoundButton.OnCheckedChangeListener listener =
new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged( CompoundButton buttonView,
boolean isChecked )
{
boolean isChecked ) {
if ( isChecked ) {
m_checked.add( btAddr );
if ( 1 == m_nMissing && 1 == m_checked.size() ) {
LinearLayout checked = m_checked.iterator().next();
CheckBox box = (CheckBox)checked
.findViewById( R.id.inviter_check );
box.setChecked( false );
m_checked.clear();
}
m_checked.add( layout );
} else {
m_checked.remove( btAddr );
m_checked.remove( layout );
// User's now making changes; don't check new views
m_setChecked = false;
}
@ -237,7 +254,10 @@ public class BTInviteDelegate extends InviteDelegate {
};
box.setOnCheckedChangeListener( listener );
if ( m_setChecked || m_checked.contains( btAddr ) ) {
if ( m_setChecked || m_checked.contains( layout ) ) {
box.setChecked( true );
} else if ( null != m_lastDev && m_lastDev.equals( btAddr ) ) {
m_lastDev = null;
box.setChecked( true );
}
return layout;

View file

@ -129,13 +129,7 @@ public class BTService extends XWService {
NetLaunchInfo m_nli;
public BTQueueElem( BTCmd cmd ) { m_cmd = cmd; m_failCount = 0; }
// public BTQueueElem( BTCmd cmd, String btAddr,
// int gameID, String gameName, int lang,
// String dict, int nPlayersT, int nPlayersH ) {
// this( cmd, null, btAddr, gameID );
// m_lang = lang; m_dict = dict; m_nPlayersT = nPlayersT;
// m_nPlayersH = nPlayersH; m_gameName = gameName;
// }
public BTQueueElem( BTCmd cmd, byte[] buf, String btAddr, int gameID ) {
this( cmd );
Assert.assertTrue( null != btAddr && 0 < btAddr.length() );
@ -271,23 +265,6 @@ public class BTService extends XWService {
context.startService( intent );
}
// public static void inviteRemote( Context context, String btAddr,
// int gameID, String initialName, int lang,
// String dict, int nPlayersT, int nPlayersH )
// {
// Intent intent = getIntentTo( context, INVITE );
// intent.putExtra( GAMEID_KEY, gameID );
// intent.putExtra( ADDR_KEY, btAddr );
// Assert.assertNotNull( initialName );
// intent.putExtra( GAMENAME_KEY, initialName );
// intent.putExtra( LANG_KEY, lang );
// intent.putExtra( DICT_KEY, dict );
// intent.putExtra( NTO_KEY, nPlayersT );
// intent.putExtra( NHE_KEY, nPlayersH );
// context.startService( intent );
// }
public static void inviteRemote( Context context, String btAddr,
NetLaunchInfo nli )
{
@ -612,6 +589,7 @@ public class BTService extends XWService {
CommsAddrRec addr = new CommsAddrRec( host.getName(),
host.getAddress() );
boolean[] isLocalP = new boolean[1];
for ( long rowid : rowids ) {
boolean consumed =
BoardDelegate.feedMessage( rowid, buffer, addr );
@ -620,10 +598,12 @@ public class BTService extends XWService {
new GameUtils.BackMoveResult();
if ( GameUtils.feedMessage( BTService.this, rowid,
buffer, addr,
m_btMsgSink, bmr ) ) {
m_btMsgSink, bmr,
isLocalP ) ) {
consumed = true;
GameUtils.postMoveNotification( BTService.this,
rowid, bmr );
rowid, bmr,
isLocalP[0] );
}
}
if ( !consumed ) {
@ -1144,7 +1124,8 @@ public class BTService extends XWService {
if ( null == rowids || 0 == rowids.length ) {
CommsAddrRec addr = nli.makeAddrRec( BTService.this );
long rowid = GameUtils.makeNewMultiGame( BTService.this, nli,
m_btMsgSink, null );
m_btMsgSink,
getUtilCtxt() );
if ( DBUtils.ROWID_NOTFOUND == rowid ) {
result = BTCmd.INVITE_FAILED;
} else {
@ -1249,13 +1230,17 @@ public class BTService extends XWService {
@Override
public int sendViaBluetooth( byte[] buf, int gameID, CommsAddrRec addr )
{
int nSent = -1;
String btAddr = getSafeAddr( addr );
Assert.assertTrue( addr.contains( CommsConnType.COMMS_CONN_BT ) );
m_sender.add( new BTQueueElem( BTCmd.MESG_SEND, buf,
btAddr, gameID ) );
return buf.length;
if ( null != btAddr && 0 < btAddr.length() ) {
m_sender.add( new BTQueueElem( BTCmd.MESG_SEND, buf, btAddr,
gameID ) );
nSent = buf.length;
} else {
DbgUtils.logf( "sendViaBluetooth(): no addr for dev %s",
addr.bt_hostName );
}
return nSent;
}
}
}

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009 - 2014 by Eric House (xwords@eehouse.org). All
* Copyright 2009 - 2016 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
@ -53,6 +53,7 @@ import java.util.concurrent.Semaphore;
import junit.framework.Assert;
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
import org.eehouse.android.xw4.jni.*;
@ -86,7 +87,7 @@ public class BoardDelegate extends DelegateBase
private GameLock m_gameLock;
private CurGameInfo m_gi;
private GameSummary m_summary;
private boolean m_relayConnected;
private boolean m_relayMissing;
private CommsTransport m_xport;
private Handler m_handler = null;
private TimerRunnable[] m_timers;
@ -96,6 +97,7 @@ public class BoardDelegate extends DelegateBase
private View m_tradeButtons;
private Button m_exchCommmitButton;
private Button m_exchCancelButton;
private SentInvitesInfo m_sentInfo;
private ArrayList<String> m_pendingChats;
@ -138,7 +140,6 @@ public class BoardDelegate extends DelegateBase
private int m_nGuestDevs = -1;
private boolean m_haveInvited = false;
private boolean m_overNotShown;
private boolean m_rematchInvitesSent = false;
private static Set<BoardDelegate> s_this = new HashSet<BoardDelegate>();
@ -430,24 +431,38 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
if ( m_relayConnected ||
if ( !m_relayMissing ||
! m_connTypes.contains(CommsConnType.COMMS_CONN_RELAY) ) {
showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION );
Assert.assertTrue( 0 < m_nMissing );
if ( m_summary.hasRematchInfo() ) {
tryRematchInvites( true );
} else {
showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION,
m_sentInfo );
}
} else {
askDropRelay();
}
}
};
OnClickListener lstnr2 = new OnClickListener() {
OnClickListener lstnrWait = new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
finish();
}
};
OnClickListener lstnrMore = new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
showOKOnlyDialog( m_sentInfo.getAsText( m_activity ) );
}
};
dialog = ab.setTitle( "foo" )
.setMessage( "" )
.setPositiveButton( "", lstnr )
.setNegativeButton( R.string.button_wait, lstnr2 )
.setNegativeButton( R.string.button_wait, lstnrWait )
.setNeutralButton( R.string.newgame_invite_more, lstnrMore )
.setOnCancelListener( new OnCancelListener() {
public void onCancel( DialogInterface dialog ) {
finish();
@ -476,37 +491,55 @@ public class BoardDelegate extends DelegateBase
AlertDialog ad = (AlertDialog)dialog;
String message;
int titleID;
boolean nukeButton = false;
boolean nukeInviteButton = false;
boolean nukeNeutButton = true;
int buttonTxt = R.string.newgame_invite;
if ( m_summary.hasRematchInfo() ) {
titleID = R.string.info_title;;
message = getString( R.string.rematch_msg );
nukeButton = true;
} else {
if ( !m_relayConnected ) {
titleID = R.string.seeking_relay;
// If relay is only means, don't allow at all
boolean relayOnly = 1 >= m_connTypes.size();
nukeButton = relayOnly;
message = getString( R.string.no_relay_conn );
if ( NetStateCache.netAvail( m_activity )
&& NetStateCache.onWifi() ) {
message += getString( R.string.wifi_warning );
}
if ( !relayOnly ) {
CommsConnTypeSet without = (CommsConnTypeSet)
m_connTypes.clone();
without.remove( CommsConnType.COMMS_CONN_RELAY );
message += "\n\n"
+ getString( R.string.drop_relay_warning_fmt,
without.toString( m_activity ) );
buttonTxt = R.string.newgame_drop_relay;
if ( m_relayMissing ) {
titleID = R.string.seeking_relay;
// If relay is only means, don't allow at all
boolean relayOnly = 1 >= m_connTypes.size();
nukeInviteButton = relayOnly;
message = getString( R.string.no_relay_conn );
if ( NetStateCache.netAvail( m_activity )
&& NetStateCache.onWifi() ) {
message += getString( R.string.wifi_warning );
}
if ( !relayOnly ) {
CommsConnTypeSet without = (CommsConnTypeSet)
m_connTypes.clone();
without.remove( CommsConnType.COMMS_CONN_RELAY );
message += "\n\n"
+ getString( R.string.drop_relay_warning_fmt,
without.toString( m_activity ) );
buttonTxt = R.string.newgame_drop_relay;
}
} else {
m_sentInfo = DBUtils.getInvitesFor( m_activity, m_rowid );
int nSent = m_sentInfo.getMinPlayerCount();
boolean invitesSent = nSent >= m_nMissing;
if ( invitesSent ) {
if ( m_summary.hasRematchInfo() ) {
titleID = R.string.waiting_rematch_title;
message = getString( R.string.rematch_msg );
} else {
titleID = R.string.waiting_invite_title;
message = getQuantityString( R.plurals.invite_sent_fmt,
nSent, nSent, m_nMissing );
}
buttonTxt = R.string.button_reinvite;
nukeNeutButton = false;
} else if ( DeviceRole.SERVER_ISCLIENT == m_gi.serverRole ) {
Assert.assertFalse( m_summary.hasRematchInfo() );
message = getString( R.string.invited_msg );
titleID = R.string.waiting_title;
nukeInviteButton = true;
} else {
titleID = R.string.waiting_title;
message = getQuantityString( R.plurals.invite_msg_fmt,
m_nMissing, m_nMissing );
}
if ( ! invitesSent && ! nukeInviteButton ) {
String ps = null;
if ( m_nMissing > 1 ) {
ps = getString( R.string.invite_multiple );
@ -519,19 +552,22 @@ public class BoardDelegate extends DelegateBase
if ( null != ps ) {
message += "\n\n" + ps;
}
message += "\n\n" + getString( R.string.invite_stays );
}
message += "\n\n" + getString( R.string.invite_stays );
}
ad.setMessage( message );
ad.setTitle( titleID );
Button posButton = ad.getButton( AlertDialog.BUTTON_POSITIVE );
posButton.setVisibility( nukeButton ? View.GONE : View.VISIBLE );
if ( !nukeButton ) {
posButton.setText( buttonTxt );
Button button = ad.getButton( AlertDialog.BUTTON_POSITIVE );
button.setVisibility( nukeInviteButton ? View.GONE : View.VISIBLE );
if ( !nukeInviteButton ) {
button.setText( buttonTxt );
}
button = ad.getButton( AlertDialog.BUTTON_NEUTRAL );
button.setVisibility( nukeNeutButton ? View.GONE : View.VISIBLE );
break;
default:
super.prepareDialog( dlgID, dialog );
@ -687,7 +723,7 @@ public class BoardDelegate extends DelegateBase
setBackgroundColor();
setKeepScreenOn();
} else if ( ! isFinishing() ) {
if ( !m_relayConnected || 0 < m_nMissing ) {
if ( m_relayMissing || 0 < m_nMissing ) {
showDialog( DlgID.DLG_INVITE );
}
}
@ -802,15 +838,16 @@ public class BoardDelegate extends DelegateBase
enable = m_gameOver && rematchSupported( false );
Utils.setItemVisible( menu, R.id.board_menu_rematch, enable );
enable = null != m_gi
boolean netGame = null != m_gi
&& DeviceRole.SERVER_STANDALONE != m_gi.serverRole;
Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, enable );
Utils.setItemVisible( menu, R.id.board_menu_game_resend,
enable && null != m_gsi &&
0 < m_gsi.nPendingMessages );
Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, netGame );
enable = netGame && null != m_gsi && 0 < m_gsi.nPendingMessages;
Utils.setItemVisible( menu, R.id.board_menu_game_resend, enable );
enable = enable && BuildConfig.DEBUG;
enable = netGame && (BuildConfig.DEBUG
|| XWPrefs.getDebugEnabled( m_activity ) );
Utils.setItemVisible( menu, R.id.board_menu_game_netstats, enable );
Utils.setItemVisible( menu, R.id.board_menu_game_invites, enable );
enable = XWPrefs.getStudyEnabled( m_activity );
Utils.setItemVisible( menu, R.id.games_menu_study, enable );
@ -890,6 +927,10 @@ public class BoardDelegate extends DelegateBase
case R.id.board_menu_game_netstats:
m_jniThread.handle( JNICmd.CMD_NETSTATS, R.string.netstats_title );
break;
case R.id.board_menu_game_invites:
SentInvitesInfo sentInfo = DBUtils.getInvitesFor( m_activity, m_rowid );
showOKOnlyDialog( sentInfo.getAsText( m_activity ) );
break;
case R.id.board_menu_undo_current:
cmd = JNICmd.CMD_UNDO_CUR;
break;
@ -950,7 +991,8 @@ public class BoardDelegate extends DelegateBase
// DlgDelegate.DlgClickNotify interface
//////////////////////////////////////////////////
@Override
public void dlgButtonClicked( Action action, int which, Object[] params )
public void dlgButtonClicked( Action action, int which,
final Object[] params )
{
boolean handled = false;
if ( AlertDialog.BUTTON_POSITIVE == which ) {
@ -1024,6 +1066,17 @@ public class BoardDelegate extends DelegateBase
case DELETE_AND_EXIT:
deleteAndClose();
break;
case ENABLE_SMS_DO:
handled = false; // so super gets called, before
// retrySMSInvites
post( new Runnable() {
public void run() {
retrySMSInvites( params );
}
} );
break;
default:
handled = false;
}
@ -1042,6 +1095,8 @@ public class BoardDelegate extends DelegateBase
Object[] params )
{
if ( action == Action.LAUNCH_INVITE_ACTION ) {
SentInvitesInfo info = params[0] instanceof SentInvitesInfo
? (SentInvitesInfo)params[0] : null;
switch( means ) {
case NFC:
if ( XWPrefs.getNFCToSelfEnabled( m_activity ) ) {
@ -1053,11 +1108,11 @@ public class BoardDelegate extends DelegateBase
}
break;
case BLUETOOTH:
BTInviteDelegate.launchForResult( m_activity, m_nMissing,
BTInviteDelegate.launchForResult( m_activity, m_nMissing, info,
RequestCode.BT_INVITE_RESULT );
break;
case SMS:
SMSInviteDelegate.launchForResult( m_activity, m_nMissing,
SMSInviteDelegate.launchForResult( m_activity, m_nMissing, info,
RequestCode.SMS_INVITE_RESULT );
break;
case RELAY:
@ -1068,7 +1123,7 @@ public class BoardDelegate extends DelegateBase
case CLIPBOARD:
NetLaunchInfo nli = new NetLaunchInfo( m_summary, m_gi, 1,
1 + m_nGuestDevs );
if ( !m_relayConnected ) {
if ( m_relayMissing ) {
nli.removeAddress( CommsConnType.COMMS_CONN_RELAY );
}
if ( InviteMeans.EMAIL == means ) {
@ -1076,6 +1131,8 @@ public class BoardDelegate extends DelegateBase
} else if ( InviteMeans.CLIPBOARD == means ) {
GameUtils.inviteURLToClip( m_activity, nli );
}
recordInviteSent( means, null );
break;
default:
Assert.fail();
@ -1291,6 +1348,8 @@ public class BoardDelegate extends DelegateBase
}
if ( null != data ) {
removeDialog( DlgID.CONFIRM_THEN );
recordInviteSent( InviteMeans.NFC, null );
}
return data;
}
@ -1475,7 +1534,7 @@ public class BoardDelegate extends DelegateBase
}
} else if ( nMissing > 0 ) {
if ( m_summary.hasRematchInfo() ) {
tryRematchInvites();
tryRematchInvites( false );
} else {
if ( !m_haveInvited ) {
m_haveInvited = true;
@ -1818,7 +1877,7 @@ public class BoardDelegate extends DelegateBase
m_nGuestDevs = nDevs;
// If we might have put up an alert earlier, take it down
dismissInviteAlert( nMissing, m_relayConnected );
dismissInviteAlert( nMissing, !m_relayMissing );
m_nMissing = nMissing; // will be 0 unless isServer is true
@ -1977,7 +2036,7 @@ public class BoardDelegate extends DelegateBase
String langName = m_gi.langName();
m_summary = DBUtils.getSummary( m_activity, m_gameLock );
m_relayConnected = !m_summary.relayConnectPending();
m_relayMissing = m_summary.relayConnectPending();
setThis( this );
@ -2139,10 +2198,10 @@ public class BoardDelegate extends DelegateBase
{
runOnUiThread( new Runnable() {
public void run() {
if ( !m_relayConnected && connected ) {
m_relayConnected = true;
if ( m_relayMissing && connected ) {
m_relayMissing = false;
}
if ( 0 == nMissing || m_relayConnected ) {
if ( 0 == nMissing || !m_relayMissing ) {
dismissDialog( DlgID.DLG_INVITE );
}
}
@ -2370,7 +2429,7 @@ public class BoardDelegate extends DelegateBase
private void tryInvites()
{
if ( 0 < m_nMissing && m_summary.hasRematchInfo() ) {
tryRematchInvites();
tryRematchInvites( false );
} else if ( null != m_missingDevs ) {
Assert.assertNotNull( m_missingMeans );
String gameName = GameUtils.getName( m_activity, m_rowid );
@ -2382,7 +2441,7 @@ public class BoardDelegate extends DelegateBase
int forceChannel = ii + m_nGuestDevs + 1;
NetLaunchInfo nli = new NetLaunchInfo( m_summary, m_gi,
nPlayers, forceChannel );
if ( !m_relayConnected ) {
if ( m_relayMissing ) {
nli.removeAddress( CommsConnType.COMMS_CONN_RELAY );
}
@ -2403,7 +2462,8 @@ public class BoardDelegate extends DelegateBase
BTService.inviteRemote( m_activity, dev, nli );
break;
case SMS:
SMSService.inviteRemote( m_activity, dev, nli );
sendSMSInviteIf( dev, nli, true );
dev = null; // don't record send a second time
break;
case RELAY:
try {
@ -2415,6 +2475,10 @@ public class BoardDelegate extends DelegateBase
}
break;
}
if ( null != dev ) {
recordInviteSent( m_missingMeans, dev );
}
}
m_missingDevs = null;
m_missingCounts = null;
@ -2643,35 +2707,75 @@ public class BoardDelegate extends DelegateBase
}
}
private void tryRematchInvites()
private void tryRematchInvites( boolean force )
{
if ( XWApp.REMATCH_SUPPORTED && !m_rematchInvitesSent ) {
m_rematchInvitesSent = true;
Assert.assertNotNull( m_summary );
Assert.assertNotNull( m_gi );
// only supports a single invite for now!
int numHere = 1;
int forceChannel = 1;
NetLaunchInfo nli = new NetLaunchInfo( m_summary, m_gi, numHere,
forceChannel );
String value;
value = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_PHONE );
if ( null != value ) {
SMSService.inviteRemote( m_activity, value, nli );
if ( XWApp.REMATCH_SUPPORTED ) {
if ( !force ) {
SentInvitesInfo info = DBUtils.getInvitesFor( m_activity, m_rowid );
force = 0 == info.getMinPlayerCount();
}
value = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_BTADDR );
if ( null != value ) {
BTService.inviteRemote( m_activity, value, nli );
}
value = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_RELAY );
if ( null != value ) {
RelayService.inviteRemote( m_activity, 0, value, nli );
if ( force ) {
Assert.assertNotNull( m_summary );
Assert.assertNotNull( m_gi );
// only supports a single invite for now!
int numHere = 1;
int forceChannel = 1;
NetLaunchInfo nli = new NetLaunchInfo( m_summary, m_gi, numHere,
forceChannel );
String value;
value = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_PHONE );
if ( null != value ) {
sendSMSInviteIf( value, nli, true );
}
value = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_BTADDR );
if ( null != value ) {
BTService.inviteRemote( m_activity, value, nli );
recordInviteSent( InviteMeans.BLUETOOTH, value );
}
value = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_RELAY );
if ( null != value ) {
RelayService.inviteRemote( m_activity, 0, value, nli );
recordInviteSent( InviteMeans.RELAY, value );
}
showToast( R.string.rematch_sent_toast );
}
}
}
private void sendSMSInviteIf( String phone, NetLaunchInfo nli,
boolean askOk )
{
if ( XWPrefs.getSMSEnabled( m_activity ) ) {
SMSService.inviteRemote( m_activity, phone, nli );
recordInviteSent( InviteMeans.SMS, phone );
} else if ( askOk ) {
showConfirmThen( R.string.warn_sms_disabled,
R.string.button_enable_sms,
R.string.button_later,
Action.ENABLE_SMS_ASK, nli, phone );
}
}
private void retrySMSInvites( Object[] params )
{
if ( null != params && 2 == params.length
&& params[0] instanceof NetLaunchInfo
&& params[1] instanceof String ) {
sendSMSInviteIf( (String)params[1], (NetLaunchInfo)params[0],
false );
} else {
DbgUtils.logf( "retrySMSInvites: tests failed" );
}
}
private void recordInviteSent( InviteMeans means, String dev )
{
DBUtils.recordInviteSent( m_activity, m_rowid, means, dev );
}
private static void noteSkip()
{
String msg = "BoardActivity.feedMessage[s](): skipped because "

View file

@ -41,8 +41,9 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String TABLE_NAME_STUDYLIST = "study";
public static final String TABLE_NAME_LOC = "loc";
public static final String TABLE_NAME_PAIRS = "pairs";
public static final String TABLE_NAME_INVITES = "invites";
private static final String DB_NAME = "xwdb";
private static final int DB_VERSION = 24;
private static final int DB_VERSION = 25;
public static final String GAME_NAME = "GAME_NAME";
public static final String VISID = "VISID";
@ -69,7 +70,8 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String CONTYPE = "CONTYPE";
public static final String SERVERROLE = "SERVERROLE";
public static final String ROOMNAME = "ROOMNAME";
public static final String INVITEID = "INVITEID";
// written but never read; can go away
// public static final String INVITEID = "INVITEID";
public static final String RELAYID = "RELAYID";
public static final String SEED = "SEED";
public static final String SMSPHONE = "SMSPHONE"; // unused -- so far
@ -104,6 +106,10 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String BLESSED = "BLESSED";
public static final String XLATION = "XLATION";
public static final String ROW = "ROW";
public static final String MEANS = "MEANS";
public static final String TARGET = "TARGET";
public static final String TIMESTAMP = "TIMESTAMP";
private Context m_context;
@ -121,7 +127,6 @@ public class DBHelper extends SQLiteOpenHelper {
,{ SERVERROLE, "INTEGER" }
,{ CONTYPE, "INTEGER" }
,{ ROOMNAME, "TEXT" }
,{ INVITEID, "TEXT" }
,{ RELAYID, "TEXT" }
,{ SEED, "INTEGER" }
,{ DICTLANG, "INTEGER" }
@ -194,6 +199,13 @@ public class DBHelper extends SQLiteOpenHelper {
,{ "UNIQUE", "(" + KEY + ")" }
};
private static final String[][] s_invitesSchema = {
{ ROW, "INTEGER" }
,{ TARGET, "TEXT" }
,{ MEANS, "INTEGER" }
,{ TIMESTAMP, "DATETIME DEFAULT CURRENT_TIMESTAMP" }
};
public DBHelper( Context context )
{
super( context, DB_NAME, null, DB_VERSION );
@ -217,6 +229,7 @@ public class DBHelper extends SQLiteOpenHelper {
createStudyTable( db );
createLocTable( db );
createPairsTable( db );
createInvitesTable( db );
}
@Override
@ -241,7 +254,6 @@ public class DBHelper extends SQLiteOpenHelper {
case 9:
addSumColumn( db, DICTLIST );
case 10:
addSumColumn( db, INVITEID );
case 11:
addSumColumn( db, REMOTEDEVS );
case 12:
@ -283,6 +295,9 @@ public class DBHelper extends SQLiteOpenHelper {
if ( !madeSumTable ) {
addSumColumn( db, EXTRAS );
}
case 24:
createInvitesTable( db );
break;
default:
db.execSQL( "DROP TABLE " + TABLE_NAME_SUM + ";" );
@ -381,6 +396,11 @@ public class DBHelper extends SQLiteOpenHelper {
createTable( db, TABLE_NAME_PAIRS, s_pairsSchema );
}
private void createInvitesTable( SQLiteDatabase db )
{
createTable( db, TABLE_NAME_INVITES, s_invitesSchema );
}
// Move all existing games to the row previously named "cur games'
private void moveToCurGames( SQLiteDatabase db )
{

View file

@ -34,6 +34,8 @@ import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import java.sql.Timestamp;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@ -52,10 +54,11 @@ import java.util.Set;
import java.util.StringTokenizer;
import junit.framework.Assert;
import org.eehouse.android.xw4.DictUtils.DictLoc;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
import org.eehouse.android.xw4.DictUtils.DictLoc;
import org.eehouse.android.xw4.loc.LocUtils;
public class DBUtils {
@ -268,12 +271,6 @@ public class DBUtils {
public static void saveSummary( Context context, GameLock lock,
GameSummary summary )
{
saveSummary( context, lock, summary, null );
}
public static void saveSummary( Context context, GameLock lock,
GameSummary summary, String inviteID )
{
boolean needsTimer = false;
Assert.assertTrue( lock.canWrite() );
@ -294,7 +291,15 @@ public class DBUtils {
values.put( DBHelper.GAMEID, summary.gameID );
values.put( DBHelper.GAME_OVER, summary.gameOver? 1 : 0 );
values.put( DBHelper.LASTMOVE, summary.lastMoveTime );
values.put( DBHelper.EXTRAS, summary.getExtras() );
// Don't overwrite extras! Sometimes this method is called from
// JNIThread which has created the summary from common code that
// doesn't know about Android additions. Leave those unset to
// avoid overwriting.
String extras = summary.getExtras();
if ( null != extras ) {
values.put( DBHelper.EXTRAS, summary.getExtras() );
}
long nextNag = summary.nextTurnIsLocal() ?
NagTurnReceiver.figureNextNag( context,
1000*(long)summary.lastMoveTime )
@ -302,9 +307,6 @@ public class DBUtils {
values.put( DBHelper.NEXTNAG, nextNag );
values.put( DBHelper.DICTLIST, summary.dictNames(DICTS_SEP) );
if ( null != inviteID ) {
values.put( DBHelper.INVITEID, inviteID );
}
if ( null != summary.scores ) {
StringBuffer sb = new StringBuffer();
@ -426,6 +428,172 @@ public class DBUtils {
return result;
}
public static class SentInvitesInfo {
public long m_rowid;
private ArrayList<InviteMeans> m_means;
private ArrayList<String> m_targets;
private ArrayList<Timestamp> m_timestamps;
private int m_cachedCount = 0;
private SentInvitesInfo( long rowID ) {
m_rowid = rowID;
m_means = new ArrayList<InviteMeans>();
m_targets = new ArrayList<String>();
m_timestamps = new ArrayList<Timestamp>();
}
private void addEntry( InviteMeans means, String target, Timestamp ts )
{
m_means.add( means );
m_targets.add( target );
m_timestamps.add( ts );
m_cachedCount = -1;
}
public InviteMeans getLastMeans()
{
return 0 < m_means.size() ? m_means.get(0) : null;
}
public String getLastDev( InviteMeans means )
{
String result = null;
for ( int ii = 0; null == result && ii < m_means.size(); ++ii ) {
if ( means == m_means.get( ii ) ) {
result = m_targets.get( ii );
}
}
return result;
}
// There will be lots of duplicates, but we can't detect them all. BUT
// if means and target are the same it's definitely a dup. So count
// them all and return the largest number we have. 99% of the time we
// care only that it's non-0.
public int getMinPlayerCount() {
if ( -1 == m_cachedCount ) {
DbgUtils.logf( "getMinPlayerCount(%H)", this );
int count = m_timestamps.size();
Map<InviteMeans, Set<String>> hashes
= new HashMap<InviteMeans, Set<String>>();
int fakeCount = 0; // make all null-targets count for one
for ( int ii = 0; ii < count; ++ii ) {
InviteMeans means = m_means.get(ii);
Set<String> devs;
if ( ! hashes.containsKey( means ) ) {
DbgUtils.logf( "creating new hash for means %s", means.toString() );
devs = new HashSet<String>();
hashes.put( means, devs );
}
devs = hashes.get( means );
String target = m_targets.get( ii );
if ( null == target ) {
target = String.format( "%d", ++fakeCount );
}
devs.add( target );
DbgUtils.logf( "added target %s for means %s", target, means.toString() );
}
// Now find the max
m_cachedCount = 0;
for ( InviteMeans means : InviteMeans.values() ) {
if ( hashes.containsKey( means ) ) {
int siz = hashes.get( means ).size();
m_cachedCount += siz;
DbgUtils.logf( "counting: means %s has unique count of %d",
means.toString(), siz );
}
}
}
DbgUtils.logf( "getMinPlayerCount(%H) => %d", this, m_cachedCount );
return m_cachedCount;
}
public String getAsText( Context context )
{
int count = m_timestamps.size();
String[] strs = new String[count];
for ( int ii = 0; ii < count; ++ii ) {
InviteMeans means = m_means.get(ii);
String target = m_targets.get(ii);
String timestamp = m_timestamps.get(ii).toString();
String msg;
switch ( means ) {
case SMS:
msg = LocUtils.getString( context, R.string.invit_expl_sms_fmt,
target, timestamp );
break;
case BLUETOOTH:
String devName = BTService.nameForAddr( target );
msg = LocUtils.getString( context, R.string.invit_expl_bt_fmt,
devName, timestamp );
break;
case RELAY:
msg = LocUtils.getString( context, R.string.invit_expl_relay_fmt,
timestamp );
break;
default:
msg = LocUtils.getString( context, R.string.invit_expl_notarget_fmt,
means.toString(), timestamp );
}
strs[ii] = msg;
}
return TextUtils.join( "\n\n", strs );
}
}
public static SentInvitesInfo getInvitesFor( Context context, long rowid )
{
SentInvitesInfo result = new SentInvitesInfo( rowid );
String[] columns = { DBHelper.MEANS, DBHelper.TIMESTAMP, DBHelper.TARGET };
String selection = String.format( "%s = %d", DBHelper.ROW, rowid );
String orderBy = DBHelper.TIMESTAMP + " DESC";
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_INVITES, columns,
selection, null, null, null, orderBy );
if ( 0 < cursor.getCount() ) {
int indxMns = cursor.getColumnIndex( DBHelper.MEANS );
int indxTS = cursor.getColumnIndex( DBHelper.TIMESTAMP );
int indxTrgt = cursor.getColumnIndex( DBHelper.TARGET );
while ( cursor.moveToNext() ) {
InviteMeans means = InviteMeans.values()[cursor.getInt( indxMns )];
Timestamp ts = Timestamp.valueOf(cursor.getString(indxTS));
String target = cursor.getString( indxTrgt );
result.addEntry( means, target, ts );
}
}
cursor.close();
db.close();
}
return result;
}
public static void recordInviteSent( Context context, long rowid,
InviteMeans means, String target )
{
ContentValues values = new ContentValues();
values.put( DBHelper.ROW, rowid );
values.put( DBHelper.MEANS, means.ordinal() );
if ( null != target ) {
values.put( DBHelper.TARGET, target );
}
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
db.insert( DBHelper.TABLE_NAME_INVITES, null, values );
db.close();
}
}
private static void setInt( long rowid, String column, int value )
{
ContentValues values = new ContentValues();
@ -938,11 +1106,17 @@ public class DBUtils {
public static void deleteGame( Context context, GameLock lock )
{
Assert.assertTrue( lock.canWrite() );
String selection = String.format( ROW_ID_FMT, lock.getRowid() );
String selSummaries = String.format( ROW_ID_FMT, lock.getRowid() );
String selInvites = String.format( "%s=%d", DBHelper.ROW, lock.getRowid() );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
db.delete( DBHelper.TABLE_NAME_SUM, selSummaries, null );
// Delete invitations too
db.delete( DBHelper.TABLE_NAME_INVITES, selInvites, null );
db.close();
}
notifyListeners( lock.getRowid(), GameChangeType.GAME_DELETED );

View file

@ -426,6 +426,12 @@ public class DelegateBase implements DlgClickNotify,
m_dlgDelegate.showConfirmThen( msg, posButton, negButton, action );
}
protected void showConfirmThen( int msg, int posButton, int negButton,
Action action, Object... params )
{
m_dlgDelegate.showConfirmThen( msg, posButton, negButton, action, params );
}
protected void showConfirmThen( int msg, int posButton, Action action,
Object... params )
{
@ -465,9 +471,10 @@ public class DelegateBase implements DlgClickNotify,
m_dlgDelegate.launchLookup( words, lang, !studyOn );
}
protected void showInviteChoicesThen( Action action )
protected void showInviteChoicesThen( Action action,
DBUtils.SentInvitesInfo info )
{
m_dlgDelegate.showInviteChoicesThen( action );
m_dlgDelegate.showInviteChoicesThen( action, info );
}
protected void showOKOnlyDialogThen( String msg, Action action )
@ -512,9 +519,9 @@ public class DelegateBase implements DlgClickNotify,
m_dlgDelegate.showDictGoneFinish();
}
protected void showSMSEnableDialog( Action action )
protected void showSMSEnableDialog( Action action, Object... params )
{
m_dlgDelegate.showSMSEnableDialog( action );
m_dlgDelegate.showSMSEnableDialog( action, params );
}
//////////////////////////////////////////////////
@ -565,14 +572,11 @@ public class DelegateBase implements DlgClickNotify,
if ( AlertDialog.BUTTON_POSITIVE == button ) {
switch( action ) {
case ENABLE_SMS_ASK:
showSMSEnableDialog( Action.ENABLE_SMS_DO );
showSMSEnableDialog( Action.ENABLE_SMS_DO, params );
handled = true;
break;
case ENABLE_SMS_DO:
boolean enabled = (Boolean)params[0];
if ( enabled ) {
XWPrefs.setSMSEnabled( m_activity, true );
}
XWPrefs.setSMSEnabled( m_activity, true );
break;
case ENABLE_BT_DO:
BTService.enable();

View file

@ -304,7 +304,10 @@ public class DictUtils {
public static void deleteDict( Context context, String name )
{
deleteDict( context, name, getDictLoc( context, name ) );
DictLoc loc = getDictLoc( context, name );
if ( null != loc ) {
deleteDict( context, name, getDictLoc(context, name) );
}
}
private static byte[] openDict( Context context, String name, DictLoc loc )

View file

@ -47,6 +47,7 @@ import java.util.Iterator;
import junit.framework.Assert;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
public class DlgDelegate {
@ -138,6 +139,8 @@ public class DlgDelegate {
private static final String STATE_KEYF = "STATE_%d";
public interface DlgClickNotify {
// These are stored in the INVITES table. Don't change order
// gratuitously
public static enum InviteMeans {
SMS, EMAIL, NFC, BLUETOOTH, CLIPBOARD, RELAY,
};
@ -280,9 +283,9 @@ public class DlgDelegate {
// Puts up alert asking to choose a reason to enable SMS, and on dismiss
// calls dlgButtonClicked with the action and in params a Boolean
// indicating whether enabling is now ok.
public void showSMSEnableDialog( Action action )
public void showSMSEnableDialog( Action action, Object... params )
{
DlgState state = new DlgState( DlgID.DIALOG_ENABLESMS, action );
DlgState state = new DlgState( DlgID.DIALOG_ENABLESMS, action, params );
addState( state );
showDialog( DlgID.DIALOG_ENABLESMS );
}
@ -366,6 +369,12 @@ public class DlgDelegate {
showConfirmThen( null, getString(msg), posButton, negButton, action, null );
}
public void showConfirmThen( int msg, int posButton, int negButton, Action action,
Object... params )
{
showConfirmThen( null, getString(msg), posButton, negButton, action, params );
}
public void showConfirmThen( int msg, int posButton, Action action,
Object[] params )
{
@ -389,13 +398,14 @@ public class DlgDelegate {
showDialog( DlgID.CONFIRM_THEN );
}
public void showInviteChoicesThen( final Action action )
public void showInviteChoicesThen( final Action action,
SentInvitesInfo info )
{
if ( (XWApp.SMS_INVITE_ENABLED && Utils.deviceSupportsSMS( m_activity ))
|| XWPrefs.getNFCToSelfEnabled( m_activity )
|| NFCUtils.nfcAvail( m_activity )[0]
|| BTService.BTAvailable() ) {
DlgState state = new DlgState( DlgID.INVITE_CHOICES_THEN, action );
DlgState state = new DlgState( DlgID.INVITE_CHOICES_THEN, action, info );
addState( state );
showDialog( DlgID.INVITE_CHOICES_THEN );
} else {
@ -614,6 +624,12 @@ public class DlgDelegate {
final ArrayList<DlgClickNotify.InviteMeans> means =
new ArrayList<DlgClickNotify.InviteMeans>();
ArrayList<String> items = new ArrayList<String>();
DlgClickNotify.InviteMeans lastMeans = null;
if ( null != state.m_params
&& state.m_params[0] instanceof SentInvitesInfo ) {
lastMeans =((SentInvitesInfo)state.m_params[0]).getLastMeans();
}
if ( XWApp.SMS_INVITE_ENABLED && Utils.deviceSupportsSMS(m_activity) ) {
items.add( getString( R.string.invite_choice_sms ) );
means.add( DlgClickNotify.InviteMeans.SMS );
@ -633,27 +649,41 @@ public class DlgDelegate {
items.add( getString( R.string.invite_choice_relay ) );
means.add( DlgClickNotify.InviteMeans.RELAY );
}
final int clipPos = means.size();
items.add( getString( R.string.slmenu_copy_sel ) );
means.add( DlgClickNotify.InviteMeans.CLIPBOARD );
final int[] sel = { -1 };
if ( null != lastMeans ) {
for ( int ii = 0; ii < means.size(); ++ii ) {
if ( lastMeans == means.get(ii) ) {
sel[0] = ii;
break;
}
}
}
OnClickListener selChanged = new OnClickListener() {
public void onClick( DialogInterface dlg, int view ) {
// First time through, enable the button
if ( -1 == sel[0] ) {
((AlertDialog)dlg)
.getButton( AlertDialog.BUTTON_POSITIVE )
.setEnabled( true );
}
sel[0] = view;
if ( view == clipPos ) {
switch ( means.get(view) ) {
case CLIPBOARD:
String msg =
getString( R.string.not_again_clip_expl_fmt,
getString(R.string.slmenu_copy_sel) );
showNotAgainDlgThen( msg, R.string.key_na_clip_expl );
break;
case SMS:
if ( ! XWPrefs.getSMSEnabled( m_activity ) ) {
showConfirmThen( R.string.warn_sms_disabled,
R.string.button_enable_sms,
R.string.button_later,
Action.ENABLE_SMS_ASK );
}
break;
}
Button button = ((AlertDialog)dlg)
.getButton( AlertDialog.BUTTON_POSITIVE );
button.setEnabled( true );
}
};
OnClickListener okClicked = new OnClickListener() {
@ -670,7 +700,7 @@ public class DlgDelegate {
AlertDialog.Builder builder = LocUtils.makeAlertBuilder( m_activity )
.setTitle( R.string.invite_choice_title )
.setSingleChoiceItems( items.toArray( new String[items.size()] ),
.setSingleChoiceItems( items.toArray( new String[items.size()] ),
sel[0], selChanged )
.setPositiveButton( android.R.string.ok, okClicked )
.setNegativeButton( android.R.string.cancel, null );
@ -683,7 +713,8 @@ public class DlgDelegate {
AlertDialog ad = (AlertDialog)dialog;
Button button = ad.getButton( AlertDialog.BUTTON_POSITIVE );
if ( null != button ) {
button.setEnabled( false );
long[] ids = ad.getListView().getCheckedItemIds();
button.setEnabled( 1 == ids.length );
}
}
@ -715,10 +746,9 @@ public class DlgDelegate {
layout.findViewById( R.id.confirm_sms_reasons );
boolean enabled = 0 < reasons.getSelectedItemPosition();
Assert.assertTrue( enabled );
Object[] params = { new Boolean(enabled), };
m_clickCallback.dlgButtonClicked( state.m_action,
AlertDialog.BUTTON_POSITIVE,
params );
state.m_params );
}
};

View file

@ -91,6 +91,11 @@ public class DlgState implements Parcelable {
this( dlgID, null, 0, null, 0, params );
}
public DlgState( DlgID dlgID, Action action, Object... params )
{
this( dlgID, null, 0, action, 0, params );
}
public int describeContents() {
return 0;
}

View file

@ -391,6 +391,15 @@ public class DwnldDelegate extends ListDelegateBase {
downloadDictInBack( context, uri, name, lstnr );
}
public static void downloadDictInBack( Context context, Uri uri,
String name,
DownloadFinishedListener lstnr )
{
Uri[] uris = new Uri[] { uri };
String[] names = new String[] { name };
downloadDictsInBack( context, uris, names, lstnr );
}
public static void downloadDictsInBack( Context context, Uri[] uris,
String[] names,
DownloadFinishedListener lstnr )
@ -406,15 +415,6 @@ public class DwnldDelegate extends ListDelegateBase {
context.startActivity( intent );
}
public static void downloadDictInBack( Context context, Uri uri,
String name,
DownloadFinishedListener lstnr )
{
Uri[] uris = new Uri[] { uri };
String[] names = new String[] { name };
downloadDictsInBack( context, uris, names, lstnr );
}
public static Intent makeAppDownloadIntent( Context context, String url )
{
Intent intent = new Intent( context, DwnldActivity.class );

View file

@ -102,6 +102,12 @@ public class GameListItem extends LinearLayout
} );
}
public GameSummary getSummary()
{
Assert.assertNotNull( m_summary );
return m_summary;
}
private void init( Handler handler, long rowid, int fieldID,
SelectableItem cb )
{
@ -320,7 +326,7 @@ public class GameListItem extends LinearLayout
}
} );
String roleSummary = summary.summarizeRole();
String roleSummary = summary.summarizeRole( m_rowid );
if ( null != roleSummary ) {
m_role.setText( roleSummary );
} else {

View file

@ -469,8 +469,8 @@ public class GameUtils {
addr, new int[] {nli.lang},
new String[] { nli.dict }, nli.nPlayersT,
nli.nPlayersH, nli.forceChannel,
nli.inviteID(), nli.gameID(), nli.gameName,
false );
nli.inviteID(), nli.gameID(),
nli.gameName, false );
}
public static long makeNewMultiGame( Context context, long groupID,
@ -543,7 +543,7 @@ public class GameUtils {
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
GameLock lock = new GameLock( rowid, true ).lock();
applyChanges( context, sink, gi, util, addr, inviteID, lock, false );
applyChanges( context, sink, gi, util, addr, lock, false );
lock.unlock();
}
@ -875,7 +875,8 @@ public class GameUtils {
public static boolean feedMessages( Context context, long rowid,
byte[][] msgs, CommsAddrRec ret,
MultiMsgSink sink, BackMoveResult bmr )
MultiMsgSink sink, BackMoveResult bmr,
boolean[] isLocalOut )
{
boolean draw = false;
Assert.assertTrue( -1 != rowid );
@ -920,7 +921,12 @@ public class GameUtils {
}
saveGame( context, gamePtr, gi, lock, false );
summarizeAndClose( context, lock, gamePtr, gi );
GameSummary summary = summarizeAndClose( context, lock,
gamePtr, gi );
if ( null != isLocalOut ) {
isLocalOut[0] = 0 <= summary.turn
&& gi.players[summary.turn].isLocal;
}
int flags = setFromFeedImpl( feedImpl );
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
@ -937,12 +943,12 @@ public class GameUtils {
public static boolean feedMessage( Context context, long rowid, byte[] msg,
CommsAddrRec ret, MultiMsgSink sink,
BackMoveResult bmr )
BackMoveResult bmr, boolean[] isLocalOut )
{
Assert.assertTrue( DBUtils.ROWID_NOTFOUND != rowid );
byte[][] msgs = new byte[1][];
msgs[0] = msg;
return feedMessages( context, rowid, msgs, ret, sink, bmr );
return feedMessages( context, rowid, msgs, ret, sink, bmr, isLocalOut );
}
// This *must* involve a reset if the language is changing!!!
@ -986,24 +992,17 @@ public class GameUtils {
} // replaceDicts
public static void applyChanges( Context context, CurGameInfo gi,
CommsAddrRec car, GameLock lock,
CommsAddrRec car, GameLock lock,
boolean forceNew )
{
applyChanges( context, gi, car, null, lock, forceNew );
}
public static void applyChanges( Context context, CurGameInfo gi,
CommsAddrRec car, String inviteID,
GameLock lock, boolean forceNew )
{
applyChanges( context, (MultiMsgSink)null, gi, (UtilCtxt)null, car,
inviteID, lock, forceNew );
lock, forceNew );
}
public static void applyChanges( Context context, MultiMsgSink sink,
CurGameInfo gi, UtilCtxt util,
CommsAddrRec car, String inviteID,
GameLock lock, boolean forceNew )
CommsAddrRec car, GameLock lock,
boolean forceNew )
{
// This should be a separate function, commitChanges() or
// somesuch. But: do we have a way to save changes to a gi
@ -1049,7 +1048,7 @@ public class GameUtils {
GameSummary summary = new GameSummary( context, gi );
XwJNI.game_summarize( gamePtr, summary );
DBUtils.saveSummary( context, lock, summary, inviteID );
DBUtils.saveSummary( context, lock, summary );
XwJNI.game_dispose( gamePtr );
} // applyChanges
@ -1086,7 +1085,8 @@ public class GameUtils {
}
public static void postMoveNotification( Context context, long rowid,
BackMoveResult bmr )
BackMoveResult bmr,
boolean isTurnNow )
{
if ( null != bmr ) {
Intent intent = GamesListDelegate.makeRowidIntent( context, rowid );
@ -1102,8 +1102,12 @@ public class GameUtils {
msg = bmr.m_chat;
}
} else if ( null != bmr.m_lmi ) {
titleID = R.string.notify_title_fmt;
msg = bmr.m_lmi.format( context ); // NPE
if ( isTurnNow ) {
titleID = R.string.notify_title_turn_fmt;
} else {
titleID = R.string.notify_title_fmt;
}
msg = bmr.m_lmi.format( context );
}
if ( 0 != titleID ) {

View file

@ -60,14 +60,15 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.DBUtils.GameChangeType;
import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
import org.eehouse.android.xw4.DwnldDelegate.DownloadFinishedListener;
import org.eehouse.android.xw4.DwnldDelegate.OnGotLcDictListener;
import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.DwnldDelegate.DownloadFinishedListener;
import org.eehouse.android.xw4.DwnldDelegate.OnGotLcDictListener;
public class GamesListDelegate extends ListDelegateBase
implements OnItemLongClickListener,
@ -1517,21 +1518,22 @@ public class GamesListDelegate extends ListDelegateBase
public void onCreateContextMenu( ContextMenu menu, View view,
ContextMenuInfo menuInfo )
{
boolean enable;
super.onCreateContextMenu( menu, view, menuInfo );
int id = 0;
boolean selected = false;
long gameRowID = 0;
GameListItem item = null;
AdapterView.AdapterContextMenuInfo info
= (AdapterView.AdapterContextMenuInfo)menuInfo;
View targetView = info.targetView;
DbgUtils.logf( "onCreateContextMenu(t=%s)",
targetView.getClass().getName() );
if ( targetView instanceof GameListItem ) {
item = (GameListItem)targetView;
id = R.menu.games_list_game_menu;
gameRowID = ((GameListItem)targetView).getRowID();
selected = m_selGames.contains( gameRowID );
selected = m_selGames.contains( item.getRowID() );
} else if ( targetView instanceof GameListGroup ) {
id = R.menu.games_list_group_menu;
@ -1548,12 +1550,17 @@ public class GamesListDelegate extends ListDelegateBase
? R.id.games_game_select : R.id.games_game_deselect;
Utils.setItemVisible( menu, hideId, false );
if ( 0 != gameRowID ) {
boolean enable = BoardDelegate.rematchSupported( m_activity,
gameRowID );
if ( null != item ) {
enable = BoardDelegate.rematchSupported( m_activity,
item.getRowID() );
Utils.setItemVisible( menu, R.id.games_game_rematch, enable );
enable = item.getSummary().isMultiGame()
&& (BuildConfig.DEBUG || XWPrefs.getDebugEnabled( m_activity ));
Utils.setItemVisible( menu, R.id.games_game_invites, enable );
}
}
}
public boolean onContextItemSelected( MenuItem item )
@ -1714,6 +1721,21 @@ public class GamesListDelegate extends ListDelegateBase
m_rowid = selRowIDs[0];
showDialog( DlgID.RENAME_GAME );
break;
// DEBUG only
case R.id.games_game_invites:
msg = DBUtils.getSummary( m_activity, selRowIDs[0] )
.conTypes.toString( m_activity );
msg = getString( R.string.invites_net_fmt, msg );
SentInvitesInfo info = DBUtils.getInvitesFor( m_activity,
selRowIDs[0] );
if ( null != info ) {
msg += "\n\n" + info.getAsText( m_activity );
}
showOKOnlyDialog( msg );
break;
default:
handled = false;
}
@ -1986,7 +2008,7 @@ public class GamesListDelegate extends ListDelegateBase
{
NetLaunchInfo nli = null;
if ( MultiService.isMissingDictIntent( intent ) ) {
nli = new NetLaunchInfo( m_activity, intent );
nli = MultiService.getMissingDictData( m_activity, intent );
} else {
Uri data = intent.getData();
if ( null != data ) {
@ -2045,9 +2067,8 @@ public class GamesListDelegate extends ListDelegateBase
DBUtils.setName( m_activity, newid, gameName );
} else {
long groupID = DBUtils.getGroupForGame( m_activity, srcRowID );
newid = GameUtils.makeNewMultiGame( m_activity, groupID,
dict, lang,
addrs, gameName );
newid = GameUtils.makeNewMultiGame( m_activity, groupID, dict,
lang, addrs, gameName );
DBUtils.addRematchInfo( m_activity, newid, btAddr, phone,
relayID );
}

View file

@ -40,8 +40,10 @@ abstract class InviteDelegate extends ListDelegateBase
public static final String DEVS = "DEVS";
public static final String COUNTS = "COUNTS";
protected static final String INTENT_KEY_NMISSING = "NMISSING";
protected static final String INTENT_KEY_LASTDEV = "LDEV";
protected int m_nMissing;
protected String m_lastDev;
protected Button m_okButton;
protected Button m_rescanButton;
protected Button m_clearButton;
@ -56,6 +58,7 @@ abstract class InviteDelegate extends ListDelegateBase
m_activity = delegator.getActivity();
Intent intent = getIntent();
m_nMissing = intent.getIntExtra( INTENT_KEY_NMISSING, -1 );
m_lastDev = intent.getStringExtra( INTENT_KEY_LASTDEV );
}
protected void init( int button_invite, int button_rescan,

View file

@ -26,6 +26,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import junit.framework.Assert;
import org.eehouse.android.xw4.loc.LocUtils;
public class MultiService {
@ -43,7 +45,7 @@ public class MultiService {
public static final String OWNER = "OWNER";
public static final String BT_NAME = "BT_NAME";
public static final String BT_ADDRESS = "BT_ADDRESS";
public static final String NLI_DATA = "nli";
private static final String NLI_DATA = "nli";
public enum DictFetchOwner { _NONE,
OWNER_SMS,
@ -128,6 +130,15 @@ public class MultiService {
return result;
}
public static NetLaunchInfo getMissingDictData( Context context,
Intent intent )
{
Assert.assertTrue( isMissingDictIntent( intent ) );
String nliData = intent.getStringExtra( NLI_DATA );
NetLaunchInfo nli = new NetLaunchInfo( context, nliData );
return nli;
}
public static Dialog missingDictDialog( Context context, Intent intent,
OnClickListener onDownload,
OnClickListener onDecline )
@ -159,7 +170,7 @@ public class MultiService {
// resend the intent, but only if the dict it names is here. (If
// it's not, we may need to try again later, e.g. because our cue
// was a focus gain.)
static boolean returnOnDownload( Context context, Intent intent )
public static boolean returnOnDownload( Context context, Intent intent )
{
boolean downloaded = isMissingDictIntent( intent );
if ( downloaded ) {

View file

@ -88,13 +88,6 @@ public class NetLaunchInfo {
init( context, data );
}
public NetLaunchInfo( Context context, Intent intent )
{
String data = intent.getStringExtra( MultiService.NLI_DATA );
Assert.assertNotNull( data );
init( context, data );
}
private NetLaunchInfo( Bundle bundle )
{
lang = bundle.getInt( MultiService.LANG );

View file

@ -82,11 +82,7 @@ public class NetStateCache {
boolean netAvail = getIsConnected( context );
if ( netAvail ) {
String msg = "netAvail(): second-guessing successful!!!";
DbgUtils.logf( msg );
if ( BuildConfig.DEBUG ) {
Utils.showToast( context, msg );
}
DbgUtils.logf( "netAvail(): second-guessing successful!!!" );
s_netAvail = true;
if ( null != s_receiver ) {
s_receiver.notifyStateChanged( context );

View file

@ -53,8 +53,6 @@ import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.UtilCtxt.DevIDType;
import org.eehouse.android.xw4.jni.UtilCtxt;
import org.eehouse.android.xw4.jni.UtilCtxtImpl;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.loc.LocUtils;
@ -256,8 +254,8 @@ public class RelayService extends XWService
// make the initial connection -- needs access to util. That
// should be fixable -- eventually.
RelayMsgSink sink = new RelayMsgSink();
UtilCtxt util = new UtilCtxtImpl( this );
long rowid = GameUtils.makeNewMultiGame( this, nli, sink, util );
long rowid = GameUtils.makeNewMultiGame( this, nli, sink,
getUtilCtxt() );
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
DBUtils.setName( this, rowid, nli.gameName );
@ -452,7 +450,8 @@ public class RelayService extends XWService
startService( this ); // bad name: will *stop* threads too
}
private void setupNotifications( String[] relayIDs, BackMoveResult[] bmrs )
private void setupNotifications( String[] relayIDs, BackMoveResult[] bmrs,
ArrayList<Boolean> locals )
{
for ( int ii = 0; ii < relayIDs.length; ++ii ) {
String relayID = relayIDs[ii];
@ -460,7 +459,8 @@ public class RelayService extends XWService
long[] rowids = DBUtils.getRowIDsFor( this, relayID );
if ( null != rowids ) {
for ( long rowid : rowids ) {
GameUtils.postMoveNotification( this, rowid, bmr );
GameUtils.postMoveNotification( this, rowid, bmr,
locals.get(ii) );
}
}
}
@ -993,9 +993,10 @@ public class RelayService extends XWService
RelayMsgSink sink = new RelayMsgSink();
sink.setRowID( rowid );
BackMoveResult bmr = new BackMoveResult();
boolean[] isLocalP = new boolean[1];
if ( GameUtils.feedMessage( this, rowid, msg, s_addr,
sink, bmr ) ) {
GameUtils.postMoveNotification( this, rowid, bmr );
sink, bmr, isLocalP ) ) {
GameUtils.postMoveNotification( this, rowid, bmr, isLocalP[0] );
} else {
DbgUtils.logdf( "feedMessage(): background dropped it" );
}
@ -1018,9 +1019,11 @@ public class RelayService extends XWService
RelayMsgSink sink = new RelayMsgSink();
int nameCount = relayIDs.length;
ArrayList<String> idsWMsgs = new ArrayList<String>( nameCount );
ArrayList<Boolean> isLocals = new ArrayList<Boolean>( nameCount );
ArrayList<BackMoveResult> bmrs =
new ArrayList<BackMoveResult>( nameCount );
boolean[] isLocalP = new boolean[1];
for ( int ii = 0; ii < nameCount; ++ii ) {
byte[][] forOne = msgs[ii];
@ -1028,12 +1031,15 @@ public class RelayService extends XWService
if ( null != forOne ) {
BackMoveResult bmr = new BackMoveResult();
sink.setRowID( rowIDs[ii] );
// since BoardDelegate.feedMessages can't know:
isLocalP[0] = false;
if ( BoardDelegate.feedMessages( rowIDs[ii], forOne, s_addr )
|| GameUtils.feedMessages( this, rowIDs[ii],
forOne, s_addr,
sink, bmr ) ) {
sink, bmr, isLocalP ) ) {
idsWMsgs.add( relayIDs[ii] );
bmrs.add( bmr );
isLocals.add( isLocalP[0] );
} else {
DbgUtils.logf( "RelayService.process(): message for %s (rowid %d)"
+ " not consumed", relayIDs[ii], rowIDs[ii] );
@ -1045,7 +1051,7 @@ public class RelayService extends XWService
idsWMsgs.toArray( tmp );
BackMoveResult[] bmrsa = new BackMoveResult[bmrs.size()];
bmrs.toArray( bmrsa );
setupNotifications( tmp, bmrsa );
setupNotifications( tmp, bmrsa, isLocals );
}
sink.send( this );
}

View file

@ -49,6 +49,7 @@ import java.util.Map;
import junit.framework.Assert;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
public class SMSInviteDelegate extends InviteDelegate {
@ -63,11 +64,16 @@ public class SMSInviteDelegate extends InviteDelegate {
private boolean m_immobileConfirmed;
private Activity m_activity;
public static void launchForResult( Activity activity, int nMissing,
public static void launchForResult( Activity activity, int nMissing,
SentInvitesInfo info,
RequestCode requestCode )
{
Intent intent = new Intent( activity, SMSInviteActivity.class );
intent.putExtra( INTENT_KEY_NMISSING, nMissing );
if ( null != info ) {
String lastDev = info.getLastDev( InviteMeans.SMS );
intent.putExtra( INTENT_KEY_LASTDEV, lastDev );
}
activity.startActivityForResult( intent, requestCode.ordinal() );
}
@ -327,7 +333,8 @@ public class SMSInviteDelegate extends InviteDelegate {
m_phoneRecs = new ArrayList<PhoneRec>(phones.length);
for ( String phone : phones ) {
PhoneRec rec = new PhoneRec( phone );
boolean matches = phone.equals( m_lastDev );
PhoneRec rec = new PhoneRec( null, phone, matches );
m_phoneRecs.add( rec );
}
}
@ -369,6 +376,24 @@ public class SMSInviteDelegate extends InviteDelegate {
saveAndRebuild();
}
private void clearIfSingle()
{
if ( 1 == m_nMissing ) {
int count = m_adapter.getCount();
for ( int ii = count - 1; ii >= 0; --ii ) {
PhoneRec rec = m_phoneRecs.get( ii );
if ( rec.m_isChecked ) {
rec.m_isChecked = false;
}
}
post( new Runnable() {
public void run() {
rebuildList( false );
}
} );
}
}
private class PhoneRec {
public String m_phone;
public String m_name;
@ -383,7 +408,7 @@ public class SMSInviteDelegate extends InviteDelegate {
this( null, phone, false );
}
public PhoneRec( String name, String phone, boolean checked )
private PhoneRec( String name, String phone, boolean checked )
{
m_phone = phone;
m_isChecked = checked;
@ -424,6 +449,7 @@ public class SMSInviteDelegate extends InviteDelegate {
new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged( CompoundButton bv,
boolean isChecked ) {
clearIfSingle();
m_phoneRecs.get(position).m_isChecked = isChecked;
tryEnable();
}

View file

@ -56,6 +56,7 @@ import java.util.Set;
import junit.framework.Assert;
import org.eehouse.android.xw4.GameUtils.BackMoveResult;
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec;
@ -348,20 +349,15 @@ public class SMSService extends XWService {
inviteRemote( phone, intent.getStringExtra( GAMEDATA_STR ) );
break;
case ADDED_MISSING:
NetLaunchInfo nli
= MultiService.getMissingDictData( this, intent );
phone = intent.getStringExtra( PHONE );
int gameID = intent.getIntExtra( MultiService.GAMEID, -1 );
String gameName = intent.getStringExtra( MultiService.GAMENAME );
int lang = intent.getIntExtra( MultiService.LANG, -1 );
String dict = intent.getStringExtra( MultiService.DICT );
int nPlayersT = intent.getIntExtra( MultiService.NPLAYERST, -1 );
int nPlayersH = intent.getIntExtra( MultiService.NPLAYERSH, -1 );
makeForInvite( phone, gameID, gameName, lang, dict,
nPlayersT, nPlayersH, 1 );
makeForInvite( phone, nli );
break;
case SEND:
phone = intent.getStringExtra( PHONE );
byte[] bytes = intent.getByteArrayExtra( BINBUFFER );
gameID = intent.getIntExtra( MultiService.GAMEID, -1 );
int gameID = intent.getIntExtra( MultiService.GAMEID, -1 );
sendPacket( phone, gameID, bytes );
break;
case REMOVE:
@ -512,18 +508,11 @@ public class SMSService extends XWService {
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) {
makeForInvite( phone, nli );
} else {
Assert.fail();
// Intent intent = MultiService
// .makeMissingDictIntent( this, gameName, lang, dict,
// nPlayersT, nPlayersH );
// intent.putExtra( PHONE, phone );
// intent.putExtra( MultiService.OWNER,
// MultiService.OWNER_SMS );
// intent.putExtra( MultiService.INVITER,
// Utils.phoneToContact( this, phone, true ) );
// intent.putExtra( MultiService.GAMEID, gameID );
// MultiService.postMissingDictNotification( this, intent,
// gameID );
Intent intent = MultiService
.makeMissingDictIntent( this, nli,
DictFetchOwner.OWNER_SMS );
MultiService.postMissingDictNotification( this, intent,
nli.gameID() );
}
} else {
DbgUtils.logf( "invalid nli from: %s", nliData );
@ -648,24 +637,13 @@ public class SMSService extends XWService {
private void makeForInvite( String phone, NetLaunchInfo nli )
{
SMSMsgSink sink = new SMSMsgSink( this );
long rowid = GameUtils.makeNewMultiGame( this, nli, sink, null );
long rowid = GameUtils.makeNewMultiGame( this, nli,
new SMSMsgSink( this ),
getUtilCtxt() );
postNotification( phone, nli.gameID(), rowid );
ackInvite( phone, nli.gameID() );
}
private void makeForInvite( String phone, int gameID, String gameName,
int lang, String dict, int nPlayersT,
int nPlayersH, int forceChannel )
{
long rowid =
GameUtils.makeNewGame( this, gameID, new CommsAddrRec( phone ),
lang, dict, nPlayersT, nPlayersH,
forceChannel, gameName );
postNotification( phone, gameID, rowid );
ackInvite( phone, gameID );
}
private PendingIntent makeStatusIntent( String msg )
{
Intent intent = new Intent( msg );
@ -729,6 +707,7 @@ public class SMSService extends XWService {
if ( null == rowids || 0 == rowids.length ) {
sendDiedPacket( addr.sms_phone, gameID );
} else {
boolean[] isLocalP = new boolean[1];
for ( long rowid : rowids ) {
if ( BoardDelegate.feedMessage( rowid, msg, addr ) ) {
// do nothing
@ -736,8 +715,9 @@ public class SMSService extends XWService {
SMSMsgSink sink = new SMSMsgSink( this );
BackMoveResult bmr = new BackMoveResult();
if ( GameUtils.feedMessage( this, rowid, msg, addr,
sink, bmr ) ) {
GameUtils.postMoveNotification( this, rowid, bmr );
sink, bmr, isLocalP ) ) {
GameUtils.postMoveNotification( this, rowid, bmr,
isLocalP[0] );
}
}
}

View file

@ -136,7 +136,12 @@ public class Utils {
public static void showToast( Context context, String msg )
{
Toast.makeText( context, msg, Toast.LENGTH_SHORT).show();
// Make this safe to call from non-looper threads
try {
Toast.makeText( context, msg, Toast.LENGTH_SHORT).show();
} catch ( java.lang.RuntimeException re ) {
DbgUtils.loge( re );
}
}
public static void showToast( Context context, int id )

View file

@ -28,13 +28,16 @@ import java.util.HashSet;
import java.util.Set;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.UtilCtxt;
import org.eehouse.android.xw4.jni.UtilCtxtImpl;
public class XWService extends Service {
protected static MultiService s_srcMgr = null;
private static Set<String> s_seen = new HashSet<String>();
private UtilCtxt m_utilCtxt;
@Override
public IBinder onBind( Intent intent )
{
@ -74,4 +77,12 @@ public class XWService extends Service {
DbgUtils.logf( "XWService.checkNotDupe(%s) => %b", inviteID, !isDupe );
return !isDupe;
}
protected UtilCtxt getUtilCtxt()
{
if ( null == m_utilCtxt ) {
m_utilCtxt = new UtilCtxtImpl( this );
}
return m_utilCtxt;
}
}

View file

@ -27,6 +27,7 @@ import java.util.Iterator;
import junit.framework.Assert;
import org.json.JSONObject;
import org.eehouse.android.xw4.DBUtils;
import org.eehouse.android.xw4.DbgUtils;
import org.eehouse.android.xw4.R;
import org.eehouse.android.xw4.Utils;
@ -196,15 +197,26 @@ public class GameSummary {
// FIXME: should report based on whatever conType is giving us a
// successful connection.
public String summarizeRole()
public String summarizeRole( long rowid )
{
String result = null;
if ( isMultiGame() ) {
int fmtID = 0;
int missing = countMissing();
if ( 0 < missing ) {
DBUtils.SentInvitesInfo si = DBUtils.getInvitesFor( m_context,
rowid );
if ( si.getMinPlayerCount() >= missing ) {
result = LocUtils.getString( m_context,
R.string.summary_invites_out );
}
}
// If we're using relay to connect, get status from that
if ( conTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
if ( anyMissing() ) {
if ( null == result
&& conTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
if ( 0 < missing ) {
if ( null == relayID || 0 == relayID.length() ) {
fmtID = R.string.summary_relay_conf_fmt;
} else {
@ -222,7 +234,7 @@ public class GameSummary {
if ( null == result ) {
if ( conTypes.contains( CommsConnType.COMMS_CONN_BT )
|| ( conTypes.contains( CommsConnType.COMMS_CONN_SMS))){
if ( anyMissing() ) {
if ( 0 < missing ) {
if ( DeviceRole.SERVER_ISSERVER == serverRole ) {
fmtID = R.string.summary_wait_host;
} else {
@ -277,16 +289,20 @@ public class GameSummary {
return result;
}
public boolean anyMissing()
private int countMissing()
{
boolean missing = false;
int result = 0;
for ( int ii = 0; ii < nPlayers; ++ii ) {
if ( !isLocal(ii) && (0 != ((1 << ii) & missingPlayers) ) ) {
missing = true;
break;
++result;
}
}
return missing;
return result;
}
public boolean anyMissing()
{
return 0 < countMissing();
}
public int giflags() {
@ -445,7 +461,7 @@ public class GameSummary {
break;
}
}
DbgUtils.logf( "hasRematchInfo() => %b", found );
// DbgUtils.logf( "hasRematchInfo() => %b", found );
}
return found;
}

View file

@ -382,7 +382,9 @@ DBMgr::RegisterDevice( const DevID* host, int clientVersion,
// coming from random, but test with increasing values initially to make
// sure duplicates are detected.
const char* devidStr = host->m_devIDString.c_str();
for ( bool success = false, ii = 0; !success; ++ii ) {
int ii;
bool success;
for ( success = false, ii = 0; !success; ++ii ) {
assert( 10 > ii ); // better to check that we're looping BECAUSE
// of uniqueness problem.
do {