Merge remote-tracking branch 'origin/android_translate' into android_translate

This commit is contained in:
Weblate 2016-05-28 08:43:40 +02:00
commit b14c6b5003
112 changed files with 3559 additions and 2192 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ core*
*.apk
xwords_4.4.0.0*
gcm_loop.shelf
nohup.out

View file

@ -1,20 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- -*- compile-command: "ant install"; -*- -->
<!-- Copyright (C) 2007 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Declare the contents of this Android application. The namespace
attribute brings in the Android platform namespace, and the package
supplies a unique name for the application. When writing your
@ -22,14 +6,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.xw4dbg"
android:versionCode="99"
android:versionCode="100"
android:versionName="@string/app_version"
>
<!-- BE SURE TO MODIFY project.project AND the variable TARGET in
<!-- BE SURE TO MODIFY project.properties AND the variable TARGET in
../scripts/setup_local_props.sh if targetSdkVersion changes!!!
-->
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
<supports-screens android:resizeable="true"
android:smallScreens="true"
@ -73,7 +57,7 @@
android:debuggable="true"
>
<activity android:name="GamesListActivity"
<activity android:name="Main"
android:label="@string/app_name"
android:launchMode="standard"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -88,25 +72,31 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="@string/xwords_nfc_mime" />
</intent-filter>
</activity>
<activity android:name="FragActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="GamesListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="DictsActivity"
android:label="@string/title_dicts_list"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="BTInviteActivity"
android:label="@string/bt_invite_title"
android:label="@string/bt_invite_title"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="SMSInviteActivity"
android:label="@string/sms_invite_title"
android:label="@string/sms_invite_title"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="sensor"
/>
<activity android:name="RelayInviteActivity"
android:label="@string/relay_invite_title"
android:label="@string/relay_invite_title"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
@ -224,10 +214,10 @@
<receiver android:name="BTReceiver">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
</intent-filter>
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
</intent-filter>
</receiver>

View file

@ -1,3 +1,4 @@
gcm.jar
armeabi
x86
android-support-v13.jar

View file

@ -8,4 +8,4 @@
# project structure.
# Project target.
target=android-14
target=android-19

View file

@ -27,3 +27,4 @@ in_arrow_active.png
in_arrow.png
out_arrow_active.png
out_arrow.png
notify.png

View file

@ -44,3 +44,6 @@ not_again_view.xml
relayinviter.xml
remote_dict_details.xml
toolbar.xml
chat_row.xml
fragact.xml
main.xml

View file

@ -125,3 +125,13 @@ XWConnAddrPreference.java
XWDevIDPreference.java
XWExpListAdapter.java
RequestCode.java
BoardFrag.java
ChatFrag.java
DictBrowseFrag.java
FragActivity.java
GameConfigFrag.java
GamesListFrag.java
Main.java
nohup.out
StudyListFrag.java
XWFragment.java

View file

