Merge remote branch 'origin/android_branch' into android_branch

This commit is contained in:
Eric House 2013-11-21 05:25:23 -08:00
commit 2d7c845ebb
134 changed files with 5047 additions and 2737 deletions

View file

@ -5,4 +5,6 @@ bin
gen gen
libs libs
proguard.cfg proguard.cfg
proguard-project.txt
obj obj
res/drawable*/*gen.png

View file

@ -22,14 +22,14 @@
to come from a domain that you own or have control over. --> to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eehouse.android.xw4" package="org.eehouse.android.xw4"
android:versionCode="61" android:versionCode="64"
android:versionName="@string/app_version" android:versionName="@string/app_version"
> >
<!-- BE SURE TO MODIFY project.project AND the variable TARGET in <!-- BE SURE TO MODIFY project.project AND the variable TARGET in
../scripts/setup_local_props.sh if targetSdkVersion changes!!! ../scripts/setup_local_props.sh if targetSdkVersion changes!!!
--> -->
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="11" /> <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14" />
<supports-screens android:resizeable="true" <supports-screens android:resizeable="true"
android:smallScreens="true" android:smallScreens="true"
@ -51,6 +51,7 @@
<uses-feature android:name="android.hardware.telephony" <uses-feature android:name="android.hardware.telephony"
android:required = "false" android:required = "false"
/> />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<!-- GCM stuff --> <!-- GCM stuff -->
<permission android:name="org.eehouse.android.xw4.permission.C2D_MESSAGE" <permission android:name="org.eehouse.android.xw4.permission.C2D_MESSAGE"
@ -59,6 +60,7 @@
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.NFC" />
<application android:icon="@drawable/icon48x48" <application android:icon="@drawable/icon48x48"
android:label="@string/app_name" android:label="@string/app_name"
@ -74,6 +76,13 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="@string/xwords_nfc_mime" />
</intent-filter>
</activity> </activity>
<activity android:name="DictsActivity" <activity android:name="DictsActivity"

View file

@ -51,20 +51,38 @@
<property name="INITIAL_CLIENT_VERS" value="3"/> <property name="INITIAL_CLIENT_VERS" value="3"/>
<target name="-pre-clean"> <target name="-pre-clean">
<exec dir="." executable="../scripts/ndksetup.sh" output="/dev/null">
<arg value="${build.target}"/>
</exec>
<exec dir="." executable="../scripts/ndkbuild.sh" output="/dev/null"> <exec dir="." executable="../scripts/ndkbuild.sh" output="/dev/null">
<arg value="clean"/> <arg value="clean"/>
</exec> </exec>
<exec dir="." executable="../scripts/mkimages.sh"
failonerror="true" output="/dev/null" >
<arg value="--clean"/>
</exec>
</target> </target>
<target name="-pre-build"> <target name="-pre-build">
<exec dir="." executable="../scripts/ndksetup.sh" output="/dev/null">
<arg value="${build.target}"/>
</exec>
<property name="CHAT_ENABLED" value="true" /> <property name="CHAT_ENABLED" value="true" />
<property name="THUMBNAIL_ENABLED" value="true" />
<exec dir="." executable="../scripts/ndkbuild.sh" failonerror="true"> <exec dir="." executable="../scripts/ndkbuild.sh" failonerror="true">
<arg value="BUILD_TARGET=${build.target}" /> <arg value="BUILD_TARGET=${build.target}" />
<arg value="-j3"/> <arg value="-j3"/>
<arg value="INITIAL_CLIENT_VERS=${INITIAL_CLIENT_VERS}" /> <arg value="INITIAL_CLIENT_VERS=${INITIAL_CLIENT_VERS}" />
<arg value="CHAT_ENABLED=${CHAT_ENABLED}" /> <arg value="CHAT_ENABLED=${CHAT_ENABLED}" />
<arg value="THUMBNAIL_ENABLED=${THUMBNAIL_ENABLED}" />
</exec> </exec>
<exec dir="." executable="../scripts/mkimages.sh"
failonerror="true" output="/dev/null"
/>
<exec dir="." executable="../scripts/gen_gcmid.sh" <exec dir="." executable="../scripts/gen_gcmid.sh"
output="src/org/eehouse/android/xw4/GCMConsts.java" output="src/org/eehouse/android/xw4/GCMConsts.java"
logError="true" logError="true"
@ -74,6 +92,8 @@
<arg value="xw4"/> <arg value="xw4"/>
<arg value="${INITIAL_CLIENT_VERS}" /> <arg value="${INITIAL_CLIENT_VERS}" />
<arg value="${CHAT_ENABLED}" /> <arg value="${CHAT_ENABLED}" />
<arg value="${THUMBNAIL_ENABLED}" />
<arg value="${build.target}" />
</exec> </exec>
</target> </target>
<!-- <!--

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g transform='translate(46.03,16.24)' id='g1584'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 5.5,5.502 -16.87,27.87 35.7,27.87 35.7,35.65 -16.87,35.65 5.5,58.02 0,63.52 -31.76,31.76 0,0 z' id='path1586'/>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 609 B

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g id='g1254'>
<g id='g1256'>
<g transform='translate(68.92,54.16)' id='g1262'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 C -0.046,0.281 -0.218,0.557 -0.476,0.82 -2.555,-1.292 -10.92,-1.642 -20.92,-1.642 -30.92,-1.642 -39.28,-1.292 -41.37,0.82 -41.62,0.557 -41.78,0.281 -41.84,0 L -41.86,0 -41.86,-0.152 C -41.86,-0.17 -41.86,-0.188 -41.86,-0.206 -41.86,-0.247 -41.85,-0.283 -41.85,-0.322 L -40.22,-33.68 -40.21,-33.68 C -40.07,-36.29 -37.07,-40.24 -20.92,-40.24 -4.766,-40.24 -1.77,-36.29 -1.629,-33.68 L -1.617,-33.68 0.013,-0.322 C 0.016,-0.283 0.028,-0.247 0.028,-0.206 0.028,-0.188 0.016,-0.17 0.016,-0.152 L 0.028,0 0,0 z' id='path1264'/>
</g>
<g transform='translate(42.16,71.94)' id='g1266'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 -0.657,-0.041 -0.657,3.221 C -0.657,4.647 -0.458,5.815 -0.223,5.815 L 2.809,5.815 8.002,5.815 11.03,5.815 C 11.27,5.815 11.46,4.647 11.46,3.221 L 11.46,0.012 C 9.658,0.105 7.779,0.164 5.835,0.164 3.82,0.164 1.863,0.105 0,0 M 15.79,-0.326 15.79,7.549 C 15.79,8.971 14.63,10.15 13.19,10.15 L -2.392,10.15 C -3.813,10.15 -4.982,8.971 -4.982,7.549 L -4.982,-0.416 C -12.69,-1.29 -17.96,-3.06 -17.96,-5.11 L -17.96,-9.003 C -17.96,-9.802 -17.15,-10.56 -15.7,-11.25 -11.9,-13.03 -3.686,-14.27 5.835,-14.27 15.36,-14.27 23.58,-13.03 27.38,-11.25 28.82,-10.56 29.64,-9.802 29.64,-9.003 L 29.64,-5.11 C 29.64,-2.988 23.96,-1.161 15.79,-0.326' id='path1268'/>
</g>
</g>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g transform='translate(83.05,50.93)' id='g1336'>
<path style='fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none' d='M 0,0 -32.12,0 -32.12,32.12 -37.98,32.12 -37.98,0 -70.1,0 -70.1,-5.861 -37.98,-5.861 -37.98,-37.98 -32.12,-37.98 -32.12,-5.861 0,-5.861 0,0 z' id='path1338'/>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 650 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 68.54,65.8 27.62,65.8 27.62,69.47 68.54,69.47 68.54,65.8 z M 68.54,56.09 27.62,56.09 27.62,59.76 68.54,59.76 68.54,56.09 z M 68.54,46.06 27.62,46.06 27.62,50.05 68.54,50.05 68.54,46.06 z M 68.38,36.35 27.46,36.35 27.46,40.18 68.38,40.18 68.38,36.35 z M 68.38,26.8 27.62,26.8 27.62,30.47 68.38,30.47 68.38,26.8 z M 61.6,80.19 21.74,80.19 21.74,15.81 74.26,15.81 74.26,80.19 61.6,80.19 z' id='path1422'/></g>
</svg>

After

Width:  |  Height:  |  Size: 839 B

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="120"
height="120"
xml:space="preserve"
id="svg2"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="down.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview7"
showgrid="false"
inkscape:zoom="1.9666667"
inkscape:cx="60"
inkscape:cy="60"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g
transform="translate(47.72,32.35)"
id="g676">
<path
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m -1.163978,-15.143535 -23.537465,32.092623 14.683237,0 0,29.964616 17.5617961,0 0,-29.964616 14.8298969,0 z"
id="path678"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g transform='translate(47.72,32.35)' id='g676'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 -16.37,22.32 -6.158,22.32 -6.158,43.16 6.056,43.16 6.056,22.32 16.37,22.32 0,0 z M -33.34,-5.839 -33.34,-11.85 33.9,-11.85 33.89,-5.839 -33.34,-5.839 z' id='path678'/>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 664 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g id='g1198' transform='translate(14.92,58.06)'>
<path id='path1200' style='fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none' d='M 0,0 0,-34.61 66.16,-34.61 66.16,0 33.08,-16.45 0,0 z'/>
</g><g id='g1202' transform='translate(14.92,72.55)'>
<path id='path1204' style='fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none' d='M 0,0 0,-9.209 33.08,-25.65 66.16,-9.209 66.16,0 0,0 z'/>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 766 B

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g id='g1388' transform='translate(66.77,29.35)'>
<path id='path1390' d='M 0,0 0,13.3 -4.16,13.3 -4.16,0 -17.26,0 -17.26,-4.162 -4.16,-4.162 -4.16,-17.46 0,-17.46 0,-4.162 13.11,-4.162 13.11,0 0,0 z' style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none'/>
</g><g id='g1392'>
<g id='g1394'>
<g id='g1400' transform='translate(33.31,41.18)'>
<path id='path1402' d='M 0,0 -2.713,2.713 16.38,21.81 19.09,19.09 0,0 z M -8.74,8.739 10.47,27.95 13.18,25.24 -6.027,6.024 -8.74,8.739 z M -10.61,-4.437 C -11.92,-4.437 -12.97,-3.381 -12.97,-2.08 -12.97,-0.778 -11.92,0.277 -10.61,0.277 -9.311,0.277 -8.256,-0.778 -8.256,-2.08 -8.256,-3.381 -9.311,-4.437 -10.61,-4.437 M 10.82,39.59 -17.15,11.85 -17.19,-8.752 3.41,-8.703 31.38,19.04 10.82,39.59 z' style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none'/>
</g>
<g id='g1404' transform='translate(32.95,84.1)'>
<path id='path1406' d='M 0,0 -12.49,-23.01 7.558,-3.594 0,0 z' style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none'/>
</g>
</g>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g id='g1522'>
<g id='g1524'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 84.01,25.99 72.01,25.99 72.01,21.99 84.01,21.99 84.01,25.99 z M 54,25.99 11.99,25.99 11.99,21.99 54,21.99 54,25.99 z M 84.01,50 42,50 42,46 84.01,46 84.01,50 z M 23.99,50 11.99,50 11.99,46 23.99,46 23.99,50 z M 84.01,74.01 72.01,74.01 72.01,70.01 84.01,70.01 84.01,74.01 z M 54,74.01 11.99,74.01 11.99,70.01 54,70.01 54,74.01 z M 62.12,85.01 C 59.31,85.01 57,82.71 57,79.89 L 57,64.12 C 57,61.3 59.31,59 62.12,59 L 63.89,59 C 66.71,59 69.01,61.3 69.01,64.12 L 69.01,79.89 C 69.01,82.71 66.71,85.01 63.89,85.01 L 62.12,85.01 z M 62.12,37 C 59.31,37 57,34.7 57,31.87 L 57,16.11 C 57,13.29 59.31,10.99 62.12,10.99 L 63.89,10.99 C 66.69,10.99 69.01,13.29 69.01,16.11 L 69.01,31.87 C 69.01,34.7 66.69,37 63.89,37 L 62.12,37 z M 32.12,61 C 29.3,61 26.99,58.7 26.99,55.88 L 26.99,40.12 C 26.99,37.3 29.3,35 32.12,35 L 33.88,35 C 36.69,35 39,37.3 39,40.12 L 39,55.88 C 39,58.7 36.69,61 33.88,61 L 32.12,61 z' id='path1530'/>
</g>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g id='g1424'>
<g id='g1426'>
<g transform='translate(38.5,25.12)' id='g1432'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 -3.633,3.633 21.93,29.2 25.56,25.57 0,0 z M -11.7,11.7 14.02,37.43 17.65,33.79 -8.072,8.067 -11.7,11.7 z M -14.21,-5.94 C -15.96,-5.94 -17.37,-4.528 -17.37,-2.783 -17.37,-1.042 -15.96,0.372 -14.21,0.372 -12.47,0.372 -11.06,-1.042 -11.06,-2.783 -11.06,-4.528 -12.47,-5.94 -14.21,-5.94 M 14.49,53.02 -22.96,15.87 -23.02,-11.72 4.564,-11.65 42.02,25.5 14.49,53.02 z' id='path1434'/>
</g>
<g transform='translate(38.02,82.6)' id='g1436'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 -16.73,-30.81 10.12,-4.812 0,0 z' id='path1438'/>
</g>
</g>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g transform='translate(79.95,67.35)' id='g764'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 -30.38,-17.54 -30.38,0 -63.89,-19.35 -30.38,-38.7 -30.38,-21.16 0,-38.7 0,0 z' id='path766'/>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 590 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g id='g1298'>
<g id='g1300'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 70.84,50.41 25.16,50.41 25.16,19.32 70.84,19.32 70.84,50.41 z M 80.88,67.78 80.88,16.74 C 80.88,15.66 80,14.78 78.92,14.78 L 17.08,14.78 C 16,14.78 15.12,15.66 15.12,16.74 L 15.12,79.26 C 15.12,80.34 16,81.22 17.08,81.22 L 25.16,81.22 25.16,57.47 60.8,57.47 60.8,81.22 67.45,81.22 80.88,67.78 z M 64.84,30.94 29.86,30.94 29.86,27.02 64.84,27.02 64.84,30.94 z M 64.84,41.95 29.86,41.95 29.86,38.02 64.84,38.02 64.84,41.95 z M 55.13,61.11 41.52,61.11 41.52,78.59 55.13,78.59 55.13,61.11 z' id='path1306'/>
</g>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 988 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g transform='translate(20.24,20.6)' id='g1090'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 5.287,24.92 38.6,27.24 38.6,27.57 5.287,29.89 0,54.81 55.52,27.4 0,0 z' id='path1092'/>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 584 B

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="120"
height="120"
xml:space="preserve"
id="svg2"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="up.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview7"
showgrid="false"
inkscape:zoom="1.9666667"
inkscape:cx="60.000001"
inkscape:cy="60.000001"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<g
id="g12"
transform="matrix(-1.25,0,0,1.25,116.39005,-0.58771313)">
<g
transform="translate(47.72,32.35)"
id="g676">
<path
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m -1.163978,-15.143535 -23.537465,32.092623 14.683237,0 0,29.964616 17.5617961,0 0,-29.964616 14.8298969,0 z"
id="path678"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1 @@
Application.mk

View file

@ -4,17 +4,17 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
COMMON_PATH=../../../common COMMON_PATH=../../../common
local_C_INCLUDES+= \ LOCAL_C_INCLUDES+= \
-I$(LOCAL_PATH)/$(COMMON_PATH) \ -I$(LOCAL_PATH)/$(COMMON_PATH) \
-I$(LOCAL_PATH)/../../../relay \ -I$(LOCAL_PATH)/../../../relay \
local_LDLIBS += -llog LOCAL_LDLIBS += -llog
ifeq ($(BUILD_TARGET),debug) ifeq ($(BUILD_TARGET),debug)
local_DEBUG = -DMEM_DEBUG -DDEBUG -DENABLE_LOGGING -DCOMMS_CHECKSUM -Wno-unused-but-set-variable LOCAL_DEBUG = -DMEM_DEBUG -DDEBUG -DENABLE_LOGGING -DCOMMS_CHECKSUM -Wno-unused-but-set-variable
endif endif
local_DEFINES += \ LOCAL_DEFINES += \
$(local_DEBUG) \ $(LOCAL_DEBUG) \
-DXWFEATURE_RELAY \ -DXWFEATURE_RELAY \
-DXWFEATURE_SMS \ -DXWFEATURE_SMS \
-DXWFEATURE_COMMSACK \ -DXWFEATURE_COMMSACK \
@ -37,17 +37,18 @@ local_DEFINES += \
-DHASH_STREAM \ -DHASH_STREAM \
-DXWFEATURE_BASE64 \ -DXWFEATURE_BASE64 \
-DXWFEATURE_DEVID \ -DXWFEATURE_DEVID \
-DCOMMON_LAYOUT \
-DINITIAL_CLIENT_VERS=${INITIAL_CLIENT_VERS} \ -DINITIAL_CLIENT_VERS=${INITIAL_CLIENT_VERS} \
-DRELAY_ROOM_DEFAULT=\"\" \ -DRELAY_ROOM_DEFAULT=\"\" \
-D__LITTLE_ENDIAN \ -D__LITTLE_ENDIAN \
ifeq ($(CHAT_ENABLED),true) ifeq ($(CHAT_ENABLED),true)
local_DEFINES += -DXWFEATURE_CHAT LOCAL_DEFINES += -DXWFEATURE_CHAT
endif endif
# -DXWFEATURE_SCOREONEPASS \ # -DXWFEATURE_SCOREONEPASS \
local_SRC_FILES += \ LOCAL_SRC_FILES += \
xwjni.c \ xwjni.c \
utilwrapper.c \ utilwrapper.c \
drawwrapper.c \ drawwrapper.c \
@ -58,7 +59,7 @@ local_SRC_FILES += \
COMMON_PATH=../../../common COMMON_PATH=../../../common
common_SRC_FILES += \ COMMON_SRC_FILES += \
$(COMMON_PATH)/boarddrw.c \ $(COMMON_PATH)/boarddrw.c \
$(COMMON_PATH)/scorebdp.c \ $(COMMON_PATH)/scorebdp.c \
$(COMMON_PATH)/dragdrpp.c \ $(COMMON_PATH)/dragdrpp.c \
@ -81,9 +82,12 @@ common_SRC_FILES += \
$(COMMON_PATH)/dbgutil.c \ $(COMMON_PATH)/dbgutil.c \
LOCAL_CFLAGS+=$(local_C_INCLUDES) $(local_DEFINES) -Wall LOCAL_CFLAGS+=$(LOCAL_C_INCLUDES) $(LOCAL_DEFINES)
LOCAL_SRC_FILES := $(linux_SRC_FILES) $(local_SRC_FILES) $(common_SRC_FILES) LOCAL_SRC_FILES := $(linux_SRC_FILES) $(LOCAL_SRC_FILES) $(COMMON_SRC_FILES)
LOCAL_MODULE := xwjni LOCAL_MODULE := xwjni
LOCAL_LDLIBS := -L${SYSROOT}/usr/lib -llog -lz LOCAL_LDLIBS := -L${SYSROOT}/usr/lib -llog -lz
include $(BUILD_SHARED_LIBRARY) include $(BUILD_SHARED_LIBRARY)
COMMON_SRC_FILES :=
COMMON_PATH :=

View file

@ -88,6 +88,30 @@ getInt( JNIEnv* env, jobject obj, const char* name )
return result; return result;
} }
void
getInts( JNIEnv* env, void* cobj, jobject jobj, const SetInfo* sis, XP_U16 nSis )
{
int ii;
for ( ii = 0; ii < nSis; ++ii ) {
const SetInfo* si = &sis[ii];
uint8_t* ptr = ((uint8_t*)cobj) + si->offset;
int val = getInt( env, jobj, si->name );
switch( si->siz ) {
case 4:
*(uint32_t*)ptr = val;
break;
case 2:
*(uint16_t*)ptr = val;
break;
case 1:
*ptr = val;
break;
}
/* XP_LOGF( "%s: wrote int %s of size %d with val %d at offset %d", */
/* __func__, si->name, si->siz, val, si->offset ); */
}
}
void void
setInt( JNIEnv* env, jobject obj, const char* name, int value ) setInt( JNIEnv* env, jobject obj, const char* name, int value )
{ {
@ -99,6 +123,34 @@ setInt( JNIEnv* env, jobject obj, const char* name, int value )
deleteLocalRef( env, cls ); deleteLocalRef( env, cls );
} }
void
setInts( JNIEnv* env, jobject jobj, void* cobj, const SetInfo* sis, XP_U16 nSis )
{
int ii;
for ( ii = 0; ii < nSis; ++ii ) {
const SetInfo* si = &sis[ii];
uint8_t* ptr = ((uint8_t*)cobj) + si->offset;
int val;
switch( si->siz ) {
case 4:
val = *(uint32_t*)ptr;
break;
case 2:
val = *(uint16_t*)ptr;
break;
case 1:
val = *ptr;
break;
default:
val = 0;
XP_ASSERT(0);
}
setInt( env, jobj, si->name, val );
/* XP_LOGF( "%s: read int %s of size %d with val %d from offset %d", */
/* __func__, si->name, si->siz, val, si->offset ); */
}
}
bool bool
setBool( JNIEnv* env, jobject obj, const char* name, bool value ) setBool( JNIEnv* env, jobject obj, const char* name, bool value )
{ {
@ -114,6 +166,19 @@ setBool( JNIEnv* env, jobject obj, const char* name, bool value )
return success; return success;
} }
void
setBools( JNIEnv* env, jobject jobj, void* cobj, const SetInfo* sis, XP_U16 nSis )
{
int ii;
for ( ii = 0; ii < nSis; ++ii ) {
const SetInfo* si = &sis[ii];
XP_Bool val = *(XP_Bool*)(((uint8_t*)cobj)+si->offset);
setBool( env, jobj, si->name, val );
/* XP_LOGF( "%s: read bool %s with val %d from offset %d", __func__, */
/* si->name, val, si->offset ); */
}
}
bool bool
setString( JNIEnv* env, jobject obj, const char* name, const XP_UCHAR* value ) setString( JNIEnv* env, jobject obj, const char* name, const XP_UCHAR* value )
{ {
@ -210,6 +275,19 @@ getBool( JNIEnv* env, jobject obj, const char* name )
return result; return result;
} }
void
getBools( JNIEnv* env, void* cobj, jobject jobj, const SetInfo* sis, XP_U16 nSis )
{
int ii;
for ( ii = 0; ii < nSis; ++ii ) {
const SetInfo* si = &sis[ii];
XP_Bool val = getBool( env, jobj, si->name );
*(XP_Bool*)(((uint8_t*)cobj)+si->offset) = val;
/* XP_LOGF( "%s: wrote bool %s with val %d at offset %d", __func__, */
/* si->name, val, si->offset ); */
}
}
jintArray jintArray
makeIntArray( JNIEnv *env, int siz, const jint* vals ) makeIntArray( JNIEnv *env, int siz, const jint* vals )
{ {
@ -275,13 +353,25 @@ getIntFromArray( JNIEnv* env, jintArray arr, bool del )
{ {
jint* ints = (*env)->GetIntArrayElements(env, arr, 0); jint* ints = (*env)->GetIntArrayElements(env, arr, 0);
int result = ints[0]; int result = ints[0];
(*env)->ReleaseIntArrayElements( env, arr, ints, 0); (*env)->ReleaseIntArrayElements( env, arr, ints, 0 );
if ( del ) { if ( del ) {
deleteLocalRef( env, arr ); deleteLocalRef( env, arr );
} }
return result; return result;
} }
void
setIntInArray( JNIEnv* env, jintArray arr, int index, int val )
{
jint* ints = (*env)->GetIntArrayElements( env, arr, 0 );
#ifdef DEBUG
jsize len = (*env)->GetArrayLength( env, arr );
XP_ASSERT( len > index );
#endif
ints[index] = val;
(*env)->ReleaseIntArrayElements( env, arr, ints, 0 );
}
jobjectArray jobjectArray
makeStringArray( JNIEnv *env, int siz, const XP_UCHAR** vals ) makeStringArray( JNIEnv *env, int siz, const XP_UCHAR** vals )
{ {

View file

@ -33,10 +33,28 @@
void and_send_on_close( XWStreamCtxt* stream, void* closure ); void and_send_on_close( XWStreamCtxt* stream, void* closure );
XWStreamCtxt* and_empty_stream( MPFORMAL AndGlobals* globals ); XWStreamCtxt* and_empty_stream( MPFORMAL AndGlobals* globals );
typedef struct _SetInfo {
const char* name;
int offset;
int siz;
} SetInfo;
#define ARR_MEMBER(obj, fld) { .name = #fld, \
.offset = OFFSET_OF(obj, fld), \
.siz = sizeof(((obj *)0)->fld) \
}
int getInt( JNIEnv* env, jobject obj, const char* name ); int getInt( JNIEnv* env, jobject obj, const char* name );
void setInt( JNIEnv* env, jobject obj, const char* name, int value ); void setInt( JNIEnv* env, jobject obj, const char* name, int value );
void setInts( JNIEnv* env, jobject jobj, void* cobj,
const SetInfo* sis, XP_U16 nSis );
void getInts( JNIEnv* env, void* cobj, jobject jobj,
const SetInfo* sis, XP_U16 nSis );
bool getBool( JNIEnv* env, jobject obj, const char* name ); bool getBool( JNIEnv* env, jobject obj, const char* name );
void getBools( JNIEnv* env, void* cobj, jobject jobj,
const SetInfo* sis, XP_U16 nSis );
bool setBool( JNIEnv* env, jobject obj, const char* name, bool value ); bool setBool( JNIEnv* env, jobject obj, const char* name, bool value );
void setBools( JNIEnv* env, jobject jobj, void* cobj,
const SetInfo* sis, XP_U16 nSis );
bool setString( JNIEnv* env, jobject obj, const char* name, const XP_UCHAR* value ); bool setString( JNIEnv* env, jobject obj, const char* name, const XP_UCHAR* value );
void getString( JNIEnv* env, jobject jlp, const char* name, XP_UCHAR* buf, void getString( JNIEnv* env, jobject jlp, const char* name, XP_UCHAR* buf,
int bufLen ); int bufLen );
@ -48,6 +66,7 @@ bool getObject( JNIEnv* env, jobject obj, const char* name, const char* sig,
jintArray makeIntArray( JNIEnv *env, int size, const jint* vals ); jintArray makeIntArray( JNIEnv *env, int size, const jint* vals );
int getIntFromArray( JNIEnv* env, jintArray arr, bool del ); int getIntFromArray( JNIEnv* env, jintArray arr, bool del );
void setIntInArray( JNIEnv* env, jintArray arr, int index, int val );
jbyteArray makeByteArray( JNIEnv* env, int size, const jbyte* vals ); jbyteArray makeByteArray( JNIEnv* env, int size, const jbyte* vals );

View file

@ -423,31 +423,34 @@ and_draw_trayBegin( DrawCtx* dctx, const XP_Rect* rect, XP_U16 owner,
return result; return result;
} }
static void static XP_Bool
and_draw_drawTile( DrawCtx* dctx, const XP_Rect* rect, const XP_UCHAR* text, and_draw_drawTile( DrawCtx* dctx, const XP_Rect* rect, const XP_UCHAR* text,
const XP_Bitmaps* bitmaps, XP_U16 val, CellFlags flags ) const XP_Bitmaps* bitmaps, XP_U16 val, CellFlags flags )
{ {
XP_Bool result;
DRAW_CBK_HEADER( "drawTile", DRAW_CBK_HEADER( "drawTile",
"(Landroid/graphics/Rect;Ljava/lang/String;II)V" ); "(Landroid/graphics/Rect;Ljava/lang/String;II)Z" );
jobject jrect = makeJRect( draw, JCACHE_RECT0, rect ); jobject jrect = makeJRect( draw, JCACHE_RECT0, rect );
jstring jtext = NULL; jstring jtext = NULL;
if ( !!text ) { if ( !!text ) {
jtext = (*env)->NewStringUTF( env, text ); jtext = (*env)->NewStringUTF( env, text );
} }
(*env)->CallVoidMethod( env, draw->jdraw, mid, result = (*env)->CallBooleanMethod( env, draw->jdraw, mid,
jrect, jtext, val, flags ); jrect, jtext, val, flags );
deleteLocalRef( env, jtext ); deleteLocalRef( env, jtext );
return result;
} }
static void static XP_Bool
and_draw_drawTileMidDrag( DrawCtx* dctx, const XP_Rect* rect, and_draw_drawTileMidDrag( DrawCtx* dctx, const XP_Rect* rect,
const XP_UCHAR* text, const XP_Bitmaps* bitmaps, const XP_UCHAR* text, const XP_Bitmaps* bitmaps,
XP_U16 val, XP_U16 owner, CellFlags flags ) XP_U16 val, XP_U16 owner, CellFlags flags )
{ {
XP_Bool result;
DRAW_CBK_HEADER( "drawTileMidDrag", DRAW_CBK_HEADER( "drawTileMidDrag",
"(Landroid/graphics/Rect;Ljava/lang/String;III)V" ); "(Landroid/graphics/Rect;Ljava/lang/String;III)Z" );
jobject jrect = makeJRect( draw, JCACHE_RECT0, rect ); jobject jrect = makeJRect( draw, JCACHE_RECT0, rect );
jstring jtext = NULL; jstring jtext = NULL;
@ -455,21 +458,21 @@ and_draw_drawTileMidDrag( DrawCtx* dctx, const XP_Rect* rect,
jtext = (*env)->NewStringUTF( env, text ); jtext = (*env)->NewStringUTF( env, text );
} }
(*env)->CallVoidMethod( env, draw->jdraw, mid, result = (*env)->CallBooleanMethod( env, draw->jdraw, mid,
jrect, jtext, val, owner, flags ); jrect, jtext, val, owner, flags );
deleteLocalRef( env, jtext ); deleteLocalRef( env, jtext );
return result;
} }
static void static XP_Bool
and_draw_drawTileBack( DrawCtx* dctx, const XP_Rect* rect, CellFlags flags ) and_draw_drawTileBack( DrawCtx* dctx, const XP_Rect* rect, CellFlags flags )
{ {
DRAW_CBK_HEADER( "drawTileBack", "(Landroid/graphics/Rect;I)V" ); DRAW_CBK_HEADER( "drawTileBack", "(Landroid/graphics/Rect;I)Z" );
jobject jrect = makeJRect( draw, JCACHE_RECT0, rect ); jobject jrect = makeJRect( draw, JCACHE_RECT0, rect );
(*env)->CallVoidMethod( env, draw->jdraw, mid, return (*env)->CallBooleanMethod( env, draw->jdraw, mid, jrect, flags );
jrect, flags );
} }
static void static void
@ -644,21 +647,23 @@ makeDraw( MPFORMAL JNIEnv** envp, jobject jdraw )
void void
destroyDraw( DrawCtx** dctx ) destroyDraw( DrawCtx** dctx )
{ {
AndDraw* draw = (AndDraw*)*dctx; if ( !!*dctx ) {
JNIEnv* env = *draw->env; AndDraw* draw = (AndDraw*)*dctx;
if ( NULL != draw->jdraw ) { JNIEnv* env = *draw->env;
(*env)->DeleteGlobalRef( env, draw->jdraw ); if ( NULL != draw->jdraw ) {
} (*env)->DeleteGlobalRef( env, draw->jdraw );
int ii;
for ( ii = 0; ii < JCACHE_COUNT; ++ii ) {
jobject jobj = draw->jCache[ii];
if ( !!jobj ) {
(*env)->DeleteGlobalRef( env, jobj );
} }
}
XP_FREE( draw->mpool, draw->vtable ); int ii;
XP_FREE( draw->mpool, draw ); for ( ii = 0; ii < JCACHE_COUNT; ++ii ) {
*dctx = NULL; jobject jobj = draw->jCache[ii];
if ( !!jobj ) {
(*env)->DeleteGlobalRef( env, jobj );
}
}
XP_FREE( draw->mpool, draw->vtable );
XP_FREE( draw->mpool, draw );
*dctx = NULL;
}
} }

View file

@ -39,47 +39,54 @@
#include "jniutlswrapper.h" #include "jniutlswrapper.h"
#include "paths.h" #include "paths.h"
static const SetInfo gi_ints[] = {
ARR_MEMBER( CurGameInfo, nPlayers )
,ARR_MEMBER( CurGameInfo, gameSeconds )
,ARR_MEMBER( CurGameInfo, boardSize )
,ARR_MEMBER( CurGameInfo, gameID )
,ARR_MEMBER( CurGameInfo, dictLang )
};
static const SetInfo gi_bools[] = {
ARR_MEMBER( CurGameInfo, hintsNotAllowed )
,ARR_MEMBER( CurGameInfo, timerEnabled )
,ARR_MEMBER( CurGameInfo, allowPickTiles )
,ARR_MEMBER( CurGameInfo, allowHintRect )
};
static CurGameInfo* static CurGameInfo*
makeGI( MPFORMAL JNIEnv* env, jobject j_gi ) makeGI( MPFORMAL JNIEnv* env, jobject jgi )
{ {
CurGameInfo* gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*gi) ); CurGameInfo* gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*gi) );
XP_UCHAR buf[256]; /* in case needs whole path */ XP_UCHAR buf[256]; /* in case needs whole path */
gi->nPlayers = getInt( env, j_gi, "nPlayers"); getInts( env, (void*)gi, jgi, gi_ints, VSIZE(gi_ints) );
gi->gameSeconds = getInt( env, j_gi, "gameSeconds"); getBools( env, (void*)gi, jgi, gi_bools, VSIZE(gi_bools) );
gi->boardSize = getInt( env, j_gi, "boardSize" );
/* Unlike on other platforms, gi is created without a call to /* Unlike on other platforms, gi is created without a call to
game_makeNewGame, which sets gameID. So check here if it's still unset game_makeNewGame, which sets gameID. So check here if it's still unset
and if necessary set it -- including back in the java world. */ and if necessary set it -- including back in the java world. */
gi->gameID = getInt( env, j_gi, "gameID" );
if ( 0 == gi->gameID ) { if ( 0 == gi->gameID ) {
while ( 0 == gi->gameID ) { while ( 0 == gi->gameID ) {
gi->gameID = getCurSeconds( env ); gi->gameID = getCurSeconds( env );
} }
setInt( env, j_gi, "gameID", gi->gameID ); setInt( env, jgi, "gameID", gi->gameID );
} }
gi->dictLang = getInt( env, j_gi, "dictLang" );
gi->hintsNotAllowed = getBool( env, j_gi, "hintsNotAllowed" );
gi->timerEnabled = getBool( env, j_gi, "timerEnabled" );
gi->allowPickTiles = getBool( env, j_gi, "allowPickTiles" );
gi->allowHintRect = getBool( env, j_gi, "allowHintRect" );
gi->phoniesAction = gi->phoniesAction =
jenumFieldToInt( env, j_gi, "phoniesAction", jenumFieldToInt( env, jgi, "phoniesAction",
PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") ); PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") );
gi->serverRole = gi->serverRole =
jenumFieldToInt( env, j_gi, "serverRole", jenumFieldToInt( env, jgi, "serverRole",
PKG_PATH("jni/CurGameInfo$DeviceRole")); PKG_PATH("jni/CurGameInfo$DeviceRole"));
getString( env, j_gi, "dictName", buf, VSIZE(buf) ); getString( env, jgi, "dictName", buf, VSIZE(buf) );
gi->dictName = copyString( mpool, buf ); gi->dictName = copyString( mpool, buf );
XP_ASSERT( gi->nPlayers <= MAX_NUM_PLAYERS ); XP_ASSERT( gi->nPlayers <= MAX_NUM_PLAYERS );
jobject jplayers; jobject jplayers;
if ( getObject( env, j_gi, "players", "[L" PKG_PATH("jni/LocalPlayer") ";", if ( getObject( env, jgi, "players", "[L" PKG_PATH("jni/LocalPlayer") ";",
&jplayers ) ) { &jplayers ) ) {
int ii; int ii;
for ( ii = 0; ii < gi->nPlayers; ++ii ) { for ( ii = 0; ii < gi->nPlayers; ++ii ) {
@ -114,14 +121,10 @@ static void
setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi ) setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi )
{ {
// set fields // set fields
setInt( env, jgi, "nPlayers", gi->nPlayers );
setInt( env, jgi, "gameSeconds", gi->gameSeconds ); setInts( env, jgi, (void*)gi, gi_ints, VSIZE(gi_ints) );
setInt( env, jgi, "boardSize", gi->boardSize ); setBools( env, jgi, (void*)gi, gi_bools, VSIZE(gi_bools) );
setInt( env, jgi, "gameID", gi->gameID );
setInt( env, jgi, "dictLang", gi->dictLang );
setBool( env, jgi, "hintsNotAllowed", gi->hintsNotAllowed );
setBool( env, jgi, "timerEnabled", gi->timerEnabled );
setBool( env, jgi, "allowPickTiles", gi->allowPickTiles );
setString( env, jgi, "dictName", gi->dictName ); setString( env, jgi, "dictName", gi->dictName );
intToJenumField( env, jgi, gi->phoniesAction, "phoniesAction", intToJenumField( env, jgi, gi->phoniesAction, "phoniesAction",
@ -155,6 +158,36 @@ setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi )
} }
} /* setJGI */ } /* setJGI */
#ifdef COMMON_LAYOUT
static const SetInfo bd_ints[] = {
ARR_MEMBER( BoardDims, left )
,ARR_MEMBER( BoardDims, top )
,ARR_MEMBER( BoardDims, width )
,ARR_MEMBER( BoardDims, height )
,ARR_MEMBER( BoardDims, scoreHt )
,ARR_MEMBER( BoardDims, scoreWidth )
,ARR_MEMBER( BoardDims, boardWidth )
,ARR_MEMBER( BoardDims, boardHt )
,ARR_MEMBER( BoardDims, trayTop )
,ARR_MEMBER( BoardDims, trayHt )
,ARR_MEMBER( BoardDims, cellSize )
,ARR_MEMBER( BoardDims, maxCellSize )
,ARR_MEMBER( BoardDims, timerWidth )
};
static void
dimsJToC( JNIEnv* env, BoardDims* out, jobject jdims )
{
getInts( env, (void*)out, jdims, bd_ints, VSIZE(bd_ints) );
}
static void
dimsCtoJ( JNIEnv* env, jobject jdims, const BoardDims* in )
{
setInts( env, jdims, (void*)in, bd_ints, VSIZE(bd_ints) );
}
#endif
static void static void
destroyGI( MPFORMAL CurGameInfo** gip ) destroyGI( MPFORMAL CurGameInfo** gip )
{ {
@ -417,7 +450,10 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
globals->util = makeUtil( MPPARM(mpool) &state->env, j_util, gi, globals->util = makeUtil( MPPARM(mpool) &state->env, j_util, gi,
globals ); globals );
globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu ); globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu );
DrawCtx* dctx = makeDraw( MPPARM(mpool) &state->env, j_draw ); DrawCtx* dctx = NULL;
if ( !!j_draw ) {
dctx = makeDraw( MPPARM(mpool) &state->env, j_draw );
}
globals->dctx = dctx; globals->dctx = dctx;
globals->xportProcs = makeXportProcs( MPPARM(mpool) &state->env, j_procs ); globals->xportProcs = makeXportProcs( MPPARM(mpool) &state->env, j_procs );
CommonPrefs cp; CommonPrefs cp;
@ -486,7 +522,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu ); globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu );
makeDicts( MPPARM(mpool) env, globals->jniutil, &dict, &dicts, jdictNames, makeDicts( MPPARM(mpool) env, globals->jniutil, &dict, &dicts, jdictNames,
jdicts, jpaths, jlang ); jdicts, jpaths, jlang );
globals->dctx = makeDraw( MPPARM(mpool) &state->env, jdraw ); if ( !!jdraw ) {
globals->dctx = makeDraw( MPPARM(mpool) &state->env, jdraw );
}
globals->xportProcs = makeXportProcs( MPPARM(mpool) &state->env, jprocs ); globals->xportProcs = makeXportProcs( MPPARM(mpool) &state->env, jprocs );
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
@ -560,6 +598,21 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveSucceeded
XWJNI_END(); XWJNI_END();
} }
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setDraw
( JNIEnv* env, jclass C, jint gamePtr, jobject jdraw )
{
XWJNI_START_GLOBALS();
DrawCtx* newDraw = makeDraw( MPPARM(mpool) &state->env, jdraw );
board_setDraw( state->game.board, newDraw );
destroyDraw( &globals->dctx );
globals->dctx = newDraw;
XWJNI_END();
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1invalAll Java_org_eehouse_android_xw4_jni_XwJNI_board_1invalAll
( JNIEnv *env, jclass C, jint gamePtr ) ( JNIEnv *env, jclass C, jint gamePtr )
@ -580,6 +633,45 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1draw
return result; return result;
} }
#ifdef COMMON_LAYOUT
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1figureLayout
( JNIEnv* env, jclass C, jint gamePtr, jobject jgi, jint left, jint top,
jint width, jint height, jint scorePct, jint trayPct, jint scoreWidth,
jint fontWidth, jint fontHt, jboolean squareTiles, jobject jdims )
{
LOG_FUNC();
XWJNI_START();
CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi );
BoardDims dims;
board_figureLayout( state->game.board, gi, left, top, width, height,
scorePct, trayPct, scoreWidth,
fontWidth, fontHt, squareTiles,
((!!jdims) ? &dims : NULL) );
destroyGI( MPPARM(mpool) &gi );
if ( !!jdims ) {
dimsCtoJ( env, jdims, &dims );
}
XWJNI_END();
LOG_RETURN_VOID();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1applyLayout
( JNIEnv* env, jclass C, jint gamePtr, jobject jdims )
{
XWJNI_START();
BoardDims dims;
dimsJToC( env, &dims, jdims );
board_applyLayout( state->game.board, &dims );
XWJNI_END();
}
#else
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setPos Java_org_eehouse_android_xw4_jni_XwJNI_board_1setPos
(JNIEnv *env, jclass C, jint gamePtr, jint left, jint top, jint width, (JNIEnv *env, jclass C, jint gamePtr, jint left, jint top, jint width,
@ -591,20 +683,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1setPos
XWJNI_END(); XWJNI_END();
} }
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1zoom
( JNIEnv* env, jclass C, jint gamePtr, jint zoomBy, jbooleanArray jCanZoom )
{
jboolean result;
XWJNI_START();
XP_Bool canInOut[2];
result = board_zoom( state->game.board, zoomBy, canInOut );
jboolean canZoom[2] = { canInOut[0], canInOut[1] };
setBoolArray( env, jCanZoom, VSIZE(canZoom), canZoom );
XWJNI_END();
return result;
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setScoreboardLoc Java_org_eehouse_android_xw4_jni_XwJNI_board_1setScoreboardLoc
( JNIEnv *env, jclass C, jint gamePtr, jint left, jint top, ( JNIEnv *env, jclass C, jint gamePtr, jint left, jint top,
@ -639,6 +717,46 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1setTrayLoc
minDividerWidth ); minDividerWidth );
XWJNI_END(); XWJNI_END();
} }
#endif
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1zoom
( JNIEnv* env, jclass C, jint gamePtr, jint zoomBy, jbooleanArray jCanZoom )
{
jboolean result;
XWJNI_START();
XP_Bool canInOut[2];
result = board_zoom( state->game.board, zoomBy, canInOut );
jboolean canZoom[2] = { canInOut[0], canInOut[1] };
setBoolArray( env, jCanZoom, VSIZE(canZoom), canZoom );
XWJNI_END();
return result;
}
#ifdef XWFEATURE_ACTIVERECT
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1getActiveRect
( JNIEnv* env, jclass C, jint gamePtr, jobject jrect, jintArray dims )
{
jboolean result;
XWJNI_START();
XP_Rect rect;
XP_U16 nCols, nRows;
result = board_getActiveRect( state->game.board, &rect, &nCols, &nRows );
if ( result ) {
setInt( env, jrect, "left", rect.left );
setInt( env, jrect, "top", rect.top );
setInt( env, jrect, "right", rect.left + rect.width );
setInt( env, jrect, "bottom", rect.top + rect.height );
if ( !!dims ) {
setIntInArray( env, dims, 0, nCols );
setIntInArray( env, dims, 1, nRows );
}
}
XWJNI_END();
return result;
}
#endif
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1handlePenDown Java_org_eehouse_android_xw4_jni_XwJNI_board_1handlePenDown
@ -1281,6 +1399,21 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1getGi
XWJNI_END(); XWJNI_END();
} }
static const SetInfo gsi_ints[] = {
ARR_MEMBER( GameStateInfo, visTileCount ),
ARR_MEMBER( GameStateInfo, trayVisState ),
};
static const SetInfo gsi_bools[] = {
ARR_MEMBER( GameStateInfo,canHint ),
ARR_MEMBER( GameStateInfo, canRedo ),
ARR_MEMBER( GameStateInfo, inTrade ),
ARR_MEMBER( GameStateInfo, tradeTilesSelected ),
ARR_MEMBER( GameStateInfo, canChat ),
ARR_MEMBER( GameStateInfo, canShuffle ),
ARR_MEMBER( GameStateInfo, curTurnSelected ),
ARR_MEMBER( GameStateInfo, canHideRack ),
};
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1getState Java_org_eehouse_android_xw4_jni_XwJNI_game_1getState
( JNIEnv* env, jclass C, jint gamePtr, jobject jgsi ) ( JNIEnv* env, jclass C, jint gamePtr, jobject jgsi )
@ -1289,15 +1422,8 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1getState
GameStateInfo info; GameStateInfo info;
game_getState( &state->game, &info ); game_getState( &state->game, &info );
setInt( env, jgsi, "visTileCount", info.visTileCount ); setInts( env, jgsi, (void*)&info, gsi_ints, VSIZE(gsi_ints) );
setInt( env, jgsi, "trayVisState", info.trayVisState ); setBools( env, jgsi, (void*)&info, gsi_bools, VSIZE(gsi_bools) );
setBool( env, jgsi, "canHint", info.canHint );
setBool( env, jgsi, "canRedo", info.canRedo);
setBool( env, jgsi, "inTrade", info.inTrade );
setBool( env, jgsi, "tradeTilesSelected", info.tradeTilesSelected );
setBool( env, jgsi, "canChat", info.canChat );
setBool( env, jgsi, "canShuffle", info.canShuffle );
setBool( env, jgsi, "curTurnSelected", info.curTurnSelected );
XWJNI_END(); XWJNI_END();
} }

