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
libs
proguard.cfg
proguard-project.txt
obj
res/drawable*/*gen.png

View file

@ -22,14 +22,14 @@
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eehouse.android.xw4"
android:versionCode="61"
android:versionCode="64"
android:versionName="@string/app_version"
>
<!-- BE SURE TO MODIFY project.project AND the variable TARGET in
../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"
android:smallScreens="true"
@ -51,6 +51,7 @@
<uses-feature android:name="android.hardware.telephony"
android:required = "false"
/>
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<!-- GCM stuff -->
<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="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.NFC" />
<application android:icon="@drawable/icon48x48"
android:label="@string/app_name"
@ -74,6 +76,13 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</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 android:name="DictsActivity"

View file

@ -51,20 +51,38 @@
<property name="INITIAL_CLIENT_VERS" value="3"/>
<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">
<arg value="clean"/>
</exec>
<exec dir="." executable="../scripts/mkimages.sh"
failonerror="true" output="/dev/null" >
<arg value="--clean"/>
</exec>
</target>
<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="THUMBNAIL_ENABLED" value="true" />
<exec dir="." executable="../scripts/ndkbuild.sh" failonerror="true">
<arg value="BUILD_TARGET=${build.target}" />
<arg value="-j3"/>
<arg value="INITIAL_CLIENT_VERS=${INITIAL_CLIENT_VERS}" />
<arg value="CHAT_ENABLED=${CHAT_ENABLED}" />
<arg value="THUMBNAIL_ENABLED=${THUMBNAIL_ENABLED}" />
</exec>
<exec dir="." executable="../scripts/mkimages.sh"
failonerror="true" output="/dev/null"
/>
<exec dir="." executable="../scripts/gen_gcmid.sh"
output="src/org/eehouse/android/xw4/GCMConsts.java"
logError="true"
@ -74,6 +92,8 @@
<arg value="xw4"/>
<arg value="${INITIAL_CLIENT_VERS}" />
<arg value="${CHAT_ENABLED}" />
<arg value="${THUMBNAIL_ENABLED}" />
<arg value="${build.target}" />
</exec>
</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)
COMMON_PATH=../../../common
local_C_INCLUDES+= \
LOCAL_C_INCLUDES+= \
-I$(LOCAL_PATH)/$(COMMON_PATH) \
-I$(LOCAL_PATH)/../../../relay \
local_LDLIBS += -llog
LOCAL_LDLIBS += -llog
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
local_DEFINES += \
$(local_DEBUG) \
LOCAL_DEFINES += \
$(LOCAL_DEBUG) \
-DXWFEATURE_RELAY \
-DXWFEATURE_SMS \
-DXWFEATURE_COMMSACK \
@ -37,17 +37,18 @@ local_DEFINES += \
-DHASH_STREAM \
-DXWFEATURE_BASE64 \
-DXWFEATURE_DEVID \
-DCOMMON_LAYOUT \
-DINITIAL_CLIENT_VERS=${INITIAL_CLIENT_VERS} \
-DRELAY_ROOM_DEFAULT=\"\" \
-D__LITTLE_ENDIAN \
ifeq ($(CHAT_ENABLED),true)
local_DEFINES += -DXWFEATURE_CHAT
LOCAL_DEFINES += -DXWFEATURE_CHAT
endif
# -DXWFEATURE_SCOREONEPASS \
local_SRC_FILES += \
LOCAL_SRC_FILES += \
xwjni.c \
utilwrapper.c \
drawwrapper.c \
@ -58,7 +59,7 @@ local_SRC_FILES += \
COMMON_PATH=../../../common
common_SRC_FILES += \
COMMON_SRC_FILES += \
$(COMMON_PATH)/boarddrw.c \
$(COMMON_PATH)/scorebdp.c \
$(COMMON_PATH)/dragdrpp.c \
@ -81,9 +82,12 @@ common_SRC_FILES += \
$(COMMON_PATH)/dbgutil.c \
LOCAL_CFLAGS+=$(local_C_INCLUDES) $(local_DEFINES) -Wall
LOCAL_SRC_FILES := $(linux_SRC_FILES) $(local_SRC_FILES) $(common_SRC_FILES)
LOCAL_CFLAGS+=$(LOCAL_C_INCLUDES) $(LOCAL_DEFINES)
LOCAL_SRC_FILES := $(linux_SRC_FILES) $(LOCAL_SRC_FILES) $(COMMON_SRC_FILES)
LOCAL_MODULE := xwjni
LOCAL_LDLIBS := -L${SYSROOT}/usr/lib -llog -lz
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;
}
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
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 );
}
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
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;
}
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
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;
}
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
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);
int result = ints[0];
(*env)->ReleaseIntArrayElements( env, arr, ints, 0);
(*env)->ReleaseIntArrayElements( env, arr, ints, 0 );
if ( del ) {
deleteLocalRef( env, arr );
}
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
makeStringArray( JNIEnv *env, int siz, const XP_UCHAR** vals )
{

View file

@ -33,10 +33,28 @@
void and_send_on_close( XWStreamCtxt* stream, void* closure );
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 );
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 );
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 );
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 );
void getString( JNIEnv* env, jobject jlp, const char* name, XP_UCHAR* buf,
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 );
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 );

View file

@ -423,31 +423,34 @@ and_draw_trayBegin( DrawCtx* dctx, const XP_Rect* rect, XP_U16 owner,
return result;
}
static void
static XP_Bool
and_draw_drawTile( DrawCtx* dctx, const XP_Rect* rect, const XP_UCHAR* text,
const XP_Bitmaps* bitmaps, XP_U16 val, CellFlags flags )
{
XP_Bool result;
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 );
jstring jtext = NULL;
if ( !!text ) {
jtext = (*env)->NewStringUTF( env, text );
}
(*env)->CallVoidMethod( env, draw->jdraw, mid,
jrect, jtext, val, flags );
result = (*env)->CallBooleanMethod( env, draw->jdraw, mid,
jrect, jtext, val, flags );
deleteLocalRef( env, jtext );
return result;
}
static void
static XP_Bool
and_draw_drawTileMidDrag( DrawCtx* dctx, const XP_Rect* rect,
const XP_UCHAR* text, const XP_Bitmaps* bitmaps,
XP_U16 val, XP_U16 owner, CellFlags flags )
{
XP_Bool result;
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 );
jstring jtext = NULL;
@ -455,21 +458,21 @@ and_draw_drawTileMidDrag( DrawCtx* dctx, const XP_Rect* rect,
jtext = (*env)->NewStringUTF( env, text );
}
(*env)->CallVoidMethod( env, draw->jdraw, mid,
jrect, jtext, val, owner, flags );
result = (*env)->CallBooleanMethod( env, draw->jdraw, mid,
jrect, jtext, val, owner, flags );
deleteLocalRef( env, jtext );
return result;
}
static void
static XP_Bool
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 );
(*env)->CallVoidMethod( env, draw->jdraw, mid,
jrect, flags );
return (*env)->CallBooleanMethod( env, draw->jdraw, mid, jrect, flags );
}
static void
@ -644,21 +647,23 @@ makeDraw( MPFORMAL JNIEnv** envp, jobject jdraw )
void
destroyDraw( DrawCtx** dctx )
{
AndDraw* draw = (AndDraw*)*dctx;
JNIEnv* env = *draw->env;
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 );
if ( !!*dctx ) {
AndDraw* draw = (AndDraw*)*dctx;
JNIEnv* env = *draw->env;
if ( NULL != draw->jdraw ) {
(*env)->DeleteGlobalRef( env, draw->jdraw );
}
}
XP_FREE( draw->mpool, draw->vtable );
XP_FREE( draw->mpool, draw );
*dctx = NULL;
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 );
XP_FREE( draw->mpool, draw );
*dctx = NULL;
}
}

View file

@ -39,47 +39,54 @@
#include "jniutlswrapper.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*
makeGI( MPFORMAL JNIEnv* env, jobject j_gi )
makeGI( MPFORMAL JNIEnv* env, jobject jgi )
{
CurGameInfo* gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*gi) );
XP_UCHAR buf[256]; /* in case needs whole path */
gi->nPlayers = getInt( env, j_gi, "nPlayers");
gi->gameSeconds = getInt( env, j_gi, "gameSeconds");
gi->boardSize = getInt( env, j_gi, "boardSize" );
getInts( env, (void*)gi, jgi, gi_ints, VSIZE(gi_ints) );
getBools( env, (void*)gi, jgi, gi_bools, VSIZE(gi_bools) );
/* Unlike on other platforms, gi is created without a call to
game_makeNewGame, which sets gameID. So check here if it's still unset
and if necessary set it -- including back in the java world. */
gi->gameID = getInt( env, j_gi, "gameID" );
if ( 0 == gi->gameID ) {
while ( 0 == gi->gameID ) {
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 =
jenumFieldToInt( env, j_gi, "phoniesAction",
jenumFieldToInt( env, jgi, "phoniesAction",
PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") );
gi->serverRole =
jenumFieldToInt( env, j_gi, "serverRole",
jenumFieldToInt( env, jgi, "serverRole",
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 );
XP_ASSERT( gi->nPlayers <= MAX_NUM_PLAYERS );
jobject jplayers;
if ( getObject( env, j_gi, "players", "[L" PKG_PATH("jni/LocalPlayer") ";",
if ( getObject( env, jgi, "players", "[L" PKG_PATH("jni/LocalPlayer") ";",
&jplayers ) ) {
int ii;
for ( ii = 0; ii < gi->nPlayers; ++ii ) {
@ -114,14 +121,10 @@ static void
setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi )
{
// set fields
setInt( env, jgi, "nPlayers", gi->nPlayers );
setInt( env, jgi, "gameSeconds", gi->gameSeconds );
setInt( env, jgi, "boardSize", gi->boardSize );
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 );
setInts( env, jgi, (void*)gi, gi_ints, VSIZE(gi_ints) );
setBools( env, jgi, (void*)gi, gi_bools, VSIZE(gi_bools) );
setString( env, jgi, "dictName", gi->dictName );
intToJenumField( env, jgi, gi->phoniesAction, "phoniesAction",
@ -155,6 +158,36 @@ setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi )
}
} /* 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
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 );
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->xportProcs = makeXportProcs( MPPARM(mpool) &state->env, j_procs );
CommonPrefs cp;
@ -486,7 +522,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu );
makeDicts( MPPARM(mpool) env, globals->jniutil, &dict, &dicts, jdictNames,
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 );
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
@ -560,6 +598,21 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveSucceeded
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
Java_org_eehouse_android_xw4_jni_XwJNI_board_1invalAll
( JNIEnv *env, jclass C, jint gamePtr )
@ -580,6 +633,45 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1draw
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
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setPos
(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();
}
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
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setScoreboardLoc
( JNIEnv *env, jclass C, jint gamePtr, jint left, jint top,
@ -639,6 +717,46 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1setTrayLoc
minDividerWidth );
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
Java_org_eehouse_android_xw4_jni_XwJNI_board_1handlePenDown
@ -1281,6 +1399,21 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1getGi
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
Java_org_eehouse_android_xw4_jni_XwJNI_game_1getState
( JNIEnv* env, jclass C, jint gamePtr, jobject jgsi )
@ -1289,15 +1422,8 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1getState
GameStateInfo info;
game_getState( &state->game, &info );
setInt( env, jgsi, "visTileCount", info.visTileCount );
setInt( env, jgsi, "trayVisState", info.trayVisState );
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 );
setInts( env, jgsi, (void*)&info, gsi_ints, VSIZE(gsi_ints) );
setBools( env, jgsi, (void*)&info, gsi_bools, VSIZE(gsi_bools) );
XWJNI_END();
}

View file

@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# 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_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"
android:layout_width="fill_parent"
@ -35,29 +12,4 @@
android:layout_weight="1"
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>

View file

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

View file

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

View file

@ -70,7 +70,7 @@
android:title="@string/board_menu_game_left" />
<item android:id="@+id/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" />
<item android:id="@+id/board_menu_game_resend"
android:title="@string/board_menu_game_resend" />

View file

@ -4,16 +4,37 @@
<group android:id="@+id/group_done">
<!-- 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"
android:alphabeticShortcut="D"
android:showAsAction="ifRoom"
android:icon="@drawable/save__gen"
/>
<item android:id="@+id/board_menu_trade"
android:title="@string/board_menu_trade"
android:alphabeticShortcut="T"
android:showAsAction="ifRoom"
/>
</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"
android:title="@string/board_menu_undo_last"
android:alphabeticShortcut="U"
@ -22,8 +43,7 @@
<item android:id="@+id/board_menu_tray"
/>
<item android:id="@+id/board_submenu_game"
android:title="@string/board_submenu_game">
<item android:title="@string/board_submenu_game">
<menu>
<item android:id="@+id/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"?>
<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:icon="@drawable/content_new"
android:icon="@drawable/content_new__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/gamel_menu_newgroup"
<item android:id="@+id/games_menu_newgroup"
android:title="@string/button_new_group"
android:icon="@android:drawable/ic_menu_add"
/>
<item android:id="@+id/gamel_menu_delete"
android:title="@string/list_item_delete"
android:icon="@drawable/ic_action_delete"
android:icon="@drawable/new_group__gen"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/gamel_menu_prefs"
<item android:id="@+id/games_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: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:icon="@android:drawable/ic_menu_info_details"
/>
<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"
<item android:id="@+id/games_menu_checkmoves"
android:title="@string/gamel_menu_checkmoves"
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 -->
<item android:id="@+id/gamel_menu_checkupdates"
<item android:id="@+id/games_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"
/>
<item android:id="@+id/gamel_menu_loaddb"
<item android:id="@+id/games_menu_loaddb"
android:title="@string/gamel_menu_loaddb"
/>
<!-- ic_menu_archive.png -->
</menu>

View file

@ -5,26 +5,24 @@
</style>
</head>
<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
with the relay, in essence switching from each game communicating
individually (and only when open) to the app communicating on behalf
of all games. You should notice only that moves are transmitted
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
them</a> they will not get fixed.
</p>
<p>This release is for the new x86-processor-based Android devices
just coming out. Otherwise identical to beta 71, it adds support for
x86 processors to existing support for ARM (which has been
99.9&#37; of the market until now.)</p>
<p>Thanks to Brendan Le Foll for the patches!</p>
<h3>New with this release</h3>
<ul>
<li>Fix relay registration problem (Thanks DS!)</li>
<li>Show Turn done/Pass in actionbar where possible</li>
<li>Add support for x86 processors.
</ul>
<h3>Next up</h3>
<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>
</ul>

View file

@ -59,9 +59,6 @@
<string name="board_menu_game_resend">Reenvia els missatges</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 -->
<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>

View file

@ -58,9 +58,6 @@
<string name="board_menu_game_resend">Přeposlat zprávy</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 -->
<string name="strd_robot_traded">Robot v tomto tahu vyměnil %d kamenů.</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
############################################################
-->
<!-- 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
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_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
one-line summary for each game in the main screen. The name
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
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 -->
<string name="board_menu_file_about">Sobre Crosswords</string>
@ -175,11 +157,6 @@
list_item_rename) -->
<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
is selected -->
<string name="confirm_reset">Você tem certeza que quer reiniciar
@ -254,9 +231,9 @@
wordlist delete confiration dialog in the case where the
wordlist to be deleted is the last in its language. The name
of the language is substituted for %s. -->
<string name="confirm_deleteonly_dictf">\u0020É a única lista de
palavras em %s instalada. Um ou mais jogos não poderão ser abertos
sem ela.</string>
<!-- <string name="confirm_deleteonly_dictf">\u0020É a única lista de -->
<!-- palavras em %s instalada. Um ou mais jogos não poderão ser abertos -->
<!-- sem ela.</string> -->
<!-- Additional text appended to text confirm_delete_dictf in the
wordlist delete confiration dialog in the case where 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
up when the dicts_item_select menuitem is chosen. The
possible answers are the three button text strings below. -->
<string name="set_default_messagef">Para que jogadores essa lista
de palavras deve ser a padrão para novos jogos? (o idioma %s será
padrão para ambos.)</string>
<!-- <string name="set_default_messagef">Para que jogadores essa lista -->
<!-- de palavras deve ser a padrão para novos jogos? (o idioma %s será -->
<!-- padrão para ambos.)</string> -->
<!-- These three strings are the text for three buttons giving
choices in respose to the dialog launched in response to the
@ -931,14 +908,6 @@
etc. -->
<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
game board window to save space -->
<string name="hide_title">Esconder barra de título</string>
@ -2156,7 +2125,6 @@
lista de palavras?</string>
<string name="button_decline">Recusar</string>
<string name="downloadingf">Baixando %s...</string>
<string name="download_done">Download concluído</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="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
mais de um grupo.</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_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>
</resources>

View file

@ -57,9 +57,6 @@
<string name="board_menu_game_resend">Preposlať správy</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 -->
<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>

View file

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

View file

@ -56,8 +56,10 @@
<string name="key_notify_sound">key_notify_sound</string>
<string name="key_notify_vibrate">key_notify_vibrate</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_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_default_loc">key_default_loc</string>
@ -80,6 +82,8 @@
<string name="key_udp_interval">key_udp_interval</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_relay">key_notagain_relay</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_values">key_na_values</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>
<!-- Nor is my email address -->
@ -109,10 +114,12 @@
<!-- other -->
<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_prefix">/and/</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="dict_url">http://eehouse.org/and_wordlists</string>
@ -126,12 +133,26 @@
<string name="redir_host">Invite redirect host</string>
<string name="dict_host">Wordlist download URL</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="relay_port">Relay game port</string>
<string name="proxy_port">Relay device port</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_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-->
@ -238,7 +259,7 @@
<item>http://dictionary.com/browse/%2$s</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>Larousse.com</item>
@ -279,4 +300,12 @@
<item>@string/confirm_sms_willpay</item>
</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>

View file

@ -13,28 +13,11 @@
# 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
menuitem in main games-list screen's menu. (The botton can
be hidden in the same way as the above text.) -->
<!-- Text of menuitem in main games-list screen's menu -->
<string name="button_new_game">Add game</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
one-line summary for each game in the main screen. The name
of the game is substituted for %1$s. Something else
@ -51,7 +34,7 @@
they aren't available yet because the connection is not
complete. Displayed in the lists of players found in each
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
states it can be in in the process of connecting to the
@ -102,10 +85,10 @@
-->
<!-- 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 -->
<string name="menu_prefs">Settings</string>
<string name="menu_prefs">App Settings</string>
<!-- Regardless of the setting of the connect_frequency
preference, checks the relay immediately for any moves for
@ -115,18 +98,15 @@
<!-- Text of progress indicator shown while check is being conducted -->
<string name="msgs_progress">Checking relay for moves
etc...</string>
etc</string>
<!-- If you choose the above option and have no networked games
you get this error message -->
<string name="no_games_to_refresh">No games found that connect via
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 -->
<string name="board_menu_file_about">About Crosswords</string>
<string name="board_menu_file_about">About Crosswords…</string>
<!--
############################################################
@ -140,11 +120,11 @@
<!-- ############## menu items ############## -->
<!-- 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 -->
<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 -->
<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 -->
<string name="list_item_delete">Delete</string>
@ -174,19 +154,14 @@
list_item_rename) -->
<string name="rename_label">Change the name of this game to:</string>
<!-- Text of confirmation dialog posted when list_item_delete menu is
selected -->
<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>
<string name="confirm_seldeletesf">Are you sure you want to delete
the %d selected game[s]? This action cannot be undone.</string>
<!-- Text of confirmation dialog posted when list_item_reset menu
is selected -->
<string name="confirm_reset">Are you sure you want to reset this
game? Resetting erases all moves and any connection
information.</string>
<string name="confirm_reset">Are you sure you want to reset the
selected game[s]? (Resetting erases all moves and any connection
information.)</string>
<!--
############################################################
@ -202,7 +177,7 @@
<!-- text of button at the bottom of the screen. It launches the
browser pointed at the site where additional wordlists can be
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
installed wordlists to describe those that are part of
@ -250,13 +225,13 @@
wordlist is substituted for %s. Sometimes one of the two
strings below is appended. -->
<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
wordlist delete confiration dialog in the case where the
wordlist to be deleted is the last in its language. The name
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
without it.</string>
<!-- 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
up when the dicts_item_select menuitem is chosen. The
possible answers are the three button text strings below. -->
<string name="set_default_messagef">For what players should this
wordlist be the default for new games? (The language %s will be
the default for both.)</string>
<string name="set_default_messagef">For what players should the
wordlist %1$s be the default for new games? (The language %2$s will be
the default for both.)</string>
<!-- These three strings are the text for three buttons giving
choices in respose to the dialog launched in response to the
@ -309,7 +284,7 @@
############################################################
-->
<!-- 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 -->
<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
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
networked games. The numbers of local and non-local players
@ -527,10 +502,10 @@
<!-- Buttons shown at bottom when board is in exchange mode,
i.e. after user has picked board_menu_trade menu item -->
<string name="button_trade_commit">Commit exchange</string>
<string name="button_trade_cancel">Cancel exchange</string>
<string name="button_trade_commit">Commit trade</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
@ -653,7 +628,7 @@
<!-- This menu begins an exchange of tiles: puts the board into
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
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>
<!-- 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
the language of the game along with how many of each there
are and how many points each is worth. This display is the
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
yet played and not in the rack of the player whose rack is
visible (whose turn it is, generally). This display will
change as tiles are used and depending on what player is
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. -->
<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
is not yet over, gives you a choice whether to end it now,
@ -797,7 +772,7 @@
############################################################
-->
<!-- 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
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") -->
<string name="prefs_dicts">Wordlists</string>
@ -922,14 +897,6 @@
etc. -->
<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
game board window to save space -->
<string name="hide_title">Hide titlebar</string>
@ -1214,12 +1181,17 @@
explanation/guidance. -->
<string name="sms_or_email">Send invitation using SMS (texting) or
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
plaintext and html formatting. These two strings are shown in the
two buttons in the dialog. -->
<string name="button_text">SMS/Text</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
someone to join a game. -->
@ -1431,7 +1403,7 @@
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>
<!-- 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
feature is not yet supported on Android.) -->
@ -1596,7 +1568,7 @@
<!-- Title for generic dialog asking a question, usually in the
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 -->
<string name="not_again_trading">You are entering tile-exchange
mode. Tap tiles to add/remove them from the set to be
exchanged. Use the buttons to commit your turn or exit exchange
mode.</string>
mode.\n\nTap tiles to add/remove them from the set to be
exchanged.\n\n</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 -->
<!-- <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
find out about recent moves. -->
<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
move.</string>
@ -1886,7 +1862,7 @@
<!-- -->
<string name="email_body_revf">(If relevant, please include the
version: \"%s\"; and make/model of your phone or
tablet.)"</string>
tablet.)</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="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>
@ -2024,7 +2000,7 @@
<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
@ -2124,7 +2100,6 @@
wordlist?</string>
<string name="button_decline">Decline</string>
<string name="downloadingf">Downloading %s...</string>
<string name="download_done">Download finished</string>
<string name="download_failed">Download unsuccessful</string>
@ -2145,14 +2120,18 @@
<string name="group_new_games">New games</string>
<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],
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="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>
<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_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="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>

View file

@ -110,18 +110,32 @@
<PreferenceScreen android:title="@string/prefs_appearance"
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:title="@string/summary_field"
android:entries="@array/game_summary_values"
android:entryValues="@array/game_summary_values"
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"
android:title="@string/hide_title"
android:summary="@string/hide_title_summary"
@ -297,11 +311,18 @@
/>
<CheckBoxPreference android:key="@string/key_logging_on"
android:title="@string/logging_on"
android:summary="@string/logging_on_summary"
android:defaultValue="false"
/>
<CheckBoxPreference android:key="@string/key_enable_debug"
android:title="Enable debug features"
android:summary="Menuitems etc."
android:title="@string/debug_features"
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"
/>

View file

@ -1,2 +1,2 @@
GitVersion.java
BuildConstants.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;
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.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.Message;
import android.content.Intent;
import java.util.concurrent.Semaphore;
import java.util.ArrayList;
import java.util.Iterator;
import android.app.Dialog;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.Semaphore;
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.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.JNIThread.*;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.JNIThread.*;
public class BoardActivity extends XWActivity
implements TransportProcs.TPMsgHandler, View.OnClickListener,
DictImportActivity.DownloadFinishedListener,
ConnStatusHandler.ConnStatusCBacks {
ConnStatusHandler.ConnStatusCBacks,
NFCUtils.NFCActor {
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 GAME_OVER = DLG_OKONLY + 14;
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 BT_INVITE_RESULT = 2;
@ -120,6 +128,7 @@ public class BoardActivity extends XWActivity
private int m_jniGamePtr;
private GameLock m_gameLock;
private CurGameInfo m_gi;
private GameSummary m_summary;
private CommsTransport m_xport;
private Handler m_handler = null;
private TimerRunnable[] m_timers;
@ -493,7 +502,7 @@ public class BoardActivity extends XWActivity
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
showEmailOrSMSThen( LAUNCH_INVITE_ACTION );
showInviteChoicesThen( LAUNCH_INVITE_ACTION );
}
};
dialog = new AlertDialog.Builder( this )
@ -505,6 +514,10 @@ public class BoardActivity extends XWActivity
}
break;
case ENABLE_NFC:
dialog = NFCUtils.makeEnableNFCDialog( this );
break;
default:
// just drop it; super.onCreateDialog likely failed
break;
@ -537,11 +550,12 @@ public class BoardActivity extends XWActivity
super.onCreate( savedInstanceState );
getBundledData( savedInstanceState );
if ( CommonPrefs.getHideTitleBar( this ) ) {
if ( CommonPrefs.getHideTitleBar( this )
&& ABUtils.haveMenuKey( this ) ) {
requestWindowFeature( Window.FEATURE_NO_TITLE );
}
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
m_pendingChats = new ArrayList<String>();
}
@ -552,14 +566,8 @@ public class BoardActivity extends XWActivity
// XWTimerReason
m_view = (BoardView)findViewById( R.id.board_view );
m_tradeButtons = findViewById( R.id.exchange_buttons );
m_exchCommmitButton = (Button)findViewById( R.id.exchange_commit );
if ( null != m_exchCommmitButton ) {
m_exchCommmitButton.setOnClickListener( this );
}
m_exchCancelButton = (Button)findViewById( R.id.exchange_cancel );
if ( null != m_exchCancelButton ) {
m_exchCancelButton.setOnClickListener( this );
}
m_exchCommmitButton = setListenerOrHide( R.id.exchange_commit );
m_exchCancelButton = setListenerOrHide( R.id.exchange_cancel );
m_volKeysZoom = XWPrefs.getVolKeysZoom( this );
Intent intent = getIntent();
@ -568,6 +576,8 @@ public class BoardActivity extends XWActivity
m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false );
m_overNotShown = true;
NFCUtils.register( this ); // Don't seem to need to unregister...
setBackgroundColor();
setKeepScreenOn();
} // onCreate
@ -629,7 +639,7 @@ public class BoardActivity extends XWActivity
if ( Activity.RESULT_CANCELED != resultCode ) {
switch ( requestCode ) {
case CHAT_REQUEST:
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
String msg = data.getStringExtra( INTENT_KEY_CHAT );
if ( null != msg && msg.length() > 0 ) {
m_pendingChats.add( msg );
@ -722,8 +732,6 @@ public class BoardActivity extends XWActivity
MenuItem item;
int strId;
updateMenus( menu );
if ( null != m_gsi ) {
inTrade = m_gsi.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.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_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 ) {
boolean enabled = null == m_gsi || m_gsi.curTurnSelected;
@ -754,15 +779,14 @@ public class BoardActivity extends XWActivity
}
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 ) ) {
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 ) {
if ( null != m_gi && DeviceRole.SERVER_STANDALONE == m_gi.serverRole ) {
Utils.setItemVisible( menu, R.id.board_menu_game_resend, false );
Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, false );
}
@ -785,7 +809,7 @@ public class BoardActivity extends XWActivity
showNotAgainDlgThen( R.string.not_again_done,
R.string.key_notagain_done, COMMIT_ACTION );
} else {
dlgButtonClicked( COMMIT_ACTION, AlertDialog.BUTTON_POSITIVE );
dlgButtonClicked( COMMIT_ACTION, AlertDialog.BUTTON_POSITIVE, null );
}
break;
@ -819,8 +843,11 @@ public class BoardActivity extends XWActivity
break;
case R.id.board_menu_trade:
showNotAgainDlgThen( R.string.not_again_trading,
R.string.key_notagain_trading,
String msg = getString( R.string.not_again_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 );
break;
@ -833,7 +860,9 @@ public class BoardActivity extends XWActivity
case R.id.board_menu_undo_last:
showConfirmThen( R.string.confirm_undo_last, UNDO_LAST_ACTION );
break;
case R.id.board_menu_invite:
showDialog( DLG_INVITE );
break;
// small devices only
case R.id.board_menu_dict:
String dictName = m_gi.dictName( m_view.getCurPlayer() );
@ -891,14 +920,20 @@ public class BoardActivity extends XWActivity
// DlgDelegate.DlgClickNotify interface
//////////////////////////////////////////////////
@Override
public void dlgButtonClicked( int id, int which )
public void dlgButtonClicked( int id, int which, Object[] params )
{
if ( LAUNCH_INVITE_ACTION == id ) {
if ( DlgDelegate.DISMISS_BUTTON != which ) {
GameUtils.launchInviteActivity( BoardActivity.this,
DlgDelegate.EMAIL_BTN == which,
m_room, null, m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers );
if ( DlgDelegate.NFC_BTN == which
&& !NFCUtils.nfcAvail( this )[1] ) {
showDialog( ENABLE_NFC );
} 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 ) {
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
//////////////////////////////////////////////////
@ -1285,6 +1338,7 @@ public class BoardActivity extends XWActivity
m_room = room;
m_missing = nMissing;
showDialog( DLG_INVITE );
ABUtils.invalidateOptionsMenuIf( this );
} else {
toastStr = getString( R.string.msg_relay_waiting, devOrder,
room, nMissing );
@ -1302,11 +1356,14 @@ public class BoardActivity extends XWActivity
m_toastStr = toastStr;
if ( naMsg == 0 ) {
dlgButtonClicked( SHOW_EXPL_ACTION,
AlertDialog.BUTTON_POSITIVE );
AlertDialog.BUTTON_POSITIVE, null );
} else {
showNotAgainDlgThen( naMsg, naKey, SHOW_EXPL_ACTION );
}
}
m_missing = nMissing;
ABUtils.invalidateOptionsMenuIf( this );
} // handleConndMessage
private class BoardUtilCtxt extends UtilCtxtImpl {
@ -1727,7 +1784,7 @@ public class BoardActivity extends XWActivity
@Override
public void showChat( final String msg )
{
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
post( new Runnable() {
public void run() {
DBUtils.appendChatHistory( BoardActivity.this,
@ -1769,21 +1826,23 @@ public class BoardActivity extends XWActivity
CommonPrefs cp = CommonPrefs.get( this );
if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
m_gi, dictNames, pairs.m_bytes,
pairs.m_paths, langName, m_utils,
m_jniu, m_view, cp, m_xport ) ) {
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, m_jniu,
m_view, cp, m_xport, dictNames,
pairs.m_bytes, pairs.m_paths,
langName );
m_gi, dictNames,
pairs.m_bytes,
pairs.m_paths, langName,
m_utils, m_jniu,
null, cp, m_xport ) ) {
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils,
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() {
public void handleMessage( Message msg ) {
switch( msg.what ) {
case JNIThread.DRAW:
m_view.invalidate();
break;
case JNIThread.DIALOG:
m_dlgBytes = (String)msg.obj;
m_dlgTitle = msg.arg1;
@ -1799,11 +1858,11 @@ public class BoardActivity extends XWActivity
updateToolbar();
if ( m_inTrade != m_gsi.inTrade ) {
m_inTrade = m_gsi.inTrade;
m_view.setInTrade( m_inTrade );
}
m_view.setInTrade( m_inTrade );
adjustTradeVisibility();
Activity self = BoardActivity.this;
Utils.invalidateOptionsMenuIf( self );
ABUtils.invalidateOptionsMenuIf( self );
}
break;
case JNIThread.GOT_WORDS:
@ -1922,7 +1981,7 @@ public class BoardActivity extends XWActivity
R.string.not_again_undo,
R.string.key_notagain_undo,
UNDO_ACTION );
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
m_toolbar.setListener( Toolbar.BUTTON_CHAT,
R.string.not_again_chat,
R.string.key_notagain_chat,
@ -2011,7 +2070,7 @@ public class BoardActivity extends XWActivity
private void startChatActivity()
{
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
Intent intent = new Intent( this, ChatActivity.class );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_rowid );
startActivityForResult( intent, CHAT_REQUEST );
@ -2035,6 +2094,14 @@ public class BoardActivity extends XWActivity
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 );
m_jniGamePtr = 0;
m_gi = null;
@ -2059,7 +2126,7 @@ public class BoardActivity extends XWActivity
private void trySendChats()
{
if ( GitVersion.CHAT_SUPPORTED && null != m_jniThread ) {
if ( BuildConstants.CHAT_SUPPORTED && null != m_jniThread ) {
Iterator<String> iter = m_pendingChats.iterator();
while ( iter.hasNext() ) {
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_NEXT, m_gsi.canHint );
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,
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()
{
m_toolbar.setVisibility( m_inTrade? View.GONE : View.VISIBLE );
@ -2231,5 +2284,18 @@ public class BoardActivity extends XWActivity
startActivity( intent );
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

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...
public class BoardDims {
public int left, top;
public int width, height; // of the bitmap
public int scoreHt;
public int boardHt;
public int scoreWidth, scoreHt;
public int boardWidth, boardHt;
public int trayTop, trayHt;
public int cellSize, maxCellSize;
public int timerWidth;
@ -37,6 +38,7 @@ public class BoardDims {
// + " left: " + left
// + " top: " + top
// + " scoreHt: " + scoreHt
// + " scoreWidth: " + scoreWidth
// + " boardHt: " + boardHt
// + " trayTop: " + trayTop
// + " trayHt: " + trayHt

View file

@ -40,7 +40,7 @@ public class ChatActivity extends XWActivity implements View.OnClickListener {
@Override
public void onCreate( Bundle savedInstanceState )
{
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
super.onCreate( savedInstanceState );
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_GROUPS = "groups";
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 VISID = "VISID";
@ -59,6 +59,7 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String HASMSGS = "HASMSGS";
public static final String CONTRACTED = "CONTRACTED";
public static final String SNAPSHOT = "SNAPSHOT";
public static final String THUMBNAIL = "THUMBNAIL";
public static final String CONTYPE = "CONTYPE";
public static final String SERVERROLE = "SERVERROLE";
public static final String ROOMNAME = "ROOMNAME";
@ -120,6 +121,7 @@ public class DBHelper extends SQLiteOpenHelper {
,{ CREATE_TIME, "INTEGER" }
,{ LASTPLAY_TIME,"INTEGER" }
,{ SNAPSHOT, "BLOB" }
,{ THUMBNAIL, "BLOB" }
};
private static final String[][] s_obitsColsAndTypes = {
@ -211,6 +213,8 @@ public class DBHelper extends SQLiteOpenHelper {
addSumColumn( db, VISID );
setColumnsEqual( db, TABLE_NAME_SUM, VISID, "rowid" );
makeAutoincrement( db, TABLE_NAME_SUM, s_summaryColsAndTypes );
case 17:
addSumColumn( db, THUMBNAIL );
// nothing yet
break;
default:

View file

@ -25,15 +25,21 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@ -41,7 +47,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.*;
@ -50,6 +55,7 @@ import org.eehouse.android.xw4.DictUtils.DictLoc;
public class DBUtils {
public static final int ROWID_NOTFOUND = -1;
public static final int GROUPID_UNSPEC = -1;
private static final String DICTS_SEP = ",";
@ -118,24 +124,23 @@ public class DBUtils {
{
initDB( context );
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 ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -179,6 +184,18 @@ public class DBUtils {
summary.lastMoveTime =
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 =
cursor.getString( cursor.getColumnIndex(DBHelper.SCORES));
int[] scores = new int[summary.nPlayers];
@ -239,7 +256,7 @@ public class DBUtils {
db.close();
}
if ( null == summary ) {
if ( null == summary && lock.canWrite() ) {
summary = GameUtils.summarize( context, lock );
saveSummary( context, lock, summary );
}
@ -259,6 +276,56 @@ public class DBUtils {
long rowid = lock.getRowid();
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 );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
@ -266,50 +333,6 @@ public class DBUtils {
if ( null == summary ) {
db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
} 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,
values, selection, null );
Assert.assertTrue( result >= 0 );
@ -323,13 +346,13 @@ public class DBUtils {
public static int countGamesUsingLang( Context context, int lang )
{
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 );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
@ -343,16 +366,16 @@ public class DBUtils {
public static int countGamesUsingDict( Context context, String dict )
{
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 );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
result = cursor.getCount();
@ -384,11 +407,11 @@ public class DBUtils {
int dflt )
{
int result = dflt;
String selection = String.format( ROW_ID_FMT, rowid );
String[] columns = { column };
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -417,14 +440,54 @@ public class DBUtils {
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 )
{
String result = null;
String[] columns = { DBHelper.RELAYID };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -440,11 +503,11 @@ public class DBUtils {
public static long[] getRowIDsFor( Context context, String relayID )
{
long[] result = null;
String[] columns = { ROW_ID };
String selection = DBHelper.RELAYID + "='" + relayID + "'";
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { ROW_ID };
String selection = DBHelper.RELAYID + "='" + relayID + "'";
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null );
result = new long[cursor.getCount()];
@ -460,11 +523,11 @@ public class DBUtils {
public static long[] getRowIDsFor( Context context, int gameID )
{
long[] result = null;
String[] columns = { ROW_ID };
String selection = String.format( DBHelper.GAMEID + "=%d", gameID );
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
result = new long[cursor.getCount()];
@ -484,11 +547,11 @@ public class DBUtils {
public static boolean haveGame( Context context, long rowid )
{
boolean result = false;
String[] columns = { ROW_ID };
String selection = String.format( ROW_ID + "=%d", rowid );
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
Assert.assertTrue( 1 >= cursor.getCount() );
@ -554,16 +617,16 @@ public class DBUtils {
NetLaunchInfo nli )
{
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 );
synchronized( s_dbHelper ) {
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,
selection, null, null, null,
DBHelper.CREATE_TIME + " DESC" ); // order by
@ -590,13 +653,13 @@ public class DBUtils {
public static String[] getRelayIDs( Context context, long[][] rowIDs )
{
String[] result = null;
initDB( context );
String[] columns = { ROW_ID, DBHelper.RELAYID };
String selection = DBHelper.RELAYID + " NOT null";
ArrayList<String> ids = new ArrayList<String>();
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
@ -626,14 +689,14 @@ public class DBUtils {
public static void addDeceased( Context context, String relayID,
int seed )
{
ContentValues values = new ContentValues();
values.put( DBHelper.RELAYID, relayID );
values.put( DBHelper.SEED, seed );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put( DBHelper.RELAYID, relayID );
values.put( DBHelper.SEED, seed );
try {
long result = db.replaceOrThrow( DBHelper.TABLE_NAME_OBITS,
"", values );
@ -648,11 +711,11 @@ public class DBUtils {
{
Obit[] result = null;
ArrayList<Obit> al = new ArrayList<Obit>();
String[] columns = { DBHelper.RELAYID, DBHelper.SEED };
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.RELAYID, DBHelper.SEED };
Cursor cursor = db.query( DBHelper.TABLE_NAME_OBITS, columns,
null, null, null, null, null );
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;
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 );
synchronized( s_dbHelper ) {
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 ) );
long rowid = db.insert( DBHelper.TABLE_NAME_SUM, null, values );
db.close();
setCached( rowid, null ); // force reread
@ -740,7 +805,7 @@ public class DBUtils {
updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values );
setCached( rowid, null ); // force reread
if ( -1 != rowid ) { // Means new game?
if ( ROWID_NOTFOUND != rowid ) { // Means new game?
notifyListeners( rowid, false );
}
invalGroupsCache();
@ -750,15 +815,15 @@ public class DBUtils {
public static byte[] loadGame( Context context, GameLock lock )
{
long rowid = lock.getRowid();
Assert.assertTrue( -1 != rowid );
Assert.assertTrue( ROWID_NOTFOUND != rowid );
byte[] result = getCached( rowid );
if ( null == result ) {
String[] columns = { DBHelper.SNAPSHOT };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -790,10 +855,10 @@ public class DBUtils {
public static void deleteGame( Context context, GameLock lock )
{
Assert.assertTrue( lock.canWrite() );
String selection = String.format( ROW_ID_FMT, lock.getRowid() );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection = String.format( ROW_ID_FMT, lock.getRowid() );
db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
db.close();
}
@ -802,13 +867,13 @@ public class DBUtils {
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 );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -826,12 +891,12 @@ public class DBUtils {
public static String getName( Context context, long rowid )
{
String result = null;
String[] columns = { DBHelper.GAME_NAME };
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -855,7 +920,7 @@ public class DBUtils {
public static HistoryPair[] getChatHistory( Context context, long rowid )
{
HistoryPair[] result = null;
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
final String localPrefix = context.getString( R.string.chat_local_id );
String history = getChatHistoryStr( context, rowid );
if ( null != history ) {
@ -892,9 +957,26 @@ public class DBUtils {
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
// that group.
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 ) {
HashMap<Long,GameGroupInfo> result =
@ -902,6 +984,7 @@ public class DBUtils {
initDB( context );
String[] columns = { ROW_ID, DBHelper.GROUPNAME,
DBHelper.EXPANDED };
String limit = 0 == nRows ? null : String.format( "%d", nRows );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_GROUPS, columns,
@ -909,7 +992,8 @@ public class DBUtils {
null, // args
null, // groupBy
null, // having
null //orderby
null, //orderby
limit
);
int idIndex = cursor.getColumnIndex( ROW_ID );
int nameIndex = cursor.getColumnIndex( DBHelper.GROUPNAME );
@ -926,9 +1010,9 @@ public class DBUtils {
Iterator<Long> iter = result.keySet().iterator();
while ( iter.hasNext() ) {
Long id = iter.next();
GameGroupInfo ggi = result.get( id );
readTurnInfo( db, id, ggi );
Long groupID = iter.next();
GameGroupInfo ggi = result.get( groupID );
readTurnInfo( db, groupID, ggi );
}
db.close();
@ -938,13 +1022,31 @@ public class DBUtils {
return s_groupsCache;
} // 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 )
{
String[] columns = { DBHelper.LASTMOVE, DBHelper.GIFLAGS,
DBHelper.TURN };
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,
selection,
null, // args
@ -997,9 +1099,9 @@ public class DBUtils {
initDB( context );
String[] columns = { ROW_ID };
String selection = String.format( "%s=%d", DBHelper.GROUPID, groupID );
String orderBy = DBHelper.CREATE_TIME + " DESC";
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String orderBy = DBHelper.CREATE_TIME + " DESC";
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, // selection
null, // args
@ -1020,39 +1122,21 @@ public class DBUtils {
return result;
}
public static Set<Long> getAllGames( Context context )
{
HashSet<Long> result = null;
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;
}
// pass ROWID_NOTFOUND to get *any* group. Because there may be
// some hidden games stored with group = -1 thanks to
// recently-fixed bugs, be sure to skip them.
public static long getGroupForGame( Context context, long rowid )
{
long result = ROWID_NOTFOUND;
long result = GROUPID_UNSPEC;
initDB( context );
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 ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
@ -1072,15 +1156,27 @@ public class DBUtils {
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 )
{
long rowid = ROWID_NOTFOUND;
long rowid = GROUPID_UNSPEC;
if ( null != name && 0 < name.length() ) {
HashMap<Long,GameGroupInfo> gameInfo = getGroups( context );
if ( null == gameInfo.get( name ) ) {
ContentValues values = new ContentValues();
values.put( DBHelper.GROUPNAME, name );
values.put( DBHelper.EXPANDED, 0 );
values.put( DBHelper.EXPANDED, 1 );
initDB( context );
synchronized( s_dbHelper ) {
@ -1097,17 +1193,17 @@ public class DBUtils {
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 );
synchronized( s_dbHelper ) {
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 );
// And nuke the group record itself
selection = String.format( ROW_ID_FMT, groupid );
db.delete( DBHelper.TABLE_NAME_GROUPS, selection, null );
db.close();
@ -1134,23 +1230,24 @@ public class DBUtils {
}
// Change group id of a game
public static void moveGame( Context context, long gameid, long groupid )
public static void moveGame( Context context, long gameid, long groupID )
{
Assert.assertTrue( GROUPID_UNSPEC != groupID );
ContentValues values = new ContentValues();
values.put( DBHelper.GROUPID, groupid );
values.put( DBHelper.GROUPID, groupID );
updateRow( context, DBHelper.TABLE_NAME_SUM, gameid, values );
}
private static String getChatHistoryStr( Context context, long rowid )
{
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 );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -1169,7 +1266,7 @@ public class DBUtils {
public static void appendChatHistory( Context context, long rowid,
String msg, boolean local )
{
if ( GitVersion.CHAT_SUPPORTED ) {
if ( BuildConstants.CHAT_SUPPORTED ) {
Assert.assertNotNull( msg );
int id = local ? R.string.chat_local_id : R.string.chat_other_id;
msg = context.getString( id ) + msg;
@ -1246,15 +1343,15 @@ public class DBUtils {
{
Assert.assertTrue( DictLoc.UNKNOWN != loc );
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 );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 >= cursor.getCount() && cursor.moveToFirst() ) {
@ -1293,25 +1390,26 @@ public class DBUtils {
DictLoc loc, DictBrowseState state )
{
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 );
synchronized( s_dbHelper ) {
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,
values, selection, null );
if ( 0 == result ) {
@ -1334,12 +1432,12 @@ public class DBUtils {
// Called from jni
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 );
synchronized( s_dbHelper ) {
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,
values, selection, null );
if ( 0 == result ) {
@ -1353,14 +1451,14 @@ public class DBUtils {
public static DictInfo dictsGetInfo( Context context, String name )
{
DictInfo result = null;
String[] columns = { DBHelper.LANGCODE,
DBHelper.WORDCOUNT,
DBHelper.MD5SUM,
DBHelper.LOC };
String selection = String.format( NAME_FMT, DBHelper.DICTNAME, name );
initDB( context );
synchronized( s_dbHelper ) {
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,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -1382,18 +1480,18 @@ public class DBUtils {
public static void dictsSetInfo( Context context, DictUtils.DictAndLoc dal,
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 );
synchronized( s_dbHelper ) {
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,
values, selection, null );
if ( 0 == result ) {
@ -1407,16 +1505,17 @@ public class DBUtils {
public static void dictsMoveInfo( Context context, String name,
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 );
synchronized( s_dbHelper ) {
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_DICTBROWSE, values, selection, null);
db.update( DBHelper.TABLE_NAME_DICTBROWSE, values, selection, null );
db.close();
}
}
@ -1424,12 +1523,13 @@ public class DBUtils {
public static void dictsRemoveInfo( Context context,
DictUtils.DictAndLoc dal )
{
String selection =
String.format( NAMELOC_FMT, DBHelper.DICTNAME,
dal.name, DBHelper.LOC, dal.loc.ordinal() );
initDB( context );
synchronized( s_dbHelper ) {
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_DICTBROWSE, selection, null );
db.close();
@ -1500,12 +1600,12 @@ public class DBUtils {
private static void updateRow( Context context, String table,
long rowid, ContentValues values )
{
String selection = String.format( ROW_ID_FMT, rowid );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
String selection = String.format( ROW_ID_FMT, rowid );
int result = db.update( table, values, selection, null );
db.close();
if ( 0 == result ) {

View file

@ -40,7 +40,7 @@ import java.util.Set;
public class DbgUtils {
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();
@ -52,13 +52,10 @@ public class DbgUtils {
public static void logEnable( Context context )
{
boolean on;
if ( XWApp.DEBUG ) {
if ( XWApp.DEBUG || BuildConstants.IS_DEBUG_BUILD ) {
on = true;
} else {
SharedPreferences sp
= PreferenceManager.getDefaultSharedPreferences( context );
String key = context.getString( R.string.key_logging_on );
on = sp.getBoolean( key, false );
on = XWPrefs.getPrefsBoolean( context, R.string.key_logging_on, false );
}
logEnable( on );
}

View file

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

View file

@ -20,6 +20,7 @@
package org.eehouse.android.xw4;
import android.text.TextUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -33,8 +34,6 @@ import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -43,24 +42,28 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView;
import android.widget.AdapterView;
import android.widget.PopupMenu;
import android.widget.TextView;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import junit.framework.Assert;
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.DictUtils.DictLoc;
public class DictsActivity extends XWExpandableListActivity
implements View.OnClickListener, XWListItem.DeleteCallback,
MountEventReceiver.SDCardNotifiee, DlgDelegate.DlgClickNotify,
implements View.OnClickListener, AdapterView.OnItemLongClickListener,
SelectableItem, MountEventReceiver.SDCardNotifiee,
DlgDelegate.DlgClickNotify,
DictImportActivity.DownloadFinishedListener {
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_LANG_EXTRA = "use_lang";
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;
@ -90,19 +88,14 @@ public class DictsActivity extends XWExpandableListActivity
// settle for a hash on the side.
private static HashMap<MenuItem, DictAndLoc> s_itemData;
private int m_lang = 0;
private String[] m_langs;
private String m_name = null;
private String m_deleteDict = null;
private String m_download;
private ExpandableListView m_expView;
private String[] m_locNames;
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 LayoutInflater m_factory;
@ -144,6 +137,7 @@ public class DictsActivity extends XWExpandableListActivity
if ( null == view ) {
view = (XWListItem)
m_factory.inflate( R.layout.list_item, null );
view.setSelCB( DictsActivity.this );
int lang = (int)getGroupId( groupPosition );
DictAndLoc[] dals = DictLangCache.getDALsHaveLang( m_context,
@ -157,9 +151,6 @@ public class DictsActivity extends XWExpandableListActivity
DictLoc loc = dal.loc;
view.setComment( m_locNames[loc.ordinal()] );
view.cache( loc );
if ( DictLoc.BUILT_IN != loc ) {
view.setDeleteCallback( DictsActivity.this );
}
} else {
view.setText( m_download );
}
@ -237,11 +228,8 @@ public class DictsActivity extends XWExpandableListActivity
protected XWListItem getSelChildView()
{
int groupPosition =
ExpandableListView.getPackedPositionGroup( m_packedPosition );
int childPosition =
ExpandableListView.getPackedPositionChild( m_packedPosition );
return (XWListItem)getChildView( groupPosition, childPosition );
Assert.assertTrue( 1 == m_selDicts.size() );
return m_selDicts.iterator().next();
}
private void addToCache( int group, int child, XWListItem view )
@ -268,45 +256,48 @@ public class DictsActivity extends XWExpandableListActivity
switch( id ) {
case MOVE_DICT:
final XWListItem[] selItems = getSelItems();
final int[] moveTo = { -1 };
message = Utils.format( this, R.string.move_dictf,
m_adapter.getSelChildView().getText() );
getJoinedNames( selItems ) );
OnClickListener newSelLstnr =
new OnClickListener() {
public void onClick( DialogInterface dlgi, int item ) {
m_moveToItm = item;
moveTo[0] = item;
AlertDialog dlg = (AlertDialog)dlgi;
Button btn =
dlg.getButton( AlertDialog.BUTTON_POSITIVE );
btn.setEnabled( m_moveToItm != m_moveFromItem );
btn.setEnabled( true );
}
};
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
XWListItem rowView = m_adapter.getSelChildView();
Assert.assertTrue( m_moveToItm != m_moveFromItem );
DictLoc toLoc = itemToRealLoc( m_moveToItm );
if ( DictUtils.moveDict( DictsActivity.this,
rowView.getText(),
m_moveFromLoc,
toLoc ) ) {
rowView.setComment( m_locNames[toLoc.ordinal()] );
rowView.cache( toLoc );
rowView.invalidate();
DBUtils.dictsMoveInfo( DictsActivity.this,
rowView.getText(),
m_moveFromLoc, toLoc );
} else {
DbgUtils.logf( "moveDict(%s) failed",
rowView.getText() );
DictLoc toLoc = itemToRealLoc( moveTo[0] );
for ( XWListItem selItem : selItems ) {
DictLoc fromLoc = (DictLoc)selItem.getCached();
String name = selItem.getText();
if ( fromLoc == toLoc ) {
DbgUtils.logf( "not moving %s: same loc", name );
} else if ( DictUtils.moveDict( DictsActivity.this,
name, fromLoc,
toLoc ) ) {
selItem.setComment( m_locNames[toLoc.ordinal()] );
selItem.cache( toLoc );
selItem.invalidate();
DBUtils.dictsMoveInfo( DictsActivity.this,
name, fromLoc, toLoc );
} else {
DbgUtils.logf( "moveDict(%s) failed", name );
}
}
}
};
dialog = new AlertDialog.Builder( this )
.setTitle( message )
.setSingleChoiceItems( makeDictDirItems(), m_moveFromItem,
.setSingleChoiceItems( makeDictDirItems(), moveTo[0],
newSelLstnr )
.setPositiveButton( R.string.button_move, lstnr )
.setNegativeButton( R.string.button_cancel, null )
@ -314,22 +305,22 @@ public class DictsActivity extends XWExpandableListActivity
break;
case SET_DEFAULT:
final XWListItem row = m_selDicts.iterator().next();
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
if ( DialogInterface.BUTTON_NEGATIVE == item
|| DialogInterface.BUTTON_POSITIVE == item ) {
setDefault( R.string.key_default_dict );
setDefault( row, R.string.key_default_dict );
}
if ( DialogInterface.BUTTON_NEGATIVE == item
|| DialogInterface.BUTTON_NEUTRAL == item ) {
setDefault( R.string.key_default_robodict );
setDefault( row, R.string.key_default_robodict );
}
}
};
XWListItem rowView = m_adapter.getSelChildView();
String lang =
DictLangCache.getLangName( this, rowView.getText() );
message = getString( R.string.set_default_messagef, lang );
String name = row.getText();
String lang = DictLangCache.getLangName( this, name);
message = getString( R.string.set_default_messagef, name, lang );
dialog = new AlertDialog.Builder( this )
.setTitle( R.string.query_title )
.setMessage( message )
@ -392,7 +383,6 @@ public class DictsActivity extends XWExpandableListActivity
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
getBundledData( savedInstanceState );
m_closedLangs = new HashSet<String>();
String[] closed = XWPrefs.getClosedLangs( this );
@ -411,15 +401,17 @@ public class DictsActivity extends XWExpandableListActivity
setContentView( R.layout.dict_browse );
m_expView = getExpandableListView();
registerForContextMenu( m_expView );
m_expView.setOnItemLongClickListener( this );
Button download = (Button)findViewById( R.id.download );
download.setOnClickListener( this );
if ( ABUtils.haveActionBar() ) {
download.setVisibility( View.GONE );
} else {
download.setOnClickListener( this );
}
mkListAdapter();
// showNotAgainDlg( R.string.not_again_dicts,
// R.string.key_notagain_dicts );
m_selDicts = new HashSet<XWListItem>();
Intent intent = getIntent();
if ( null != intent ) {
@ -436,6 +428,8 @@ public class DictsActivity extends XWExpandableListActivity
downloadNewDict( intent );
}
}
m_origTitle = getTitle();
} // onCreate
@Override
@ -448,35 +442,6 @@ public class DictsActivity extends XWExpandableListActivity
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
protected void onStop() {
MountEventReceiver.unregister( this );
@ -495,65 +460,61 @@ public class DictsActivity extends XWExpandableListActivity
}
@Override
public void onCreateContextMenu( ContextMenu menu, View view,
ContextMenuInfo menuInfo )
{
super.onCreateContextMenu( menu, view, menuInfo );
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 );
public void onBackPressed() {
if ( 0 == m_selDicts.size() ) {
super.onBackPressed();
} else {
clearSelections();
}
}
@Override
public boolean onContextItemSelected( MenuItem item )
{
boolean handled = false;
ExpandableListContextMenuInfo info = null;
try {
info = (ExpandableListContextMenuInfo)item.getMenuInfo();
} catch (ClassCastException cce) {
DbgUtils.loge( cce );
return false;
}
m_packedPosition = info.packedPosition;
int id = item.getItemId();
switch( id ) {
case R.id.dicts_item_move:
askMoveDict( (XWListItem)info.targetView );
@Override
public boolean onCreateOptionsMenu( Menu menu )
{
MenuInflater inflater = getMenuInflater();
inflater.inflate( R.menu.dicts_menu, menu );
return true;
}
@Override
public boolean onPrepareOptionsMenu( Menu menu )
{
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;
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 );
break;
default:
handled = false;
}
return handled;
return handled || super.onOptionsItemSelected( item );
}
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
= PreferenceManager.getDefaultSharedPreferences( this );
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
// to YY. So we need both XX and YY. There may be several
// options for YY?
private void askMoveDict( XWListItem item )
private void askMoveSelDicts()
{
m_moveFromLoc = (DictLoc)item.getCached();
showDialog( MOVE_DICT );
}
// XWListItem.DeleteCallback interface
public void deleteCalled( XWListItem item )
{
String dict = item.getText();
String msg = getString( R.string.confirm_delete_dictf, dict );
// OnItemLongClickListener interface
public boolean onItemLongClick( AdapterView<?> parent, View view,
int position, long id ) {
boolean success = view instanceof SelectableItem.LongClickHandler;
if ( success ) {
((SelectableItem.LongClickHandler)view).longClicked();
}
return success;
}
m_deleteDict = dict;
m_moveFromLoc = (DictLoc)item.getCached();
private boolean selItemsVolatile()
{
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
// 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
// available dict in its language.
if ( 1 < DictLangCache.getDictCount( this, dict ) ) {
// there's another; do nothing
} else {
int fmtid = 0;
int langcode = DictLangCache.getDictLangCode( this, dict );
DictAndLoc[] langDals = DictLangCache.getDALsHaveLang( this,
langcode );
int nUsingLang = DBUtils.countGamesUsingLang( this, langcode );
for ( XWListItem item : items ) {
String dict = item.getText();
if ( 1 < DictLangCache.getDictCount( this, dict ) ) {
// there's another; do nothing
} else {
String newMsg = null;
int langcode = DictLangCache.getDictLangCode( this, dict );
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 ( 0 < nUsingLang ) {
fmtid = R.string.confirm_deleteonly_dictf;
if ( 1 == langDals.length ) { // last in this language?
if ( 0 < nUsingLang ) {
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
public void cardMounted( boolean nowMounted )
@ -644,12 +629,18 @@ public class DictsActivity extends XWExpandableListActivity
}
// DlgDelegate.DlgClickNotify interface
public void dlgButtonClicked( int id, int which )
public void dlgButtonClicked( int id, int which, Object[] params )
{
switch( id ) {
case DELETE_DICT_ACTION:
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;
default:
@ -711,6 +702,49 @@ public class DictsActivity extends XWExpandableListActivity
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()
{
@ -723,9 +757,6 @@ public class DictsActivity extends XWExpandableListActivity
if ( !showDownload && DictLoc.DOWNLOAD == loc ) {
continue;
}
if ( loc.equals( m_moveFromLoc ) ) {
m_moveFromItem = nextI;
}
items[nextI++] = m_locNames[loc.ordinal()];
}
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 {
public void doPopup( final Context context, View button,
String curDict ) {
@ -821,7 +878,9 @@ public class DictsActivity extends XWExpandableListActivity
.setOnMenuItemClickListener( listener );
// 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 );
for ( DictAndLoc dal : dals ) {
@ -857,4 +916,4 @@ public class DictsActivity extends XWExpandableListActivity
context.startActivity( intent );
}
}
}

View file

@ -43,12 +43,13 @@ public class DlgDelegate {
public static final int DIALOG_OKONLY = 2;
public static final int DIALOG_NOTAGAIN = 3;
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 DIALOG_LAST = DLG_DICTGONE;
public static final int SMS_BTN = AlertDialog.BUTTON_POSITIVE;
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 SKIP_CALLBACK = -1;
@ -56,7 +57,7 @@ public class DlgDelegate {
private static final String STATE_KEYF = "STATE_%d";
public interface DlgClickNotify {
void dlgButtonClicked( int id, int button );
void dlgButtonClicked( int id, int button, Object[] params );
}
private Activity m_activity;
@ -118,8 +119,8 @@ public class DlgDelegate {
case CONFIRM_THEN:
dialog = createConfirmThenDialog( state, id );
break;
case TEXT_OR_HTML_THEN:
dialog = createHtmlThenDialog( state, id );
case INVITE_CHOICES_THEN:
dialog = createInviteChoicesDialog( state, id );
break;
case DLG_DICTGONE:
dialog = createDictGoneDialog();
@ -157,24 +158,45 @@ public class DlgDelegate {
}
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 it's set, do the action without bothering with the
// dialog
if ( SKIP_CALLBACK != callbackID ) {
m_clickCallback.dlgButtonClicked( callbackID,
AlertDialog.BUTTON_POSITIVE );
post( new Runnable() {
public void run() {
m_clickCallback
.dlgButtonClicked( callbackID,
AlertDialog.BUTTON_POSITIVE,
params );
}
});
}
} else {
String msg = m_activity.getString( msgID );
DlgState state =
new DlgState( DIALOG_NOTAGAIN, msg, callbackID, prefsKey );
new DlgState( DIALOG_NOTAGAIN, msg, callbackID, prefsKey,
params );
addState( state );
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 )
{
showNotAgainDlgThen( msgID, prefsKey, SKIP_CALLBACK );
@ -182,27 +204,40 @@ public class DlgDelegate {
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 )
{
showConfirmThen( msg, posButton, callbackID, null );
}
public void showConfirmThen( String msg, int posButton, int callbackID,
Object[] params )
{
DlgState state = new DlgState( CONFIRM_THEN, msg, posButton,
callbackID, 0 );
callbackID, 0, params );
addState( state );
m_activity.showDialog( CONFIRM_THEN );
}
public void showEmailOrSMSThen( final int callbackID )
public void showInviteChoicesThen( final int callbackID )
{
if ( Utils.deviceSupportsSMS( m_activity ) ) {
DlgState state = new DlgState( TEXT_OR_HTML_THEN, callbackID );
if ( Utils.deviceSupportsSMS( m_activity )
|| NFCUtils.nfcAvail( m_activity )[0] ) {
DlgState state = new DlgState( INVITE_CHOICES_THEN, callbackID );
addState( state );
m_activity.showDialog( TEXT_OR_HTML_THEN );
m_activity.showDialog( INVITE_CHOICES_THEN );
} else {
post( new Runnable() {
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 );
vers.setText( String.format( m_activity.getString(R.string.about_versf),
m_activity.getString(R.string.app_version),
GitVersion.VERS ) );
BuildConstants.GIT_REV ) );
TextView xlator = (TextView)view.findViewById( R.id.about_xlator );
String str = m_activity.getString( R.string.xlator );
@ -345,7 +380,8 @@ public class DlgDelegate {
if ( SKIP_CALLBACK != state.m_cbckID ) {
m_clickCallback.
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 );
}
private Dialog createHtmlThenDialog( DlgState state, int id )
private Dialog createInviteChoicesDialog( DlgState state, int id )
{
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()
@ -411,7 +464,8 @@ public class DlgDelegate {
public void onClick( DialogInterface dlg, int button ) {
if ( SKIP_CALLBACK != state.m_cbckID ) {
m_clickCallback.dlgButtonClicked( state.m_cbckID,
button );
button,
state.m_params );
}
}
};
@ -428,7 +482,8 @@ public class DlgDelegate {
dropState( state );
if ( SKIP_CALLBACK != state.m_cbckID ) {
m_clickCallback.dlgButtonClicked( state.m_cbckID,
DISMISS_BUTTON );
DISMISS_BUTTON,
state.m_params );
}
m_activity.removeDialog( id );
}

View file

@ -29,6 +29,7 @@ public class DlgState implements Parcelable {
public int m_posButton;
public int m_cbckID = 0;
public int m_prefsKey;
public Object[] m_params;
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 );
}
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,
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_msg = msg;
m_posButton = posButton;
m_cbckID = cbckID;
m_prefsKey = prefsKey;
m_params = params;
}
public DlgState( int id, int cbckID )

View file

@ -26,6 +26,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
@ -47,10 +48,13 @@ public class ExpiringDelegate {
private boolean m_haveTurnLocal = false;
private long m_startSecs;
private Runnable m_runnable = null;
private boolean m_selected;
private Drawable m_origDrawable;
// these can be static as drawing's all in same thread.
private static Rect s_rect;
private static Paint s_paint;
private static float[] s_points;
private static Drawable s_selDrawable;
static {
s_rect = new Rect();
@ -58,12 +62,18 @@ public class ExpiringDelegate {
s_paint.setStyle(Paint.Style.STROKE);
s_paint.setStrokeWidth( 1 );
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_view = view;
m_origDrawable = view.getBackground();
}
public void setHandler( 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 )
{
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 );
m_view.getDrawingRect( s_rect );
int width = s_rect.width();
@ -178,14 +201,14 @@ public class ExpiringDelegate {
m_pct = (int)((100 * passed) / INTERVAL_SECS);
if ( m_pct > 100 ) {
m_pct = 100;
} else {
} else if ( null != m_handler ) {
long onePct = INTERVAL_SECS / 100;
long lastStart = m_startSecs + (onePct * m_pct);
Assert.assertTrue( lastStart <= now );
long nextStartIn = lastStart + onePct - now;
// 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 )
{
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 );
}

View file

@ -28,6 +28,7 @@ import android.widget.TextView;
class ExpiringTextView extends TextView {
private ExpiringDelegate m_delegate = null;
private Context m_context;
protected boolean m_selected = false;
public ExpiringTextView( Context context, AttributeSet attrs )
{
@ -38,9 +39,9 @@ class ExpiringTextView extends TextView {
public void setPct( Handler handler, boolean haveTurn,
boolean haveTurnLocal, long startSecs )
{
if ( null == m_delegate ) {
m_delegate = new ExpiringDelegate( m_context, this, handler );
}
ExpiringDelegate delegate = getDelegate();
delegate.setHandler( handler );
setPct( haveTurn, haveTurnLocal, startSecs );
}
@ -52,6 +53,12 @@ class ExpiringTextView extends TextView {
}
}
protected void toggleSelected()
{
m_selected = !m_selected;
getDelegate().setSelected( m_selected );
}
@Override
protected void onDraw( Canvas canvas )
{
@ -60,4 +67,12 @@ class ExpiringTextView extends TextView {
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
public void dlgButtonClicked( int id, int button )
public void dlgButtonClicked( int id, int button, Object[] params )
{
switch( id ) {
case LOCKED_CHANGE_ACTION:

View file

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

View file

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

View file

@ -91,7 +91,8 @@ public class GameLock {
long sleptTime = 0;
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 ( ; ; ) {

View file

@ -23,9 +23,13 @@ package org.eehouse.android.xw4;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.net.Uri;
import android.text.Html;
import android.text.TextUtils;
import android.view.Display;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Arrays;
@ -46,6 +50,8 @@ public class GameUtils {
public static final String INTENT_KEY_ROWID = "rowid";
public static final String INTENT_FORRESULT_ROWID = "forresult";
private static Integer s_minScreen;
public static class NoSuchGameException extends RuntimeException {
public NoSuchGameException() {
super(); // superfluous
@ -114,7 +120,8 @@ public class GameUtils {
}
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();
} else {
saveGame( context, gamePtr, gi, lockDest, true );
@ -180,10 +187,13 @@ public class GameUtils {
public static GameSummary summarize( Context context, GameLock lock )
{
GameSummary result = null;
CurGameInfo gi = new CurGameInfo( context );
int gamePtr = loadMakeGame( context, gi, lock );
return summarizeAndClose( context, lock, gamePtr, gi );
if ( 0 < gamePtr ) {
result = summarizeAndClose( context, lock, gamePtr, gi );
}
return result;
}
public static long dupeGame( Context context, long rowidIn )
@ -289,6 +299,58 @@ public class GameUtils {
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,
CurGameInfo gi, GameLock lock,
boolean setCreate )
@ -298,10 +360,10 @@ public class GameUtils {
}
public static long saveNewGame( Context context, int gamePtr,
CurGameInfo gi )
CurGameInfo gi, long groupID )
{
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
GameLock lock = DBUtils.saveNewGame( context, stream );
GameLock lock = DBUtils.saveNewGame( context, stream, groupID );
long rowid = lock.getRowid();
lock.unlock();
return rowid;
@ -315,22 +377,33 @@ public class GameUtils {
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;
byte[] bytes = XwJNI.gi_to_stream( gi );
if ( null != bytes ) {
GameLock lock = DBUtils.saveNewGame( context, bytes );
GameLock lock = DBUtils.saveNewGame( context, bytes, groupID );
rowid = lock.getRowid();
lock.unlock();
}
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 nPlayersT, int nPlayersH,
String inviteID, int gameID,
@ -338,7 +411,8 @@ public class GameUtils {
{
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] );
lang[0] = gi.dictLang;
dict[0] = gi.dictName;
@ -353,7 +427,7 @@ public class GameUtils {
// Will need to add a setNPlayers() method to gi to make this
// work
Assert.assertTrue( gi.nPlayers == nPlayersT );
rowid = saveNew( context, gi );
rowid = saveNew( context, gi, groupID );
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
GameLock lock = new GameLock( rowid, true ).lock();
@ -364,8 +438,8 @@ public class GameUtils {
return rowid;
}
public static long makeNewNetGame( Context context, String room,
String inviteID, int[] lang,
public static long makeNewNetGame( Context context, long groupID,
String room, String inviteID, int[] lang,
String[] dict, int nPlayersT,
int nPlayersH )
{
@ -375,29 +449,38 @@ public class GameUtils {
CommsAddrRec addr = new CommsAddrRec( relayName, relayPort );
addr.ip_relay_invite = room;
return makeNewMultiGame( context, addr, lang, dict, nPlayersT,
nPlayersH, inviteID, 0, false );
return makeNewMultiGame( context, groupID, addr, lang, dict,
nPlayersT, nPlayersH, inviteID, 0, false );
}
public static long makeNewNetGame( Context context, String room,
String inviteID, int lang, String dict,
int nPlayers )
public static long makeNewNetGame( Context context, long groupID,
String room, String inviteID, int lang,
String dict, int nPlayers )
{
int[] langarr = { lang };
String[] dictArr = { dict };
return makeNewNetGame( context, room, inviteID, langarr, dictArr,
nPlayers, 1 );
return makeNewNetGame( context, groupID, room, inviteID, langarr,
dictArr, nPlayers, 1 );
}
public static long makeNewNetGame( Context context, NetLaunchInfo info )
{
return makeNewNetGame( context, info.room, info.inviteID, info.lang,
info.dict, info.nPlayersT );
return makeNewNetGame( context, DBUtils.GROUPID_UNSPEC, info.room,
info.inviteID, info.lang, info.dict,
info.nPlayersT );
}
public static long makeNewBTGame( Context context, int gameID,
CommsAddrRec addr, int lang,
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;
int[] langa = { lang };
@ -405,13 +488,22 @@ public class GameUtils {
if ( isHost ) {
addr = new CommsAddrRec( null, null );
}
return makeNewMultiGame( context, addr, langa, null, nPlayersT,
nPlayersH, null, gameID, isHost );
return makeNewMultiGame( context, groupID, addr, langa, null,
nPlayersT, nPlayersH, null, gameID, isHost );
}
public static long makeNewSMSGame( Context context, int gameID,
CommsAddrRec addr, int lang,
String dict, int nPlayersT,
CommsAddrRec addr,
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 )
{
long rowid = -1;
@ -421,64 +513,71 @@ public class GameUtils {
if ( isHost ) {
addr = new CommsAddrRec(CommsAddrRec.CommsConnType.COMMS_CONN_SMS);
}
return makeNewMultiGame( context, addr, langa, dicta, nPlayersT,
nPlayersH, null, gameID, isHost );
String inviteID = GameUtils.formatGameID( gameID );
return makeNewMultiGame( context, groupID, addr, langa, dicta,
nPlayersT, nPlayersH, inviteID, gameID,
isHost );
}
public static void launchInviteActivity( Context context,
boolean choseEmail,
public static void launchInviteActivity( Activity activity, int chosen,
String room, String inviteID,
int lang, String dict,
int nPlayers )
{
if ( null == inviteID ) {
inviteID = makeRandomID();
}
Uri gameUri = NetLaunchInfo.makeLaunchUri( context, room, inviteID,
lang, dict, nPlayers );
Assert.assertNotNull( inviteID );
if ( null != gameUri ) {
int fmtId = choseEmail? R.string.invite_htmf : R.string.invite_txtf;
int choiceID;
String message = context.getString( fmtId, gameUri.toString() );
if ( DlgDelegate.NFC_BTN == chosen ) {
Utils.showToast( activity, R.string.sms_ready_text );
} else {
Uri gameUri = NetLaunchInfo.makeLaunchUri( activity, room, inviteID,
lang, dict, nPlayers );
String msgString = null == gameUri ? null : gameUri.toString();
Intent intent = new Intent();
if ( choseEmail ) {
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) );
if ( null != msgString ) {
boolean choseEmail = DlgDelegate.EMAIL_BTN == chosen;
File attach = null;
File tmpdir = XWApp.ATTACH_SUPPORTED ?
DictUtils.getDownloadDir( context ) : null;
if ( null != tmpdir ) { // no attachment
attach = makeJsonFor( tmpdir, room, inviteID, lang,
dict, nPlayers );
}
int fmtId = choseEmail? R.string.invite_htmf : R.string.invite_txtf;
int choiceID;
String message = activity.getString( fmtId, msgString );
if ( null == attach ) { // no attachment
intent.setType( "message/rfc822");
Intent intent = new Intent();
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 {
String mime = context.getString( R.string.invite_mime );
intent.setType( mime );
Uri uri = Uri.fromFile( attach );
intent.putExtra( Intent.EXTRA_STREAM, uri );
intent.setAction( Intent.ACTION_VIEW );
intent.setType( "vnd.android-dir/mms-sms" );
intent.putExtra( "sms_body", message );
choiceID = R.string.invite_chooser_sms;
}
choiceID = R.string.invite_chooser_email;
} else {
intent.setAction( Intent.ACTION_VIEW );
intent.setType( "vnd.android-dir/mms-sms" );
intent.putExtra( "sms_body", message );
choiceID = R.string.invite_chooser_sms;
String choiceType = activity.getString( choiceID );
String chooserMsg =
Utils.format( activity, R.string.invite_chooserf, choiceType );
activity.startActivity( Intent.createChooser( intent, chooserMsg ) );
}
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 );
}
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()
{
int rint = newGameID();
return String.format( "%X", rint ).substring( 0, 4 );
return formatGameID( rint );
}
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 );
}
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 )
{
lang = bundle.getInt( LANG );
@ -134,6 +149,25 @@ public class NetLaunchInfo {
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()
{
return m_valid;

View file

@ -1,6 +1,6 @@
/* -*- 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.
*
* 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_GAMEID = "GAMEID";
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_SMS = 2;
private static final int INVITE_FOR_BT = 3;
@ -58,7 +59,8 @@ public class NewGameActivity extends XWActivity {
// Dialogs
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_nameForBT;
private boolean m_firingPrefs = false;
@ -68,6 +70,7 @@ public class NewGameActivity extends XWActivity {
private long m_newRowID = -1;
private String m_gameName;
private int m_gameID;
private long m_groupID;
private String m_remoteDev;
@Override
@ -76,6 +79,8 @@ public class NewGameActivity extends XWActivity {
super.onCreate( savedInstanceState );
getBundledData( savedInstanceState );
m_groupID = getIntent().getLongExtra( GROUPID_EXTRA, -1 );
setContentView( R.layout.new_game );
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() {
@Override
public void onClick( View v ) {
@ -149,12 +154,12 @@ public class NewGameActivity extends XWActivity {
// DlgDelegate.DlgClickNotify interface
@Override
public void dlgButtonClicked( int id, int which )
public void dlgButtonClicked( int id, int which, Object[] params )
{
switch( id ) {
case NEW_GAME_ACTION:
if ( DlgDelegate.DISMISS_BUTTON != which ) {
makeNewGame( true, true, DlgDelegate.EMAIL_BTN == which );
makeNewGame( true, true, which );
}
break;
default:
@ -232,8 +237,8 @@ public class NewGameActivity extends XWActivity {
m_gameID, m_gameName,
m_lang, m_dict, 2, 1 );
long rowid = GameUtils.
makeNewSMSGame( thiz, m_gameID, null,
m_lang, m_dict, 2, 1 );
makeNewSMSGame( thiz, m_groupID, m_gameID,
null, m_lang, m_dict, 2, 1 );
DBUtils.setName( thiz, rowid, m_gameName );
GameUtils.launchGame( thiz, rowid, true );
finish();
@ -250,6 +255,9 @@ public class NewGameActivity extends XWActivity {
.create();
Utils.setRemoveOnDismiss( this, dialog, id );
break;
case ENABLE_NFC:
dialog = NFCUtils.makeEnableNFCDialog( this );
break;
}
}
@ -285,8 +293,8 @@ public class NewGameActivity extends XWActivity {
public void run() {
long rowid =
GameUtils.makeNewBTGame( NewGameActivity.this,
gameID, null, m_lang,
2, 1 );
m_groupID, gameID, null,
m_lang, 2, 1 );
DBUtils.setName( NewGameActivity.this,
rowid, m_gameName );
GameUtils.launchGame( NewGameActivity.this,
@ -305,59 +313,67 @@ public class NewGameActivity extends XWActivity {
{
if ( launch && networked ) {
// Let 'em cancel before we make the game
showEmailOrSMSThen( NEW_GAME_ACTION );
showInviteChoicesThen( NEW_GAME_ACTION );
} else {
makeNewGame( networked, launch, false );
makeNewGame( networked, launch, DlgDelegate.SMS_BTN );
}
}
private void makeNewGame( boolean networked, boolean launch,
boolean choseEmail )
int chosen )
{
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 ( networked ) {
room = GameUtils.makeRandomID();
inviteID = GameUtils.makeRandomID();
rowid = GameUtils.makeNewNetGame( this, room, inviteID, lang,
dict, nPlayers, 1 );
if ( DlgDelegate.NFC_BTN == chosen
&& !NFCUtils.nfcAvail( this )[1] ) {
showDialog( ENABLE_NFC );
} 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 ) {
GameUtils.launchInviteActivity( this, choseEmail, room,
inviteID, lang[0], dict[0],
nPlayers );
room = GameUtils.makeRandomID();
inviteID = GameUtils.makeRandomID();
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 )
{
int gameID = GameUtils.newGameID();
if ( !useDefaults ) {
m_newRowID = GameUtils.makeNewBTGame( NewGameActivity.this,
gameID, null, m_lang,
2, 1 ); // initial defaults
Intent intent = new Intent( this, GameConfig.class );
intent.setAction( Intent.ACTION_EDIT );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_newRowID );
intent.putExtra( GameUtils.INTENT_FORRESULT_ROWID, true );
startActivityForResult( intent, CONFIG_FOR_BT );
} else {
BTInviteActivity.launchForResult( this, 1, INVITE_FOR_BT );
if ( XWApp.BTSUPPORTED ) {
int gameID = GameUtils.newGameID();
if ( !useDefaults ) {
m_newRowID = GameUtils.makeNewBTGame( NewGameActivity.this,
m_groupID, gameID, null,
m_lang, 2, 1 );
Intent intent = new Intent( this, GameConfig.class );
intent.setAction( Intent.ACTION_EDIT );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_newRowID );
intent.putExtra( GameUtils.INTENT_FORRESULT_ROWID, true );
startActivityForResult( intent, CONFIG_FOR_BT );
} else {
BTInviteActivity.launchForResult( this, 1, INVITE_FOR_BT );
}
}
}
@ -366,8 +382,8 @@ public class NewGameActivity extends XWActivity {
int gameID = GameUtils.newGameID();
if ( !useDefaults ) {
m_newRowID = GameUtils.makeNewSMSGame( NewGameActivity.this,
gameID, null, m_lang,
m_dict, 2, 1 );
m_groupID, gameID, null,
m_lang, m_dict, 2, 1 );
String name = Utils.format( this, R.string.dft_sms_namef,
gameID & 0xFFFF );
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;
import android.app.AlertDialog;
import android.app.Dialog;
import android.preference.PreferenceActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.view.View;
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_ALL = 2;
public static final int CONFIRM_SMS = 3;
public static final int EXPLAIN_TITLE = 4;
private String m_keyLogging;
private String m_smsToasting;
private String m_smsEnable;
private String m_downloadPath;
private String m_thumbEnabled;
private String m_thumbSize;
private String m_hideTitle;
@Override
protected Dialog onCreateDialog( int id )
@ -97,6 +104,13 @@ public class PrefsActivity extends PreferenceActivity
case CONFIRM_SMS:
dialog = SMSCheckBoxPreference.onCreateDialog( this, id );
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 ) {
@ -123,6 +137,9 @@ public class PrefsActivity extends PreferenceActivity
m_smsToasting = getString( R.string.key_show_sms );
m_smsEnable = getString( R.string.key_enable_sms );
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.setOnClickListener( new View.OnClickListener() {
@ -185,7 +202,18 @@ public class PrefsActivity extends PreferenceActivity
}
}
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 );
}
}
}
private void relaunch()

View file

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

View file

@ -190,7 +190,7 @@ public class SMSInviteActivity extends InviteActivity {
// DlgDelegate.DlgClickNotify interface
@Override
public void dlgButtonClicked( int id, int which )
public void dlgButtonClicked( int id, int which, Object[] params )
{
switch( which ) {
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 );
if ( null != button ) {
@ -126,8 +127,8 @@ public class Toolbar {
setListener( index, listener );
}
public void setLongClickListener( int index, final int msgID, final int prefsKey,
final int callback )
public void setLongClickListener( int index, final int msgID,
final int prefsKey, final int callback )
{
View.OnLongClickListener listener = new View.OnLongClickListener() {
public boolean onLongClick( View view ) {

View file

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

View file

@ -28,7 +28,6 @@ import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences;
@ -72,24 +71,6 @@ public class Utils {
private static Boolean s_hasSmallScreen = null;
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() {}
public static int nextRandomInt()
@ -172,7 +153,7 @@ public class Utils {
String[] addrs = { context.getString( R.string.email_author_email ) };
intent.putExtra( Intent.EXTRA_EMAIL, addrs );
String body = format( context, R.string.email_body_revf,
GitVersion.VERS );
BuildConstants.GIT_REV );
intent.putExtra( Intent.EXTRA_TEXT, body );
String chooserMsg = context.getString( R.string.email_author_chooser );
context.startActivity( Intent.createChooser( intent, chooserMsg ) );
@ -376,13 +357,6 @@ public class Utils {
item.setEnabled( enabled );
}
public static void invalidateOptionsMenuIf( Activity activity )
{
if ( null != s_safeInval ) {
s_safeInval.doInval( activity );
}
}
public static boolean hasSmallScreen( Context context )
{
if ( null == s_hasSmallScreen ) {

View file

@ -106,6 +106,12 @@ public class XWActivity extends Activity
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,
int action )
{
@ -147,9 +153,9 @@ public class XWActivity extends Activity
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()
@ -178,7 +184,7 @@ public class XWActivity extends Activity
}
// DlgDelegate.DlgClickNotify interface
public void dlgButtonClicked( int id, int which )
public void dlgButtonClicked( int id, int which, Object[] params )
{
Assert.fail();
}

View file

@ -43,7 +43,7 @@ public class XWApp extends Application {
public static final String SMS_PUBLIC_HEADER = "-XW4";
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 Boolean s_onEmulator = null;

View file

@ -84,6 +84,12 @@ public class XWExpandableListActivity extends ExpandableListActivity
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,
int action )
{
@ -111,23 +117,25 @@ public class XWExpandableListActivity extends ExpandableListActivity
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
public void dlgButtonClicked( int id, int which )
public void dlgButtonClicked( int id, int which, Object[] params )
{
Assert.fail();
}

View file

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

View file

@ -20,19 +20,25 @@
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.util.AttributeSet;
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 Context m_context;
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 {
void deleteCalled( XWListItem item );
@ -69,17 +75,29 @@ public class XWListItem extends LinearLayout {
public void setDeleteCallback( DeleteCallback cb )
{
m_cb = cb;
m_delCb = cb;
ImageButton button = (ImageButton)findViewById( R.id.del );
button.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View view ) {
m_cb.deleteCalled( XWListItem.this );
m_delCb.deleteCalled( XWListItem.this );
}
} );
button.setVisibility( View.VISIBLE );
}
public void setSelCB( SelectableItem selCB )
{
m_selCb = selCB;
}
public void setSelected( boolean selected )
{
if ( selected != m_selected ) {
toggleSelected();
}
}
@Override
public void setEnabled( boolean enabled )
{
@ -105,4 +123,21 @@ public class XWListItem extends LinearLayout {
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;
public class XWListPreference extends ListPreference {
protected Context m_context;
public XWListPreference( Context context, AttributeSet attrs )
{
super( context, attrs );
m_context = context;
}
protected void onAttachedToActivity()

View file

@ -27,6 +27,8 @@ import android.text.TextUtils;
import com.google.android.gcm.GCMRegistrar;
import java.util.ArrayList;
import junit.framework.Assert;
public class XWPrefs {
public static boolean getSMSEnabled( Context context )
@ -39,6 +41,11 @@ public class XWPrefs {
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 )
{
return getPrefsString( context, R.string.key_relay_host );
@ -296,12 +303,19 @@ public class XWPrefs {
public static long getDefaultNewGameGroup( Context context )
{
return getPrefsLong( context, R.string.key_default_group,
DBUtils.ROWID_NOTFOUND );
long groupID = getPrefsLong( context, R.string.key_default_group,
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 )
{
Assert.assertTrue( DBUtils.GROUPID_UNSPEC != val );
setPrefsLong( context, R.string.key_default_group, val );
}
@ -328,6 +342,32 @@ public class XWPrefs {
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 )
{
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.res.Resources;
import android.graphics.Paint;
import android.os.Build;
import android.preference.PreferenceManager;
import junit.framework.Assert;
import org.eehouse.android.xw4.XWPrefs;
@ -239,7 +241,9 @@ public class CommonPrefs extends XWPrefs {
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 )
@ -252,11 +256,6 @@ public class CommonPrefs extends XWPrefs {
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 )
{
return getPrefsBoolean( context, R.string.key_keep_screenon, false );

View file

@ -60,11 +60,12 @@ public class CurGameInfo {
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;
nPlayers = 2;
gameSeconds = 60 * nPlayers *
@ -81,6 +82,11 @@ public class CurGameInfo {
allowHintRect = false;
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
// to cons up a LocalPlayer instance.
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,
int flags );
boolean trayBegin ( Rect rect, int owner, int score );
void drawTile( Rect rect, String text, int val, int flags );
void drawTileMidDrag ( Rect rect, String text, int val, int owner,
int flags );
void drawTileBack( Rect rect, int flags );
boolean drawTile( Rect rect, String text, int val, int flags );
boolean drawTileMidDrag ( Rect rect, String text, int val, int owner,
int flags );
boolean drawTileBack( Rect rect, int flags );
void drawTrayDivider( Rect rect, int flags );
void score_pendingScore( Rect rect, int score, int playerNum, int curTurn,
int flags );

View file

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

View file

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