@ -13,7 +13,8 @@
ant_out.txt
bin
gen
local.properties
obj
proguard.cfg
res/drawable*/*gen.png
.idea/
*.iml

View file

@ -1 +0,0 @@
XWords4

View file

@ -1,229 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<Objective-C-extensions>
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
<option name="RELEASE_STYLE" value="IVAR" />
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" />
<pair source="c" header="h" />
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</value>
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
</component>
</project>

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View file

@ -1,3 +0,0 @@
<component name="CopyrightManager">
<settings default="" />
</component>

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="1.7" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View file

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.7</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/XWords4.iml" filepath="$PROJECT_DIR$/XWords4.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
</component>
</project>

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="99"
android:versionCode="101"
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="14" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
<supports-screens android:resizeable="true"
android:smallScreens="true"
@ -69,7 +69,7 @@
android:name=".XWApp"
>
<activity android:name="GamesListActivity"
<activity android:name="Main"
android:label="@string/app_name"
android:launchMode="standard"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -84,9 +84,15 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="@string/xwords_nfc_mime" />
</intent-filter>
</activity>
<activity android:name="FragActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="GamesListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="DictsActivity"
android:label="@string/title_dicts_list"
android:configChanges="keyboardHidden|orientation|screenSize"

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="XWords4" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="XWords4" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
<afterSyncTasks>
<task>generateDebugAndroidTestSources</task>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/debug" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/tmp" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 14 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="gcm" level="project" />
</component>
</module>

View file

@ -12,6 +12,7 @@ apply plugin: 'com.android.application'
dependencies {
compile files('../libs/gcm.jar')
compile files('../libs/android-support-v13.jar')
}
android {
@ -19,7 +20,7 @@ android {
buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 7
targetSdkVersion 14
targetSdkVersion 19
}
// Rename all output artifacts to include version information
@ -103,6 +104,27 @@ android {
lintOptions {
abortOnError false
}
def gitrev = "git describe --tags".execute().text
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile =
new File(output.outputFile.parent,
output.outputFile.name.replace(".apk", "-${gitrev}.apk"))
}
}
}
// Prevent release builds. They haven't been tested, and now that
// f-droid has been releasing them they're KNOWN not to work.
android.applicationVariants.all { variant ->
if ( variant.name.endsWith("Release") ) {
String NAME = "check" + variant.name.capitalize() + "Manifest"
task "$NAME"(overwrite: true) << {
throw new RuntimeException('<<<<< Release builds should not be built '
+ 'using gradle! Please use ant for now. >>>>>');
}
}
}
dependencies {

File diff suppressed because it is too large Load diff

View file

@ -13,10 +13,10 @@
</style>
</head>
<body>
<h2>Crosswords 4.4.105 release</h2>
<h2>Crosswords 4.4.107 release</h2>
<p>This release fixes a problem with rematches not preserving game
settings like "Allow hints."</p>
<p>This version, for F-droid release only, fixes build problems that
caused a number of crashes and other issues.</p>
<div id="survey">
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
@ -26,11 +26,7 @@
<h3>New with this release</h3>
<ul>
<li>When creating a game to rematch, preserve the original's
settings for hints, board size, timer and phonies</li>
<li>Fix tiles left of tray divider sometimes being included in hints</li>
<li>Unless "View tiles out-of-turn" is enabled, show summary of
last move when scoreboard entry is tapped</li>
<li>Force release builds to be done with ant rather than gradle</li>
</ul>
<p>(The full changelog
@ -38,6 +34,8 @@
<h3>Next up</h3>
<ul>
<li>More chat improvements, especially allowing to send and
receive without closing the chat window</li>
<li>Take advantage of Marshmallow's new permissions model (where
the app only asks for them when it needs them.)
</ul>

View file

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.android.tools.build:gradle:2.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -26,7 +26,7 @@ subprojects {
afterEvaluate {project ->
if (project.hasProperty("android")) {
android {
compileSdkVersion 14
compileSdkVersion 19
buildToolsVersion '22.0.1'
}
}

View file

@ -1,6 +1,6 @@
#Mon Nov 09 19:22:17 PST 2015
#Thu Apr 07 12:32:10 PDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip

View file

@ -250,12 +250,13 @@ and_util_turnChanged( XW_UtilCtxt* uc, XP_S16 turn )
#endif
static void
and_util_informMove( XW_UtilCtxt* uc, XWStreamCtxt* expl, XWStreamCtxt* words )
and_util_informMove( XW_UtilCtxt* uc, XP_S16 turn, XWStreamCtxt* expl,
XWStreamCtxt* words )
{
UTIL_CBK_HEADER( "informMove", "(Ljava/lang/String;Ljava/lang/String;)V" );
UTIL_CBK_HEADER( "informMove", "(ILjava/lang/String;Ljava/lang/String;)V" );
jstring jexpl = streamToJString( env, expl );
jstring jwords = !!words ? streamToJString( env, words ) : NULL;
(*env)->CallVoidMethod( env, util->jutil, mid, jexpl, jwords );
(*env)->CallVoidMethod( env, util->jutil, mid, turn, jexpl, jwords );
deleteLocalRefs( env, jexpl, jwords, DELETE_NO_REF );
UTIL_CBK_TAIL();
}
@ -503,7 +504,7 @@ and_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi,
static void
and_util_showChat( XW_UtilCtxt* uc, const XP_UCHAR const* msg, XP_S16 from )
{
UTIL_CBK_HEADER( "showChat", "(Ljava/lang/String;Ljava/lang/String;)V" );
UTIL_CBK_HEADER( "showChat", "(Ljava/lang/String;ILjava/lang/String;)V" );
jstring jname = NULL;
if ( 0 <= from ) {
LocalPlayer* lp = &uc->gameInfo->players[from];
@ -512,7 +513,7 @@ and_util_showChat( XW_UtilCtxt* uc, const XP_UCHAR const* msg, XP_S16 from )
}
jstring jmsg = (*env)->NewStringUTF( env, msg );
(*env)->CallVoidMethod( env, util->jutil, mid, jmsg, jname );
(*env)->CallVoidMethod( env, util->jutil, mid, jmsg, from, jname );
deleteLocalRefs( env, jmsg, jname, DELETE_NO_REF );
UTIL_CBK_TAIL();
}

View file

@ -748,10 +748,10 @@ Java_org_eehouse_android_xw4_jni_XwJNI_envDone
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject j_gi, jobject j_util,
jobject jniu, jobject j_draw, jobject j_cp, jobject j_procs,
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject j_gi,
jobjectArray j_names, jobjectArray j_dicts, jobjectArray j_paths,
jstring j_lang )
jstring j_lang, jobject j_util, jobject jniu, jobject j_draw,
jobject j_cp, jobject j_procs )
{
XWJNI_START_GLOBALS();
EnvThreadInfo* ti = &state->globalJNI->ti;
@ -1127,6 +1127,17 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1getTrayVisState
return result;
}
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1getSelPlayer
(JNIEnv* env, jclass C, GamePtrType gamePtr)
{
jint result;
XWJNI_START();
result = board_getSelPlayer( state->game.board );
XWJNI_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1hideTray
(JNIEnv* env, jclass C, GamePtrType gamePtr)

Binary file not shown.

View file

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

View file

@ -6,27 +6,28 @@
android:layout_height="fill_parent"
>
<!-- history is kept in a scrolling list of textview elems so
different style can be applied based on whether they're local or
remote. Inserted at runtime.... -->
<ScrollView android:id="@+id/scroll"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
>
<LinearLayout android:id="@+id/chat_history"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<!-- contents inserted at runtime -->
<TableLayout android:id="@+id/chat_history"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</ScrollView>
<EditText android:id="@+id/chat_edit"
android:layout_width="wrap_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="false"
android:inputType="textCapSentences"
android:layout_weight="0"
android:hint="@string/chat_hint"
/>
</LinearLayout>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FF202020"
/>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:id="@+id/chat_row_name"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginRight="8dp"
/>
<TextView android:id="@+id/chat_row_text"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="false"
android:layout_width="0dp"
android:layout_weight="1"
/>
</TableRow>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF000007"
>
<!-- probably want some explanatory text here -->
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FF000000"
>
<Button android:id="@+id/activity_button"
android:text="@string/activity_button"
style="@style/evenly_spaced_horizontal"
/>
<Button android:id="@+id/fragment_button"
android:text="@string/fragment_button"
style="@style/evenly_spaced_horizontal"
/>
</LinearLayout>
</LinearLayout>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_version">4.4.105</string>
<string name="app_version">4.4.107</string>
</resources>

View file

@ -1291,6 +1291,8 @@
<!-- Text on the button that causes the contents of the
message-composition field to be sent. -->
<string name="chat_send">Send</string>
<!-- "Hint" in empty chat text field -->
<string name="chat_hint">Type here...</string>
<!--
############################################################
@ -2020,6 +2022,7 @@
<string name="menu_zoom">Zoom in/out</string>
<!-- -->
<string name="menu_chat">Chat</string>
<string name="chat_sender_fmt">%1$s:</string>
<!-- -->
<string name="menu_toggle_values">Toggle values</string>
@ -2029,6 +2032,8 @@
<!-- -->
<string name="game_list_tmp">Building game summary…</string>
<string name="summary_busy">Summary unavailable</string>
<string name="connstat_net_noaddr">This networked game has no way
to connect and will never be playable.\n\n(It was probably created
from an invitation that didn\'t specify any way of connecting
@ -2367,6 +2372,9 @@
<string name="force_tablet_title">Force tablet layout</string>
<string name="force_tablet_summary">Even if my screen is too small</string>
<string name="activity_button">Single-pane</string>
<string name="fragment_button">Dual-pane</string>
<!-- Nagging: title of notification reminder message -->
<string name="nag_title">Reminder: It\'s your turn</string>
<!-- body of warning notification reminder message. First three
@ -2407,9 +2415,10 @@
devices. Would you like to open the Android Settings Panel to add
one or more?\n\n(You may also need to open it on the device you
want to pair with.)</string>
<string name="app_not_found_fmt">Unable to connect to Crosswords
on the device %1$s. Please check that the device is within range
and that Crosswords is installed on it.</string>
<string name="app_not_found_fmt">Unable to connect via Bluetooth
to Crosswords on the device %1$s. Please check that the device is
within Bluetooth range and that Crosswords is installed on
it.</string>
<!-- label within default wordlists in app preferences -->
<string name="default_language">Default language</string>

View file

@ -90,6 +90,12 @@
<item name="android:layout_weight">1</item>
</style>
<!-- <style name="spaced_buttons"> -->
<!-- <item name="android:layout_width">fill_parent</item> -->
<!-- <item name="android:layout_height">wrap_content</item> -->
<!-- <item name="android:layout_weight">1</item> -->
<!-- </style> -->
<style name="newgame_connicon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>

View file

@ -1116,6 +1116,8 @@
<!-- Text on the button that causes the contents of the
message-composition field to be sent. -->
<string name="chat_send">Dnes</string>
<!-- "Hint" in empty chat text field -->
<string name="chat_hint">Epyt ereh...</string>
<!--
############################################################
# :Menus:
@ -1747,12 +1749,14 @@
<string name="menu_zoom">Mooz tuo/ni</string>
<!-- -->
<string name="menu_chat">Tahc</string>
<string name="chat_sender_fmt">%1$s:</string>
<!-- -->
<string name="menu_toggle_values">Elggot seulav</string>
<!-- board menu for small devices only -->
<string name="board_menu_dict">Esworb tsildrow</string>
<!-- -->
<string name="game_list_tmp">Gnidliub emag yrammus…</string>
<string name="summary_busy">Yrammus elbaliavanu</string>
<string name="connstat_net_noaddr">Siht dekrowten emag sah on yaw
ot tcennoc dna lliw reven eb elbayalp.\n\ntI( saw ylbaborp detaerc
morf na noitativni taht ndid\'t yficeps yna yaw fo gnitcennoc
@ -2032,6 +2036,8 @@
tahw\'s elbaliava.</string>
<string name="force_tablet_title">Ecrof telbat tuoyal</string>
<string name="force_tablet_summary">Neve fi ym neercs si oot llams</string>
<string name="activity_button">Enap-elgnis</string>
<string name="fragment_button">Enap-laud</string>
<!-- Nagging: title of notification reminder message -->
<string name="nag_title">Rednimer: Ti\'s ruoy nrut</string>
<!-- body of warning notification reminder message. First three

View file

@ -1116,6 +1116,8 @@
<!-- Text on the button that causes the contents of the
message-composition field to be sent. -->
<string name="chat_send">SEND</string>
<!-- "Hint" in empty chat text field -->
<string name="chat_hint">TYPE HERE...</string>
<!--
############################################################
# :Menus:
@ -1747,12 +1749,14 @@
<string name="menu_zoom">ZOOM IN/OUT</string>
<!-- -->
<string name="menu_chat">CHAT</string>
<string name="chat_sender_fmt">%1$s:</string>
<!-- -->
<string name="menu_toggle_values">TOGGLE VALUES</string>
<!-- board menu for small devices only -->
<string name="board_menu_dict">BROWSE WORDLIST</string>
<!-- -->
<string name="game_list_tmp">BUILDING GAME SUMMARY…</string>
<string name="summary_busy">SUMMARY UNAVAILABLE</string>
<string name="connstat_net_noaddr">THIS NETWORKED GAME HAS NO WAY
TO CONNECT AND WILL NEVER BE PLAYABLE.\n\n(IT WAS PROBABLY CREATED
FROM AN INVITATION THAT DIDN\'T SPECIFY ANY WAY OF CONNECTING
@ -2032,6 +2036,8 @@
WHAT\'S AVAILABLE.</string>
<string name="force_tablet_title">FORCE TABLET LAYOUT</string>
<string name="force_tablet_summary">EVEN IF MY SCREEN IS TOO SMALL</string>
<string name="activity_button">SINGLE-PANE</string>
<string name="fragment_button">DUAL-PANE</string>
<!-- Nagging: title of notification reminder message -->
<string name="nag_title">REMINDER: IT\'S YOUR TURN</string>
<!-- body of warning notification reminder message. First three

View file

@ -237,14 +237,6 @@ public class BTInviteDelegate extends InviteDelegate {
public void onCheckedChanged( CompoundButton buttonView,
boolean isChecked ) {
if ( isChecked ) {
if ( 1 == m_nMissing && 1 == m_checked.size() ) {
LinearLayout checked = m_checked.iterator().next();
CheckBox box = (CheckBox)checked
.findViewById( R.id.inviter_check );
box.setChecked( false );
m_checked.clear();
}
m_checked.add( layout );
} else {
m_checked.remove( layout );

View file

@ -53,7 +53,9 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.XWService.ReceiveResult;
import junit.framework.Assert;
@ -573,44 +575,20 @@ public class BTService extends XWService {
BluetoothDevice host = socket.getRemoteDevice();
addAddr( host );
// check if still here
long[] rowids = DBUtils.getRowIDsFor( BTService.this,
gameID );
boolean haveGame = null != rowids && 0 < rowids.length;
BTCmd result = haveGame ?
BTCmd.MESG_ACCPT : BTCmd.MESG_GAMEGONE;
CommsAddrRec addr = new CommsAddrRec( host.getName(),
host.getAddress() );
ReceiveResult rslt
= BTService.this.receiveMessage( BTService.this, gameID,
m_btMsgSink, buffer, addr );
BTCmd result = rslt == ReceiveResult.GAME_GONE ?
BTCmd.MESG_GAMEGONE : BTCmd.MESG_ACCPT;
DataOutputStream os =
new DataOutputStream( socket.getOutputStream() );
os.writeByte( result.ordinal() );
os.flush();
socket.close();
CommsAddrRec addr = new CommsAddrRec( host.getName(),
host.getAddress() );
boolean[] isLocalP = new boolean[1];
for ( long rowid : rowids ) {
boolean consumed =
BoardDelegate.feedMessage( rowid, buffer, addr );
if ( !consumed && haveGame ) {
GameUtils.BackMoveResult bmr =
new GameUtils.BackMoveResult();
if ( GameUtils.feedMessage( BTService.this, rowid,
buffer, addr,
m_btMsgSink, bmr,
isLocalP ) ) {
consumed = true;
GameUtils.postMoveNotification( BTService.this,
rowid, bmr,
isLocalP[0] );
}
}
if ( !consumed ) {
DbgUtils.logf( "nobody took msg for gameID %X",
gameID );
}
}
} else {
DbgUtils.logf( "receiveMessages: read only %d of %d bytes",
nRead, len );
@ -850,7 +828,7 @@ public class BTService extends XWService {
}
if ( null == reply ) {
sendResult( MultiEvent.APP_NOT_FOUND, dev.getName() );
sendResult( MultiEvent.APP_NOT_FOUND_BT, dev.getName() );
} else {
switch ( reply ) {
case BAD_PROTO:
@ -1015,7 +993,8 @@ public class BTService extends XWService {
synchronized( m_addrs ) {
for ( BluetoothDevice dev : pairedDevs ) {
int clazz = dev.getBluetoothClass().getMajorDeviceClass();
if ( Major.PHONE == clazz || Major.COMPUTER == clazz ) {
if ( Major.PHONE == clazz
|| (XWApp.BT_SCAN_COMPUTERS && Major.COMPUTER == clazz) ) {
m_addrs.add( dev.getAddress() );
}
}
@ -1186,6 +1165,9 @@ public class BTService extends XWService {
private void updateStatusOut( boolean success )
{
// Intent intent = GamesListDelegate.makeGameIDIntent( this, gameID );
// Utils.postNotification( this, intent, R.string.new_btmove_title,
// body, (int)rowid );
ConnStatusHandler
.updateStatusOut( this, null, CommsConnType.COMMS_CONN_BT, success );
}

View file

@ -89,7 +89,6 @@ public class BoardDelegate extends DelegateBase
private CurGameInfo m_gi;
private GameSummary m_summary;
private boolean m_relayMissing;
private CommsTransport m_xport;
private Handler m_handler = null;
private TimerRunnable[] m_timers;
private Runnable m_screenTimer;
@ -128,6 +127,7 @@ public class BoardDelegate extends DelegateBase
private Thread m_blockingThread;
private JNIThread m_jniThread;
private JNIThread m_jniThreadRef;
private JNIThread.GameStateInfo m_gsi;
private DlgID m_blockingDlgID = DlgID.NONE;
@ -142,57 +142,6 @@ public class BoardDelegate extends DelegateBase
private boolean m_haveInvited = false;
private boolean m_overNotShown;
private static Set<BoardDelegate> s_this = new HashSet<BoardDelegate>();
public static boolean feedMessage( long rowid, byte[] msg,
CommsAddrRec ret )
{
return feedMessages( rowid, new byte[][]{msg}, ret );
}
public static boolean feedMessages( long rowid, byte[][] msgs,
CommsAddrRec ret )
{
boolean delivered = false;
Assert.assertNotNull( msgs );
int size;
synchronized( s_this ) {
size = s_this.size();
if ( 1 == size ) {
BoardDelegate self = s_this.iterator().next();
Assert.assertNotNull( self.m_gi );
Assert.assertNotNull( self.m_gameLock );
Assert.assertNotNull( self.m_jniThread );
if ( rowid == self.m_rowid ) {
delivered = true; // even if no messages!
for ( byte[] msg : msgs ) {
self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, ret );
}
}
}
}
if ( 1 < size ) {
noteSkip();
}
return delivered;
}
private static void setThis( BoardDelegate self )
{
synchronized( s_this ) {
Assert.assertTrue( !s_this.contains(self) ); // here
s_this.add( self );
}
}
private static void clearThis( BoardDelegate self )
{
synchronized( s_this ) {
Assert.assertTrue( s_this.contains( self ) );
s_this.remove( self );
}
}
public class TimerRunnable implements Runnable {
private int m_why;
private int m_when;
@ -231,7 +180,7 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
m_jniThread.handle( JNICmd.CMD_RESET );
handleViaThread( JNICmd.CMD_RESET );
}
};
ab.setNegativeButton( R.string.button_retry, lstnr );
@ -421,8 +370,7 @@ public class BoardDelegate extends DelegateBase
public void
onClick( DialogInterface dlg,
int item ) {
m_jniThread.
handle(JNICmd.CMD_ENDGAME);
handleViaThread(JNICmd.CMD_ENDGAME);
}
})
.setNegativeButton( R.string.button_no, null )
@ -595,6 +543,7 @@ public class BoardDelegate extends DelegateBase
m_timers = new TimerRunnable[4]; // needs to be in sync with
// XWTimerReason
m_view = (BoardView)findViewById( R.id.board_view );
m_view.setBoardDelegate( this );
if ( ! ABUtils.haveActionBar() ) {
m_tradeButtons = findViewById( R.id.exchange_buttons );
if ( null != m_tradeButtons ) {
@ -614,20 +563,65 @@ public class BoardDelegate extends DelegateBase
m_haveInvited = args.getBoolean( GameUtils.INVITED, false );
m_overNotShown = true;
Handler handler = new Handler() {
public void handleMessage( Message msg ) {
switch( msg.what ) {
case JNIThread.DIALOG:
m_dlgBytes = (String)msg.obj;
m_dlgTitle = msg.arg1;
showDialog( DlgID.DLG_OKONLY );
break;
case JNIThread.QUERY_ENDGAME:
showDialog( DlgID.QUERY_ENDGAME );
break;
case JNIThread.TOOLBAR_STATES:
if ( null != m_jniThread ) {
m_gsi =
m_jniThread.getGameStateInfo();
updateToolbar();
if ( m_inTrade != m_gsi.inTrade ) {
m_inTrade = m_gsi.inTrade;
}
m_view.setInTrade( m_inTrade );
adjustTradeVisibility();
invalidateOptionsMenuIf();
}
break;
case JNIThread.GOT_WORDS:
CurGameInfo gi = m_jniThreadRef.getGI();
launchLookup( wordsToArray((String)msg.obj),
gi.dictLang );
break;
case JNIThread.GAME_OVER:
m_dlgBytes = (String)msg.obj;
m_dlgTitle = msg.arg1;
showDialog( DlgID.GAME_OVER );
break;
case JNIThread.MSGS_SENT:
int nSent = (Integer)msg.obj;
showToast( getQuantityString( R.plurals.resent_msgs_fmt,
nSent, nSent ) );
break;
}
}
};
m_jniThreadRef = JNIThread.getRetained( m_rowid, true ).
configure( m_activity, m_view, m_utils, this, handler );
m_jniGamePtr = m_jniThreadRef.getGamePtr();
Assert.assertNotNull( m_jniGamePtr );
// see http://stackoverflow.com/questions/680180/where-to-stop- \
// destroy-threads-in-android-service-class
m_jniThreadRef.setDaemonOnce( true ); // firing
m_jniThreadRef.startOnce();
NFCUtils.register( m_activity, this ); // Don't seem to need to unregister...
setBackgroundColor();
setKeepScreenOn();
} // init
protected void onPause()
{
m_handler = null;
ConnStatusHandler.setHandler( null );
waitCloseGame( true );
super.onPause();
}
@Override
protected void onStart()
{
@ -639,12 +633,41 @@ public class BoardDelegate extends DelegateBase
protected void onResume()
{
super.onResume();
// if( BuildConfig.DEBUG ) {
// GameUtils.postSelfNotification( m_activity, m_rowid );
// }
doResume( false );
}
protected void onPause()
{
closeIfFinishing( false );
m_handler = null;
ConnStatusHandler.setHandler( null );
waitCloseGame( true );
pauseGame();
super.onPause();
}
@Override
protected void onStop()
{
if ( isFinishing() ) {
m_jniThreadRef.release();
m_jniThreadRef = null;
}
super.onStop();
}
@Override
protected void onDestroy()
{
closeIfFinishing( true );
if ( null != m_jniThreadRef ) {
m_jniThreadRef.release();
m_jniThreadRef = null;
// Assert.assertNull( m_jniThreadRef ); // firing
}
GamesListDelegate.boardDestroyed( m_rowid );
super.onDestroy();
}
@ -718,7 +741,7 @@ public class BoardDelegate extends DelegateBase
m_firingPrefs = false;
m_volKeysZoom = XWPrefs.getVolKeysZoom( m_activity );
if ( null != m_jniThread ) {
m_jniThread.handle( JNICmd.CMD_PREFS_CHANGE );
handleViaThread( JNICmd.CMD_PREFS_CHANGE );
}
// in case of change...
setBackgroundColor();
@ -731,13 +754,33 @@ public class BoardDelegate extends DelegateBase
}
}
@Override
public void orientationChanged()
{
boolean isPortrait = isPortrait();
DbgUtils.logdf( "BoardDelegate.orientationChanged(isPortrait=%b)",
isPortrait );
positionToolbar( isPortrait );
m_view.orientationChanged();
}
private void positionToolbar( boolean isPortrait )
{
if ( null != findViewById( R.id.tbar_parent_hor ) ) {
if ( null == m_toolbar ) {
m_toolbar = new Toolbar( m_activity, this );
}
m_toolbar.setIsPortrait( isPortrait );
}
}
protected boolean onKeyDown( int keyCode, KeyEvent event )
{
boolean handled = false;
if ( null != m_jniThread ) {
XwJNI.XP_Key xpKey = keyCodeToXPKey( keyCode );
if ( XwJNI.XP_Key.XP_KEY_NONE != xpKey ) {
m_jniThread.handle( JNICmd.CMD_KEYDOWN, xpKey );
handleViaThread( JNICmd.CMD_KEYDOWN, xpKey );
} else {
switch( keyCode ) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
@ -760,7 +803,7 @@ public class BoardDelegate extends DelegateBase
if ( null != m_jniThread ) {
XwJNI.XP_Key xpKey = keyCodeToXPKey( keyCode );
if ( XwJNI.XP_Key.XP_KEY_NONE != xpKey ) {
m_jniThread.handle( JNICmd.CMD_KEYUP, xpKey );
handleViaThread( JNICmd.CMD_KEYUP, xpKey );
handled = true;
}
}
@ -923,10 +966,10 @@ public class BoardDelegate extends DelegateBase
cmd = JNICmd.CMD_TOGGLE_TRAY;
break;
case R.id.games_menu_study:
StudyListDelegate.launchOrAlert( m_activity, m_gi.dictLang, this );
StudyListDelegate.launchOrAlert( getDelegator(), m_gi.dictLang, this );
break;
case R.id.board_menu_game_netstats:
m_jniThread.handle( JNICmd.CMD_NETSTATS, R.string.netstats_title );
handleViaThread( JNICmd.CMD_NETSTATS, R.string.netstats_title );
break;
case R.id.board_menu_game_invites:
SentInvitesInfo sentInfo = DBUtils.getInvitesFor( m_activity, m_rowid );
@ -942,28 +985,27 @@ public class BoardDelegate extends DelegateBase
// small devices only
case R.id.board_menu_dict:
String dictName = m_gi.dictName( m_view.getCurPlayer() );
DictBrowseDelegate.launch( m_activity, dictName );
DictBrowseDelegate.launch( getDelegator(), dictName );
break;
case R.id.board_menu_game_counts:
m_jniThread.handle( JNICmd.CMD_COUNTS_VALUES,
R.string.counts_values_title );
handleViaThread( JNICmd.CMD_COUNTS_VALUES,
R.string.counts_values_title );
break;
case R.id.board_menu_game_left:
m_jniThread.handle( JNICmd.CMD_REMAINING,
R.string.tiles_left_title );
handleViaThread( JNICmd.CMD_REMAINING, R.string.tiles_left_title );
break;
case R.id.board_menu_game_history:
m_jniThread.handle( JNICmd.CMD_HISTORY, R.string.history_title );
handleViaThread( JNICmd.CMD_HISTORY, R.string.history_title );
break;
case R.id.board_menu_game_resign:
m_jniThread.handle( JNICmd.CMD_FINAL, R.string.history_title );
handleViaThread( JNICmd.CMD_FINAL, R.string.history_title );
break;
case R.id.board_menu_game_resend:
m_jniThread.handle( JNICmd.CMD_RESEND, true, false, true );
handleViaThread( JNICmd.CMD_RESEND, true, false, true );
break;
case R.id.gamel_menu_checkmoves:
@ -983,7 +1025,7 @@ public class BoardDelegate extends DelegateBase
}
if ( handled && cmd != JNICmd.CMD_NONE ) {
m_jniThread.handle( cmd );
handleViaThread( cmd );
}
return handled;
}
@ -1021,11 +1063,11 @@ public class BoardDelegate extends DelegateBase
String curDict = m_gi.dictName( m_view.getCurPlayer() );
View button = m_toolbar.getViewFor( Toolbar.BUTTON_BROWSE_DICT );
if ( Action.BUTTON_BROWSEALL_ACTION == action &&
DictsActivity.handleDictsPopup( m_activity, button,
DictsDelegate.handleDictsPopup( getDelegator(), button,
curDict, m_gi.dictLang ) ){
break;
}
DictBrowseDelegate.launch( m_activity, curDict );
DictBrowseDelegate.launch( getDelegator(), curDict );
break;
case PREV_HINT_ACTION:
cmd = JNICmd.CMD_PREV_HINT;
@ -1083,7 +1125,7 @@ public class BoardDelegate extends DelegateBase
}
if ( JNICmd.CMD_NONE != cmd ) {
checkAndHandle( cmd );
handleViaThread( cmd );
}
}
@ -1148,9 +1190,9 @@ public class BoardDelegate extends DelegateBase
public void onClick( View view )
{
if ( view == m_exchCommmitButton ) {
m_jniThread.handle( JNICmd.CMD_COMMIT );
handleViaThread( JNICmd.CMD_COMMIT );
} else if ( view == m_exchCancelButton ) {
m_jniThread.handle( JNICmd.CMD_CANCELTRADE );
handleViaThread( JNICmd.CMD_CANCELTRADE );
}
}
@ -1236,7 +1278,7 @@ public class BoardDelegate extends DelegateBase
public void tpmRelayConnd( final String room, final int devOrder,
final boolean allHere, final int nMissing )
{
post( new Runnable() {
runOnUiThread( new Runnable() {
public void run() {
handleConndMessage( room, devOrder, allHere, nMissing );
}
@ -1390,8 +1432,8 @@ public class BoardDelegate extends DelegateBase
private void deleteAndClose()
{
GameUtils.deleteGame( m_activity, m_gameLock, false );
waitCloseGame( false );
GameUtils.deleteGame( m_activity, m_rowid, false );
finish();
}
@ -1422,7 +1464,7 @@ public class BoardDelegate extends DelegateBase
finish();
GameUtils.launchGame( m_activity, m_rowid, m_haveInvited );
GameUtils.launchGame( getDelegator(), m_rowid, m_haveInvited );
}
private void setGotGameDict( String getDict )
@ -1432,7 +1474,7 @@ public class BoardDelegate extends DelegateBase
String msg = getString( R.string.reload_new_dict_fmt, getDict );
showToast( msg );
finish();
GameUtils.launchGame( m_activity, m_rowid, false );
GameUtils.launchGame( getDelegator(), m_rowid, false );
}
private XwJNI.XP_Key keyCodeToXPKey( int keyCode )
@ -1579,7 +1621,7 @@ public class BoardDelegate extends DelegateBase
@Override
public void requestTime()
{
post( new Runnable() {
runOnUiThread( new Runnable() {
public void run() {
if ( null != m_jniThread ) {
m_jniThread.handleBkgrnd( JNICmd.CMD_DO );
@ -1591,8 +1633,7 @@ public class BoardDelegate extends DelegateBase
@Override
public void remSelected()
{
m_jniThread.handle( JNICmd.CMD_REMAINING,
R.string.tiles_left_title );
handleViaThread( JNICmd.CMD_REMAINING, R.string.tiles_left_title );
}
@Override
@ -1603,7 +1644,7 @@ public class BoardDelegate extends DelegateBase
if ( newRole != m_gi.serverRole ) {
m_gi.serverRole = newRole;
if ( !isServer ) {
m_jniThread.handle( JNICmd.CMD_SWITCHCLIENT );
handleViaThread( JNICmd.CMD_SWITCHCLIENT );
}
}
}
@ -1744,7 +1785,7 @@ public class BoardDelegate extends DelegateBase
R.string.key_notagain_turnchanged );
}
} );
m_jniThread.handle( JNICmd. CMD_ZOOM, -8 );
handleViaThread( JNICmd. CMD_ZOOM, -8 );
}
}
@ -1894,10 +1935,21 @@ public class BoardDelegate extends DelegateBase
}
@Override
public void informMove( String expl, String words )
public void informMove( int turn, String expl, String words )
{
m_words = null == words? null : wordsToArray( words );
nonBlockingDialog( DlgID.DLG_SCORES, expl );
if ( isVisible() ) {
Utils.playNotificationSound( m_activity );
} else {
LastMoveInfo lmi = new LastMoveInfo();
XwJNI.model_getPlayersLastScore( m_jniGamePtr, turn, lmi );
GameUtils.BackMoveResult bmr = new GameUtils.BackMoveResult();
bmr.m_lmi = lmi;
boolean[] locals = m_gi.playersLocal();
GameUtils.postMoveNotification( m_activity, m_rowid,
bmr, locals[turn] );
}
}
@Override
@ -1947,7 +1999,7 @@ public class BoardDelegate extends DelegateBase
public void notifyGameOver()
{
m_gameOver = true;
m_jniThread.handle( JNICmd.CMD_POST_OVER );
handleViaThread( JNICmd.CMD_POST_OVER );
}
// public void yOffsetChange( int maxOffset, int oldOffset, int newOffset )
@ -1983,14 +2035,18 @@ public class BoardDelegate extends DelegateBase
// and may stack dialogs on top of this one. Including later
// chat-messages.
@Override
public void showChat( final String msg, String fromPlayer )
public void showChat( final String msg, final int fromIndx,
String fromPlayer )
{
if ( BuildConstants.CHAT_SUPPORTED ) {
post( new Runnable() {
runOnUiThread( new Runnable() {
public void run() {
DBUtils.appendChatHistory( m_activity,
m_rowid, msg, false );
startChatActivity();
DBUtils.appendChatHistory( m_activity, m_rowid, msg,
fromIndx );
if ( ! ChatDelegate.append( m_rowid, msg,
fromIndx ) ) {
startChatActivity();
}
}
} );
}
@ -2006,7 +2062,7 @@ public class BoardDelegate extends DelegateBase
}
try {
loadGame( isStart );
resumeGame( isStart );
if ( !isStart ) {
setKeepScreenOn();
ConnStatusHandler.setHandler( this );
@ -2016,159 +2072,176 @@ public class BoardDelegate extends DelegateBase
}
}
private void loadGame( boolean isStart )
private void resumeGame( boolean isStart )
{
if ( null == m_jniGamePtr ) {
try {
String gameName = DBUtils.getName( m_activity, m_rowid );
String[] dictNames = GameUtils.dictNames( m_activity, m_rowid );
DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, dictNames );
if ( null == m_jniThread ) {
m_jniThread = m_jniThreadRef.retain();
m_gi = m_jniThread.getGI();
m_summary = m_jniThread.getSummary();
m_gameLock = m_jniThread.getLock();
if ( pairs.anyMissing( dictNames ) ) {
showDictGoneFinish();
} else {
Assert.assertNull( m_gameLock );
m_gameLock = new GameLock( m_rowid, true ).lock();
m_view.startHandling( m_activity, m_jniThread, m_connTypes );
byte[] stream = GameUtils.savedGame( m_activity, m_gameLock );
m_gi = new CurGameInfo( m_activity );
m_gi.setName( gameName );
XwJNI.gi_from_stream( m_gi, stream );
String langName = m_gi.langName();
handleViaThread( JNICmd.CMD_START );
m_summary = DBUtils.getSummary( m_activity, m_gameLock );
m_relayMissing = m_summary.relayConnectPending();
if ( !CommonPrefs.getHideTitleBar( m_activity ) ) {
setTitle( GameUtils.getName( m_activity, m_rowid ) );
}
setThis( this );
positionToolbar( isPortrait() );
populateToolbar();
adjustTradeVisibility();
m_jniGamePtr = XwJNI.initJNI( m_rowid );
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
m_xport = new CommsTransport( m_activity, this, m_rowid,
m_gi.serverRole );
}
CommonPrefs cp = CommonPrefs.get( m_activity );
if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
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 );
}
Handler handler = new Handler() {
public void handleMessage( Message msg ) {
switch( msg.what ) {
case JNIThread.DIALOG:
m_dlgBytes = (String)msg.obj;
m_dlgTitle = msg.arg1;
showDialog( DlgID.DLG_OKONLY );
break;
case JNIThread.QUERY_ENDGAME:
showDialog( DlgID.QUERY_ENDGAME );
break;
case JNIThread.TOOLBAR_STATES:
if ( null != m_jniThread ) {
m_gsi =
m_jniThread.getGameStateInfo();
updateToolbar();
if ( m_inTrade != m_gsi.inTrade ) {
m_inTrade = m_gsi.inTrade;
}
m_view.setInTrade( m_inTrade );
adjustTradeVisibility();
invalidateOptionsMenuIf();
}
break;
case JNIThread.GOT_WORDS:
launchLookup( wordsToArray((String)msg.obj),
m_gi.dictLang );
break;
case JNIThread.GAME_OVER:
m_dlgBytes = (String)msg.obj;
m_dlgTitle = msg.arg1;
showDialog( DlgID.GAME_OVER );
break;
case JNIThread.MSGS_SENT:
int nSent = (Integer)msg.obj;
showToast( getQuantityString( R.plurals.resent_msgs_fmt,
nSent, nSent ) );
break;
}
}
};
m_jniThread =
new JNIThread( m_jniGamePtr, stream, m_gi,
m_view, m_gameLock, m_activity, handler );
// see http://stackoverflow.com/questions/680180/where-to-stop-\
// destroy-threads-in-android-service-class
m_jniThread.setDaemon( true );
m_jniThread.start();
m_view.startHandling( m_activity, m_jniThread, m_jniGamePtr, m_gi,
m_connTypes );
if ( null != m_xport ) {
// informMissing should have been called by now
Assert.assertNotNull( m_connTypes );
m_xport.setReceiver( m_jniThread, m_handler );
}
m_jniThread.handle( JNICmd.CMD_START );
if ( !CommonPrefs.getHideTitleBar( m_activity ) ) {
setTitle( GameUtils.getName( m_activity, m_rowid ) );
}
if ( null != findViewById( R.id.tbar_parent_hor ) ) {
int orient = m_activity.getResources().getConfiguration().orientation;
boolean isLandscape = Configuration.ORIENTATION_LANDSCAPE == orient;
m_toolbar = new Toolbar( m_activity, this, isLandscape );
}
populateToolbar();
adjustTradeVisibility();
int flags = DBUtils.getMsgFlags( m_activity, m_rowid );
if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) {
startChatActivity();
}
if ( m_overNotShown ) {
boolean auto = false;
if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) {
m_gameOver = true;
} else if ( DBUtils.gameOver( m_activity, m_rowid ) ) {
m_gameOver = true;
auto = true;
}
if ( m_gameOver ) {
m_overNotShown = false;
m_jniThread.handle( JNICmd.CMD_POST_OVER, auto );
}
}
if ( 0 != flags ) {
DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE );
}
Utils.cancelNotification( m_activity, (int)m_rowid );
if ( null != m_xport ) {
warnIfNoTransport();
trySendChats();
tickle( isStart );
tryInvites();
}
int flags = DBUtils.getMsgFlags( m_activity, m_rowid );
if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) {
startChatActivity();
}
if ( m_overNotShown ) {
boolean auto = false;
if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) {
m_gameOver = true;
} else if ( DBUtils.gameOver( m_activity, m_rowid ) ) {
m_gameOver = true;
auto = true;
}
} catch ( GameUtils.NoSuchGameException nsge ) {
DbgUtils.loge( nsge );
finish();
if ( m_gameOver ) {
m_overNotShown = false;
handleViaThread( JNICmd.CMD_POST_OVER, auto );
}
}
if ( 0 != flags ) {
DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE );
}
Utils.cancelNotification( m_activity, (int)m_rowid );
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
warnIfNoTransport();
trySendChats();
tickle( isStart );
tryInvites();
}
}
} // loadGame
} // resumeGame
// private void loadGame( boolean isStart )
// {
// if ( null == m_jniGamePtr ) {
// try {
// String gameName = DBUtils.getName( m_activity, m_rowid );
// String[] dictNames;
// Assert.assertNull( m_gameLock );
// m_gameLock = m_jniThreadRef.getLock();
// if ( null == m_gameLock ) {
// dictNames = GameUtils.dictNames( m_activity, m_rowid );
// } else {
// dictNames = GameUtils.dictNames( m_activity, m_gameLock );
// }
// DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, dictNames );
// if ( pairs.anyMissing( dictNames ) ) {
// showDictGoneFinish();
// } else {
// if ( null == m_gameLock ) {
// m_gameLock = new GameLock( m_rowid, true ).lock();
// }
// // PENDING: there's no point in re-opening the game if
// // it's already open!
// byte[] stream = GameUtils.savedGame( m_activity, m_gameLock );
// m_gi = new CurGameInfo( m_activity );
// m_gi.setName( gameName );
// XwJNI.gi_from_stream( m_gi, stream );
// String langName = m_gi.langName();
// m_summary = DBUtils.getSummary( m_activity, m_gameLock );
// m_relayMissing = m_summary.relayConnectPending();
// setThis( this );
// m_jniGamePtr = XwJNI.initJNI( m_rowid );
// if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
// m_xport = new CommsTransport( m_activity, this, m_rowid,
// m_gi.serverRole );
// }
// CommonPrefs cp = CommonPrefs.get( m_activity );
// if ( null == stream ||
// ! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
// 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_jniThread = m_jniThreadRef.retain();
// // see http://stackoverflow.com/questions/680180/where-to-stop-\
// // destroy-threads-in-android-service-class
// // m_jniThread.setDaemonOnce( true ); // firing
// // m_jniThread.startOnce();
// m_view.startHandling( m_activity, m_jniThread, m_jniGamePtr, m_gi,
// m_connTypes );
// if ( null != m_xport ) {
// // informMissing should have been called by now
// Assert.assertNotNull( m_connTypes );
// m_xport.setReceiver( m_jniThread, m_handler );
// }
// handleViaThread( JNICmd.CMD_START );
// if ( !CommonPrefs.getHideTitleBar( m_activity ) ) {
// setTitle( GameUtils.getName( m_activity, m_rowid ) );
// }
// positionToolbar( isPortrait() );
// populateToolbar();
// adjustTradeVisibility();
// int flags = DBUtils.getMsgFlags( m_activity, m_rowid );
// if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) {
// startChatActivity();
// }
// if ( m_overNotShown ) {
// boolean auto = false;
// if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) {
// m_gameOver = true;
// } else if ( DBUtils.gameOver( m_activity, m_rowid ) ) {
// m_gameOver = true;
// auto = true;
// }
// if ( m_gameOver ) {
// m_overNotShown = false;
// handleViaThread( JNICmd.CMD_POST_OVER, auto );
// }
// }
// if ( 0 != flags ) {
// DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE );
// }
// Utils.cancelNotification( m_activity, (int)m_rowid );
// if ( null != m_xport ) {
// warnIfNoTransport();
// trySendChats();
// tickle( isStart );
// tryInvites();
// }
// }
// } catch ( GameUtils.NoSuchGameException nsge ) {
// DbgUtils.loge( nsge );
// finish();
// }
// }
// } // loadGame
private void tickle( boolean force )
{
@ -2190,8 +2263,7 @@ public class BoardDelegate extends DelegateBase
}
if ( 0 < m_connTypes.size() ) {
m_jniThread.handle( JNIThread.JNICmd.CMD_RESEND, force, true,
false );
handleViaThread( JNIThread.JNICmd.CMD_RESEND, force, true, false );
}
}
@ -2223,13 +2295,6 @@ public class BoardDelegate extends DelegateBase
}
}
private void checkAndHandle( JNICmd cmd )
{
if ( null != m_jniThread ) {
m_jniThread.handle( cmd );
}
}
private void populateToolbar()
{
if ( null != m_toolbar ) {
@ -2344,7 +2409,7 @@ public class BoardDelegate extends DelegateBase
}
m_dlgBytes = txt;
post( new Runnable() {
runOnUiThread( new Runnable() {
public void run() {
showDialog( dlgID );
}
@ -2355,7 +2420,7 @@ public class BoardDelegate extends DelegateBase
{
boolean handled = null != m_jniThread;
if ( handled ) {
m_jniThread.handle( JNICmd.CMD_ZOOM, zoomBy );
handleViaThread( JNICmd.CMD_ZOOM, zoomBy );
}
return handled;
}
@ -2363,29 +2428,38 @@ public class BoardDelegate extends DelegateBase
private void startChatActivity()
{
if ( BuildConstants.CHAT_SUPPORTED ) {
Intent intent = new Intent( m_activity, ChatActivity.class );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_rowid );
startActivityForResult( intent, RequestCode.CHAT_REQUEST );
int curPlayer = XwJNI.board_getSelPlayer( m_jniGamePtr );
String[] names = m_gi.playerNames();
boolean[] locs = m_gi.playersLocal(); // to convert old histories
ChatDelegate.startForResult( getDelegator(), RequestCode.CHAT_REQUEST,
m_rowid, curPlayer, names, locs );
}
}
private void waitCloseGame( boolean save )
private void closeIfFinishing( boolean force )
{
if ( null != m_jniGamePtr ) {
if ( null != m_xport ) {
m_xport.waitToStop();
m_xport = null;
}
if ( null == m_handler ) {
// DbgUtils.logf( "closeIfFinishing(): already closed" );
} else if ( force || isFinishing() ) {
// DbgUtils.logf( "closeIfFinishing: closing rowid %d", m_rowid );
m_handler = null;
ConnStatusHandler.setHandler( null );
waitCloseGame( true );
} else {
handleViaThread( JNICmd.CMD_SAVE );
// DbgUtils.logf( "closeIfFinishing(): not finishing (yet)" );
}
}
private void pauseGame()
{
if ( null != m_jniThread ) {
interruptBlockingThread();
if ( null != m_jniThread ) {
m_jniThread.waitToStop( save );
m_jniThread = null;
}
m_view.stopHandling();
m_jniThread.release();
m_jniThread = null;
clearThis( this );
m_view.stopHandling();
if ( XWPrefs.getThumbEnabled( m_activity ) ) {
// Before we dispose, and after JNIThread has
@ -2395,11 +2469,18 @@ public class BoardDelegate extends DelegateBase
DBUtils.saveThumbnail( m_activity, m_gameLock, thumb );
}
m_jniGamePtr.release();
m_jniGamePtr = null;
m_gi = null;
m_gameLock = null;
}
}
private void waitCloseGame( boolean save )
{
pauseGame();
if ( null != m_jniThread ) {
// m_jniGamePtr.release();
// m_jniGamePtr = null;
m_gameLock.unlock();
// m_gameLock.unlock(); // likely the problem
m_gameLock = null;
}
}
@ -2421,7 +2502,7 @@ public class BoardDelegate extends DelegateBase
if ( BuildConstants.CHAT_SUPPORTED && null != m_jniThread ) {
Iterator<String> iter = m_pendingChats.iterator();
while ( iter.hasNext() ) {
m_jniThread.handle( JNICmd.CMD_SENDCHAT, iter.next() );
handleViaThread( JNICmd.CMD_SENDCHAT, iter.next() );
}
m_pendingChats.clear();
}
@ -2554,7 +2635,8 @@ public class BoardDelegate extends DelegateBase
if ( canPost ) {
m_handler.post( runnable );
} else {
DbgUtils.logf( "post: dropping because handler null" );
DbgUtils.logf( "BoardDelegate.post(): dropping b/c handler null" );
DbgUtils.printStack();
}
return canPost;
}
@ -2776,10 +2858,13 @@ public class BoardDelegate extends DelegateBase
DBUtils.recordInviteSent( m_activity, m_rowid, means, dev );
}
private static void noteSkip()
private void handleViaThread( JNICmd cmd, Object... args )
{
String msg = "BoardActivity.feedMessage[s](): skipped because "
+ "too many open Boards";
DbgUtils.logf(msg );
if ( null == m_jniThread ) {
DbgUtils.logf( "BoardDelegate: not calling handle(%s)", cmd.toString() );
DbgUtils.printStack();
} else {
m_jniThread.handle( cmd, args );
}
}
} // class BoardDelegate

View file

@ -0,0 +1,32 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2014 - 2016 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.os.Bundle;
public class BoardFrag extends XWFragment {
@Override
public void onCreate( Bundle sis )
{
super.onCreate( new BoardDelegate( this, sis ), sis, true );
}
}

View file

@ -55,6 +55,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
private static final int PINCH_THRESHOLD = 40;
private Context m_context;
private BoardDelegate m_boardDlgt;
private int m_defaultFontHt;
private int m_mediumFontHt;
private Runnable m_invalidator;
@ -62,6 +63,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
private CurGameInfo m_gi;
private boolean m_isSolo;
private int m_layoutWidth;
private int m_dimsTossCount; // hack hack hack!!
private int m_layoutHeight;
private BoardCanvas m_canvas; // owns the bitmap
private JNIThread m_jniThread;
@ -103,17 +105,17 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
case MotionEvent.ACTION_DOWN:
m_lastSpacing = MULTI_INACTIVE;
if ( !ConnStatusHandler.handleDown( xx, yy ) ) {
m_jniThread.handle( JNIThread.JNICmd.CMD_PEN_DOWN, xx, yy );
handle( JNIThread.JNICmd.CMD_PEN_DOWN, xx, yy );
}
break;
case MotionEvent.ACTION_MOVE:
if ( ConnStatusHandler.handleMove( xx, yy ) ) {
} else if ( MULTI_INACTIVE == m_lastSpacing ) {
m_jniThread.handle( JNIThread.JNICmd.CMD_PEN_MOVE, xx, yy );
handle( JNIThread.JNICmd.CMD_PEN_MOVE, xx, yy );
} else {
int zoomBy = figureZoom( event );
if ( 0 != zoomBy ) {
m_jniThread.handle( JNIThread.JNICmd.CMD_ZOOM,
handle( JNIThread.JNICmd.CMD_ZOOM,
zoomBy < 0 ? -2 : 2 );
}
}
@ -122,12 +124,12 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
if ( ConnStatusHandler.handleUp( xx, yy ) ) {
// do nothing
} else {
m_jniThread.handle( JNIThread.JNICmd.CMD_PEN_UP, xx, yy );
handle( JNIThread.JNICmd.CMD_PEN_UP, xx, yy );
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_POINTER_2_DOWN:
m_jniThread.handle( JNIThread.JNICmd.CMD_PEN_UP, xx, yy );
handle( JNIThread.JNICmd.CMD_PEN_UP, xx, yy );
m_lastSpacing = getSpacing( event );
break;
case MotionEvent.ACTION_POINTER_UP:
@ -142,33 +144,24 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
return wantMore; // true required to get subsequent events
}
// private void printMode( String comment, int mode )
// {
// comment += ": ";
// switch( mode ) {
// case View.MeasureSpec.AT_MOST:
// comment += "AT_MOST";
// break;
// case View.MeasureSpec.EXACTLY:
// comment += "EXACTLY";
// break;
// case View.MeasureSpec.UNSPECIFIED:
// comment += "UNSPECIFIED";
// break;
// default:
// comment += "<bogus>";
// }
// DbgUtils.logf( comment );
// }
@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec )
{
// One of the android sample apps ignores mode entirely:
// int w = MeasureSpec.getSize(widthMeasureSpec);
// int h = MeasureSpec.getSize(heightMeasureSpec);
// int d = w == 0 ? h : h == 0 ? w : w < h ? w : h;
// setMeasuredDimension(d, d);
// DbgUtils.logf( "onMeasure(width: %s, height: %s)",
// MeasureSpec.toString( widthMeasureSpec ),
// MeasureSpec.toString( heightMeasureSpec ) );
if ( null != m_dims ) {
if ( m_boardDlgt.isPortrait() != (m_dims.height > m_dims.width) ) {
// square possible; will break above!
Assert.assertTrue( m_dims.height != m_dims.width );
DbgUtils.logf( "onMeasure: discarding m_dims" );
if ( ++m_dimsTossCount < 4 ) {
m_dims = null;
m_layoutWidth = m_layoutHeight = 0;
}
}
}
int width, height;
m_measuredFromDims = null != m_dims;
@ -189,8 +182,17 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
width = minWidth;
}
setMeasuredDimension( width, height );
DbgUtils.logdf( "BoardView.onMeasure: calling setMeasuredDimension( width=%d, height=%d )",
width, height );
}
// public void onSizeChanged( int width, int height, int oldWidth, int oldHeight )
// {
// DbgUtils.logf( "BoardView.onSizeChanged(): width: %d => %d; height: %d => %d",
// oldWidth, width, oldHeight, height );
// super.onSizeChanged( width, height, oldWidth, oldHeight );
// }
// This will be called from the UI thread
@Override
protected void onDraw( Canvas canvas )
@ -201,7 +203,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
ConnStatusHandler.draw( m_context, canvas, getResources(),
m_connTypes, m_isSolo );
} else {
DbgUtils.logf( "board not laid out yet" );
DbgUtils.logf( "BoardView.onDraw(): board not laid out yet" );
}
}
}
@ -212,12 +214,15 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
final int height = getHeight();
boolean layoutDone = width == m_layoutWidth && height == m_layoutHeight;
if ( layoutDone ) {
// nothing to do
DbgUtils.logf( "layoutBoardOnce(): layoutDone true" );
} else if ( null == m_gi ) {
// nothing to do either
DbgUtils.logf( "layoutBoardOnce(): no m_gi" );
} else if ( null == m_jniThread ) {
// nothing to do either
DbgUtils.logf( "layoutBoardOnce(): no m_jniThread" );
} else if ( null == m_dims ) {
DbgUtils.logf( "layoutBoardOnce(): null m_dims" );
// m_canvas = null;
// need to synchronize??
Paint paint = new Paint();
@ -228,10 +233,12 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
int timerWidth = scratch.width();
int fontWidth =
Math.min(m_defaultFontHt, timerWidth / timerTxt.length());
m_jniThread.handle( JNIThread.JNICmd.CMD_LAYOUT, width, height,
DbgUtils.logf( "layoutBoardOnce(): posting JNICmd.CMD_LAYOUT(w=%d, h=%d)", width, height );
handle( JNIThread.JNICmd.CMD_LAYOUT, width, height,
fontWidth, m_defaultFontHt );
// We'll be back....
} else {
DbgUtils.logf( "layoutBoardOnce(): DOING IT" );
// If board size has changed we need a new bitmap
int bmHeight = 1 + m_dims.height;
int bmWidth = 1 + m_dims.width;
@ -257,37 +264,43 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
} else {
m_canvas.setJNIThread( m_jniThread );
}
m_jniThread.handle( JNIThread.JNICmd.CMD_SETDRAW, m_canvas );
m_jniThread.handle( JNIThread.JNICmd.CMD_DRAW );
handle( JNIThread.JNICmd.CMD_SETDRAW, m_canvas );
handle( JNIThread.JNICmd.CMD_DRAW );
// set so we know we're done
m_layoutWidth = width;
m_layoutHeight = height;
layoutDone = true;
}
DbgUtils.logf( "layoutBoardOnce()=>%b", layoutDone );
return layoutDone;
} // layoutBoardOnce
protected void setBoardDelegate( BoardDelegate dlgt )
{
m_boardDlgt = dlgt;
}
// BoardHandler interface implementation
public void startHandling( Activity parent, JNIThread thread,
XwJNI.GamePtr gamePtr, CurGameInfo gi,
CommsConnTypeSet connTypes )
{
DbgUtils.logf( "BoardView.startHandling(thread=%H)", thread );
m_parent = parent;
m_jniThread = thread;
m_jniGamePtr = gamePtr;
m_gi = gi;
m_isSolo = CurGameInfo.DeviceRole.SERVER_STANDALONE == gi.serverRole;
m_jniGamePtr = thread.getGamePtr();
m_gi = thread.getGI();
m_isSolo = CurGameInfo.DeviceRole.SERVER_STANDALONE == m_gi.serverRole;
m_connTypes = connTypes;
m_layoutWidth = 0;
m_layoutHeight = 0;
s_isFirstDraw = s_curGameID != gi.gameID;
s_curGameID = gi.gameID;
s_isFirstDraw = s_curGameID != m_gi.gameID;
s_curGameID = m_gi.gameID;
// Set the jni layout if we already have one
if ( null != m_dims ) {
m_jniThread.handle( JNIThread.JNICmd.CMD_LAYOUT, m_dims );
handle( JNIThread.JNICmd.CMD_LAYOUT, m_dims );
}
// Make sure we draw. Sometimes when we're reloading after
@ -307,19 +320,22 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
// SyncedDraw interface implementation
public void doJNIDraw()
{
boolean drew;
boolean drew = false;
synchronized( this ) {
if ( !XwJNI.board_draw( m_jniGamePtr ) ) {
DbgUtils.logf( "doJNIDraw: draw not complete" );
if ( null != m_jniGamePtr ) {
drew = XwJNI.board_draw( m_jniGamePtr );
}
}
// Force update now that we have bits to copy
m_parent.runOnUiThread( m_invalidator );
if ( drew ) {
m_parent.runOnUiThread( m_invalidator );
}
}
public void dimsChanged( BoardDims dims )
{
DbgUtils.logdf( "dimsChanged(%s)", dims.toString() );
m_dims = dims;
m_parent.runOnUiThread( new Runnable() {
public void run()
@ -329,6 +345,14 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
});
}
protected void orientationChanged()
{
m_dims = null;
m_layoutWidth = m_layoutHeight = 0;
m_dimsTossCount = 0;
requestLayout();
}
public void setInTrade( boolean inTrade )
{
if ( null != m_canvas ) {
@ -373,4 +397,13 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
return zoomDir;
}
private void handle( JNIThread.JNICmd cmd, Object... args )
{
if ( null == m_jniThread ) {
DbgUtils.logf( "BoardView: not calling handle(%s)", cmd.toString() );
DbgUtils.printStack();
} else {
m_jniThread.handle( cmd, args );
}
}
}

View file

@ -28,19 +28,34 @@ import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View.OnLayoutChangeListener;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import junit.framework.Assert;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.jni.JNIThread;
public class ChatDelegate extends DelegateBase {
private static final String INTENT_KEY_PLAYER = "intent_key_player";
private static final String INTENT_KEY_NAMES = "intent_key_names";
private static final String INTENT_KEY_LOCS = "intent_key_locs";
private static ChatDelegate s_visibleThis;
private long m_rowid;
private int m_curPlayer;
private String[] m_names;
private Activity m_activity;
private EditText mEdit;
private EditText m_edit;
private TableLayout m_layout;
private ScrollView m_scroll;
private JNIThread m_jniThreadRef;
public ChatDelegate( Delegator delegator, Bundle savedInstanceState )
{
@ -51,9 +66,10 @@ public class ChatDelegate extends DelegateBase {
@Override
protected void init( Bundle savedInstanceState )
{
DbgUtils.logf( "ChatDelegate.init()" );
if ( BuildConstants.CHAT_SUPPORTED ) {
mEdit = (EditText)findViewById( R.id.chat_edit );
mEdit.addTextChangedListener( new TextWatcher() {
m_edit = (EditText)findViewById( R.id.chat_edit );
m_edit.addTextChangedListener( new TextWatcher() {
public void afterTextChanged( Editable s ) {
invalidateOptionsMenuIf();
}
@ -63,30 +79,31 @@ public class ChatDelegate extends DelegateBase {
int before, int count ) {}
} );
m_rowid = getIntent().getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 );
DBUtils.HistoryPair[] pairs = DBUtils.getChatHistory( m_activity, m_rowid );
if ( null != pairs ) {
LinearLayout layout = (LinearLayout)
findViewById( R.id.chat_history );
Bundle args = getArguments();
m_rowid = args.getLong( GameUtils.INTENT_KEY_ROWID, -1 );
m_curPlayer = args.getInt( INTENT_KEY_PLAYER, -1 );
m_names = args.getStringArray( INTENT_KEY_NAMES );
boolean[] locals = args.getBooleanArray( INTENT_KEY_LOCS );
for ( DBUtils.HistoryPair pair : pairs ) {
TextView view = (TextView)
inflate( pair.sourceLocal
? R.layout.chat_history_local
: R.layout.chat_history_remote );
view.setText( pair.msg );
layout.addView( view );
}
}
final ScrollView scroll = (ScrollView)findViewById( R.id.scroll );
scroll.post(new Runnable() {
m_scroll = (ScrollView)findViewById( R.id.scroll );
m_layout = (TableLayout)findViewById( R.id.chat_history );
m_layout.addOnLayoutChangeListener( new OnLayoutChangeListener() {
@Override
public void run() {
scroll.fullScroll(View.FOCUS_DOWN);
public void onLayoutChange( View vv, int ll, int tt, int rr,
int bb, int ol, int ot,
int or, int ob ) {
scrollDown();
}
});
DBUtils.HistoryPair[] pairs
= DBUtils.getChatHistory( m_activity, m_rowid, locals );
if ( null != pairs ) {
for ( DBUtils.HistoryPair pair : pairs ) {
addRow( pair.msg, pair.playerIndx );
}
}
// scrollDown();
String title = getString( R.string.chat_title_fmt,
GameUtils.getName( m_activity, m_rowid ) );
@ -97,10 +114,59 @@ public class ChatDelegate extends DelegateBase {
}
}
@Override
protected void onResume()
{
super.onResume();
m_jniThreadRef = JNIThread.getRetained( m_rowid );
if ( null == m_jniThreadRef ) {
DbgUtils.logf( "ChatDelegate.onResume(): m_jniThreadRef null; exiting" );
finish();
} else {
s_visibleThis = this;
}
}
@Override
protected void onPause()
{
if ( null != m_jniThreadRef ) {
m_jniThreadRef.release();
}
s_visibleThis = null;
super.onPause();
}
private void addRow( String msg, int playerIndx )
{
TableRow row = (TableRow)inflate( R.layout.chat_row );
if ( m_curPlayer == playerIndx ) {
row.setBackgroundColor(0xFF202020);
}
TextView view = (TextView)row.findViewById( R.id.chat_row_text );
view.setText( msg );
view = (TextView)row.findViewById( R.id.chat_row_name );
view.setText( getString( R.string.chat_sender_fmt,
m_names[playerIndx] ) );
m_layout.addView( row );
scrollDown();
}
private void scrollDown()
{
m_scroll.post( new Runnable() {
@Override
public void run() {
m_scroll.fullScroll( View.FOCUS_DOWN );
}
});
}
@Override
public boolean onPrepareOptionsMenu( Menu menu )
{
String text = mEdit.getText().toString();
String text = m_edit.getText().toString();
boolean haveText = null != text && 0 < text.length();
Utils.setItemVisible( menu, R.id.chat_menu_send, haveText );
return true;
@ -117,17 +183,26 @@ public class ChatDelegate extends DelegateBase {
}
break;
case R.id.chat_menu_send:
String text = mEdit.getText().toString();
String text = m_edit.getText().toString();
if ( null == text || text.length() == 0 ) {
setResult( Activity.RESULT_CANCELED );
finish();
} else {
DBUtils.appendChatHistory( m_activity, m_rowid, text, true );
DBUtils.appendChatHistory( m_activity, m_rowid, text, m_curPlayer );
addRow( text, m_curPlayer );
m_edit.setText( null );
Intent result = new Intent();
result.putExtra( BoardDelegate.INTENT_KEY_CHAT, text );
setResult( Activity.RESULT_OK, result );
m_jniThreadRef.sendChat( text );
// if ( null != jniThread ) {
// jniThread.handle( JNIThread.JNICmd.CMD_SENDCHAT, text );
// } else {
// Intent result = new Intent();
// result.putExtra( BoardDelegate.INTENT_KEY_CHAT, text );
// setResult( Activity.RESULT_OK, result );
// finish();
// }
}
finish();
// finish();
break;
default:
handled = false;
@ -143,8 +218,8 @@ public class ChatDelegate extends DelegateBase {
case CLEAR_ACTION:
if ( AlertDialog.BUTTON_POSITIVE == which ) {
DBUtils.clearChatHistory( m_activity, m_rowid );
LinearLayout layout =
(LinearLayout)findViewById( R.id.chat_history );
TableLayout layout =
(TableLayout)findViewById( R.id.chat_history );
layout.removeAllViews();
}
break;
@ -152,4 +227,37 @@ public class ChatDelegate extends DelegateBase {
super.dlgButtonClicked( action, which, params );
}
}
public static boolean append( long rowid, String msg, int fromIndx )
{
boolean handled = null != s_visibleThis
&& s_visibleThis.m_rowid == rowid;
if ( handled ) {
s_visibleThis.addRow( msg, fromIndx );
Utils.playNotificationSound( s_visibleThis.m_activity );
}
return handled;
}
public static void startForResult( Delegator delegator,
RequestCode requestCode,
long rowID, int curPlayer,
String[] names, boolean[] locs )
{
Assert.assertFalse( -1 == curPlayer );
Bundle bundle = new Bundle();
bundle.putLong( GameUtils.INTENT_KEY_ROWID, rowID );
bundle.putInt( INTENT_KEY_PLAYER, curPlayer );
bundle.putStringArray( INTENT_KEY_NAMES, names );
bundle.putBooleanArray( INTENT_KEY_LOCS, locs );
Activity activity = delegator.getActivity();
if ( activity instanceof FragActivity ) {
FragActivity.addFragment( new ChatFrag(), bundle, delegator );
} else {
Intent intent = new Intent( activity, ChatActivity.class );
intent.putExtras( bundle );
activity.startActivityForResult( intent, requestCode.ordinal() );
}
}
}

View file

@ -0,0 +1,31 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2016 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.os.Bundle;
public class ChatFrag extends XWFragment {
@Override
public void onCreate( Bundle sis )
{
super.onCreate( new ChatDelegate( this, sis ), sis, true );
}
}

View file

@ -20,25 +20,23 @@
package org.eehouse.android.xw4;
import android.content.Context;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.UnresolvedAddressException;
import java.nio.ByteBuffer;
import java.net.InetSocketAddress;
import java.util.Vector;
import java.util.Iterator;
import java.util.Vector;
import junit.framework.Assert;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.jni.JNIThread.*;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.JNIThread.*;
public class CommsTransport implements TransportProcs,
NetStateCache.StateChangedIf {
@ -49,7 +47,6 @@ public class CommsTransport implements TransportProcs,
private JNIThread m_jniThread;
private CommsThread m_thread;
private TransportProcs.TPMsgHandler m_tpHandler;
private Handler m_handler;
private boolean m_done = false;
private Vector<ByteBuffer> m_buffersOut;
@ -214,10 +211,9 @@ public class CommsTransport implements TransportProcs,
} // loop
}
public void setReceiver( JNIThread jnit, Handler handler )
public void setReceiver( JNIThread jnit )
{
m_jniThread = jnit;
m_handler = handler;
}
public void waitToStop()

View file

@ -385,8 +385,8 @@ public class ConnStatusHandler {
String as64 = XWPrefs.getPrefsString( context,
R.string.key_connstat_data );
if ( null != as64 && 0 < as64.length() ) {
byte[] bytes = XwJNI.base64Decode( as64 );
try {
byte[] bytes = XwJNI.base64Decode( as64 );
ObjectInputStream ois =
new ObjectInputStream( new ByteArrayInputStream(bytes) );
s_records =

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All
* Copyright 2009-2016 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
@ -42,8 +42,10 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String TABLE_NAME_LOC = "loc";
public static final String TABLE_NAME_PAIRS = "pairs";
public static final String TABLE_NAME_INVITES = "invites";
public static final String TABLE_NAME_CHAT = "chat";
public static final String TABLE_NAME_LOGS = "logs";
private static final String DB_NAME = "xwdb";
private static final int DB_VERSION = 25;
private static final int DB_VERSION = 27;
public static final String GAME_NAME = "GAME_NAME";
public static final String VISID = "VISID";
@ -111,6 +113,10 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String TARGET = "TARGET";
public static final String TIMESTAMP = "TIMESTAMP";
public static final String SENDER = "SENDER";
public static final String MESSAGE = "MESSAGE";
public static final String TAG = "TAG";
private Context m_context;
private static final String[][] s_summaryColsAndTypes = {
@ -206,6 +212,18 @@ public class DBHelper extends SQLiteOpenHelper {
,{ TIMESTAMP, "DATETIME DEFAULT CURRENT_TIMESTAMP" }
};
private static final String[][] s_chatsSchema = {
{ ROW, "INTEGER" }
,{ SENDER, "INTEGER" }
,{ MESSAGE, "TEXT" }
};
private static final String[][] s_logsSchema = {
{ TIMESTAMP, "DATETIME DEFAULT CURRENT_TIMESTAMP" },
{ MESSAGE, "TEXT" },
{ TAG, "TEXT" },
};
public DBHelper( Context context )
{
super( context, DB_NAME, null, DB_VERSION );
@ -230,13 +248,15 @@ public class DBHelper extends SQLiteOpenHelper {
createLocTable( db );
createPairsTable( db );
createInvitesTable( db );
createChatsTable( db );
createLogsTable( db );
}
@Override
@SuppressWarnings("fallthrough")
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion )
{
DbgUtils.logf( "onUpgrade: old: %d; new: %d", oldVersion, newVersion );
DbgUtils.logf( false, "onUpgrade: old: %d; new: %d", oldVersion, newVersion );
boolean madeSumTable = false;
switch( oldVersion ) {
@ -297,7 +317,11 @@ public class DBHelper extends SQLiteOpenHelper {
}
case 24:
createInvitesTable( db );
case 25:
createChatsTable( db );
case 26:
createLogsTable( db );
break;
default:
db.execSQL( "DROP TABLE " + TABLE_NAME_SUM + ";" );
@ -401,6 +425,16 @@ public class DBHelper extends SQLiteOpenHelper {
createTable( db, TABLE_NAME_INVITES, s_invitesSchema );
}
private void createChatsTable( SQLiteDatabase db )
{
createTable( db, TABLE_NAME_CHAT, s_chatsSchema );
}
private void createLogsTable( SQLiteDatabase db )
{
createTable( db, TABLE_NAME_LOGS, s_logsSchema );
}
// Move all existing games to the row previously named "cur games'
private void moveToCurGames( SQLiteDatabase db )
{
@ -426,7 +460,7 @@ public class DBHelper extends SQLiteOpenHelper {
db.beginTransaction();
try {
String query;
String[] columnNames = DBUtils.getColumns( db, name );
String[] columnNames = getColumns( db, name );
if ( null != columnNames ) { // no data means no need to copy
query = String.format( "ALTER table %s RENAME TO 'temp_%s'",
name, name );
@ -498,6 +532,15 @@ public class DBHelper extends SQLiteOpenHelper {
return result;
}
private static String[] getColumns( SQLiteDatabase db, String name )
{
String query = String.format( "SELECT * FROM %s LIMIT 1", name );
Cursor cursor = db.rawQuery( query, null );
String[] colNames = cursor.getColumnNames();
cursor.close();
return colNames;
}
private class TableAndVersion {
public String name;
public int addedVersion;

View file

@ -67,6 +67,9 @@ public class DBUtils {
public static final int GROUPID_UNSPEC = -1;
public static final String KEY_NEWGAMECOUNT = "DBUtils.newGameCount";
// how many log rows to keep? (0 means off)
private static final int LOGLIMIT = 0;
private static final String DICTS_SEP = ",";
private static final String ROW_ID = "rowid";
@ -96,13 +99,13 @@ public class DBUtils {
}
public static class HistoryPair {
private HistoryPair( String p_msg, boolean p_sourceLocal )
private HistoryPair( String p_msg, int p_playerIndx )
{
msg = p_msg;
sourceLocal = p_sourceLocal;
playerIndx = p_playerIndx;
}
String msg;
boolean sourceLocal;
int playerIndx;
}
public static class DictBrowseState {
@ -118,10 +121,14 @@ public class DBUtils {
long maxMillis )
{
GameSummary result = null;
GameLock lock = new GameLock( rowid, false ).lock( maxMillis );
if ( null != lock ) {
result = getSummary( context, lock );
lock.unlock();
try {
GameLock lock = new GameLock( rowid, false ).lock( maxMillis );
if ( null != lock ) {
result = getSummary( context, lock );
lock.unlock();
}
} catch ( GameLock.GameLockedException gle ) {
DbgUtils.loge( gle );
}
return result;
}
@ -595,7 +602,7 @@ public class DBUtils {
}
private static void setInt( long rowid, String column, int value )
private static void setSummaryInt( long rowid, String column, int value )
{
ContentValues values = new ContentValues();
values.put( column, value );
@ -604,13 +611,13 @@ public class DBUtils {
public static void setMsgFlags( long rowid, int flags )
{
setInt( rowid, DBHelper.HASMSGS, flags );
setSummaryInt( rowid, DBHelper.HASMSGS, flags );
notifyListeners( rowid, GameChangeType.GAME_CHANGED );
}
public static void setExpanded( long rowid, boolean expanded )
{
setInt( rowid, DBHelper.CONTRACTED, expanded?0:1 );
setSummaryInt( rowid, DBHelper.CONTRACTED, expanded?0:1 );
}
private static int getInt( Context context, long rowid, String column,
@ -1107,8 +1114,9 @@ public class DBUtils {
public static void deleteGame( Context context, GameLock lock )
{
Assert.assertTrue( lock.canWrite() );
String selSummaries = String.format( ROW_ID_FMT, lock.getRowid() );
String selInvites = String.format( "%s=%d", DBHelper.ROW, lock.getRowid() );
long rowid = lock.getRowid();
String selSummaries = String.format( ROW_ID_FMT, rowid );
String selInvites = String.format( "%s=%d", DBHelper.ROW, rowid );
initDB( context );
synchronized( s_dbHelper ) {
@ -1117,6 +1125,9 @@ public class DBUtils {
// Delete invitations too
db.delete( DBHelper.TABLE_NAME_INVITES, selInvites, null );
// Delete chats too -- same sel as for invites
db.delete( DBHelper.TABLE_NAME_CHAT, selInvites, null );
db.close();
}
@ -1176,21 +1187,92 @@ public class DBUtils {
updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values );
}
public static HistoryPair[] getChatHistory( Context context, long rowid )
private static HistoryPair[] convertChatString( Context context, long rowid,
boolean[] playersLocal )
{
HistoryPair[] result = null;
String oldHistory = getChatHistoryStr( context, rowid );
if ( null != oldHistory ) {
DbgUtils.logdf( "convertChatString(): got string: %s", oldHistory );
ArrayList<ContentValues> valuess = new ArrayList<ContentValues>();
ArrayList<HistoryPair> pairs = new ArrayList<HistoryPair>();
String localPrefix = LocUtils.getString( context, R.string.chat_local_id );
String rmtPrefix = LocUtils.getString( context, R.string.chat_other_id );
DbgUtils.logdf( "convertChatString(): prefixes: \"%s\" and \"%s\"", localPrefix, rmtPrefix );
String[] msgs = oldHistory.split( "\n" );
DbgUtils.logdf( "convertChatString(): split into %d", msgs.length );
int localPlayerIndx = -1;
int remotePlayerIndx = -1;
for ( int ii = playersLocal.length - 1; ii >= 0; --ii ) {
if ( playersLocal[ii] ) {
localPlayerIndx = ii;
} else {
remotePlayerIndx = ii;
}
}
for ( String msg : msgs ) {
DbgUtils.logdf( "convertChatString(): msg: %s", msg );
int indx = -1;
String prefix = null;
if ( msg.startsWith( localPrefix ) ) {
DbgUtils.logdf( "convertChatString(): msg: %s starts with %s", msg, localPrefix );
prefix = localPrefix;
indx = localPlayerIndx;
} else if ( msg.startsWith( rmtPrefix ) ) {
DbgUtils.logdf( "convertChatString(): msg: %s starts with %s", msg, rmtPrefix );
prefix = rmtPrefix;
indx = remotePlayerIndx;
} else {
DbgUtils.logdf( "convertChatString(): msg: %s starts with neither", msg );
}
if ( -1 != indx ) {
DbgUtils.logdf( "convertChatString(): removing substring %s; was: %s", prefix, msg );
msg = msg.substring( prefix.length(), msg.length() );
DbgUtils.logdf( "convertChatString(): removED substring; now %s", msg );
valuess.add( cvForChat( rowid, msg, indx ) );
HistoryPair pair = new HistoryPair(msg, indx );
pairs.add( pair );
}
}
result = pairs.toArray( new HistoryPair[pairs.size()] );
appendChatHistory( context, valuess );
// clearChatHistoryString( context, rowid );
}
return result;
}
public static HistoryPair[] getChatHistory( Context context, long rowid,
boolean[] playersLocal )
{
HistoryPair[] result = null;
if ( BuildConstants.CHAT_SUPPORTED ) {
final String localPrefix =
LocUtils.getString( context, R.string.chat_local_id );
String history = getChatHistoryStr( context, rowid );
if ( null != history ) {
String[] msgs = history.split( "\n" );
result = new HistoryPair[msgs.length];
for ( int ii = 0; ii < result.length; ++ii ) {
String msg = msgs[ii];
boolean isLocal = msg.startsWith( localPrefix );
result[ii] = new HistoryPair( msg, isLocal );
String[] columns = { DBHelper.SENDER, DBHelper.MESSAGE };
String selection = String.format( "%s=%d", DBHelper.ROW, rowid );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_CHAT, columns,
selection, null, null, null, null );
if ( 0 < cursor.getCount() ) {
result = new HistoryPair[cursor.getCount()];
int msgIndex = cursor.getColumnIndex( DBHelper.MESSAGE );
int plyrIndex = cursor.getColumnIndex( DBHelper.SENDER );
for ( int ii = 0; cursor.moveToNext(); ++ii ) {
String msg = cursor.getString( msgIndex );
int plyr = cursor.getInt( plyrIndex );
HistoryPair pair = new HistoryPair(msg, plyr );
result[ii] = pair;
}
}
cursor.close();
db.close();
}
if ( null == result ) {
result = convertChatString( context, rowid, playersLocal );
}
}
return result;
@ -1674,26 +1756,57 @@ public class DBUtils {
return result;
}
public static void appendChatHistory( Context context, long rowid,
String msg, boolean local )
private static void appendChatHistory( Context context,
ArrayList<ContentValues> valuess )
{
if ( BuildConstants.CHAT_SUPPORTED ) {
Assert.assertNotNull( msg );
int id = local ? R.string.chat_local_id : R.string.chat_other_id;
msg = LocUtils.getString( context, id ) + msg;
String cur = getChatHistoryStr( context, rowid );
if ( null != cur ) {
msg = cur + "\n" + msg;
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
for ( ContentValues values : valuess ) {
db.insert( DBHelper.TABLE_NAME_CHAT, null, values );
}
saveChatHistory( context, rowid, msg );
db.close();
}
}
private static ContentValues cvForChat( long rowid, String msg, int plyr )
{
ContentValues values = new ContentValues();
values.put( DBHelper.ROW, rowid );
values.put( DBHelper.MESSAGE, msg );
values.put( DBHelper.SENDER, plyr );
return values;
}
public static void appendChatHistory( Context context, long rowid,
String msg, int fromPlayer )
{
Assert.assertNotNull( msg );
Assert.assertFalse( -1 == fromPlayer );
ArrayList<ContentValues> valuess = new ArrayList<ContentValues>();
valuess.add( cvForChat( rowid, msg, fromPlayer ) );
appendChatHistory( context, valuess );
DbgUtils.logf( "appendChatHistory: inserted \"%s\" from player %d",
msg, fromPlayer );
} // appendChatHistory
public static void clearChatHistory( Context context, long rowid )
{
saveChatHistory( context, rowid, null );
String selection = String.format( "%s = %d", DBHelper.ROW, rowid );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
db.delete( DBHelper.TABLE_NAME_CHAT, selection, null );
// for now, remove any old-format history too. Later when it's
// removed once converted (after that process is completely
// debugged), this can be removed.
ContentValues values = new ContentValues();
values.putNull( DBHelper.CHAT_HISTORY );
updateRowImpl( db, DBHelper.TABLE_NAME_SUM, rowid, values );
db.close();
}
}
public static void setDBChangeListener( DBChangeListener listener )
@ -1960,15 +2073,6 @@ public class DBUtils {
return exists;
}
public static String[] getColumns( SQLiteDatabase db, String name )
{
String query = String.format( "SELECT * FROM %s LIMIT 1", name );
Cursor cursor = db.rawQuery( query, null );
String[] colNames = cursor.getColumnNames();
cursor.close();
return colNames;
}
public static void addToStudyList( Context context, String word,
int lang )
{
@ -2304,6 +2408,37 @@ public class DBUtils {
return bytes;
}
public static void appendLog( String tag, String msg )
{
Context context = XWApp.getContext();
if ( null != context ) {
appendLog( context, msg );
}
}
private static void appendLog( Context context, String msg )
{
if ( 0 < LOGLIMIT ) {
ContentValues values = new ContentValues();
values.put( DBHelper.MESSAGE, msg );
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
long rowid = db.insert( DBHelper.TABLE_NAME_LOGS, null, values );
if ( 0 == (rowid % (LOGLIMIT / 10)) ) {
String where =
String.format( "not rowid in (select rowid from %s order by TIMESTAMP desc limit %d)",
DBHelper.TABLE_NAME_LOGS, LOGLIMIT );
int nGone = db.delete( DBHelper.TABLE_NAME_LOGS, where, null );
DbgUtils.logf( false, "appendLog(): deleted %d rows", nGone );
}
db.close();
}
}
}
private static void copyGameDB( Context context, boolean toSDCard )
{
String name = DBHelper.getDBName();
@ -2338,20 +2473,12 @@ public class DBUtils {
BuildConstants.VARIANT );
}
// Chat is independent of the GameLock mechanism because it's not
// touching the SNAPSHOT column.
private static void saveChatHistory( Context context, long rowid,
String history )
{
ContentValues values = new ContentValues();
if ( null != history ) {
values.put( DBHelper.CHAT_HISTORY, history );
} else {
values.putNull( DBHelper.CHAT_HISTORY );
}
values.put( DBHelper.LASTPLAY_TIME, new Date().getTime() );
updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values );
}
// private static void clearChatHistoryString( Context context, long rowid )
// {
// ContentValues values = new ContentValues();
// values.putNull( DBHelper.CHAT_HISTORY );
// updateRow( context, DBHelper.TABLE_NAME_SUM, rowid, values );
// }
private static void initDB( Context context )
{
@ -2363,16 +2490,21 @@ public class DBUtils {
}
}
private static int updateRowImpl( SQLiteDatabase db, String table,
long rowid, ContentValues values )
{
String selection = String.format( ROW_ID_FMT, rowid );
return db.update( table, values, selection, null );
}
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();
int result = db.update( table, values, selection, null );
int result = updateRowImpl( db, table, rowid, values );
db.close();
if ( 0 == result ) {
DbgUtils.logf( "updateRow failed" );

View file

@ -26,6 +26,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.preference.PreferenceManager;
@ -60,21 +61,40 @@ public class DbgUtils {
logEnable( on );
}
public static void logf( String msg )
public static void logf( String msg )
{
logf( true, msg );
}
public static void logf( boolean persist, String msg )
{
if ( s_doLog ) {
s_time.setToNow();
String time = s_time.format("[%H:%M:%S]");
String time = "";
// No need for timestamp on marshmallow, as the OS provides it
if ( true /*Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1*/ ) {
s_time.setToNow();
time = s_time.format("[%H:%M:%S]-");
}
long id = Thread.currentThread().getId();
Log.d( TAG, time + "-" + id + "-" + msg );
msg = time + id + "-" + msg;
Log.d( TAG, msg );
if ( persist && BuildConfig.DEBUG ) {
DBUtils.appendLog( TAG, msg );
}
}
} // logf
public static void logf( String format, Object... args )
{
logf( true, format, args );
}
public static void logf( boolean persist, String format, Object... args )
{
if ( s_doLog ) {
Formatter formatter = new Formatter();
logf( formatter.format( format, args ).toString() );
logf( persist, formatter.format( format, args ).toString() );
}
} // logf