View file

@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target. # Project target.
target=Google Inc.:Google APIs:11 target=Google Inc.:Google APIs:14

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -5,29 +5,6 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
> >
<LinearLayout android:id="@+id/empty_games_list"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/empty_games_list"
android:textAppearance="?android:attr/textAppearanceMedium"
android:background="#FF202020"
android:gravity="center"
android:paddingLeft="8dp"
android:paddingRight="8dp"
/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/empty_games_list2"
android:background="#FF202020"
android:gravity="center"
android:paddingLeft="8dp"
android:paddingRight="8dp"
/>
</LinearLayout>
<ExpandableListView android:id="@id/android:list" <ExpandableListView android:id="@id/android:list"
android:layout_width="fill_parent" android:layout_width="fill_parent"
@ -35,29 +12,4 @@
android:layout_weight="1" android:layout_weight="1"
android:drawSelectorOnTop="false" android:drawSelectorOnTop="false"
/> />
<TextView android:id="@+id/empty_list_msg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:gravity="center_vertical|center_horizontal"
android:text="@string/empty_list_msg"
/>
<LinearLayout android:id="@+id/new_buttons"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:paddingRight="8dp"
>
<Button android:id="@+id/new_game"
android:text="@string/button_new_game"
style="@style/spaced_buttons"
/>
<Button android:id="@+id/new_group"
android:text="@string/button_new_group"
style="@style/spaced_buttons"
/>
</LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- -*- compile-command: "cd ../../; ant install"; -*- --> <!-- -*- compile-command: "cd ../../; ant install"; -*- -->
<!-- top-level layout is hozontal, with an image and another layout --> <!-- top-level layout is hozontal, with an image and another layout -->
<org.eehouse.android.xw4.GameListItem <org.eehouse.android.xw4.GameListItem
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
@ -12,7 +11,6 @@
android:focusable="true" android:focusable="true"
android:clickable="true" android:clickable="true"
android:background="@android:drawable/list_selector_background" android:background="@android:drawable/list_selector_background"
android:paddingLeft="12dp"
> >
<TextView android:id="@+id/view_unloaded" <TextView android:id="@+id/view_unloaded"
@ -36,11 +34,18 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_gravity="center_vertical|center_horizontal" android:layout_gravity="center_vertical|center_horizontal"
android:layout_weight="0"
android:paddingLeft="8dip" android:paddingLeft="8dip"
android:paddingRight="8dip" android:paddingRight="8dip"
/> />
<ImageView android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:padding="2dip"
android:background="#FF7F7F7F"
/>
<!-- this layout is vertical, holds everything but the status <!-- this layout is vertical, holds everything but the status
icon[s] (plural later) --> icon[s] (plural later) -->
<LinearLayout android:orientation="vertical" <LinearLayout android:orientation="vertical"

View file

@ -88,7 +88,7 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
> >
<Button android:id="@+id/newgame_invite" <Button android:id="@+id/newgame_invite_net"
android:text="@string/newgame_invite" android:text="@string/newgame_invite"
style="@style/spaced_buttons" style="@style/spaced_buttons"
/> />

View file

@ -70,7 +70,7 @@
android:title="@string/board_menu_game_left" /> android:title="@string/board_menu_game_left" />
<item android:id="@+id/board_menu_game_history" <item android:id="@+id/board_menu_game_history"
android:title="@string/board_menu_game_history" /> android:title="@string/board_menu_game_history" />
<item android:id="@+id/board_menu_game_final" <item android:id="@+id/board_menu_game_resign"
android:title="@string/board_menu_game_final" /> android:title="@string/board_menu_game_final" />
<item android:id="@+id/board_menu_game_resend" <item android:id="@+id/board_menu_game_resend"
android:title="@string/board_menu_game_resend" /> android:title="@string/board_menu_game_resend" />

View file

@ -4,16 +4,37 @@
<group android:id="@+id/group_done"> <group android:id="@+id/group_done">
<!-- title set in BoardActivity --> <!-- title set in BoardActivity -->
<item android:id="@+id/board_menu_invite"
android:title="@string/board_menu_invite"
android:showAsAction="ifRoom"
android:icon="@drawable/send__gen"
/>
<item android:id="@+id/board_menu_done" <item android:id="@+id/board_menu_done"
android:alphabeticShortcut="D" android:alphabeticShortcut="D"
android:showAsAction="ifRoom" android:showAsAction="ifRoom"
android:icon="@drawable/save__gen"
/> />
<item android:id="@+id/board_menu_trade" <item android:id="@+id/board_menu_trade"
android:title="@string/board_menu_trade" android:title="@string/board_menu_trade"
android:alphabeticShortcut="T" android:alphabeticShortcut="T"
android:showAsAction="ifRoom"
/> />
</group> </group>
<item android:id="@+id/board_menu_trade_cancel"
android:title="@string/button_trade_cancel"
android:showAsAction="ifRoom"
android:icon="@drawable/back__gen"
/>
<item android:id="@+id/board_menu_trade_commit"
android:title="@string/button_trade_commit"
android:showAsAction="ifRoom"
android:icon="@drawable/save__gen"
/>
<item android:id="@+id/board_menu_undo_last" <item android:id="@+id/board_menu_undo_last"
android:title="@string/board_menu_undo_last" android:title="@string/board_menu_undo_last"
android:alphabeticShortcut="U" android:alphabeticShortcut="U"
@ -22,8 +43,7 @@
<item android:id="@+id/board_menu_tray" <item android:id="@+id/board_menu_tray"
/> />
<item android:id="@+id/board_submenu_game" <item android:title="@string/board_submenu_game">
android:title="@string/board_submenu_game">
<menu> <menu>
<item android:id="@+id/board_menu_game_counts" <item android:id="@+id/board_menu_game_counts"
android:title="@string/board_menu_game_counts" /> android:title="@string/board_menu_game_counts" />

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/dicts_item_move"
android:title="@string/dicts_item_move"
/>
<item android:id="@+id/dicts_item_select"
android:title="@string/dicts_item_select"
/>
</menu>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/dicts_download"
android:title="@string/download_dicts"
android:icon="@drawable/download__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/dicts_delete"
android:title="@string/list_item_delete"
android:icon="@drawable/content_discard__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/dicts_move"
android:title="@string/dicts_item_move"
/>
<item android:id="@+id/dicts_select"
android:title="@string/dicts_item_select"
/>
</menu>

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/list_group_default"
android:title="@string/list_group_default"
/>
<item android:id="@+id/list_group_rename"
android:title="@string/list_group_rename"
/>
<item android:id="@+id/list_group_moveup"
android:title="@string/list_group_moveup"
/>
<item android:id="@+id/list_group_movedown"
android:title="@string/list_group_movedown"
/>
<item android:id="@+id/list_group_delete"
android:title="@string/list_group_delete"
/>
</menu>

View file

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/list_item_config"
android:title="@string/list_item_config"
/>
<item android:id="@+id/list_item_rename"
android:title="@string/list_item_rename"
/>
<item android:id="@+id/list_item_move"
android:title="@string/list_item_move"
/>
<item android:id="@+id/list_item_delete"
android:title="@string/list_item_delete"
/>
<item android:id="@+id/list_item_reset"
android:title="@string/list_item_reset"
/>
<item android:id="@+id/list_item_new_from"
android:title="@string/list_item_new_from"
/>
<item android:id="@+id/list_item_copy"
android:title="@string/list_item_copy"
/>
<!-- <item android:id="@+id/list_item_hide" -->
<!-- android:title="@string/list_item_hide" -->
<!-- /> -->
<!-- <item android:id="@+id/list_item_move_up" -->
<!-- android:title="@string/list_item_move_up" -->
<!-- /> -->
<!-- <item android:id="@+id/list_item_move_down" -->
<!-- android:title="@string/list_item_move_down" -->
<!-- /> -->
<!-- <item android:id="@+id/list_item_move_to_top" -->
<!-- android:title="@string/list_item_move_to_top" -->
<!-- /> -->
<!-- <item android:id="@+id/list_item_move_to_bottom" -->
<!-- android:title="@string/list_item_move_to_bottom" -->
<!-- /> -->
</menu>

View file

@ -1,52 +1,103 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/gamel_menu_newgame" <item android:id="@+id/games_menu_newgame"
android:title="@string/button_new_game" android:title="@string/button_new_game"
android:icon="@drawable/content_new" android:icon="@drawable/content_new__gen"
android:showAsAction="ifRoom" android:showAsAction="ifRoom"
/> />
<item android:id="@+id/gamel_menu_newgroup" <item android:id="@+id/games_menu_newgroup"
android:title="@string/button_new_group" android:title="@string/button_new_group"
android:icon="@android:drawable/ic_menu_add" android:icon="@drawable/new_group__gen"
/>
<item android:id="@+id/gamel_menu_delete"
android:title="@string/list_item_delete"
android:icon="@drawable/ic_action_delete"
android:showAsAction="ifRoom" android:showAsAction="ifRoom"
/> />
<item android:id="@+id/gamel_menu_prefs" <item android:id="@+id/games_menu_prefs"
android:title="@string/menu_prefs" android:title="@string/menu_prefs"
android:icon="@android:drawable/ic_menu_preferences" android:icon="@drawable/prefs__gen"
android:showAsAction="ifRoom"
/> />
<item android:id="@+id/gamel_menu_dicts" <item android:id="@+id/games_menu_dicts"
android:title="@string/gamel_menu_dicts" android:title="@string/gamel_menu_dicts"
android:icon="@drawable/dicticon" android:icon="@drawable/dict__gen"
android:showAsAction="ifRoom"
/> />
<item android:id="@+id/gamel_menu_about" <item android:id="@+id/games_menu_email"
android:title="@string/board_menu_file_email"
android:icon="@drawable/email__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_menu_about"
android:title="@string/board_menu_file_about" android:title="@string/board_menu_file_about"
android:icon="@android:drawable/ic_menu_info_details" android:icon="@android:drawable/ic_menu_info_details"
/> />
<item android:id="@+id/games_menu_checkmoves"
<item android:id="@+id/gamel_menu_email"
android:title="@string/board_menu_file_email"
android:icon="@android:drawable/ic_menu_send"
/>
<item android:id="@+id/gamel_menu_checkmoves"
android:title="@string/gamel_menu_checkmoves" android:title="@string/gamel_menu_checkmoves"
android:icon="@drawable/stat_notify_sync" android:icon="@drawable/stat_notify_sync"
/> />
<!-- Game items -->
<item android:id="@+id/games_game_delete"
android:title="@string/list_item_delete"
android:icon="@drawable/content_discard__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_game_config"
android:title="@string/list_item_config"
android:icon="@drawable/content_edit"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_game_move"
android:title="@string/list_item_move"
android:icon="@drawable/relabel__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_game_reset"
android:title="@string/list_item_reset"
android:icon="@drawable/reset__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_game_new_from"
android:title="@string/list_item_new_from"
/>
<item android:id="@+id/games_game_rename"
android:title="@string/list_item_rename"
/>
<item android:id="@+id/games_game_copy"
android:title="@string/list_item_copy"
/>
<!-- Group items -->
<item android:id="@+id/games_group_moveup"
android:title="@string/list_group_moveup"
android:icon="@drawable/up__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_group_movedown"
android:title="@string/list_group_movedown"
android:icon="@drawable/down__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_group_delete"
android:title="@string/list_group_delete"
android:icon="@drawable/content_discard__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/games_group_default"
android:title="@string/list_group_default"
/>
<item android:id="@+id/games_group_rename"
android:title="@string/list_group_rename"
/>
<!-- Debug only menuitems --> <!-- Debug only menuitems -->
<item android:id="@+id/gamel_menu_checkupdates" <item android:id="@+id/games_menu_checkupdates"
android:title="@string/gamel_menu_checkupdates" android:title="@string/gamel_menu_checkupdates"
/> />
<item android:id="@+id/gamel_menu_storedb" <item android:id="@+id/games_menu_storedb"
android:title="@string/gamel_menu_storedb" android:title="@string/gamel_menu_storedb"
/> />
<item android:id="@+id/gamel_menu_loaddb" <item android:id="@+id/games_menu_loaddb"
android:title="@string/gamel_menu_loaddb" android:title="@string/gamel_menu_loaddb"
/> />
<!-- ic_menu_archive.png -->
</menu> </menu>

View file

@ -5,26 +5,24 @@
</style> </style>
</head> </head>
<body> <body>
<b>Crosswords 4.4 beta 69 release</b> <b>Crosswords 4.4 beta 72 release</b>
<p>This release features a major upgrade to how the app communicates <p>This release is for the new x86-processor-based Android devices
with the relay, in essence switching from each game communicating just coming out. Otherwise identical to beta 71, it adds support for
individually (and only when open) to the app communicating on behalf x86 processors to existing support for ARM (which has been
of all games. You should notice only that moves are transmitted 99.9&#37; of the market until now.)</p>
more reliably. But this being new code you may also notice bugs.
If you don't <a href="mailto:xwords@eehouse.org">tell me about <p>Thanks to Brendan Le Foll for the patches!</p>
them</a> they will not get fixed.
</p>
<h3>New with this release</h3> <h3>New with this release</h3>
<ul> <ul>
<li>Fix relay registration problem (Thanks DS!)</li> <li>Add support for x86 processors.
<li>Show Turn done/Pass in actionbar where possible</li>
</ul> </ul>
<h3>Next up</h3> <h3>Next up</h3>
<ul> <ul>
<li>Take advantage of newer Android UI features, e.g. Action Bar</li> <li>Add thumbnail of board to main screen</li>
<li>Use Actionbar and select/act pattern on more screens</li>
<li>Offer "Rematch" when game's over</li> <li>Offer "Rematch" when game's over</li>
</ul> </ul>

View file

@ -59,9 +59,6 @@
<string name="board_menu_game_resend">Reenvia els missatges</string> <string name="board_menu_game_resend">Reenvia els missatges</string>
<string name="board_menu_file_about">Quant al Crosswords</string> <string name="board_menu_file_about">Quant al Crosswords</string>
<string name="gamel_menu_delete_all">Suprimeix-les totes</string>
<!-- <string name="gamel_menu_view_hidden">Partides amagades</string> -->
<!-- returned by util_getUserString --> <!-- returned by util_getUserString -->
<string name="strd_robot_traded">El robot ha canviat %d fitxes en aquest torn.</string> <string name="strd_robot_traded">El robot ha canviat %d fitxes en aquest torn.</string>
<string name="str_robot_moved">El robot ha fet aquesta jugada:</string> <string name="str_robot_moved">El robot ha fet aquesta jugada:</string>

View file

@ -58,9 +58,6 @@
<string name="board_menu_game_resend">Přeposlat zprávy</string> <string name="board_menu_game_resend">Přeposlat zprávy</string>
<string name="board_menu_file_about">O Crosswords</string> <string name="board_menu_file_about">O Crosswords</string>
<string name="gamel_menu_delete_all">Vymazat vše</string>
<!-- <string name="gamel_menu_view_hidden">Hidden games</string> -->
<!-- returned by util_getUserString --> <!-- returned by util_getUserString -->
<string name="strd_robot_traded">Robot v tomto tahu vyměnil %d kamenů.</string> <string name="strd_robot_traded">Robot v tomto tahu vyměnil %d kamenů.</string>
<string name="str_robot_moved">Robot provedl tento tah:</string> <string name="str_robot_moved">Robot provedl tento tah:</string>

View file

@ -13,15 +13,6 @@
# This is the first screen you see when you launch Crosswords # This is the first screen you see when you launch Crosswords
############################################################ ############################################################
--> -->
<!-- These two messages appear at the top of the list of games
(unless the hide_intro preferences checkbox is checked.)-->
<string name="empty_games_list">Use o botão abaixo para criar um novo
jogo. Selecione um jogo existente para continuá-lo ou dê um toque
longo para outras opções.</string>
<!-- -->
<string name="empty_games_list2">Você pode esconder essa mensagem
e os botões abaixo na seção Aparência das Configurações (acessada
através do botão menu do seu dispositivo.)</string>
<!-- Text of button at bottom of main games-list screen and of <!-- Text of button at bottom of main games-list screen and of
menuitem in main games-list screen's menu. (The botton can menuitem in main games-list screen's menu. (The botton can
@ -29,12 +20,6 @@
<string name="button_new_game">Novo jogo</string> <string name="button_new_game">Novo jogo</string>
<string name="button_new_group">Novo grupo</string> <string name="button_new_group">Novo grupo</string>
<!-- When the game list is empty and the above messages and button
are hidden via preferences, this text is shown -->
<string name="empty_list_msg">Use a tecla menu para adicionar um
jogo.</string>
<!-- Used to format game name plus some other information as the <!-- Used to format game name plus some other information as the
one-line summary for each game in the main screen. The name one-line summary for each game in the main screen. The name
of the game is substituted for %1$s. Something else of the game is substituted for %1$s. Something else
@ -122,9 +107,6 @@
<string name="no_games_to_refresh">Nenhum jogo encontrado que conecta <string name="no_games_to_refresh">Nenhum jogo encontrado que conecta
pelo servidor.</string> pelo servidor.</string>
<!-- Deletes all games on the device (after confirmation) -->
<string name="gamel_menu_delete_all">Excluir todos</string>
<!-- Brings up "About Crosswords" dialog --> <!-- Brings up "About Crosswords" dialog -->
<string name="board_menu_file_about">Sobre Crosswords</string> <string name="board_menu_file_about">Sobre Crosswords</string>
@ -175,11 +157,6 @@
list_item_rename) --> list_item_rename) -->
<string name="rename_label">Mudar o nome desse jogo para:</string> <string name="rename_label">Mudar o nome desse jogo para:</string>
<!-- Text of confirmation dialog posted when list_item_delete menu is
selected -->
<string name="confirm_delete">Você tem certeza que quer excluir
esse jogo?</string>
<!-- Text of confirmation dialog posted when list_item_reset menu <!-- Text of confirmation dialog posted when list_item_reset menu
is selected --> is selected -->
<string name="confirm_reset">Você tem certeza que quer reiniciar <string name="confirm_reset">Você tem certeza que quer reiniciar
@ -254,9 +231,9 @@
wordlist delete confiration dialog in the case where the wordlist delete confiration dialog in the case where the
wordlist to be deleted is the last in its language. The name wordlist to be deleted is the last in its language. The name
of the language is substituted for %s. --> of the language is substituted for %s. -->
<string name="confirm_deleteonly_dictf">\u0020É a única lista de <!-- <string name="confirm_deleteonly_dictf">\u0020É a única lista de -->
palavras em %s instalada. Um ou mais jogos não poderão ser abertos <!-- palavras em %s instalada. Um ou mais jogos não poderão ser abertos -->
sem ela.</string> <!-- sem ela.</string> -->
<!-- Additional text appended to text confirm_delete_dictf in the <!-- Additional text appended to text confirm_delete_dictf in the
wordlist delete confiration dialog in the case where the wordlist delete confiration dialog in the case where the
wordlist to be deleted is NOT the last in its language. The wordlist to be deleted is NOT the last in its language. The
@ -275,9 +252,9 @@
<!-- Used as the text fo the confirming/querying dialog that goes <!-- Used as the text fo the confirming/querying dialog that goes
up when the dicts_item_select menuitem is chosen. The up when the dicts_item_select menuitem is chosen. The
possible answers are the three button text strings below. --> possible answers are the three button text strings below. -->
<string name="set_default_messagef">Para que jogadores essa lista <!-- <string name="set_default_messagef">Para que jogadores essa lista -->
de palavras deve ser a padrão para novos jogos? (o idioma %s será <!-- de palavras deve ser a padrão para novos jogos? (o idioma %s será -->
padrão para ambos.)</string> <!-- padrão para ambos.)</string> -->
<!-- These three strings are the text for three buttons giving <!-- These three strings are the text for three buttons giving
choices in respose to the dialog launched in response to the choices in respose to the dialog launched in response to the
@ -931,14 +908,6 @@
etc. --> etc. -->
<string name="game_summary_field_state">Estado do jogo</string> <string name="game_summary_field_state">Estado do jogo</string>
<!-- The main screen, by default, has a "add game" button and some
text at the top that take up space and are unnecessary for
experienced users. This checkbox hides them. -->
<string name="hide_intro">Esconder dicas da tela principal</string>
<!-- clarification of above -->
<string name="hide_intro_summary">São úteis somente quando você
está começando...</string>
<!-- Checkbox that when set prevents showing a title bar in the <!-- Checkbox that when set prevents showing a title bar in the
game board window to save space --> game board window to save space -->
<string name="hide_title">Esconder barra de título</string> <string name="hide_title">Esconder barra de título</string>
@ -2156,7 +2125,6 @@
lista de palavras?</string> lista de palavras?</string>
<string name="button_decline">Recusar</string> <string name="button_decline">Recusar</string>
<string name="downloadingf">Baixando %s...</string>
<string name="download_done">Download concluído</string> <string name="download_done">Download concluído</string>
<string name="download_failed">Download falhou</string> <string name="download_failed">Download falhou</string>
@ -2184,9 +2152,6 @@
<string name="rename_group_label">Mudar o nome desse grupo para:</string> <string name="rename_group_label">Mudar o nome desse grupo para:</string>
<string name="game_name_group_title">Nomear grupo</string> <string name="game_name_group_title">Nomear grupo</string>
<string name="cannot_delete_default_group">O grupo para novos jogos
não pode ser apagado."</string>
<string name="no_move_onegroup">Não é possível mover até que exista <string name="no_move_onegroup">Não é possível mover até que exista
mais de um grupo.</string> mais de um grupo.</string>
<string name="group_namef">%1$s (%2$d jogos)</string> <string name="group_namef">%1$s (%2$d jogos)</string>
@ -2199,8 +2164,6 @@
<string name="square_tiles">Peças quadradas</string> <string name="square_tiles">Peças quadradas</string>
<string name="square_tiles_summary">Mesmo se puderem ser mais altas</string> <string name="square_tiles_summary">Mesmo se puderem ser mais altas</string>
<string name="change_groupf">Mover jogo %s</string>
<string name="show_wordlist_browser">Listas de palavras</string> <string name="show_wordlist_browser">Listas de palavras</string>
</resources> </resources>

View file

@ -57,9 +57,6 @@
<string name="board_menu_game_resend">Preposlať správy</string> <string name="board_menu_game_resend">Preposlať správy</string>
<string name="board_menu_file_about">O Crosswords</string> <string name="board_menu_file_about">O Crosswords</string>
<string name="gamel_menu_delete_all">Vymazať všetko</string>
<!-- <string name="gamel_menu_view_hidden">Hidden games</string> -->
<!-- returned by util_getUserString --> <!-- returned by util_getUserString -->
<string name="strd_robot_traded">Robot v tomto ťahu vymenil %d kameňov.</string> <string name="strd_robot_traded">Robot v tomto ťahu vymenil %d kameňov.</string>
<string name="str_robot_moved">Robot uskutočnil tento ťah:</string> <string name="str_robot_moved">Robot uskutočnil tento ťah:</string>

View file

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

View file

@ -56,8 +56,10 @@
<string name="key_notify_sound">key_notify_sound</string> <string name="key_notify_sound">key_notify_sound</string>
<string name="key_notify_vibrate">key_notify_vibrate</string> <string name="key_notify_vibrate">key_notify_vibrate</string>
<string name="key_enable_sms">key_enable_sms</string> <string name="key_enable_sms">key_enable_sms</string>
<string name="key_hide_intro">key_hide_intro</string>
<string name="key_keep_screenon">key_keep_screenon</string> <string name="key_keep_screenon">key_keep_screenon</string>
<string name="key_thumbsize">key_thumbsize2</string>
<string name="key_thumb_enabled">key_thumb_disabled</string>
<string name="key_summary_field">key_summary_field</string> <string name="key_summary_field">key_summary_field</string>
<string name="key_default_loc">key_default_loc</string> <string name="key_default_loc">key_default_loc</string>
@ -80,6 +82,8 @@
<string name="key_udp_interval">key_udp_interval</string> <string name="key_udp_interval">key_udp_interval</string>
<string name="key_notagain_sync">key_notagain_sync</string> <string name="key_notagain_sync">key_notagain_sync</string>
<string name="key_notagain_newselect">key_notagain_newselect</string>
<string name="key_notagain_backclears">key_notagain_backclears</string>
<string name="key_notagain_chat">key_notagain_chat</string> <string name="key_notagain_chat">key_notagain_chat</string>
<string name="key_notagain_relay">key_notagain_relay</string> <string name="key_notagain_relay">key_notagain_relay</string>
<string name="key_notagain_hintprev">key_notagain_hintprev</string> <string name="key_notagain_hintprev">key_notagain_hintprev</string>
@ -102,6 +106,7 @@
<string name="key_na_browseall">key_na_browseall</string> <string name="key_na_browseall">key_na_browseall</string>
<string name="key_na_values">key_na_values</string> <string name="key_na_values">key_na_values</string>
<string name="key_enable_debug">key_enable_debug</string> <string name="key_enable_debug">key_enable_debug</string>
<string name="key_enable_dup_invite">key_enable_dup_invite</string>
<string name="key_download_path">key_download_path</string> <string name="key_download_path">key_download_path</string>
<!-- Nor is my email address --> <!-- Nor is my email address -->
@ -109,10 +114,12 @@
<!-- other --> <!-- other -->
<string name="default_host">eehouse.org</string> <string name="default_host">eehouse.org</string>
<string name="xwords_nfc_mime">application/org.eehouse.android.xw4</string>
<string name="invite_host">eehouse.org</string> <string name="invite_host">eehouse.org</string>
<string name="invite_prefix">/and/</string> <string name="invite_prefix">/and/</string>
<string name="invite_mime">application/x-xwordsinvite</string> <string name="invite_mime">application/x-xwordsinvite</string>
<string name="game_summary_field_rowid">rowid</string>
<string name="game_summary_field_gameid">gameid</string>
<!--string name="invite_mime">text/plain</string--> <!--string name="invite_mime">text/plain</string-->
<string name="dict_url">http://eehouse.org/and_wordlists</string> <string name="dict_url">http://eehouse.org/and_wordlists</string>
@ -126,12 +133,26 @@
<string name="redir_host">Invite redirect host</string> <string name="redir_host">Invite redirect host</string>
<string name="dict_host">Wordlist download URL</string> <string name="dict_host">Wordlist download URL</string>
<string name="logging_on">Enable logging</string> <string name="logging_on">Enable logging</string>
<string name="logging_on_summary">(release builds only)</string>
<string name="debug_features">Enable debug features</string>
<string name="debug_features_summary">Menuitems etc. (release builds
only)</string>
<string name="git_rev_title">Source version id</string> <string name="git_rev_title">Source version id</string>
<string name="relay_port">Relay game port</string> <string name="relay_port">Relay game port</string>
<string name="proxy_port">Relay device port</string> <string name="proxy_port">Relay device port</string>
<string name="name_dict_fmt">%1$s/%2$s</string> <string name="name_dict_fmt">%1$s/%2$s</string>
<string name="gamel_menu_storedb">Write DB to SD card</string> <string name="gamel_menu_storedb">Write DB to SD card</string>
<string name="gamel_menu_loaddb">Load DB from SD card</string> <string name="gamel_menu_loaddb">Load DB from SD card</string>
<string name="enable_dupes_title">Accept duplicate invites</string>
<string name="enable_dupes_summary">Accept invitations more than once</string>
<string name="game_thumb_half">1/2</string>
<string name="game_thumb_third">1/3</string>
<string name="game_thumb_quarter">1/4</string>
<string name="game_thumb_fifth">1/5</string>
<string name="game_thumb_sixth">1/6</string>
<!--string name="dict_url">http://10.0.2.2/~eehouse/and_dicts</string--> <!--string name="dict_url">http://10.0.2.2/~eehouse/and_dicts</string-->
@ -238,7 +259,7 @@
<item>http://dictionary.com/browse/%2$s</item> --> <item>http://dictionary.com/browse/%2$s</item> -->
<!-- --> <!-- -->
<item>TheFreeDictionary.com</item> <item>TheFreeDictionary.com</item>
<item>:en:es:</item> <item>:en:es:de:</item>
<item>http://%1$s.thefreedictionary.com/_/dict.aspx?word=%2$s</item> <item>http://%1$s.thefreedictionary.com/_/dict.aspx?word=%2$s</item>
<!-- --> <!-- -->
<item>Larousse.com</item> <item>Larousse.com</item>
@ -279,4 +300,12 @@
<item>@string/confirm_sms_willpay</item> <item>@string/confirm_sms_willpay</item>
</string-array> </string-array>
<string-array name="game_thumb_values">
<item>@string/game_thumb_half</item>
<item>@string/game_thumb_third</item>
<item>@string/game_thumb_quarter</item>
<item>@string/game_thumb_fifth</item>
<item>@string/game_thumb_sixth</item>
</string-array>
</resources> </resources>

View file

@ -13,28 +13,11 @@
# This is the first screen you see when you launch Crosswords # This is the first screen you see when you launch Crosswords
############################################################ ############################################################
--> -->
<!-- These two messages appear at the top of the list of games
(unless the hide_intro preferences checkbox is checked.)-->
<string name="empty_games_list">Use the button below to create a
game. Tap an existing game to play it or long-tap it for other
options.</string>
<!-- -->
<string name="empty_games_list2">You can hide this and the buttons
below in the Appearance section of Settings (accessed via your
device\'s menu key.)</string>
<!-- Text of button at bottom of main games-list screen and of <!-- Text of menuitem in main games-list screen's menu -->
menuitem in main games-list screen's menu. (The botton can
be hidden in the same way as the above text.) -->
<string name="button_new_game">Add game</string> <string name="button_new_game">Add game</string>
<string name="button_new_group">Add group</string> <string name="button_new_group">Add group</string>
<!-- When the game list is empty and the above messages and button
are hidden via preferences, this text is shown -->
<string name="empty_list_msg">Use the menu key to add a
game.</string>
<!-- Used to format game name plus some other information as the <!-- Used to format game name plus some other information as the
one-line summary for each game in the main screen. The name one-line summary for each game in the main screen. The name
of the game is substituted for %1$s. Something else of the game is substituted for %1$s. Something else
@ -51,7 +34,7 @@
they aren't available yet because the connection is not they aren't available yet because the connection is not
complete. Displayed in the lists of players found in each complete. Displayed in the lists of players found in each
game listing. --> game listing. -->
<string name="missing_player">(not here yet...)</string> <string name="missing_player">(not here yet)</string>
<!-- The display of each networked game includes one of three <!-- The display of each networked game includes one of three
states it can be in in the process of connecting to the states it can be in in the process of connecting to the
@ -102,10 +85,10 @@
--> -->
<!-- Brings up the Wordlists (formerly Dictionaries) screen --> <!-- Brings up the Wordlists (formerly Dictionaries) screen -->
<string name="gamel_menu_dicts">Wordlists</string> <string name="gamel_menu_dicts">Wordlists</string>
<!-- text of menu that brings up the Settings (preferences) dialog --> <!-- text of menu that brings up the Settings (preferences) dialog -->
<string name="menu_prefs">Settings</string> <string name="menu_prefs">App Settings</string>
<!-- Regardless of the setting of the connect_frequency <!-- Regardless of the setting of the connect_frequency
preference, checks the relay immediately for any moves for preference, checks the relay immediately for any moves for
@ -115,18 +98,15 @@
<!-- Text of progress indicator shown while check is being conducted --> <!-- Text of progress indicator shown while check is being conducted -->
<string name="msgs_progress">Checking relay for moves <string name="msgs_progress">Checking relay for moves
etc...</string> etc</string>
<!-- If you choose the above option and have no networked games <!-- If you choose the above option and have no networked games
you get this error message --> you get this error message -->
<string name="no_games_to_refresh">No games found that connect via <string name="no_games_to_refresh">No games found that connect via
the relay.</string> the relay.</string>
<!-- Deletes all games on the device (after confirmation) -->
<string name="gamel_menu_delete_all">Delete all</string>
<!-- Brings up "About Crosswords" dialog --> <!-- Brings up "About Crosswords" dialog -->
<string name="board_menu_file_about">About Crosswords</string> <string name="board_menu_file_about">About Crosswords…</string>
<!-- <!--
############################################################ ############################################################
@ -140,11 +120,11 @@
<!-- ############## menu items ############## --> <!-- ############## menu items ############## -->
<!-- pulls up dialog to configure the selected game --> <!-- pulls up dialog to configure the selected game -->
<string name="list_item_config">Game settings...</string> <string name="list_item_config">Game settings</string>
<!-- pulls up dialog to rename (change name of) the selected game --> <!-- pulls up dialog to rename (change name of) the selected game -->
<string name="list_item_rename">Rename...</string> <string name="list_item_rename">Rename</string>
<!-- pulls up dialog to change the group of the selected game --> <!-- pulls up dialog to change the group of the selected game -->
<string name="list_item_move">Move to group...</string> <string name="list_item_move">Move to group</string>
<!-- pulls up dialog to delete the selected game --> <!-- pulls up dialog to delete the selected game -->
<string name="list_item_delete">Delete</string> <string name="list_item_delete">Delete</string>
@ -174,19 +154,14 @@
list_item_rename) --> list_item_rename) -->
<string name="rename_label">Change the name of this game to:</string> <string name="rename_label">Change the name of this game to:</string>
<!-- Text of confirmation dialog posted when list_item_delete menu is <string name="confirm_seldeletesf">Are you sure you want to delete
selected --> the %d selected game[s]? This action cannot be undone.</string>
<string name="confirm_delete">Are you sure you want to delete this
game?</string>
<string name="confirm_seldeletes">Are you sure you want to delete
all selected games? This action cannot be undone.</string>
<!-- Text of confirmation dialog posted when list_item_reset menu <!-- Text of confirmation dialog posted when list_item_reset menu
is selected --> is selected -->
<string name="confirm_reset">Are you sure you want to reset this <string name="confirm_reset">Are you sure you want to reset the
game? Resetting erases all moves and any connection selected game[s]? (Resetting erases all moves and any connection
information.</string> information.)</string>
<!-- <!--
############################################################ ############################################################
@ -202,7 +177,7 @@
<!-- text of button at the bottom of the screen. It launches the <!-- text of button at the bottom of the screen. It launches the
browser pointed at the site where additional wordlists can be browser pointed at the site where additional wordlists can be
found. --> found. -->
<string name="download_dicts">Download more...</string> <string name="download_dicts">Download more</string>
<!-- One of the strings used in the right column of the list of <!-- One of the strings used in the right column of the list of
installed wordlists to describe those that are part of installed wordlists to describe those that are part of
@ -250,13 +225,13 @@
wordlist is substituted for %s. Sometimes one of the two wordlist is substituted for %s. Sometimes one of the two
strings below is appended. --> strings below is appended. -->
<string name="confirm_delete_dictf">Are you sure you want to <string name="confirm_delete_dictf">Are you sure you want to
delete %s?</string> delete the wordlist[s] %s?</string>
<!-- Additional text appended to text confirm_delete_dictf in the <!-- Additional text appended to text confirm_delete_dictf in the
wordlist delete confiration dialog in the case where the wordlist delete confiration dialog in the case where the
wordlist to be deleted is the last in its language. The name wordlist to be deleted is the last in its language. The name
of the language is substituted for %s. --> of the language is substituted for %s. -->
<string name="confirm_deleteonly_dictf">\u0020It is the only %s <string name="confirm_deleteonly_dictf">%1$s is the only %2$s
wordlist installed. One or more games will be unopenable wordlist installed. One or more games will be unopenable
without it.</string> without it.</string>
<!-- Additional text appended to text confirm_delete_dictf in the <!-- Additional text appended to text confirm_delete_dictf in the
@ -277,9 +252,9 @@
<!-- Used as the text fo the confirming/querying dialog that goes <!-- Used as the text fo the confirming/querying dialog that goes
up when the dicts_item_select menuitem is chosen. The up when the dicts_item_select menuitem is chosen. The
possible answers are the three button text strings below. --> possible answers are the three button text strings below. -->
<string name="set_default_messagef">For what players should this <string name="set_default_messagef">For what players should the
wordlist be the default for new games? (The language %s will be wordlist %1$s be the default for new games? (The language %2$s will be
the default for both.)</string> the default for both.)</string>
<!-- These three strings are the text for three buttons giving <!-- These three strings are the text for three buttons giving
choices in respose to the dialog launched in response to the choices in respose to the dialog launched in response to the
@ -309,7 +284,7 @@
############################################################ ############################################################
--> -->
<!-- window title (game name substituted for %s) --> <!-- window title (game name substituted for %s) -->
<string name="title_game_configf">%s settings</string> <string name="title_game_configf">Settings for %s</string>
<!-- alternate window title used when game is networked --> <!-- alternate window title used when game is networked -->
<string name="title_gamenet_configf">%s settings (networked)</string> <string name="title_gamenet_configf">%s settings (networked)</string>
@ -326,7 +301,7 @@
<!-- text for separator above the list of players that's used for <!-- text for separator above the list of players that's used for
non-networked games --> non-networked games -->
<string name="players_label_standalone">Players</string> <string name="players_label_standalone">Players (tap to edit)</string>
<!-- text for separator above the list of players that's used for <!-- text for separator above the list of players that's used for
networked games. The numbers of local and non-local players networked games. The numbers of local and non-local players
@ -527,10 +502,10 @@
<!-- Buttons shown at bottom when board is in exchange mode, <!-- Buttons shown at bottom when board is in exchange mode,
i.e. after user has picked board_menu_trade menu item --> i.e. after user has picked board_menu_trade menu item -->
<string name="button_trade_commit">Commit exchange</string> <string name="button_trade_commit">Commit trade</string>
<string name="button_trade_cancel">Cancel exchange</string> <string name="button_trade_cancel">Cancel trade</string>
<string name="entering_trade">Tap tiles to select...</string> <string name="entering_trade">Tap tiles to select</string>
<!-- Bonus value hint that's displayed in gray text in the colored <!-- Bonus value hint that's displayed in gray text in the colored
@ -653,7 +628,7 @@
<!-- This menu begins an exchange of tiles: puts the board into <!-- This menu begins an exchange of tiles: puts the board into
trade mode. --> trade mode. -->
<string name="board_menu_trade">Exchange tiles</string> <string name="board_menu_trade">Trade</string>
<!-- hide and shows the tray. On devices where there is enough <!-- hide and shows the tray. On devices where there is enough
room for the full board and tray to be shown then hiding the room for the full board and tray to be shown then hiding the
@ -669,23 +644,23 @@
<string name="board_menu_undo_last">Undo last</string> <string name="board_menu_undo_last">Undo last</string>
<!-- Title of submenu --> <!-- Title of submenu -->
<string name="board_submenu_game">Game</string> <string name="board_submenu_game">Game -></string>
<!-- menu on Game submenu: brings up dialog listing all tiles in <!-- menu on Game submenu: brings up dialog listing all tiles in
the language of the game along with how many of each there the language of the game along with how many of each there
are and how many points each is worth. This display is the are and how many points each is worth. This display is the
same throughout the game. --> same throughout the game. -->
<string name="board_menu_game_counts">Counts and values</string> <string name="board_menu_game_counts">Counts and values</string>
<!-- menu on Game submenu: brings up dialog listing all tiles not <!-- menu on Game submenu: brings up dialog listing all tiles not
yet played and not in the rack of the player whose rack is yet played and not in the rack of the player whose rack is
visible (whose turn it is, generally). This display will visible (whose turn it is, generally). This display will
change as tiles are used and depending on what player is change as tiles are used and depending on what player is
asking. --> asking. -->
<string name="board_menu_game_left">Tiles remaining</string> <string name="board_menu_game_left">Tiles remaining</string>
<!-- Brings up listing of all moves played so far this game. --> <!-- Brings up listing of all moves played so far this game. -->
<string name="board_menu_game_history">Game history</string> <string name="board_menu_game_history">Game history</string>
<!-- Brings up explanation of the game's final score. If the game <!-- Brings up explanation of the game's final score. If the game
is not yet over, gives you a choice whether to end it now, is not yet over, gives you a choice whether to end it now,
@ -797,7 +772,7 @@
############################################################ ############################################################
--> -->
<!-- window title --> <!-- window title -->
<string name="title_prefs">Crosswords settings</string> <string name="title_prefs">Crosswords Settings</string>
<!-- <!--
############################################################ ############################################################
@ -851,7 +826,7 @@
<!-- Used to indicate that a preference is not enabled, i.e. not <!-- Used to indicate that a preference is not enabled, i.e. not
part of the game and that the user should ignore it. --> part of the game and that the user should ignore it. -->
<string name="tell_unused">Not used yet...</string> <string name="tell_unused">Not used yet</string>
<!-- sub-preference for dictionaries (soon to be called "word lists") --> <!-- sub-preference for dictionaries (soon to be called "word lists") -->
<string name="prefs_dicts">Wordlists</string> <string name="prefs_dicts">Wordlists</string>
@ -922,14 +897,6 @@
etc. --> etc. -->
<string name="game_summary_field_state">Game state</string> <string name="game_summary_field_state">Game state</string>
<!-- The main screen, by default, has a "add game" button and some
text at the top that take up space and are unnecessary for
experienced users. This checkbox hides them. -->
<string name="hide_intro">Hide main-page hint</string>
<!-- clarification of above -->
<string name="hide_intro_summary">It\'s useful only when you\'re
starting out...</string>
<!-- Checkbox that when set prevents showing a title bar in the <!-- Checkbox that when set prevents showing a title bar in the
game board window to save space --> game board window to save space -->
<string name="hide_title">Hide titlebar</string> <string name="hide_title">Hide titlebar</string>
@ -1214,12 +1181,17 @@
explanation/guidance. --> explanation/guidance. -->
<string name="sms_or_email">Send invitation using SMS (texting) or <string name="sms_or_email">Send invitation using SMS (texting) or
via email?</string> via email?</string>
<string name="nfc_or_email">Send invitation using NFC (Android
beaming NEW) or via email?</string>
<string name="nfc_or_sms_or_email">Send invitation using SMS
(texting) or NFC (\"Android beaming\" NEW) or via email?</string>
<!-- When an invitation is sent, the user gets to choose between <!-- When an invitation is sent, the user gets to choose between
plaintext and html formatting. These two strings are shown in the plaintext and html formatting. These two strings are shown in the
two buttons in the dialog. --> two buttons in the dialog. -->
<string name="button_text">SMS/Text</string> <string name="button_text">SMS/Text</string>
<string name="button_html">Email</string> <string name="button_html">Email</string>
<string name="button_nfc">NFC</string>
<!-- This is the subject line of the email/text sent to invite <!-- This is the subject line of the email/text sent to invite
someone to join a game. --> someone to join a game. -->
@ -1431,7 +1403,7 @@
Guest wordlists; Host wins.</string> Guest wordlists; Host wins.</string>
<string name="downloading_dictf">Downloading %s...</string> <string name="downloading_dictf">Downloading %s</string>
<!-- <!--
############################################################ ############################################################
@ -1558,7 +1530,7 @@
sure you want to create another?</string> sure you want to create another?</string>
<!-- Title of generic dialog used to display information --> <!-- Title of generic dialog used to display information -->
<string name="info_title">FYI...</string> <string name="info_title">FYI</string>
<!-- title of dialog allowing user to pick tiles "face up". (This <!-- title of dialog allowing user to pick tiles "face up". (This
feature is not yet supported on Android.) --> feature is not yet supported on Android.) -->
@ -1596,7 +1568,7 @@
<!-- Title for generic dialog asking a question, usually in the <!-- Title for generic dialog asking a question, usually in the
middle of a game, like "do you want to commit this move?"--> middle of a game, like "do you want to commit this move?"-->
<string name="query_title">A question...</string> <string name="query_title">A question</string>
<!-- <!--
########################################################### ###########################################################
@ -1629,9 +1601,13 @@
<!-- Shown when the user chooses the "board_menu_trade" menu --> <!-- Shown when the user chooses the "board_menu_trade" menu -->
<string name="not_again_trading">You are entering tile-exchange <string name="not_again_trading">You are entering tile-exchange
mode. Tap tiles to add/remove them from the set to be mode.\n\nTap tiles to add/remove them from the set to be
exchanged. Use the buttons to commit your turn or exit exchange exchanged.\n\n</string>
mode.</string>
<string name="not_again_trading_buttons">Use the buttons to
commit your turn or exit exchange mode.</string>
<string name="not_again_trading_menu">Use the menu or action bar
to commit your turn or exit exchange mode.</string>
<!-- Currently not used --> <!-- Currently not used -->
<!-- <string name="not_again_newgame">The new game you have created has --> <!-- <string name="not_again_newgame">The new game you have created has -->
@ -1741,7 +1717,7 @@
another players turn. The idea is to give a hint about how to another players turn. The idea is to give a hint about how to
find out about recent moves. --> find out about recent moves. -->
<string name="not_again_turnchanged">The player whose turn it is <string name="not_again_turnchanged">The player whose turn it is
is drawn large in the scoreboard. Hold your finger on a name in is drawn large in the scoreboard.\n\nHold your finger on a name in
the scoreboard to get details about that player\'s most recent the scoreboard to get details about that player\'s most recent
move.</string> move.</string>
@ -1886,7 +1862,7 @@
<!-- --> <!-- -->
<string name="email_body_revf">(If relevant, please include the <string name="email_body_revf">(If relevant, please include the
version: \"%s\"; and make/model of your phone or version: \"%s\"; and make/model of your phone or
tablet.)"</string> tablet.)</string>
<string name="newgame_enable_bt">Turn Bluetooth on</string> <string name="newgame_enable_bt">Turn Bluetooth on</string>
@ -1898,7 +1874,7 @@
<!-- --> <!-- -->
<string name="scan_progress">Scanning for Crosswords on paired devices</string> <string name="scan_progress">Scanning for Crosswords on paired devices</string>
<!-- --> <!-- -->
<string name="invite_progress">Sending new game info...</string> <string name="invite_progress">Sending new game info</string>
<!-- --> <!-- -->
<string name="summary_wait_host">Waiting for connection[s]</string> <string name="summary_wait_host">Waiting for connection[s]</string>
@ -2024,7 +2000,7 @@
<string name="board_menu_dict">Browse wordlist</string> <string name="board_menu_dict">Browse wordlist</string>
<!-- --> <!-- -->
<string name="game_list_tmp">Loading game summary...</string> <string name="game_list_tmp">Building game summary…</string>
<!-- --> <!-- -->
<string name="connstat_nonet">This is a standalone game. There is <string name="connstat_nonet">This is a standalone game. There is
@ -2124,7 +2100,6 @@
wordlist?</string> wordlist?</string>
<string name="button_decline">Decline</string> <string name="button_decline">Decline</string>
<string name="downloadingf">Downloading %s...</string>
<string name="download_done">Download finished</string> <string name="download_done">Download finished</string>
<string name="download_failed">Download unsuccessful</string> <string name="download_failed">Download unsuccessful</string>
@ -2145,14 +2120,18 @@
<string name="group_new_games">New games</string> <string name="group_new_games">New games</string>
<string name="group_confirm_del">Are you sure you want to delete <string name="group_confirm_del">Are you sure you want to delete
this group?</string> the selected group?</string>
<string name="groups_confirm_del">Are you sure you want to delete
the selected groups?</string>
<string name="group_confirm_delf">\u0020(It contains %d game[s], <string name="group_confirm_delf">\u0020(It contains %d game[s],
which will also be deleted.)</string> which will also be deleted.)</string>
<string name="groups_confirm_delf">\u0020(They contains %d game[s],
which will also be deleted.)</string>
<string name="rename_group_label">Change the name of this group to:</string> <string name="rename_group_label">Change the name of this group to:</string>
<string name="game_name_group_title">Name group</string> <string name="game_name_group_title">Name group</string>
<string name="cannot_delete_default_group">The group for new games <string name="cannot_delete_default_groupf">The group for new games, %s,
cannot be deleted.</string> cannot be deleted.</string>
<string name="no_move_onegroup">Moving is impossible until there <string name="no_move_onegroup">Moving is impossible until there
@ -2169,9 +2148,43 @@
<string name="square_tiles">Square rack tiles</string> <string name="square_tiles">Square rack tiles</string>
<string name="square_tiles_summary">Even if they can be taller</string> <string name="square_tiles_summary">Even if they can be taller</string>
<string name="change_groupf">Move game %s</string> <string name="change_group">Move games</string>
<string name="show_wordlist_browser">Wordlist browser</string> <string name="show_wordlist_browser">Wordlist browser</string>
<string name="relay_alert_title">Message from relay</string> <string name="relay_alert_title">Message from relay</string>
<string name="not_again_newselect">Tapping a game opens it.\n\nYou
can instead tap the icons at the left to select or deselect games,
then act on selected games, e.g. to delete them, using the menu or
\"Actionbar.\"</string>
<string name="not_again_backclears">The back button clears any
selection instead of exiting. Hit it again to exit the
app.</string>
<string name="sel_gamesf">Games: %d</string>
<string name="sel_groupsf">Groups: %d</string>
<string name="summary_thumbsize">Thumbnail size</string>
<string name="sel_dictsf">Wordlists: %d</string>
<string name="thumb_enabled">Show thumbnails</string>
<string name="summary_thumb_enabled">Display snapshots of games</string>
<string name="prefs_thumb">Thumbnails</string>
<string name="prefs_thumb_summary">Setting for game snapshots</string>
<string name="dropped_dupe">Invitation received but ignored: it
has already been used to create a game.</string>
<string name="cur_menu_markerf">%1$s (yours)</string>
<string name="board_menu_invite">Invite</string>
<string name="enable_nfc">NFC is turned off on this device. You
can use the Android Settings app to turn it on .</string>
<string name="button_go_settings">Launch Settings</string>
<string name="no_hide_titlebar">This setting is ignored on devices
like yours that depend on the \"Action bar.\"</string>
<string name="sms_ready_text">Tap the receiving device now</string>
</resources> </resources>

View file

@ -110,18 +110,32 @@
<PreferenceScreen android:title="@string/prefs_appearance" <PreferenceScreen android:title="@string/prefs_appearance"
android:summary="@string/prefs_appearance_summary" android:summary="@string/prefs_appearance_summary"
> >
<org.eehouse.android.xw4.XWListPreference <PreferenceScreen android:title="@string/prefs_thumb"
android:summary="@string/prefs_thumb_summary"
>
<CheckBoxPreference android:key="@string/key_thumb_enabled"
android:title="@string/thumb_enabled"
android:summary="@string/summary_thumb_enabled"
android:defaultValue="true"
/>
<org.eehouse.android.xw4.XWListPreference
android:key="@string/key_thumbsize"
android:title="@string/summary_thumbsize"
android:entries="@array/game_thumb_values"
android:entryValues="@array/game_thumb_values"
android:defaultValue="@string/game_thumb_third"
/>
</PreferenceScreen>
<org.eehouse.android.xw4.XWSumListPreference
android:key="@string/key_summary_field" android:key="@string/key_summary_field"
android:title="@string/summary_field" android:title="@string/summary_field"
android:entries="@array/game_summary_values" android:entries="@array/game_summary_values"
android:entryValues="@array/game_summary_values" android:entryValues="@array/game_summary_values"
android:defaultValue="@string/game_summary_field_opponents" android:defaultValue="@string/game_summary_field_opponents"
/> />
<CheckBoxPreference android:key="@string/key_hide_intro"
android:title="@string/hide_intro"
android:summary="@string/hide_intro_summary"
android:defaultValue="false"
/>
<CheckBoxPreference android:key="@string/key_hide_title" <CheckBoxPreference android:key="@string/key_hide_title"
android:title="@string/hide_title" android:title="@string/hide_title"
android:summary="@string/hide_title_summary" android:summary="@string/hide_title_summary"
@ -297,11 +311,18 @@
/> />
<CheckBoxPreference android:key="@string/key_logging_on" <CheckBoxPreference android:key="@string/key_logging_on"
android:title="@string/logging_on" android:title="@string/logging_on"
android:summary="@string/logging_on_summary"
android:defaultValue="false" android:defaultValue="false"
/> />
<CheckBoxPreference android:key="@string/key_enable_debug" <CheckBoxPreference android:key="@string/key_enable_debug"
android:title="Enable debug features" android:title="@string/debug_features"
android:summary="Menuitems etc." android:summary="@string/debug_features_summary"
android:defaultValue="false"
/>
<CheckBoxPreference android:key="@string/key_enable_dup_invite"
android:title="@string/enable_dupes_title"
android:summary="@string/enable_dupes_summary"
android:defaultValue="false" android:defaultValue="false"
/> />

View file

@ -1,2 +1,2 @@
GitVersion.java BuildConstants.java
GCMConsts.java GCMConsts.java

View file

@ -0,0 +1,91 @@
/* -*- 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 android.app.Activity;
import android.content.Context;
import android.view.ViewConfiguration;
public class ABUtils {
private static int s_sdkVersion =
Integer.valueOf( android.os.Build.VERSION.SDK );
private static interface SafeInvalOptionsMenu {
public void doInval( Activity activity );
}
private static class SafeInvalOptionsMenuImpl
implements SafeInvalOptionsMenu {
public void doInval( Activity activity ) {
activity.invalidateOptionsMenu();
}
}
private static SafeInvalOptionsMenu s_safeInval = null;
private static interface SafeHasMenuKey {
public boolean hasMenuKey( Context context );
}
private static class SafeHasMenuKeyImpl
implements SafeHasMenuKey {
public boolean hasMenuKey( Context context )
{
return ViewConfiguration.get(context).hasPermanentMenuKey();
}
}
private static SafeHasMenuKey s_safeHas = null;
static {
if ( 11 <= s_sdkVersion ) {
s_safeInval = new SafeInvalOptionsMenuImpl();
}
if ( 14 <= s_sdkVersion ) {
s_safeHas = new SafeHasMenuKeyImpl();
}
}
public static void invalidateOptionsMenuIf( Activity activity )
{
if ( null != s_safeInval ) {
s_safeInval.doInval( activity );
}
}
public static boolean haveActionBar()
{
return null != s_safeInval;
}
// http://stackoverflow.com/questions/10929579/how-to-check-if-android-phone-has-hardware-menu-button-in-android-2-1:
// If SDK <= 10, assume yes; >= 14, use the API; in the middle,
// assume no
public static boolean haveMenuKey( Context context )
{
boolean result;
if ( s_sdkVersion <= 10 ) {
result = true;
} else if ( s_sdkVersion < 14 ) {
result = false;
} else {
result = s_safeHas.hasMenuKey( context );
}
return result;
}
}

View file

@ -21,45 +21,52 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuInflater;
import android.view.KeyEvent;
import android.view.Window;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.content.Intent; import android.text.TextUtils;
import java.util.concurrent.Semaphore;
import java.util.ArrayList; import android.view.KeyEvent;
import java.util.Iterator; import android.view.Menu;
import android.app.Dialog; import android.view.MenuInflater;
import android.app.AlertDialog; import android.view.MenuItem;
import android.app.ProgressDialog; import android.view.View;
import android.content.DialogInterface; import android.view.Window;
import android.content.DialogInterface.OnDismissListener;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.Semaphore;
import junit.framework.Assert; import junit.framework.Assert;
import android.content.res.Configuration;
import android.content.pm.ActivityInfo;
import org.eehouse.android.xw4.jni.*; import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.JNIThread.*;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.JNIThread.*;
public class BoardActivity extends XWActivity public class BoardActivity extends XWActivity
implements TransportProcs.TPMsgHandler, View.OnClickListener, implements TransportProcs.TPMsgHandler, View.OnClickListener,
DictImportActivity.DownloadFinishedListener, DictImportActivity.DownloadFinishedListener,
ConnStatusHandler.ConnStatusCBacks { ConnStatusHandler.ConnStatusCBacks,
NFCUtils.NFCActor {
public static final String INTENT_KEY_CHAT = "chat"; public static final String INTENT_KEY_CHAT = "chat";
@ -79,6 +86,7 @@ public class BoardActivity extends XWActivity
private static final int DLG_GETDICT = DLG_OKONLY + 13; private static final int DLG_GETDICT = DLG_OKONLY + 13;
private static final int GAME_OVER = DLG_OKONLY + 14; private static final int GAME_OVER = DLG_OKONLY + 14;
private static final int DLG_CONNSTAT = DLG_OKONLY + 15; private static final int DLG_CONNSTAT = DLG_OKONLY + 15;
private static final int ENABLE_NFC = DLG_OKONLY + 16;
private static final int CHAT_REQUEST = 1; private static final int CHAT_REQUEST = 1;
private static final int BT_INVITE_RESULT = 2; private static final int BT_INVITE_RESULT = 2;
@ -120,6 +128,7 @@ public class BoardActivity extends XWActivity
private int m_jniGamePtr; private int m_jniGamePtr;
private GameLock m_gameLock; private GameLock m_gameLock;
private CurGameInfo m_gi; private CurGameInfo m_gi;
private GameSummary m_summary;
private CommsTransport m_xport; private CommsTransport m_xport;
private Handler m_handler = null; private Handler m_handler = null;
private TimerRunnable[] m_timers; private TimerRunnable[] m_timers;
@ -493,7 +502,7 @@ public class BoardActivity extends XWActivity
lstnr = new DialogInterface.OnClickListener() { lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog, public void onClick( DialogInterface dialog,
int item ) { int item ) {
showEmailOrSMSThen( LAUNCH_INVITE_ACTION ); showInviteChoicesThen( LAUNCH_INVITE_ACTION );
} }
}; };
dialog = new AlertDialog.Builder( this ) dialog = new AlertDialog.Builder( this )
@ -505,6 +514,10 @@ public class BoardActivity extends XWActivity
} }
break; break;
case ENABLE_NFC:
dialog = NFCUtils.makeEnableNFCDialog( this );
break;
default: default:
// just drop it; super.onCreateDialog likely failed // just drop it; super.onCreateDialog likely failed
break; break;
@ -537,11 +550,12 @@ public class BoardActivity extends XWActivity
super.onCreate( savedInstanceState ); super.onCreate( savedInstanceState );
getBundledData( savedInstanceState ); getBundledData( savedInstanceState );
if ( CommonPrefs.getHideTitleBar( this ) ) { if ( CommonPrefs.getHideTitleBar( this )
&& ABUtils.haveMenuKey( this ) ) {
requestWindowFeature( Window.FEATURE_NO_TITLE ); requestWindowFeature( Window.FEATURE_NO_TITLE );
} }
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
m_pendingChats = new ArrayList<String>(); m_pendingChats = new ArrayList<String>();
} }
@ -552,14 +566,8 @@ public class BoardActivity extends XWActivity
// XWTimerReason // XWTimerReason
m_view = (BoardView)findViewById( R.id.board_view ); m_view = (BoardView)findViewById( R.id.board_view );
m_tradeButtons = findViewById( R.id.exchange_buttons ); m_tradeButtons = findViewById( R.id.exchange_buttons );
m_exchCommmitButton = (Button)findViewById( R.id.exchange_commit ); m_exchCommmitButton = setListenerOrHide( R.id.exchange_commit );
if ( null != m_exchCommmitButton ) { m_exchCancelButton = setListenerOrHide( R.id.exchange_cancel );
m_exchCommmitButton.setOnClickListener( this );
}
m_exchCancelButton = (Button)findViewById( R.id.exchange_cancel );
if ( null != m_exchCancelButton ) {
m_exchCancelButton.setOnClickListener( this );
}
m_volKeysZoom = XWPrefs.getVolKeysZoom( this ); m_volKeysZoom = XWPrefs.getVolKeysZoom( this );
Intent intent = getIntent(); Intent intent = getIntent();
@ -568,6 +576,8 @@ public class BoardActivity extends XWActivity
m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false );
m_overNotShown = true; m_overNotShown = true;
NFCUtils.register( this ); // Don't seem to need to unregister...
setBackgroundColor(); setBackgroundColor();
setKeepScreenOn(); setKeepScreenOn();
} // onCreate } // onCreate
@ -629,7 +639,7 @@ public class BoardActivity extends XWActivity
if ( Activity.RESULT_CANCELED != resultCode ) { if ( Activity.RESULT_CANCELED != resultCode ) {
switch ( requestCode ) { switch ( requestCode ) {
case CHAT_REQUEST: case CHAT_REQUEST:
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
String msg = data.getStringExtra( INTENT_KEY_CHAT ); String msg = data.getStringExtra( INTENT_KEY_CHAT );
if ( null != msg && msg.length() > 0 ) { if ( null != msg && msg.length() > 0 ) {
m_pendingChats.add( msg ); m_pendingChats.add( msg );
@ -722,8 +732,6 @@ public class BoardActivity extends XWActivity
MenuItem item; MenuItem item;
int strId; int strId;
updateMenus( menu );
if ( null != m_gsi ) { if ( null != m_gsi ) {
inTrade = m_gsi.inTrade; inTrade = m_gsi.inTrade;
menu.setGroupVisible( R.id.group_done, !inTrade ); menu.setGroupVisible( R.id.group_done, !inTrade );
@ -736,11 +744,28 @@ public class BoardActivity extends XWActivity
} }
item = menu.findItem( R.id.board_menu_tray ); item = menu.findItem( R.id.board_menu_tray );
item.setTitle( strId ); item.setTitle( strId );
hideShowItem( menu, R.id.board_menu_flip, m_gsi.visTileCount >= 1 );
hideShowItem( menu, R.id.board_menu_toggle, m_gsi.visTileCount >= 1 );
hideShowItem( menu, R.id.board_menu_juggle, m_gsi.canShuffle );
hideShowItem( menu, R.id.board_menu_undo_current, m_gsi.canRedo );
hideShowItem( menu, R.id.board_menu_hint_prev, m_gsi.canHint );
hideShowItem( menu, R.id.board_menu_hint_next, m_gsi.canHint );
hideShowItem( menu, R.id.board_menu_chat,
BuildConstants.CHAT_SUPPORTED && m_gsi.canChat );
hideShowItem( menu, R.id.board_menu_tray,
!inTrade && m_gsi.canHideRack );
} }
Utils.setItemVisible( menu, R.id.board_menu_invite, 0 < m_missing );
Utils.setItemVisible( menu, R.id.board_menu_undo_last, !inTrade ); Utils.setItemVisible( menu, R.id.board_menu_undo_last, !inTrade );
Utils.setItemVisible( menu, R.id.board_menu_tray, !inTrade );
Utils.setItemVisible( menu, R.id.board_submenu_game, !inTrade ); Utils.setItemVisible( menu, R.id.board_menu_trade_cancel, inTrade );
Utils.setItemVisible( menu, R.id.board_menu_trade_commit,
inTrade && m_gsi.tradeTilesSelected );
Utils.setItemVisible( menu, R.id.board_menu_trade,
0 == m_missing && !m_gameOver && !inTrade );
Utils.setItemVisible( menu, R.id.board_menu_game_resign, !inTrade );
if ( !inTrade ) { if ( !inTrade ) {
boolean enabled = null == m_gsi || m_gsi.curTurnSelected; boolean enabled = null == m_gsi || m_gsi.curTurnSelected;
@ -754,15 +779,14 @@ public class BoardActivity extends XWActivity
} }
item.setTitle( strId ); item.setTitle( strId );
} }
if ( m_gameOver || DBUtils.gameOver( this, m_rowid ) ) {
m_gameOver = true;
item = menu.findItem( R.id.board_menu_game_resign );
item.setTitle( R.string.board_menu_game_final );
}
} }
if ( m_gameOver || DBUtils.gameOver( this, m_rowid ) ) { if ( null != m_gi && DeviceRole.SERVER_STANDALONE == m_gi.serverRole ) {
m_gameOver = true;
item = menu.findItem( R.id.board_menu_game_resign );
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.board_menu_game_resend, false );
Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, false ); Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, false );
} }
@ -785,7 +809,7 @@ public class BoardActivity extends XWActivity
showNotAgainDlgThen( R.string.not_again_done, showNotAgainDlgThen( R.string.not_again_done,
R.string.key_notagain_done, COMMIT_ACTION ); R.string.key_notagain_done, COMMIT_ACTION );
} else { } else {
dlgButtonClicked( COMMIT_ACTION, AlertDialog.BUTTON_POSITIVE ); dlgButtonClicked( COMMIT_ACTION, AlertDialog.BUTTON_POSITIVE, null );
} }
break; break;
@ -819,8 +843,11 @@ public class BoardActivity extends XWActivity
break; break;
case R.id.board_menu_trade: case R.id.board_menu_trade:
showNotAgainDlgThen( R.string.not_again_trading, String msg = getString( R.string.not_again_trading );
R.string.key_notagain_trading, int strID = ABUtils.haveActionBar() ? R.string.not_again_trading_menu
: R.string. not_again_trading_buttons;
msg += getString( strID );
showNotAgainDlgThen( msg, R.string.key_notagain_trading,
START_TRADE_ACTION ); START_TRADE_ACTION );
break; break;
@ -833,7 +860,9 @@ public class BoardActivity extends XWActivity
case R.id.board_menu_undo_last: case R.id.board_menu_undo_last:
showConfirmThen( R.string.confirm_undo_last, UNDO_LAST_ACTION ); showConfirmThen( R.string.confirm_undo_last, UNDO_LAST_ACTION );
break; break;
case R.id.board_menu_invite:
showDialog( DLG_INVITE );
break;
// small devices only // small devices only
case R.id.board_menu_dict: case R.id.board_menu_dict:
String dictName = m_gi.dictName( m_view.getCurPlayer() ); String dictName = m_gi.dictName( m_view.getCurPlayer() );
@ -891,14 +920,20 @@ public class BoardActivity extends XWActivity
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
////////////////////////////////////////////////// //////////////////////////////////////////////////
@Override @Override
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
if ( LAUNCH_INVITE_ACTION == id ) { if ( LAUNCH_INVITE_ACTION == id ) {
if ( DlgDelegate.DISMISS_BUTTON != which ) { if ( DlgDelegate.DISMISS_BUTTON != which ) {
GameUtils.launchInviteActivity( BoardActivity.this, if ( DlgDelegate.NFC_BTN == which
DlgDelegate.EMAIL_BTN == which, && !NFCUtils.nfcAvail( this )[1] ) {
m_room, null, m_gi.dictLang, showDialog( ENABLE_NFC );
m_gi.dictName, m_gi.nPlayers ); } else {
String inviteID = GameUtils.formatGameID( m_gi.gameID );
GameUtils.launchInviteActivity( this, which, m_room,
inviteID, m_gi.dictLang,
m_gi.dictName,
m_gi.nPlayers );
}
} }
} else if ( AlertDialog.BUTTON_POSITIVE == which ) { } else if ( AlertDialog.BUTTON_POSITIVE == which ) {
JNICmd cmd = JNICmd.CMD_NONE; JNICmd cmd = JNICmd.CMD_NONE;
@ -1145,6 +1180,24 @@ public class BoardActivity extends XWActivity
} }
} }
//////////////////////////////////////////////////
// NFCUtils.NFCActor
//////////////////////////////////////////////////
public String makeNFCMessage()
{
String data = null;
if ( 0 < m_missing ) { // Isn't there a better test??
String inviteID = String.format( "%X", m_gi.gameID );
String room = m_summary.roomName;
Assert.assertNotNull( room );
data = NetLaunchInfo.makeLaunchJSON( this, room, inviteID,
m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers );
}
return data;
}
////////////////////////////////////////////////// //////////////////////////////////////////////////
// ConnStatusHandler.ConnStatusCBacks // ConnStatusHandler.ConnStatusCBacks
////////////////////////////////////////////////// //////////////////////////////////////////////////
@ -1285,6 +1338,7 @@ public class BoardActivity extends XWActivity
m_room = room; m_room = room;
m_missing = nMissing; m_missing = nMissing;
showDialog( DLG_INVITE ); showDialog( DLG_INVITE );
ABUtils.invalidateOptionsMenuIf( this );
} else { } else {
toastStr = getString( R.string.msg_relay_waiting, devOrder, toastStr = getString( R.string.msg_relay_waiting, devOrder,
room, nMissing ); room, nMissing );
@ -1302,11 +1356,14 @@ public class BoardActivity extends XWActivity
m_toastStr = toastStr; m_toastStr = toastStr;
if ( naMsg == 0 ) { if ( naMsg == 0 ) {
dlgButtonClicked( SHOW_EXPL_ACTION, dlgButtonClicked( SHOW_EXPL_ACTION,
AlertDialog.BUTTON_POSITIVE ); AlertDialog.BUTTON_POSITIVE, null );
} else { } else {
showNotAgainDlgThen( naMsg, naKey, SHOW_EXPL_ACTION ); showNotAgainDlgThen( naMsg, naKey, SHOW_EXPL_ACTION );
} }
} }
m_missing = nMissing;
ABUtils.invalidateOptionsMenuIf( this );
} // handleConndMessage } // handleConndMessage
private class BoardUtilCtxt extends UtilCtxtImpl { private class BoardUtilCtxt extends UtilCtxtImpl {
@ -1727,7 +1784,7 @@ public class BoardActivity extends XWActivity
@Override @Override
public void showChat( final String msg ) public void showChat( final String msg )
{ {
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
post( new Runnable() { post( new Runnable() {
public void run() { public void run() {
DBUtils.appendChatHistory( BoardActivity.this, DBUtils.appendChatHistory( BoardActivity.this,
@ -1769,21 +1826,23 @@ public class BoardActivity extends XWActivity
CommonPrefs cp = CommonPrefs.get( this ); CommonPrefs cp = CommonPrefs.get( this );
if ( null == stream || if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream, ! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
m_gi, dictNames, pairs.m_bytes, m_gi, dictNames,
pairs.m_paths, langName, m_utils, pairs.m_bytes,
m_jniu, m_view, cp, m_xport ) ) { pairs.m_paths, langName,
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, m_jniu, m_utils, m_jniu,
m_view, cp, m_xport, dictNames, null, cp, m_xport ) ) {
pairs.m_bytes, pairs.m_paths, XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils,
langName ); m_jniu, null, cp, m_xport,
dictNames, pairs.m_bytes,
pairs.m_paths, langName );
} }
m_summary = new GameSummary( this, m_gi );
XwJNI.game_summarize( m_jniGamePtr, m_summary );
Handler handler = new Handler() { Handler handler = new Handler() {
public void handleMessage( Message msg ) { public void handleMessage( Message msg ) {
switch( msg.what ) { switch( msg.what ) {
case JNIThread.DRAW:
m_view.invalidate();
break;
case JNIThread.DIALOG: case JNIThread.DIALOG:
m_dlgBytes = (String)msg.obj; m_dlgBytes = (String)msg.obj;
m_dlgTitle = msg.arg1; m_dlgTitle = msg.arg1;
@ -1799,11 +1858,11 @@ public class BoardActivity extends XWActivity
updateToolbar(); updateToolbar();
if ( m_inTrade != m_gsi.inTrade ) { if ( m_inTrade != m_gsi.inTrade ) {
m_inTrade = m_gsi.inTrade; m_inTrade = m_gsi.inTrade;
m_view.setInTrade( m_inTrade );
} }
m_view.setInTrade( m_inTrade );
adjustTradeVisibility(); adjustTradeVisibility();
Activity self = BoardActivity.this; Activity self = BoardActivity.this;
Utils.invalidateOptionsMenuIf( self ); ABUtils.invalidateOptionsMenuIf( self );
} }
break; break;
case JNIThread.GOT_WORDS: case JNIThread.GOT_WORDS:
@ -1922,7 +1981,7 @@ public class BoardActivity extends XWActivity
R.string.not_again_undo, R.string.not_again_undo,
R.string.key_notagain_undo, R.string.key_notagain_undo,
UNDO_ACTION ); UNDO_ACTION );
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
m_toolbar.setListener( Toolbar.BUTTON_CHAT, m_toolbar.setListener( Toolbar.BUTTON_CHAT,
R.string.not_again_chat, R.string.not_again_chat,
R.string.key_notagain_chat, R.string.key_notagain_chat,
@ -2011,7 +2070,7 @@ public class BoardActivity extends XWActivity
private void startChatActivity() private void startChatActivity()
{ {
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
Intent intent = new Intent( this, ChatActivity.class ); Intent intent = new Intent( this, ChatActivity.class );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_rowid ); intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_rowid );
startActivityForResult( intent, CHAT_REQUEST ); startActivityForResult( intent, CHAT_REQUEST );
@ -2035,6 +2094,14 @@ public class BoardActivity extends XWActivity
clearThis(); clearThis();
if ( XWPrefs.getThumbEnabled( this ) ) {
// Before we dispose, and after JNIThread has
// relinquished interest, redraw on smaller scale.
Bitmap thumb =
GameUtils.takeSnapshot( this, m_jniGamePtr, m_gi );
DBUtils.saveThumbnail( this, m_gameLock, thumb );
}
XwJNI.game_dispose( m_jniGamePtr ); XwJNI.game_dispose( m_jniGamePtr );
m_jniGamePtr = 0; m_jniGamePtr = 0;
m_gi = null; m_gi = null;
@ -2059,7 +2126,7 @@ public class BoardActivity extends XWActivity
private void trySendChats() private void trySendChats()
{ {
if ( GitVersion.CHAT_SUPPORTED && null != m_jniThread ) { if ( BuildConstants.CHAT_SUPPORTED && null != m_jniThread ) {
Iterator<String> iter = m_pendingChats.iterator(); Iterator<String> iter = m_pendingChats.iterator();
while ( iter.hasNext() ) { while ( iter.hasNext() ) {
m_jniThread.handle( JNICmd.CMD_SENDCHAT, iter.next() ); m_jniThread.handle( JNICmd.CMD_SENDCHAT, iter.next() );
@ -2107,7 +2174,7 @@ public class BoardActivity extends XWActivity
m_toolbar.update( Toolbar.BUTTON_HINT_PREV, m_gsi.canHint ); m_toolbar.update( Toolbar.BUTTON_HINT_PREV, m_gsi.canHint );
m_toolbar.update( Toolbar.BUTTON_HINT_NEXT, m_gsi.canHint ); m_toolbar.update( Toolbar.BUTTON_HINT_NEXT, m_gsi.canHint );
m_toolbar.update( Toolbar.BUTTON_CHAT, m_toolbar.update( Toolbar.BUTTON_CHAT,
GitVersion.CHAT_SUPPORTED && m_gsi.canChat ); BuildConstants.CHAT_SUPPORTED && m_gsi.canChat );
m_toolbar.update( Toolbar.BUTTON_BROWSE_DICT, m_toolbar.update( Toolbar.BUTTON_BROWSE_DICT,
null != m_gi.dictName( m_view.getCurPlayer() ) ); null != m_gi.dictName( m_view.getCurPlayer() ) );
} }
@ -2120,20 +2187,6 @@ public class BoardActivity extends XWActivity
} }
} }
private void updateMenus( Menu menu )
{
if ( null != m_gsi ) {
hideShowItem( menu, R.id.board_menu_flip, m_gsi.visTileCount >= 1 );
hideShowItem( menu, R.id.board_menu_toggle, m_gsi.visTileCount >= 1 );
hideShowItem( menu, R.id.board_menu_juggle, m_gsi.canShuffle );
hideShowItem( menu, R.id.board_menu_undo_current, m_gsi.canRedo );
hideShowItem( menu, R.id.board_menu_hint_prev, m_gsi.canHint );
hideShowItem( menu, R.id.board_menu_hint_next, m_gsi.canHint );
hideShowItem( menu, R.id.board_menu_chat,
GitVersion.CHAT_SUPPORTED && m_gsi.canChat );
}
}
private void adjustTradeVisibility() private void adjustTradeVisibility()
{ {
m_toolbar.setVisibility( m_inTrade? View.GONE : View.VISIBLE ); m_toolbar.setVisibility( m_inTrade? View.GONE : View.VISIBLE );
@ -2232,4 +2285,17 @@ public class BoardActivity extends XWActivity
finish(); finish();
} }
private Button setListenerOrHide( int id )
{
Button button = (Button)findViewById( id );
if ( null != button ) {
if ( ABUtils.haveActionBar() ) {
button.setVisibility( View.INVISIBLE );
} else {
button.setOnClickListener( this );
}
}
return button;
}
} // class BoardActivity } // class BoardActivity

View file

@ -0,0 +1,867 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2009 - 2013 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.res.Resources;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Handler;
import android.graphics.RectF;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
import org.eehouse.android.xw4.jni.DrawCtx;
import org.eehouse.android.xw4.jni.DrawScoreInfo;
import org.eehouse.android.xw4.jni.CommonPrefs;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.jni.XwJNI;
import junit.framework.Assert;
public class BoardCanvas extends Canvas implements DrawCtx {
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
private static final int FRAME_GREY = 0xFF101010;
private static final int SCORE_HT_DROP = 2;
private static final boolean DEBUG_DRAWFRAMES = false;
private static final int NOT_TURN_ALPHA = 0x3FFFFFFF;
private static final int IN_TRADE_ALPHA = 0x3FFFFFFF;
private static final boolean FRAME_TRAY_RECTS = false; // for debugging
private static final float MIN_FONT_DIPS = 14.0f;
private Activity m_activity;
private Bitmap m_bitmap;
private JNIThread m_jniThread;
private Paint m_fillPaint;
private Paint m_strokePaint;
private Paint m_drawPaint;
private Paint m_tileStrokePaint;
private String[][] m_scores;
private String m_remText;
private int m_mediumFontHt;
private int m_defaultFontHt;
private int m_minRemWidth;
private Rect m_boundsScratch = new Rect();
private Rect m_letterRect;
private Rect m_valRect;
private int[] m_bonusColors;
private int[] m_playerColors;
private int[] m_otherColors;
private String[] m_bonusSummaries;
private CommonPrefs m_prefs;
private int m_lastSecsLeft;
private int m_lastTimerPlayer;
private boolean m_inTrade;
private boolean m_darkOnLight;
private Drawable m_origin;
private boolean m_blackArrow;
private Drawable m_rightArrow;
private Drawable m_downArrow;
private int m_trayOwner = -1;
private int m_pendingScore;
private int m_dictPtr = 0;
private String[] m_dictChars;
private boolean m_hasSmallScreen;
private int m_backgroundUsed = 0x00000000;
// FontDims: exists to translate space available to the largest
// font we can draw within that space taking advantage of our use
// being limited to a known small subset of glyphs. We need two
// numbers from this: the textHeight to pass to Paint.setTextSize,
// and the descent to use when drawing. Both can be calculated
// proportionally. We know the ht we passed to Paint to get the
// height we've now measured; that gives a percent to multiply any
// future wantHt by. Ditto for the descent
private class FontDims {
FontDims( float askedHt, int topRow, int bottomRow, float width ) {
// DbgUtils.logf( "FontDims(): askedHt=" + askedHt );
// DbgUtils.logf( "FontDims(): topRow=" + topRow );
// DbgUtils.logf( "FontDims(): bottomRow=" + bottomRow );
// DbgUtils.logf( "FontDims(): width=" + width );
float gotHt = bottomRow - topRow + 1;
m_htProportion = gotHt / askedHt;
Assert.assertTrue( (bottomRow+1) >= askedHt );
float descent = (bottomRow+1) - askedHt;
// DbgUtils.logf( "descent: " + descent );
m_descentProportion = descent / askedHt;
Assert.assertTrue( m_descentProportion >= 0 );
m_widthProportion = width / askedHt;
// DbgUtils.logf( "m_htProportion: " + m_htProportion );
// DbgUtils.logf( "m_descentProportion: " + m_descentProportion );
}
private float m_htProportion;
private float m_descentProportion;
private float m_widthProportion;
int heightFor( int ht ) { return (int)(ht / m_htProportion); }
int descentFor( int ht ) { return (int)(ht * m_descentProportion); }
int widthFor( int width ) { return (int)(width / m_widthProportion); }
}
private FontDims m_fontDims;
public BoardCanvas( Activity activity, Bitmap bitmap, JNIThread jniThread,
BoardDims dims )
{
super( bitmap );
m_activity = activity;
m_bitmap = bitmap;
m_jniThread = jniThread;
m_hasSmallScreen = Utils.hasSmallScreen( activity );
float scale = activity.getResources().getDisplayMetrics().density;
m_defaultFontHt = (int)(MIN_FONT_DIPS * scale + 0.5f);
m_mediumFontHt = m_defaultFontHt * 3 / 2;
if ( null != dims ) {
m_minRemWidth = dims.cellSize;
}
m_drawPaint = new Paint();
m_fillPaint = new Paint( Paint.ANTI_ALIAS_FLAG );
m_strokePaint = new Paint();
m_strokePaint.setStyle( Paint.Style.STROKE );
Resources res = activity.getResources();
m_origin = res.getDrawable( R.drawable.origin );
m_prefs = CommonPrefs.get( activity );
m_playerColors = m_prefs.playerColors;
m_bonusColors = m_prefs.bonusColors;
m_otherColors = m_prefs.otherColors;
int[] ids = { R.string.bonus_l2x_summary,
R.string.bonus_w2x_summary ,
R.string.bonus_l3x_summary,
R.string.bonus_w3x_summary };
m_bonusSummaries = new String[1 + ids.length];
for ( int ii = 0; ii < ids.length; ++ii ) {
m_bonusSummaries[ ii+1 ] = res.getString( ids[ii] );
}
m_boundsScratch.set( 0, 0, bitmap.getWidth(), bitmap.getHeight() );
fillRect( m_boundsScratch, WHITE );
}
public void setJNIThread( JNIThread jniThread )
{
if ( ! jniThread.equals( m_jniThread ) ) {
DbgUtils.logf( "BoardCanvas changing threads" );
m_jniThread = jniThread;
}
}
public int getCurPlayer()
{
return m_trayOwner;
}
public int curPending()
{
return m_pendingScore;
}
public void setInTrade( boolean inTrade )
{
if ( m_inTrade != inTrade ) {
m_inTrade = inTrade;
m_jniThread.handle( JNIThread.JNICmd.CMD_INVALALL );
}
}
// DrawCtxt interface implementation
public boolean scoreBegin( Rect rect, int numPlayers, int[] scores,
int remCount )
{
fillRectOther( rect, CommonPrefs.COLOR_BACKGRND );
m_scores = new String[numPlayers][];
return true;
}
public boolean measureRemText( Rect r, int nTilesLeft, int[] width,
int[] height )
{
boolean showREM = 0 <= nTilesLeft;
if ( showREM ) {
// should cache a formatter
m_remText = String.format( "%d", nTilesLeft );
m_fillPaint.setTextSize( m_mediumFontHt );
m_fillPaint.getTextBounds( m_remText, 0, m_remText.length(),
m_boundsScratch );
int minWidth = m_boundsScratch.width();
if ( minWidth < m_minRemWidth ) {
minWidth = m_minRemWidth; // it's a button; make it bigger
}
width[0] = minWidth;
height[0] = m_boundsScratch.height();
}
return showREM;
}
public void drawRemText( Rect rInner, Rect rOuter, int nTilesLeft,
boolean focussed )
{
int indx = focussed ? CommonPrefs.COLOR_FOCUS
: CommonPrefs.COLOR_TILE_BACK;
fillRectOther( rOuter, indx );
m_fillPaint.setColor( adjustColor(BLACK) );
drawCentered( m_remText, rInner, null );
}
public void measureScoreText( Rect rect, DrawScoreInfo dsi,
int[] width, int[] height )
{
String[] scoreInfo = new String[dsi.isTurn?1:2];
int indx = 0;
StringBuffer sb = new StringBuffer();
// If it's my turn I get one line. Otherwise squeeze into
// two.
if ( dsi.isTurn ) {
sb.append( dsi.name );
sb.append( ":" );
} else {
scoreInfo[indx++] = dsi.name;
}
sb.append( dsi.totalScore );
if ( dsi.nTilesLeft >= 0 ) {
sb.append( ":" );
sb.append( dsi.nTilesLeft );
}
scoreInfo[indx] = sb.toString();
m_scores[dsi.playerNum] = scoreInfo;
int rectHt = rect.height();
if ( !dsi.isTurn ) {
rectHt /= 2;
}
int textHeight = rectHt - SCORE_HT_DROP;
if ( textHeight < m_defaultFontHt ) {
textHeight = m_defaultFontHt;
}
m_fillPaint.setTextSize( textHeight );
int needWidth = 0;
for ( int ii = 0; ii < scoreInfo.length; ++ii ) {
m_fillPaint.getTextBounds( scoreInfo[ii], 0, scoreInfo[ii].length(),
m_boundsScratch );
if ( needWidth < m_boundsScratch.width() ) {
needWidth = m_boundsScratch.width();
}
}
if ( needWidth > rect.width() ) {
needWidth = rect.width();
}
width[0] = needWidth;
height[0] = rect.height();
}
public void score_drawPlayer( Rect rInner, Rect rOuter,
int gotPct, DrawScoreInfo dsi )
{
if ( 0 != (dsi.flags & CELL_ISCURSOR) ) {
fillRectOther( rOuter, CommonPrefs.COLOR_FOCUS );
} else if ( DEBUG_DRAWFRAMES && dsi.selected ) {
fillRectOther( rOuter, CommonPrefs.COLOR_FOCUS );
}
String[] texts = m_scores[dsi.playerNum];
int color = m_playerColors[dsi.playerNum];
if ( !m_prefs.allowPeek ) {
color = adjustColor( color );
}
m_fillPaint.setColor( color );
int height = rOuter.height() / texts.length;
rOuter.bottom = rOuter.top + height;
for ( String text : texts ) {
drawCentered( text, rOuter, null );
rOuter.offset( 0, height );
}
if ( DEBUG_DRAWFRAMES ) {
m_strokePaint.setColor( BLACK );
drawRect( rInner, m_strokePaint );
}
}
public void drawTimer( Rect rect, int player, int secondsLeft )
{
if ( (m_lastSecsLeft != secondsLeft || m_lastTimerPlayer != player) ) {
m_lastSecsLeft = secondsLeft;
m_lastTimerPlayer = player;
String negSign = secondsLeft < 0? "-":"";
secondsLeft = Math.abs( secondsLeft );
String time = String.format( "%s%d:%02d", negSign, secondsLeft/60,
secondsLeft%60 );
fillRectOther( rect, CommonPrefs.COLOR_BACKGRND );
m_fillPaint.setColor( m_playerColors[player] );
Rect shorter = new Rect( rect );
shorter.inset( 0, shorter.height() / 5 );
drawCentered( time, shorter, null );
m_jniThread.handle( JNIThread.JNICmd.CMD_DRAW );
}
}
public boolean boardBegin( Rect rect, int cellWidth, int cellHeight )
{
return true;
}
public boolean drawCell( final Rect rect, String text, int tile, int value,
int owner, int bonus, int hintAtts,
final int flags )
{
boolean canDraw = figureFontDims();
if ( canDraw ) {
int backColor;
boolean empty = 0 != (flags & (CELL_DRAGSRC|CELL_ISEMPTY));
boolean pending = 0 != (flags & CELL_HIGHLIGHT);
String bonusStr = null;
if ( m_inTrade ) {
fillRectOther( rect, CommonPrefs.COLOR_BACKGRND );
}
if ( owner < 0 ) {
owner = 0;
}
int foreColor = m_playerColors[owner];
if ( 0 != (flags & CELL_ISCURSOR) ) {
backColor = m_otherColors[CommonPrefs.COLOR_FOCUS];
} else if ( empty ) {
if ( 0 == bonus ) {
backColor = m_otherColors[CommonPrefs.COLOR_NOTILE];
} else {
backColor = m_bonusColors[bonus];
bonusStr = m_bonusSummaries[bonus];
}
} else if ( pending ) {
if ( darkOnLight() ) {
foreColor = WHITE;
backColor = BLACK;
} else {
foreColor = BLACK;
backColor = WHITE;
}
} else {
backColor = m_otherColors[CommonPrefs.COLOR_TILE_BACK];
}
fillRect( rect, adjustColor( backColor ) );
if ( empty ) {
if ( (CELL_ISSTAR & flags) != 0 ) {
m_origin.setBounds( rect );
m_origin.setAlpha( m_inTrade? IN_TRADE_ALPHA >> 24 : 255 );
m_origin.draw( BoardCanvas.this );
} else if ( null != bonusStr ) {
int color = m_otherColors[CommonPrefs.COLOR_BONUSHINT];
m_fillPaint.setColor( adjustColor(color) );
Rect brect = new Rect( rect );
brect.inset( 0, brect.height()/10 );
drawCentered( bonusStr, brect, m_fontDims );
}
} else {
m_fillPaint.setColor( adjustColor(foreColor) );
drawCentered( text, rect, m_fontDims );
}
if ( (CELL_ISBLANK & flags) != 0 ) {
markBlank( rect, backColor );
}
// frame the cell
m_strokePaint.setColor( adjustColor(FRAME_GREY) );
drawRect( rect, m_strokePaint );
drawCrosshairs( rect, flags );
}
return canDraw;
} // drawCell
private boolean m_arrowHintShown = false;
public void drawBoardArrow( Rect rect, int bonus, boolean vert,
int hintAtts, int flags )
{
// figure out if the background is more dark than light
boolean useDark = darkOnLight();
if ( m_blackArrow != useDark ) {
m_blackArrow = useDark;
m_downArrow = m_rightArrow = null;
}
Drawable arrow;
if ( vert ) {
if ( null == m_downArrow ) {
m_downArrow = loadAndRecolor( R.drawable.downarrow, useDark );
}
arrow = m_downArrow;
} else {
if ( null == m_rightArrow ) {
m_rightArrow = loadAndRecolor( R.drawable.rightarrow, useDark );
}
arrow = m_rightArrow;
}
rect.inset( 2, 2 );
arrow.setBounds( rect );
arrow.draw( BoardCanvas.this );
if ( !m_arrowHintShown ) {
m_arrowHintShown = true;
// Handler handler = getHandler();
// if ( null != handler ) {
// handler.post( new Runnable() {
// public void run() {
// m_parent.
// showNotAgainDlgThen( R.string.not_again_arrow,
// R.string.
// key_notagain_arrow );
// } } );
// }
}
}
public boolean trayBegin( Rect rect, int owner, int score )
{
m_trayOwner = owner;
m_pendingScore = score;
return true;
}
public boolean drawTile( Rect rect, String text, int val, int flags )
{
return drawTileImpl( rect, text, val, flags, true );
}
public boolean drawTileMidDrag( Rect rect, String text, int val, int owner,
int flags )
{
return drawTileImpl( rect, text, val, flags, false );
}
public boolean drawTileBack( Rect rect, int flags )
{
return drawTileImpl( rect, "?", -1, flags, true );
}
public void drawTrayDivider( Rect rect, int flags )
{
boolean isCursor = 0 != (flags & CELL_ISCURSOR);
boolean selected = 0 != (flags & CELL_HIGHLIGHT);
int index = isCursor? CommonPrefs.COLOR_FOCUS : CommonPrefs.COLOR_BACKGRND;
rect.inset( 0, 1 );
fillRectOther( rect, index );
rect.inset( rect.width()/4, 0 );
if ( selected ) {
drawRect( rect, m_strokePaint );
} else {
fillRect( rect, m_playerColors[m_trayOwner] );
}
}
public void score_pendingScore( Rect rect, int score, int playerNum,
int curTurn, int flags )
{
String text = score >= 0? String.format( "%d", score ) : "??";
int otherIndx = (0 == (flags & CELL_ISCURSOR))
? CommonPrefs.COLOR_BACKGRND : CommonPrefs.COLOR_FOCUS;
++rect.top;
fillRectOther( rect, otherIndx );
int playerColor = m_playerColors[playerNum];
if ( playerNum != curTurn ) {
playerColor &= NOT_TURN_ALPHA;
}
m_fillPaint.setColor( playerColor );
rect.bottom -= rect.height() / 2;
drawCentered( text, rect, null );
rect.offset( 0, rect.height() );
drawCentered( m_activity.getResources().getString( R.string.pts ),
rect, null );
}
public void objFinished( /*BoardObjectType*/int typ, Rect rect )
{
if ( DrawCtx.OBJ_BOARD == typ ) {
// On squat screens, where I can't use the full width for
// the board (without scrolling), the right-most cells
// don't draw their right borders due to clipping, so draw
// for them.
m_strokePaint.setColor( adjustColor(FRAME_GREY) );
int xx = rect.left + rect.width() - 1;
drawLine( xx, rect.top, xx, rect.top + rect.height(),
m_strokePaint );
}
}
public void dictChanged( final int dictPtr )
{
if ( m_dictPtr != dictPtr ) {
if ( 0 == dictPtr ) {
m_fontDims = null;
m_dictChars = null;
} else if ( m_dictPtr == 0 ||
!XwJNI.dict_tilesAreSame( m_dictPtr, dictPtr ) ) {
m_fontDims = null;
m_dictChars = null;
m_activity.runOnUiThread( new Runnable() {
public void run() {
m_dictChars = XwJNI.dict_getChars( dictPtr );
// draw again
if ( null != m_jniThread ) {
m_jniThread.handle( JNIThread.JNICmd
.CMD_INVALALL );
}
}
});
}
m_dictPtr = dictPtr;
}
}
private boolean drawTileImpl( Rect rect, String text, int val,
int flags, boolean clearBack )
{
boolean canDraw = figureFontDims();
if ( canDraw ) {
// boolean valHidden = (flags & CELL_VALHIDDEN) != 0;
boolean notEmpty = (flags & CELL_ISEMPTY) == 0;
boolean isCursor = (flags & CELL_ISCURSOR) != 0;
save( Canvas.CLIP_SAVE_FLAG );
rect.top += 1;
clipRect( rect );
if ( clearBack ) {
fillRectOther( rect, CommonPrefs.COLOR_BACKGRND );
}
if ( isCursor || notEmpty ) {
int color = m_otherColors[isCursor? CommonPrefs.COLOR_FOCUS
: CommonPrefs.COLOR_TILE_BACK];
if ( !clearBack ) {
color &= 0x7FFFFFFF; // translucent if being dragged.
}
fillRect( rect, color );
m_fillPaint.setColor( m_playerColors[m_trayOwner] );
if ( notEmpty ) {
positionDrawTile( rect, text, val );
Paint paint = getTileStrokePaint( rect );
drawRect( rect, paint ); // frame
if ( 0 != (flags & CELL_HIGHLIGHT) ) {
int width = (int)paint.getStrokeWidth();
rect.inset( width, width );
drawRect( rect, paint ); // frame
}
}
}
restoreToCount(1); // in case new canvas....
}
return canDraw;
} // drawTileImpl
private void drawCrosshairs( final Rect rect, final int flags )
{
int color = m_otherColors[CommonPrefs.COLOR_FOCUS];
if ( 0 != (flags & CELL_CROSSHOR) ) {
Rect hairRect = new Rect( rect );
hairRect.inset( 0, hairRect.height() / 3 );
fillRect( hairRect, color );
}
if ( 0 != (flags & CELL_CROSSVERT) ) {
Rect hairRect = new Rect( rect );
hairRect.inset( hairRect.width() / 3, 0 );
fillRect( hairRect, color );
}
}
private void drawCentered( String text, Rect rect, FontDims fontDims )
{
drawIn( text, rect, fontDims, Paint.Align.CENTER );
}
private void drawIn( String text, Rect rect, FontDims fontDims,
Paint.Align align )
{
int descent = -1;
int textSize;
if ( null == fontDims ) {
textSize = rect.height() - SCORE_HT_DROP;
} else {
int height = rect.height() - 4; // borders and padding, 2 each
descent = fontDims.descentFor( height );
textSize = fontDims.heightFor( height );
// DbgUtils.logf( "using descent: " + descent + " and textSize: "
// + textSize + " in height " + height );
}
m_fillPaint.setTextSize( textSize );
if ( descent == -1 ) {
descent = m_fillPaint.getFontMetricsInt().descent;
}
descent += 2;
m_fillPaint.getTextBounds( text, 0, text.length(), m_boundsScratch );
int extra = rect.width() - m_boundsScratch.width();
if ( 0 >= extra ) {
m_fillPaint.setTextAlign( Paint.Align.LEFT );
drawScaled( text, rect, m_boundsScratch, descent );
} else {
int bottom = rect.bottom - descent;
int origin = rect.left;
if ( Paint.Align.CENTER == align ) {
origin += rect.width() / 2;
} else {
origin += (extra / 5) - m_boundsScratch.left;
}
m_fillPaint.setTextAlign( align );
drawText( text, origin, bottom, m_fillPaint );
}
} // drawCentered
private void drawScaled( String text, final Rect rect,
Rect textBounds, int descent )
{
textBounds.bottom = rect.height();
Bitmap bitmap = Bitmap.createBitmap( textBounds.width(),
rect.height(),
Bitmap.Config.ARGB_8888 );
Canvas canvas = new Canvas( bitmap );
int bottom = textBounds.bottom - descent;
canvas.drawText( text, -textBounds.left, bottom, m_fillPaint );
drawBitmap( bitmap, null, rect, m_drawPaint );
}
private void positionDrawTile( final Rect rect, String text, int val )
{
final int offset = 2;
if ( null != text ) {
if ( null == m_letterRect ) {
m_letterRect = new Rect( 0, 0, rect.width() - offset,
rect.height() * 3 / 4 );
}
m_letterRect.offsetTo( rect.left + offset, rect.top + offset );
drawIn( text, m_letterRect, m_fontDims, Paint.Align.LEFT );
if ( FRAME_TRAY_RECTS ) {
drawRect( m_letterRect, m_strokePaint );
}
}
if ( val >= 0 ) {
int divisor = m_hasSmallScreen ? 3 : 4;
if ( null == m_valRect ) {
m_valRect = new Rect( 0, 0, rect.width() / divisor,
rect.height() / divisor );
m_valRect.inset( offset, offset );
}
m_valRect.offsetTo( rect.right - (rect.width() / divisor),
rect.bottom - (rect.height() / divisor) );
text = String.format( "%d", val );
m_fillPaint.setTextSize( m_valRect.height() );
m_fillPaint.setTextAlign( Paint.Align.RIGHT );
drawText( text, m_valRect.right, m_valRect.bottom,
m_fillPaint );
if ( FRAME_TRAY_RECTS ) {
drawRect( m_valRect, m_strokePaint );
}
}
}
private void fillRectOther( Rect rect, int index )
{
fillRect( rect, m_otherColors[index] );
}
private void fillRect( Rect rect, int color )
{
m_fillPaint.setColor( color );
drawRect( rect, m_fillPaint );
}
private boolean figureFontDims()
{
if ( null == m_fontDims && null != m_dictChars ) {
final int ht = 24;
final int width = 20;
Paint paint = new Paint(); // CommonPrefs.getFontFlags()??
paint.setStyle( Paint.Style.STROKE );
paint.setTextAlign( Paint.Align.LEFT );
paint.setTextSize( ht );
Bitmap bitmap = Bitmap.createBitmap( width, (ht*3)/2,
Bitmap.Config.ARGB_8888 );
Canvas canvas = new Canvas( bitmap );
// FontMetrics fmi = paint.getFontMetrics();
// DbgUtils.logf( "ascent: " + fmi.ascent );
// DbgUtils.logf( "bottom: " + fmi.bottom );
// DbgUtils.logf( "descent: " + fmi.descent );
// DbgUtils.logf( "leading: " + fmi.leading );
// DbgUtils.logf( "top : " + fmi.top );
// DbgUtils.logf( "using as baseline: " + ht );
Rect bounds = new Rect();
int maxWidth = 0;
for ( String str : m_dictChars ) {
if ( str.length() == 1 && str.charAt(0) >= 32 ) {
canvas.drawText( str, 0, ht, paint );
paint.getTextBounds( str, 0, 1, bounds );
if ( maxWidth < bounds.right ) {
maxWidth = bounds.right;
}
}
}
// for ( int row = 0; row < bitmap.getHeight(); ++row ) {
// StringBuffer sb = new StringBuffer( bitmap.getWidth() );
// for ( int col = 0; col < bitmap.getWidth(); ++col ) {
// int pixel = bitmap.getPixel( col, row );
// sb.append( pixel==0? "." : "X" );
// }
// DbgUtils.logf( sb.append(row).toString() );
// }
int topRow = 0;
findTop:
for ( int row = 0; row < bitmap.getHeight(); ++row ) {
for ( int col = 0; col < bitmap.getWidth(); ++col ) {
if ( 0 != bitmap.getPixel( col, row ) ){
topRow = row;
break findTop;
}
}
}
int bottomRow = 0;
findBottom:
for ( int row = bitmap.getHeight() - 1; row > topRow; --row ) {
for ( int col = 0; col < bitmap.getWidth(); ++col ) {
if ( 0 != bitmap.getPixel( col, row ) ){
bottomRow = row;
break findBottom;
}
}
}
m_fontDims = new FontDims( ht, topRow, bottomRow, maxWidth );
}
return null != m_fontDims;
} // figureFontDims
private int adjustColor( int color )
{
if ( m_inTrade ) {
color = color & IN_TRADE_ALPHA;
}
return color;
}
private boolean darkOnLight()
{
int background = m_otherColors[ CommonPrefs.COLOR_NOTILE ];
if ( background != m_backgroundUsed ) {
m_backgroundUsed = background;
m_darkOnLight = isLightColor( background );
}
return m_darkOnLight;
}
private void markBlank( final Rect rect, int backColor )
{
RectF oval = new RectF( rect.left, rect.top, rect.right, rect.bottom );
int curColor = 0;
boolean whiteOnBlack = !isLightColor( backColor );
if ( whiteOnBlack ) {
curColor = m_strokePaint.getColor();
m_strokePaint.setColor( WHITE );
}
drawArc( oval, 0, 360, false, m_strokePaint );
if ( whiteOnBlack ) {
m_strokePaint.setColor( curColor );
}
}
private Drawable loadAndRecolor( int resID, boolean useDark )
{
Resources res = m_activity.getResources();
Drawable arrow = res.getDrawable( resID );
if ( !useDark ) {
Bitmap src = ((BitmapDrawable)arrow).getBitmap();
Bitmap bitmap = src.copy( Bitmap.Config.ARGB_8888, true );
for ( int xx = 0; xx < bitmap.getWidth(); ++xx ) {
for( int yy = 0; yy < bitmap.getHeight(); ++yy ) {
if ( BLACK == bitmap.getPixel( xx, yy ) ) {
bitmap.setPixel( xx, yy, WHITE );
}
}
}
arrow = new BitmapDrawable(bitmap);
}
return arrow;
}
private boolean isLightColor( int color )
{
int sum = 0;
for ( int ii = 0; ii < 3; ++ii ) {
sum += color & 0xFF;
color >>= 8;
}
boolean result = sum > (127*3);
return result;
}
private Paint getTileStrokePaint( final Rect rect )
{
if ( null == m_tileStrokePaint ) {
Paint paint = new Paint();
paint.setStyle( Paint.Style.STROKE );
paint.setStrokeWidth( Math.max( 2, rect.width() / 20 ) );
m_tileStrokePaint = paint;
}
return m_tileStrokePaint;
}
}