View file

@ -19,14 +19,15 @@
package org.eehouse.android.xw4;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.graphics.Rect;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.os.Bundle;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -55,6 +56,7 @@ public class DelegateBase implements DlgClickNotify,
private int m_optionsMenuID;
private int m_layoutID;
private View m_rootView;
private boolean m_isVisible;
public DelegateBase( Delegator delegator, Bundle bundle, int layoutID )
{
@ -86,6 +88,23 @@ public class DelegateBase implements DlgClickNotify,
protected void onDestroy() {}
protected void onWindowFocusChanged( boolean hasFocus ) {}
protected boolean onBackPressed() { return false; }
public void orientationChanged() {}
protected void requestWindowFeature( int feature ) {}
// Fragments only
protected View inflateView( LayoutInflater inflater, ViewGroup container )
{
View view = null;
int layoutID = getLayoutID();
if ( 0 < layoutID ) {
view = inflater.inflate( layoutID, container, false );
LocUtils.xlateView( m_activity, view );
setContentView( view );
}
return view;
}
protected void onActivityResult( RequestCode requestCode, int resultCode,
Intent data )
{
@ -94,11 +113,13 @@ public class DelegateBase implements DlgClickNotify,
protected void onResume()
{
m_isVisible = true;
XWService.setListener( this );
}
protected void onPause()
{
m_isVisible = false;
XWService.setListener( null );
}
@ -131,6 +152,8 @@ public class DelegateBase implements DlgClickNotify,
return m_activity.getIntent();
}
protected Delegator getDelegator() { return m_delegator; }
protected int getLayoutID()
{
return m_layoutID;
@ -187,7 +210,11 @@ public class DelegateBase implements DlgClickNotify,
protected void setResult( int result, Intent intent )
{
m_activity.setResult( result, intent );
if ( m_activity instanceof FragActivity ) {
Assert.fail();
} else {
m_activity.setResult( result, intent );
}
}
protected void setResult( int result )
@ -207,7 +234,34 @@ public class DelegateBase implements DlgClickNotify,
protected void finish()
{
m_activity.finish();
if ( m_activity instanceof FragActivity ) {
((FragActivity)m_activity).finishFragment();
} else {
m_activity.finish();
}
}
protected boolean isPortrait()
{
int[] containerDims = getContainerDims( new int[2] );
boolean result = containerDims[0] < containerDims[1];
DbgUtils.logdf( "%s.isPortrait() => %b", getClass().getName(), result );
return result;
}
protected int[] getContainerDims( int[] outDims )
{
if ( m_activity instanceof FragActivity ) {
((FragActivity)m_activity).getFragmentDims( outDims );
} else {
Rect rect = new Rect();
m_rootView.getWindowVisibleDisplayFrame( rect );
outDims[0] = rect.width();
outDims[1] = rect.height();
}
DbgUtils.logdf( "%s.getContainerDims(): width => %d, height => %d",
getClass().getName(), outDims[0], outDims[1] );
return outDims;
}
protected String getString( int resID, Object... params )
@ -523,6 +577,8 @@ public class DelegateBase implements DlgClickNotify,
{
m_dlgDelegate.showSMSEnableDialog( action, params );
}
protected boolean isVisible() { return m_isVisible; }
//////////////////////////////////////////////////
// MultiService.MultiEventListener interface
@ -541,7 +597,7 @@ public class DelegateBase implements DlgClickNotify,
case BAD_PROTO_SMS:
fmtId = R.string.sms_bad_proto_fmt;
break;
case APP_NOT_FOUND:
case APP_NOT_FOUND_BT:
fmtId = R.string.app_not_found_fmt;
break;
case RELAY_ALERT:

View file

@ -163,14 +163,15 @@ public class DictBrowseDelegate extends ListDelegateBase
protected void init( Bundle savedInstanceState )
{
Intent intent = getIntent();
String name = null == intent? null:intent.getStringExtra( DICT_NAME );
Bundle args = getArguments();
String name = null == args? null : args.getString( DICT_NAME );
Assert.assertNotNull( name );
if ( null == name ) {
finish();
} else {
m_name = name;
m_loc =
DictUtils.DictLoc.values()[intent.getIntExtra( DICT_LOC, 0 )];
DictUtils.DictLoc.values()[args.getInt( DICT_LOC, 0 )];
m_lang = DictLangCache.getDictLangCode( m_activity, name );
String[] names = { name };
@ -363,9 +364,9 @@ public class DictBrowseDelegate extends ListDelegateBase
DBUtils.dictsSetOffset( m_activity, m_name, m_loc, m_browseState );
m_browseState = null;
startActivity( getIntent() );
finish(); // pop fragment stack before adding new (only it doesn't work)
finish();
launch( getDelegator(), getArguments() );
}
}
@ -421,19 +422,31 @@ public class DictBrowseDelegate extends ListDelegateBase
m_maxSpinner.setOnItemSelectedListener( this );
}
private static void launch( Delegator delegator, Bundle bundle )
{
Activity activity = delegator.getActivity();
if ( activity instanceof FragActivity ) {
FragActivity.addFragment( new DictBrowseFrag(), bundle, delegator );
} else {
Intent intent = new Intent( activity, DictBrowseActivity.class );
intent.putExtras( bundle );
activity.startActivity( intent );
}
}
public static void launch( Context caller, String name,
public static void launch( Delegator delegator, String name,
DictUtils.DictLoc loc )
{
Intent intent = new Intent( caller, DictBrowseActivity.class );
intent.putExtra( DICT_NAME, name );
intent.putExtra( DICT_LOC, loc.ordinal() );
caller.startActivity( intent );
Bundle bundle = new Bundle();
bundle.putString( DICT_NAME, name );
bundle.putInt( DICT_LOC, loc.ordinal() );
launch( delegator, bundle );
}
public static void launch( Context caller, String name )
public static void launch( Delegator delegator, String name )
{
DictUtils.DictLoc loc = DictUtils.getDictLoc( caller, name );
launch( caller, name, loc );
DictUtils.DictLoc loc
= DictUtils.getDictLoc( delegator.getActivity(), name );
launch( delegator, name, loc );
}
}

View file

@ -0,0 +1,31 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2016 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.os.Bundle;
public class DictBrowseFrag extends XWFragment {
@Override
public void onCreate( Bundle sis )
{
super.onCreate( new DictBrowseDelegate( this, sis ), sis, true );
}
}

View file

@ -26,7 +26,6 @@ import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.PopupMenu;
import java.util.HashMap;
@ -35,11 +34,6 @@ import org.eehouse.android.xw4.loc.LocUtils;
public class DictsActivity extends XWActivity {
private static interface SafePopup {
public void doPopup( Context context, View button,
String curDict, int lang );
}
private static SafePopup s_safePopup = null;
// I can't provide a subclass of MenuItem to hold DictAndLoc, so
// settle for a hash on the side.
private DictsDelegate m_dlgt;
@ -51,71 +45,6 @@ public class DictsActivity extends XWActivity {
super.onCreate( savedInstanceState, m_dlgt );
} // onCreate
@Override
public void onBackPressed() {
if ( !m_dlgt.onBackPressed() ) {
super.onBackPressed();
}
}
private static class SafePopupImpl implements SafePopup {
public void doPopup( final Context context, View button,
String curDict, int lang ) {
final HashMap<MenuItem, DictAndLoc> itemData
= new HashMap<MenuItem, DictAndLoc>();
MenuItem.OnMenuItemClickListener listener =
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick( MenuItem item )
{
DictAndLoc dal = itemData.get( item );
DictBrowseDelegate.launch( context, dal.name,
dal.loc );
return true;
}
};
PopupMenu popup = new PopupMenu( context, button );
Menu menu = popup.getMenu();
// Add at top but save until have dal info
MenuItem curItem =
menu.add( LocUtils.getString( context,
R.string.cur_menu_marker_fmt,
curDict ) );
DictAndLoc[] dals = DictLangCache.getDALsHaveLang( context, lang );
for ( DictAndLoc dal : dals ) {
MenuItem item = dal.name.equals(curDict)
? curItem : menu.add( dal.name );
item.setOnMenuItemClickListener( listener );
itemData.put( item, dal );
}
popup.show();
}
}
public static boolean handleDictsPopup( Context context, View button,
String curDict, int lang )
{
int nDicts = DictLangCache.getLangCount( context, lang );
if ( null == s_safePopup && 1 < nDicts ) {
int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK );
if ( 11 <= sdkVersion ) {
s_safePopup = new SafePopupImpl();
}
}
boolean canHandle = null != s_safePopup && 1 < nDicts;
if ( canHandle ) {
s_safePopup.doPopup( context, button, curDict, lang );
}
return canHandle;
}
public static void start( Context context )
{
Intent intent = new Intent( context, DictsActivity.class );

View file

@ -44,6 +44,7 @@ import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
import java.net.HttpURLConnection;
@ -107,6 +108,53 @@ public class DictsDelegate extends ListDelegateBase
private String m_lastDict;
private String m_noteNone;
private static interface SafePopup {
public void doPopup( Delegator dlgtor, View button,
String curDict, int lang );
}
private static SafePopup s_safePopup = null;
private static class SafePopupImpl implements SafePopup {
public void doPopup( final Delegator dlgtor, View button,
String curDict, int lang ) {
final HashMap<MenuItem, DictAndLoc> itemData
= new HashMap<MenuItem, DictAndLoc>();
MenuItem.OnMenuItemClickListener listener =
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick( MenuItem item )
{
DictAndLoc dal = itemData.get( item );
DictBrowseDelegate.launch( dlgtor, dal.name,
dal.loc );
return true;
}
};
Context context = dlgtor.getActivity();
PopupMenu popup = new PopupMenu( context, button );
Menu menu = popup.getMenu();
// Add at top but save until have dal info
MenuItem curItem =
menu.add( LocUtils.getString( context,
R.string.cur_menu_marker_fmt,
curDict ) );
DictAndLoc[] dals = DictLangCache.getDALsHaveLang( context, lang );
for ( DictAndLoc dal : dals ) {
MenuItem item = dal.name.equals(curDict)
? curItem : menu.add( dal.name );
item.setOnMenuItemClickListener( listener );
itemData.put( item, dal );
}
popup.show();
}
}
private static class DictInfo implements Comparable {
public String m_name;
public String m_lang;
@ -518,7 +566,7 @@ public class DictsDelegate extends ListDelegateBase
switchShowingRemote( m_checkbox.isChecked() );
} else {
XWListItem item = (XWListItem)view;
DictBrowseDelegate.launch( m_activity, item.getText(),
DictBrowseDelegate.launch( getDelegator(), item.getText(),
(DictLoc)item.getCached() );
}
}
@ -1017,6 +1065,25 @@ public class DictsDelegate extends ListDelegateBase
new GetDefaultDictTask( context, lc, lstnr ).execute();
}
public static boolean handleDictsPopup( Delegator delegator, View button,
String curDict, int lang )
{
Context context = delegator.getActivity();
int nDicts = DictLangCache.getLangCount( context, lang );
if ( null == s_safePopup && 1 < nDicts ) {
int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK );
if ( 11 <= sdkVersion ) {
s_safePopup = new SafePopupImpl();
}
}
boolean canHandle = null != s_safePopup && 1 < nDicts;
if ( canHandle ) {
s_safePopup.doPopup( delegator, button, curDict, lang );
}
return canHandle;
}
//////////////////////////////////////////////////////////////////////
// XWListItem.ExpandedListener interface
//////////////////////////////////////////////////////////////////////