View file

@ -23,9 +23,10 @@ package org.eehouse.android.xw4;
// Why does this have to be its own class... // Why does this have to be its own class...
public class BoardDims { public class BoardDims {
public int left, top;
public int width, height; // of the bitmap public int width, height; // of the bitmap
public int scoreHt; public int scoreWidth, scoreHt;
public int boardHt; public int boardWidth, boardHt;
public int trayTop, trayHt; public int trayTop, trayHt;
public int cellSize, maxCellSize; public int cellSize, maxCellSize;
public int timerWidth; public int timerWidth;
@ -37,6 +38,7 @@ public class BoardDims {
// + " left: " + left // + " left: " + left
// + " top: " + top // + " top: " + top
// + " scoreHt: " + scoreHt // + " scoreHt: " + scoreHt
// + " scoreWidth: " + scoreWidth
// + " boardHt: " + boardHt // + " boardHt: " + boardHt
// + " trayTop: " + trayTop // + " trayTop: " + trayTop
// + " trayHt: " + trayHt // + " trayHt: " + trayHt

View file

@ -40,7 +40,7 @@ public class ChatActivity extends XWActivity implements View.OnClickListener {
@Override @Override
public void onCreate( Bundle savedInstanceState ) public void onCreate( Bundle savedInstanceState )
{ {
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
super.onCreate( savedInstanceState ); super.onCreate( savedInstanceState );
setContentView( R.layout.chat ); setContentView( R.layout.chat );

View file

@ -37,7 +37,7 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String TABLE_NAME_DICTINFO = "dictinfo"; public static final String TABLE_NAME_DICTINFO = "dictinfo";
public static final String TABLE_NAME_GROUPS = "groups"; public static final String TABLE_NAME_GROUPS = "groups";
private static final String DB_NAME = "xwdb"; private static final String DB_NAME = "xwdb";
private static final int DB_VERSION = 17; private static final int DB_VERSION = 18;
public static final String GAME_NAME = "GAME_NAME"; public static final String GAME_NAME = "GAME_NAME";
public static final String VISID = "VISID"; public static final String VISID = "VISID";
@ -59,6 +59,7 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String HASMSGS = "HASMSGS"; public static final String HASMSGS = "HASMSGS";
public static final String CONTRACTED = "CONTRACTED"; public static final String CONTRACTED = "CONTRACTED";
public static final String SNAPSHOT = "SNAPSHOT"; public static final String SNAPSHOT = "SNAPSHOT";
public static final String THUMBNAIL = "THUMBNAIL";
public static final String CONTYPE = "CONTYPE"; public static final String CONTYPE = "CONTYPE";
public static final String SERVERROLE = "SERVERROLE"; public static final String SERVERROLE = "SERVERROLE";
public static final String ROOMNAME = "ROOMNAME"; public static final String ROOMNAME = "ROOMNAME";
@ -120,6 +121,7 @@ public class DBHelper extends SQLiteOpenHelper {
,{ CREATE_TIME, "INTEGER" } ,{ CREATE_TIME, "INTEGER" }
,{ LASTPLAY_TIME,"INTEGER" } ,{ LASTPLAY_TIME,"INTEGER" }
,{ SNAPSHOT, "BLOB" } ,{ SNAPSHOT, "BLOB" }
,{ THUMBNAIL, "BLOB" }
}; };
private static final String[][] s_obitsColsAndTypes = { private static final String[][] s_obitsColsAndTypes = {
@ -211,6 +213,8 @@ public class DBHelper extends SQLiteOpenHelper {
addSumColumn( db, VISID ); addSumColumn( db, VISID );
setColumnsEqual( db, TABLE_NAME_SUM, VISID, "rowid" ); setColumnsEqual( db, TABLE_NAME_SUM, VISID, "rowid" );
makeAutoincrement( db, TABLE_NAME_SUM, s_summaryColsAndTypes ); makeAutoincrement( db, TABLE_NAME_SUM, s_summaryColsAndTypes );
case 17:
addSumColumn( db, THUMBNAIL );
// nothing yet // nothing yet
break; break;
default: default:

View file

@ -25,15 +25,21 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.text.TextUtils; import android.text.TextUtils;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -41,7 +47,6 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.jni.*; import org.eehouse.android.xw4.jni.*;
@ -50,6 +55,7 @@ import org.eehouse.android.xw4.DictUtils.DictLoc;
public class DBUtils { public class DBUtils {
public static final int ROWID_NOTFOUND = -1; public static final int ROWID_NOTFOUND = -1;
public static final int GROUPID_UNSPEC = -1;
private static final String DICTS_SEP = ","; private static final String DICTS_SEP = ",";
@ -118,24 +124,23 @@ public class DBUtils {
{ {
initDB( context ); initDB( context );
GameSummary summary = null; GameSummary summary = null;
String[] columns = { ROW_ID,
DBHelper.NUM_MOVES, DBHelper.NUM_PLAYERS,
DBHelper.MISSINGPLYRS,
DBHelper.GAME_OVER, DBHelper.PLAYERS,
DBHelper.TURN, DBHelper.GIFLAGS,
DBHelper.CONTYPE, DBHelper.SERVERROLE,
DBHelper.ROOMNAME, DBHelper.RELAYID,
/*DBHelper.SMSPHONE,*/ DBHelper.SEED,
DBHelper.DICTLANG, DBHelper.GAMEID,
DBHelper.SCORES, DBHelper.HASMSGS,
DBHelper.LASTPLAY_TIME, DBHelper.REMOTEDEVS,
DBHelper.LASTMOVE, DBHelper.THUMBNAIL
};
String selection = String.format( ROW_ID_FMT, lock.getRowid() );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { ROW_ID,
DBHelper.NUM_MOVES, DBHelper.NUM_PLAYERS,
DBHelper.MISSINGPLYRS,
DBHelper.GAME_OVER, DBHelper.PLAYERS,
DBHelper.TURN, DBHelper.GIFLAGS,
DBHelper.CONTYPE, DBHelper.SERVERROLE,
DBHelper.ROOMNAME, DBHelper.RELAYID,
/*DBHelper.SMSPHONE,*/ DBHelper.SEED,
DBHelper.DICTLANG, DBHelper.GAMEID,
DBHelper.SCORES, DBHelper.HASMSGS,
DBHelper.LASTPLAY_TIME, DBHelper.REMOTEDEVS,
DBHelper.LASTMOVE
};
String selection = String.format( ROW_ID_FMT, lock.getRowid() );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -179,6 +184,18 @@ public class DBUtils {
summary.lastMoveTime = summary.lastMoveTime =
cursor.getInt(cursor.getColumnIndex(DBHelper.LASTMOVE)); cursor.getInt(cursor.getColumnIndex(DBHelper.LASTMOVE));
if ( BuildConstants.THUMBNAIL_SUPPORTED ) {
byte[] data =
cursor.getBlob( cursor.
getColumnIndex(DBHelper.THUMBNAIL));
if ( null != data ) {
Bitmap thumb =
BitmapFactory.decodeByteArray( data, 0,
data.length );
summary.setThumbnail( thumb );
}
}
String scoresStr = String scoresStr =
cursor.getString( cursor.getColumnIndex(DBHelper.SCORES)); cursor.getString( cursor.getColumnIndex(DBHelper.SCORES));
int[] scores = new int[summary.nPlayers]; int[] scores = new int[summary.nPlayers];
@ -239,7 +256,7 @@ public class DBUtils {
db.close(); db.close();
} }
if ( null == summary ) { if ( null == summary && lock.canWrite() ) {
summary = GameUtils.summarize( context, lock ); summary = GameUtils.summarize( context, lock );
saveSummary( context, lock, summary ); saveSummary( context, lock, summary );
} }
@ -259,6 +276,56 @@ public class DBUtils {
long rowid = lock.getRowid(); long rowid = lock.getRowid();
String selection = String.format( ROW_ID_FMT, rowid ); String selection = String.format( ROW_ID_FMT, rowid );
ContentValues values = null;
if ( null != summary ) {
values = new ContentValues();
values.put( DBHelper.NUM_MOVES, summary.nMoves );
values.put( DBHelper.NUM_PLAYERS, summary.nPlayers );
values.put( DBHelper.MISSINGPLYRS, summary.missingPlayers );
values.put( DBHelper.TURN, summary.turn );
values.put( DBHelper.GIFLAGS, summary.giflags() );
values.put( DBHelper.PLAYERS,
summary.summarizePlayers() );
values.put( DBHelper.DICTLANG, summary.dictLang );
values.put( DBHelper.GAMEID, summary.gameID );
values.put( DBHelper.GAME_OVER, summary.gameOver? 1 : 0 );
values.put( DBHelper.LASTMOVE, summary.lastMoveTime );
values.put( DBHelper.DICTLIST, summary.dictNames(DICTS_SEP) );
values.put( DBHelper.HASMSGS, summary.pendingMsgLevel );
if ( null != inviteID ) {
values.put( DBHelper.INVITEID, inviteID );
}
if ( null != summary.scores ) {
StringBuffer sb = new StringBuffer();
for ( int score : summary.scores ) {
sb.append( String.format( "%d ", score ) );
}
values.put( DBHelper.SCORES, sb.toString() );
}
if ( null != summary.conType ) {
values.put( DBHelper.CONTYPE, summary.conType.ordinal() );
values.put( DBHelper.SEED, summary.seed );
switch( summary.conType ) {
case COMMS_CONN_RELAY:
values.put( DBHelper.ROOMNAME, summary.roomName );
values.put( DBHelper.RELAYID, summary.relayID );
break;
case COMMS_CONN_BT:
case COMMS_CONN_SMS:
values.put( DBHelper.REMOTEDEVS,
summary.summarizeDevs() );
break;
}
}
values.put( DBHelper.SERVERROLE, summary.serverRole.ordinal() );
addThumb( summary.getThumbnail(), values );
}
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
@ -266,50 +333,6 @@ public class DBUtils {
if ( null == summary ) { if ( null == summary ) {
db.delete( DBHelper.TABLE_NAME_SUM, selection, null ); db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
} else { } else {
ContentValues values = new ContentValues();
values.put( DBHelper.NUM_MOVES, summary.nMoves );
values.put( DBHelper.NUM_PLAYERS, summary.nPlayers );
values.put( DBHelper.MISSINGPLYRS, summary.missingPlayers );
values.put( DBHelper.TURN, summary.turn );
values.put( DBHelper.GIFLAGS, summary.giflags() );
values.put( DBHelper.PLAYERS,
summary.summarizePlayers() );
values.put( DBHelper.DICTLANG, summary.dictLang );
values.put( DBHelper.GAMEID, summary.gameID );
values.put( DBHelper.GAME_OVER, summary.gameOver? 1 : 0 );
values.put( DBHelper.LASTMOVE, summary.lastMoveTime );
values.put( DBHelper.DICTLIST, summary.dictNames(DICTS_SEP) );
values.put( DBHelper.HASMSGS, summary.pendingMsgLevel );
if ( null != inviteID ) {
values.put( DBHelper.INVITEID, inviteID );
}
if ( null != summary.scores ) {
StringBuffer sb = new StringBuffer();
for ( int score : summary.scores ) {
sb.append( String.format( "%d ", score ) );
}
values.put( DBHelper.SCORES, sb.toString() );
}
if ( null != summary.conType ) {
values.put( DBHelper.CONTYPE, summary.conType.ordinal() );
values.put( DBHelper.SEED, summary.seed );
switch( summary.conType ) {
case COMMS_CONN_RELAY:
values.put( DBHelper.ROOMNAME, summary.roomName );
values.put( DBHelper.RELAYID, summary.relayID );
break;
case COMMS_CONN_BT:
case COMMS_CONN_SMS:
values.put( DBHelper.REMOTEDEVS,
summary.summarizeDevs() );
break;
}
}
values.put( DBHelper.SERVERROLE, summary.serverRole.ordinal() );
long result = db.update( DBHelper.TABLE_NAME_SUM, long result = db.update( DBHelper.TABLE_NAME_SUM,
values, selection, null ); values, selection, null );
Assert.assertTrue( result >= 0 ); Assert.assertTrue( result >= 0 );
@ -323,13 +346,13 @@ public class DBUtils {
public static int countGamesUsingLang( Context context, int lang ) public static int countGamesUsingLang( Context context, int lang )
{ {
int result = 0; int result = 0;
String selection = String.format( "%s = %d", DBHelper.DICTLANG,
lang );
// null for columns will return whole rows: bad
String[] columns = { DBHelper.DICTLANG };
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String selection = String.format( "%s = %d", DBHelper.DICTLANG,
lang );
// null for columns will return whole rows: bad
String[] columns = { DBHelper.DICTLANG };
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
@ -343,16 +366,16 @@ public class DBUtils {
public static int countGamesUsingDict( Context context, String dict ) public static int countGamesUsingDict( Context context, String dict )
{ {
int result = 0; int result = 0;
String pattern = String.format( "%%%s%s%s%%",
DICTS_SEP, dict, DICTS_SEP );
String selection = String.format( "%s LIKE '%s'",
DBHelper.DICTLIST, pattern );
// null for columns will return whole rows: bad. But
// might as well make it an int for speed
String[] columns = { DBHelper.DICTLANG };
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String pattern = String.format( "%%%s%s%s%%",
DICTS_SEP, dict, DICTS_SEP );
String selection = String.format( "%s LIKE '%s'",
DBHelper.DICTLIST, pattern );
// null for columns will return whole rows: bad. But
// might as well make it an int for speed
String[] columns = { DBHelper.DICTLANG };
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
result = cursor.getCount(); result = cursor.getCount();
@ -384,11 +407,11 @@ public class DBUtils {
int dflt ) int dflt )
{ {
int result = dflt; int result = dflt;
String selection = String.format( ROW_ID_FMT, rowid );
String[] columns = { column };
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String selection = String.format( ROW_ID_FMT, rowid );
String[] columns = { column };
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -417,14 +440,54 @@ public class DBUtils {
return 0 != getInt( context, rowid, DBHelper.GAME_OVER, 0 ); return 0 != getInt( context, rowid, DBHelper.GAME_OVER, 0 );
} }
public static void saveThumbnail( Context context, GameLock lock,
Bitmap thumb )
{
if ( BuildConstants.THUMBNAIL_SUPPORTED ) {
long rowid = lock.getRowid();
String selection = String.format( ROW_ID_FMT, rowid );
ContentValues values = new ContentValues();
addThumb( thumb, values );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
long result = db.update( DBHelper.TABLE_NAME_SUM,
values, selection, null );
Assert.assertTrue( result >= 0 );
db.close();
notifyListeners( rowid, false );
}
}
}
public static void clearThumbnails( Context context )
{
if ( BuildConstants.THUMBNAIL_SUPPORTED ) {
ContentValues values = new ContentValues();
values.putNull( DBHelper.THUMBNAIL );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
long result = db.update( DBHelper.TABLE_NAME_SUM,
values, null, null );
db.close();
notifyListeners( -1, true );
}
}
}
public static String getRelayID( Context context, long rowid ) public static String getRelayID( Context context, long rowid )
{ {
String result = null; String result = null;
String[] columns = { DBHelper.RELAYID };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.RELAYID };
String selection = String.format( ROW_ID_FMT, rowid );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -440,11 +503,11 @@ public class DBUtils {
public static long[] getRowIDsFor( Context context, String relayID ) public static long[] getRowIDsFor( Context context, String relayID )
{ {
long[] result = null; long[] result = null;
String[] columns = { ROW_ID };
String selection = DBHelper.RELAYID + "='" + relayID + "'";
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { ROW_ID };
String selection = DBHelper.RELAYID + "='" + relayID + "'";
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
result = new long[cursor.getCount()]; result = new long[cursor.getCount()];
@ -460,11 +523,11 @@ public class DBUtils {
public static long[] getRowIDsFor( Context context, int gameID ) public static long[] getRowIDsFor( Context context, int gameID )
{ {
long[] result = null; long[] result = null;
String[] columns = { ROW_ID };
String selection = String.format( DBHelper.GAMEID + "=%d", gameID );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { ROW_ID };
String selection = String.format( DBHelper.GAMEID + "=%d", gameID );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
result = new long[cursor.getCount()]; result = new long[cursor.getCount()];
@ -484,11 +547,11 @@ public class DBUtils {
public static boolean haveGame( Context context, long rowid ) public static boolean haveGame( Context context, long rowid )
{ {
boolean result = false; boolean result = false;
String[] columns = { ROW_ID };
String selection = String.format( ROW_ID + "=%d", rowid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { ROW_ID };
String selection = String.format( ROW_ID + "=%d", rowid );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
Assert.assertTrue( 1 >= cursor.getCount() ); Assert.assertTrue( 1 >= cursor.getCount() );
@ -554,16 +617,16 @@ public class DBUtils {
NetLaunchInfo nli ) NetLaunchInfo nli )
{ {
Date result = null; Date result = null;
String[] columns = { DBHelper.CREATE_TIME };
String selection =
String.format( "%s='%s' AND %s='%s' AND %s=%d AND %s=%d",
DBHelper.ROOMNAME, nli.room,
DBHelper.INVITEID, nli.inviteID,
DBHelper.DICTLANG, nli.lang,
DBHelper.NUM_PLAYERS, nli.nPlayersT );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.CREATE_TIME };
String selection =
String.format( "%s='%s' AND %s='%s' AND %s=%d AND %s=%d",
DBHelper.ROOMNAME, nli.room,
DBHelper.INVITEID, nli.inviteID,
DBHelper.DICTLANG, nli.lang,
DBHelper.NUM_PLAYERS, nli.nPlayersT );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, selection, null, null, null,
DBHelper.CREATE_TIME + " DESC" ); // order by DBHelper.CREATE_TIME + " DESC" ); // order by
@ -590,13 +653,13 @@ public class DBUtils {
public static String[] getRelayIDs( Context context, long[][] rowIDs ) public static String[] getRelayIDs( Context context, long[][] rowIDs )
{ {
String[] result = null; String[] result = null;
initDB( context ); String[] columns = { ROW_ID, DBHelper.RELAYID };
String selection = DBHelper.RELAYID + " NOT null";
ArrayList<String> ids = new ArrayList<String>(); ArrayList<String> ids = new ArrayList<String>();
initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { ROW_ID, DBHelper.RELAYID };
String selection = DBHelper.RELAYID + " NOT null";
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
@ -626,14 +689,14 @@ public class DBUtils {
public static void addDeceased( Context context, String relayID, public static void addDeceased( Context context, String relayID,
int seed ) int seed )
{ {
ContentValues values = new ContentValues();
values.put( DBHelper.RELAYID, relayID );
values.put( DBHelper.SEED, seed );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put( DBHelper.RELAYID, relayID );
values.put( DBHelper.SEED, seed );
try { try {
long result = db.replaceOrThrow( DBHelper.TABLE_NAME_OBITS, long result = db.replaceOrThrow( DBHelper.TABLE_NAME_OBITS,
"", values ); "", values );
@ -648,11 +711,11 @@ public class DBUtils {
{ {
Obit[] result = null; Obit[] result = null;
ArrayList<Obit> al = new ArrayList<Obit>(); ArrayList<Obit> al = new ArrayList<Obit>();
String[] columns = { DBHelper.RELAYID, DBHelper.SEED };
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.RELAYID, DBHelper.SEED };
Cursor cursor = db.query( DBHelper.TABLE_NAME_OBITS, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_OBITS, columns,
null, null, null, null, null ); null, null, null, null, null );
if ( 0 < cursor.getCount() ) { if ( 0 < cursor.getCount() ) {
@ -693,25 +756,27 @@ public class DBUtils {
} }
} }
public static GameLock saveNewGame( Context context, byte[] bytes ) public static GameLock saveNewGame( Context context, byte[] bytes,
long groupID )
{ {
Assert.assertTrue( GROUPID_UNSPEC != groupID );
GameLock lock = null; GameLock lock = null;
ContentValues values = new ContentValues();
values.put( DBHelper.SNAPSHOT, bytes );
long timestamp = new Date().getTime();
values.put( DBHelper.CREATE_TIME, timestamp );
values.put( DBHelper.LASTPLAY_TIME, timestamp );
values.put( DBHelper.GROUPID, groupID );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put( DBHelper.SNAPSHOT, bytes );
long timestamp = new Date().getTime();
values.put( DBHelper.CREATE_TIME, timestamp );
values.put( DBHelper.LASTPLAY_TIME, timestamp );
values.put( DBHelper.GROUPID,
XWPrefs.getDefaultNewGameGroup( context ) );
values.put( DBHelper.VISID, maxVISID( db ) ); values.put( DBHelper.VISID, maxVISID( db ) );
long rowid = db.insert( DBHelper.TABLE_NAME_SUM, null, values ); long rowid = db.insert( DBHelper.TABLE_NAME_SUM, null, values );
db.close();
setCached( rowid, null ); // force reread setCached( rowid, null ); // force reread
@ -740,7 +805,7 @@ public class DBUtils {
updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values ); updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values );
setCached( rowid, null ); // force reread setCached( rowid, null ); // force reread
if ( -1 != rowid ) { // Means new game? if ( ROWID_NOTFOUND != rowid ) { // Means new game?
notifyListeners( rowid, false ); notifyListeners( rowid, false );
} }
invalGroupsCache(); invalGroupsCache();
@ -750,15 +815,15 @@ public class DBUtils {
public static byte[] loadGame( Context context, GameLock lock ) public static byte[] loadGame( Context context, GameLock lock )
{ {
long rowid = lock.getRowid(); long rowid = lock.getRowid();
Assert.assertTrue( -1 != rowid ); Assert.assertTrue( ROWID_NOTFOUND != rowid );
byte[] result = getCached( rowid ); byte[] result = getCached( rowid );
if ( null == result ) { if ( null == result ) {
String[] columns = { DBHelper.SNAPSHOT };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.SNAPSHOT };
String selection = String.format( ROW_ID_FMT, rowid );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -790,10 +855,10 @@ public class DBUtils {
public static void deleteGame( Context context, GameLock lock ) public static void deleteGame( Context context, GameLock lock )
{ {
Assert.assertTrue( lock.canWrite() ); Assert.assertTrue( lock.canWrite() );
String selection = String.format( ROW_ID_FMT, lock.getRowid() );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection = String.format( ROW_ID_FMT, lock.getRowid() );
db.delete( DBHelper.TABLE_NAME_SUM, selection, null ); db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
db.close(); db.close();
} }
@ -802,13 +867,13 @@ public class DBUtils {
public static int getVisID( Context context, long rowid ) public static int getVisID( Context context, long rowid )
{ {
int result = -1; int result = ROWID_NOTFOUND;
String[] columns = { DBHelper.VISID };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.VISID };
String selection = String.format( ROW_ID_FMT, rowid );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -826,12 +891,12 @@ public class DBUtils {
public static String getName( Context context, long rowid ) public static String getName( Context context, long rowid )
{ {
String result = null; String result = null;
String[] columns = { DBHelper.GAME_NAME };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.GAME_NAME };
String selection = String.format( ROW_ID_FMT, rowid );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -855,7 +920,7 @@ public class DBUtils {
public static HistoryPair[] getChatHistory( Context context, long rowid ) public static HistoryPair[] getChatHistory( Context context, long rowid )
{ {
HistoryPair[] result = null; HistoryPair[] result = null;
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
final String localPrefix = context.getString( R.string.chat_local_id ); final String localPrefix = context.getString( R.string.chat_local_id );
String history = getChatHistoryStr( context, rowid ); String history = getChatHistoryStr( context, rowid );
if ( null != history ) { if ( null != history ) {
@ -892,9 +957,26 @@ public class DBUtils {
s_groupsCache = null; s_groupsCache = null;
} }
private static void addThumb( Bitmap thumb, ContentValues values )
{
if ( null == thumb ) {
values.putNull( DBHelper.THUMBNAIL );
} else {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
thumb.compress( CompressFormat.PNG, 0, bas );
values.put( DBHelper.THUMBNAIL, bas.toByteArray() );
}
}
// Return map of string (group name) to info about all games in // Return map of string (group name) to info about all games in
// that group. // that group.
public static HashMap<Long,GameGroupInfo> getGroups( Context context ) public static HashMap<Long,GameGroupInfo> getGroups( Context context )
{
return getGroups( context, 0 );
}
private static HashMap<Long,GameGroupInfo> getGroups( Context context,
int nRows )
{ {
if ( null == s_groupsCache ) { if ( null == s_groupsCache ) {
HashMap<Long,GameGroupInfo> result = HashMap<Long,GameGroupInfo> result =
@ -902,6 +984,7 @@ public class DBUtils {
initDB( context ); initDB( context );
String[] columns = { ROW_ID, DBHelper.GROUPNAME, String[] columns = { ROW_ID, DBHelper.GROUPNAME,
DBHelper.EXPANDED }; DBHelper.EXPANDED };
String limit = 0 == nRows ? null : String.format( "%d", nRows );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_GROUPS, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_GROUPS, columns,
@ -909,7 +992,8 @@ public class DBUtils {
null, // args null, // args
null, // groupBy null, // groupBy
null, // having null, // having
null //orderby null, //orderby
limit
); );
int idIndex = cursor.getColumnIndex( ROW_ID ); int idIndex = cursor.getColumnIndex( ROW_ID );
int nameIndex = cursor.getColumnIndex( DBHelper.GROUPNAME ); int nameIndex = cursor.getColumnIndex( DBHelper.GROUPNAME );
@ -926,9 +1010,9 @@ public class DBUtils {
Iterator<Long> iter = result.keySet().iterator(); Iterator<Long> iter = result.keySet().iterator();
while ( iter.hasNext() ) { while ( iter.hasNext() ) {
Long id = iter.next(); Long groupID = iter.next();
GameGroupInfo ggi = result.get( id ); GameGroupInfo ggi = result.get( groupID );
readTurnInfo( db, id, ggi ); readTurnInfo( db, groupID, ggi );
} }
db.close(); db.close();
@ -938,13 +1022,31 @@ public class DBUtils {
return s_groupsCache; return s_groupsCache;
} // getGroups } // getGroups
private static void readTurnInfo( SQLiteDatabase db, long id, // public static void unhideTo( Context context, long groupID )
// {
// Assert.assertTrue( GROUPID_UNSPEC != groupID );
// initDB( context );
// synchronized( s_dbHelper ) {
// SQLiteDatabase db = s_dbHelper.getWritableDatabase();
// ContentValues values = new ContentValues();
// values.put( DBHelper.GROUPID, groupID );
// String selection = String.format( "%s = %d", DBHelper.GROUPID,
// GROUPID_UNSPEC );
// long result = db.update( DBHelper.TABLE_NAME_SUM,
// values, selection, null );
// db.close();
// notifyListeners( ROWID_NOTFOUND, true );
// }
// }
private static void readTurnInfo( SQLiteDatabase db, long groupID,
GameGroupInfo ggi ) GameGroupInfo ggi )
{ {
String[] columns = { DBHelper.LASTMOVE, DBHelper.GIFLAGS, String[] columns = { DBHelper.LASTMOVE, DBHelper.GIFLAGS,
DBHelper.TURN }; DBHelper.TURN };
String orderBy = DBHelper.LASTMOVE; String orderBy = DBHelper.LASTMOVE;
String selection = String.format( "%s=%d", DBHelper.GROUPID, id ); String selection = String.format( "%s=%d", DBHelper.GROUPID, groupID );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, selection,
null, // args null, // args
@ -997,9 +1099,9 @@ public class DBUtils {
initDB( context ); initDB( context );
String[] columns = { ROW_ID }; String[] columns = { ROW_ID };
String selection = String.format( "%s=%d", DBHelper.GROUPID, groupID ); String selection = String.format( "%s=%d", DBHelper.GROUPID, groupID );
String orderBy = DBHelper.CREATE_TIME + " DESC";
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String orderBy = DBHelper.CREATE_TIME + " DESC";
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, // selection selection, // selection
null, // args null, // args
@ -1020,39 +1122,21 @@ public class DBUtils {
return result; return result;
} }
public static Set<Long> getAllGames( Context context ) // pass ROWID_NOTFOUND to get *any* group. Because there may be
{ // some hidden games stored with group = -1 thanks to
HashSet<Long> result = null; // recently-fixed bugs, be sure to skip them.
initDB( context );
String[] columns = { ROW_ID };
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
null, // selection
null, // args
null, // groupBy
null, // having
null
);
int index = cursor.getColumnIndex( ROW_ID );
result = new HashSet<Long>( cursor.getCount() );
for ( int ii = 0; cursor.moveToNext(); ++ii ) {
long rowid = cursor.getInt( index );
result.add( rowid );
}
cursor.close();
db.close();
}
return result;
}
public static long getGroupForGame( Context context, long rowid ) public static long getGroupForGame( Context context, long rowid )
{ {
long result = ROWID_NOTFOUND; long result = GROUPID_UNSPEC;
initDB( context ); initDB( context );
String[] columns = { DBHelper.GROUPID }; String[] columns = { DBHelper.GROUPID };
String selection = String.format( ROW_ID_FMT, rowid );
String selection = String.format( "%s != %d", DBHelper.GROUPID,
DBUtils.GROUPID_UNSPEC );
if ( ROWID_NOTFOUND != rowid ) {
selection += " AND " + String.format( ROW_ID_FMT, rowid );
}
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
@ -1072,15 +1156,27 @@ public class DBUtils {
return result; return result;
} }
public static long getAnyGroup( Context context )
{
long result = GROUPID_UNSPEC;
HashMap<Long,GameGroupInfo> groups = getGroups( context, 1 );
Iterator<Long> iter = groups.keySet().iterator();
if ( iter.hasNext() ) {
result = iter.next();
}
Assert.assertTrue( GROUPID_UNSPEC != result );
return result;
}
public static long addGroup( Context context, String name ) public static long addGroup( Context context, String name )
{ {
long rowid = ROWID_NOTFOUND; long rowid = GROUPID_UNSPEC;
if ( null != name && 0 < name.length() ) { if ( null != name && 0 < name.length() ) {
HashMap<Long,GameGroupInfo> gameInfo = getGroups( context ); HashMap<Long,GameGroupInfo> gameInfo = getGroups( context );
if ( null == gameInfo.get( name ) ) { if ( null == gameInfo.get( name ) ) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put( DBHelper.GROUPNAME, name ); values.put( DBHelper.GROUPNAME, name );
values.put( DBHelper.EXPANDED, 0 ); values.put( DBHelper.EXPANDED, 1 );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
@ -1097,17 +1193,17 @@ public class DBUtils {
public static void deleteGroup( Context context, long groupid ) public static void deleteGroup( Context context, long groupid )
{ {
// Nuke games having this group id
String selection =
String.format( "%s=%d", DBHelper.GROUPID, groupid );
// And nuke the group record itself
selection = String.format( ROW_ID_FMT, groupid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
// Nuke games having this group id
String selection =
String.format( "%s=%d", DBHelper.GROUPID, groupid );
db.delete( DBHelper.TABLE_NAME_SUM, selection, null ); db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
// And nuke the group record itself
selection = String.format( ROW_ID_FMT, groupid );
db.delete( DBHelper.TABLE_NAME_GROUPS, selection, null ); db.delete( DBHelper.TABLE_NAME_GROUPS, selection, null );
db.close(); db.close();
@ -1134,23 +1230,24 @@ public class DBUtils {
} }
// Change group id of a game // Change group id of a game
public static void moveGame( Context context, long gameid, long groupid ) public static void moveGame( Context context, long gameid, long groupID )
{ {
Assert.assertTrue( GROUPID_UNSPEC != groupID );
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put( DBHelper.GROUPID, groupid ); values.put( DBHelper.GROUPID, groupID );
updateRow( context, DBHelper.TABLE_NAME_SUM, gameid, values ); updateRow( context, DBHelper.TABLE_NAME_SUM, gameid, values );
} }
private static String getChatHistoryStr( Context context, long rowid ) private static String getChatHistoryStr( Context context, long rowid )
{ {
String result = null; String result = null;
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
String[] columns = { DBHelper.CHAT_HISTORY };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.CHAT_HISTORY };
String selection = String.format( ROW_ID_FMT, rowid );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -1169,7 +1266,7 @@ public class DBUtils {
public static void appendChatHistory( Context context, long rowid, public static void appendChatHistory( Context context, long rowid,
String msg, boolean local ) String msg, boolean local )
{ {
if ( GitVersion.CHAT_SUPPORTED ) { if ( BuildConstants.CHAT_SUPPORTED ) {
Assert.assertNotNull( msg ); Assert.assertNotNull( msg );
int id = local ? R.string.chat_local_id : R.string.chat_other_id; int id = local ? R.string.chat_local_id : R.string.chat_other_id;
msg = context.getString( id ) + msg; msg = context.getString( id ) + msg;
@ -1246,15 +1343,15 @@ public class DBUtils {
{ {
Assert.assertTrue( DictLoc.UNKNOWN != loc ); Assert.assertTrue( DictLoc.UNKNOWN != loc );
DictBrowseState result = null; DictBrowseState result = null;
String[] columns = { DBHelper.ITERPOS, DBHelper.ITERTOP,
DBHelper.ITERMIN, DBHelper.ITERMAX,
DBHelper.WORDCOUNTS, DBHelper.ITERPREFIX };
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
name, DBHelper.LOC, loc.ordinal() );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.ITERPOS, DBHelper.ITERTOP,
DBHelper.ITERMIN, DBHelper.ITERMAX,
DBHelper.WORDCOUNTS, DBHelper.ITERPREFIX };
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
name, DBHelper.LOC, loc.ordinal() );
Cursor cursor = db.query( DBHelper.TABLE_NAME_DICTBROWSE, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_DICTBROWSE, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 >= cursor.getCount() && cursor.moveToFirst() ) { if ( 1 >= cursor.getCount() && cursor.moveToFirst() ) {
@ -1293,25 +1390,26 @@ public class DBUtils {
DictLoc loc, DictBrowseState state ) DictLoc loc, DictBrowseState state )
{ {
Assert.assertTrue( DictLoc.UNKNOWN != loc ); Assert.assertTrue( DictLoc.UNKNOWN != loc );
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
name, DBHelper.LOC, loc.ordinal() );
ContentValues values = new ContentValues();
values.put( DBHelper.ITERPOS, state.m_pos );
values.put( DBHelper.ITERTOP, state.m_top );
values.put( DBHelper.ITERMIN, state.m_minShown );
values.put( DBHelper.ITERMAX, state.m_maxShown );
values.put( DBHelper.ITERPREFIX, state.m_prefix );
if ( null != state.m_counts ) {
String[] nums = new String[state.m_counts.length];
for ( int ii = 0; ii < nums.length; ++ii ) {
nums[ii] = String.format( "%d", state.m_counts[ii] );
}
values.put( DBHelper.WORDCOUNTS, TextUtils.join( ":", nums ) );
}
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
name, DBHelper.LOC, loc.ordinal() );
ContentValues values = new ContentValues();
values.put( DBHelper.ITERPOS, state.m_pos );
values.put( DBHelper.ITERTOP, state.m_top );
values.put( DBHelper.ITERMIN, state.m_minShown );
values.put( DBHelper.ITERMAX, state.m_maxShown );
values.put( DBHelper.ITERPREFIX, state.m_prefix );
if ( null != state.m_counts ) {
String[] nums = new String[state.m_counts.length];
for ( int ii = 0; ii < nums.length; ++ii ) {
nums[ii] = String.format( "%d", state.m_counts[ii] );
}
values.put( DBHelper.WORDCOUNTS, TextUtils.join( ":", nums ) );
}
int result = db.update( DBHelper.TABLE_NAME_DICTBROWSE, int result = db.update( DBHelper.TABLE_NAME_DICTBROWSE,
values, selection, null ); values, selection, null );
if ( 0 == result ) { if ( 0 == result ) {
@ -1334,12 +1432,12 @@ public class DBUtils {
// Called from jni // Called from jni
public static void dictsSetMD5Sum( Context context, String name, String sum ) public static void dictsSetMD5Sum( Context context, String name, String sum )
{ {
String selection = String.format( NAME_FMT, DBHelper.DICTNAME, name );
ContentValues values = new ContentValues();
values.put( DBHelper.MD5SUM, sum );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection = String.format( NAME_FMT, DBHelper.DICTNAME, name );
ContentValues values = new ContentValues();
values.put( DBHelper.MD5SUM, sum );
int result = db.update( DBHelper.TABLE_NAME_DICTINFO, int result = db.update( DBHelper.TABLE_NAME_DICTINFO,
values, selection, null ); values, selection, null );
if ( 0 == result ) { if ( 0 == result ) {
@ -1353,14 +1451,14 @@ public class DBUtils {
public static DictInfo dictsGetInfo( Context context, String name ) public static DictInfo dictsGetInfo( Context context, String name )
{ {
DictInfo result = null; DictInfo result = null;
String[] columns = { DBHelper.LANGCODE,
DBHelper.WORDCOUNT,
DBHelper.MD5SUM,
DBHelper.LOC };
String selection = String.format( NAME_FMT, DBHelper.DICTNAME, name );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.LANGCODE,
DBHelper.WORDCOUNT,
DBHelper.MD5SUM,
DBHelper.LOC };
String selection = String.format( NAME_FMT, DBHelper.DICTNAME, name );
Cursor cursor = db.query( DBHelper.TABLE_NAME_DICTINFO, columns, Cursor cursor = db.query( DBHelper.TABLE_NAME_DICTINFO, columns,
selection, null, null, null, null ); selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -1382,18 +1480,18 @@ public class DBUtils {
public static void dictsSetInfo( Context context, DictUtils.DictAndLoc dal, public static void dictsSetInfo( Context context, DictUtils.DictAndLoc dal,
DictInfo info ) DictInfo info )
{ {
String selection =
String.format( NAME_FMT, DBHelper.DICTNAME, dal.name );
ContentValues values = new ContentValues();
values.put( DBHelper.LANGCODE, info.langCode );
values.put( DBHelper.WORDCOUNT, info.wordCount );
values.put( DBHelper.MD5SUM, info.md5Sum );
values.put( DBHelper.LOC, dal.loc.ordinal() );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection =
String.format( NAME_FMT, DBHelper.DICTNAME, dal.name );
ContentValues values = new ContentValues();
values.put( DBHelper.LANGCODE, info.langCode );
values.put( DBHelper.WORDCOUNT, info.wordCount );
values.put( DBHelper.MD5SUM, info.md5Sum );
values.put( DBHelper.LOC, dal.loc.ordinal() );
int result = db.update( DBHelper.TABLE_NAME_DICTINFO, int result = db.update( DBHelper.TABLE_NAME_DICTINFO,
values, selection, null ); values, selection, null );
if ( 0 == result ) { if ( 0 == result ) {
@ -1407,16 +1505,17 @@ public class DBUtils {
public static void dictsMoveInfo( Context context, String name, public static void dictsMoveInfo( Context context, String name,
DictLoc fromLoc, DictLoc toLoc ) DictLoc fromLoc, DictLoc toLoc )
{ {
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
name, DBHelper.LOC, fromLoc.ordinal() );
ContentValues values = new ContentValues();
values.put( DBHelper.LOC, toLoc.ordinal() );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
name, DBHelper.LOC, fromLoc.ordinal() );
ContentValues values = new ContentValues();
values.put( DBHelper.LOC, toLoc.ordinal() );
db.update( DBHelper.TABLE_NAME_DICTINFO, values, selection, null ); db.update( DBHelper.TABLE_NAME_DICTINFO, values, selection, null );
db.update( DBHelper.TABLE_NAME_DICTBROWSE, values, selection, null); db.update( DBHelper.TABLE_NAME_DICTBROWSE, values, selection, null );
db.close(); db.close();
} }
} }
@ -1424,12 +1523,13 @@ public class DBUtils {
public static void dictsRemoveInfo( Context context, public static void dictsRemoveInfo( Context context,
DictUtils.DictAndLoc dal ) DictUtils.DictAndLoc dal )
{ {
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
dal.name, DBHelper.LOC, dal.loc.ordinal() );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
dal.name, DBHelper.LOC, dal.loc.ordinal() );
db.delete( DBHelper.TABLE_NAME_DICTINFO, selection, null ); db.delete( DBHelper.TABLE_NAME_DICTINFO, selection, null );
db.delete( DBHelper.TABLE_NAME_DICTBROWSE, selection, null ); db.delete( DBHelper.TABLE_NAME_DICTBROWSE, selection, null );
db.close(); db.close();
@ -1500,12 +1600,12 @@ public class DBUtils {
private static void updateRow( Context context, String table, private static void updateRow( Context context, String table,
long rowid, ContentValues values ) long rowid, ContentValues values )
{ {
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase(); SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection = String.format( ROW_ID_FMT, rowid );
int result = db.update( table, values, selection, null ); int result = db.update( table, values, selection, null );
db.close(); db.close();
if ( 0 == result ) { if ( 0 == result ) {

View file

@ -40,7 +40,7 @@ import java.util.Set;
public class DbgUtils { public class DbgUtils {
private static final String TAG = "XW4"; private static final String TAG = "XW4";
private static boolean s_doLog = true; private static boolean s_doLog = BuildConstants.IS_DEBUG_BUILD;
private static Time s_time = new Time(); private static Time s_time = new Time();
@ -52,13 +52,10 @@ public class DbgUtils {
public static void logEnable( Context context ) public static void logEnable( Context context )
{ {
boolean on; boolean on;
if ( XWApp.DEBUG ) { if ( XWApp.DEBUG || BuildConstants.IS_DEBUG_BUILD ) {
on = true; on = true;
} else { } else {
SharedPreferences sp on = XWPrefs.getPrefsBoolean( context, R.string.key_logging_on, false );
= PreferenceManager.getDefaultSharedPreferences( context );
String key = context.getString( R.string.key_logging_on );
on = sp.getBoolean( key, false );
} }
logEnable( on ); logEnable( on );
} }

View file

@ -128,6 +128,9 @@ public class DictBrowseActivity extends XWListActivity
// SectionIndexer // SectionIndexer
public int getPositionForSection( int section ) public int getPositionForSection( int section )
{ {
if ( section >= m_indices.length ) {
section = m_indices.length - 1;
}
return m_indices[section]; return m_indices[section];
} }
@ -301,7 +304,7 @@ public class DictBrowseActivity extends XWListActivity
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
////////////////////////////////////////////////// //////////////////////////////////////////////////
@Override @Override
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
Assert.assertTrue( FINISH_ACTION == id ); Assert.assertTrue( FINISH_ACTION == id );
finish(); finish();

View file

@ -20,6 +20,7 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.text.TextUtils;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
@ -33,8 +34,6 @@ import android.database.DataSetObserver;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextMenu;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -43,24 +42,28 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ExpandableListAdapter; import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.AdapterView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.TextView; import android.widget.TextView;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.DictUtils.DictAndLoc; import org.eehouse.android.xw4.DictUtils.DictAndLoc;
import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIUtilsImpl; import org.eehouse.android.xw4.jni.JNIUtilsImpl;
import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.DictUtils.DictLoc; import org.eehouse.android.xw4.DictUtils.DictLoc;
public class DictsActivity extends XWExpandableListActivity public class DictsActivity extends XWExpandableListActivity
implements View.OnClickListener, XWListItem.DeleteCallback, implements View.OnClickListener, AdapterView.OnItemLongClickListener,
MountEventReceiver.SDCardNotifiee, DlgDelegate.DlgClickNotify, SelectableItem, MountEventReceiver.SDCardNotifiee,
DlgDelegate.DlgClickNotify,
DictImportActivity.DownloadFinishedListener { DictImportActivity.DownloadFinishedListener {
private static interface SafePopup { private static interface SafePopup {
@ -71,11 +74,6 @@ public class DictsActivity extends XWExpandableListActivity
private static final String DICT_DOLAUNCH = "do_launch"; private static final String DICT_DOLAUNCH = "do_launch";
private static final String DICT_LANG_EXTRA = "use_lang"; private static final String DICT_LANG_EXTRA = "use_lang";
private static final String DICT_NAME_EXTRA = "use_dict"; private static final String DICT_NAME_EXTRA = "use_dict";
private static final String PACKED_POSITION = "packed_position";
private static final String DELETE_DICT = "delete_dict";
private static final String NAME = "name";
private static final String LANG = "lang";
private static final String MOVEFROMLOC = "movefromloc";
private HashSet<String> m_closedLangs; private HashSet<String> m_closedLangs;
@ -90,19 +88,14 @@ public class DictsActivity extends XWExpandableListActivity
// settle for a hash on the side. // settle for a hash on the side.
private static HashMap<MenuItem, DictAndLoc> s_itemData; private static HashMap<MenuItem, DictAndLoc> s_itemData;
private int m_lang = 0;
private String[] m_langs; private String[] m_langs;
private String m_name = null;
private String m_deleteDict = null;
private String m_download; private String m_download;
private ExpandableListView m_expView; private ExpandableListView m_expView;
private String[] m_locNames; private String[] m_locNames;
private DictListAdapter m_adapter; private DictListAdapter m_adapter;
private HashSet<XWListItem> m_selDicts;
private CharSequence m_origTitle;
private long m_packedPosition;
private DictLoc m_moveFromLoc;
private int m_moveFromItem;
private int m_moveToItm;
private boolean m_launchedForMissing = false; private boolean m_launchedForMissing = false;
private LayoutInflater m_factory; private LayoutInflater m_factory;
@ -144,6 +137,7 @@ public class DictsActivity extends XWExpandableListActivity
if ( null == view ) { if ( null == view ) {
view = (XWListItem) view = (XWListItem)
m_factory.inflate( R.layout.list_item, null ); m_factory.inflate( R.layout.list_item, null );
view.setSelCB( DictsActivity.this );
int lang = (int)getGroupId( groupPosition ); int lang = (int)getGroupId( groupPosition );
DictAndLoc[] dals = DictLangCache.getDALsHaveLang( m_context, DictAndLoc[] dals = DictLangCache.getDALsHaveLang( m_context,
@ -157,9 +151,6 @@ public class DictsActivity extends XWExpandableListActivity
DictLoc loc = dal.loc; DictLoc loc = dal.loc;
view.setComment( m_locNames[loc.ordinal()] ); view.setComment( m_locNames[loc.ordinal()] );
view.cache( loc ); view.cache( loc );
if ( DictLoc.BUILT_IN != loc ) {
view.setDeleteCallback( DictsActivity.this );
}
} else { } else {
view.setText( m_download ); view.setText( m_download );
} }
@ -237,11 +228,8 @@ public class DictsActivity extends XWExpandableListActivity
protected XWListItem getSelChildView() protected XWListItem getSelChildView()
{ {
int groupPosition = Assert.assertTrue( 1 == m_selDicts.size() );
ExpandableListView.getPackedPositionGroup( m_packedPosition ); return m_selDicts.iterator().next();
int childPosition =
ExpandableListView.getPackedPositionChild( m_packedPosition );
return (XWListItem)getChildView( groupPosition, childPosition );
} }
private void addToCache( int group, int child, XWListItem view ) private void addToCache( int group, int child, XWListItem view )
@ -268,45 +256,48 @@ public class DictsActivity extends XWExpandableListActivity
switch( id ) { switch( id ) {
case MOVE_DICT: case MOVE_DICT:
final XWListItem[] selItems = getSelItems();
final int[] moveTo = { -1 };
message = Utils.format( this, R.string.move_dictf, message = Utils.format( this, R.string.move_dictf,
m_adapter.getSelChildView().getText() ); getJoinedNames( selItems ) );
OnClickListener newSelLstnr = OnClickListener newSelLstnr =
new OnClickListener() { new OnClickListener() {
public void onClick( DialogInterface dlgi, int item ) { public void onClick( DialogInterface dlgi, int item ) {
m_moveToItm = item; moveTo[0] = item;
AlertDialog dlg = (AlertDialog)dlgi; AlertDialog dlg = (AlertDialog)dlgi;
Button btn = Button btn =
dlg.getButton( AlertDialog.BUTTON_POSITIVE ); dlg.getButton( AlertDialog.BUTTON_POSITIVE );
btn.setEnabled( m_moveToItm != m_moveFromItem ); btn.setEnabled( true );
} }
}; };
lstnr = new OnClickListener() { lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) { public void onClick( DialogInterface dlg, int item ) {
XWListItem rowView = m_adapter.getSelChildView(); DictLoc toLoc = itemToRealLoc( moveTo[0] );
Assert.assertTrue( m_moveToItm != m_moveFromItem ); for ( XWListItem selItem : selItems ) {
DictLoc toLoc = itemToRealLoc( m_moveToItm ); DictLoc fromLoc = (DictLoc)selItem.getCached();
if ( DictUtils.moveDict( DictsActivity.this, String name = selItem.getText();
rowView.getText(), if ( fromLoc == toLoc ) {
m_moveFromLoc, DbgUtils.logf( "not moving %s: same loc", name );
toLoc ) ) { } else if ( DictUtils.moveDict( DictsActivity.this,
rowView.setComment( m_locNames[toLoc.ordinal()] ); name, fromLoc,
rowView.cache( toLoc ); toLoc ) ) {
rowView.invalidate(); selItem.setComment( m_locNames[toLoc.ordinal()] );
DBUtils.dictsMoveInfo( DictsActivity.this, selItem.cache( toLoc );
rowView.getText(), selItem.invalidate();
m_moveFromLoc, toLoc ); DBUtils.dictsMoveInfo( DictsActivity.this,
} else { name, fromLoc, toLoc );
DbgUtils.logf( "moveDict(%s) failed", } else {
rowView.getText() ); DbgUtils.logf( "moveDict(%s) failed", name );
}
} }
} }
}; };
dialog = new AlertDialog.Builder( this ) dialog = new AlertDialog.Builder( this )
.setTitle( message ) .setTitle( message )
.setSingleChoiceItems( makeDictDirItems(), m_moveFromItem, .setSingleChoiceItems( makeDictDirItems(), moveTo[0],
newSelLstnr ) newSelLstnr )
.setPositiveButton( R.string.button_move, lstnr ) .setPositiveButton( R.string.button_move, lstnr )
.setNegativeButton( R.string.button_cancel, null ) .setNegativeButton( R.string.button_cancel, null )
@ -314,22 +305,22 @@ public class DictsActivity extends XWExpandableListActivity
break; break;
case SET_DEFAULT: case SET_DEFAULT:
final XWListItem row = m_selDicts.iterator().next();
lstnr = new OnClickListener() { lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) { public void onClick( DialogInterface dlg, int item ) {
if ( DialogInterface.BUTTON_NEGATIVE == item if ( DialogInterface.BUTTON_NEGATIVE == item
|| DialogInterface.BUTTON_POSITIVE == item ) { || DialogInterface.BUTTON_POSITIVE == item ) {
setDefault( R.string.key_default_dict ); setDefault( row, R.string.key_default_dict );
} }
if ( DialogInterface.BUTTON_NEGATIVE == item if ( DialogInterface.BUTTON_NEGATIVE == item
|| DialogInterface.BUTTON_NEUTRAL == item ) { || DialogInterface.BUTTON_NEUTRAL == item ) {
setDefault( R.string.key_default_robodict ); setDefault( row, R.string.key_default_robodict );
} }
} }
}; };
XWListItem rowView = m_adapter.getSelChildView(); String name = row.getText();
String lang = String lang = DictLangCache.getLangName( this, name);
DictLangCache.getLangName( this, rowView.getText() ); message = getString( R.string.set_default_messagef, name, lang );
message = getString( R.string.set_default_messagef, lang );
dialog = new AlertDialog.Builder( this ) dialog = new AlertDialog.Builder( this )
.setTitle( R.string.query_title ) .setTitle( R.string.query_title )
.setMessage( message ) .setMessage( message )
@ -392,7 +383,6 @@ public class DictsActivity extends XWExpandableListActivity
protected void onCreate( Bundle savedInstanceState ) protected void onCreate( Bundle savedInstanceState )
{ {
super.onCreate( savedInstanceState ); super.onCreate( savedInstanceState );
getBundledData( savedInstanceState );
m_closedLangs = new HashSet<String>(); m_closedLangs = new HashSet<String>();
String[] closed = XWPrefs.getClosedLangs( this ); String[] closed = XWPrefs.getClosedLangs( this );
@ -411,15 +401,17 @@ public class DictsActivity extends XWExpandableListActivity
setContentView( R.layout.dict_browse ); setContentView( R.layout.dict_browse );
m_expView = getExpandableListView(); m_expView = getExpandableListView();
registerForContextMenu( m_expView ); m_expView.setOnItemLongClickListener( this );
Button download = (Button)findViewById( R.id.download ); Button download = (Button)findViewById( R.id.download );
download.setOnClickListener( this ); if ( ABUtils.haveActionBar() ) {
download.setVisibility( View.GONE );
} else {
download.setOnClickListener( this );
}
mkListAdapter(); mkListAdapter();
m_selDicts = new HashSet<XWListItem>();
// showNotAgainDlg( R.string.not_again_dicts,
// R.string.key_notagain_dicts );
Intent intent = getIntent(); Intent intent = getIntent();
if ( null != intent ) { if ( null != intent ) {
@ -436,6 +428,8 @@ public class DictsActivity extends XWExpandableListActivity
downloadNewDict( intent ); downloadNewDict( intent );
} }
} }
m_origTitle = getTitle();
} // onCreate } // onCreate
@Override @Override
@ -448,35 +442,6 @@ public class DictsActivity extends XWExpandableListActivity
expandGroups(); expandGroups();
} }
@Override
protected void onSaveInstanceState( Bundle outState )
{
super.onSaveInstanceState( outState );
outState.putLong( PACKED_POSITION, m_packedPosition );
outState.putString( NAME, m_name );
outState.putInt( LANG, m_lang );
outState.putString( DELETE_DICT, m_deleteDict );
if ( null != m_moveFromLoc ) {
outState.putInt( MOVEFROMLOC, m_moveFromLoc.ordinal() );
}
}
private void getBundledData( Bundle savedInstanceState )
{
if ( null != savedInstanceState ) {
m_packedPosition = savedInstanceState.getLong( PACKED_POSITION );
m_name = savedInstanceState.getString( NAME );
m_lang = savedInstanceState.getInt( LANG );
m_deleteDict = savedInstanceState.getString( DELETE_DICT );
int tmp = savedInstanceState.getInt( MOVEFROMLOC, -1 );
if ( -1 != tmp ) {
m_moveFromLoc = DictLoc.values()[tmp];
}
}
}
@Override @Override
protected void onStop() { protected void onStop() {
MountEventReceiver.unregister( this ); MountEventReceiver.unregister( this );
@ -495,65 +460,61 @@ public class DictsActivity extends XWExpandableListActivity
} }
@Override @Override
public void onCreateContextMenu( ContextMenu menu, View view, public void onBackPressed() {
ContextMenuInfo menuInfo ) if ( 0 == m_selDicts.size() ) {
{ super.onBackPressed();
super.onCreateContextMenu( menu, view, menuInfo ); } else {
clearSelections();
ExpandableListView.ExpandableListContextMenuInfo info
= (ExpandableListView.ExpandableListContextMenuInfo)menuInfo;
long packedPosition = info.packedPosition;
int childPosition = ExpandableListView.
getPackedPositionChild( packedPosition );
// int groupPosition = ExpandableListView.
// getPackedPositionGroup( packedPosition );
// DbgUtils.logf( "onCreateContextMenu: group: %d; child: %d",
// groupPosition, childPosition );
// We don't have a menu yet for languages, just for their dict
// children
if ( childPosition >= 0 ) {
MenuInflater inflater = getMenuInflater();
inflater.inflate( R.menu.dicts_item_menu, menu );
XWListItem row = (XWListItem)info.targetView;
DictLoc loc = (DictLoc)row.getCached();
if ( loc == DictLoc.BUILT_IN
|| ! DictUtils.haveWriteableSD() ) {
menu.removeItem( R.id.dicts_item_move );
}
String title = getString( R.string.game_item_menu_titlef,
row.getText() );
menu.setHeaderTitle( title );
} }
} }
@Override @Override
public boolean onContextItemSelected( MenuItem item ) public boolean onCreateOptionsMenu( Menu menu )
{ {
boolean handled = false; MenuInflater inflater = getMenuInflater();
ExpandableListContextMenuInfo info = null; inflater.inflate( R.menu.dicts_menu, menu );
try {
info = (ExpandableListContextMenuInfo)item.getMenuInfo();
} catch (ClassCastException cce) {
DbgUtils.loge( cce );
return false;
}
m_packedPosition = info.packedPosition; return true;
}
int id = item.getItemId(); @Override
switch( id ) { public boolean onPrepareOptionsMenu( Menu menu )
case R.id.dicts_item_move: {
askMoveDict( (XWListItem)info.targetView ); int nSel = m_selDicts.size();
Utils.setItemVisible( menu, R.id.dicts_download,
0 == nSel && ABUtils.haveActionBar() );
Utils.setItemVisible( menu, R.id.dicts_select, 1 == nSel );
boolean allVolatile = selItemsVolatile();
Utils.setItemVisible( menu, R.id.dicts_move,
allVolatile && DictUtils.haveWriteableSD() );
Utils.setItemVisible( menu, R.id.dicts_delete, allVolatile );
return super.onPrepareOptionsMenu( menu );
}
public boolean onOptionsItemSelected( MenuItem item )
{
boolean handled = true;
switch ( item.getItemId() ) {
case R.id.dicts_download:
startDownload( 0, null );
break; break;
case R.id.dicts_item_select: case R.id.dicts_delete:
deleteSelected();
break;
case R.id.dicts_move:
askMoveSelDicts();
break;
case R.id.dicts_select:
showDialog( SET_DEFAULT ); showDialog( SET_DEFAULT );
break; break;
default:
handled = false;
} }
return handled; return handled || super.onOptionsItemSelected( item );
} }
private void downloadNewDict( Intent intent ) private void downloadNewDict( Intent intent )
@ -567,9 +528,8 @@ public class DictsActivity extends XWExpandableListActivity
} }
} }
private void setDefault( int keyId ) private void setDefault( XWListItem view, int keyId )
{ {
XWListItem view = m_adapter.getSelChildView();
SharedPreferences sp SharedPreferences sp
= PreferenceManager.getDefaultSharedPreferences( this ); = PreferenceManager.getDefaultSharedPreferences( this );
SharedPreferences.Editor editor = sp.edit(); SharedPreferences.Editor editor = sp.edit();
@ -582,20 +542,39 @@ public class DictsActivity extends XWExpandableListActivity
// Move dict. Put up dialog asking user to confirm move from XX // Move dict. Put up dialog asking user to confirm move from XX
// to YY. So we need both XX and YY. There may be several // to YY. So we need both XX and YY. There may be several
// options for YY? // options for YY?
private void askMoveDict( XWListItem item ) private void askMoveSelDicts()
{ {
m_moveFromLoc = (DictLoc)item.getCached();
showDialog( MOVE_DICT ); showDialog( MOVE_DICT );
} }
// XWListItem.DeleteCallback interface // OnItemLongClickListener interface
public void deleteCalled( XWListItem item ) public boolean onItemLongClick( AdapterView<?> parent, View view,
{ int position, long id ) {
String dict = item.getText(); boolean success = view instanceof SelectableItem.LongClickHandler;
String msg = getString( R.string.confirm_delete_dictf, dict ); if ( success ) {
((SelectableItem.LongClickHandler)view).longClicked();
}
return success;
}
m_deleteDict = dict; private boolean selItemsVolatile()
m_moveFromLoc = (DictLoc)item.getCached(); {
boolean result = 0 < m_selDicts.size();
for ( Iterator<XWListItem> iter = m_selDicts.iterator();
result && iter.hasNext(); ) {
DictLoc loc = (DictLoc)iter.next().getCached();
if ( loc == DictLoc.BUILT_IN ) {
result = false;
}
}
return result;
}
private void deleteSelected()
{
XWListItem[] items = getSelItems();
String msg = getString( R.string.confirm_delete_dictf,
getJoinedNames( items ) );
// When and what to warn about. First off, if there's another // When and what to warn about. First off, if there's another
// identical dict, simply confirm. Or if nobody's using this // identical dict, simply confirm. Or if nobody's using this
@ -604,30 +583,36 @@ public class DictsActivity extends XWExpandableListActivity
// want different warnings depending on whether it's the last // want different warnings depending on whether it's the last
// available dict in its language. // available dict in its language.
if ( 1 < DictLangCache.getDictCount( this, dict ) ) { for ( XWListItem item : items ) {
// there's another; do nothing String dict = item.getText();
} else { if ( 1 < DictLangCache.getDictCount( this, dict ) ) {
int fmtid = 0; // there's another; do nothing
int langcode = DictLangCache.getDictLangCode( this, dict ); } else {
DictAndLoc[] langDals = DictLangCache.getDALsHaveLang( this, String newMsg = null;
langcode ); int langcode = DictLangCache.getDictLangCode( this, dict );
int nUsingLang = DBUtils.countGamesUsingLang( this, langcode ); String langName = DictLangCache.getLangName( this, langcode );
DictAndLoc[] langDals = DictLangCache.getDALsHaveLang( this,
langcode );
int nUsingLang = DBUtils.countGamesUsingLang( this, langcode );
if ( 1 == langDals.length ) { // last in this language? if ( 1 == langDals.length ) { // last in this language?
if ( 0 < nUsingLang ) { if ( 0 < nUsingLang ) {
fmtid = R.string.confirm_deleteonly_dictf; newMsg = getString( R.string.confirm_deleteonly_dictf,
dict, langName );
}
} else if ( 0 < DBUtils.countGamesUsingDict( this, dict ) ) {
newMsg = getString( R.string.confirm_deletemore_dictf,
langName );
}
if ( null != newMsg ) {
msg += "\n\n" + newMsg;
} }
} else if ( 0 < DBUtils.countGamesUsingDict( this, dict ) ) {
fmtid = R.string.confirm_deletemore_dictf;
}
if ( 0 != fmtid ) {
msg += getString( fmtid, DictLangCache.
getLangName( this, langcode ) );
} }
} }
showConfirmThen( msg, R.string.button_delete, DELETE_DICT_ACTION ); showConfirmThen( msg, R.string.button_delete, DELETE_DICT_ACTION,
} (Object)items );
} // deleteSelected
// MountEventReceiver.SDCardNotifiee interface // MountEventReceiver.SDCardNotifiee interface
public void cardMounted( boolean nowMounted ) public void cardMounted( boolean nowMounted )
@ -644,12 +629,18 @@ public class DictsActivity extends XWExpandableListActivity
} }
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
switch( id ) { switch( id ) {
case DELETE_DICT_ACTION: case DELETE_DICT_ACTION:
if ( DialogInterface.BUTTON_POSITIVE == which ) { if ( DialogInterface.BUTTON_POSITIVE == which ) {
deleteDict( m_deleteDict, m_moveFromLoc ); XWListItem[] items = (XWListItem[])params[0];
for ( XWListItem item : items ) {
String name = item.getText();
DictLoc loc = (DictLoc)item.getCached();
deleteDict( name, loc );
}
clearSelections();
} }
break; break;
default: default:
@ -711,6 +702,49 @@ public class DictsActivity extends XWExpandableListActivity
XWPrefs.setClosedLangs( this, asArray ); XWPrefs.setClosedLangs( this, asArray );
} }
private void clearSelections()
{
if ( 0 < m_selDicts.size() ) {
XWListItem[] items = getSelItems();
m_selDicts.clear();
for ( XWListItem item : items ) {
item.setSelected( false );
}
}
}
private String getJoinedNames( XWListItem[] items )
{
String[] names = new String[items.length];
int ii = 0;
for ( XWListItem item : items ) {
names[ii++] = item.getText();
}
return TextUtils.join( ", ", names );
}
private XWListItem[] getSelItems()
{
XWListItem[] items = new XWListItem[m_selDicts.size()];
int indx = 0;
for ( Iterator<XWListItem> iter = m_selDicts.iterator();
iter.hasNext(); ) {
items[indx++] = iter.next();
}
return items;
}
private void setTitleBar()
{
int nSels = m_selDicts.size();
if ( 0 < nSels ) {
setTitle( getString( R.string.sel_dictsf, nSels ) );
} else {
setTitle( m_origTitle );
}
}
private String[] makeDictDirItems() private String[] makeDictDirItems()
{ {
@ -723,9 +757,6 @@ public class DictsActivity extends XWExpandableListActivity
if ( !showDownload && DictLoc.DOWNLOAD == loc ) { if ( !showDownload && DictLoc.DOWNLOAD == loc ) {
continue; continue;
} }
if ( loc.equals( m_moveFromLoc ) ) {
m_moveFromItem = nextI;
}
items[nextI++] = m_locNames[loc.ordinal()]; items[nextI++] = m_locNames[loc.ordinal()];
} }
return items; return items;
@ -793,6 +824,32 @@ public class DictsActivity extends XWExpandableListActivity
} }
} }
// SelectableItem interface
public void itemClicked( SelectableItem.LongClickHandler clicked,
GameSummary summary )
{
DbgUtils.logf( "itemClicked not implemented" );
}
public void itemToggled( SelectableItem.LongClickHandler toggled,
boolean selected )
{
XWListItem dictView = (XWListItem)toggled;
if ( selected ) {
m_selDicts.add( dictView );
} else {
m_selDicts.remove( dictView );
}
ABUtils.invalidateOptionsMenuIf( this );
setTitleBar();
}
public boolean getSelected( SelectableItem.LongClickHandler obj )
{
XWListItem dictView = (XWListItem)obj;
return m_selDicts.contains( dictView );
}
private static class SafePopupImpl implements SafePopup { private static class SafePopupImpl implements SafePopup {
public void doPopup( final Context context, View button, public void doPopup( final Context context, View button,
String curDict ) { String curDict ) {
@ -821,7 +878,9 @@ public class DictsActivity extends XWExpandableListActivity
.setOnMenuItemClickListener( listener ); .setOnMenuItemClickListener( listener );
// Add at top but save until have dal info // Add at top but save until have dal info
MenuItem curItem = menu.add( curDict ); MenuItem curItem =
menu.add( context.getString( R.string.cur_menu_markerf,
curDict ) );
DictAndLoc[] dals = DictUtils.dictList( context ); DictAndLoc[] dals = DictUtils.dictList( context );
for ( DictAndLoc dal : dals ) { for ( DictAndLoc dal : dals ) {

View file

@ -43,12 +43,13 @@ public class DlgDelegate {
public static final int DIALOG_OKONLY = 2; public static final int DIALOG_OKONLY = 2;
public static final int DIALOG_NOTAGAIN = 3; public static final int DIALOG_NOTAGAIN = 3;
public static final int CONFIRM_THEN = 4; public static final int CONFIRM_THEN = 4;
public static final int TEXT_OR_HTML_THEN = 5; public static final int INVITE_CHOICES_THEN = 5;
public static final int DLG_DICTGONE = 6; public static final int DLG_DICTGONE = 6;
public static final int DIALOG_LAST = DLG_DICTGONE; public static final int DIALOG_LAST = DLG_DICTGONE;
public static final int SMS_BTN = AlertDialog.BUTTON_POSITIVE; public static final int SMS_BTN = AlertDialog.BUTTON_POSITIVE;
public static final int EMAIL_BTN = AlertDialog.BUTTON_NEGATIVE; public static final int EMAIL_BTN = AlertDialog.BUTTON_NEGATIVE;
public static final int NFC_BTN = AlertDialog.BUTTON_NEUTRAL;
public static final int DISMISS_BUTTON = 0; public static final int DISMISS_BUTTON = 0;
public static final int SKIP_CALLBACK = -1; public static final int SKIP_CALLBACK = -1;
@ -56,7 +57,7 @@ public class DlgDelegate {
private static final String STATE_KEYF = "STATE_%d"; private static final String STATE_KEYF = "STATE_%d";
public interface DlgClickNotify { public interface DlgClickNotify {
void dlgButtonClicked( int id, int button ); void dlgButtonClicked( int id, int button, Object[] params );
} }
private Activity m_activity; private Activity m_activity;
@ -118,8 +119,8 @@ public class DlgDelegate {
case CONFIRM_THEN: case CONFIRM_THEN:
dialog = createConfirmThenDialog( state, id ); dialog = createConfirmThenDialog( state, id );
break; break;
case TEXT_OR_HTML_THEN: case INVITE_CHOICES_THEN:
dialog = createHtmlThenDialog( state, id ); dialog = createInviteChoicesDialog( state, id );
break; break;
case DLG_DICTGONE: case DLG_DICTGONE:
dialog = createDictGoneDialog(); dialog = createDictGoneDialog();
@ -157,24 +158,45 @@ public class DlgDelegate {
} }
public void showNotAgainDlgThen( int msgID, int prefsKey, public void showNotAgainDlgThen( int msgID, int prefsKey,
int callbackID ) final int callbackID,
final Object[] params )
{
showNotAgainDlgThen( m_activity.getString( msgID ), prefsKey,
callbackID, params );
}
public void showNotAgainDlgThen( String msg, int prefsKey,
final int callbackID,
final Object[] params )
{ {
if ( XWPrefs.getPrefsBoolean( m_activity, prefsKey, false ) ) { if ( XWPrefs.getPrefsBoolean( m_activity, prefsKey, false ) ) {
// If it's set, do the action without bothering with the // If it's set, do the action without bothering with the
// dialog // dialog
if ( SKIP_CALLBACK != callbackID ) { if ( SKIP_CALLBACK != callbackID ) {
m_clickCallback.dlgButtonClicked( callbackID, post( new Runnable() {
AlertDialog.BUTTON_POSITIVE ); public void run() {
m_clickCallback
.dlgButtonClicked( callbackID,
AlertDialog.BUTTON_POSITIVE,
params );
}
});
} }
} else { } else {
String msg = m_activity.getString( msgID );
DlgState state = DlgState state =
new DlgState( DIALOG_NOTAGAIN, msg, callbackID, prefsKey ); new DlgState( DIALOG_NOTAGAIN, msg, callbackID, prefsKey,
params );
addState( state ); addState( state );
m_activity.showDialog( DIALOG_NOTAGAIN ); m_activity.showDialog( DIALOG_NOTAGAIN );
} }
} }
public void showNotAgainDlgThen( int msgID, int prefsKey,
int callbackID )
{
showNotAgainDlgThen( msgID, prefsKey, callbackID, null );
}
public void showNotAgainDlgThen( int msgID, int prefsKey ) public void showNotAgainDlgThen( int msgID, int prefsKey )
{ {
showNotAgainDlgThen( msgID, prefsKey, SKIP_CALLBACK ); showNotAgainDlgThen( msgID, prefsKey, SKIP_CALLBACK );
@ -182,27 +204,40 @@ public class DlgDelegate {
public void showConfirmThen( String msg, int callbackID ) public void showConfirmThen( String msg, int callbackID )
{ {
showConfirmThen( msg, R.string.button_ok, callbackID ); showConfirmThen( msg, R.string.button_ok, callbackID, null );
}
public void showConfirmThen( String msg, int callbackID, Object[] params )
{
showConfirmThen( msg, R.string.button_ok, callbackID, params );
} }
public void showConfirmThen( String msg, int posButton, int callbackID ) public void showConfirmThen( String msg, int posButton, int callbackID )
{
showConfirmThen( msg, posButton, callbackID, null );
}
public void showConfirmThen( String msg, int posButton, int callbackID,
Object[] params )
{ {
DlgState state = new DlgState( CONFIRM_THEN, msg, posButton, DlgState state = new DlgState( CONFIRM_THEN, msg, posButton,
callbackID, 0 ); callbackID, 0, params );
addState( state ); addState( state );
m_activity.showDialog( CONFIRM_THEN ); m_activity.showDialog( CONFIRM_THEN );
} }
public void showEmailOrSMSThen( final int callbackID ) public void showInviteChoicesThen( final int callbackID )
{ {
if ( Utils.deviceSupportsSMS( m_activity ) ) { if ( Utils.deviceSupportsSMS( m_activity )
DlgState state = new DlgState( TEXT_OR_HTML_THEN, callbackID ); || NFCUtils.nfcAvail( m_activity )[0] ) {
DlgState state = new DlgState( INVITE_CHOICES_THEN, callbackID );
addState( state ); addState( state );
m_activity.showDialog( TEXT_OR_HTML_THEN ); m_activity.showDialog( INVITE_CHOICES_THEN );
} else { } else {
post( new Runnable() { post( new Runnable() {
public void run() { public void run() {
m_clickCallback.dlgButtonClicked( callbackID, EMAIL_BTN ); m_clickCallback.dlgButtonClicked( callbackID, EMAIL_BTN,
null );
} }
}); });
} }
@ -295,7 +330,7 @@ public class DlgDelegate {
TextView vers = (TextView)view.findViewById( R.id.version_string ); TextView vers = (TextView)view.findViewById( R.id.version_string );
vers.setText( String.format( m_activity.getString(R.string.about_versf), vers.setText( String.format( m_activity.getString(R.string.about_versf),
m_activity.getString(R.string.app_version), m_activity.getString(R.string.app_version),
GitVersion.VERS ) ); BuildConstants.GIT_REV ) );
TextView xlator = (TextView)view.findViewById( R.id.about_xlator ); TextView xlator = (TextView)view.findViewById( R.id.about_xlator );
String str = m_activity.getString( R.string.xlator ); String str = m_activity.getString( R.string.xlator );
@ -345,7 +380,8 @@ public class DlgDelegate {
if ( SKIP_CALLBACK != state.m_cbckID ) { if ( SKIP_CALLBACK != state.m_cbckID ) {
m_clickCallback. m_clickCallback.
dlgButtonClicked( state.m_cbckID, dlgButtonClicked( state.m_cbckID,
AlertDialog.BUTTON_POSITIVE ); AlertDialog.BUTTON_POSITIVE,
state.m_params );
} }
} }
}; };
@ -374,17 +410,34 @@ public class DlgDelegate {
return setCallbackDismissListener( dialog, state, id ); return setCallbackDismissListener( dialog, state, id );
} }
private Dialog createHtmlThenDialog( DlgState state, int id ) private Dialog createInviteChoicesDialog( DlgState state, int id )
{ {
OnClickListener lstnr = mkCallbackClickListener( state ); OnClickListener lstnr = mkCallbackClickListener( state );
Dialog dialog = new AlertDialog.Builder( m_activity )
.setTitle( R.string.query_title )
.setMessage( R.string.sms_or_email )
.setPositiveButton( R.string.button_text, lstnr )
.setNegativeButton( R.string.button_html, lstnr )
.create();
return setCallbackDismissListener( dialog, state, id ); boolean haveSMS = Utils.deviceSupportsSMS( m_activity );
boolean haveNFC = NFCUtils.nfcAvail( m_activity )[0];
int msgID;
if ( haveSMS && haveNFC ) {
msgID = R.string.nfc_or_sms_or_email;
} else if ( haveSMS ) {
msgID = R.string.sms_or_email;
} else {
msgID = R.string.nfc_or_email;
}
AlertDialog.Builder builder = new AlertDialog.Builder( m_activity )
.setTitle( R.string.query_title )
.setMessage( msgID )
.setNegativeButton( R.string.button_html, lstnr );
if ( haveSMS ) {
builder.setPositiveButton( R.string.button_text, lstnr );
}
if ( haveNFC ) {
builder.setNeutralButton( R.string.button_nfc, lstnr );
}
return setCallbackDismissListener( builder.create(), state, id );
} }
private Dialog createDictGoneDialog() private Dialog createDictGoneDialog()
@ -411,7 +464,8 @@ public class DlgDelegate {
public void onClick( DialogInterface dlg, int button ) { public void onClick( DialogInterface dlg, int button ) {
if ( SKIP_CALLBACK != state.m_cbckID ) { if ( SKIP_CALLBACK != state.m_cbckID ) {
m_clickCallback.dlgButtonClicked( state.m_cbckID, m_clickCallback.dlgButtonClicked( state.m_cbckID,
button ); button,
state.m_params );
} }
} }
}; };
@ -428,7 +482,8 @@ public class DlgDelegate {
dropState( state ); dropState( state );
if ( SKIP_CALLBACK != state.m_cbckID ) { if ( SKIP_CALLBACK != state.m_cbckID ) {
m_clickCallback.dlgButtonClicked( state.m_cbckID, m_clickCallback.dlgButtonClicked( state.m_cbckID,
DISMISS_BUTTON ); DISMISS_BUTTON,
state.m_params );
} }
m_activity.removeDialog( id ); m_activity.removeDialog( id );
} }

View file

@ -29,6 +29,7 @@ public class DlgState implements Parcelable {
public int m_posButton; public int m_posButton;
public int m_cbckID = 0; public int m_cbckID = 0;
public int m_prefsKey; public int m_prefsKey;
public Object[] m_params;
public DlgState( int id, String msg, int cbckID ) public DlgState( int id, String msg, int cbckID )
{ {
@ -40,14 +41,28 @@ public class DlgState implements Parcelable {
this( id, msg, R.string.button_ok, cbckID, prefsKey ); this( id, msg, R.string.button_ok, cbckID, prefsKey );
} }
public DlgState( int id, String msg, int cbckID, int prefsKey,
Object[] params )
{
this( id, msg, R.string.button_ok, cbckID, prefsKey );
m_params = params;
}
public DlgState( int id, String msg, int posButton, public DlgState( int id, String msg, int posButton,
int cbckID, int prefsKey ) int cbckID, int prefsKey )
{
this( id, msg, posButton, cbckID, prefsKey, null );
}
public DlgState( int id, String msg, int posButton,
int cbckID, int prefsKey, Object[] params )
{ {
m_id = id; m_id = id;
m_msg = msg; m_msg = msg;
m_posButton = posButton; m_posButton = posButton;
m_cbckID = cbckID; m_cbckID = cbckID;
m_prefsKey = prefsKey; m_prefsKey = prefsKey;
m_params = params;
} }
public DlgState( int id, int cbckID ) public DlgState( int id, int cbckID )

View file

@ -26,6 +26,7 @@ import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Handler; import android.os.Handler;
import android.view.View; import android.view.View;
@ -47,10 +48,13 @@ public class ExpiringDelegate {
private boolean m_haveTurnLocal = false; private boolean m_haveTurnLocal = false;
private long m_startSecs; private long m_startSecs;
private Runnable m_runnable = null; private Runnable m_runnable = null;
private boolean m_selected;
private Drawable m_origDrawable;
// these can be static as drawing's all in same thread. // these can be static as drawing's all in same thread.
private static Rect s_rect; private static Rect s_rect;
private static Paint s_paint; private static Paint s_paint;
private static float[] s_points; private static float[] s_points;
private static Drawable s_selDrawable;
static { static {
s_rect = new Rect(); s_rect = new Rect();
@ -58,12 +62,18 @@ public class ExpiringDelegate {
s_paint.setStyle(Paint.Style.STROKE); s_paint.setStyle(Paint.Style.STROKE);
s_paint.setStrokeWidth( 1 ); s_paint.setStrokeWidth( 1 );
s_points = new float[4*6]; s_points = new float[4*6];
s_selDrawable = new ColorDrawable( XWApp.SEL_COLOR );
} }
public ExpiringDelegate( Context context, View view, Handler handler ) public ExpiringDelegate( Context context, View view )
{ {
m_context = context; m_context = context;
m_view = view; m_view = view;
m_origDrawable = view.getBackground();
}
public void setHandler( Handler handler )
{
m_handler = handler; m_handler = handler;
} }
@ -85,9 +95,22 @@ public class ExpiringDelegate {
} }
} }
public void setSelected( boolean selected )
{
m_selected = selected;
if ( selected ) {
m_origDrawable = m_view.getBackground();
m_view.setBackgroundDrawable( s_selDrawable );
} else {
m_view.setBackgroundDrawable( m_origDrawable );
}
}
public void onDraw( Canvas canvas ) public void onDraw( Canvas canvas )
{ {
if ( m_active && m_doFrame ) { if ( m_selected ) {
// do nothing; the drawable's set already
} else if ( m_active && m_doFrame ) {
Assert.assertTrue( 0 <= m_pct && m_pct <= 100 ); Assert.assertTrue( 0 <= m_pct && m_pct <= 100 );
m_view.getDrawingRect( s_rect ); m_view.getDrawingRect( s_rect );
int width = s_rect.width(); int width = s_rect.width();
@ -178,14 +201,14 @@ public class ExpiringDelegate {
m_pct = (int)((100 * passed) / INTERVAL_SECS); m_pct = (int)((100 * passed) / INTERVAL_SECS);
if ( m_pct > 100 ) { if ( m_pct > 100 ) {
m_pct = 100; m_pct = 100;
} else { } else if ( null != m_handler ) {
long onePct = INTERVAL_SECS / 100; long onePct = INTERVAL_SECS / 100;
long lastStart = m_startSecs + (onePct * m_pct); long lastStart = m_startSecs + (onePct * m_pct);
Assert.assertTrue( lastStart <= now ); Assert.assertTrue( lastStart <= now );
long nextStartIn = lastStart + onePct - now; long nextStartIn = lastStart + onePct - now;
// DbgUtils.logf( "pct change %d seconds from now", nextStartIn ); // DbgUtils.logf( "pct change %d seconds from now", nextStartIn );
m_handler.postDelayed( mkRunnable(), 1000 * nextStartIn ); m_handler.postDelayed( mkRunnable(), 1000 * nextStartIn ); // NPE
} }
} }
} }

View file

@ -38,7 +38,8 @@ public class ExpiringLinearLayout extends LinearLayout {
boolean haveTurnLocal, long startSecs ) boolean haveTurnLocal, long startSecs )
{ {
if ( null == m_delegate ) { if ( null == m_delegate ) {
m_delegate = new ExpiringDelegate( m_context, this, handler ); m_delegate = new ExpiringDelegate( m_context, this );
m_delegate.setHandler( handler );
} }
m_delegate.configure( haveTurn, haveTurnLocal, startSecs ); m_delegate.configure( haveTurn, haveTurnLocal, startSecs );
} }

View file

@ -28,6 +28,7 @@ import android.widget.TextView;
class ExpiringTextView extends TextView { class ExpiringTextView extends TextView {
private ExpiringDelegate m_delegate = null; private ExpiringDelegate m_delegate = null;
private Context m_context; private Context m_context;
protected boolean m_selected = false;
public ExpiringTextView( Context context, AttributeSet attrs ) public ExpiringTextView( Context context, AttributeSet attrs )
{ {
@ -38,9 +39,9 @@ class ExpiringTextView extends TextView {
public void setPct( Handler handler, boolean haveTurn, public void setPct( Handler handler, boolean haveTurn,
boolean haveTurnLocal, long startSecs ) boolean haveTurnLocal, long startSecs )
{ {
if ( null == m_delegate ) { ExpiringDelegate delegate = getDelegate();
m_delegate = new ExpiringDelegate( m_context, this, handler ); delegate.setHandler( handler );
}
setPct( haveTurn, haveTurnLocal, startSecs ); setPct( haveTurn, haveTurnLocal, startSecs );
} }
@ -52,6 +53,12 @@ class ExpiringTextView extends TextView {
} }
} }
protected void toggleSelected()
{
m_selected = !m_selected;
getDelegate().setSelected( m_selected );
}
@Override @Override
protected void onDraw( Canvas canvas ) protected void onDraw( Canvas canvas )
{ {
@ -60,4 +67,12 @@ class ExpiringTextView extends TextView {
m_delegate.onDraw( canvas ); m_delegate.onDraw( canvas );
} }
} }
private ExpiringDelegate getDelegate()
{
if ( null == m_delegate ) {
m_delegate = new ExpiringDelegate( m_context, this );
}
return m_delegate;
}
} }

View file

@ -600,7 +600,7 @@ public class GameConfig extends XWActivity
} }
@Override @Override
public void dlgButtonClicked( int id, int button ) public void dlgButtonClicked( int id, int button, Object[] params )
{ {
switch( id ) { switch( id ) {
case LOCKED_CHANGE_ACTION: case LOCKED_CHANGE_ACTION:

View file

@ -45,18 +45,12 @@ public class GameListAdapter implements ExpandableListAdapter {
private ExpandableListView m_list; private ExpandableListView m_list;
private int m_fieldID; private int m_fieldID;
private Handler m_handler; private Handler m_handler;
private LoadItemCB m_cb; private SelectableItem m_cb;
private long[] m_positions; private long[] m_positions;
public interface LoadItemCB {
public void itemClicked( long rowid, GameSummary summary );
public void itemToggled( long rowid, boolean selected );
public boolean getSelected( long rowid );
}
public GameListAdapter( Context context, ExpandableListView list, public GameListAdapter( Context context, ExpandableListView list,
Handler handler, LoadItemCB cb, long[] positions, Handler handler, SelectableItem cb,
String fieldName ) long[] positions, String fieldName )
{ {
m_context = context; m_context = context;
m_list = list; m_list = list;
@ -67,7 +61,7 @@ public class GameListAdapter implements ExpandableListAdapter {
m_fieldID = fieldToID( fieldName ); m_fieldID = fieldToID( fieldName );
} }
public long[] getPositions() public long[] getGroupPositions()
{ {
Set<Long> keys = gameInfo().keySet(); // do not modify!!!! Set<Long> keys = gameInfo().keySet(); // do not modify!!!!
if ( null == m_positions || m_positions.length != keys.size() ) { if ( null == m_positions || m_positions.length != keys.size() ) {
@ -99,7 +93,7 @@ public class GameListAdapter implements ExpandableListAdapter {
{ {
int src = getGroupPosition( groupid ); int src = getGroupPosition( groupid );
int dest = src + moveBy; int dest = src + moveBy;
long[] positions = getPositions(); long[] positions = getGroupPositions();
boolean success = 0 <= dest && dest < positions.length; boolean success = 0 <= dest && dest < positions.length;
if ( success ) { if ( success ) {
long tmp = positions[src]; long tmp = positions[src];
@ -123,7 +117,7 @@ public class GameListAdapter implements ExpandableListAdapter {
public long getRowIDFor( int group, int child ) public long getRowIDFor( int group, int child )
{ {
long rowid = DBUtils.ROWID_NOTFOUND; long rowid = DBUtils.ROWID_NOTFOUND;
long[] rows = getRows( getPositions()[group] ); long[] rows = getRows( getGroupPositions()[group] );
if ( child < rows.length ) { if ( child < rows.length ) {
rowid = rows[child]; rowid = rows[child];
} }
@ -141,7 +135,7 @@ public class GameListAdapter implements ExpandableListAdapter {
public long getGroupIDFor( int groupPos ) public long getGroupIDFor( int groupPos )
{ {
long id = getPositions()[groupPos]; long id = getGroupPositions()[groupPos];
return id; return id;
} }
@ -152,9 +146,14 @@ public class GameListAdapter implements ExpandableListAdapter {
return ggi.m_name; return ggi.m_name;
} }
public void clearSelected( Set<Long> rowids ) public void clearSelectedGames( long[] rowids )
{ {
deselect( rowids ); deselectGames( rowids );
}
public void clearSelectedGroups( HashSet<Long> groupIDs )
{
deselectGroups( groupIDs );
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -178,11 +177,7 @@ public class GameListAdapter implements ExpandableListAdapter {
DBUtils.setGroupExpanded( m_context, groupid, false ); DBUtils.setGroupExpanded( m_context, groupid, false );
long[] rowids = DBUtils.getGroupGames( m_context, groupid ); long[] rowids = DBUtils.getGroupGames( m_context, groupid );
HashSet<Long> asSet = new HashSet<Long>(rowids.length); deselectGames( rowids );
for ( long rowid : rowids ) {
asSet.add( rowid );
}
deselect( asSet );
} }
public void onGroupExpanded( int groupPosition ) public void onGroupExpanded( int groupPosition )
@ -223,6 +218,7 @@ public class GameListAdapter implements ExpandableListAdapter {
GameListItem result = GameListItem result =
GameListItem.makeForRow( m_context, rowid, m_handler, GameListItem.makeForRow( m_context, rowid, m_handler,
groupPosition, m_fieldID, m_cb ); groupPosition, m_fieldID, m_cb );
result.setSelected( m_cb.getSelected( result ) );
return result; return result;
} }
@ -232,9 +228,10 @@ public class GameListAdapter implements ExpandableListAdapter {
// if ( null != convertView ) { // if ( null != convertView ) {
// DbgUtils.logf( "getGroupView gave non-null convertView" ); // DbgUtils.logf( "getGroupView gave non-null convertView" );
// } // }
GameListGroup view = (GameListGroup) long groupID = getGroupIDFor( groupPosition );
Utils.inflate(m_context, R.layout.game_list_group ); GameListGroup view =
view.setGroupPosition( groupPosition ); GameListGroup.makeForPosition( m_context, groupPosition, groupID,
m_cb );
if ( !isExpanded ) { if ( !isExpanded ) {
GameGroupInfo ggi = getInfoForGroup( groupPosition ); GameGroupInfo ggi = getInfoForGroup( groupPosition );
@ -247,6 +244,8 @@ public class GameListAdapter implements ExpandableListAdapter {
groupNames()[groupPosition], nKids ); groupNames()[groupPosition], nKids );
view.setText( name ); view.setText( name );
view.setSelected( m_cb.getSelected( view ) );
return view; return view;
} }
@ -274,7 +273,12 @@ public class GameListAdapter implements ExpandableListAdapter {
public int getChildrenCount( int groupPosition ) public int getChildrenCount( int groupPosition )
{ {
long[] rows = getRows( getPositions()[groupPosition] ); return getChildrenCount( getGroupPositions()[groupPosition] );
}
public int getChildrenCount( long groupID )
{
long[] rows = getRows( groupID );
return rows.length; return rows.length;
} }
@ -321,7 +325,7 @@ public class GameListAdapter implements ExpandableListAdapter {
public String[] groupNames() public String[] groupNames()
{ {
HashMap<Long,GameGroupInfo> info = gameInfo(); HashMap<Long,GameGroupInfo> info = gameInfo();
long[] positions = getPositions(); long[] positions = getGroupPositions();
String[] names = new String[ positions.length ]; String[] names = new String[ positions.length ];
for ( int ii = 0; ii < names.length; ++ii ) { for ( int ii = 0; ii < names.length; ++ii ) {
names[ii] = info.get(positions[ii]).m_name; names[ii] = info.get(positions[ii]).m_name;
@ -332,7 +336,7 @@ public class GameListAdapter implements ExpandableListAdapter {
public int getGroupPosition( long groupid ) public int getGroupPosition( long groupid )
{ {
int result = -1; int result = -1;
long[] positions = getPositions(); long[] positions = getGroupPositions();
for ( int pos = 0; pos < positions.length; ++pos ) { for ( int pos = 0; pos < positions.length; ++pos ) {
if ( positions[pos] == groupid ) { if ( positions[pos] == groupid ) {
result = pos; result = pos;
@ -361,27 +365,47 @@ public class GameListAdapter implements ExpandableListAdapter {
private GameGroupInfo getInfoForGroup( int groupPosition ) private GameGroupInfo getInfoForGroup( int groupPosition )
{ {
return gameInfo().get( getPositions()[groupPosition] ); return gameInfo().get( getGroupPositions()[groupPosition] );
} }
private void deselect( Set<Long> rowids ) private void deselectGames( long[] rowids )
{ {
GameListItem[] items = new GameListItem[rowids.size()]; GameListItem[] items = new GameListItem[rowids.length];
getGameItemsFor( rowids, items ); getGameItemsFor( rowids, items );
for ( GameListItem item : items ) { for ( GameListItem item : items ) {
item.setSelected( false ); if ( null != item ) {
item.setSelected( false );
}
} }
} }
private void getGameItemsFor( Set<Long> rowids, GameListItem[] items ) private void deselectGroups( HashSet<Long> groupids )
{ {
groupids = (HashSet<Long>)groupids.clone();
for ( Iterator<Long>iter = groupids.iterator();
iter.hasNext(); ) {
int pos = getGroupPosition( iter.next() );
if ( 0 <= pos ) { // still exists?
GameListGroup group = getGroupItemFor( pos );
group.setSelected( false );
}
}
}
private void getGameItemsFor( long[] rowids, GameListItem[] items )
{
Set<Long> rowidsSet = new HashSet<Long>();
for ( long rowid : rowids ) {
rowidsSet.add( rowid );
}
int next = 0; int next = 0;
int count = m_list.getChildCount(); int count = m_list.getChildCount();
for ( int ii = 0; ii < count; ++ii ) { for ( int ii = 0; ii < count; ++ii ) {
View view = m_list.getChildAt( ii ); View view = m_list.getChildAt( ii );
if ( view instanceof GameListItem ) { if ( view instanceof GameListItem ) {
GameListItem tryme = (GameListItem)view; GameListItem tryme = (GameListItem)view;
if ( rowids.contains( tryme.getRowID() ) ) { if ( rowidsSet.contains( tryme.getRowID() ) ) {
items[next++] = tryme; items[next++] = tryme;
if ( next >= items.length ) { if ( next >= items.length ) {
break; break;
@ -393,8 +417,7 @@ public class GameListAdapter implements ExpandableListAdapter {
private GameListItem getGameItemFor( long rowid ) private GameListItem getGameItemFor( long rowid )
{ {
Set<Long> rowids = new HashSet<Long>(1); long[] rowids = { rowid };
rowids.add( rowid );
GameListItem[] items = new GameListItem[1]; GameListItem[] items = new GameListItem[1];
getGameItemsFor( rowids, items ); getGameItemsFor( rowids, items );
return items[0]; return items[0];
@ -420,10 +443,12 @@ public class GameListAdapter implements ExpandableListAdapter {
private int fieldToID( String fieldName ) private int fieldToID( String fieldName )
{ {
int[] ids = { int[] ids = {
R.string.game_summary_field_empty R.string.game_summary_field_empty,
,R.string.game_summary_field_language R.string.game_summary_field_language,
,R.string.game_summary_field_opponents R.string.game_summary_field_opponents,
,R.string.game_summary_field_state R.string.game_summary_field_state,
R.string.game_summary_field_rowid,
R.string.game_summary_field_gameid,
}; };
int result = -1; int result = -1;
for ( int id : ids ) { for ( int id : ids ) {

View file

@ -23,11 +23,30 @@ package org.eehouse.android.xw4;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import org.eehouse.android.xw4.DBUtils.GameGroupInfo; import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
public class GameListGroup extends ExpiringTextView { public class GameListGroup extends ExpiringTextView
implements SelectableItem.LongClickHandler
{
private int m_groupPosition; private int m_groupPosition;
private long m_groupID;
private boolean m_expanded;
private SelectableItem m_cb;
public static GameListGroup makeForPosition( Context context,
int groupPosition,
long groupID,
SelectableItem cb )
{
GameListGroup result =
(GameListGroup)Utils.inflate( context, R.layout.game_list_group );
result.m_cb = cb;
result.m_groupPosition = groupPosition;
result.m_groupID = groupID;
return result;
}
public GameListGroup( Context cx, AttributeSet as ) public GameListGroup( Context cx, AttributeSet as )
{ {
@ -43,4 +62,30 @@ public class GameListGroup extends ExpiringTextView {
{ {
return m_groupPosition; return m_groupPosition;
} }
public long getGroupID()
{
return m_groupID;
}
public void setSelected( boolean selected )
{
// If new value and state not in sync, force change in state
if ( selected != m_selected ) {
toggleSelected();
}
}
// GameListAdapter.ClickHandler interface
public void longClicked()
{
toggleSelected();
}
protected void toggleSelected()
{
super.toggleSelected();
m_cb.itemToggled( this, m_selected );
}
} }

View file

@ -20,7 +20,9 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -43,38 +45,54 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.GameSummary; import org.eehouse.android.xw4.jni.GameSummary;
public class GameListItem extends LinearLayout public class GameListItem extends LinearLayout
implements View.OnClickListener { implements View.OnClickListener, SelectableItem.LongClickHandler {
private static HashSet<Long> s_invalRows = new HashSet<Long>(); private static HashSet<Long> s_invalRows = new HashSet<Long>();
private Activity m_activity;
private Context m_context; private Context m_context;
private boolean m_loaded; private boolean m_loaded;
private long m_rowid; private long m_rowid;
private View m_hideable; private View m_hideable;
private ImageView m_thumb;
private ExpiringTextView m_name; private ExpiringTextView m_name;
private boolean m_expanded, m_haveTurn, m_haveTurnLocal; private boolean m_expanded, m_haveTurn, m_haveTurnLocal;
private long m_lastMoveTime; private long m_lastMoveTime;
private ImageButton m_expandButton; private ImageButton m_expandButton;
private Handler m_handler; private Handler m_handler;
private GameSummary m_summary; private GameSummary m_summary;
private GameListAdapter.LoadItemCB m_cb; private SelectableItem m_cb;
private int m_fieldID; private int m_fieldID;
private int m_loadingCount; private int m_loadingCount;
private int m_groupPosition; private int m_groupPosition;
private Drawable m_origDrawable; private Drawable m_origDrawable;
private boolean m_selected = false;
public GameListItem( Context cx, AttributeSet as ) public GameListItem( Context cx, AttributeSet as )
{ {
super( cx, as ); super( cx, as );
m_context = cx; m_context = cx;
if ( cx instanceof Activity ) {
m_activity = (Activity)cx;
}
m_loaded = false; m_loaded = false;
m_rowid = DBUtils.ROWID_NOTFOUND; m_rowid = DBUtils.ROWID_NOTFOUND;
m_lastMoveTime = 0; m_lastMoveTime = 0;
m_loadingCount = 0; m_loadingCount = 0;
setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View v ) {
// if selected, just un-select
if ( null != m_summary ) {
m_cb.itemClicked( GameListItem.this, m_summary );
}
}
} );
} }
private void init( Handler handler, long rowid, int groupPosition, private void init( Handler handler, long rowid, int groupPosition,
int fieldID, GameListAdapter.LoadItemCB cb ) int fieldID, SelectableItem cb )
{ {
m_handler = handler; m_handler = handler;
m_rowid = rowid; m_rowid = rowid;
@ -105,7 +123,7 @@ public class GameListItem extends LinearLayout
public void setSelected( boolean selected ) public void setSelected( boolean selected )
{ {
// If new value and state not in sync, force change in state // If new value and state not in sync, force change in state
if ( selected != (null != m_origDrawable) ) { if ( selected != m_selected ) {
toggleSelected(); toggleSelected();
} }
} }
@ -151,6 +169,9 @@ public class GameListItem extends LinearLayout
public void onClick( View view ) { public void onClick( View view ) {
m_expanded = !m_expanded; m_expanded = !m_expanded;
DBUtils.setExpanded( m_rowid, m_expanded ); DBUtils.setExpanded( m_rowid, m_expanded );
makeThumbnailIf( m_expanded );
showHide(); showHide();
} }
@ -172,6 +193,11 @@ public class GameListItem extends LinearLayout
R.drawable.expander_ic_maximized : R.drawable.expander_ic_maximized :
R.drawable.expander_ic_minimized); R.drawable.expander_ic_minimized);
m_hideable.setVisibility( m_expanded? View.VISIBLE : View.GONE ); m_hideable.setVisibility( m_expanded? View.VISIBLE : View.GONE );
if ( BuildConstants.THUMBNAIL_SUPPORTED ) {
int vis = m_expanded && XWPrefs.getThumbEnabled( m_context )
? View.VISIBLE : View.GONE;
m_thumb.setVisibility( vis );
}
m_name.setBackgroundColor( android.R.color.transparent ); m_name.setBackgroundColor( android.R.color.transparent );
m_name.setPct( m_handler, m_haveTurn && !m_expanded, m_name.setPct( m_handler, m_haveTurn && !m_expanded,
@ -188,6 +214,12 @@ public class GameListItem extends LinearLayout
switch ( m_fieldID ) { switch ( m_fieldID ) {
case R.string.game_summary_field_empty: case R.string.game_summary_field_empty:
break; break;
case R.string.game_summary_field_gameid:
value = String.format( "%X", m_summary.gameID );
break;
case R.string.game_summary_field_rowid:
value = String.format( "%d", m_rowid );
break;
case R.string.game_summary_field_language: case R.string.game_summary_field_language:
value = value =
DictLangCache.getLangName( m_context, DictLangCache.getLangName( m_context,
@ -214,19 +246,12 @@ public class GameListItem extends LinearLayout
return state; return state;
} }
private void setData( final GameSummary summary ) private void setData( GameSummary summary, boolean expanded )
{ {
if ( null != summary ) { if ( null != summary ) {
TextView tview; TextView tview;
String state = setName(); String state = setName();
setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View v ) {
m_cb.itemClicked( m_rowid, summary );
}
} );
LinearLayout list = LinearLayout list =
(LinearLayout)findViewById( R.id.player_list ); (LinearLayout)findViewById( R.id.player_list );
list.removeAllViews(); list.removeAllViews();
@ -283,6 +308,11 @@ public class GameListItem extends LinearLayout
} }
} ); } );
if ( BuildConstants.THUMBNAIL_SUPPORTED ) {
m_thumb = (ImageView)findViewById( R.id.thumbnail );
m_thumb.setImageBitmap( summary.getThumbnail() );
}
tview = (TextView)findViewById( R.id.role ); tview = (TextView)findViewById( R.id.role );
String roleSummary = summary.summarizeRole(); String roleSummary = summary.summarizeRole();
if ( null != roleSummary ) { if ( null != roleSummary ) {
@ -291,8 +321,6 @@ public class GameListItem extends LinearLayout
tview.setVisibility( View.GONE ); tview.setVisibility( View.GONE );
} }
boolean expanded = DBUtils.getExpanded( m_context, m_rowid );
update( expanded, summary.lastMoveTime, haveATurn, update( expanded, summary.lastMoveTime, haveATurn,
haveALocalTurn ); haveALocalTurn );
} }
@ -300,14 +328,24 @@ public class GameListItem extends LinearLayout
private void toggleSelected() private void toggleSelected()
{ {
if ( null == m_origDrawable ) { m_selected = !m_selected;
if ( m_selected ) {
m_origDrawable = getBackground(); m_origDrawable = getBackground();
setBackgroundColor( XWApp.SEL_COLOR ); setBackgroundColor( XWApp.SEL_COLOR );
} else { } else {
setBackgroundDrawable( m_origDrawable ); setBackgroundDrawable( m_origDrawable );
m_origDrawable = null;
} }
m_cb.itemToggled( m_rowid, null != m_origDrawable ); m_cb.itemToggled( this, m_selected );
}
private void makeThumbnailIf( boolean expanded )
{
if ( expanded && null != m_activity && null != m_summary
&& null == m_summary.getThumbnail()
&& XWPrefs.getThumbEnabled( m_context ) ) {
m_summary.setThumbnail( GameUtils.loadMakeBitmap( m_activity,
m_rowid ) );
}
} }
private class LoadItemTask extends AsyncTask<Void, Void, GameSummary> { private class LoadItemTask extends AsyncTask<Void, Void, GameSummary> {
@ -322,23 +360,23 @@ public class GameListItem extends LinearLayout
{ {
if ( 0 == --m_loadingCount ) { if ( 0 == --m_loadingCount ) {
m_summary = summary; m_summary = summary;
setData( summary ); boolean expanded = DBUtils.getExpanded( m_context, m_rowid );
makeThumbnailIf( expanded );
setData( summary, expanded );
setLoaded( null != m_summary ); setLoaded( null != m_summary );
synchronized( s_invalRows ) { synchronized( s_invalRows ) {
s_invalRows.remove( m_rowid ); s_invalRows.remove( m_rowid );
} }
} }
if ( m_cb.getSelected( m_rowid ) && null != m_origDrawable ) {
toggleSelected();
}
} }
} // class LoadItemTask } // class LoadItemTask
public static GameListItem makeForRow( Context context, long rowid, public static GameListItem makeForRow( Context context, long rowid,
Handler handler, int groupPosition, Handler handler, int groupPosition,
int fieldID, int fieldID,
GameListAdapter.LoadItemCB cb ) SelectableItem cb )
{ {
GameListItem result = GameListItem result =
(GameListItem)Utils.inflate( context, R.layout.game_list_item ); (GameListItem)Utils.inflate( context, R.layout.game_list_item );
@ -367,5 +405,11 @@ public class GameListItem extends LinearLayout
// } // }
// return TextUtils.join(",", strs ); // return TextUtils.join(",", strs );
// } // }
// GameListAdapter.ClickHandler interface
public void longClicked()
{
toggleSelected();
}
} }

View file

@ -91,7 +91,8 @@ public class GameLock {
long sleptTime = 0; long sleptTime = 0;
if ( XWApp.DEBUG_LOCKS ) { if ( XWApp.DEBUG_LOCKS ) {
DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid, maxMillis ); DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid,
maxMillis );
} }
for ( ; ; ) { for ( ; ; ) {

View file

@ -23,9 +23,13 @@ package org.eehouse.android.xw4;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.net.Uri; import android.net.Uri;
import android.text.Html; import android.text.Html;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.Display;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.util.Arrays; import java.util.Arrays;
@ -46,6 +50,8 @@ public class GameUtils {
public static final String INTENT_KEY_ROWID = "rowid"; public static final String INTENT_KEY_ROWID = "rowid";
public static final String INTENT_FORRESULT_ROWID = "forresult"; public static final String INTENT_FORRESULT_ROWID = "forresult";
private static Integer s_minScreen;
public static class NoSuchGameException extends RuntimeException { public static class NoSuchGameException extends RuntimeException {
public NoSuchGameException() { public NoSuchGameException() {
super(); // superfluous super(); // superfluous
@ -114,7 +120,8 @@ public class GameUtils {
} }
if ( null == lockDest ) { if ( null == lockDest ) {
long rowid = saveNewGame( context, gamePtr, gi ); long groupID = DBUtils.getGroupForGame( context, lockSrc.getRowid() );
long rowid = saveNewGame( context, gamePtr, gi, groupID );
lockDest = new GameLock( rowid, true ).lock(); lockDest = new GameLock( rowid, true ).lock();
} else { } else {
saveGame( context, gamePtr, gi, lockDest, true ); saveGame( context, gamePtr, gi, lockDest, true );
@ -180,10 +187,13 @@ public class GameUtils {
public static GameSummary summarize( Context context, GameLock lock ) public static GameSummary summarize( Context context, GameLock lock )
{ {
GameSummary result = null;
CurGameInfo gi = new CurGameInfo( context ); CurGameInfo gi = new CurGameInfo( context );
int gamePtr = loadMakeGame( context, gi, lock ); int gamePtr = loadMakeGame( context, gi, lock );
if ( 0 < gamePtr ) {
return summarizeAndClose( context, lock, gamePtr, gi ); result = summarizeAndClose( context, lock, gamePtr, gi );
}
return result;
} }
public static long dupeGame( Context context, long rowidIn ) public static long dupeGame( Context context, long rowidIn )
@ -289,6 +299,58 @@ public class GameUtils {
return gamePtr; return gamePtr;
} }
public static Bitmap loadMakeBitmap( Activity activity, long rowid )
{
Bitmap thumb = null;
GameLock lock = new GameLock( rowid, false );
if ( lock.tryLock() ) {
CurGameInfo gi = new CurGameInfo( activity );
int gamePtr = loadMakeGame( activity, gi, lock );
if ( 0 != gamePtr ) {
thumb = takeSnapshot( activity, gamePtr, gi );
XwJNI.game_dispose( gamePtr );
DBUtils.saveThumbnail( activity, lock, thumb );
}
lock.unlock();
}
return thumb;
}
public static Bitmap takeSnapshot( Activity activity, int gamePtr,
CurGameInfo gi )
{
Bitmap thumb = null;
if ( BuildConstants.THUMBNAIL_SUPPORTED ) {
if ( XWPrefs.getThumbEnabled( activity ) ) {
int nCols = gi.boardSize;
int scale = XWPrefs.getThumbScale( activity );
Assert.assertTrue( 0 < scale );
if ( null == s_minScreen ) {
Display display =
activity.getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
s_minScreen = new Integer( Math.min( width, height ) );
}
int dim = s_minScreen / scale;
int size = dim - (dim % nCols);
thumb = Bitmap.createBitmap( size, size, Bitmap.Config.ARGB_8888 );
XwJNI.board_figureLayout( gamePtr, gi, 0, 0, size, size,
0, 0, 0, 20, 20, false, null );
ThumbCanvas canvas = new ThumbCanvas( activity, thumb );
XwJNI.board_setDraw( gamePtr, canvas );
XwJNI.board_invalAll( gamePtr );
XwJNI.board_draw( gamePtr );
}
}
return thumb;
}
public static long saveGame( Context context, int gamePtr, public static long saveGame( Context context, int gamePtr,
CurGameInfo gi, GameLock lock, CurGameInfo gi, GameLock lock,
boolean setCreate ) boolean setCreate )
@ -298,10 +360,10 @@ public class GameUtils {
} }
public static long saveNewGame( Context context, int gamePtr, public static long saveNewGame( Context context, int gamePtr,
CurGameInfo gi ) CurGameInfo gi, long groupID )
{ {
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi ); byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
GameLock lock = DBUtils.saveNewGame( context, stream ); GameLock lock = DBUtils.saveNewGame( context, stream, groupID );
long rowid = lock.getRowid(); long rowid = lock.getRowid();
lock.unlock(); lock.unlock();
return rowid; return rowid;
@ -315,22 +377,33 @@ public class GameUtils {
public static GameLock saveNewGame( Context context, byte[] bytes ) public static GameLock saveNewGame( Context context, byte[] bytes )
{ {
return DBUtils.saveNewGame( context, bytes ); return saveNewGame( context, bytes, DBUtils.GROUPID_UNSPEC );
} }
public static long saveNew( Context context, CurGameInfo gi ) public static GameLock saveNewGame( Context context, byte[] bytes,
long groupID )
{ {
return DBUtils.saveNewGame( context, bytes, groupID );
}
public static long saveNew( Context context, CurGameInfo gi, long groupID )
{
if ( DBUtils.GROUPID_UNSPEC == groupID ) {
groupID = XWPrefs.getDefaultNewGameGroup( context );
}
long rowid = DBUtils.ROWID_NOTFOUND; long rowid = DBUtils.ROWID_NOTFOUND;
byte[] bytes = XwJNI.gi_to_stream( gi ); byte[] bytes = XwJNI.gi_to_stream( gi );
if ( null != bytes ) { if ( null != bytes ) {
GameLock lock = DBUtils.saveNewGame( context, bytes ); GameLock lock = DBUtils.saveNewGame( context, bytes, groupID );
rowid = lock.getRowid(); rowid = lock.getRowid();
lock.unlock(); lock.unlock();
} }
return rowid; return rowid;
} }
private static long makeNewMultiGame( Context context, CommsAddrRec addr, private static long makeNewMultiGame( Context context, long groupID,
CommsAddrRec addr,
int[] lang, String[] dict, int[] lang, String[] dict,
int nPlayersT, int nPlayersH, int nPlayersT, int nPlayersH,
String inviteID, int gameID, String inviteID, int gameID,
@ -338,7 +411,8 @@ public class GameUtils {
{ {
long rowid = -1; long rowid = -1;
CurGameInfo gi = new CurGameInfo( context, true ); Assert.assertNotNull( inviteID ); // firing
CurGameInfo gi = new CurGameInfo( context, inviteID );
gi.setLang( lang[0], dict[0] ); gi.setLang( lang[0], dict[0] );
lang[0] = gi.dictLang; lang[0] = gi.dictLang;
dict[0] = gi.dictName; dict[0] = gi.dictName;
@ -353,7 +427,7 @@ public class GameUtils {
// Will need to add a setNPlayers() method to gi to make this // Will need to add a setNPlayers() method to gi to make this
// work // work
Assert.assertTrue( gi.nPlayers == nPlayersT ); Assert.assertTrue( gi.nPlayers == nPlayersT );
rowid = saveNew( context, gi ); rowid = saveNew( context, gi, groupID );
if ( DBUtils.ROWID_NOTFOUND != rowid ) { if ( DBUtils.ROWID_NOTFOUND != rowid ) {
GameLock lock = new GameLock( rowid, true ).lock(); GameLock lock = new GameLock( rowid, true ).lock();
@ -364,8 +438,8 @@ public class GameUtils {
return rowid; return rowid;
} }
public static long makeNewNetGame( Context context, String room, public static long makeNewNetGame( Context context, long groupID,
String inviteID, int[] lang, String room, String inviteID, int[] lang,
String[] dict, int nPlayersT, String[] dict, int nPlayersT,
int nPlayersH ) int nPlayersH )
{ {
@ -375,29 +449,38 @@ public class GameUtils {
CommsAddrRec addr = new CommsAddrRec( relayName, relayPort ); CommsAddrRec addr = new CommsAddrRec( relayName, relayPort );
addr.ip_relay_invite = room; addr.ip_relay_invite = room;
return makeNewMultiGame( context, addr, lang, dict, nPlayersT, return makeNewMultiGame( context, groupID, addr, lang, dict,
nPlayersH, inviteID, 0, false ); nPlayersT, nPlayersH, inviteID, 0, false );
} }
public static long makeNewNetGame( Context context, String room, public static long makeNewNetGame( Context context, long groupID,
String inviteID, int lang, String dict, String room, String inviteID, int lang,
int nPlayers ) String dict, int nPlayers )
{ {
int[] langarr = { lang }; int[] langarr = { lang };
String[] dictArr = { dict }; String[] dictArr = { dict };
return makeNewNetGame( context, room, inviteID, langarr, dictArr, return makeNewNetGame( context, groupID, room, inviteID, langarr,
nPlayers, 1 ); dictArr, nPlayers, 1 );
} }
public static long makeNewNetGame( Context context, NetLaunchInfo info ) public static long makeNewNetGame( Context context, NetLaunchInfo info )
{ {
return makeNewNetGame( context, info.room, info.inviteID, info.lang, return makeNewNetGame( context, DBUtils.GROUPID_UNSPEC, info.room,
info.dict, info.nPlayersT ); info.inviteID, info.lang, info.dict,
info.nPlayersT );
} }
public static long makeNewBTGame( Context context, int gameID, public static long makeNewBTGame( Context context, int gameID,
CommsAddrRec addr, int lang, CommsAddrRec addr, int lang,
int nPlayersT, int nPlayersH ) int nPlayersT, int nPlayersH )
{
return makeNewBTGame( context, DBUtils.GROUPID_UNSPEC, gameID, addr,
lang, nPlayersT, nPlayersH );
}
public static long makeNewBTGame( Context context, long groupID,
int gameID, CommsAddrRec addr, int lang,
int nPlayersT, int nPlayersH )
{ {
long rowid = -1; long rowid = -1;
int[] langa = { lang }; int[] langa = { lang };
@ -405,13 +488,22 @@ public class GameUtils {
if ( isHost ) { if ( isHost ) {
addr = new CommsAddrRec( null, null ); addr = new CommsAddrRec( null, null );
} }
return makeNewMultiGame( context, addr, langa, null, nPlayersT, return makeNewMultiGame( context, groupID, addr, langa, null,
nPlayersH, null, gameID, isHost ); nPlayersT, nPlayersH, null, gameID, isHost );
} }
public static long makeNewSMSGame( Context context, int gameID, public static long makeNewSMSGame( Context context, int gameID,
CommsAddrRec addr, int lang, CommsAddrRec addr,
String dict, int nPlayersT, int lang, String dict, int nPlayersT,
int nPlayersH )
{
return makeNewSMSGame( context, DBUtils.GROUPID_UNSPEC, gameID, addr,
lang, dict, nPlayersT, nPlayersH );
}
public static long makeNewSMSGame( Context context, long groupID,
int gameID, CommsAddrRec addr,
int lang, String dict, int nPlayersT,
int nPlayersH ) int nPlayersH )
{ {
long rowid = -1; long rowid = -1;
@ -421,64 +513,71 @@ public class GameUtils {
if ( isHost ) { if ( isHost ) {
addr = new CommsAddrRec(CommsAddrRec.CommsConnType.COMMS_CONN_SMS); addr = new CommsAddrRec(CommsAddrRec.CommsConnType.COMMS_CONN_SMS);
} }
return makeNewMultiGame( context, addr, langa, dicta, nPlayersT, String inviteID = GameUtils.formatGameID( gameID );
nPlayersH, null, gameID, isHost ); return makeNewMultiGame( context, groupID, addr, langa, dicta,
nPlayersT, nPlayersH, inviteID, gameID,
isHost );
} }
public static void launchInviteActivity( Context context, public static void launchInviteActivity( Activity activity, int chosen,
boolean choseEmail,
String room, String inviteID, String room, String inviteID,
int lang, String dict, int lang, String dict,
int nPlayers ) int nPlayers )
{ {
if ( null == inviteID ) { Assert.assertNotNull( inviteID );
inviteID = makeRandomID();
}
Uri gameUri = NetLaunchInfo.makeLaunchUri( context, room, inviteID,
lang, dict, nPlayers );
if ( null != gameUri ) { if ( DlgDelegate.NFC_BTN == chosen ) {
int fmtId = choseEmail? R.string.invite_htmf : R.string.invite_txtf; Utils.showToast( activity, R.string.sms_ready_text );
int choiceID; } else {
String message = context.getString( fmtId, gameUri.toString() ); Uri gameUri = NetLaunchInfo.makeLaunchUri( activity, room, inviteID,
lang, dict, nPlayers );
String msgString = null == gameUri ? null : gameUri.toString();
Intent intent = new Intent(); if ( null != msgString ) {
if ( choseEmail ) { boolean choseEmail = DlgDelegate.EMAIL_BTN == chosen;
intent.setAction( Intent.ACTION_SEND );
String subject =
Utils.format( context, R.string.invite_subjectf, room );
intent.putExtra( Intent.EXTRA_SUBJECT, subject );
intent.putExtra( Intent.EXTRA_TEXT, Html.fromHtml(message) );
File attach = null; int fmtId = choseEmail? R.string.invite_htmf : R.string.invite_txtf;
File tmpdir = XWApp.ATTACH_SUPPORTED ? int choiceID;
DictUtils.getDownloadDir( context ) : null; String message = activity.getString( fmtId, msgString );
if ( null != tmpdir ) { // no attachment
attach = makeJsonFor( tmpdir, room, inviteID, lang,
dict, nPlayers );
}
if ( null == attach ) { // no attachment Intent intent = new Intent();
intent.setType( "message/rfc822"); if ( choseEmail ) {
intent.setAction( Intent.ACTION_SEND );
String subject =
Utils.format( activity, R.string.invite_subjectf, room );
intent.putExtra( Intent.EXTRA_SUBJECT, subject );
intent.putExtra( Intent.EXTRA_TEXT, Html.fromHtml(message) );
File attach = null;
File tmpdir = XWApp.ATTACH_SUPPORTED ?
DictUtils.getDownloadDir( activity ) : null;
if ( null != tmpdir ) { // no attachment
attach = makeJsonFor( tmpdir, room, inviteID, lang,
dict, nPlayers );
}
if ( null == attach ) { // no attachment
intent.setType( "message/rfc822");
} else {
String mime = activity.getString( R.string.invite_mime );
intent.setType( mime );
Uri uri = Uri.fromFile( attach );
intent.putExtra( Intent.EXTRA_STREAM, uri );
}
choiceID = R.string.invite_chooser_email;
} else { } else {
String mime = context.getString( R.string.invite_mime ); intent.setAction( Intent.ACTION_VIEW );
intent.setType( mime ); intent.setType( "vnd.android-dir/mms-sms" );
Uri uri = Uri.fromFile( attach ); intent.putExtra( "sms_body", message );
intent.putExtra( Intent.EXTRA_STREAM, uri ); choiceID = R.string.invite_chooser_sms;
} }
choiceID = R.string.invite_chooser_email; String choiceType = activity.getString( choiceID );
} else { String chooserMsg =
intent.setAction( Intent.ACTION_VIEW ); Utils.format( activity, R.string.invite_chooserf, choiceType );
intent.setType( "vnd.android-dir/mms-sms" ); activity.startActivity( Intent.createChooser( intent, chooserMsg ) );
intent.putExtra( "sms_body", message );
choiceID = R.string.invite_chooser_sms;
} }
String choiceType = context.getString( choiceID );
String chooserMsg =
Utils.format( context, R.string.invite_chooserf, choiceType );
context.startActivity( Intent.createChooser( intent, chooserMsg ) );
} }
} }
@ -766,10 +865,17 @@ public class GameUtils {
activity.startActivity( intent ); activity.startActivity( intent );
} }
public static String formatGameID( int gameID )
{
Assert.assertTrue( 0 != gameID );
// substring: Keep it short so fits in SMS better
return String.format( "%X", gameID ).substring( 0, 4 );
}
public static String makeRandomID() public static String makeRandomID()
{ {
int rint = newGameID(); int rint = newGameID();
return String.format( "%X", rint ).substring( 0, 4 ); return formatGameID( rint );
} }
public static int newGameID() public static int newGameID()

View file

@ -0,0 +1,162 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2013 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.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.nfc.NfcManager;
import android.os.Parcelable;
import junit.framework.Assert;
public class NFCUtils {
public interface NFCActor {
String makeNFCMessage();
}
private static boolean s_inSDK;
private static boolean[] s_nfcAvail;
private static SafeNFC s_safeNFC;
static {
s_inSDK = 14 <= Integer.valueOf( android.os.Build.VERSION.SDK );
if ( s_inSDK ) {
s_safeNFC = new SafeNFCImpl();
}
}
private static interface SafeNFC {
public void register( Activity activity );
}
private static class SafeNFCImpl implements SafeNFC {
public void register( final Activity activity )
{
Assert.assertTrue( activity instanceof NFCActor );
final NFCActor actor = (NFCActor)activity;
NfcAdapter.CreateNdefMessageCallback cb =
new NfcAdapter.CreateNdefMessageCallback() {
public NdefMessage createNdefMessage( NfcEvent event ) {
NdefMessage msg = null;
String data = actor.makeNFCMessage();
if ( null != data ) {
msg = makeMessage( activity, data );
}
return msg;
}
};
NfcManager manager =
(NfcManager)activity.getSystemService( Context.NFC_SERVICE );
NfcAdapter adapter = manager.getDefaultAdapter();
adapter.setNdefPushMessageCallback( cb, activity );
}
}
// Return array of two booleans, the first indicating whether the
// device supports NFC and the second whether it's on. Only the
// second can change.
public static boolean[] nfcAvail( Context context )
{
if ( null == s_nfcAvail ) {
s_nfcAvail = new boolean[] {
s_inSDK && null != getNFCAdapter( context ),
false
};
}
if ( s_nfcAvail[0] ) {
s_nfcAvail[1] = getNFCAdapter( context ).isEnabled();
}
return s_nfcAvail;
}
public static String getFromIntent( Intent intent )
{
String result = null;
if ( NfcAdapter.ACTION_NDEF_DISCOVERED.equals( intent.getAction() ) ) {
Parcelable[] rawMsgs =
intent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES );
// only one message sent during the beam
NdefMessage msg = (NdefMessage)rawMsgs[0];
// record 0 contains the MIME type, record 1 is the AAR, if present
result = new String( msg.getRecords()[0].getPayload() );
}
return result;
}
public static void register( Activity activity )
{
if ( null != s_safeNFC ) {
s_safeNFC.register( activity );
}
}
public static Dialog makeEnableNFCDialog( final Activity activity )
{
DialogInterface.OnClickListener lstnr
= new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
activity.
startActivity( new Intent("android.settings"
+ ".NFC_SETTINGS" ) );
}
};
return new AlertDialog.Builder( activity )
.setTitle( R.string.info_title )
.setMessage( R.string.enable_nfc )
.setPositiveButton( R.string.button_cancel, null )
.setNegativeButton( R.string.button_go_settings, lstnr )
.create();
}
private static NdefMessage makeMessage( Activity activity, String data )
{
String mimeType = activity.getString( R.string.xwords_nfc_mime );
NdefMessage msg = new NdefMessage( new NdefRecord[] {
new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
mimeType.getBytes(), new byte[0],
data.getBytes())
,NdefRecord.
createApplicationRecord( activity.getPackageName() )
});
return msg;
}
private static NfcAdapter getNFCAdapter( Context context )
{
NfcManager manager =
(NfcManager)context.getSystemService( Context.NFC_SERVICE );
return manager.getDefaultAdapter();
}
}

View file

@ -57,6 +57,21 @@ public class NetLaunchInfo {
bundle.putBoolean( VALID, m_valid ); bundle.putBoolean( VALID, m_valid );
} }
public NetLaunchInfo( String data )
{
try {
JSONObject json = new JSONObject( data );
room = json.getString( MultiService.ROOM );
inviteID = json.getString( MultiService.INVITEID );
lang = json.getInt( MultiService.LANG );
dict = json.getString( MultiService.DICT );
nPlayersT = json.getInt( MultiService.NPLAYERST );
m_valid = true;
} catch ( org.json.JSONException jse ) {
m_valid = false;
}
}
public NetLaunchInfo( Bundle bundle ) public NetLaunchInfo( Bundle bundle )
{ {
lang = bundle.getInt( LANG ); lang = bundle.getInt( LANG );
@ -134,6 +149,25 @@ public class NetLaunchInfo {
return ub.build(); return ub.build();
} }
public static String makeLaunchJSON( Context context, String room,
String inviteID, int lang,
String dict, int nPlayersT )
{
String result = null;
try {
result = new JSONObject()
.put( MultiService.ROOM, room )
.put( MultiService.INVITEID, inviteID )
.put( MultiService.LANG, lang )
.put( MultiService.DICT, dict )
.put( MultiService.NPLAYERST, nPlayersT )
.toString();
} catch ( org.json.JSONException jse ) {
DbgUtils.loge( jse );
}
return result;
}
public boolean isValid() public boolean isValid()
{ {
return m_valid; return m_valid;

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */ /* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/* /*
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All * Copyright 2009 - 2013 by Eric House (xwords@eehouse.org). All
* rights reserved. * rights reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -51,6 +51,7 @@ public class NewGameActivity extends XWActivity {
private static final String SAVE_REMOTEGAME = "REMOTEGAME"; private static final String SAVE_REMOTEGAME = "REMOTEGAME";
private static final String SAVE_GAMEID = "GAMEID"; private static final String SAVE_GAMEID = "GAMEID";
private static final String SAVE_NAMEFOR = "SAVE_NAMEFOR"; private static final String SAVE_NAMEFOR = "SAVE_NAMEFOR";
private static final String GROUPID_EXTRA = "groupid";
private static final int CONFIG_FOR_BT = 1; private static final int CONFIG_FOR_BT = 1;
private static final int CONFIG_FOR_SMS = 2; private static final int CONFIG_FOR_SMS = 2;
private static final int INVITE_FOR_BT = 3; private static final int INVITE_FOR_BT = 3;
@ -58,6 +59,7 @@ public class NewGameActivity extends XWActivity {
// Dialogs // Dialogs
private static final int NAME_GAME = DlgDelegate.DIALOG_LAST + 1; private static final int NAME_GAME = DlgDelegate.DIALOG_LAST + 1;
private static final int ENABLE_NFC = DlgDelegate.DIALOG_LAST + 2;
private boolean m_showsOn; private boolean m_showsOn;
private boolean m_nameForBT; private boolean m_nameForBT;
@ -68,6 +70,7 @@ public class NewGameActivity extends XWActivity {
private long m_newRowID = -1; private long m_newRowID = -1;
private String m_gameName; private String m_gameName;
private int m_gameID; private int m_gameID;
private long m_groupID;
private String m_remoteDev; private String m_remoteDev;
@Override @Override
@ -76,6 +79,8 @@ public class NewGameActivity extends XWActivity {
super.onCreate( savedInstanceState ); super.onCreate( savedInstanceState );
getBundledData( savedInstanceState ); getBundledData( savedInstanceState );
m_groupID = getIntent().getLongExtra( GROUPID_EXTRA, -1 );
setContentView( R.layout.new_game ); setContentView( R.layout.new_game );
TextView desc = (TextView)findViewById( R.id.newgame_local_desc ); TextView desc = (TextView)findViewById( R.id.newgame_local_desc );
@ -99,7 +104,7 @@ public class NewGameActivity extends XWActivity {
} }
} ); } );
button = (Button)findViewById( R.id.newgame_invite ); button = (Button)findViewById( R.id.newgame_invite_net );
button.setOnClickListener( new View.OnClickListener() { button.setOnClickListener( new View.OnClickListener() {
@Override @Override
public void onClick( View v ) { public void onClick( View v ) {
@ -149,12 +154,12 @@ public class NewGameActivity extends XWActivity {
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
@Override @Override
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
switch( id ) { switch( id ) {
case NEW_GAME_ACTION: case NEW_GAME_ACTION:
if ( DlgDelegate.DISMISS_BUTTON != which ) { if ( DlgDelegate.DISMISS_BUTTON != which ) {
makeNewGame( true, true, DlgDelegate.EMAIL_BTN == which ); makeNewGame( true, true, which );
} }
break; break;
default: default:
@ -232,8 +237,8 @@ public class NewGameActivity extends XWActivity {
m_gameID, m_gameName, m_gameID, m_gameName,
m_lang, m_dict, 2, 1 ); m_lang, m_dict, 2, 1 );
long rowid = GameUtils. long rowid = GameUtils.
makeNewSMSGame( thiz, m_gameID, null, makeNewSMSGame( thiz, m_groupID, m_gameID,
m_lang, m_dict, 2, 1 ); null, m_lang, m_dict, 2, 1 );
DBUtils.setName( thiz, rowid, m_gameName ); DBUtils.setName( thiz, rowid, m_gameName );
GameUtils.launchGame( thiz, rowid, true ); GameUtils.launchGame( thiz, rowid, true );
finish(); finish();
@ -250,6 +255,9 @@ public class NewGameActivity extends XWActivity {
.create(); .create();
Utils.setRemoveOnDismiss( this, dialog, id ); Utils.setRemoveOnDismiss( this, dialog, id );
break;
case ENABLE_NFC:
dialog = NFCUtils.makeEnableNFCDialog( this );
break; break;
} }
} }
@ -285,8 +293,8 @@ public class NewGameActivity extends XWActivity {
public void run() { public void run() {
long rowid = long rowid =
GameUtils.makeNewBTGame( NewGameActivity.this, GameUtils.makeNewBTGame( NewGameActivity.this,
gameID, null, m_lang, m_groupID, gameID, null,
2, 1 ); m_lang, 2, 1 );
DBUtils.setName( NewGameActivity.this, DBUtils.setName( NewGameActivity.this,
rowid, m_gameName ); rowid, m_gameName );
GameUtils.launchGame( NewGameActivity.this, GameUtils.launchGame( NewGameActivity.this,
@ -305,59 +313,67 @@ public class NewGameActivity extends XWActivity {
{ {
if ( launch && networked ) { if ( launch && networked ) {
// Let 'em cancel before we make the game // Let 'em cancel before we make the game
showEmailOrSMSThen( NEW_GAME_ACTION ); showInviteChoicesThen( NEW_GAME_ACTION );
} else { } else {
makeNewGame( networked, launch, false ); makeNewGame( networked, launch, DlgDelegate.SMS_BTN );
} }
} }
private void makeNewGame( boolean networked, boolean launch, private void makeNewGame( boolean networked, boolean launch,
boolean choseEmail ) int chosen )
{ {
String room = null; if ( DlgDelegate.NFC_BTN == chosen
String inviteID = null; && !NFCUtils.nfcAvail( this )[1] ) {
long rowid; showDialog( ENABLE_NFC );
int[] lang = {0};
String[] dict = {null};
final int nPlayers = 2; // hard-coded for no-configure case
if ( networked ) {
room = GameUtils.makeRandomID();
inviteID = GameUtils.makeRandomID();
rowid = GameUtils.makeNewNetGame( this, room, inviteID, lang,
dict, nPlayers, 1 );
} else { } else {
rowid = GameUtils.saveNew( this, new CurGameInfo( this ) ); String room = null;
} String inviteID = null;
long rowid;
int[] lang = {0};
String[] dict = {null};
final int nPlayers = 2; // hard-coded for no-configure case
if ( launch ) {
GameUtils.launchGame( this, rowid, networked );
if ( networked ) { if ( networked ) {
GameUtils.launchInviteActivity( this, choseEmail, room, room = GameUtils.makeRandomID();
inviteID, lang[0], dict[0], inviteID = GameUtils.makeRandomID();
nPlayers ); rowid = GameUtils.makeNewNetGame( this, m_groupID, room, inviteID,
lang, dict, nPlayers, 1 );
} else {
rowid = GameUtils.saveNew( this, new CurGameInfo( this ),
m_groupID );
} }
} else {
GameUtils.doConfig( this, rowid, GameConfig.class );
}
finish(); if ( launch ) {
GameUtils.launchGame( this, rowid, networked );
if ( networked ) {
GameUtils.launchInviteActivity( this, chosen, room,
inviteID, lang[0], dict[0],
nPlayers );
}
} else {
GameUtils.doConfig( this, rowid, GameConfig.class );
}
finish();
}
} }
private void makeNewBTGame( boolean useDefaults ) private void makeNewBTGame( boolean useDefaults )
{ {
int gameID = GameUtils.newGameID(); if ( XWApp.BTSUPPORTED ) {
if ( !useDefaults ) { int gameID = GameUtils.newGameID();
m_newRowID = GameUtils.makeNewBTGame( NewGameActivity.this, if ( !useDefaults ) {
gameID, null, m_lang, m_newRowID = GameUtils.makeNewBTGame( NewGameActivity.this,
2, 1 ); // initial defaults m_groupID, gameID, null,
Intent intent = new Intent( this, GameConfig.class ); m_lang, 2, 1 );
intent.setAction( Intent.ACTION_EDIT ); Intent intent = new Intent( this, GameConfig.class );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_newRowID ); intent.setAction( Intent.ACTION_EDIT );
intent.putExtra( GameUtils.INTENT_FORRESULT_ROWID, true ); intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_newRowID );
startActivityForResult( intent, CONFIG_FOR_BT ); intent.putExtra( GameUtils.INTENT_FORRESULT_ROWID, true );
} else { startActivityForResult( intent, CONFIG_FOR_BT );
BTInviteActivity.launchForResult( this, 1, INVITE_FOR_BT ); } else {
BTInviteActivity.launchForResult( this, 1, INVITE_FOR_BT );
}
} }
} }
@ -366,8 +382,8 @@ public class NewGameActivity extends XWActivity {
int gameID = GameUtils.newGameID(); int gameID = GameUtils.newGameID();
if ( !useDefaults ) { if ( !useDefaults ) {
m_newRowID = GameUtils.makeNewSMSGame( NewGameActivity.this, m_newRowID = GameUtils.makeNewSMSGame( NewGameActivity.this,
gameID, null, m_lang, m_groupID, gameID, null,
m_dict, 2, 1 ); m_lang, m_dict, 2, 1 );
String name = Utils.format( this, R.string.dft_sms_namef, String name = Utils.format( this, R.string.dft_sms_namef,
gameID & 0xFFFF ); gameID & 0xFFFF );
DBUtils.setName( this, m_newRowID, name ); DBUtils.setName( this, m_newRowID, name );
@ -468,4 +484,11 @@ public class NewGameActivity extends XWActivity {
} }
} }
} }
public static void startActivity( Activity parent, long groupID )
{
Intent intent = new Intent( parent, NewGameActivity.class );
intent.putExtra( GROUPID_EXTRA, groupID );
parent.startActivity( intent );
}
} }

View file

@ -19,13 +19,15 @@
*/ */
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.preference.PreferenceActivity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
@ -37,11 +39,16 @@ public class PrefsActivity extends PreferenceActivity
private static final int REVERT_COLORS = 1; private static final int REVERT_COLORS = 1;
private static final int REVERT_ALL = 2; private static final int REVERT_ALL = 2;
public static final int CONFIRM_SMS = 3; public static final int CONFIRM_SMS = 3;
public static final int EXPLAIN_TITLE = 4;
private String m_keyLogging; private String m_keyLogging;
private String m_smsToasting; private String m_smsToasting;
private String m_smsEnable; private String m_smsEnable;
private String m_downloadPath; private String m_downloadPath;
private String m_thumbEnabled;
private String m_thumbSize;
private String m_hideTitle;
@Override @Override
protected Dialog onCreateDialog( int id ) protected Dialog onCreateDialog( int id )
@ -97,6 +104,13 @@ public class PrefsActivity extends PreferenceActivity
case CONFIRM_SMS: case CONFIRM_SMS:
dialog = SMSCheckBoxPreference.onCreateDialog( this, id ); dialog = SMSCheckBoxPreference.onCreateDialog( this, id );
break; break;
case EXPLAIN_TITLE:
dialog = new AlertDialog.Builder( this )
.setMessage( R.string.no_hide_titlebar )
.setTitle( R.string.info_title )
.setPositiveButton( R.string.button_ok, null )
.create();
break;
} }
if ( null == dialog && null != lstnr ) { if ( null == dialog && null != lstnr ) {
@ -123,6 +137,9 @@ public class PrefsActivity extends PreferenceActivity
m_smsToasting = getString( R.string.key_show_sms ); m_smsToasting = getString( R.string.key_show_sms );
m_smsEnable = getString( R.string.key_enable_sms ); m_smsEnable = getString( R.string.key_enable_sms );
m_downloadPath = getString( R.string.key_download_path ); m_downloadPath = getString( R.string.key_download_path );
m_thumbEnabled = getString( R.string.key_thumb_enabled );
m_thumbSize = getString( R.string.key_thumbsize );
m_hideTitle = getString( R.string.key_hide_title );
Button button = (Button)findViewById( R.id.revert_colors ); Button button = (Button)findViewById( R.id.revert_colors );
button.setOnClickListener( new View.OnClickListener() { button.setOnClickListener( new View.OnClickListener() {
@ -185,6 +202,17 @@ public class PrefsActivity extends PreferenceActivity
} }
} }
DictUtils.invalDictList(); DictUtils.invalDictList();
} else if ( key.equals( m_thumbEnabled )
|| key.equals( m_thumbSize ) ) {
DBUtils.clearThumbnails( this );
} else if ( key.equals( m_hideTitle ) ) {
if ( sp.getBoolean( key, false ) && ABUtils.haveActionBar() ) {
CheckBoxPreference pref
= (CheckBoxPreference)findPreference(key);
pref.setChecked( false );
pref.setEnabled( false );
showDialog( EXPLAIN_TITLE );
}
} }
} }

View file

@ -367,7 +367,7 @@ public class RelayService extends XWService
private boolean startFetchThreadIf() private boolean startFetchThreadIf()
{ {
DbgUtils.logf( "startFetchThreadIf()" ); // DbgUtils.logf( "startFetchThreadIf()" );
boolean handled = !XWApp.UDP_ENABLED; boolean handled = !XWApp.UDP_ENABLED;
if ( handled && null == m_fetchThread ) { if ( handled && null == m_fetchThread ) {
m_fetchThread = new Thread( null, new Runnable() { m_fetchThread = new Thread( null, new Runnable() {
@ -416,7 +416,7 @@ public class RelayService extends XWService
resetExitTimer(); resetExitTimer();
gotPacket( packet ); gotPacket( packet );
} catch ( java.io.InterruptedIOException iioe ) { } catch ( java.io.InterruptedIOException iioe ) {
DbgUtils.logf( "FYI: udp receive timeout" ); // DbgUtils.logf( "FYI: udp receive timeout" );
} catch( java.io.IOException ioe ) { } catch( java.io.IOException ioe ) {
break; break;
} }
@ -675,8 +675,8 @@ public class RelayService extends XWService
DbgUtils.logf( "registering devID \"%s\" (type=%s)", devid, DbgUtils.logf( "registering devID \"%s\" (type=%s)", devid,
typ.toString() ); typ.toString() );
out.writeShort( GitVersion.CLIENT_VERS_RELAY ); out.writeShort( BuildConstants.CLIENT_VERS_RELAY );
writeVLIString( out, GitVersion.VERS ); writeVLIString( out, BuildConstants.GIT_REV );
writeVLIString( out, Build.MODEL ); writeVLIString( out, Build.MODEL );
writeVLIString( out, Build.VERSION.RELEASE ); writeVLIString( out, Build.VERSION.RELEASE );
@ -1191,7 +1191,7 @@ public class RelayService extends XWService
long interval = Utils.getCurSeconds() - m_lastGamePacketReceived; long interval = Utils.getCurSeconds() - m_lastGamePacketReceived;
result = interval < MAX_KEEPALIVE_SECS; result = interval < MAX_KEEPALIVE_SECS;
} }
DbgUtils.logf( "RelayService.shouldMaintainConnection=>%b", result ); // DbgUtils.logf( "RelayService.shouldMaintainConnection=>%b", result );
return result; return result;
} }

View file

@ -190,7 +190,7 @@ public class SMSInviteActivity extends InviteActivity {
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
@Override @Override
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
switch( which ) { switch( which ) {
case AlertDialog.BUTTON_POSITIVE: case AlertDialog.BUTTON_POSITIVE:

View file

@ -0,0 +1,38 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2009-2013 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 org.eehouse.android.xw4.jni.GameSummary;
// Interfaces used for (list) items that are selectable via
// long-tapping.
public interface SelectableItem {
public interface LongClickHandler {
public void longClicked();
}
public void itemClicked( LongClickHandler clicked, GameSummary summary );
public void itemToggled( LongClickHandler toggled, boolean selected );
public boolean getSelected( LongClickHandler obj );
}

View file

@ -0,0 +1,50 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2013 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.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Rect;
public class ThumbCanvas extends BoardCanvas {
public ThumbCanvas( Activity activity, Bitmap bitmap )
{
super( activity, bitmap, null, null );
DbgUtils.logf( "creating new ThumbCanvas" );
}
// These should not be needed if common code gets fixed! So the
// whole class should go away. PENDING
@Override
public boolean scoreBegin( Rect rect, int numPlayers, int[] scores,
int remCount )
{
return false;
}
@Override
public boolean trayBegin( Rect rect, int owner, int score )
{
return false;
}
}

View file

@ -107,7 +107,8 @@ public class Toolbar {
} }
} }
private void setLongClickListener( int index, View.OnLongClickListener listener ) private void setLongClickListener( int index,
View.OnLongClickListener listener )
{ {
ImageButton button = getViewFor( index ); ImageButton button = getViewFor( index );
if ( null != button ) { if ( null != button ) {
@ -126,8 +127,8 @@ public class Toolbar {
setListener( index, listener ); setListener( index, listener );
} }
public void setLongClickListener( int index, final int msgID, final int prefsKey, public void setLongClickListener( int index, final int msgID,
final int callback ) final int prefsKey, final int callback )
{ {
View.OnLongClickListener listener = new View.OnLongClickListener() { View.OnLongClickListener listener = new View.OnLongClickListener() {
public boolean onLongClick( View view ) { public boolean onLongClick( View view ) {

View file

@ -121,7 +121,7 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
appParams.put( k_NAME, packageName ); appParams.put( k_NAME, packageName );
appParams.put( k_AVERS, versionCode ); appParams.put( k_AVERS, versionCode );
appParams.put( k_GVERS, GitVersion.VERS ); appParams.put( k_GVERS, BuildConstants.GIT_REV );
appParams.put( k_INSTALLER, installer ); appParams.put( k_INSTALLER, installer );
if ( devOK( context ) ) { if ( devOK( context ) ) {
appParams.put( k_DEVOK, true ); appParams.put( k_DEVOK, true );

View file

@ -28,7 +28,6 @@ import android.app.PendingIntent;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -72,24 +71,6 @@ public class Utils {
private static Boolean s_hasSmallScreen = null; private static Boolean s_hasSmallScreen = null;
private static Random s_random = new Random(); private static Random s_random = new Random();
private static interface SafeInvalOptionsMenu {
public void doInval( Activity activity );
}
private static class SafeInvalOptionsMenuImpl
implements SafeInvalOptionsMenu {
public void doInval( Activity activity ) {
activity.invalidateOptionsMenu();
}
}
private static SafeInvalOptionsMenu s_safeInval = null;
static {
int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK );
if ( 11 <= sdkVersion ) {
s_safeInval = new SafeInvalOptionsMenuImpl();
}
}
private Utils() {} private Utils() {}
public static int nextRandomInt() public static int nextRandomInt()
@ -172,7 +153,7 @@ public class Utils {
String[] addrs = { context.getString( R.string.email_author_email ) }; String[] addrs = { context.getString( R.string.email_author_email ) };
intent.putExtra( Intent.EXTRA_EMAIL, addrs ); intent.putExtra( Intent.EXTRA_EMAIL, addrs );
String body = format( context, R.string.email_body_revf, String body = format( context, R.string.email_body_revf,
GitVersion.VERS ); BuildConstants.GIT_REV );
intent.putExtra( Intent.EXTRA_TEXT, body ); intent.putExtra( Intent.EXTRA_TEXT, body );
String chooserMsg = context.getString( R.string.email_author_chooser ); String chooserMsg = context.getString( R.string.email_author_chooser );
context.startActivity( Intent.createChooser( intent, chooserMsg ) ); context.startActivity( Intent.createChooser( intent, chooserMsg ) );
@ -376,13 +357,6 @@ public class Utils {
item.setEnabled( enabled ); item.setEnabled( enabled );
} }
public static void invalidateOptionsMenuIf( Activity activity )
{
if ( null != s_safeInval ) {
s_safeInval.doInval( activity );
}
}
public static boolean hasSmallScreen( Context context ) public static boolean hasSmallScreen( Context context )
{ {
if ( null == s_hasSmallScreen ) { if ( null == s_hasSmallScreen ) {

View file

@ -106,6 +106,12 @@ public class XWActivity extends Activity
m_delegate.showAboutDialog(); m_delegate.showAboutDialog();
} }
protected void showNotAgainDlgThen( String msg, int prefsKey,
int action )
{
m_delegate.showNotAgainDlgThen( msg, prefsKey, action, null );
}
protected void showNotAgainDlgThen( int msgID, int prefsKey, protected void showNotAgainDlgThen( int msgID, int prefsKey,
int action ) int action )
{ {
@ -147,9 +153,9 @@ public class XWActivity extends Activity
m_delegate.showConfirmThen( getString(msg), posButton, action ); m_delegate.showConfirmThen( getString(msg), posButton, action );
} }
public void showEmailOrSMSThen( int action ) public void showInviteChoicesThen( int action )
{ {
m_delegate.showEmailOrSMSThen( action ); m_delegate.showInviteChoicesThen( action );
} }
protected void doSyncMenuitem() protected void doSyncMenuitem()
@ -178,7 +184,7 @@ public class XWActivity extends Activity
} }
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
Assert.fail(); Assert.fail();
} }

View file

@ -43,7 +43,7 @@ public class XWApp extends Application {
public static final String SMS_PUBLIC_HEADER = "-XW4"; public static final String SMS_PUBLIC_HEADER = "-XW4";
public static final int MAX_TRAY_TILES = 7; // comtypes.h public static final int MAX_TRAY_TILES = 7; // comtypes.h
public static final int SEL_COLOR = Color.argb( 255, 127, 127, 255 ); public static final int SEL_COLOR = Color.argb( 0xFF, 0x09, 0x70, 0x93 );
private static UUID s_UUID = null; private static UUID s_UUID = null;
private static Boolean s_onEmulator = null; private static Boolean s_onEmulator = null;

View file

@ -84,6 +84,12 @@ public class XWExpandableListActivity extends ExpandableListActivity
m_delegate.doSyncMenuitem(); m_delegate.doSyncMenuitem();
} }
protected void showNotAgainDlgThen( int msgID, int prefsKey,
int action, Object... params )
{
m_delegate.showNotAgainDlgThen( msgID, prefsKey, action, params );
}
protected void showNotAgainDlgThen( int msgID, int prefsKey, protected void showNotAgainDlgThen( int msgID, int prefsKey,
int action ) int action )
{ {
@ -111,23 +117,25 @@ public class XWExpandableListActivity extends ExpandableListActivity
m_delegate.showOKOnlyDialog( msg ); m_delegate.showOKOnlyDialog( msg );
} }
protected void showConfirmThen( String msg, int action ) protected void showConfirmThen( String msg, int action, Object... params )
{ {
m_delegate.showConfirmThen( msg, action ); m_delegate.showConfirmThen( msg, action, params );
} }
protected void showConfirmThen( String msg, int posButton, int action ) protected void showConfirmThen( String msg, int posButton, int action,
Object... params )
{ {
m_delegate.showConfirmThen( msg, posButton, action ); m_delegate.showConfirmThen( msg, posButton, action, params );
} }
protected void showConfirmThen( int msg, int posButton, int action ) protected void showConfirmThen( int msg, int posButton, int action,
Object... params )
{ {
m_delegate.showConfirmThen( getString(msg), posButton, action ); m_delegate.showConfirmThen( getString(msg), posButton, action, params );
} }
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
Assert.fail(); Assert.fail();
} }

View file

@ -163,7 +163,7 @@ public class XWListActivity extends ListActivity
} }
// DlgDelegate.DlgClickNotify interface // DlgDelegate.DlgClickNotify interface
public void dlgButtonClicked( int id, int which ) public void dlgButtonClicked( int id, int which, Object[] params )
{ {
Assert.fail(); Assert.fail();
} }

View file

@ -20,19 +20,25 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.widget.LinearLayout;
import android.view.View;
import android.widget.TextView;
import android.widget.ImageButton;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
public class XWListItem extends LinearLayout {
public class XWListItem extends LinearLayout
implements SelectableItem.LongClickHandler {
private int m_position; private int m_position;
private Context m_context; private Context m_context;
private Object m_cached; private Object m_cached;
private DeleteCallback m_cb; private DeleteCallback m_delCb;
private Drawable m_origDrawable;
private boolean m_selected = false;
private SelectableItem m_selCb;
public interface DeleteCallback { public interface DeleteCallback {
void deleteCalled( XWListItem item ); void deleteCalled( XWListItem item );
@ -69,17 +75,29 @@ public class XWListItem extends LinearLayout {
public void setDeleteCallback( DeleteCallback cb ) public void setDeleteCallback( DeleteCallback cb )
{ {
m_cb = cb; m_delCb = cb;
ImageButton button = (ImageButton)findViewById( R.id.del ); ImageButton button = (ImageButton)findViewById( R.id.del );
button.setOnClickListener( new View.OnClickListener() { button.setOnClickListener( new View.OnClickListener() {
@Override @Override
public void onClick( View view ) { public void onClick( View view ) {
m_cb.deleteCalled( XWListItem.this ); m_delCb.deleteCalled( XWListItem.this );
} }
} ); } );
button.setVisibility( View.VISIBLE ); button.setVisibility( View.VISIBLE );
} }
public void setSelCB( SelectableItem selCB )
{
m_selCb = selCB;
}
public void setSelected( boolean selected )
{
if ( selected != m_selected ) {
toggleSelected();
}
}
@Override @Override
public void setEnabled( boolean enabled ) public void setEnabled( boolean enabled )
{ {
@ -105,4 +123,21 @@ public class XWListItem extends LinearLayout {
return m_cached; return m_cached;
} }
// SelectableItem.LongClickHandler interface
public void longClicked()
{
toggleSelected();
}
private void toggleSelected()
{
m_selected = !m_selected;
if ( m_selected ) {
m_origDrawable = getBackground();
setBackgroundColor( XWApp.SEL_COLOR );
} else {
setBackgroundDrawable( m_origDrawable );
}
m_selCb.itemToggled( this, m_selected );
}
} }

View file

@ -27,10 +27,12 @@ import android.util.AttributeSet;
import junit.framework.Assert; import junit.framework.Assert;
public class XWListPreference extends ListPreference { public class XWListPreference extends ListPreference {
protected Context m_context;
public XWListPreference( Context context, AttributeSet attrs ) public XWListPreference( Context context, AttributeSet attrs )
{ {
super( context, attrs ); super( context, attrs );
m_context = context;
} }
protected void onAttachedToActivity() protected void onAttachedToActivity()

View file

@ -27,6 +27,8 @@ import android.text.TextUtils;
import com.google.android.gcm.GCMRegistrar; import com.google.android.gcm.GCMRegistrar;
import java.util.ArrayList; import java.util.ArrayList;
import junit.framework.Assert;
public class XWPrefs { public class XWPrefs {
public static boolean getSMSEnabled( Context context ) public static boolean getSMSEnabled( Context context )
@ -39,6 +41,11 @@ public class XWPrefs {
return getPrefsBoolean( context, R.string.key_enable_debug, false ); return getPrefsBoolean( context, R.string.key_enable_debug, false );
} }
public static boolean getSecondInviteAllowed( Context context )
{
return getPrefsBoolean( context, R.string.key_enable_dup_invite, false );
}
public static String getDefaultRelayHost( Context context ) public static String getDefaultRelayHost( Context context )
{ {
return getPrefsString( context, R.string.key_relay_host ); return getPrefsString( context, R.string.key_relay_host );
@ -296,12 +303,19 @@ public class XWPrefs {
public static long getDefaultNewGameGroup( Context context ) public static long getDefaultNewGameGroup( Context context )
{ {
return getPrefsLong( context, R.string.key_default_group, long groupID = getPrefsLong( context, R.string.key_default_group,
DBUtils.ROWID_NOTFOUND ); DBUtils.GROUPID_UNSPEC );
if ( DBUtils.GROUPID_UNSPEC == groupID ) {
groupID = DBUtils.getAnyGroup( context );
setPrefsLong( context, R.string.key_default_group, groupID );
}
Assert.assertTrue( DBUtils.GROUPID_UNSPEC != groupID );
return groupID;
} }
public static void setDefaultNewGameGroup( Context context, long val ) public static void setDefaultNewGameGroup( Context context, long val )
{ {
Assert.assertTrue( DBUtils.GROUPID_UNSPEC != val );
setPrefsLong( context, R.string.key_default_group, val ); setPrefsLong( context, R.string.key_default_group, val );
} }
@ -328,6 +342,32 @@ public class XWPrefs {
return posns; return posns;
} }
public static boolean getThumbEnabled( Context context )
{
return getPrefsBoolean( context, R.string.key_thumb_enabled, false );
}
public static int getThumbScale( Context context )
{
String scale = getPrefsString( context, R.string.key_thumbsize );
int result = -1;
final int[][] data = {
{ R.string.game_thumb_half, 2 }
,{ R.string.game_thumb_third, 3 }
,{ R.string.game_thumb_quarter, 4 }
,{ R.string.game_thumb_fifth, 5 }
,{ R.string.game_thumb_sixth, 6 }
};
for ( int[] datum : data ) {
if ( context.getString(datum[0]).equals(scale) ) {
result = datum[1];
break;
}
}
return result;
}
protected static String getPrefsString( Context context, int keyID ) protected static String getPrefsString( Context context, int keyID )
{ {
String key = context.getString( keyID ); String key = context.getString( keyID );

View file

@ -0,0 +1,70 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2010 - 2011 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.util.AttributeSet;
public class XWSumListPreference extends XWListPreference {
private static final int[] s_ADDROWS = {
R.string.game_summary_field_rowid,
R.string.game_summary_field_gameid,
};
public XWSumListPreference( Context context, AttributeSet attrs )
{
super( context, attrs );
}
// Why I exist: insert the rowid and gameid lines if debug is on
protected void onAttachedToActivity()
{
super.onAttachedToActivity();
if ( BuildConstants.IS_DEBUG_BUILD ||
XWPrefs.getDebugEnabled( m_context ) ) {
CharSequence[] entries = getEntries();
CharSequence lastRow = entries[entries.length - 1];
boolean done = false;
String[] addRows = new String[s_ADDROWS.length];
for ( int ii = 0; !done && ii < s_ADDROWS.length; ++ii ) {
String addRow = m_context.getString( s_ADDROWS[ii] );
done = lastRow.equals( addRow );
addRows[ii] = addRow;
}
if ( !done ) {
CharSequence[] newEntries =
new CharSequence[entries.length + addRows.length];
System.arraycopy( entries, 0, newEntries, 0,
entries.length );
System.arraycopy( addRows, 0, newEntries, entries.length,
addRows.length );
setEntries( newEntries );
setEntryValues( newEntries );
}
}
}
}

View file

@ -24,7 +24,9 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Paint; import android.graphics.Paint;
import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.XWPrefs; import org.eehouse.android.xw4.XWPrefs;
@ -239,7 +241,9 @@ public class CommonPrefs extends XWPrefs {
public static boolean getHideTitleBar( Context context ) public static boolean getHideTitleBar( Context context )
{ {
return getPrefsBoolean( context, R.string.key_hide_title, false ); boolean hideByDefault = 11 > Integer.valueOf( Build.VERSION.SDK );
return getPrefsBoolean( context, R.string.key_hide_title,
hideByDefault );
} }
public static boolean getSoundNotify( Context context ) public static boolean getSoundNotify( Context context )
@ -252,11 +256,6 @@ public class CommonPrefs extends XWPrefs {
return getPrefsBoolean( context, R.string.key_notify_vibrate, false ); return getPrefsBoolean( context, R.string.key_notify_vibrate, false );
} }
public static boolean getHideIntro( Context context )
{
return getPrefsBoolean( context, R.string.key_hide_intro, false );
}
public static boolean getKeepScreenOn( Context context ) public static boolean getKeepScreenOn( Context context )
{ {
return getPrefsBoolean( context, R.string.key_keep_screenon, false ); return getPrefsBoolean( context, R.string.key_keep_screenon, false );

View file

@ -60,11 +60,12 @@ public class CurGameInfo {
public CurGameInfo( Context context ) public CurGameInfo( Context context )
{ {
this( context, false ); this( context, (String)null );
} }
public CurGameInfo( Context context, boolean isNetworked ) public CurGameInfo( Context context, String inviteID )
{ {
boolean isNetworked = null != inviteID;
m_context = context; m_context = context;
nPlayers = 2; nPlayers = 2;
gameSeconds = 60 * nPlayers * gameSeconds = 60 * nPlayers *
@ -81,6 +82,11 @@ public class CurGameInfo {
allowHintRect = false; allowHintRect = false;
m_smartness = 0; // needs to be set from players m_smartness = 0; // needs to be set from players
try {
gameID = (null == inviteID) ? 0 : Integer.parseInt( inviteID, 16 );
} catch ( Exception ex ) {
}
// Always create MAX_NUM_PLAYERS so jni code doesn't ever have // Always create MAX_NUM_PLAYERS so jni code doesn't ever have
// to cons up a LocalPlayer instance. // to cons up a LocalPlayer instance.
for ( int ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) { for ( int ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {

View file

@ -63,10 +63,10 @@ public interface DrawCtx {
void drawBoardArrow ( Rect rect, int bonus, boolean vert, int hintAtts, void drawBoardArrow ( Rect rect, int bonus, boolean vert, int hintAtts,
int flags ); int flags );
boolean trayBegin ( Rect rect, int owner, int score ); boolean trayBegin ( Rect rect, int owner, int score );
void drawTile( Rect rect, String text, int val, int flags ); boolean drawTile( Rect rect, String text, int val, int flags );
void drawTileMidDrag ( Rect rect, String text, int val, int owner, boolean drawTileMidDrag ( Rect rect, String text, int val, int owner,
int flags ); int flags );
void drawTileBack( Rect rect, int flags ); boolean drawTileBack( Rect rect, int flags );
void drawTrayDivider( Rect rect, int flags ); void drawTrayDivider( Rect rect, int flags );
void score_pendingScore( Rect rect, int score, int playerNum, int curTurn, void score_pendingScore( Rect rect, int score, int playerNum, int curTurn,
int flags ); int flags );

View file

@ -21,11 +21,14 @@
package org.eehouse.android.xw4.jni; package org.eehouse.android.xw4.jni;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.text.TextUtils; import android.text.TextUtils;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.DbgUtils;
import org.eehouse.android.xw4.R; import org.eehouse.android.xw4.R;
import org.eehouse.android.xw4.Utils; import org.eehouse.android.xw4.Utils;
import org.eehouse.android.xw4.DbgUtils;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
@ -66,6 +69,7 @@ public class GameSummary {
private CurGameInfo m_gi; private CurGameInfo m_gi;
private Context m_context; private Context m_context;
private String[] m_remotePhones; private String[] m_remotePhones;
private Bitmap m_thumb;
private GameSummary() {} private GameSummary() {}
@ -85,6 +89,16 @@ public class GameSummary {
m_gi = gi; m_gi = gi;
} }
public void setThumbnail( Bitmap thumb )
{
m_thumb = thumb;
}
public Bitmap getThumbnail()
{
return m_thumb;
}
public boolean inNetworkGame() public boolean inNetworkGame()
{ {
return null != relayID; return null != relayID;

View file

@ -22,6 +22,7 @@
package org.eehouse.android.xw4.jni; package org.eehouse.android.xw4.jni;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Handler; import android.os.Handler;
@ -31,21 +32,25 @@ import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
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.BoardDims;
import org.eehouse.android.xw4.ConnStatusHandler;
import org.eehouse.android.xw4.DBUtils;
import org.eehouse.android.xw4.DbgUtils;
import org.eehouse.android.xw4.GameLock; import org.eehouse.android.xw4.GameLock;
import org.eehouse.android.xw4.GameUtils; import org.eehouse.android.xw4.GameUtils;
import org.eehouse.android.xw4.DBUtils; import org.eehouse.android.xw4.R;
import org.eehouse.android.xw4.Toolbar; import org.eehouse.android.xw4.Toolbar;
import org.eehouse.android.xw4.XWPrefs;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.DrawCtx;
import junit.framework.Assert; import junit.framework.Assert;
public class JNIThread extends Thread { public class JNIThread extends Thread {
public enum JNICmd { CMD_NONE, public enum JNICmd { CMD_NONE,
// CMD_RUN,
CMD_DRAW, CMD_DRAW,
CMD_SETDRAW,
CMD_INVALALL, CMD_INVALALL,
CMD_LAYOUT, CMD_LAYOUT,
CMD_START, CMD_START,
@ -91,12 +96,11 @@ public class JNIThread extends Thread {
}; };
public static final int RUNNING = 1; public static final int RUNNING = 1;
public static final int DRAW = 2; public static final int DIALOG = 2;
public static final int DIALOG = 3; public static final int QUERY_ENDGAME = 3;
public static final int QUERY_ENDGAME = 4; public static final int TOOLBAR_STATES = 4;
public static final int TOOLBAR_STATES = 5; public static final int GOT_WORDS = 5;
public static final int GOT_WORDS = 6; public static final int GAME_OVER = 6;
public static final int GAME_OVER = 7;
public class GameStateInfo implements Cloneable { public class GameStateInfo implements Cloneable {
public int visTileCount; public int visTileCount;
@ -108,6 +112,7 @@ public class JNIThread extends Thread {
public boolean canChat; public boolean canChat;
public boolean canShuffle; public boolean canShuffle;
public boolean curTurnSelected; public boolean curTurnSelected;
public boolean canHideRack;
public GameStateInfo clone() { public GameStateInfo clone() {
GameStateInfo obj = null; GameStateInfo obj = null;
try { try {
@ -230,32 +235,25 @@ public class JNIThread extends Thread {
Message.obtain( m_handler, DIALOG, titleArg, 0, text ).sendToTarget(); Message.obtain( m_handler, DIALOG, titleArg, 0, text ).sendToTarget();
} }
private void doLayout( BoardDims dims ) private void doLayout( int width, int height, int fontWidth,
int fontHeight )
{ {
int scoreWidth = dims.width - dims.cellSize; BoardDims dims = new BoardDims();
ConnStatusHandler.setRect( scoreWidth, 0, scoreWidth + dims.cellSize,
dims.scoreHt );
if ( m_gi.timerEnabled ) { boolean squareTiles = XWPrefs.getSquareTiles( m_context );
scoreWidth -= dims.timerWidth; int statusWidth = width / 15;
XwJNI.board_setTimerLoc( m_jniGamePtr, scoreWidth, 0, int scoreWidth = width - statusWidth;
dims.timerWidth, dims.scoreHt ); XwJNI.board_figureLayout( m_jniGamePtr, m_gi, 0, 0, width, height,
} 150, 200, scoreWidth, fontWidth,
XwJNI.board_setScoreboardLoc( m_jniGamePtr, 0, 0, scoreWidth, fontHeight, squareTiles, dims );
dims.scoreHt, true );
// Have no idea why I was doing -1 below, but it breaks layout ConnStatusHandler.setRect( dims.left + scoreWidth, dims.top,
// for small (QVGA) boards. If it needs to be done, do it dims.left + scoreWidth + statusWidth,
// early in figureBoardDims so the calculations that follow dims.top + dims.scoreHt );
// are consistent.
XwJNI.board_setPos( m_jniGamePtr, 0, dims.scoreHt,
dims.width/*-1*/, dims.boardHt, dims.maxCellSize,
false );
XwJNI.board_setTrayLoc( m_jniGamePtr, 0, dims.trayTop, XwJNI.board_applyLayout( m_jniGamePtr, dims );
dims.width/*-1*/, dims.trayHt, kMinDivWidth );
XwJNI.board_invalAll( m_jniGamePtr ); m_drawer.dimsChanged( dims );
} }
private boolean nextSame( JNICmd cmd ) private boolean nextSame( JNICmd cmd )
@ -329,6 +327,11 @@ public class JNIThread extends Thread {
args = elem.m_args; args = elem.m_args;
switch( elem.m_cmd ) { switch( elem.m_cmd ) {
// case CMD_RUN:
// Runnable proc = (Runnable)args[0];
// proc.run();
// break;
case CMD_SAVE: case CMD_SAVE:
if ( nextSame( JNICmd.CMD_SAVE ) ) { if ( nextSame( JNICmd.CMD_SAVE ) ) {
continue; continue;
@ -348,13 +351,26 @@ public class JNIThread extends Thread {
draw = true; draw = true;
break; break;
case CMD_SETDRAW:
XwJNI.board_setDraw( m_jniGamePtr, (DrawCtx)args[0] );
XwJNI.board_invalAll( m_jniGamePtr );
break;
case CMD_INVALALL: case CMD_INVALALL:
XwJNI.board_invalAll( m_jniGamePtr ); XwJNI.board_invalAll( m_jniGamePtr );
draw = true; draw = true;
break; break;
case CMD_LAYOUT: case CMD_LAYOUT:
doLayout( (BoardDims)args[0] ); Object args0 = args[0];
BoardDims dims = null;
if ( args0 instanceof BoardDims ) {
dims = (BoardDims)args0;
XwJNI.board_applyLayout( m_jniGamePtr, dims );
} else {
doLayout( (Integer)args0, (Integer)args[1],
(Integer)args[2], (Integer)args[3] );
}
draw = true; draw = true;
// check and disable zoom button at limit // check and disable zoom button at limit
handle( JNICmd.CMD_ZOOM, 0 ); handle( JNICmd.CMD_ZOOM, 0 );
@ -615,10 +631,6 @@ public class JNIThread extends Thread {
// of the same bitmap for blitting. // of the same bitmap for blitting.
m_drawer.doJNIDraw(); m_drawer.doJNIDraw();
// main UI thread has to invalidate view as it created
// it.
Message.obtain( m_handler, DRAW ).sendToTarget();
checkButtons(); checkButtons();
} }
} // for } // for
@ -640,6 +652,17 @@ public class JNIThread extends Thread {
{ {
QueueElem elem = new QueueElem( cmd, true, args ); QueueElem elem = new QueueElem( cmd, true, args );
m_queue.add( elem ); m_queue.add( elem );
if ( m_stopped && ! JNICmd.CMD_NONE.equals(cmd) ) {
DbgUtils.logf( "WARNING: adding %s to stopped thread!!!",
cmd.toString() );
DbgUtils.printStack();
}
} }
// public void run( boolean isUI, Runnable runnable )
// {
// Object[] args = { runnable };
// QueueElem elem = new QueueElem( JNICmd.CMD_RUN, isUI, args );
// m_queue.add( elem );
// }
} }

Some files were not shown because too many files have changed in this diff Show more