View file

@ -38,11 +38,13 @@ import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.ref.WeakReference;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import junit.framework.Assert;
@ -153,6 +155,8 @@ public class DlgDelegate {
void showNotAgainDlgThen( int msgID, int prefsKey, Action action );
}
private static Map<Integer, WeakReference<DelegateBase>> s_pendings
= new HashMap<Integer, WeakReference<DelegateBase>>();
private Activity m_activity;
private DelegateBase m_dlgt;
private DlgClickNotify m_clickCallback;
@ -201,8 +205,12 @@ public class DlgDelegate {
protected void showDialog( DlgID dlgID )
{
int id = dlgID.ordinal();
if ( m_activity instanceof FragActivity ) {
s_pendings.put( id, new WeakReference<DelegateBase>(m_dlgt) );
}
if ( !m_activity.isFinishing() ) {
m_activity.showDialog( dlgID.ordinal() );
m_activity.showDialog( id );
}
}
@ -872,6 +880,26 @@ public class DlgDelegate {
m_dlgStates.put( state.m_id, state );
}
public static Dialog onCreateDialog( int id )
{
Dialog result = null;
WeakReference<DelegateBase> ref = s_pendings.get( id );
DelegateBase dlgt = ref.get();
if ( null != dlgt ) {
result = dlgt.onCreateDialog( id );
}
return result;
}
public static void onPrepareDialog( int id, Dialog dialog )
{
WeakReference<DelegateBase> ref = s_pendings.get( id );
DelegateBase dlgt = ref.get();
if ( null != dlgt ) {
dlgt.prepareDialog( DlgID.values()[id], dialog );
}
}
private String getString( int id, Object... params )
{
return m_dlgt.getString( id, params );

View file

@ -30,7 +30,10 @@ public class DwnldActivity extends XWActivity {
protected void onCreate( Bundle savedInstanceState )
{
requestWindowFeature( Window.FEATURE_NO_TITLE );
requestWindowFeature( Window.FEATURE_LEFT_ICON );
getWindow().setFeatureDrawableResource( Window.FEATURE_LEFT_ICON,
R.drawable.icon48x48 );
DwnldDelegate dlgt =
new DwnldDelegate( this, savedInstanceState );

View file

@ -31,6 +31,10 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import junit.framework.Assert;
public class ExpiringDelegate {
@ -48,7 +52,6 @@ public class ExpiringDelegate {
private int m_backPct = -1;
private Drawable m_back = null;
private boolean m_doFrame = false;
private Handler m_handler;
private boolean m_haveTurnLocal = false;
private long m_startSecs;
private Runnable m_runnable = null;
@ -59,12 +62,79 @@ public class ExpiringDelegate {
private static float[] s_points;
private DrawSelDelegate m_dsdel;
// Combine all the timers into one. Since WeakReferences to the same
// object aren't equal we need a separate set of their hash codes to
// prevent storing duplicates.
private static class ExpUpdater implements Runnable {
private Handler m_handler;
private ArrayList<WeakReference<ExpiringDelegate>> m_refs
= new ArrayList<WeakReference<ExpiringDelegate>>();
private Set<Integer> m_hashes = new HashSet<Integer>();
public void run() {
int sizeBefore;
ArrayList<ExpiringDelegate> dlgts = new ArrayList<ExpiringDelegate>();
synchronized( this ) {
sizeBefore = m_refs.size();
m_hashes.clear();
Iterator<WeakReference<ExpiringDelegate>> iter = m_refs.iterator();
while ( iter.hasNext() ) {
WeakReference<ExpiringDelegate> ref = iter.next();
ExpiringDelegate dlgt = ref.get();
if ( null == dlgt/* || dlgts.contains( dlgt )*/ ) {
iter.remove();
} else {
dlgts.add(dlgt);
m_hashes.add( dlgt.hashCode() );
}
}
}
DbgUtils.logdf( "ExpUpdater: ref had %d refs, now has %d expiringdelegate views",
sizeBefore, dlgts.size() );
for ( ExpiringDelegate dlgt : dlgts ) {
dlgt.timerFired();
}
reschedule();
}
private void reschedule()
{
m_handler.postDelayed( this, INTERVAL_SECS * 1000 / 100 );
}
private void add( ExpiringDelegate self )
{
int hash = self.hashCode();
synchronized( this ) {
if ( ! m_hashes.contains( hash ) ) {
m_hashes.add( hash );
m_refs.add( new WeakReference<ExpiringDelegate>(self) );
}
}
}
private void setHandler( Handler handler )
{
if ( handler != m_handler ) {
DbgUtils.logdf( "handler changing from %H to %H",
m_handler, handler );
m_handler = handler;
reschedule();
}
}
}
private static ExpUpdater s_updater;
static {
s_rect = new Rect();
s_paint = new Paint();
s_paint.setStyle(Paint.Style.STROKE);
s_paint.setStrokeWidth( 1 );
s_points = new float[4*6];
s_updater = new ExpUpdater();
}
public ExpiringDelegate( Context context, View view )
@ -76,7 +146,7 @@ public class ExpiringDelegate {
public void setHandler( Handler handler )
{
m_handler = handler;
s_updater.setHandler( handler );
}
public void configure( boolean haveTurn, boolean haveTurnLocal,
@ -204,54 +274,22 @@ public class ExpiringDelegate {
m_pct = 100;
} else if ( m_pct < 0 ) {
m_pct = 0;
} 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 );
} else {
s_updater.add( this );
}
}
}
private Runnable mkRunnable()
private void timerFired()
{
if ( null == m_runnable ) {
m_runnable = mkRunnable( this );
if ( m_active ) {
figurePct();
if ( m_haveTurnLocal ) {
m_back = null;
setBackground();
}
m_view.invalidate();
}
return m_runnable;
}
private static Runnable mkRunnable( ExpiringDelegate self )
{
final WeakReference<ExpiringDelegate> selfRef
= new WeakReference<ExpiringDelegate>( self );
return new Runnable() {
public void run() {
ExpiringDelegate dlgt = selfRef.get();
if ( null != dlgt ) {
if ( XWApp.DEBUG_EXP_TIMERS ) {
DbgUtils.logf( "ExpiringDelegate: timer fired"
+ " for %H", this );
}
if ( dlgt.m_active ) {
dlgt.figurePct();
if ( dlgt.m_haveTurnLocal ) {
dlgt.m_back = null;
dlgt.setBackground();
}
if ( XWApp.DEBUG_EXP_TIMERS ) {
DbgUtils.logf( "ExpiringDelegate: invalidating"
+ " view %H", dlgt.m_view );
}
dlgt.m_view.invalidate();
}
} else {
DbgUtils.logf( "ExpiringDelegate.run(): reference gc'd" );
}
}
};
}
}

View file

@ -0,0 +1,309 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2014-2016 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.graphics.Rect;
import android.app.Dialog;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentManager.BackStackEntry;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.LinearLayout;
import android.widget.LinearLayout;
import junit.framework.Assert;
public class FragActivity extends FragmentActivity
implements FragmentManager.OnBackStackChangedListener {
private static final int MAX_PANES_LANDSCAPE = 3;
public interface OrientChangeListener {
void orientationChanged();
}
private static FragActivity s_this;
private LinearLayout m_root;
private int m_nextID = 0x00FFFFFF;
private int m_maxPanes;
private Boolean m_isPortrait;
@Override
public void onCreate( Bundle savedInstanceState )
{
s_this = this;
super.onCreate( savedInstanceState );
setContentView( R.layout.fragact );
m_root = (LinearLayout)findViewById( R.id.main_container );
getSupportFragmentManager().addOnBackStackChangedListener( this );
m_maxPanes = maxPanes();
// Nothing to do if we're restarting
if ( savedInstanceState == null ) {
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
addFragmentImpl( new GamesListFrag(), getIntent().getExtras(), null );
}
}
@Override
public void onBackPressed()
{
DbgUtils.logf( "FragActivity.onBackPressed()" );
super.onBackPressed();
}
@Override
public void onConfigurationChanged( Configuration newConfig )
{
Rect rect = new Rect();
m_root.getWindowVisibleDisplayFrame( rect );
boolean isPortrait
= Configuration.ORIENTATION_PORTRAIT == newConfig.orientation;
DbgUtils.logf( "FragActivity.onConfigurationChanged(isPortrait=%b)",
isPortrait );
m_isPortrait = isPortrait;
if ( isPortrait != (rect.width() <= rect.height()) ) {
DbgUtils.logdf( "FragActivity.onConfigurationChanged(): isPortrait:"
+ " %b; width: %d; height: %d",
isPortrait, rect.width(), rect.height() );
}
int maxPanes = isPortrait? 1 : MAX_PANES_LANDSCAPE;
if ( m_maxPanes != maxPanes ) {
m_maxPanes = maxPanes;
setVisiblePanes();
}
tellOrientationChanged();
super.onConfigurationChanged( newConfig );
}
protected void getFragmentDims( int[] dims )
{
Rect rect = new Rect();
m_root.getWindowVisibleDisplayFrame( rect );
int width = rect.width();
int height = rect.height();
if ( null != m_isPortrait && m_isPortrait && height < width ) {
int tmp = width;
width = height;
height = tmp;
}
dims[0] = width / Math.min( m_maxPanes, m_root.getChildCount() );
dims[1] = height;
}
@Override
protected Dialog onCreateDialog( int id )
{
return DlgDelegate.onCreateDialog( id );
}
@Override
protected void onPrepareDialog( int id, Dialog dialog )
{
DlgDelegate.onPrepareDialog( id, dialog );
}
//////////////////////////////////////////////////////////////////////
// FragmentManager.OnBackStackChangedListener
//////////////////////////////////////////////////////////////////////
public void onBackStackChanged()
{
DbgUtils.logf( "FragActivity.onBackStackChanged()" );
// make sure the right-most are visible
int fragCount = getSupportFragmentManager().getBackStackEntryCount();
if ( 0 == fragCount ) {
finish();
} else if ( fragCount == m_root.getChildCount() - 1 ) {
m_root.removeViewAt( fragCount );
setVisiblePanes();
}
}
// public void launchDictFrag( Bundle args )
// {
// // DictBrowseFrag dbf = new DictBrowseFrag();
// // dbf.setArguments( args );
// // addFragment( dbf );
// }
protected void finishFragment()
{
popFragment( null );
}
protected void popFragment( Fragment frag )
{
getSupportFragmentManager().popBackStack();
}
private void addFragmentImpl( Fragment fragment, Bundle bundle,
Delegator parent )
{
fragment.setArguments( bundle );
addFragmentImpl( fragment, parent );
}
private void addFragmentImpl( Fragment fragment, Delegator delegator )
{
String newName = fragment.getClass().getName();
boolean replace = false;
FragmentManager fm = getSupportFragmentManager();
int fragCount = fm.getBackStackEntryCount();
int containerCount = m_root.getChildCount();
DbgUtils.logf( "fragCount: %d; containerCount: %d", fragCount, containerCount );
// Assert.assertTrue( fragCount == containerCount );
// Replace IF we're adding something of the same class at right OR if
// we're adding something with the existing left pane as its parent
// (delegator)
if ( 0 < fragCount ) {
FragmentManager.BackStackEntry entry = fm.getBackStackEntryAt( fragCount - 1 );
String curName = entry.getName();
DbgUtils.logf( "name of last entry: %s", curName );
replace = curName.equals( newName );
if ( !replace && 1 < fragCount ) {
entry = fm.getBackStackEntryAt( fragCount - 2 );
curName = entry.getName();
String delName = delegator.getClass().getName();
DbgUtils.logf( "comparing %s, %s", curName, delName );
replace = curName.equals( delName );
}
if ( replace ) {
fm.popBackStack();
}
}
// Replace doesn't seem to work with generated IDs, so we'll create a
// new FrameLayout each time. If we're replacing, we'll replace the
// current rightmost FrameLayout. Otherwise we'll add a new one.
FrameLayout cont = new FrameLayout( this );
cont.setLayoutParams( new LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) );
int id = --m_nextID;
cont.setId( id );
m_root.addView( cont, replace ? containerCount - 1 : containerCount );
if ( !replace && containerCount >= m_maxPanes ) {
int indx = containerCount - m_maxPanes;
View child = m_root.getChildAt( indx );
child.setVisibility( View.GONE );
setMenuVisibility( child, false );
DbgUtils.logf( "hiding %dth container", indx );
}
fm.beginTransaction()
.add( id, fragment )
.addToBackStack( newName )
.commit();
// fm.executePendingTransactions();
}
private void setVisiblePanes()
{
// hide all but the right-most m_maxPanes children
int nPanes = m_root.getChildCount();
for ( int ii = 0; ii < nPanes; ++ii ) {
View child = m_root.getChildAt( ii );
boolean visible = ii >= nPanes - m_maxPanes;
DbgUtils.logf( "pane %d: visible=%b", ii, visible );
child.setVisibility( visible ? View.VISIBLE : View.GONE );
setMenuVisibility( child, visible );
}
}
private void setMenuVisibility( View cont, boolean visible )
{
FrameLayout layout = (FrameLayout)cont;
FragmentManager fm = getSupportFragmentManager();
int hidingId = layout.getId();
Fragment frag = fm.findFragmentById( hidingId );
if ( null != frag ) { // hasn't been popped?
frag.setMenuVisibility( visible );
}
}
// Walk all Fragment children and if they care notify of change.
private void tellOrientationChanged()
{
FragmentManager fm = getSupportFragmentManager();
int nPanes = m_root.getChildCount();
for ( int ii = 0; ii < nPanes; ++ii ) {
FrameLayout frame = (FrameLayout)m_root.getChildAt( ii );
int id = frame.getId();
Fragment frag = fm.findFragmentById( id );
if ( null == frag ) {
DbgUtils.logf( "tellOrienationChanged: NO FRAG at %d, id=%d", ii, id );
} else if ( frag instanceof OrientChangeListener ) {
((OrientChangeListener)frag).orientationChanged();
}
}
}
private int maxPanes()
{
int result;
int orientation = getResources().getConfiguration().orientation;
if ( XWPrefs.getIsTablet( this )
&& Configuration.ORIENTATION_LANDSCAPE == orientation ) {
result = 2;
} else {
result = 1;
}
return result;
}
private static FragActivity getThis()
{
Assert.assertNotNull( s_this );
return s_this;
}
public static void addFragment( Fragment fragment, Bundle bundle )
{
addFragment( fragment, bundle, null );
}
public static void addFragment( Fragment fragment, Bundle bundle,
Delegator parent )
{
getThis().addFragmentImpl( fragment, bundle, parent );
}
public static void addFragmentForResult( Fragment fragment, Bundle bundle,
RequestCode requestCode, Delegator parent )
{
getThis().addFragmentImpl( fragment, bundle, parent );
}
}

View file

@ -37,13 +37,13 @@ public class GCMIntentService extends GCMBaseIntentService {
@Override
protected void onError( Context context, String error )
{
DbgUtils.logf("GCMIntentService.onError(%s)", error );
DbgUtils.logdf("GCMIntentService.onError(%s)", error );
}
@Override
protected void onRegistered( Context context, String regId )
{
DbgUtils.logf( "GCMIntentService.onRegistered(%s)", regId );
DbgUtils.logdf( "GCMIntentService.onRegistered(%s)", regId );
DevID.setGCMDevID( context, regId );
notifyRelayService( context, true );
}
@ -51,7 +51,7 @@ public class GCMIntentService extends GCMBaseIntentService {
@Override
protected void onUnregistered( Context context, String regId )
{
DbgUtils.logf( "GCMIntentService.onUnregistered(%s)", regId );
DbgUtils.logdf( "GCMIntentService.onUnregistered(%s)", regId );
DevID.clearGCMDevID( context );
RelayService.devIDChanged();
notifyRelayService( context, false );
@ -60,13 +60,13 @@ public class GCMIntentService extends GCMBaseIntentService {
@Override
protected void onMessage( Context context, Intent intent )
{
DbgUtils.logf( "GCMIntentService.onMessage()" );
DbgUtils.logdf( "GCMIntentService.onMessage()" );
notifyRelayService( context, true );
String value;
boolean ignoreIt = XWApp.GCM_IGNORED;
if ( ignoreIt ) {
DbgUtils.logf( "received GCM but ignoring it" );
DbgUtils.logdf( "received GCM but ignoring it" );
} else {
value = intent.getStringExtra( "checkUpdates" );
if ( null != value && Boolean.parseBoolean( value ) ) {
@ -123,7 +123,7 @@ public class GCMIntentService extends GCMBaseIntentService {
GCMRegistrar.checkDevice( app );
// GCMRegistrar.checkManifest( app );
String regId = DevID.getGCMDevID( app );
if (regId.equals("")) {
if ( regId.equals("") ) {
GCMRegistrar.register( app, GCMConsts.SENDER_ID );
}
} catch ( UnsupportedOperationException uoe ) {

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009 - 2015 by Eric House (xwords@eehouse.org). All rights
* Copyright 2009 - 2016 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -83,7 +83,6 @@ public class GameConfigDelegate extends DelegateBase
private Button m_playButton;
private ImageButton m_refreshRoomsButton;
private View m_connectSetRelay;
private View m_connectSetSMS;
private Spinner m_dictSpinner;
private Spinner m_playerDictSpinner;
private Spinner m_roomChoose;
@ -463,9 +462,10 @@ public class GameConfigDelegate extends DelegateBase
m_cp = CommonPrefs.get( m_activity );
Intent intent = getIntent();
m_rowid = intent.getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 );
m_forResult = intent.getBooleanExtra( INTENT_FORRESULT_ROWID, false );
Bundle args = getArguments();
m_rowid = args.getLong( GameUtils.INTENT_KEY_ROWID, DBUtils.ROWID_NOTFOUND );
Assert.assertTrue( DBUtils.ROWID_NOTFOUND != m_rowid );
m_forResult = args.getBoolean( INTENT_FORRESULT_ROWID, false );
m_connectSetRelay = findViewById( R.id.connect_set_relay );
@ -1167,7 +1167,7 @@ public class GameConfigDelegate extends DelegateBase
&& 0 == m_car.ip_relay_invite.length() ) {
showOKOnlyDialog( R.string.no_empty_rooms );
} else {
GameUtils.launchGameAndFinish( m_activity, m_rowid );
GameUtils.launchGameAndFinish( getDelegator(), m_rowid );
}
}
@ -1196,14 +1196,24 @@ public class GameConfigDelegate extends DelegateBase
return DeviceRole.SERVER_STANDALONE == m_giOrig.serverRole;
}
public static void editForResult( Activity parent, RequestCode requestCode,
public static void editForResult( Delegator delegator,
RequestCode requestCode,
long rowID )
{
Intent intent = new Intent( parent, GameConfigActivity.class );
intent.setAction( Intent.ACTION_EDIT );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, rowID );
intent.putExtra( INTENT_FORRESULT_ROWID, true );
parent.startActivityForResult( intent, requestCode.ordinal() );
Activity activity = delegator.getActivity();
Bundle bundle = new Bundle();
bundle.putLong( GameUtils.INTENT_KEY_ROWID, rowID );
bundle.putBoolean( INTENT_FORRESULT_ROWID, true );
if ( activity instanceof FragActivity ) {
FragActivity.addFragmentForResult( new GameConfigFrag(), bundle,
requestCode, delegator );
} else {
Intent intent = new Intent( activity, GameConfigActivity.class );
intent.setAction( Intent.ACTION_EDIT );
intent.putExtras( bundle );
activity.startActivityForResult( intent, requestCode.ordinal() );
}
}
private void setConnLabel()

View file

@ -0,0 +1,31 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2016 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.os.Bundle;
public class GameConfigFrag extends XWFragment {
@Override
public void onCreate( Bundle sis )
{
super.onCreate( new GameConfigDelegate( this, sis ), sis, true );
}
}

View file

@ -43,13 +43,16 @@ import java.util.concurrent.LinkedBlockingQueue;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.loc.LocUtils;
public class GameListItem extends LinearLayout
implements View.OnClickListener, SelectableItem.LongClickHandler {
private static final int SUMMARY_WAIT_MSECS = 1000;
private static HashSet<Long> s_invalRows = new HashSet<Long>();
private Activity m_activity;
@ -59,7 +62,7 @@ public class GameListItem extends LinearLayout
private View m_hideable;
private ImageView m_thumb;
private ExpiringTextView m_name;
private View m_viewUnloaded;
private TextView m_viewUnloaded;
private View m_viewLoaded;
private LinearLayout m_list;
private TextView m_state;
@ -129,13 +132,7 @@ public class GameListItem extends LinearLayout
// as we're back on the UI thread.
++m_loadingCount;
LoadItemTask task = new LoadItemTask();
if ( false && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
// Actually run these in parallel if the OS supports it
task.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR );
} else {
task.execute();
}
new LoadItemTask().execute();
}
public void invalName()
@ -195,7 +192,7 @@ public class GameListItem extends LinearLayout
m_name = (ExpiringTextView)findViewById( R.id.game_name );
m_expandButton = (ImageButton)findViewById( R.id.expander );
m_expandButton.setOnClickListener( this );
m_viewUnloaded = findViewById( R.id.view_unloaded );
m_viewUnloaded = (TextView)findViewById( R.id.view_unloaded );
m_viewLoaded = findViewById( R.id.view_loaded );
m_list = (LinearLayout)findViewById( R.id.player_list );
m_state = (TextView)findViewById( R.id.state );
@ -357,13 +354,23 @@ public class GameListItem extends LinearLayout
@Override
protected GameSummary doInBackground( Void... unused )
{
return DBUtils.getSummary( m_context, m_rowid, 150 );
return DBUtils.getSummary( m_context, m_rowid, SUMMARY_WAIT_MSECS );
} // doInBackground
@Override
protected void onPostExecute( GameSummary summary )
{
if ( 0 == --m_loadingCount ) {
if ( null == summary ) {
// Try again. Maybe it's open
JNIThread thread = JNIThread.getRetained( m_rowid );
if ( null != thread ) {
summary = DBUtils.getSummary( m_context,
thread.getLock() );
thread.release();
}
}
m_summary = summary;
boolean expanded = DBUtils.getExpanded( m_context, m_rowid );
@ -371,6 +378,11 @@ public class GameListItem extends LinearLayout
setData( summary, expanded );
setLoaded( null != m_summary );
if ( null == summary ) {
m_viewUnloaded
.setText( LocUtils.getString( m_context,
R.string.summary_busy ) );
}
synchronized( s_invalRows ) {
s_invalRows.remove( m_rowid );
}

View file

@ -28,12 +28,22 @@ import junit.framework.Assert;
// obtainable when other read locks are granted but not when a
// write lock is. Write-locks are exclusive.
public class GameLock {
private static final int SLEEP_TIME = 25;
private static final boolean DEBUG_LOCKS = false;
private static final boolean THROW_ON_LOCKED = true;
private static final int SLEEP_TIME = 100;
private static final long ASSERT_TIME = 2000;
private static final long THROW_TIME = 1000;
private long m_rowid;
private boolean m_isForWrite;
private int m_lockCount;
private StackTraceElement[] m_lockTrace;
static {
Assert.assertTrue( THROW_TIME <= ASSERT_TIME );
}
public static class GameLockedException extends RuntimeException {}
private static HashMap<Long, GameLock>
s_locks = new HashMap<Long,GameLock>();
@ -43,7 +53,7 @@ public class GameLock {
m_rowid = rowid;
m_isForWrite = isForWrite;
m_lockCount = 0;
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.GameLock(rowid:%d,isForWrite:%b)=>"
+ "this: %H", rowid, isForWrite, this );
DbgUtils.printStack();
@ -69,26 +79,26 @@ public class GameLock {
++m_lockCount;
gotIt = true;
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
StackTraceElement[] trace
= Thread.currentThread().getStackTrace();
m_lockTrace = new StackTraceElement[trace.length];
System.arraycopy( trace, 0, m_lockTrace, 0, trace.length );
}
} else if ( this == owner && ! m_isForWrite ) {
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "tryLock(): incrementing lock count" );
}
Assert.assertTrue( 0 == m_lockCount );
++m_lockCount;
gotIt = true;
owner = null;
} else if ( XWApp.DEBUG_LOCKS ) {
} else if ( DEBUG_LOCKS ) {
DbgUtils.logf( "tryLock(): rowid %d already held by lock %H",
m_rowid, owner );
}
}
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.tryLock %H (rowid=%d) => %b",
this, m_rowid, gotIt );
}
@ -105,11 +115,11 @@ public class GameLock {
public GameLock lock( long maxMillis )
{
GameLock result = null;
final long assertTime = 2000;
Assert.assertTrue( maxMillis < assertTime );
Assert.assertTrue( maxMillis <= ASSERT_TIME );
Assert.assertTrue( maxMillis <= THROW_TIME );
long sleptTime = 0;
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid,
maxMillis );
}
@ -120,9 +130,9 @@ public class GameLock {
result = this;
break;
}
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.lock() %H failed; sleeping", this );
if ( 0 == sleptTime || sleptTime + SLEEP_TIME >= assertTime ) {
if ( 0 == sleptTime || sleptTime + SLEEP_TIME >= ASSERT_TIME ) {
DbgUtils.logf( "lock %H seeking stack:", curOwner );
DbgUtils.printStack( curOwner.m_lockTrace );
DbgUtils.logf( "lock %H seeking stack:", this );
@ -137,18 +147,20 @@ public class GameLock {
break;
}
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.lock() %H awake; "
+ "sleptTime now %d millis", this, sleptTime );
}
if ( 0 < maxMillis && sleptTime >= maxMillis ) {
break;
} else if ( sleptTime >= assertTime ) {
if ( XWApp.DEBUG_LOCKS ) {
} else if ( THROW_ON_LOCKED && sleptTime >= THROW_TIME ) {
throw new GameLockedException();
} else if ( sleptTime >= ASSERT_TIME ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "lock %H overlocked", this );
}
Assert.fail();
Assert.fail(); // firing
}
}
// DbgUtils.logf( "GameLock.lock(%s) done", m_path );
@ -167,7 +179,7 @@ public class GameLock {
}
--m_lockCount;
if ( XWApp.DEBUG_LOCKS ) {
if ( DEBUG_LOCKS ) {
DbgUtils.logf( "GameLock.unlock: this: %H (rowid:%d) unlocked",
this, m_rowid );
}

View file

@ -27,6 +27,7 @@ import android.graphics.Bitmap;
import android.graphics.Rect;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.text.TextUtils;
@ -219,6 +220,13 @@ public class GameUtils {
return rowid;
}
public static void deleteGame( Context context, GameLock lock, boolean informNow )
{
tellDied( context, lock, informNow );
Utils.cancelNotification( context, (int)lock.getRowid() );
DBUtils.deleteGame( context, lock );
}
public static boolean deleteGame( Context context, long rowid,
boolean informNow )
{
@ -226,9 +234,7 @@ public class GameUtils {
// does this need to be synchronized?
GameLock lock = new GameLock( rowid, true );
if ( lock.tryLock() ) {
tellDied( context, lock, informNow );
Utils.cancelNotification( context, (int)rowid );
DBUtils.deleteGame( context, lock );
deleteGame( context, lock, informNow );
lock.unlock();
success = true;
} else {
@ -368,6 +374,7 @@ public class GameUtils {
ThumbCanvas canvas = new ThumbCanvas( context, thumb );
XwJNI.board_setDraw( gamePtr, canvas );
XwJNI.board_invalAll( gamePtr );
Assert.assertNotNull( gamePtr );
XwJNI.board_draw( gamePtr );
}
}
@ -745,6 +752,15 @@ public class GameUtils {
// }
// }
public static String[] dictNames( Context context, GameLock lock )
{
byte[] stream = savedGame( context, lock );
CurGameInfo gi = new CurGameInfo( context );
XwJNI.gi_from_stream( gi, stream );
return gi.dictNames();
}
public static String[] dictNames( Context context, long rowid,
int[] missingLang )
{
@ -822,26 +838,39 @@ public class GameUtils {
return file.endsWith( XWConstants.GAME_EXTN );
}
public static void launchGame( Activity activity, long rowid,
public static Bundle makeLaunchExtras( long rowid, boolean invited )
{
Bundle bundle = new Bundle();
bundle.putLong( INTENT_KEY_ROWID, rowid );
if ( invited ) {
bundle.putBoolean( INVITED, true );
}
return bundle;
}
public static void launchGame( Delegator delegator, long rowid,
boolean invited )
{
Intent intent = new Intent( activity, BoardActivity.class );
intent.putExtra( INTENT_KEY_ROWID, rowid );
if ( invited ) {
intent.putExtra( INVITED, true );
Activity activity = delegator.getActivity();
Bundle extras = makeLaunchExtras( rowid, invited );
if ( activity instanceof FragActivity ) {
FragActivity.addFragment( new BoardFrag(), extras, delegator );
} else {
Intent intent = new Intent( activity, BoardActivity.class );
intent.putExtras( extras );
activity.startActivity( intent );
}
activity.startActivity( intent );
}
public static void launchGame( Activity activity, long rowid )
public static void launchGame( Delegator delegator, long rowid )
{
launchGame( activity, rowid, false );
launchGame( delegator, rowid, false );
}
public static void launchGameAndFinish( Activity activity, long rowid )
public static void launchGameAndFinish( Delegator delegator, long rowid )
{
launchGame( activity, rowid );
activity.finish();
launchGame( delegator, rowid );
delegator.getActivity().finish();
}
private static class FeedUtilsImpl extends UtilCtxtImpl {
@ -862,9 +891,9 @@ public class GameUtils {
m_gameOver = false;
}
@Override
public void showChat( String msg, String fromName )
public void showChat( String msg, int fromIndx, String fromName )
{
DBUtils.appendChatHistory( m_context, m_rowid, msg, false );
DBUtils.appendChatHistory( m_context, m_rowid, msg, fromIndx );
m_gotChat = true;
m_chatFrom = fromName;
m_chat = msg;
@ -1036,11 +1065,10 @@ public class GameUtils {
}
if ( forceNew || !madeGame ) {
XwJNI.game_makeNewGame( gamePtr, gi, util,
JNIUtilsImpl.get(context),
(DrawCtx)null,
cp, sink, dictNames, pairs.m_bytes,
pairs.m_paths, langName );
XwJNI.game_makeNewGame( gamePtr, gi, dictNames, pairs.m_bytes,
pairs.m_paths, langName, util,
JNIUtilsImpl.get(context), (DrawCtx)null,
cp, sink );
}
if ( null != car ) {
@ -1059,12 +1087,20 @@ public class GameUtils {
DBUtils.saveSummary( context, lock, summary );
} // applyChanges
public static void doConfig( Activity activity, long rowid, Class clazz )
public static void doConfig( Delegator delegator, long rowid )
{
Intent intent = new Intent( activity, clazz );
intent.setAction( Intent.ACTION_EDIT );
intent.putExtra( INTENT_KEY_ROWID, rowid );
activity.startActivity( intent );
Bundle extras = new Bundle();
extras.putLong( INTENT_KEY_ROWID, rowid );
Activity activity = delegator.getActivity();
if ( activity instanceof FragActivity ) {
FragActivity.addFragment( new GameConfigFrag(), extras, delegator );
} else {
Intent intent = new Intent( activity, GameConfigActivity.class );
intent.setAction( Intent.ACTION_EDIT );
intent.putExtras( extras );
activity.startActivity( intent );
}
}
public static String formatGameID( int gameID )
@ -1090,6 +1126,14 @@ public class GameUtils {
return rint;
}
// public static void postSelfNotification( Context context, long rowid )
// {
// Assert.assertTrue( BuildConfig.DEBUG );
// Intent intent = GamesListDelegate.makeRowidIntent( context, rowid );
// Utils.postNotification( context, intent, "launch",
// String.format("%d", rowid), (int)rowid );
// }
public static void postMoveNotification( Context context, long rowid,
BackMoveResult bmr,
boolean isTurnNow )

View file

@ -54,4 +54,12 @@ public class GamesListActivity extends XWActivity {
super.onNewIntent( intent );
m_dlgt.onNewIntent( intent );
}
//////////////////////////////////////////////////////////////////////
// GamesListDelegator interface
//////////////////////////////////////////////////////////////////////
public void launchGame( long rowID, boolean invited )
{
GameUtils.launchGame( this, rowID, invited );
}
}

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009 - 2015 by Eric House (xwords@eehouse.org). All
* Copyright 2009 - 2016 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
@ -566,7 +566,6 @@ public class GamesListDelegate extends ListDelegateBase
private Activity m_activity;
private static GamesListDelegate s_self;
private Delegator m_delegator;
private GameListAdapter m_adapter;
private Handler m_handler;
private String m_missingDict;
@ -581,7 +580,7 @@ public class GamesListDelegate extends ListDelegateBase
private String m_nameField;
private NetLaunchInfo m_netLaunchInfo;
private GameNamer m_namer;
private Set<Long> m_launchedGames;
private Set<Long> m_launchedGames; // prevent problems with double-taps
private boolean m_menuPrepared;
private boolean m_moveAfterNewGroup;
private Set<Long> m_selGames;
@ -596,7 +595,6 @@ public class GamesListDelegate extends ListDelegateBase
public GamesListDelegate( Delegator delegator, Bundle sis )
{
super( delegator, sis, R.layout.game_list, R.menu.games_list_menu );
m_delegator = delegator;
m_activity = delegator.getActivity();
m_launchedGames = new HashSet<Long>();
s_self = this;
@ -920,6 +918,9 @@ public class GamesListDelegate extends ListDelegateBase
@Override
protected void init( Bundle savedInstanceState )
{
m_origTitle = getTitle();
Assert.assertTrue( m_origTitle.equals( getString(R.string.app_name) ) );
m_handler = new Handler();
// Next line useful if contents of DB are crashing app on start
// DBUtils.saveDB( m_activity );
@ -946,18 +947,22 @@ public class GamesListDelegate extends ListDelegateBase
};
mkListAdapter();
ListView listView = getListView();
listView.setOnItemLongClickListener( this );
getListView().setOnItemLongClickListener( this );
NetUtils.informOfDeaths( m_activity );
tryStartsFromIntent( getIntent() );
getDictForLangIf();
m_origTitle = getTitle();
} // init
// protected View onCreateView( Bundle savedInstanceState )
// {
// View result = inflate( R.layout.game_list );
// // init( savedInstanceState );
// return result;
// }
// called when we're brought to the front (probably as a result of
// notification)
protected void onNewIntent( Intent intent )
@ -984,7 +989,9 @@ public class GamesListDelegate extends ListDelegateBase
protected void onDestroy()
{
DBUtils.clearDBChangeListener( this );
s_self = null;
if ( s_self == this ) {
s_self = null;
}
}
protected void onSaveInstanceState( Bundle outState )
@ -1040,16 +1047,7 @@ public class GamesListDelegate extends ListDelegateBase
if ( hasFocus ) {
updateField();
if ( 0 != m_launchedGames.size() ) {
long rowid = m_launchedGames.iterator().next();
m_launchedGames.remove( rowid );
if ( m_adapter.inExpandedGroup( rowid ) ) {
setSelGame( rowid );
} else {
clearSelections();
}
}
m_launchedGames.clear();
}
}
@ -1103,11 +1101,13 @@ public class GamesListDelegate extends ListDelegateBase
public void itemClicked( SelectableItem.LongClickHandler clicked,
GameSummary summary )
{
DbgUtils.logf( "GamesListDelegate.itemClicked()" );
// We need a way to let the user get back to the basic-config
// dialog in case it was dismissed. That way it to check for
// an empty room name.
if ( clicked instanceof GameListItem ) {
long rowid = ((GameListItem)clicked).getRowID();
DbgUtils.logf( "GamesListDelegate.itemClicked(%d)", rowid );
if ( ! m_launchedGames.contains( rowid ) ) {
showNotAgainDlgThen( R.string.not_again_newselect,
R.string.key_notagain_newselect,
@ -1297,7 +1297,7 @@ public class GamesListDelegate extends ListDelegateBase
if ( !cancelled ) {
long rowID = data.getLongExtra( GameUtils.INTENT_KEY_ROWID,
DBUtils.ROWID_NOTFOUND );
GameUtils.launchGame( m_activity, rowID );
GameUtils.launchGame( getDelegator(), rowID );
}
break;
}
@ -1325,7 +1325,7 @@ public class GamesListDelegate extends ListDelegateBase
@Override
public boolean onPrepareOptionsMenu( Menu menu )
{
int nGamesSelected = m_selGames.size();
int nGamesSelected = m_selGames.size(); // NPE!
int nGroupsSelected = m_selGroupIDs.size();
int groupCount = m_adapter.getGroupCount();
m_menuPrepared = 0 == nGamesSelected || 0 == nGroupsSelected;
@ -1482,7 +1482,7 @@ public class GamesListDelegate extends ListDelegateBase
break;
case R.id.games_menu_study:
StudyListDelegate.launchOrAlert( m_activity, StudyListDelegate.NO_LANG, this );
StudyListDelegate.launchOrAlert( getDelegator(), StudyListDelegate.NO_LANG, this );
break;
case R.id.games_menu_about:
@ -1677,7 +1677,7 @@ public class GamesListDelegate extends ListDelegateBase
break;
case R.id.games_game_config:
GameUtils.doConfig( m_activity, selRowIDs[0], GameConfigActivity.class );
GameUtils.doConfig( getDelegator(), selRowIDs[0] );
break;
case R.id.games_game_move:
@ -1967,7 +1967,13 @@ public class GamesListDelegate extends ListDelegateBase
private void startFirstHasDict( long rowid )
{
if ( -1 != rowid && DBUtils.haveGame( m_activity, rowid ) ) {
if ( GameUtils.gameDictsHere( m_activity, rowid ) ) {
boolean haveDict;
try {
haveDict = GameUtils.gameDictsHere( m_activity, rowid );
} catch ( GameLock.GameLockedException gle ) {
haveDict = true;
}
if ( haveDict ) {
launchGame( rowid );
}
}
@ -2270,6 +2276,12 @@ public class GamesListDelegate extends ListDelegateBase
{
boolean madeGame = DBUtils.ROWID_NOTFOUND != m_missingDictRowId;
if ( madeGame ) {
// if ( R.id.games_game_reset == m_missingDictMenuId ) {
// long[] rowIDs = { m_missingDictRowId };
// doConfirmReset( rowIDs );
// } else {
// launchGame( m_missingDictRowId );
// }
// save in case checkWarnNoDict needs to set them
long rowID = m_missingDictRowId;
int menuID = m_missingDictMenuId;
@ -2280,7 +2292,7 @@ public class GamesListDelegate extends ListDelegateBase
long[] rowIDs = { rowID };
doConfirmReset( rowIDs );
} else if ( checkWarnNoDict( rowID ) ) {
GameUtils.launchGame( m_activity, rowID );
GameUtils.launchGame( getDelegator(), rowID );
}
}
return madeGame;
@ -2288,9 +2300,16 @@ public class GamesListDelegate extends ListDelegateBase
private void launchGame( long rowid, boolean invited )
{
// DbgUtils.logf( "launchGame(%d)", rowid );
// if ( ! m_launchedGames.contains( rowid ) ) {
// m_launchedGames.add( rowid );
// m_delegator.launchGame( rowid, invited );
if ( ! m_launchedGames.contains( rowid ) ) {
m_launchedGames.add( rowid );
GameUtils.launchGame( m_activity, rowid, invited );
if ( m_adapter.inExpandedGroup( rowid ) ) {
setSelGame( rowid );
}
GameUtils.launchGame( getDelegator(), rowid, invited );
}
}
@ -2320,13 +2339,19 @@ public class GamesListDelegate extends ListDelegateBase
{
GameSummary summary = (GameSummary)params[1];
long rowid = (Long)params[0];
DbgUtils.logf( "GamesListDelegate.doOpenGame(%d)", rowid );
if ( summary.conTypes.contains( CommsAddrRec.CommsConnType.COMMS_CONN_RELAY )
&& summary.roomName.length() == 0 ) {
Assert.fail();
} else {
if ( checkWarnNoDict( rowid ) ) {
launchGame( rowid );
try {
if ( checkWarnNoDict( rowid ) ) {
launchGame( rowid );
}
} catch ( GameLock.GameLockedException gle ) {
DbgUtils.loge( gle );
finish();
}
}
}
@ -2465,11 +2490,12 @@ public class GamesListDelegate extends ListDelegateBase
if ( doConfigure ) {
// configure it
GameConfigDelegate.editForResult( m_activity, RequestCode
.CONFIG_GAME, rowID );
GameConfigDelegate.editForResult( getDelegator(),
RequestCode.CONFIG_GAME,
rowID );
} else {
// launch it
GameUtils.launchGame( m_activity, rowID );
GameUtils.launchGame( getDelegator(), rowID );
}
}
}

View file

@ -0,0 +1,32 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2014 - 2016 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.os.Bundle;
public class GamesListFrag extends XWFragment {
@Override
public void onCreate( Bundle sis )
{
super.onCreate( new GamesListDelegate( this, sis ), sis, true );
}
}

View file

@ -69,5 +69,4 @@ public class ListDelegateBase extends DelegateBase {
setListAdapter( adapter );
listView.setSelectionFromTop( pos, top );
}
}

View file

@ -0,0 +1,63 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2014 - 2016 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.Intent;
import android.os.Bundle;
import android.view.View.OnClickListener;
import android.view.View;
public class Main extends Activity {
@Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
if ( BuildConfig.DEBUG && XWApp.OFFER_DUALPANE ) {
setContentView( R.layout.main );
findViewById( R.id.activity_button )
.setOnClickListener( new OnClickListener() {
@Override
public void onClick( View v ) {
startActivity( GamesListActivity.class );
}
} );
findViewById( R.id.fragment_button )
.setOnClickListener( new OnClickListener() {
@Override
public void onClick( View v ) {
startActivity( FragActivity.class );
}
} );
} else {
startActivity( GamesListActivity.class );
finish();
}
}
private void startActivity( Class clazz )
{
startActivity( new Intent( this, clazz ) );
}
}

View file

@ -50,7 +50,7 @@ public class MultiMsgSink implements TransportProcs {
// rowID is used as token to identify game on relay. Anything that
// uniquely identifies a game on a device would work
public long getRowID() { return m_rowid; };
public void setRowID( long rowID ) { m_rowid = rowID; };
public MultiMsgSink setRowID( long rowID ) { m_rowid = rowID; return this; };
// These will be overridden by e.g. BTService which for sendViaBluetooth()
// can just insert a message into its queue

View file

@ -61,7 +61,7 @@ public class MultiService {
public enum MultiEvent { _INVALID,
BAD_PROTO_BT,
BAD_PROTO_SMS,
APP_NOT_FOUND,
APP_NOT_FOUND_BT,
BT_ENABLED,
BT_DISABLED,
SCAN_DONE,
@ -197,5 +197,4 @@ public class MultiService {
}
return downloaded;
}
}

View file

@ -238,11 +238,8 @@ public class PrefsDelegate extends DelegateBase
{
if ( AlertDialog.BUTTON_POSITIVE == button
&& action == Action.ENABLE_SMS_DO ) {
boolean enabled = (Boolean)params[0];
if ( enabled ) {
XWPrefs.setSMSEnabled( m_activity, true );
SMSCheckBoxPreference.setChecked();
}
XWPrefs.setSMSEnabled( m_activity, true );
SMSCheckBoxPreference.setChecked();
} else {
super.dlgButtonClicked( action, button, params );
}

View file

@ -54,6 +54,7 @@ import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.UtilCtxt.DevIDType;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.loc.LocUtils;
public class RelayService extends XWService
@ -248,22 +249,26 @@ public class RelayService extends XWService
long[] rowids = DBUtils.getRowIDsFor( this, nli.gameID() );
if ( (null == rowids || 0 == rowids.length)
|| XWPrefs.getRelayInviteToSelfEnabled( this )) {
CommsAddrRec addr = nli.makeAddrRec( this );
// We can't pass a message sink, meaning we can't let the device
// connect to the inviting game, because it needs to be open to
// make the initial connection -- needs access to util. That
// should be fixable -- eventually.
RelayMsgSink sink = new RelayMsgSink();
long rowid = GameUtils.makeNewMultiGame( this, nli, sink,
getUtilCtxt() );
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
DBUtils.setName( this, rowid, nli.gameName );
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) {
long rowid = GameUtils.makeNewMultiGame( this, nli,
new RelayMsgSink(),
getUtilCtxt() );
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
DBUtils.setName( this, rowid, nli.gameName );
}
String body = LocUtils.getString( this,
R.string.new_relay_body );
GameUtils.postInvitedNotification( this, nli.gameID(), body,
rowid );
}
String body = LocUtils.getString( this,
R.string.new_relay_body );
GameUtils.postInvitedNotification( this, nli.gameID(), body,
rowid );
} else {
Intent intent = MultiService
.makeMissingDictIntent( this, nli,
DictFetchOwner.OWNER_RELAY );
MultiService.postMissingDictNotification( this, intent,
nli.gameID() );
}
}
}
@ -312,6 +317,12 @@ public class RelayService extends XWService
return intent;
}
@Override
protected MultiMsgSink getSink( long rowid )
{
return new RelayMsgSink().setRowID( rowid );
}
@Override
public void onCreate()
{
@ -395,7 +406,7 @@ public class RelayService extends XWService
String relayID = intent.getStringExtra( RELAY_ID );
sendNoConnMessage( rowid, relayID, msg );
} else {
feedMessage( rowid, msg );
receiveMessage( this, rowid, null, msg, s_addr );
}
break;
case INVITE:
@ -466,6 +477,15 @@ public class RelayService extends XWService
}
}
// private void setupNotification( long rowid )
// {
// Intent intent = GamesListDelegate.makeRowidIntent( this, rowid );
// String msg = LocUtils.getString( this, R.string.notify_body_fmt,
// GameUtils.getName( this, rowid ) );
// Utils.postNotification( this, intent, R.string.notify_title,
// msg, (int)rowid );
// }
private boolean startFetchThreadIf()
{
// DbgUtils.logf( "startFetchThreadIf()" );
@ -982,27 +1002,6 @@ public class RelayService extends XWService
return devid;
}
private void feedMessage( long rowid, byte[] msg )
{
DbgUtils.logdf( "RelayService::feedMessage: %d bytes for rowid %d",
msg.length, rowid );
if ( BoardDelegate.feedMessage( rowid, msg, s_addr ) ) {
DbgUtils.logdf( "feedMessage: board ate it" );
// do nothing
} else {
RelayMsgSink sink = new RelayMsgSink();
sink.setRowID( rowid );
BackMoveResult bmr = new BackMoveResult();
boolean[] isLocalP = new boolean[1];
if ( GameUtils.feedMessage( this, rowid, msg, s_addr,
sink, bmr, isLocalP ) ) {
GameUtils.postMoveNotification( this, rowid, bmr, isLocalP[0] );
} else {
DbgUtils.logdf( "feedMessage(): background dropped it" );
}
}
}
private void fetchAndProcess()
{
long[][] rowIDss = new long[1][];
@ -1026,33 +1025,14 @@ public class RelayService extends XWService
boolean[] isLocalP = new boolean[1];
for ( int ii = 0; ii < nameCount; ++ii ) {
byte[][] forOne = msgs[ii];
// if game has messages, open it and feed 'em to it.
if ( null != forOne ) {
BackMoveResult bmr = new BackMoveResult();
sink.setRowID( rowIDs[ii] );
// since BoardDelegate.feedMessages can't know:
isLocalP[0] = false;
if ( BoardDelegate.feedMessages( rowIDs[ii], forOne, s_addr )
|| GameUtils.feedMessages( this, rowIDs[ii],
forOne, s_addr,
sink, bmr, isLocalP ) ) {
idsWMsgs.add( relayIDs[ii] );
bmrs.add( bmr );
isLocals.add( isLocalP[0] );
} else {
DbgUtils.logf( "RelayService.process(): message for %s (rowid %d)"
+ " not consumed", relayIDs[ii], rowIDs[ii] );
long rowid = rowIDs[ii];
sink.setRowID( rowid );
for ( byte[] msg : forOne ) {
receiveMessage( this, rowid, sink, msg, s_addr );
}
}
}
if ( 0 < idsWMsgs.size() ) {
String[] tmp = new String[idsWMsgs.size()];
idsWMsgs.toArray( tmp );
BackMoveResult[] bmrsa = new BackMoveResult[bmrs.size()];
bmrs.toArray( bmrsa );
setupNotifications( tmp, bmrsa, isLocals );
}
sink.send( this );
}
}

View file

@ -85,6 +85,9 @@ public class SMSInviteDelegate extends InviteDelegate {
protected void init( Bundle savedInstanceState )
{
// super.init( R.id.button_invite, R.id.button_add,
// R.id.button_clear, R.id.invite_desc,
// R.string.invite_sms_desc_fmt );
String msg = getString( R.string.button_invite );
msg = getQuantityString( R.plurals.invite_sms_desc_fmt, m_nMissing,
m_nMissing, msg );
@ -376,24 +379,6 @@ public class SMSInviteDelegate extends InviteDelegate {
saveAndRebuild();
}
private void clearIfSingle()
{
if ( 1 == m_nMissing ) {
int count = m_adapter.getCount();
for ( int ii = count - 1; ii >= 0; --ii ) {
PhoneRec rec = m_phoneRecs.get( ii );
if ( rec.m_isChecked ) {
rec.m_isChecked = false;
}
}
post( new Runnable() {
public void run() {
rebuildList( false );
}
} );
}
}
private class PhoneRec {
public String m_phone;
public String m_name;
@ -449,7 +434,6 @@ public class SMSInviteDelegate extends InviteDelegate {
new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged( CompoundButton bv,
boolean isChecked ) {
clearIfSingle();
m_phoneRecs.get(position).m_isChecked = isChecked;
tryEnable();
}

View file

@ -55,6 +55,7 @@ import java.util.Set;
import junit.framework.Assert;
import org.eehouse.android.xw4.XWService.ReceiveResult;
import org.eehouse.android.xw4.GameUtils.BackMoveResult;
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
import org.eehouse.android.xw4.MultiService.MultiEvent;
@ -62,6 +63,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.loc.LocUtils;
public class SMSService extends XWService {
@ -287,6 +289,12 @@ public class SMSService extends XWService {
return s_showToasts;
}
@Override // abstract
protected MultiMsgSink getSink( long rowid )
{
return new SMSMsgSink( this );
}
@Override
public void onCreate()
{
@ -703,24 +711,9 @@ public class SMSService extends XWService {
private void feedMessage( int gameID, byte[] msg, CommsAddrRec addr )
{
long[] rowids = DBUtils.getRowIDsFor( this, gameID );
if ( null == rowids || 0 == rowids.length ) {
ReceiveResult rslt = receiveMessage( this, gameID, null, msg, addr );
if ( ReceiveResult.GAME_GONE == rslt ) {
sendDiedPacket( addr.sms_phone, gameID );
} else {
boolean[] isLocalP = new boolean[1];
for ( long rowid : rowids ) {
if ( BoardDelegate.feedMessage( rowid, msg, addr ) ) {
// do nothing
} else {
SMSMsgSink sink = new SMSMsgSink( this );
BackMoveResult bmr = new BackMoveResult();
if ( GameUtils.feedMessage( this, rowid, msg, addr,
sink, bmr, isLocalP ) ) {
GameUtils.postMoveNotification( this, rowid, bmr,
isLocalP[0] );
}
}
}
}
}

View file

@ -19,8 +19,10 @@
package org.eehouse.android.xw4;
import android.app.Activity;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@ -291,6 +293,7 @@ public class StudyListDelegate extends ListDelegateBase
}
}
DbgUtils.logf( "creating studylist adapter" );
ArrayAdapter<String> adapter = new
ArrayAdapter<String>( m_activity,
android.R.layout.simple_spinner_item,
@ -342,10 +345,11 @@ public class StudyListDelegate extends ListDelegateBase
setTitleBar();
}
public static void launchOrAlert( Activity activity, int lang,
public static void launchOrAlert( Delegator delegator, int lang,
DlgDelegate.HasDlgDelegate dlg )
{
String msg = null;
Activity activity = delegator.getActivity();
if ( 0 == DBUtils.studyListLangs( activity ).length ) {
msg = LocUtils.getString( activity, R.string.study_no_lists );
} else if ( NO_LANG != lang &&
@ -359,9 +363,14 @@ public class StudyListDelegate extends ListDelegateBase
bundle.putInt( START_LANG, lang );
}
Intent intent = new Intent( activity, StudyListActivity.class );
intent.putExtras( bundle );
activity.startActivity( intent );
if ( activity instanceof FragActivity ) {
StudyListFrag frag = new StudyListFrag();
((FragActivity)activity).addFragment( frag, bundle, delegator );
} else {
Intent intent = new Intent( activity, StudyListActivity.class );
intent.putExtras( bundle );
activity.startActivity( intent );
}
}
if ( null != msg ) {

View file

@ -0,0 +1,32 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2014 - 2016 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.os.Bundle;
public class StudyListFrag extends XWFragment {
@Override
public void onCreate( Bundle sis )
{
super.onCreate( new StudyListDelegate( this, sis ), sis, true );
}
}

View file

@ -72,17 +72,12 @@ public class Toolbar {
private DlgDelegate.HasDlgDelegate m_dlgDlgt;
private LinearLayout m_layout;
private boolean m_visible;
private Boolean m_isPortrait;
private enum ORIENTATION { ORIENT_PORTRAIT, ORIENT_LANDSCAPE };
private ORIENTATION m_curOrient = null;
public Toolbar( Activity activity, HasDlgDelegate dlgDlgt,
boolean isLandscape )
public Toolbar( Activity activity, HasDlgDelegate dlgDlgt )
{
m_activity = activity;
m_dlgDlgt = dlgDlgt;
setIsLandscape( isLandscape );
}
public void setVisible( boolean visible )
@ -123,18 +118,10 @@ public class Toolbar {
setLongClickListener( index, listener );
}
private void setIsLandscape( boolean landscape )
protected void setIsPortrait( boolean isPortrait )
{
if ( landscape && m_curOrient == ORIENTATION.ORIENT_LANDSCAPE ) {
// do nothing
} else if ( !landscape && m_curOrient == ORIENTATION.ORIENT_PORTRAIT ) {
// do nothing
} else {
if ( landscape ) {
m_curOrient = ORIENTATION.ORIENT_LANDSCAPE;
} else {
m_curOrient = ORIENTATION.ORIENT_PORTRAIT;
}
if ( null == m_isPortrait || m_isPortrait != isPortrait ) {
m_isPortrait = isPortrait;
doShowHide();
}
}
@ -151,20 +138,21 @@ public class Toolbar {
private void doShowHide()
{
Assert.assertTrue( null != m_curOrient );
boolean isLandscape = ORIENTATION.ORIENT_LANDSCAPE == m_curOrient;
Assert.assertTrue( null != m_isPortrait );
if ( null == m_layout ) {
m_layout = (LinearLayout)LocUtils.inflate( m_activity, R.layout.toolbar );
m_layout.setOrientation(ORIENTATION.ORIENT_PORTRAIT == m_curOrient
? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL );
} else {
((ViewGroup)m_layout.getParent()).removeView( m_layout );
}
m_layout.setOrientation( m_isPortrait ?
LinearLayout.HORIZONTAL : LinearLayout.VERTICAL );
int id = isLandscape ? R.id.tbar_parent_vert : R.id.tbar_parent_hor;
ViewGroup scroller = (ViewGroup)m_activity.findViewById( id );
if ( null != scroller ) {
// Google's had reports of a crash adding second view
scroller.removeAllViews();
scroller.addView( m_layout ); // failing
}
int id = m_isPortrait ? R.id.tbar_parent_hor : R.id.tbar_parent_vert;
ViewGroup scroller = (ViewGroup)m_activity.findViewById( id );
if ( null != scroller ) {
// Google's had reports of a crash adding second view
scroller.removeAllViews();
scroller.addView( m_layout );
}
m_layout.setVisibility( m_visible? View.VISIBLE : View.GONE );

View file

@ -35,16 +35,15 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.media.RingtoneManager;
import android.net.Uri;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.TelephonyManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
@ -239,6 +238,15 @@ public class Utils {
nm.cancel( id );
}
public static void playNotificationSound( Context context )
{
if ( CommonPrefs.getSoundNotify( context ) ) {
Uri uri = RingtoneManager
.getDefaultUri( RingtoneManager.TYPE_NOTIFICATION );
RingtoneManager.getRingtone( context, uri ).play();
}
}
// adapted from
// http://stackoverflow.com/questions/2174048/how-to-look-up-a-contacts-name-from-their-phone-number-on-android
public static String phoneToContact( Context context, String phone,

View file

@ -22,6 +22,7 @@ package org.eehouse.android.xw4;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextMenu;
@ -34,6 +35,8 @@ import android.widget.ListView;
import junit.framework.Assert;
import junit.framework.Assert;
public class XWActivity extends Activity implements Delegator {
private DelegateBase m_dlgt;
@ -176,6 +179,13 @@ public class XWActivity extends Activity implements Delegator {
m_dlgt.prepareDialog( DlgID.values()[id], dialog );
}
@Override
public void onConfigurationChanged( Configuration newConfig )
{
m_dlgt.orientationChanged();
super.onConfigurationChanged( newConfig );
}
@Override
protected void onActivityResult( int requestCode, int resultCode,
Intent data )

View file

@ -38,14 +38,16 @@ public class XWApp extends Application {
public static final boolean REMATCH_SUPPORTED = true;
public static final boolean RELAYINVITE_SUPPORTED = false;
public static final boolean ATTACH_SUPPORTED = false;
public static final boolean DEBUG_LOCKS = false;
public static final boolean LOG_LIFECYLE = false;
public static final boolean LOG_LIFECYLE = true;
public static final boolean DEBUG_EXP_TIMERS = false;
public static final boolean GCM_IGNORED = false;
public static final boolean UDP_ENABLED = true;
public static final boolean SMS_INVITE_ENABLED = true;
public static final boolean LOCUTILS_ENABLED = false;
public static final boolean CONTEXT_MENUS_ENABLED = true;
public static final boolean OFFER_DUALPANE = false;
// BT class "COMPUTERS" includes tablets like the Nexus 9
public static final boolean BT_SCAN_COMPUTERS = true;
public static final String SMS_PUBLIC_HEADER = "-XW4";
public static final int MAX_TRAY_TILES = 7; // comtypes.h
@ -53,10 +55,12 @@ public class XWApp extends Application {
private static UUID s_UUID = null;
private static Boolean s_onEmulator = null;
private static Context s_context = null;
@Override
public void onCreate()
{
s_context = this;
super.onCreate();
// This one line should always get logged even if logging is
@ -117,4 +121,6 @@ public class XWApp extends Application {
}
return s_onEmulator;
}
public static Context getContext() { return s_context; }
}

View file

@ -0,0 +1,161 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2014 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.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;
import junit.framework.Assert;
public class XWFragment extends Fragment
implements Delegator,
FragActivity.OrientChangeListener {
private DelegateBase m_dlgt;
private boolean m_hasOptionsMenu = false;
protected void onCreate( DelegateBase dlgt, Bundle sis, boolean hasOptionsMenu )
{
m_hasOptionsMenu = hasOptionsMenu;
this.onCreate( dlgt, sis );
}
protected void onCreate( DelegateBase dlgt, Bundle sis )
{
DbgUtils.logdf( "%s.onCreate() called", this.getClass().getName() );
super.onCreate( sis );
m_dlgt = dlgt;
}
@Override
public View onCreateView( LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState )
{
DbgUtils.logdf( "%s.onCreateView() called", this.getClass().getName() );
return m_dlgt.inflateView( inflater, container );
}
@Override
public void onActivityCreated( Bundle savedInstanceState )
{
DbgUtils.logdf( "%s.onActivityCreated() called", this.getClass().getName() );
m_dlgt.init( savedInstanceState );
super.onActivityCreated( savedInstanceState );
if ( m_hasOptionsMenu ) {
setHasOptionsMenu( true );
}
}
@Override
public void onPause()
{
DbgUtils.logdf( "%s.onPause() called", this.getClass().getName() );
m_dlgt.onPause();
super.onPause();
}
@Override
public void onResume()
{
super.onResume();
m_dlgt.onResume();
}
@Override
public void onStart()
{
DbgUtils.logdf( "%s.onStart() called", this.getClass().getName() );
super.onStart();
m_dlgt.onStart();
}
@Override
public void onStop()
{
DbgUtils.logdf( "%s.onStop() called", this.getClass().getName() );
m_dlgt.onStop();
super.onStop();
}
@Override
public void onDestroy()
{
DbgUtils.logdf( "%s.onDestroy() called", this.getClass().getName() );
m_dlgt.onDestroy();
super.onDestroy();
}
@Override
public void onPrepareOptionsMenu( Menu menu )
{
m_dlgt.onPrepareOptionsMenu( menu );
}
@Override
public void onCreateOptionsMenu( Menu menu, MenuInflater inflater )
{
m_dlgt.onCreateOptionsMenu( menu, inflater );
}
@Override
public boolean onOptionsItemSelected( MenuItem item )
{
return m_dlgt.onOptionsItemSelected( item );
}
public void finish()
{
Assert.fail();
}
// FragActivity.OrientChangeListener
public void orientationChanged()
{
m_dlgt.orientationChanged();
}
public ListView getListView()
{
ListView view = (ListView)m_dlgt.findViewById( android.R.id.list );
return view;
}
public void setListAdapter( ListAdapter adapter )
{
getListView().setAdapter( adapter );
}
public ListAdapter getListAdapter()
{
return getListView().getAdapter();
}
}

View file

@ -21,17 +21,23 @@
package org.eehouse.android.xw4;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import java.util.HashSet;
import java.util.Set;
import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.JNIThread;
import org.eehouse.android.xw4.jni.UtilCtxt;
import org.eehouse.android.xw4.jni.UtilCtxtImpl;
public class XWService extends Service {
class XWService extends Service {
public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED };
protected static MultiService s_srcMgr = null;
private static Set<String> s_seen = new HashSet<String>();
@ -85,4 +91,56 @@ public class XWService extends Service {
}
return m_utilCtxt;
}
// Meant to be overridden
protected MultiMsgSink getSink( long rowid ) { Assert.fail(); return null; }
protected ReceiveResult receiveMessage( Context context, int gameID,
MultiMsgSink sink, byte[] msg,
CommsAddrRec addr )
{
ReceiveResult result;
long[] rowids = DBUtils.getRowIDsFor( context, gameID );
if ( null == rowids || 0 == rowids.length ) {
result = ReceiveResult.GAME_GONE;
} else {
result = ReceiveResult.UNCONSUMED;
for ( long rowid : rowids ) {
if ( receiveMessage( context, rowid, sink, msg, addr ) ) {
result = ReceiveResult.OK;
}
}
}
return result;
}
protected boolean receiveMessage( Context context, long rowid,
MultiMsgSink sink, byte[] msg,
CommsAddrRec addr )
{
boolean allConsumed = true;
boolean[] isLocalP = new boolean[1];
JNIThread jniThread = JNIThread.getRetained( rowid, false );
boolean consumed = false;
if ( null != jniThread ) {
consumed = true;
jniThread.receive( msg, addr ).release();
} else {
GameUtils.BackMoveResult bmr =
new GameUtils.BackMoveResult();
if ( null == sink ) {
sink = getSink( rowid );
}
if ( GameUtils.feedMessage( context, rowid, msg, addr,
sink, bmr, isLocalP ) ) {
consumed = true;
GameUtils.postMoveNotification( context, rowid, bmr,
isLocalP[0] );
}
}
if ( allConsumed && !consumed ) {
allConsumed = false;
}
return allConsumed;
}
}

View file

@ -26,7 +26,6 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
public interface BoardHandler {
void startHandling( Activity parent, JNIThread thread,
XwJNI.GamePtr gamePtr, CurGameInfo gi,
CommsConnTypeSet connTypes );
void stopHandling();
}

View file

@ -295,6 +295,24 @@ public class CurGameInfo {
return !consistent;
}
public String[] playerNames()
{
String[] names = new String[nPlayers];
for ( int ii = 0; ii < nPlayers; ++ii ) {
names[ii] = players[ii].name;
}
return names;
}
public boolean[] playersLocal()
{
boolean[] locs = new boolean[nPlayers];
for ( int ii = 0; ii < nPlayers; ++ii ) {
locs[ii] = players[ii].isLocal;
}
return locs;
}
public String[] visibleNames( boolean withDicts )
{
String nameFmt = withDicts?

View file

@ -30,11 +30,17 @@ import android.os.Message;
import java.lang.InterruptedException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.HashSet;
import java.util.Set;
import org.eehouse.android.xw4.CommsTransport;
import org.eehouse.android.xw4.ConnStatusHandler;
import org.eehouse.android.xw4.DBUtils;
import org.eehouse.android.xw4.DbgUtils;
import org.eehouse.android.xw4.DictUtils;
import org.eehouse.android.xw4.GameLock;
import org.eehouse.android.xw4.GameUtils;
import org.eehouse.android.xw4.R;
@ -49,6 +55,21 @@ import junit.framework.Assert;
public class JNIThread extends Thread {
private static Set<JNIThread> s_curThreads = new HashSet<JNIThread>();
public static JNIThread getCurrent() {
JNIThread result = null;
synchronized( s_curThreads ) {
// DbgUtils.logf( "JNIThread.getCurrent(): have %d threads",
// s_curThreads.size() );
if ( 1 == s_curThreads.size() ) {
result = s_curThreads.iterator().next();
}
}
// DbgUtils.logf( "JNIThread.getCurrent() => %H", result );
return result;
}
public enum JNICmd { CMD_NONE,
// CMD_RUN,
CMD_DRAW,
@ -134,7 +155,7 @@ public class JNIThread extends Thread {
private boolean m_stopped = false;
private boolean m_saveOnStop = false;
private GamePtr m_jniGamePtr;
private byte[] m_gameAtStart;
private byte[] m_lastSavedState;
private GameLock m_lock;
private Context m_context;
private CurGameInfo m_gi;
@ -143,6 +164,10 @@ public class JNIThread extends Thread {
private static final int kMinDivWidth = 10;
private int m_connsIconID = 0;
private String m_newDict = null;
private long m_rowid;
private int m_refCount;
private CommsTransport m_xport;
private GameSummary m_summary;
private LinkedBlockingQueue<QueueElem> m_queue;
@ -156,22 +181,77 @@ public class JNIThread extends Thread {
Object[] m_args;
}
public JNIThread( GamePtr gamePtr, byte[] gameAtStart, CurGameInfo gi,
SyncedDraw drawer, GameLock lock, Context context,
Handler handler )
private JNIThread( long rowid )
{
m_jniGamePtr = gamePtr;
m_gameAtStart = gameAtStart;
m_gi = gi;
m_drawer = drawer;
m_lock = lock;
m_context = context;
m_handler = handler;
m_rowid = rowid;
m_queue = new LinkedBlockingQueue<QueueElem>();
}
public JNIThread configure( Context context, SyncedDraw drawer,
UtilCtxtImpl utils,
TransportProcs.TPMsgHandler xportHandler,
Handler handler )
{
m_context = context;
m_drawer = drawer;
m_handler = handler;
public void waitToStop( boolean save )
if ( null == m_lock ) {
m_lock = new GameLock( m_rowid, true ).lock();
} else {
m_jniGamePtr.release(); // let the old game copy go
}
// If this isn't true then the queue has to be allowed to empty,
// working on the old game state, before we can re-use any of this.
Assert.assertTrue( 0 == m_queue.size() );
m_jniGamePtr = XwJNI.initJNI( m_rowid );
String[] dictNames = GameUtils.dictNames( context, m_lock );
DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames );
Assert.assertFalse( pairs.anyMissing( dictNames ) ); // PENDING
byte[] stream = GameUtils.savedGame( context, m_lock );
Assert.assertNotNull( stream );
m_gi = new CurGameInfo( context );
m_gi.setName( DBUtils.getName( context, m_rowid ) );
XwJNI.gi_from_stream( m_gi, stream );
m_summary = DBUtils.getSummary( context, m_lock );
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
m_xport = new CommsTransport( context, xportHandler, m_rowid,
m_gi.serverRole );
m_xport.setReceiver( this );
}
CommonPrefs cp = CommonPrefs.get( context );
JNIUtils jniUtils = JNIUtilsImpl.get( context );
if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream,
m_gi, dictNames,
pairs.m_bytes,
pairs.m_paths,
m_gi.langName(),
utils, jniUtils,
null, cp, m_xport ) ) {
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, dictNames,
pairs.m_bytes, pairs.m_paths,
m_gi.langName(), utils, jniUtils,
null, cp, m_xport );
}
m_lastSavedState = stream;
return this;
}
public GamePtr getGamePtr() { return m_jniGamePtr; }
public CurGameInfo getGI() { return m_gi; }
public GameSummary getSummary() { return m_summary; }
public GameLock getLock() { return m_lock; }
private void waitToStop( boolean save )
{
synchronized ( this ) {
m_stopped = true;
@ -188,6 +268,9 @@ public class JNIThread extends Thread {
} catch ( java.lang.InterruptedException ie ) {
DbgUtils.loge( ie );
}
// m_jniGamePtr.release();
// m_jniGamePtr = null;
m_lock.unlock();
}
public boolean busy()
@ -297,8 +380,14 @@ public class JNIThread extends Thread {
m_gi.dictName = m_newDict;
}
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi );
if ( Arrays.equals( m_gameAtStart, state ) ) {
// DbgUtils.logf( "no change in game; can skip saving" );
boolean arraysEqual = Arrays.equals( m_lastSavedState, state );
boolean hashesEqual = Arrays.hashCode( m_lastSavedState) == Arrays.hashCode(state);
DbgUtils.logf( "arraysEqual: %b; hashesEqual: %b", arraysEqual, hashesEqual );
// PENDING: once certain this is true, stop saving the full array and
// instead save the hash. Also, update it after each save.
Assert.assertTrue( arraysEqual == hashesEqual );
if ( Arrays.equals( m_lastSavedState, state ) ) {
DbgUtils.logdf( "JNIThread.save_jni(): no change in game; can skip saving" );
} else {
synchronized( this ) {
Assert.assertNotNull( m_lock );
@ -308,10 +397,27 @@ public class JNIThread extends Thread {
DBUtils.saveSummary( m_context, m_lock, summary );
// There'd better be no way for saveGame above to fail!
XwJNI.game_saveSucceeded( m_jniGamePtr );
m_lastSavedState = state;
}
}
}
boolean m_running = false;
public void startOnce()
{
if ( !m_running ) {
m_running = true;
start();
}
}
public void setDaemonOnce( boolean val )
{
if ( !m_running ) {
setDaemon( val );
}
}
@SuppressWarnings("fallthrough")
public void run()
{
@ -340,11 +446,6 @@ public class JNIThread extends Thread {
continue;
}
save_jni();
// This is gross: we take the relay connection down
// then bring it right back up again each time there's
// a message received (to save any state changes it
// brought). There must be a better way.
// XwJNI.comms_start( m_jniGamePtr );
break;
case CMD_DRAW:
@ -609,20 +710,31 @@ public class JNIThread extends Thread {
} else {
DbgUtils.logf( "JNIThread.run(): exiting without saving" );
}
XwJNI.threadDone();
m_jniGamePtr.release();
m_jniGamePtr = null;
// XwJNI.threadDone();
} // run
public void handleBkgrnd( JNICmd cmd, Object... args )
{
QueueElem elem = new QueueElem( cmd, false, args );
// DbgUtils.logf( "adding: %s", cmd.toString() );
m_queue.add( elem );
m_queue.add( new QueueElem( cmd, false, args ) );
}
public JNIThread receive( byte[] msg, CommsAddrRec addr )
{
handle( JNICmd.CMD_RECEIVE, msg, addr );
return this;
}
public void sendChat( String chat )
{
handle( JNICmd.CMD_SENDCHAT, chat );
}
public void handle( JNICmd cmd, Object... args )
{
QueueElem elem = new QueueElem( cmd, true, args );
m_queue.add( elem );
m_queue.add( new QueueElem( cmd, true, args ) );
if ( m_stopped && ! JNICmd.CMD_NONE.equals(cmd) ) {
DbgUtils.logf( "WARNING: adding %s to stopped thread!!!",
cmd.toString() );
@ -639,10 +751,59 @@ public class JNIThread extends Thread {
return XwJNI.server_do( gamePtr );
}
// public void run( boolean isUI, Runnable runnable )
// {
// Object[] args = { runnable };
// QueueElem elem = new QueueElem( JNICmd.CMD_RUN, isUI, args );
// m_queue.add( elem );
// }
private static Map<Long, JNIThread> s_instances = new HashMap<Long, JNIThread>();
private void retain_sync()
{
++m_refCount;
DbgUtils.logf( "JNIThread.retain_sync(rowid=%d): m_refCount: %d",
m_rowid, m_refCount );
}
public JNIThread retain()
{
synchronized( s_instances ) {
retain_sync();
}
return this;
}
public void release()
{
boolean stop = false;
synchronized( s_instances ) {
if ( 0 == --m_refCount ) {
s_instances.remove( m_rowid );
stop = true;
}
}
DbgUtils.logf( "JNIThread.release(rowid=%d): m_refCount: %d",
m_rowid, m_refCount );
if ( stop ) {
waitToStop( true );
} else {
handle( JNICmd.CMD_SAVE ); // in case releaser has made changes
}
}
public static JNIThread getRetained( long rowid )
{
return getRetained( rowid, false );
}
public static JNIThread getRetained( long rowid, boolean makeNew )
{
JNIThread result = null;
synchronized( s_instances ) {
result = s_instances.get( rowid );
if ( null == result && makeNew ) {
result = new JNIThread( rowid );
s_instances.put( rowid, result );
}
if ( null != result ) {
result.retain_sync();
}
}
return result;
}
}

View file

@ -130,7 +130,7 @@ public interface UtilCtxt {
static final int ERR_RELAY_BASE = 17;
void userError( int id );
void informMove( String expl, String words );
void informMove( int turn, String expl, String words );
void informUndo();
void informNetDict( int lang, String oldName, String newName,
@ -146,7 +146,7 @@ public interface UtilCtxt {
boolean warnIllegalWord( String dict, String[] words, int turn,
boolean turnLost );
void showChat( String msg, String fromPlayer );
void showChat( String msg, int fromIndx, String fromName );
boolean phoneNumbersSame( String num1, String num2 );
}

View file

@ -254,7 +254,7 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "userError" );
}
public void informMove( String expl, String words )
public void informMove( int turn, String expl, String words )
{
subclassOverride( "informMove" );
}
@ -293,7 +293,7 @@ public class UtilCtxtImpl implements UtilCtxt {
}
// These need to go into some sort of chat DB, not dropped.
public void showChat( String msg, String fromPlayer )
public void showChat( String msg, int fromIndx, String fromName )
{
subclassOverride( "showChat" );
}

View file

@ -34,18 +34,37 @@ public class XwJNI {
public static class GamePtr {
private int m_ptr = 0;
private int m_refCount = 0;
private long m_rowid;
private GamePtr( int ptr ) { m_ptr = ptr; }
private GamePtr( int ptr, long rowid ) {
m_ptr = ptr;
m_rowid = rowid;
retain();
}
public int ptr() { Assert.assertTrue( 0 != m_ptr ); return m_ptr; }
public synchronized GamePtr retain()
{
++m_refCount;
DbgUtils.logdf( "GamePtr.retain(this=%H, rowid=%d): refCount now %d",
this, m_rowid, m_refCount );
return this;
}
// Force (via an assert in finalize() below) that this is called. It's
// better if jni stuff isn't being done on the finalizer thread
public void release()
public synchronized void release()
{
if ( 0 != m_ptr ) {
game_dispose( this );
m_ptr = 0;
--m_refCount;
DbgUtils.logdf( "GamePtr.release(this=%H, rowid=%d): refCount now %d",
this, m_rowid, m_refCount );
if ( 0 == m_refCount ) {
if ( 0 != m_ptr ) {
game_dispose( this );
m_ptr = 0;
}
}
}
@ -134,7 +153,7 @@ public class XwJNI {
int seed = Utils.nextRandomInt();
String tag = String.format( "%d", rowid );
int ptr = initJNI( getJNI().m_ptr, seed, tag );
GamePtr result = 0 == ptr ? null : new GamePtr( ptr );
GamePtr result = 0 == ptr ? null : new GamePtr( ptr, rowid );
return result;
}
@ -151,14 +170,14 @@ public class XwJNI {
public static native void game_makeNewGame( GamePtr gamePtr,
CurGameInfo gi,
UtilCtxt util,
JNIUtils jniu,
DrawCtx draw, CommonPrefs cp,
TransportProcs procs,
String[] dictNames,
byte[][] dictBytes,
String[] dictPaths,
String langName );
String langName,
UtilCtxt util,
JNIUtils jniu,
DrawCtx draw, CommonPrefs cp,
TransportProcs procs );
public static native boolean game_makeFromStream( GamePtr gamePtr,
byte[] stream,
@ -179,18 +198,18 @@ public class XwJNI {
JNIUtils jniu, CommonPrefs cp,
String[] dictNames, byte[][] dictBytes,
String[] dictPaths, String langName ) {
game_makeNewGame( gamePtr, gi, (UtilCtxt)null, jniu,
(DrawCtx)null, cp, (TransportProcs)null,
dictNames, dictBytes, dictPaths, langName );
game_makeNewGame( gamePtr, gi, dictNames, dictBytes, dictPaths, langName,
(UtilCtxt)null, jniu, (DrawCtx)null, cp,
(TransportProcs)null );
}
public static void game_makeNewGame( GamePtr gamePtr, CurGameInfo gi,
JNIUtils jniu, CommonPrefs cp,
TransportProcs procs,
String[] dictNames, byte[][] dictBytes,
String[] dictPaths, String langName ) {
game_makeNewGame( gamePtr, gi, (UtilCtxt)null, jniu, (DrawCtx)null,
cp, procs, dictNames, dictBytes, dictPaths, langName );
String[] dictPaths, String langName,
JNIUtils jniu, CommonPrefs cp,
TransportProcs procs ) {
game_makeNewGame( gamePtr, gi, dictNames, dictBytes, dictPaths, langName,
(UtilCtxt)null, jniu, (DrawCtx)null, cp, procs );
}
public static boolean game_makeFromStream( GamePtr gamePtr,
@ -296,6 +315,7 @@ public class XwJNI {
public static native boolean board_commitTurn( GamePtr gamePtr );
public static native boolean board_flip( GamePtr gamePtr );
public static native boolean board_replaceTiles( GamePtr gamePtr );
public static native int board_getSelPlayer( GamePtr gamePtr );
public static native boolean board_redoReplacedTiles( GamePtr gamePtr );
public static native void board_resetEngine( GamePtr gamePtr );
public static native boolean board_requestHint( GamePtr gamePtr,

View file

@ -35,9 +35,9 @@ public class LocActivity extends XWActivity {
super.onCreate( savedInstanceState, m_dlgt );
} // onCreate
@Override
public boolean onCreateOptionsMenu( Menu menu )
{
return m_dlgt.onCreateOptionsMenu( menu );
}
// @Override
// public boolean onCreateOptionsMenu( Menu menu )
// {
// return m_dlgt.onCreateOptionsMenu( menu );
// }
}

View file

@ -1,10 +1,10 @@
#!/bin/sh
#!/bin/bash
set -u -e
usage() {
[ $# -ge 1 ] && echo "Error: $1"
echo "usage: $(basename $0) [-e|-d] [-p /path/to/.apk]"
echo "usage: $(basename $0) [-e|-d] (-p /path/to/.apk)+"
exit 1
}
@ -12,8 +12,13 @@ if [ ! -e build.xml ]; then
usage "No build.xml; please run me from the top android directory"
fi
APK=./bin/XWords4-debug.apk
APKS=''
DEVICES=''
DIRNAME=$(basename $(pwd))
ADB="$(which adb)"
MAIN=GamesListActivity
MAIN=Main
case $DIRNAME in
XWords4-dbg)
PKG=xw4dbg
@ -29,22 +34,20 @@ case $DIRNAME in
;;
esac
DEVICES=''
while [ $# -ge 1 ]; do
case $1 in
-e)
DEV="$(adb devices | grep '^emulator' | awk '{print $1}')"
DEV="$($ADB devices | grep '^emulator' | awk '{print $1}')"
DEVICES="$DEVICES $DEV"
;;
-d)
DEV="$(adb devices | grep -v emulator | grep 'device$' | awk '{print $1}')"
DEV="$($ADB devices | grep -v emulator | grep 'device$' | awk '{print $1}')"
DEVICES="$DEVICES $DEV"
;;
-p)
[ $# -gt 1 ] || usage "-p requires an argument"
shift
APK=$1
APKS="$APKS $1"
;;
*)
usage
@ -53,22 +56,30 @@ while [ $# -ge 1 ]; do
shift
done
[ -e $APK ] || usage "$APK not found"
if [ -z "$DEVICES" ]; then
while read LINE; do
if echo $LINE | grep -q "device$"; then
DEVICE=$(echo $LINE | awk '{print $1}')
DEVICES="$DEVICES $DEVICE"
fi
done <<< "$($ADB devices)"
fi
if [ -n "$DEVICES" ]; then
echo "installing this binary. Check the age..."
ls -l $APK
echo ""
# If no apk specified, take the newest built
if [ -z "$APKS" ]; then
APKS=$(ls -t bin/*.apk | head -n 1)
fi
COUNT=0
for DEVICE in $DEVICES; do
echo $DEVICE
adb -s $DEVICE install -r $APK
adb -s $DEVICE shell am start \
-n org.eehouse.android.${PKG}/org.eehouse.android.${PKG}.GamesListActivity
COUNT=$((COUNT+1))
for APK in $APKS; do
echo "installing $APK; details:"
ls -l $APK
$ADB -s $DEVICE install -r $APK
$ADB -s $DEVICE shell am start \
-n org.eehouse.android.${PKG}/org.eehouse.android.${PKG}.${MAIN}
done
COUNT=$((COUNT+1))
done
echo "installed into $COUNT devices/emulators"

View file

@ -0,0 +1,60 @@
#!/bin/bash
set -e -u
SERIAL=''
declare -a SERIALS=[]
ADB=$(which adb)
usage() {
[ $# -ge 1 ] && echo "ERROR: $1"
echo "usage $0 # start logcat, allowing choice if more than one device available"
exit 1
}
listSerials() {
COUNT=0
while read LINE; do
SERIALS[$COUNT]=$(echo $LINE | awk '{print $1}')
COUNT=$((COUNT+1))
done <<< "$(adb devices | grep 'device$')"
}
setSerial() {
NSERIALS=${#SERIALS[@]}
while :; do
echo "There are multiple devices. Please type the index (1-$NSERIALS) of the one you want"
COUNT=0
while read LINE; do
echo -n "$((COUNT+1)): "
echo ${SERIALS[$COUNT]}
COUNT=$((COUNT+1))
done <<< "$(adb devices | grep 'device$')"
read TYPED
if [ $TYPED -gt 0 -a $TYPED -le $COUNT ]; then
SERIAL="-s ${SERIALS[$((TYPED-1))]}"
break
fi
echo "$TYPED is not between 1 and $NSERIALS"
done
}
[ $# -ge 1 ] && usage "unexpected parameter: $1"
listSerials
COUNT=${#SERIALS[@]}
echo "COUNT: $COUNT"
case $COUNT in
0)
usage "no devices found"
;;
1)
SERIAL="-s $SERIALS[0]"
;;
*)
setSerial
;;
esac
$ADB $SERIAL logcat

View file

@ -1,6 +1,6 @@
#!/bin/bash
set -e -u
set -e -u -x
# use this: adb -s 04bd25af2523aae6 shell "pm list packages" | grep org.eehouse
@ -29,7 +29,7 @@ getPackage() {
WD=$(pwd)
while :; do
if [ -e ${WD}/android/XWords4/AndroidManifest.xml -a -d ${WD}/common ]; then
if [ -e ${WD}/AndroidManifest.xml -a -e ${WD}/build.xml ]; then
break
elif [ ${WD} = '/' ]; then
usage "reached / without finding AndroidManifest.xml"
@ -37,7 +37,7 @@ while :; do
WD=$(cd $WD/.. && pwd)
fi
done
WD=$(cd $WD/android/XWords4 && pwd)
WD=$(cd $WD && pwd)
# find aapt
DIR=$(dirname $(which android))

View file

@ -5,7 +5,7 @@ set -e -u
usage () {
echo "usage: $(basename $0) [--tag tagname | --branch branchname] [--variant variant]"
echo " # (uses current branch as default)"
exit 0
exit 1
}
TAG=""

View file

@ -74,13 +74,17 @@ esac
# generate local.properties
[ -e local.properties ] || ../scripts/setup_local_props.sh
ant $CMDS
# If this fails, the "set -e" above means we won't try to install anything
[ -n "$CMDS" ] && ant $CMDS
if [ -n "$UNINSTALL" ]; then
uninstall
fi
if [ -n "$INSTALL" ]; then
adb-install.sh -e -d
# Find the newest apk in this directory and install it
APK=$(ls -t bin/*.apk | head -n 1)
adb-install.sh -p $APK
fi
# if [ "$CMDS" != "${CMDS%%install}" ]; then

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