mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-08 05:24:39 +01:00
Merge branch 'android_branch' into android_translate
This commit is contained in:
commit
1625ec9062
116 changed files with 2778 additions and 1675 deletions
|
@ -1,8 +1,10 @@
|
|||
git:
|
||||
depth: false
|
||||
language: android
|
||||
jdk: oraclejdk8
|
||||
env:
|
||||
global:
|
||||
- ANDROID_TARGET=android-15
|
||||
- ANDROID_TARGET=android-14
|
||||
- ANDROID_ABI=armeabi-v7a,x86
|
||||
- secure: d8PwteM+xp1IRU3QkvmHtxh+1Ta9n/kl/SJ3EZa3iColVVXY1etzjY3cKrEGKKMJuI4be30kPzvNw9/BVTawDpnU9/NtWqykJ8QHXNWnZIvUQ/kxHBS1DbcstmcYU9gvR83EFb8BT+Y9frpNfMcZDlSvBpEGqDQEPmxiDzSmjdUmJJQWStncxL9pE+lCdM6lHBgtfYoMMiqCQF/DxkQisjyUVF4mbTGuT9JOOWjVsTGPA7ehzsWDHoJ3p2ai8UKHAYucUWZcTt4rkq9l35ExvgKd3L8luk8U3X3Fk9yzVhPJC56T0XNbNrsQ2W7/7oGRv6EQFV3aKDZimJ7CVjBcEjZmPxeUVvCsMW8XB41ZvYcy6xsjF96oyjn1gb0r/2mZbTaWP0izSTwMYZ5vFNKUamDtRZgrneD0lfvXgfTzirrCU7FqO2RH7ZK5PQpSgSoZxKsKyeyFPEa2ihivc95rz1MS6mamle9wrIlSAgEGcaZMIYvKiOnCLk7CZCKuwm2dhYPgzCHW3PUopay59BBwMsSqWpxsiHEr5jYGpb0pHGbzPTJNUpg1LNQX5eMQOMlEt7rfpoC7JG24hR9vxl4Yf9LhxYlSwUiPy7TYHdbA0kUS68skfzxU6+ekWZF2QFM+L4vWCYmEHDy7n+I0df+PavycgNW989ROlAKhQjtMyqM=
|
||||
android:
|
||||
|
@ -10,7 +12,7 @@ android:
|
|||
- tools
|
||||
- platform-tools
|
||||
- build-tools-27.0.3
|
||||
- android-23
|
||||
- android-26
|
||||
licenses:
|
||||
- android-sdk-preview-license-.+
|
||||
- android-sdk-license-.+
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
def INITIAL_CLIENT_VERS = 8
|
||||
def VERSION_CODE_BASE = 135
|
||||
def VERSION_NAME = '4.4.139'
|
||||
def VERSION_CODE_BASE = 136
|
||||
def VERSION_NAME = '4.4.140'
|
||||
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
|
||||
def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID")
|
||||
def BUILD_INFO_NAME = "build-info.txt"
|
||||
|
@ -23,8 +23,8 @@ if ( FABRIC_API_KEY && hasProperty('useCrashlytics') ) {
|
|||
apply plugin: 'io.fabric'
|
||||
}
|
||||
repositories {
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
google()
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -32,8 +32,8 @@ android {
|
|||
// default changes and .travis.yml can be kept in sync
|
||||
buildToolsVersion '27.0.3'
|
||||
defaultConfig {
|
||||
minSdkVersion 8
|
||||
targetSdkVersion 23
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode VERSION_CODE_BASE
|
||||
versionName VERSION_NAME
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ android {
|
|||
productFlavors {
|
||||
all {
|
||||
buildConfigField "String", "BUILD_INFO_NAME", "\"${BUILD_INFO_NAME}\""
|
||||
resValue "string", "invite_prefix", "/and/"
|
||||
}
|
||||
|
||||
xw4 {
|
||||
|
@ -71,7 +72,6 @@ android {
|
|||
manifestPlaceholders = [ APP_ID: applicationId ]
|
||||
resValue "string", "app_name", "CrossWords"
|
||||
resValue "string", "nbs_port", "3344"
|
||||
resValue "string", "invite_prefix", "/and/"
|
||||
buildConfigField "boolean", "WIDIR_ENABLED", "false"
|
||||
buildConfigField "boolean", "RELAYINVITE_SUPPORTED", "false"
|
||||
|
||||
|
@ -84,7 +84,6 @@ android {
|
|||
manifestPlaceholders = [ APP_ID: applicationId ]
|
||||
resValue "string", "app_name", "CrossWords"
|
||||
resValue "string", "nbs_port", "3344"
|
||||
resValue "string", "invite_prefix", "/and/"
|
||||
buildConfigField "boolean", "WIDIR_ENABLED", "false"
|
||||
buildConfigField "boolean", "RELAYINVITE_SUPPORTED", "false"
|
||||
|
||||
|
@ -96,7 +95,6 @@ android {
|
|||
manifestPlaceholders = [ FABRIC_API_KEY: "$FABRIC_API_KEY", APP_ID: applicationId, ]
|
||||
resValue "string", "app_name", "CrossDbg"
|
||||
resValue "string", "nbs_port", "3345"
|
||||
resValue "string", "invite_prefix", "/anddbg/"
|
||||
buildConfigField "boolean", "WIDIR_ENABLED", "true"
|
||||
buildConfigField "boolean", "RELAYINVITE_SUPPORTED", "true"
|
||||
|
||||
|
@ -197,11 +195,19 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
SUPPORT_LIB_VERSION = '27.1.1'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Look into replacing this with a fetch too PENDING
|
||||
xw4Implementation files('../libs/gcm.jar')
|
||||
|
||||
implementation 'com.android.support:support-v4:23.4.0'
|
||||
implementation "com.android.support:support-v4:$SUPPORT_LIB_VERSION"
|
||||
implementation "com.android.support:support-compat:$SUPPORT_LIB_VERSION"
|
||||
|
||||
implementation "android.arch.lifecycle:extensions:1.1.1"
|
||||
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"
|
||||
|
||||
// 2.6.8 is probably as far forward as I can go without upping my
|
||||
// min-supported SDK version
|
||||
|
@ -222,7 +228,13 @@ task copyStrings(type: Exec) {
|
|||
|
||||
task ndkSetup(type: Exec) {
|
||||
workingDir '../'
|
||||
commandLine "./scripts/ndksetup.sh", "--with-clang", "--arm-only"
|
||||
// remove ', "--arm-only"' for Genymotion builds
|
||||
|
||||
// I'm putting ARM back for a while. It's too much trouble having
|
||||
// builds, including those built by travis, that don't run on the
|
||||
// emulator. Maybe remove this change before each release?
|
||||
commandLine "./scripts/ndksetup.sh", "--with-clang"
|
||||
// commandLine "./scripts/ndksetup.sh", "--with-clang", "--arm-only"
|
||||
}
|
||||
|
||||
task myPreBuild(dependsOn: ['ndkSetup', 'mkImages', 'copyStrings', 'mkXml']) {
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<!-- Required for wifi-direct -->
|
||||
<!-- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> -->
|
||||
|
@ -113,6 +115,7 @@
|
|||
<receiver android:name="OnBootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name="RelayReceiver"/>
|
||||
|
@ -179,9 +182,17 @@
|
|||
<activity android:name=".loc.LocItemEditActivity"
|
||||
/>
|
||||
|
||||
<service android:name="RelayService"/>
|
||||
<service android:name="BTService"/>
|
||||
<service android:name="SMSService"/>
|
||||
<service android:name="BTService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false"
|
||||
/>
|
||||
<service android:name="SMSService"
|
||||
android:exported="false"
|
||||
/>
|
||||
<service android:name="RelayService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
/>
|
||||
|
||||
<receiver android:name=".MountEventReceiver">
|
||||
<intent-filter>
|
||||
|
@ -201,6 +212,9 @@
|
|||
<intent-filter>
|
||||
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.eehouse.android.ACTION_STOP_BT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="SMSReceiver" >
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>CrossWords 4.4.139 release</h2>
|
||||
<h2>CrossWords 4.4.140 release</h2>
|
||||
|
||||
<p>This release fixes a bug with picking tiles face-up</p>
|
||||
<p>This release updates translations for Catalan, French, German and Japanese.</p>
|
||||
|
||||
<div id="survey">
|
||||
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
|
||||
|
@ -25,19 +25,18 @@
|
|||
|
||||
<h3>New with this release</h3>
|
||||
<ul>
|
||||
<li>Don't put up multiple tile pickers. (This feature is rarely
|
||||
used, but had apparently been broken for a while.)</li>
|
||||
<li>Fix two more bugs reported by users (Thanks!)</li>
|
||||
<li>Include new translations for Catalan and Norwegian</li>
|
||||
<li>Include changes made by Weblate volunteers for four
|
||||
languages. (Thanks!)</li>
|
||||
</ul>
|
||||
|
||||
<p>(The full changelog
|
||||
<p>(The full changelog
|
||||
is <a href="http://xwords.sf.net/and_changes.php">here</a>.)</p>
|
||||
|
||||
<h3>Next up</h3>
|
||||
<ul>
|
||||
<li>Continue work to support WiFi Direct (currently working
|
||||
sporadically)</li>
|
||||
<li>Make changes in how background networking and bluetooth work
|
||||
that Google's mandating for all releases after 1
|
||||
November.</li>
|
||||
</ul>
|
||||
|
||||
<p>Please let me know
|
||||
|
|
|
@ -33,7 +33,6 @@ import android.widget.TextView;
|
|||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2012 by Eric House (xwords@eehouse.org). All rights
|
||||
* reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package org.eehouse.android.xw4;
|
||||
|
||||
public class Assert {
|
||||
private static final String TAG = Assert.class.getSimpleName();
|
||||
|
||||
public static void fail() {
|
||||
assertTrue(false);
|
||||
}
|
||||
|
||||
public static void assertFalse(boolean val)
|
||||
{
|
||||
assertTrue(! val);
|
||||
}
|
||||
|
||||
public static void assertTrue(boolean val) {
|
||||
if (! val) {
|
||||
Log.e( TAG, "firing assert!" );
|
||||
DbgUtils.printStack( TAG );
|
||||
junit.framework.Assert.fail();
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotNull( Object val )
|
||||
{
|
||||
assertTrue( val != null );
|
||||
}
|
||||
|
||||
public static void assertNull( Object val )
|
||||
{
|
||||
assertTrue( val == null );
|
||||
}
|
||||
|
||||
public static void assertEquals( Object obj1, Object obj2 )
|
||||
{
|
||||
assertTrue( (obj1 == null && obj2 == null)
|
||||
|| (obj1 != null && obj1.equals(obj2)) );
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2009 - 2016 by Eric House (xwords@eehouse.org). All rights
|
||||
* Copyright 2009 - 2019 by Eric House (xwords@eehouse.org). All rights
|
||||
* reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -22,26 +22,87 @@ package org.eehouse.android.xw4;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class BTInviteDelegate extends InviteDelegate {
|
||||
private static final int[] BUTTONIDS = { R.id.button_add,
|
||||
private static final String TAG = BTInviteDelegate.class.getSimpleName();
|
||||
private static final String KEY_PERSIST = TAG + "_persist";
|
||||
private static final int[] BUTTONIDS = { R.id.button_scan,
|
||||
R.id.button_settings,
|
||||
R.id.button_clear,
|
||||
};
|
||||
private static final boolean ENABLE_FAKER = false;
|
||||
|
||||
private Activity m_activity;
|
||||
private TwoStringPair[] m_pairs;
|
||||
private ProgressDialog m_progress;
|
||||
|
||||
private static class Persisted implements Serializable {
|
||||
List<TwoStringPair> pairs;
|
||||
// HashMap: m_stamps is serialized, so can't be abstract type
|
||||
HashMap<String, Long> stamps = new HashMap<>();
|
||||
|
||||
void add( String devAddress, String devName ) {
|
||||
// If it's already there, update it. Otherwise create new
|
||||
boolean alreadyHave = false;
|
||||
if ( null == pairs ) {
|
||||
pairs = new ArrayList<>();
|
||||
} else {
|
||||
for ( TwoStringPair pair : pairs ) {
|
||||
alreadyHave = pair.str2.equals(devName);
|
||||
if ( alreadyHave ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !alreadyHave ) {
|
||||
pairs.add( new TwoStringPair( devAddress, devName ) );
|
||||
}
|
||||
stamps.put( devName, System.currentTimeMillis() );
|
||||
sort();
|
||||
}
|
||||
|
||||
void remove(final Set<? extends InviterItem> checked)
|
||||
{
|
||||
for ( InviterItem item : checked ) {
|
||||
TwoStringPair pair = (TwoStringPair)item;
|
||||
stamps.remove( pair.str2 );
|
||||
pairs.remove( pair );
|
||||
}
|
||||
}
|
||||
|
||||
boolean empty() { return pairs == null || pairs.size() == 0; }
|
||||
|
||||
private void sort()
|
||||
{
|
||||
Collections.sort( pairs, new Comparator<TwoStringPair>() {
|
||||
@Override
|
||||
public int compare( TwoStringPair rec1, TwoStringPair rec2 ) {
|
||||
long val1 = stamps.get( rec1.str2 );
|
||||
long val2 = stamps.get( rec2.str2 );
|
||||
return (int)(val2 - val1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
private Persisted mPersisted;
|
||||
|
||||
public static void launchForResult( Activity activity, int nMissing,
|
||||
SentInvitesInfo info,
|
||||
|
@ -68,27 +129,38 @@ public class BTInviteDelegate extends InviteDelegate {
|
|||
@Override
|
||||
protected void init( Bundle savedInstanceState )
|
||||
{
|
||||
String msg = getString( R.string.bt_pick_addall_button );
|
||||
msg = getQuantityString( R.plurals.invite_bt_desc_fmt, m_nMissing,
|
||||
m_nMissing, msg );
|
||||
String msg = getQuantityString( R.plurals.invite_bt_desc_fmt_2, m_nMissing,
|
||||
m_nMissing )
|
||||
+ getString( R.string.invite_bt_desc_postscript );
|
||||
super.init( msg, 0 );
|
||||
|
||||
addButtonBar( R.layout.bt_buttons, BUTTONIDS );
|
||||
BTService.clearDevices( m_activity, null ); // will return names
|
||||
|
||||
load();
|
||||
if ( mPersisted.empty() ) {
|
||||
scan();
|
||||
} else {
|
||||
updateListAdapter( mPersisted.pairs );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBarButtonClicked( int id )
|
||||
{
|
||||
switch( id ) {
|
||||
case R.id.button_add:
|
||||
case R.id.button_scan:
|
||||
scan();
|
||||
break;
|
||||
case R.id.button_settings:
|
||||
BTService.openBTSettings( m_activity );
|
||||
break;
|
||||
case R.id.button_clear:
|
||||
removeSelected();
|
||||
mPersisted.remove( getChecked() );
|
||||
store();
|
||||
|
||||
clearChecked();
|
||||
updateListAdapter( mPersisted.pairs );
|
||||
tryEnable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -101,20 +173,24 @@ public class BTInviteDelegate extends InviteDelegate {
|
|||
case SCAN_DONE:
|
||||
post( new Runnable() {
|
||||
public void run() {
|
||||
synchronized( BTInviteDelegate.this ) {
|
||||
m_progress.cancel();
|
||||
|
||||
m_pairs = null;
|
||||
if ( 0 < args.length ) {
|
||||
m_pairs = TwoStringPair.make( (String[])(args[0]),
|
||||
(String[])(args[1]) );
|
||||
}
|
||||
|
||||
updateListAdapter( m_pairs );
|
||||
tryEnable();
|
||||
if ( mPersisted.empty() ) {
|
||||
makeNotAgainBuilder( R.string.not_again_emptybtscan,
|
||||
R.string.key_notagain_emptybtscan )
|
||||
.show();
|
||||
}
|
||||
}
|
||||
} );
|
||||
break;
|
||||
case HOST_PONGED:
|
||||
post( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processScanResult( (BluetoothDevice)args[0] );
|
||||
}
|
||||
} );
|
||||
break;
|
||||
default:
|
||||
super.eventOccurred( event, args );
|
||||
}
|
||||
|
@ -123,10 +199,18 @@ public class BTInviteDelegate extends InviteDelegate {
|
|||
@Override
|
||||
protected void onChildAdded( View child, InviterItem data )
|
||||
{
|
||||
TwoStrsItem item = (TwoStrsItem)child;
|
||||
TwoStringPair pair = (TwoStringPair)data;
|
||||
// null: we don't display mac address
|
||||
((TwoStrsItem)child).setStrings( pair.str2, null );
|
||||
String devName = ((TwoStringPair)data).str2;
|
||||
|
||||
String msg = null;
|
||||
if ( mPersisted.stamps.containsKey( devName ) ) {
|
||||
CharSequence elapsed = DateUtils
|
||||
.getRelativeTimeSpanString( mPersisted.stamps.get( devName ),
|
||||
System.currentTimeMillis(),
|
||||
DateUtils.SECOND_IN_MILLIS );
|
||||
msg = getString( R.string.bt_scan_age_fmt, elapsed );
|
||||
}
|
||||
|
||||
((TwoStrsItem)child).setStrings( devName, msg );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -142,9 +226,16 @@ public class BTInviteDelegate extends InviteDelegate {
|
|||
|
||||
private void scan()
|
||||
{
|
||||
if ( ENABLE_FAKER && Utils.nextRandomInt() % 5 == 0 ) {
|
||||
mPersisted.add( "00:00:00:00:00:00", "Do Not Invite Me" );
|
||||
}
|
||||
|
||||
int count = BTService.getPairedCount( m_activity );
|
||||
if ( 0 < count ) {
|
||||
BTService.scan( m_activity );
|
||||
String msg = getQuantityString( R.plurals.bt_scan_progress_fmt, count, count );
|
||||
m_progress = ProgressDialog.show( m_activity, msg, null, true, true );
|
||||
|
||||
BTService.scan( m_activity, 5000 );
|
||||
} else {
|
||||
makeConfirmThenBuilder( R.string.bt_no_devs,
|
||||
Action.OPEN_BT_PREFS_ACTION )
|
||||
|
@ -153,17 +244,34 @@ public class BTInviteDelegate extends InviteDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
private void removeSelected()
|
||||
private void processScanResult( BluetoothDevice dev )
|
||||
{
|
||||
Set<InviterItem> checked = getChecked();
|
||||
String[] devs = new String[checked.size()];
|
||||
Iterator<InviterItem> iter = checked.iterator();
|
||||
for ( int ii = 0; iter.hasNext(); ++ii ) {
|
||||
TwoStringPair pair = (TwoStringPair)iter.next();
|
||||
devs[ii] = pair.str1;
|
||||
DbgUtils.assertOnUIThread();
|
||||
|
||||
mPersisted.add( dev.getAddress(), dev.getName() );
|
||||
store();
|
||||
|
||||
updateListAdapter( mPersisted.pairs );
|
||||
tryEnable();
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
try {
|
||||
String str64 = DBUtils.getStringFor( m_activity, KEY_PERSIST, null );
|
||||
mPersisted = (Persisted)Utils.string64ToSerializable( str64 );
|
||||
} catch ( Exception ex ) {} // NPE, de-serialization problems, etc.
|
||||
|
||||
if ( null == mPersisted ) {
|
||||
mPersisted = new Persisted();
|
||||
}
|
||||
BTService.clearDevices( m_activity, devs );
|
||||
}
|
||||
|
||||
private void store()
|
||||
{
|
||||
String str64 = mPersisted == null
|
||||
? "" : Utils.serializableToString64( mPersisted );
|
||||
DBUtils.setStringFor( m_activity, KEY_PERSIST, str64 );
|
||||
}
|
||||
|
||||
// DlgDelegate.DlgClickNotify interface
|
||||
|
|
|
@ -29,6 +29,9 @@ import android.content.Intent;
|
|||
public class BTReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = BTReceiver.class.getSimpleName();
|
||||
|
||||
// This string is also used in AndroidManifest (as a string literal)
|
||||
static final String ACTION_STOP_BT = "org.eehouse.android.ACTION_STOP_BT";
|
||||
|
||||
@Override
|
||||
public void onReceive( Context context, Intent intent )
|
||||
{
|
||||
|
@ -37,9 +40,14 @@ public class BTReceiver extends BroadcastReceiver {
|
|||
Log.d( TAG, "BTReceiver.onReceive(action=%s, intent=%s)",
|
||||
action, intent.toString() );
|
||||
|
||||
if ( action.equals( BluetoothDevice.ACTION_ACL_CONNECTED ) ) {
|
||||
BTService.startService( context );
|
||||
} else if ( action.equals( BluetoothAdapter.ACTION_STATE_CHANGED ) ) {
|
||||
switch (action ) {
|
||||
case ACTION_STOP_BT:
|
||||
BTService.stopBackground( context );
|
||||
break;
|
||||
case BluetoothDevice.ACTION_ACL_CONNECTED:
|
||||
BTService.onACLConnected( context );
|
||||
break;
|
||||
case BluetoothAdapter.ACTION_STATE_CHANGED:
|
||||
int newState =
|
||||
intent.getIntExtra( BluetoothAdapter.EXTRA_STATE, -1 );
|
||||
switch ( newState ) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,7 +27,6 @@ import java.net.Socket;
|
|||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class BiDiSockWrap {
|
||||
private static final String TAG = BiDiSockWrap.class.getSimpleName();
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.graphics.RectF;
|
|||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.BoardDims;
|
||||
import org.eehouse.android.xw4.jni.CommonPrefs;
|
||||
|
|
|
@ -29,7 +29,6 @@ import android.view.View;
|
|||
import android.view.View.MeasureSpec;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class BoardContainer extends ViewGroup {
|
||||
private static final String TAG = BoardContainer.class.getSimpleName();
|
||||
|
|
|
@ -49,7 +49,6 @@ import java.util.Map;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
|
@ -188,16 +187,14 @@ public class BoardDelegate extends DelegateBase
|
|||
}
|
||||
break;
|
||||
|
||||
case GAME_OVER:
|
||||
case DLG_CONNSTAT: {
|
||||
case GAME_OVER: {
|
||||
GameSummary summary = (GameSummary)params[0];
|
||||
int title = (Integer)params[1];
|
||||
String msg = (String)params[2];
|
||||
ab.setTitle( title )
|
||||
.setMessage( msg )
|
||||
.setPositiveButton( android.R.string.ok, null );
|
||||
if ( DlgID.GAME_OVER == dlgID
|
||||
&& rematchSupported( m_activity, true, summary ) ) {
|
||||
if ( rematchSupported( m_activity, true, summary ) ) {
|
||||
lstnr = new OnClickListener() {
|
||||
public void onClick( DialogInterface dlg,
|
||||
int whichButton ) {
|
||||
|
@ -216,25 +213,6 @@ public class BoardDelegate extends DelegateBase
|
|||
};
|
||||
ab.setNeutralButton( R.string.button_archive, lstnr );
|
||||
}
|
||||
|
||||
} else if ( DlgID.DLG_CONNSTAT == dlgID
|
||||
&& BuildConfig.DEBUG && null != m_connTypes
|
||||
&& (m_connTypes.contains( CommsConnType.COMMS_CONN_RELAY )
|
||||
|| m_connTypes.contains( CommsConnType.COMMS_CONN_P2P )) ) {
|
||||
|
||||
lstnr = new OnClickListener() {
|
||||
public void onClick( DialogInterface dlg,
|
||||
int whichButton ) {
|
||||
NetStateCache.reset( m_activity );
|
||||
if ( m_connTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
|
||||
RelayService.reset( m_activity );
|
||||
}
|
||||
if ( m_connTypes.contains( CommsConnType.COMMS_CONN_P2P ) ) {
|
||||
WiDirService.reset( m_activity );
|
||||
}
|
||||
}
|
||||
};
|
||||
ab.setNegativeButton( R.string.button_reconnect, lstnr );
|
||||
}
|
||||
dialog = ab.create();
|
||||
}
|
||||
|
@ -536,7 +514,7 @@ public class BoardDelegate extends DelegateBase
|
|||
finish();
|
||||
}
|
||||
};
|
||||
alert.setNoDismissListenerNeg( ab, R.string.button_wait, lstnr );
|
||||
alert.setNoDismissListenerNeg( ab, R.string.button_close, lstnr );
|
||||
}
|
||||
|
||||
dialog = ab.create();
|
||||
|
@ -588,17 +566,21 @@ public class BoardDelegate extends DelegateBase
|
|||
m_haveInvited = args.getBoolean( GameUtils.INVITED, false );
|
||||
m_overNotShown = true;
|
||||
|
||||
m_jniThreadRef = JNIThread.getRetained( m_rowid, true );
|
||||
// getRetained() can in threory fail to get the lock and so will
|
||||
// return null. Let m_jniThreadRef stay null in that case; doResume()
|
||||
// will finish() in that case.
|
||||
m_jniThreadRef = JNIThread.getRetained( m_activity, m_rowid, true );
|
||||
if ( null != m_jniThreadRef ) {
|
||||
// see http://stackoverflow.com/questions/680180/where-to-stop- \
|
||||
// destroy-threads-in-android-service-class
|
||||
m_jniThreadRef.setDaemonOnce( true );
|
||||
m_jniThreadRef.startOnce();
|
||||
|
||||
// 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...
|
||||
|
||||
NFCUtils.register( m_activity, this ); // Don't seem to need to unregister...
|
||||
|
||||
setBackgroundColor();
|
||||
setKeepScreenOn();
|
||||
setBackgroundColor();
|
||||
setKeepScreenOn();
|
||||
}
|
||||
} // init
|
||||
|
||||
@Override
|
||||
|
@ -628,7 +610,7 @@ public class BoardDelegate extends DelegateBase
|
|||
@Override
|
||||
protected void onStop()
|
||||
{
|
||||
if ( isFinishing() ) {
|
||||
if ( isFinishing() && null != m_jniThreadRef ) {
|
||||
m_jniThreadRef.release();
|
||||
m_jniThreadRef = null;
|
||||
}
|
||||
|
@ -1217,7 +1199,9 @@ public class BoardDelegate extends DelegateBase
|
|||
RequestCode.BT_INVITE_RESULT );
|
||||
break;
|
||||
case SMS:
|
||||
Perms23.tryGetPerms( this, Perm.SEND_SMS, R.string.sms_invite_rationale,
|
||||
Perms23.tryGetPerms( this, new Perm[] { Perm.SEND_SMS,
|
||||
Perm.RECEIVE_SMS },
|
||||
R.string.sms_invite_rationale,
|
||||
Action.INVITE_SMS, m_mySIS.nMissing, info );
|
||||
break;
|
||||
case RELAY:
|
||||
|
@ -1468,6 +1452,7 @@ public class BoardDelegate extends DelegateBase
|
|||
//////////////////////////////////////////////////
|
||||
// ConnStatusHandler.ConnStatusCBacks
|
||||
//////////////////////////////////////////////////
|
||||
@Override
|
||||
public void invalidateParent()
|
||||
{
|
||||
runOnUiThread(new Runnable() {
|
||||
|
@ -1478,22 +1463,13 @@ public class BoardDelegate extends DelegateBase
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusClicked()
|
||||
{
|
||||
final String msg = ConnStatusHandler.getStatusText( m_activity, m_connTypes );
|
||||
post( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if ( null == msg ) {
|
||||
askNoAddrsDelete();
|
||||
} else {
|
||||
showDialogFragment( DlgID.DLG_CONNSTAT, null,
|
||||
R.string.info_title, msg );
|
||||
}
|
||||
}
|
||||
} );
|
||||
onStatusClicked( m_jniGamePtr );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Handler getHandler()
|
||||
{
|
||||
return m_handler;
|
||||
|
@ -1506,15 +1482,6 @@ public class BoardDelegate extends DelegateBase
|
|||
finish();
|
||||
}
|
||||
|
||||
private void askNoAddrsDelete()
|
||||
{
|
||||
makeConfirmThenBuilder( R.string.connstat_net_noaddr,
|
||||
Action.DELETE_AND_EXIT )
|
||||
.setPosButton( R.string.list_item_delete )
|
||||
.setNegButton( R.string.button_close_game )
|
||||
.show();
|
||||
}
|
||||
|
||||
private void askDropRelay()
|
||||
{
|
||||
String msg = getString( R.string.confirm_drop_relay );
|
||||
|
@ -1853,46 +1820,52 @@ public class BoardDelegate extends DelegateBase
|
|||
{
|
||||
int resid = 0;
|
||||
boolean asToast = false;
|
||||
String msg = null;
|
||||
switch( code ) {
|
||||
case UtilCtxt.ERR_TILES_NOT_IN_LINE:
|
||||
case ERR_TILES_NOT_IN_LINE:
|
||||
resid = R.string.str_tiles_not_in_line;
|
||||
break;
|
||||
case UtilCtxt.ERR_NO_EMPTIES_IN_TURN:
|
||||
case ERR_NO_EMPTIES_IN_TURN:
|
||||
resid = R.string.str_no_empties_in_turn;
|
||||
break;
|
||||
case UtilCtxt.ERR_TWO_TILES_FIRST_MOVE:
|
||||
case ERR_TWO_TILES_FIRST_MOVE:
|
||||
resid = R.string.str_two_tiles_first_move;
|
||||
break;
|
||||
case UtilCtxt.ERR_TILES_MUST_CONTACT:
|
||||
case ERR_TILES_MUST_CONTACT:
|
||||
resid = R.string.str_tiles_must_contact;
|
||||
break;
|
||||
case UtilCtxt.ERR_NOT_YOUR_TURN:
|
||||
case ERR_NOT_YOUR_TURN:
|
||||
resid = R.string.str_not_your_turn;
|
||||
break;
|
||||
case UtilCtxt.ERR_NO_PEEK_ROBOT_TILES:
|
||||
case ERR_NO_PEEK_ROBOT_TILES:
|
||||
resid = R.string.str_no_peek_robot_tiles;
|
||||
break;
|
||||
case UtilCtxt.ERR_NO_EMPTY_TRADE:
|
||||
case ERR_NO_EMPTY_TRADE:
|
||||
// This should not be possible as the button's
|
||||
// disabled when no tiles selected.
|
||||
Assert.fail();
|
||||
break;
|
||||
case UtilCtxt.ERR_TOO_FEW_TILES_LEFT_TO_TRADE:
|
||||
case ERR_TOO_MANY_TRADE:
|
||||
int nLeft = XwJNI.server_countTilesInPool( m_jniGamePtr );
|
||||
msg = getQuantityString( R.plurals.too_many_trade_fmt,
|
||||
nLeft, nLeft );
|
||||
break;
|
||||
case ERR_TOO_FEW_TILES_LEFT_TO_TRADE:
|
||||
resid = R.string.str_too_few_tiles_left_to_trade;
|
||||
break;
|
||||
case UtilCtxt.ERR_CANT_UNDO_TILEASSIGN:
|
||||
case ERR_CANT_UNDO_TILEASSIGN:
|
||||
resid = R.string.str_cant_undo_tileassign;
|
||||
break;
|
||||
case UtilCtxt.ERR_CANT_HINT_WHILE_DISABLED:
|
||||
case ERR_CANT_HINT_WHILE_DISABLED:
|
||||
resid = R.string.str_cant_hint_while_disabled;
|
||||
break;
|
||||
case UtilCtxt.ERR_NO_PEEK_REMOTE_TILES:
|
||||
case ERR_NO_PEEK_REMOTE_TILES:
|
||||
resid = R.string.str_no_peek_remote_tiles;
|
||||
break;
|
||||
case UtilCtxt.ERR_REG_UNEXPECTED_USER:
|
||||
case ERR_REG_UNEXPECTED_USER:
|
||||
resid = R.string.str_reg_unexpected_user;
|
||||
break;
|
||||
case UtilCtxt.ERR_SERVER_DICT_WINS:
|
||||
case ERR_SERVER_DICT_WINS:
|
||||
resid = R.string.str_server_dict_wins;
|
||||
break;
|
||||
case ERR_REG_SERVER_SANS_REMOTE:
|
||||
|
@ -1904,17 +1877,21 @@ public class BoardDelegate extends DelegateBase
|
|||
break;
|
||||
}
|
||||
|
||||
if ( resid != 0 ) {
|
||||
if ( null == msg && resid != 0 ) {
|
||||
msg = getString( resid );
|
||||
}
|
||||
|
||||
if ( null != msg ) {
|
||||
if ( asToast ) {
|
||||
final int residf = resid;
|
||||
final String msgf = msg;
|
||||
runOnUiThread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showToast( residf );
|
||||
showToast( msgf );
|
||||
}
|
||||
} );
|
||||
} else {
|
||||
nonBlockingDialog( DlgID.DLG_OKONLY, getString( resid ) );
|
||||
nonBlockingDialog( DlgID.DLG_OKONLY, msg );
|
||||
}
|
||||
}
|
||||
} // userError
|
||||
|
@ -2066,9 +2043,9 @@ public class BoardDelegate extends DelegateBase
|
|||
|
||||
private void doResume( boolean isStart )
|
||||
{
|
||||
boolean success = true;
|
||||
boolean success = null != m_jniThreadRef;
|
||||
boolean firstStart = null == m_handler;
|
||||
if ( firstStart ) {
|
||||
if ( success && firstStart ) {
|
||||
m_handler = new Handler();
|
||||
|
||||
success = m_jniThreadRef.configure( m_activity, m_view, m_utils, this,
|
||||
|
@ -2631,9 +2608,7 @@ public class BoardDelegate extends DelegateBase
|
|||
|
||||
private boolean inArchiveGroup()
|
||||
{
|
||||
String archiveName = LocUtils
|
||||
.getString( m_activity, R.string.group_name_archive );
|
||||
long archiveGroup = DBUtils.getGroup( m_activity, archiveName );
|
||||
long archiveGroup = DBUtils.getArchiveGroup( m_activity );
|
||||
long curGroup = DBUtils.getGroupForGame( m_activity, m_rowid );
|
||||
return curGroup == archiveGroup;
|
||||
}
|
||||
|
@ -2770,18 +2745,21 @@ public class BoardDelegate extends DelegateBase
|
|||
GamePtr gamePtr = null;
|
||||
GameSummary summary = null;
|
||||
CurGameInfo gi = null;
|
||||
JNIThread thread = JNIThread.getRetained( rowID );
|
||||
JNIThread thread = JNIThread.getRetained( activity, rowID );
|
||||
if ( null != thread ) {
|
||||
gamePtr = thread.getGamePtr().retain();
|
||||
summary = thread.getSummary();
|
||||
gi = thread.getGI();
|
||||
} else {
|
||||
GameLock lock = new GameLock( rowID, false );
|
||||
if ( lock.tryLock() ) {
|
||||
summary = DBUtils.getSummary( activity, lock );
|
||||
gi = new CurGameInfo( activity );
|
||||
gamePtr = GameUtils.loadMakeGame( activity, gi, lock );
|
||||
lock.unlock();
|
||||
try ( GameLock lock = GameLock.getFor( rowID ).tryLockRO() ) {
|
||||
if ( null != lock ) {
|
||||
summary = DBUtils.getSummary( activity, lock );
|
||||
gi = new CurGameInfo( activity );
|
||||
gamePtr = GameUtils.loadMakeGame( activity, gi, lock );
|
||||
} else {
|
||||
DbgUtils.toastNoLock( TAG, activity,
|
||||
"setupRematchFor(%d)", rowID );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.util.AttributeSet;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.BoardDims;
|
||||
import org.eehouse.android.xw4.jni.BoardHandler;
|
||||
|
@ -46,7 +45,6 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
|
|||
|
||||
private static final float MIN_FONT_DIPS = 10.0f;
|
||||
private static final int MULTI_INACTIVE = -1;
|
||||
private static final int VERSION_CODES_N = 24; // until we're building on SDK 24...
|
||||
|
||||
private static boolean s_isFirstDraw;
|
||||
private static int s_curGameID;
|
||||
|
@ -205,7 +203,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
|
|||
synchronized( this ) {
|
||||
if ( layoutBoardOnce() && m_measuredFromDims ) {
|
||||
Bitmap bitmap = s_bitmap;
|
||||
if ( Build.VERSION.SDK_INT >= VERSION_CODES_N ) {
|
||||
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
|
||||
bitmap = Bitmap.createBitmap(bitmap);
|
||||
}
|
||||
canvas.drawBitmap( bitmap, 0, 0, new Paint() );
|
||||
|
@ -224,7 +222,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
|
|||
final int height = getHeight();
|
||||
boolean layoutDone = width == m_layoutWidth && height == m_layoutHeight;
|
||||
if ( layoutDone ) {
|
||||
Log.d( TAG, "layoutBoardOnce(): layoutDone true" );
|
||||
// Log.d( TAG, "layoutBoardOnce(): layoutDone true" );
|
||||
} else if ( null == m_gi ) {
|
||||
// nothing to do either
|
||||
Log.d( TAG, "layoutBoardOnce(): no m_gi" );
|
||||
|
@ -283,7 +281,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
|
|||
m_layoutHeight = height;
|
||||
layoutDone = true;
|
||||
}
|
||||
Log.d( TAG, "layoutBoardOnce()=>%b", layoutDone );
|
||||
// Log.d( TAG, "layoutBoardOnce()=>%b", layoutDone );
|
||||
return layoutDone;
|
||||
} // layoutBoardOnce
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2010 - 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.os.Build;
|
||||
import android.content.Context;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
|
||||
public class Channels {
|
||||
|
||||
enum ID {
|
||||
FOREGROUND(R.string.foreground_channel_expl,
|
||||
NotificationManager.IMPORTANCE_LOW),
|
||||
GAME_EVENT(R.string.gameevent_channel_expl,
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
|
||||
private int mExpl;
|
||||
private int mImportance;
|
||||
private ID( int expl, int imp )
|
||||
{
|
||||
mExpl = expl;
|
||||
mImportance = imp;
|
||||
}
|
||||
|
||||
public int getDesc() { return mExpl; }
|
||||
private int getImportance() { return mImportance; }
|
||||
}
|
||||
|
||||
private static Set<ID> sChannelsMade = new HashSet<>();
|
||||
public static String getChannelID( Context context, ID id )
|
||||
{
|
||||
final String name = id.toString();
|
||||
if ( ! sChannelsMade.contains( id ) ) {
|
||||
sChannelsMade.add( id );
|
||||
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) {
|
||||
NotificationManager notMgr = (NotificationManager)
|
||||
context.getSystemService( Context.NOTIFICATION_SERVICE );
|
||||
|
||||
NotificationChannel channel = notMgr.getNotificationChannel( name );
|
||||
if ( channel == null ) {
|
||||
String channelDescription = context.getString( id.getDesc() );
|
||||
channel = new NotificationChannel( name, channelDescription,
|
||||
id.getImportance() );
|
||||
channel.enableVibration( true );
|
||||
notMgr.createNotificationChannel( channel );
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -40,7 +40,6 @@ import android.widget.TextView;
|
|||
import java.text.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.jni.JNIThread;
|
||||
|
@ -131,7 +130,7 @@ public class ChatDelegate extends DelegateBase {
|
|||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
m_jniThreadRef = JNIThread.getRetained( m_rowid );
|
||||
m_jniThreadRef = JNIThread.getRetained( m_activity, m_rowid );
|
||||
if ( null == m_jniThreadRef ) {
|
||||
Log.w( TAG, "onResume(): m_jniThreadRef null; exiting" );
|
||||
finish();
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.eehouse.android.xw4;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
|
|
|
@ -32,10 +32,10 @@ import android.provider.Settings;
|
|||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
@ -164,7 +164,9 @@ public class ConnStatusHandler {
|
|||
return s_downOnMe && s_rect.contains( xx, yy );
|
||||
}
|
||||
|
||||
public static String getStatusText( Context context, CommsConnTypeSet connTypes )
|
||||
public static String getStatusText( Context context,
|
||||
CommsConnTypeSet connTypes,
|
||||
CommsAddrRec addr )
|
||||
{
|
||||
String msg;
|
||||
if ( null == connTypes || 0 == connTypes.size() ) {
|
||||
|
@ -177,9 +179,12 @@ public class ConnStatusHandler {
|
|||
R.string.connstat_net_fmt,
|
||||
connTypes.toString( context, true )));
|
||||
for ( CommsConnType typ : connTypes.getTypes() ) {
|
||||
String did = addDebugInfo( context, typ );
|
||||
sb.append( String.format( "\n\n*** %s %s***\n",
|
||||
typ.longName( context ), did ) );
|
||||
sb.append( String.format( "\n\n*** %s ", typ.longName( context ) ) );
|
||||
String did = addDebugInfo( context, addr, typ );
|
||||
if ( null != did ) {
|
||||
sb.append( did ).append( " " );
|
||||
}
|
||||
sb.append( "***\n" );
|
||||
|
||||
// For sends we list failures too.
|
||||
SuccessRecord record = recordFor( context, typ, false );
|
||||
|
@ -533,23 +538,39 @@ public class ConnStatusHandler {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static String addDebugInfo( Context context, CommsConnType typ )
|
||||
private static String addDebugInfo( Context context, CommsAddrRec addr,
|
||||
CommsConnType typ )
|
||||
{
|
||||
String result = "";
|
||||
String result = null;
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
switch ( typ ) {
|
||||
case COMMS_CONN_RELAY:
|
||||
result = String.format( "(DevID: %d; host: %s) ",
|
||||
result = String.format( "DevID: %d; host: %s",
|
||||
DevID.getRelayDevIDInt(context),
|
||||
XWPrefs.getDefaultRelayHost(context) );
|
||||
break;
|
||||
case COMMS_CONN_P2P:
|
||||
result = WiDirService.formatNetStateInfo();
|
||||
break;
|
||||
case COMMS_CONN_BT:
|
||||
if ( null != addr ) {
|
||||
result = addr.bt_hostName;
|
||||
}
|
||||
break;
|
||||
case COMMS_CONN_SMS:
|
||||
if ( null != addr ) {
|
||||
result = addr.sms_phone;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( null != result ) {
|
||||
result = "(" + result + ")";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import android.widget.LinearLayout;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.eehouse.android.xw4.loc.LocUtils;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class DBAlert extends XWDialogFragment {
|
||||
private static final String TAG = DBAlert.class.getSimpleName();
|
||||
|
|
|
@ -33,7 +33,6 @@ import android.graphics.BitmapFactory;
|
|||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DictUtils.DictLoc;
|
||||
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
|
||||
|
@ -367,22 +366,27 @@ public class DBUtils {
|
|||
public static void addRematchInfo( Context context, long rowid, String btAddr,
|
||||
String phone, String relayID, String p2pAddr )
|
||||
{
|
||||
GameLock lock = new GameLock( rowid, true ).lock();
|
||||
GameSummary summary = getSummary( context, lock );
|
||||
if ( null != btAddr ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_BTADDR, btAddr );
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).tryLock() ) {
|
||||
Assert.assertNotNull( lock );
|
||||
if ( null != lock ) {
|
||||
GameSummary summary = getSummary( context, lock );
|
||||
if ( null != btAddr ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_BTADDR, btAddr );
|
||||
}
|
||||
if ( null != phone ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_PHONE, phone );
|
||||
}
|
||||
if ( null != relayID ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_RELAY, relayID );
|
||||
}
|
||||
if ( null != p2pAddr ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_P2P, p2pAddr );
|
||||
}
|
||||
saveSummary( context, lock, summary );
|
||||
} else {
|
||||
Log.e( TAG, "addRematchInfo(%d): unable to lock game" );
|
||||
}
|
||||
}
|
||||
if ( null != phone ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_PHONE, phone );
|
||||
}
|
||||
if ( null != relayID ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_RELAY, relayID );
|
||||
}
|
||||
if ( null != p2pAddr ) {
|
||||
summary.putStringExtra( GameSummary.EXTRA_REMATCH_P2P, p2pAddr );
|
||||
}
|
||||
saveSummary( context, lock, summary );
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public static int countGamesUsingLang( Context context, int lang )
|
||||
|
@ -1053,7 +1057,8 @@ public class DBUtils {
|
|||
|
||||
setCached( rowid, null ); // force reread
|
||||
|
||||
lock = new GameLock( rowid, true ).lock();
|
||||
lock = GameLock.getFor( rowid ).tryLock();
|
||||
Assert.assertNotNull( lock );
|
||||
notifyListeners( rowid, GameChangeType.GAME_CREATED );
|
||||
}
|
||||
|
||||
|
@ -1114,14 +1119,14 @@ public class DBUtils {
|
|||
|
||||
public static void deleteGame( Context context, long rowid )
|
||||
{
|
||||
GameLock lock = new GameLock( rowid, true ).lock( 300 );
|
||||
if ( null != lock ) {
|
||||
deleteGame( context, lock );
|
||||
lock.unlock();
|
||||
} else {
|
||||
Log.e( TAG, "deleteGame: unable to lock rowid %d", rowid );
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
Assert.fail();
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).lock( 300 ) ) {
|
||||
if ( null != lock ) {
|
||||
deleteGame( context, lock );
|
||||
} else {
|
||||
Log.e( TAG, "deleteGame: unable to lock rowid %d", rowid );
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1786,6 +1791,14 @@ public class DBUtils {
|
|||
invalGroupsCache();
|
||||
}
|
||||
|
||||
public static long getArchiveGroup( Context context )
|
||||
{
|
||||
String archiveName = LocUtils
|
||||
.getString( context, R.string.group_name_archive );
|
||||
long archiveGroup = getGroup( context, archiveName );
|
||||
return archiveGroup;
|
||||
}
|
||||
|
||||
// Change group id of a game
|
||||
public static void moveGame( Context context, long rowid, long groupID )
|
||||
{
|
||||
|
@ -2395,7 +2408,7 @@ public class DBUtils {
|
|||
|
||||
public static void setIntFor( Context context, String key, int value )
|
||||
{
|
||||
// Log.df( "DBUtils.setIntFor(key=%s, val=%d)", key, value );
|
||||
// Log.d( TAG, "DBUtils.setIntFor(key=%s, val=%d)", key, value );
|
||||
String asStr = String.format( "%d", value );
|
||||
setStringFor( context, key, asStr );
|
||||
}
|
||||
|
@ -2406,7 +2419,7 @@ public class DBUtils {
|
|||
if ( null != asStr ) {
|
||||
dflt = Integer.parseInt( asStr );
|
||||
}
|
||||
// Log.df( "DBUtils.getIntFor(key=%s)=>%d", key, dflt );
|
||||
// Log.d( TAG, "DBUtils.getIntFor(key=%s)=>%d", key, dflt );
|
||||
return dflt;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.content.Intent;
|
|||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.Time;
|
||||
|
||||
|
@ -34,7 +33,6 @@ import java.util.Formatter;
|
|||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
@ -101,9 +99,24 @@ public class DbgUtils {
|
|||
showf( context, LocUtils.getString( context, formatid ), args );
|
||||
} // showf
|
||||
|
||||
public static void toastNoLock( String tag, Context context, String format,
|
||||
Object... args )
|
||||
{
|
||||
format = "Unable to lock game; " + format;
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
showf( context, format, args );
|
||||
}
|
||||
Log.w( tag, format, args );
|
||||
}
|
||||
|
||||
public static void assertOnUIThread()
|
||||
{
|
||||
Assert.assertTrue( Looper.getMainLooper().equals(Looper.myLooper()) );
|
||||
assertOnUIThread( true );
|
||||
}
|
||||
|
||||
public static void assertOnUIThread( boolean isOnThread )
|
||||
{
|
||||
Assert.assertTrue( isOnThread == Utils.isOnUIThread() );
|
||||
}
|
||||
|
||||
public static void printStack( String tag, StackTraceElement[] trace )
|
||||
|
@ -123,6 +136,12 @@ public class DbgUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void printStack( String tag, Exception ex )
|
||||
{
|
||||
String stackTrace = android.util.Log.getStackTraceString(ex);
|
||||
Log.d( tag, stackTrace );
|
||||
}
|
||||
|
||||
static String extrasToString( Intent intent )
|
||||
{
|
||||
Bundle bundle = intent.getExtras();
|
||||
|
|
|
@ -22,12 +22,15 @@ package org.eehouse.android.xw4;
|
|||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -40,8 +43,6 @@ import android.widget.CheckBox;
|
|||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ConfirmThenBuilder;
|
||||
|
@ -49,6 +50,12 @@ import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
|
|||
import org.eehouse.android.xw4.DlgDelegate.NotAgainBuilder;
|
||||
import org.eehouse.android.xw4.DlgDelegate.OkOnlyBuilder;
|
||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.GameSummary;
|
||||
import org.eehouse.android.xw4.jni.XwJNI.GamePtr;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
@ -144,14 +151,15 @@ public class DelegateBase implements DlgClickNotify,
|
|||
protected void onResume()
|
||||
{
|
||||
m_isVisible = true;
|
||||
XWService.setListener( this );
|
||||
XWServiceHelper.setListener( this );
|
||||
runIfVisible();
|
||||
}
|
||||
|
||||
protected void onPause()
|
||||
{
|
||||
m_isVisible = false;
|
||||
XWService.setListener( null );
|
||||
XWServiceHelper.setListener( null );
|
||||
m_dlgDelegate.onPausing();
|
||||
}
|
||||
|
||||
protected DelegateBase curThis()
|
||||
|
@ -415,10 +423,44 @@ public class DelegateBase implements DlgClickNotify,
|
|||
|
||||
protected Dialog makeDialog( DBAlert alert, Object[] params )
|
||||
{
|
||||
Dialog dialog = null;
|
||||
DlgID dlgID = alert.getDlgID();
|
||||
Log.d( TAG, "%s.makeDialog(): not handling %s", getClass().getSimpleName(),
|
||||
dlgID.toString() );
|
||||
return null;
|
||||
switch ( dlgID ) {
|
||||
case DLG_CONNSTAT: {
|
||||
AlertDialog.Builder ab = makeAlertBuilder();
|
||||
GameSummary summary = (GameSummary)params[0];
|
||||
int title = (Integer)params[1];
|
||||
String msg = (String)params[2];
|
||||
final CommsConnTypeSet conTypes = summary.conTypes;
|
||||
ab.setTitle( title )
|
||||
.setMessage( msg )
|
||||
.setPositiveButton( android.R.string.ok, null );
|
||||
if ( BuildConfig.DEBUG && null != conTypes
|
||||
&& (conTypes.contains( CommsConnType.COMMS_CONN_RELAY )
|
||||
|| conTypes.contains( CommsConnType.COMMS_CONN_P2P )) ) {
|
||||
OnClickListener lstnr = new OnClickListener() {
|
||||
public void onClick( DialogInterface dlg,
|
||||
int whichButton ) {
|
||||
NetStateCache.reset( m_activity );
|
||||
if ( conTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
|
||||
RelayService.reset( getActivity() );
|
||||
}
|
||||
if ( conTypes.contains( CommsConnType.COMMS_CONN_P2P ) ) {
|
||||
WiDirService.reset( getActivity() );
|
||||
}
|
||||
}
|
||||
};
|
||||
ab.setNegativeButton( R.string.button_reconnect, lstnr );
|
||||
}
|
||||
dialog = ab.create();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.d( TAG, "%s.makeDialog(): not handling %s", getClass().getSimpleName(),
|
||||
dlgID.toString() );
|
||||
break;
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
protected void showDialogFragment( final DlgID dlgID, final Object... params )
|
||||
|
@ -594,6 +636,53 @@ public class DelegateBase implements DlgClickNotify,
|
|||
runIfVisible();
|
||||
}
|
||||
|
||||
public void onStatusClicked( GamePtr gamePtr )
|
||||
{
|
||||
Context context = getActivity();
|
||||
CommsAddrRec[] addrs = XwJNI.comms_getAddrs( gamePtr );
|
||||
CommsAddrRec addr = null != addrs && 0 < addrs.length ? addrs[0] : null;
|
||||
final GameSummary summary = GameUtils.getSummary( context, gamePtr.getRowid(), 1 );
|
||||
if ( null != summary ) {
|
||||
final String msg = ConnStatusHandler
|
||||
.getStatusText( context, summary.conTypes, addr );
|
||||
|
||||
post( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if ( null == msg ) {
|
||||
askNoAddrsDelete();
|
||||
} else {
|
||||
showDialogFragment( DlgID.DLG_CONNSTAT, summary,
|
||||
R.string.info_title, msg );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public void onStatusClicked( long rowid )
|
||||
{
|
||||
Log.d( TAG, "onStatusClicked(%d)", rowid );
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).tryLockRO() ) {
|
||||
if ( null != lock ) {
|
||||
GamePtr gamePtr = GameUtils.loadMakeGame( getActivity(), lock );
|
||||
if ( null != gamePtr ) {
|
||||
onStatusClicked( gamePtr );
|
||||
gamePtr.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void askNoAddrsDelete()
|
||||
{
|
||||
makeConfirmThenBuilder( R.string.connstat_net_noaddr,
|
||||
Action.DELETE_AND_EXIT )
|
||||
.setPosButton( R.string.list_item_delete )
|
||||
.setNegButton( R.string.button_close_game )
|
||||
.show();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// MultiService.MultiEventListener interface
|
||||
//////////////////////////////////////////////////
|
||||
|
@ -601,10 +690,6 @@ public class DelegateBase implements DlgClickNotify,
|
|||
{
|
||||
int fmtId = 0;
|
||||
switch( event ) {
|
||||
case BT_ERR_COUNT:
|
||||
int count = (Integer)args[0];
|
||||
Log.i( TAG, "Bluetooth error count: %d", count );
|
||||
break;
|
||||
case BAD_PROTO_BT:
|
||||
fmtId = R.string.bt_bad_proto_fmt;
|
||||
break;
|
||||
|
|
|
@ -37,7 +37,6 @@ import android.widget.SectionIndexer;
|
|||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.content.res.Resources;
|
|||
import android.os.Handler;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
|
||||
import org.eehouse.android.xw4.DictUtils.DictLoc;
|
||||
|
@ -327,7 +326,7 @@ public class DictLangCache {
|
|||
public static void inval( final Context context, String name,
|
||||
DictLoc loc, boolean added )
|
||||
{
|
||||
DBUtils.dictsRemoveInfo( context, name );
|
||||
DBUtils.dictsRemoveInfo( context, DictUtils.removeDictExtn( name ) );
|
||||
|
||||
if ( added ) {
|
||||
DictAndLoc dal = new DictAndLoc( name, loc );
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.content.Context;
|
|||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
|
|
|
@ -45,7 +45,6 @@ import android.widget.ListView;
|
|||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
|
||||
import org.eehouse.android.xw4.DictUtils.DictLoc;
|
||||
|
@ -1064,23 +1063,6 @@ public class DictsDelegate extends ListDelegateBase
|
|||
return items;
|
||||
}
|
||||
|
||||
// private static Intent mkDownloadIntent( Context context, String dict_url )
|
||||
// {
|
||||
// Uri uri = Uri.parse( dict_url );
|
||||
// Intent intent = new Intent( Intent.ACTION_VIEW, uri );
|
||||
// intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
|
||||
// return intent;
|
||||
// }
|
||||
|
||||
private static Intent mkDownloadIntent( Context context,
|
||||
int lang, String dict )
|
||||
{
|
||||
Assert.fail();
|
||||
return null;
|
||||
// String dict_url = Utils.makeDictUri( context, lang, dict );
|
||||
// return mkDownloadIntent( context, dict_url );
|
||||
}
|
||||
|
||||
public static void downloadForResult( Delegator delegator, RequestCode requestCode,
|
||||
int lang )
|
||||
{
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.widget.TextView;
|
|||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class DisablesItem extends LinearLayout {
|
||||
private CommsConnType m_type;
|
||||
|
|
|
@ -34,7 +34,6 @@ import android.view.View;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
|
||||
|
||||
|
@ -327,6 +326,11 @@ public class DlgDelegate {
|
|||
m_handler = new Handler();
|
||||
}
|
||||
|
||||
void onPausing()
|
||||
{
|
||||
stopProgress();
|
||||
}
|
||||
|
||||
private void showOKOnlyDialogThen( String msg, Action action,
|
||||
Object[] params, int titleId )
|
||||
{
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.eehouse.android.xw4.DlgDelegate.Action;
|
|||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ import java.util.List;
|
|||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class DlgState implements Parcelable {
|
||||
private static final String TAG = DlgState.class.getSimpleName();
|
||||
|
|
|
@ -34,7 +34,6 @@ import android.widget.LinearLayout;
|
|||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
|
|
@ -30,7 +30,6 @@ import android.widget.AdapterView;
|
|||
import android.widget.Button;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import android.graphics.drawable.Drawable;
|
|||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
|
|
@ -45,7 +45,6 @@ import android.widget.TextView;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DictLangCache.LangsArrayAdapter;
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
|
@ -484,7 +483,7 @@ public class GameConfigDelegate extends DelegateBase
|
|||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
m_jniThread = JNIThread.getRetained( m_rowid );
|
||||
m_jniThread = JNIThread.getRetained( m_activity, m_rowid );
|
||||
super.onResume();
|
||||
loadGame();
|
||||
}
|
||||
|
@ -537,11 +536,14 @@ public class GameConfigDelegate extends DelegateBase
|
|||
if ( null == m_giOrig ) {
|
||||
m_giOrig = new CurGameInfo( m_activity );
|
||||
|
||||
GameLock gameLock;
|
||||
XwJNI.GamePtr gamePtr;
|
||||
GameLock gameLock = null;
|
||||
XwJNI.GamePtr gamePtr = null;
|
||||
if ( null == m_jniThread ) {
|
||||
gameLock = new GameLock( m_rowid, false ).lock();
|
||||
gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig, gameLock );
|
||||
gameLock = GameLock.getFor( m_rowid ).tryLockRO();
|
||||
if ( null != gameLock ) {
|
||||
gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig,
|
||||
gameLock );
|
||||
}
|
||||
} else {
|
||||
gameLock = m_jniThread.getLock();
|
||||
gamePtr = m_jniThread.getGamePtr();
|
||||
|
@ -1232,7 +1234,7 @@ public class GameConfigDelegate extends DelegateBase
|
|||
{
|
||||
if ( !isFinishing() ) {
|
||||
GameLock gameLock = m_jniThread == null
|
||||
? new GameLock( m_rowid, true ).lock()
|
||||
? GameLock.getFor( m_rowid ).tryLock()
|
||||
: m_jniThread.getLock();
|
||||
GameUtils.applyChanges( m_activity, m_gi, m_car, m_disabMap,
|
||||
gameLock, forceNew );
|
||||
|
|
|
@ -33,7 +33,6 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.GameSummary;
|
||||
import org.eehouse.android.xw4.jni.JNIThread;
|
||||
|
|
|
@ -20,179 +20,223 @@
|
|||
|
||||
package org.eehouse.android.xw4;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
// Implements read-locks and write-locks per game. A read lock is
|
||||
// obtainable when other read locks are granted but not when a
|
||||
// write lock is. Write-locks are exclusive.
|
||||
public class GameLock {
|
||||
public class GameLock implements AutoCloseable {
|
||||
private static final String TAG = GameLock.class.getSimpleName();
|
||||
|
||||
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 ASSERT_TIME = 2000;
|
||||
private static final long THROW_TIME = 1000;
|
||||
private long m_rowid;
|
||||
private boolean m_isForWrite;
|
||||
private int m_lockCount;
|
||||
private Thread m_ownerThread;
|
||||
private StackTraceElement[] m_lockTrace;
|
||||
private Stack<Owner> mOwners = new Stack<>();
|
||||
private boolean mReadOnly;
|
||||
|
||||
static {
|
||||
Assert.assertTrue( THROW_TIME <= ASSERT_TIME );
|
||||
private static class Owner {
|
||||
Thread mThread;
|
||||
String mTrace;
|
||||
|
||||
Owner()
|
||||
{
|
||||
mThread = Thread.currentThread();
|
||||
// mTrace = mThread.getStackTrace();
|
||||
mTrace = android.util.Log.getStackTraceString(new Exception());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format( "Owner: {%s/%s}", mThread, mTrace );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("{this: %H; rowid: %d; count: %d; ro: %b}",
|
||||
this, m_rowid, mOwners.size(), mReadOnly);
|
||||
}
|
||||
|
||||
public static class GameLockedException extends RuntimeException {}
|
||||
|
||||
private static HashMap<Long, GameLock>
|
||||
s_locks = new HashMap<Long,GameLock>();
|
||||
|
||||
public GameLock( long rowid, boolean isForWrite )
|
||||
{
|
||||
Assert.assertTrue( DBUtils.ROWID_NOTFOUND != rowid );
|
||||
m_rowid = rowid;
|
||||
m_isForWrite = isForWrite;
|
||||
m_lockCount = 0;
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "GameLock(rowid:%d,isForWrite:%b)=>this: %H",
|
||||
rowid, isForWrite, this );
|
||||
DbgUtils.printStack( TAG );
|
||||
}
|
||||
}
|
||||
|
||||
// This could be written to allow multiple read locks. Let's
|
||||
// see if not doing that causes problems.
|
||||
public boolean tryLock()
|
||||
{
|
||||
return null == tryLockImpl(); // unowned?
|
||||
}
|
||||
|
||||
private GameLock tryLockImpl()
|
||||
{
|
||||
GameLock owner = null;
|
||||
boolean gotIt = false;
|
||||
synchronized( s_locks ) {
|
||||
owner = s_locks.get( m_rowid );
|
||||
if ( null == owner ) { // unowned
|
||||
Assert.assertTrue( 0 == m_lockCount );
|
||||
s_locks.put( m_rowid, this );
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
m_ownerThread = Thread.currentThread();
|
||||
}
|
||||
++m_lockCount;
|
||||
gotIt = true;
|
||||
|
||||
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 ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "tryLock(): incrementing lock count" );
|
||||
}
|
||||
Assert.assertTrue( 0 == m_lockCount );
|
||||
++m_lockCount;
|
||||
gotIt = true;
|
||||
owner = null;
|
||||
} else if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "tryLock(): rowid %d already held by lock %H",
|
||||
m_rowid, owner );
|
||||
}
|
||||
}
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "tryLock %H (rowid=%d) => %b", this, m_rowid, gotIt );
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
||||
// Wait forever (but may assert if too long)
|
||||
public GameLock lock()
|
||||
{
|
||||
return this.lock( 0 );
|
||||
}
|
||||
|
||||
// Version that's allowed to return null -- if maxMillis > 0
|
||||
public GameLock lock( long maxMillis )
|
||||
private static Map<Long, WeakReference<GameLock>> sLockMap = new HashMap<>();
|
||||
public static GameLock getFor( long rowid )
|
||||
{
|
||||
GameLock result = null;
|
||||
Assert.assertTrue( maxMillis <= ASSERT_TIME );
|
||||
Assert.assertTrue( maxMillis <= THROW_TIME );
|
||||
long sleptTime = 0;
|
||||
synchronized ( sLockMap ) {
|
||||
if ( sLockMap.containsKey( rowid ) ) {
|
||||
result = sLockMap.get( rowid ).get();
|
||||
}
|
||||
if ( null == result ) {
|
||||
result = new GameLock( rowid );
|
||||
sLockMap.put( rowid, new WeakReference(result) );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private GameLock( long rowid ) { m_rowid = rowid; }
|
||||
|
||||
// We grant a lock IFF:
|
||||
// * Count is 0
|
||||
// OR
|
||||
// * existing locks are ReadOnly and this request is readOnly
|
||||
// OR
|
||||
// * the requesting thread already holds the lock
|
||||
private GameLock tryLockImpl( boolean readOnly )
|
||||
{
|
||||
GameLock result = null;
|
||||
synchronized ( mOwners ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.d( TAG, "%s.tryLockImpl(ro=%b)", this, readOnly );
|
||||
}
|
||||
// Thread thisThread = Thread.currentThread();
|
||||
boolean grant = false;
|
||||
if ( mOwners.empty() ) {
|
||||
grant = true;
|
||||
} else if ( mReadOnly && readOnly ) {
|
||||
grant = true;
|
||||
// } else if ( thisThread == mOwnerThread ) {
|
||||
// grant = true;
|
||||
}
|
||||
|
||||
if ( grant ) {
|
||||
mOwners.push( new Owner() );
|
||||
mReadOnly = readOnly;
|
||||
result = this;
|
||||
}
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.d( TAG, "%s.tryLockImpl(ro=%b) => %s", this, readOnly, result );
|
||||
if ( null == result ) {
|
||||
Log.d( TAG, "Unable to lock; cur owner: %s; would-be owner: %s",
|
||||
mOwners.peek(), new Owner() );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// // This could be written to allow multiple read locks. Let's
|
||||
// // see if not doing that causes problems.
|
||||
public GameLock tryLock()
|
||||
{
|
||||
return tryLockImpl( false );
|
||||
}
|
||||
|
||||
public GameLock tryLockRO()
|
||||
{
|
||||
return tryLockImpl( true );
|
||||
}
|
||||
|
||||
private GameLock lockImpl( long timeoutMS, boolean readOnly ) throws InterruptedException
|
||||
{
|
||||
long startMS = System.currentTimeMillis();
|
||||
long endMS = startMS + timeoutMS;
|
||||
synchronized ( mOwners ) {
|
||||
while ( null == tryLockImpl( readOnly ) ) {
|
||||
long now = System.currentTimeMillis();
|
||||
if ( now >= endMS ) {
|
||||
throw new GameLockedException();
|
||||
}
|
||||
mOwners.wait( endMS - now );
|
||||
}
|
||||
}
|
||||
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "lock %H (rowid:%d, maxMillis=%d)", this, m_rowid,
|
||||
maxMillis );
|
||||
long tookMS = System.currentTimeMillis() - startMS;
|
||||
Log.d( TAG, "%s.lockImpl() returning after %d ms", this, tookMS );
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
for ( ; ; ) {
|
||||
GameLock curOwner = tryLockImpl();
|
||||
if ( null == curOwner ) {
|
||||
result = this;
|
||||
break;
|
||||
}
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "lock() %H failed; sleeping", this );
|
||||
if ( 0 == sleptTime || sleptTime + SLEEP_TIME >= ASSERT_TIME ) {
|
||||
Log.i( TAG, "lock %H seeking stack:", curOwner );
|
||||
DbgUtils.printStack( TAG, curOwner.m_lockTrace );
|
||||
Log.i( TAG, "lock %H seeking stack:", this );
|
||||
DbgUtils.printStack( TAG );
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep( SLEEP_TIME ); // milliseconds
|
||||
sleptTime += SLEEP_TIME;
|
||||
} catch( InterruptedException ie ) {
|
||||
Log.ex( TAG, ie );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "lock() %H awake; "
|
||||
+ "sleptTime now %d millis", this, sleptTime );
|
||||
}
|
||||
|
||||
if ( 0 < maxMillis && sleptTime >= maxMillis ) {
|
||||
break;
|
||||
} else if ( THROW_ON_LOCKED && sleptTime >= THROW_TIME ) {
|
||||
throw new GameLockedException();
|
||||
} else if ( sleptTime >= ASSERT_TIME ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "lock %H overlocked", this );
|
||||
}
|
||||
Assert.fail();
|
||||
}
|
||||
@NonNull
|
||||
public GameLock lock() throws InterruptedException
|
||||
{
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
DbgUtils.assertOnUIThread( false );
|
||||
}
|
||||
// DbgUtils.logf( "GameLock.lock(%s) done", m_path );
|
||||
GameLock result = lockImpl( Long.MAX_VALUE, false );
|
||||
Assert.assertNotNull( result );
|
||||
return result;
|
||||
}
|
||||
|
||||
// @NonNull
|
||||
// public GameLock lockRO() throws InterruptedException
|
||||
// {
|
||||
// if ( BuildConfig.DEBUG ) {
|
||||
// DbgUtils.assertOnUIThread( false );
|
||||
// }
|
||||
// GameLock result = lockImpl( Long.MAX_VALUE, true );
|
||||
// Assert.assertNotNull( result );
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// Version that's allowed to return null -- if maxMillis > 0
|
||||
public GameLock lock( long maxMillis ) throws GameLockedException
|
||||
{
|
||||
Assert.assertTrue( maxMillis <= THROW_TIME );
|
||||
GameLock result = null;
|
||||
try {
|
||||
result = lockImpl( maxMillis, false );
|
||||
} catch (InterruptedException ex) {
|
||||
Log.d( TAG, "lock(): got %s", ex.getMessage() );
|
||||
}
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.d( TAG, "%s.lock(%d) => %s", this, maxMillis, result );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public GameLock lockRO( long maxMillis )
|
||||
{
|
||||
Assert.assertTrue( maxMillis <= THROW_TIME );
|
||||
GameLock lock = null;
|
||||
try {
|
||||
lock = lockImpl( maxMillis, true );
|
||||
} catch ( InterruptedException ex ) {
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
|
||||
public void unlock()
|
||||
{
|
||||
// DbgUtils.logf( "GameLock.unlock(%s)", m_path );
|
||||
synchronized( s_locks ) {
|
||||
Assert.assertTrue( this == s_locks.get(m_rowid) );
|
||||
// Need to get this working before can switch to using ReentrantLock
|
||||
// if ( BuildConfig.DEBUG ) {
|
||||
// Assert.assertTrue( Thread.currentThread().equals(m_ownerThread) );
|
||||
// }
|
||||
if ( 1 == m_lockCount ) {
|
||||
s_locks.remove( m_rowid );
|
||||
} else {
|
||||
Assert.assertTrue( !m_isForWrite );
|
||||
}
|
||||
--m_lockCount;
|
||||
|
||||
synchronized ( mOwners ) {
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.i( TAG, "unlock: this: %H (rowid:%d) unlocked", this, m_rowid );
|
||||
Log.d( TAG, "%s.unlock()", this );
|
||||
}
|
||||
Thread oldThread = mOwners.pop().mThread;
|
||||
|
||||
// It's ok for different threads to hold the same RO lock
|
||||
if ( !mReadOnly && oldThread != Thread.currentThread() ) {
|
||||
Log.e( TAG, "unlock(): unequal threads: %s => %s", oldThread,
|
||||
Thread.currentThread() );
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
if ( mOwners.empty() ) {
|
||||
mOwners.notifyAll();
|
||||
}
|
||||
if ( DEBUG_LOCKS ) {
|
||||
Log.d( TAG, "%s.unlock() DONE", this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
|
||||
public long getRowid()
|
||||
{
|
||||
return m_rowid;
|
||||
|
@ -201,9 +245,9 @@ public class GameLock {
|
|||
// used only for asserts
|
||||
public boolean canWrite()
|
||||
{
|
||||
boolean result = m_isForWrite && 1 == m_lockCount;
|
||||
boolean result = !mReadOnly; // && 1 == mLockCount[0];
|
||||
if ( !result ) {
|
||||
Log.w( TAG, "canWrite(): %H, returning false", this );
|
||||
Log.w( TAG, "%s.canWrite(): => false", this );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.text.Html;
|
|||
import android.text.TextUtils;
|
||||
import android.view.Display;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommonPrefs;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
|
@ -93,9 +92,12 @@ public class GameUtils {
|
|||
|
||||
public static byte[] savedGame( Context context, long rowid )
|
||||
{
|
||||
GameLock lock = new GameLock( rowid, false ).lock();
|
||||
byte[] result = savedGame( context, lock );
|
||||
lock.unlock();
|
||||
byte[] result = null;
|
||||
try (GameLock lock = GameLock.getFor( rowid ).tryLockRO() ) {
|
||||
if ( null != lock ) {
|
||||
result = savedGame( context, lock );
|
||||
}
|
||||
}
|
||||
|
||||
if ( null == result ) {
|
||||
throw new NoSuchGameException();
|
||||
|
@ -145,7 +147,7 @@ public class GameUtils {
|
|||
if ( null == lockDest ) {
|
||||
long groupID = DBUtils.getGroupForGame( context, lockSrc.getRowid() );
|
||||
long rowid = saveNewGame( context, gamePtr, gi, groupID );
|
||||
lockDest = new GameLock( rowid, true ).lock();
|
||||
lockDest = GameLock.getFor( rowid ).tryLock();
|
||||
} else {
|
||||
saveGame( context, gamePtr, gi, lockDest, true );
|
||||
}
|
||||
|
@ -158,16 +160,17 @@ public class GameUtils {
|
|||
public static boolean resetGame( Context context, long rowidIn )
|
||||
{
|
||||
boolean success = false;
|
||||
GameLock lock = new GameLock( rowidIn, true ).lock( 500 );
|
||||
if ( null != lock ) {
|
||||
tellDied( context, lock, true );
|
||||
resetGame( context, lock, lock, false );
|
||||
lock.unlock();
|
||||
try ( GameLock lock = GameLock.getFor( rowidIn ).lock( 500 ) ) {
|
||||
if ( null != lock ) {
|
||||
tellDied( context, lock, true );
|
||||
resetGame( context, lock, lock, false );
|
||||
|
||||
Utils.cancelNotification( context, (int)rowidIn );
|
||||
success = true;
|
||||
} else {
|
||||
Log.w( TAG, "resetGame: unable to open rowid %d", rowidIn );
|
||||
Utils.cancelNotification( context, (int)rowidIn );
|
||||
success = true;
|
||||
} else {
|
||||
DbgUtils.toastNoLock( TAG, context, "resetGame(): rowid %d",
|
||||
rowidIn );
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
@ -216,13 +219,13 @@ public class GameUtils {
|
|||
long maxMillis )
|
||||
{
|
||||
GameSummary result = null;
|
||||
JNIThread thread = JNIThread.getRetained( rowid );
|
||||
JNIThread thread = JNIThread.getRetained( context, rowid );
|
||||
GameLock lock = null;
|
||||
if ( null != thread ) {
|
||||
lock = thread.getLock();
|
||||
} else {
|
||||
try {
|
||||
lock = new GameLock( rowid, false ).lock( maxMillis );
|
||||
lock = GameLock.getFor( rowid ).lockRO( maxMillis );
|
||||
} catch ( GameLock.GameLockedException gle ) {
|
||||
Log.ex( TAG, gle );
|
||||
}
|
||||
|
@ -249,11 +252,11 @@ public class GameUtils {
|
|||
long rowid = DBUtils.ROWID_NOTFOUND;
|
||||
GameLock lockSrc = null;
|
||||
|
||||
JNIThread thread = JNIThread.getRetained( rowidIn, false );
|
||||
JNIThread thread = JNIThread.getRetained( context, rowidIn );
|
||||
if ( null != thread ) {
|
||||
lockSrc = thread.getLock();
|
||||
} else {
|
||||
lockSrc = new GameLock( rowidIn, false ).lock( 300 );
|
||||
lockSrc = GameLock.getFor( rowidIn ).lockRO( 300 );
|
||||
}
|
||||
|
||||
if ( null != lockSrc ) {
|
||||
|
@ -275,9 +278,13 @@ public class GameUtils {
|
|||
|
||||
public static void deleteGame( Context context, GameLock lock, boolean informNow )
|
||||
{
|
||||
tellDied( context, lock, informNow );
|
||||
Utils.cancelNotification( context, (int)lock.getRowid() );
|
||||
DBUtils.deleteGame( context, lock );
|
||||
if ( null != lock ) {
|
||||
tellDied( context, lock, informNow );
|
||||
Utils.cancelNotification( context, (int)lock.getRowid() );
|
||||
DBUtils.deleteGame( context, lock );
|
||||
} else {
|
||||
Log.e( TAG, "deleteGame(): null lock; doing nothing" );
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean deleteGame( Context context, long rowid,
|
||||
|
@ -285,14 +292,15 @@ public class GameUtils {
|
|||
{
|
||||
boolean success;
|
||||
// does this need to be synchronized?
|
||||
GameLock lock = new GameLock( rowid, true );
|
||||
if ( lock.tryLock() ) {
|
||||
deleteGame( context, lock, informNow );
|
||||
lock.unlock();
|
||||
success = true;
|
||||
} else {
|
||||
Log.w( TAG, "deleteGame: unable to delete rowid %d", rowid );
|
||||
success = false;
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).tryLock() ) {
|
||||
if ( null != lock ) {
|
||||
deleteGame( context, lock, informNow );
|
||||
success = true;
|
||||
} else {
|
||||
DbgUtils.toastNoLock( TAG, context, "deleteGame(): rowid %d",
|
||||
rowid );
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
@ -389,16 +397,16 @@ public class GameUtils {
|
|||
public static Bitmap loadMakeBitmap( Context context, long rowid )
|
||||
{
|
||||
Bitmap thumb = null;
|
||||
GameLock lock = new GameLock( rowid, false );
|
||||
if ( lock.tryLock() ) {
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
GamePtr gamePtr = loadMakeGame( context, gi, lock );
|
||||
if ( null != gamePtr ) {
|
||||
thumb = takeSnapshot( context, gamePtr, gi );
|
||||
gamePtr.release();
|
||||
DBUtils.saveThumbnail( context, lock, thumb );
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).tryLockRO() ) {
|
||||
if ( null != lock ) {
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
GamePtr gamePtr = loadMakeGame( context, gi, lock );
|
||||
if ( null != gamePtr ) {
|
||||
thumb = takeSnapshot( context, gamePtr, gi );
|
||||
gamePtr.release();
|
||||
DBUtils.saveThumbnail( context, lock, thumb );
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
return thumb;
|
||||
}
|
||||
|
@ -504,9 +512,10 @@ public class GameUtils {
|
|||
CurGameInfo gi, long groupID )
|
||||
{
|
||||
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
|
||||
GameLock lock = DBUtils.saveNewGame( context, stream, groupID, null );
|
||||
long rowid = lock.getRowid();
|
||||
lock.unlock();
|
||||
long rowid;
|
||||
try ( GameLock lock = DBUtils.saveNewGame( context, stream, groupID, null ) ) {
|
||||
rowid = lock.getRowid();
|
||||
}
|
||||
return rowid;
|
||||
}
|
||||
|
||||
|
@ -537,10 +546,10 @@ public class GameUtils {
|
|||
long rowid = DBUtils.ROWID_NOTFOUND;
|
||||
byte[] bytes = XwJNI.gi_to_stream( gi );
|
||||
if ( null != bytes ) {
|
||||
GameLock lock = DBUtils.saveNewGame( context, bytes, groupID,
|
||||
gameName );
|
||||
rowid = lock.getRowid();
|
||||
lock.unlock();
|
||||
try ( GameLock lock = DBUtils.saveNewGame( context, bytes, groupID,
|
||||
gameName ) ) {
|
||||
rowid = lock.getRowid();
|
||||
}
|
||||
}
|
||||
return rowid;
|
||||
}
|
||||
|
@ -638,65 +647,17 @@ public class GameUtils {
|
|||
}
|
||||
|
||||
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
|
||||
GameLock lock = new GameLock( rowid, true ).lock();
|
||||
applyChanges( context, sink, gi, util, addr, null, lock, false );
|
||||
lock.unlock();
|
||||
// Use tryLock in case we're on UI thread. It's guaranteed to
|
||||
// succeed because we just created the rowid.
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).tryLock() ) {
|
||||
Assert.assertNotNull( lock );
|
||||
applyChanges( context, sink, gi, util, addr, null, lock, false );
|
||||
}
|
||||
}
|
||||
|
||||
return rowid;
|
||||
}
|
||||
|
||||
public static long makeNewGame( Context context, MultiMsgSink sink,
|
||||
int gameID, CommsAddrRec addr, int lang,
|
||||
String dict, int nPlayersT,
|
||||
int nPlayersH, int forceChannel,
|
||||
String gameName )
|
||||
{
|
||||
return makeNewGame( context, sink, DBUtils.GROUPID_UNSPEC, gameID, addr,
|
||||
lang, dict, nPlayersT, nPlayersH, forceChannel,
|
||||
gameName );
|
||||
}
|
||||
|
||||
public static long makeNewGame( Context context, int gameID,
|
||||
CommsAddrRec addr, int lang,
|
||||
String dict, int nPlayersT,
|
||||
int nPlayersH, int forceChannel,
|
||||
String gameName )
|
||||
{
|
||||
return makeNewGame( context, DBUtils.GROUPID_UNSPEC, gameID, addr,
|
||||
lang, dict, nPlayersT, nPlayersH, forceChannel,
|
||||
gameName );
|
||||
}
|
||||
|
||||
public static long makeNewGame( Context context, long groupID, int gameID,
|
||||
CommsAddrRec addr, int lang, String dict,
|
||||
int nPlayersT, int nPlayersH,
|
||||
int forceChannel, String gameName )
|
||||
{
|
||||
return makeNewGame( context, null, groupID, gameID, addr, lang, dict,
|
||||
nPlayersT, nPlayersH, forceChannel, gameName );
|
||||
}
|
||||
|
||||
public static long makeNewGame( Context context, MultiMsgSink sink,
|
||||
long groupID, int gameID, CommsAddrRec addr,
|
||||
int lang, String dict,
|
||||
int nPlayersT, int nPlayersH,
|
||||
int forceChannel, String gameName )
|
||||
{
|
||||
long rowid = DBUtils.ROWID_NOTFOUND;
|
||||
int[] langa = { lang };
|
||||
String[] dicta = { dict };
|
||||
boolean isHost = null == addr;
|
||||
if ( isHost ) {
|
||||
addr = new CommsAddrRec( null, null );
|
||||
}
|
||||
String inviteID = GameUtils.formatGameID( gameID );
|
||||
return makeNewMultiGame( context, sink, (UtilCtxt)null, groupID, addr,
|
||||
langa, dicta, null, nPlayersT, nPlayersH,
|
||||
forceChannel, inviteID, gameID, gameName,
|
||||
isHost );
|
||||
}
|
||||
|
||||
// @SuppressLint({ "NewApi", "NewApi", "NewApi", "NewApi" })
|
||||
// @SuppressWarnings("deprecation")
|
||||
// @TargetApi(11)
|
||||
|
@ -865,7 +826,7 @@ public class GameUtils {
|
|||
return file.endsWith( XWConstants.GAME_EXTN );
|
||||
}
|
||||
|
||||
public static Bundle makeLaunchExtras( long rowid, boolean invited )
|
||||
private static Bundle makeLaunchExtras( long rowid, boolean invited )
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong( INTENT_KEY_ROWID, rowid );
|
||||
|
@ -949,54 +910,57 @@ public class GameUtils {
|
|||
// have the lock and we'll never get it. Better to drop
|
||||
// the message than fire the hung-lock assert. Messages
|
||||
// belong in local pre-delivery storage anyway.
|
||||
GameLock lock = new GameLock( rowid, true ).lock( 150 );
|
||||
if ( null != lock ) {
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid );
|
||||
GamePtr gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock );
|
||||
if ( null != gamePtr ) {
|
||||
XwJNI.comms_resendAll( gamePtr, false, false );
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).lock( 150 ) ) {
|
||||
if ( null != lock ) {
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid );
|
||||
GamePtr gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock );
|
||||
if ( null != gamePtr ) {
|
||||
XwJNI.comms_resendAll( gamePtr, false, false );
|
||||
|
||||
Assert.assertNotNull( ret );
|
||||
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret );
|
||||
XwJNI.comms_ackAny( gamePtr );
|
||||
Assert.assertNotNull( ret );
|
||||
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret );
|
||||
XwJNI.comms_ackAny( gamePtr );
|
||||
|
||||
// update gi to reflect changes due to messages
|
||||
XwJNI.game_getGi( gamePtr, gi );
|
||||
// update gi to reflect changes due to messages
|
||||
XwJNI.game_getGi( gamePtr, gi );
|
||||
|
||||
if ( draw && XWPrefs.getThumbEnabled( context ) ) {
|
||||
Bitmap bitmap = takeSnapshot( context, gamePtr, gi );
|
||||
DBUtils.saveThumbnail( context, lock, bitmap );
|
||||
}
|
||||
if ( draw && XWPrefs.getThumbEnabled( context ) ) {
|
||||
Bitmap bitmap = takeSnapshot( context, gamePtr, gi );
|
||||
DBUtils.saveThumbnail( context, lock, bitmap );
|
||||
}
|
||||
|
||||
if ( null != bmr ) {
|
||||
if ( null != feedImpl.m_chat ) {
|
||||
bmr.m_chat = feedImpl.m_chat;
|
||||
bmr.m_chatFrom = feedImpl.m_chatFrom;
|
||||
bmr.m_chatTs = feedImpl.m_ts;
|
||||
} else {
|
||||
LastMoveInfo lmi = new LastMoveInfo();
|
||||
XwJNI.model_getPlayersLastScore( gamePtr, -1, lmi );
|
||||
bmr.m_lmi = lmi;
|
||||
if ( null != bmr ) {
|
||||
if ( null != feedImpl.m_chat ) {
|
||||
bmr.m_chat = feedImpl.m_chat;
|
||||
bmr.m_chatFrom = feedImpl.m_chatFrom;
|
||||
bmr.m_chatTs = feedImpl.m_ts;
|
||||
} else {
|
||||
LastMoveInfo lmi = new LastMoveInfo();
|
||||
XwJNI.model_getPlayersLastScore( gamePtr, -1, lmi );
|
||||
bmr.m_lmi = lmi;
|
||||
}
|
||||
}
|
||||
|
||||
saveGame( context, gamePtr, gi, lock, false );
|
||||
GameSummary summary = summarizeAndRelease( context, lock,
|
||||
gamePtr, gi );
|
||||
if ( null != isLocalOut ) {
|
||||
isLocalOut[0] = 0 <= summary.turn
|
||||
&& gi.players[summary.turn].isLocal;
|
||||
}
|
||||
|
||||
int flags = setFromFeedImpl( feedImpl );
|
||||
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
|
||||
draw = true;
|
||||
int curFlags = DBUtils.getMsgFlags( context, rowid );
|
||||
DBUtils.setMsgFlags( rowid, flags | curFlags );
|
||||
}
|
||||
}
|
||||
|
||||
saveGame( context, gamePtr, gi, lock, false );
|
||||
GameSummary summary = summarizeAndRelease( context, lock,
|
||||
gamePtr, gi );
|
||||
if ( null != isLocalOut ) {
|
||||
isLocalOut[0] = 0 <= summary.turn
|
||||
&& gi.players[summary.turn].isLocal;
|
||||
}
|
||||
|
||||
int flags = setFromFeedImpl( feedImpl );
|
||||
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
|
||||
draw = true;
|
||||
int curFlags = DBUtils.getMsgFlags( context, rowid );
|
||||
DBUtils.setMsgFlags( rowid, flags | curFlags );
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
} catch ( GameLock.GameLockedException gle ) {
|
||||
DbgUtils.toastNoLock( TAG, context, "feedMessage(): dropping message "
|
||||
+ " for %d", rowid );
|
||||
}
|
||||
}
|
||||
return draw;
|
||||
|
@ -1008,35 +972,37 @@ public class GameUtils {
|
|||
public static boolean replaceDicts( Context context, long rowid,
|
||||
String oldDict, String newDict )
|
||||
{
|
||||
GameLock lock = new GameLock( rowid, true ).lock(300);
|
||||
boolean success = null != lock;
|
||||
if ( success ) {
|
||||
byte[] stream = savedGame( context, lock );
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
XwJNI.gi_from_stream( gi, stream );
|
||||
boolean success;
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).lock(300) ) {
|
||||
success = null != lock;
|
||||
if ( success ) {
|
||||
byte[] stream = savedGame( context, lock );
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
XwJNI.gi_from_stream( gi, stream );
|
||||
|
||||
// first time required so dictNames() will work
|
||||
gi.replaceDicts( context, newDict );
|
||||
// first time required so dictNames() will work
|
||||
gi.replaceDicts( context, newDict );
|
||||
|
||||
String[] dictNames = gi.dictNames();
|
||||
DictUtils.DictPairs pairs = DictUtils.openDicts( context,
|
||||
dictNames );
|
||||
String[] dictNames = gi.dictNames();
|
||||
DictUtils.DictPairs pairs = DictUtils.openDicts( context,
|
||||
dictNames );
|
||||
|
||||
GamePtr gamePtr =
|
||||
XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
||||
pairs.m_bytes, pairs.m_paths,
|
||||
gi.langName( context ), null,
|
||||
null, CommonPrefs.get( context ), null );
|
||||
// second time required as game_makeFromStream can overwrite
|
||||
gi.replaceDicts( context, newDict );
|
||||
GamePtr gamePtr =
|
||||
XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
||||
pairs.m_bytes, pairs.m_paths,
|
||||
gi.langName( context ), null,
|
||||
null, CommonPrefs.get( context ), null );
|
||||
// second time required as game_makeFromStream can overwrite
|
||||
gi.replaceDicts( context, newDict );
|
||||
|
||||
saveGame( context, gamePtr, gi, lock, false );
|
||||
saveGame( context, gamePtr, gi, lock, false );
|
||||
|
||||
summarizeAndRelease( context, lock, gamePtr, gi );
|
||||
summarizeAndRelease( context, lock, gamePtr, gi );
|
||||
|
||||
lock.unlock();
|
||||
} else {
|
||||
Log.w( TAG, "replaceDicts: unable to open rowid %d", rowid );
|
||||
} else {
|
||||
DbgUtils.toastNoLock( TAG, context, "replaceDicts(): rowid %d",
|
||||
rowid );
|
||||
}
|
||||
}
|
||||
return success;
|
||||
} // replaceDicts
|
||||
|
@ -1292,32 +1258,32 @@ public class GameUtils {
|
|||
}
|
||||
}
|
||||
|
||||
GameLock lock = new GameLock( rowid, false );
|
||||
if ( lock.tryLock() ) {
|
||||
CurGameInfo gi = new CurGameInfo( m_context );
|
||||
MultiMsgSink sink = new MultiMsgSink( m_context, rowid );
|
||||
GamePtr gamePtr = loadMakeGame( m_context, gi, sink, lock );
|
||||
if ( null != gamePtr ) {
|
||||
int nSent = XwJNI.comms_resendAll( gamePtr, true,
|
||||
m_filter, false );
|
||||
gamePtr.release();
|
||||
Log.d( TAG, "Resender.doInBackground(): sent %d "
|
||||
+ "messages for rowid %d", nSent, rowid );
|
||||
nSentTotal += sink.numSent();
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).tryLockRO() ) {
|
||||
if ( null != lock ) {
|
||||
CurGameInfo gi = new CurGameInfo( m_context );
|
||||
MultiMsgSink sink = new MultiMsgSink( m_context, rowid );
|
||||
GamePtr gamePtr = loadMakeGame( m_context, gi, sink, lock );
|
||||
if ( null != gamePtr ) {
|
||||
int nSent = XwJNI.comms_resendAll( gamePtr, true,
|
||||
m_filter, false );
|
||||
gamePtr.release();
|
||||
Log.d( TAG, "Resender.doInBackground(): sent %d "
|
||||
+ "messages for rowid %d", nSent, rowid );
|
||||
nSentTotal += sink.numSent();
|
||||
} else {
|
||||
Log.d( TAG, "Resender.doInBackground(): loadMakeGame()"
|
||||
+ " failed for rowid %d", rowid );
|
||||
}
|
||||
} else {
|
||||
Log.d( TAG, "Resender.doInBackground(): loadMakeGame()"
|
||||
+ " failed for rowid %d", rowid );
|
||||
}
|
||||
lock.unlock();
|
||||
} else {
|
||||
JNIThread jniThread = JNIThread.getRetained( rowid, false );
|
||||
if ( null != jniThread ) {
|
||||
jniThread.handle( JNIThread.JNICmd.CMD_RESEND, false,
|
||||
false, false );
|
||||
jniThread.release();
|
||||
} else {
|
||||
Log.w( TAG, "Resender.doInBackground: unable to unlock %d",
|
||||
rowid );
|
||||
JNIThread jniThread = JNIThread.getRetained( m_context, rowid );
|
||||
if ( null != jniThread ) {
|
||||
jniThread.handle( JNIThread.JNICmd.CMD_RESEND, false,
|
||||
false, false );
|
||||
jniThread.release();
|
||||
} else {
|
||||
Log.w( TAG, "Resender.doInBackground: unable to unlock %d",
|
||||
rowid );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ import android.widget.LinearLayout;
|
|||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils.GameChangeType;
|
||||
import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
|
||||
|
@ -86,6 +85,7 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
|
||||
private static final String RELAYIDS_EXTRA = "relayids";
|
||||
private static final String ROWID_EXTRA = "rowid";
|
||||
private static final String BACKGROUND_EXTRA = "bkgrnd";
|
||||
private static final String GAMEID_EXTRA = "gameid";
|
||||
private static final String REMATCH_ROWID_EXTRA = "rm_rowid";
|
||||
private static final String REMATCH_DICT_EXTRA = "rm_dict";
|
||||
|
@ -987,6 +987,16 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
} );
|
||||
|
||||
updateField();
|
||||
|
||||
// RECEIVE_SMS is required now (Oreo/SDK_26) but wasn't
|
||||
// before. There's logic elsewhere to ask for it AND SEND_SMS, but if
|
||||
// the user's already granted SEND_SMS we can get RECEIVE_SMS just by
|
||||
// asking (OS will grant without user interaction) since they're in
|
||||
// the same group. So just do it now. This code can be removed
|
||||
// later...
|
||||
if ( Perms23.havePermission( Perm.SEND_SMS ) ) {
|
||||
Perms23.tryGetPerms( this, Perm.RECEIVE_SMS, 0, Action.SKIP_CALLBACK );
|
||||
}
|
||||
} // init
|
||||
|
||||
@Override
|
||||
|
@ -1221,14 +1231,14 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
final Object ... args )
|
||||
{
|
||||
switch( event ) {
|
||||
case HOST_PONGED:
|
||||
post( new Runnable() {
|
||||
public void run() {
|
||||
DbgUtils.showf( m_activity,
|
||||
"Pong from %s", args[0].toString() );
|
||||
}
|
||||
});
|
||||
break;
|
||||
// case HOST_PONGED:
|
||||
// post( new Runnable() {
|
||||
// public void run() {
|
||||
// DbgUtils.showf( m_activity,
|
||||
// "Pong from %s", args[0].toString() );
|
||||
// }
|
||||
// });
|
||||
// break;
|
||||
case BT_GAME_CREATED:
|
||||
post( new Runnable() {
|
||||
public void run() {
|
||||
|
@ -1661,9 +1671,11 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
enable = BoardDelegate.rematchSupported( m_activity, rowID );
|
||||
Utils.setItemVisible( menu, R.id.games_game_rematch, enable );
|
||||
|
||||
enable = item.getSummary().isMultiGame()
|
||||
boolean isMultiGame = item.getSummary().isMultiGame();
|
||||
enable = isMultiGame
|
||||
&& (BuildConfig.DEBUG || XWPrefs.getDebugEnabled( m_activity ));
|
||||
Utils.setItemVisible( menu, R.id.games_game_invites, enable );
|
||||
Utils.setItemVisible( menu, R.id.games_game_netstats, isMultiGame );
|
||||
|
||||
enable = !m_launchedGames.contains( rowID );
|
||||
Utils.setItemVisible( menu, R.id.games_game_delete, enable );
|
||||
|
@ -1767,7 +1779,7 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
return handled;
|
||||
}
|
||||
|
||||
private boolean handleSelGamesItem( int itemID, final long[] selRowIDs )
|
||||
private boolean handleSelGamesItem( int itemID, long[] selRowIDs )
|
||||
{
|
||||
boolean handled = true;
|
||||
boolean dropSels = false;
|
||||
|
@ -1806,8 +1818,9 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
.show();
|
||||
break;
|
||||
case R.id.games_game_copy:
|
||||
final long selRowID = selRowIDs[0];
|
||||
final GameSummary smry = GameUtils.getSummary( m_activity,
|
||||
selRowIDs[0] );
|
||||
selRowID );
|
||||
if ( smry.inRelayGame() ) {
|
||||
makeOkOnlyBuilder( R.string.no_copy_network ).show();
|
||||
} else {
|
||||
|
@ -1816,14 +1829,14 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
public void run() {
|
||||
Activity self = m_activity;
|
||||
byte[] stream =
|
||||
GameUtils.savedGame( self, selRowIDs[0] );
|
||||
GameUtils.savedGame( self, selRowID );
|
||||
long groupID = XWPrefs
|
||||
.getDefaultNewGameGroup( self );
|
||||
GameLock lock =
|
||||
GameUtils.saveNewGame( self, stream, groupID );
|
||||
DBUtils.saveSummary( self, lock, smry );
|
||||
m_mySIS.selGames.add( lock.getRowid() );
|
||||
lock.unlock();
|
||||
try ( GameLock lock =
|
||||
GameUtils.saveNewGame( self, stream, groupID ) ) {
|
||||
DBUtils.saveSummary( self, lock, smry );
|
||||
m_mySIS.selGames.add( lock.getRowid() );
|
||||
}
|
||||
mkListAdapter();
|
||||
}
|
||||
});
|
||||
|
@ -1838,6 +1851,10 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
showDialogFragment( DlgID.RENAME_GAME, selRowIDs[0] );
|
||||
break;
|
||||
|
||||
case R.id.games_game_netstats:
|
||||
onStatusClicked( selRowIDs[0] );
|
||||
break;
|
||||
|
||||
// DEBUG only
|
||||
case R.id.games_game_invites:
|
||||
msg = GameUtils.getSummary( m_activity, selRowIDs[0] )
|
||||
|
@ -2023,9 +2040,7 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
try {
|
||||
hasDicts = GameUtils.gameDictsHere( m_activity, rowid, missingNames,
|
||||
missingLang );
|
||||
} catch ( GameUtils.NoSuchGameException nsge ) {
|
||||
hasDicts = true; // irrelevant question
|
||||
} catch ( GameLock.GameLockedException gle ) {
|
||||
} catch ( GameLock.GameLockedException | GameUtils.NoSuchGameException ex ) {
|
||||
hasDicts = true; // irrelevant question
|
||||
}
|
||||
|
||||
|
@ -2096,7 +2111,8 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
boolean haveDict;
|
||||
try {
|
||||
haveDict = GameUtils.gameDictsHere( m_activity, rowid );
|
||||
} catch ( GameLock.GameLockedException gle ) {
|
||||
} catch ( GameLock.GameLockedException
|
||||
| GameUtils.NoSuchGameException gle ) {
|
||||
haveDict = true;
|
||||
}
|
||||
if ( haveDict ) {
|
||||
|
@ -2237,7 +2253,12 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
int lang = extras.getInt( REMATCH_LANG_EXTRA, -1 );
|
||||
String json = extras.getString( REMATCH_PREFS_EXTRA );
|
||||
|
||||
// Don't save rematch in Archive group
|
||||
long groupID = DBUtils.getGroupForGame( m_activity, srcRowID );
|
||||
if ( groupID == DBUtils.getArchiveGroup( m_activity ) ) {
|
||||
groupID = XWPrefs.getDefaultNewGameGroup( m_activity );
|
||||
}
|
||||
|
||||
newid = GameUtils.makeNewMultiGame( m_activity, groupID, dict,
|
||||
lang, json, addrs, gameName );
|
||||
DBUtils.addRematchInfo( m_activity, newid, btAddr, phone,
|
||||
|
@ -2267,6 +2288,14 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
}
|
||||
}
|
||||
|
||||
private void tryBackgroundIntent( Intent intent )
|
||||
{
|
||||
if ( intent.getBooleanExtra( BACKGROUND_EXTRA, false ) ) {
|
||||
makeOkOnlyBuilder( R.string.btservice_expl )
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void askDefaultName()
|
||||
{
|
||||
String name = CommonPrefs.getDefaultPlayerName( m_activity, 0, true );
|
||||
|
@ -2481,6 +2510,7 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
startRematch( intent );
|
||||
tryAlert( intent );
|
||||
tryNFCIntent( intent );
|
||||
tryBackgroundIntent( intent );
|
||||
}
|
||||
|
||||
private void doOpenGame( Object[] params )
|
||||
|
@ -2665,6 +2695,13 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
return intent;
|
||||
}
|
||||
|
||||
public static Intent makeBackgroundIntent( Context context )
|
||||
{
|
||||
Intent intent = makeSelfIntent( context );
|
||||
intent.putExtra( BACKGROUND_EXTRA, true );
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent makeRowidIntent( Context context, long rowid )
|
||||
{
|
||||
Intent intent = makeSelfIntent( context );
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.widget.ImageButton;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import android.content.Intent;
|
|||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
class HostDelegate extends DelegateBase {
|
||||
private static final String ACTION = "ACTION";
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.widget.Button;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||
|
|
|
@ -36,14 +36,17 @@ import android.widget.FrameLayout;
|
|||
import android.widget.ListView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
abstract class InviteDelegate extends ListDelegateBase
|
||||
implements View.OnClickListener,
|
||||
ViewGroup.OnHierarchyChangeListener {
|
||||
|
@ -54,34 +57,25 @@ abstract class InviteDelegate extends ListDelegateBase
|
|||
String getDev(); // the string that identifies this item in results
|
||||
}
|
||||
|
||||
protected static class TwoStringPair implements InviterItem {
|
||||
public String str1;
|
||||
protected static class TwoStringPair implements InviterItem, Serializable {
|
||||
private String mDev;
|
||||
public String str2;
|
||||
|
||||
public TwoStringPair( String str1, String str2 ) {
|
||||
this.str1 = str1; this.str2 = str2;
|
||||
public TwoStringPair( String dev, String str2 ) {
|
||||
mDev = dev; this.str2 = str2;
|
||||
}
|
||||
|
||||
public static TwoStringPair[] make( String[] names, String[] addrs )
|
||||
{
|
||||
TwoStringPair[] pairs = new TwoStringPair[names.length];
|
||||
for ( int ii = 0; ii < pairs.length; ++ii ) {
|
||||
pairs[ii] = new TwoStringPair( names[ii], addrs[ii] );
|
||||
}
|
||||
return pairs;
|
||||
}
|
||||
|
||||
public String getDev() { return str1; }
|
||||
public String getDev() { return mDev; }
|
||||
|
||||
public boolean equals( InviterItem item )
|
||||
{
|
||||
boolean result = false;
|
||||
if ( null != item ) {
|
||||
TwoStringPair pair = (TwoStringPair)item;
|
||||
result = str1.equals( pair.str1 )
|
||||
result = mDev.equals( pair.mDev )
|
||||
&& ((null == str2 && null == pair.str2)
|
||||
|| str2.equals( pair.str2 ) );
|
||||
Log.d( TAG, "%s.equals(%s) => %b", str1, pair.str1, result );
|
||||
Log.d( TAG, "%s.equals(%s) => %b", mDev, pair.mDev, result );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -153,14 +147,17 @@ abstract class InviteDelegate extends ListDelegateBase
|
|||
for ( int id : buttonBarItemIds ) {
|
||||
bar.findViewById( id ).setOnClickListener( listener );
|
||||
}
|
||||
|
||||
tryEnable();
|
||||
}
|
||||
|
||||
protected void updateListAdapter( InviterItem[] items )
|
||||
protected void updateListAdapter( List<? extends InviterItem> items )
|
||||
{
|
||||
updateListAdapter( R.layout.two_strs_item, items );
|
||||
}
|
||||
|
||||
protected void updateListAdapter( int itemId, InviterItem[] items )
|
||||
protected void updateListAdapter( int itemId,
|
||||
List<? extends InviterItem> items )
|
||||
{
|
||||
updateChecked( items );
|
||||
m_adapter = new InviteItemsAdapter( itemId, items );
|
||||
|
@ -174,13 +171,7 @@ abstract class InviteDelegate extends ListDelegateBase
|
|||
}
|
||||
}
|
||||
|
||||
protected void onBarButtonClicked( int id )
|
||||
{
|
||||
Assert.fail(); // subclass must implement
|
||||
}
|
||||
|
||||
// Subclasses can do something here
|
||||
protected void addToButtonBar( FrameLayout container ) {}
|
||||
abstract void onBarButtonClicked( int id );
|
||||
|
||||
////////////////////////////////////////
|
||||
// View.OnClickListener
|
||||
|
@ -246,7 +237,7 @@ abstract class InviteDelegate extends ListDelegateBase
|
|||
protected void clearChecked() { m_checked.clear(); }
|
||||
|
||||
// Figure which previously-checked items belong in the new set.
|
||||
private void updateChecked( InviterItem[] newItems )
|
||||
private void updateChecked( List<? extends InviterItem> newItems )
|
||||
{
|
||||
Set<InviterItem> old = new HashSet<InviterItem>();
|
||||
old.addAll( m_checked );
|
||||
|
@ -282,11 +273,13 @@ abstract class InviteDelegate extends ListDelegateBase
|
|||
private InviterItem[] m_items;
|
||||
private int m_itemId;
|
||||
|
||||
public InviteItemsAdapter( int itemID, InviterItem[] items )
|
||||
public InviteItemsAdapter( int itemID, List<? extends InviterItem> items )
|
||||
{
|
||||
super( null == items? 0 : items.length );
|
||||
super( null == items? 0 : items.size() );
|
||||
m_itemId = itemID;
|
||||
m_items = items;
|
||||
if ( null != items ) {
|
||||
m_items = items.toArray( new InviterItem[items.size()] );
|
||||
}
|
||||
// m_items = new LinearLayout[getCount()];
|
||||
}
|
||||
|
||||
|
|
|
@ -23,21 +23,23 @@ package org.eehouse.android.xw4;
|
|||
import java.util.Formatter;
|
||||
|
||||
public class Log {
|
||||
private static final String PRE_TAG = BuildConfig.FLAVOR + "-";
|
||||
|
||||
public static void d( String tag, String fmt, Object... args ) {
|
||||
String str = new Formatter().format( fmt, args ).toString();
|
||||
android.util.Log.d( tag, str );
|
||||
android.util.Log.d( PRE_TAG + tag, str );
|
||||
}
|
||||
public static void w( String tag, String fmt, Object... args ) {
|
||||
String str = new Formatter().format( fmt, args ).toString();
|
||||
android.util.Log.w( tag, str );
|
||||
android.util.Log.w( PRE_TAG + tag, str );
|
||||
}
|
||||
public static void e( String tag, String fmt, Object... args ) {
|
||||
String str = new Formatter().format( fmt, args ).toString();
|
||||
android.util.Log.e( tag, str );
|
||||
android.util.Log.e( PRE_TAG + tag, str );
|
||||
}
|
||||
public static void i( String tag, String fmt, Object... args ) {
|
||||
String str = new Formatter().format( fmt, args ).toString();
|
||||
android.util.Log.i( tag, str );
|
||||
android.util.Log.i( PRE_TAG + tag, str );
|
||||
}
|
||||
|
||||
public static void ex( String tag, Exception exception )
|
||||
|
@ -45,5 +47,4 @@ public class Log {
|
|||
w( tag, "Exception: %s", exception.toString() );
|
||||
DbgUtils.printStack( tag, exception.getStackTrace() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import android.os.Bundle;
|
|||
import android.support.v4.app.DialogFragment;
|
||||
import java.io.Serializable;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ import android.widget.LinearLayout;
|
|||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
@ -138,8 +137,8 @@ public class MainActivity extends XWActivity
|
|||
}
|
||||
} );
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
DbgUtils.showf( this, "Putting off handling intent; %d waiting",
|
||||
m_runWhenSafe.size() );
|
||||
Log.d( TAG, "Putting off handling intent; %d waiting",
|
||||
m_runWhenSafe.size() );
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.eehouse.android.xw4;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
|
|
|
@ -26,7 +26,6 @@ import android.content.DialogInterface.OnClickListener;
|
|||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
@ -88,7 +87,6 @@ public class MultiService {
|
|||
SMS_SEND_FAILED_NOPERMISSION,
|
||||
|
||||
BT_GAME_CREATED,
|
||||
BT_ERR_COUNT,
|
||||
|
||||
RELAY_ALERT,
|
||||
};
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils.NeedsNagInfo;
|
||||
import org.eehouse.android.xw4.jni.GameSummary;
|
||||
|
|
|
@ -39,7 +39,6 @@ import java.util.Iterator;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||
|
|
|
@ -29,7 +29,6 @@ import android.net.NetworkInfo;
|
|||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
|
||||
|
@ -54,17 +53,21 @@ public class NetStateCache {
|
|||
|
||||
public static void register( Context context, StateChangedIf proc )
|
||||
{
|
||||
initIfNot( context );
|
||||
synchronized( s_ifs ) {
|
||||
s_ifs.add( proc );
|
||||
if ( Utils.isOnUIThread() ) {
|
||||
initIfNot( context );
|
||||
synchronized( s_ifs ) {
|
||||
s_ifs.add( proc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void unregister( Context context, StateChangedIf proc )
|
||||
{
|
||||
initIfNot( context );
|
||||
synchronized( s_ifs ) {
|
||||
s_ifs.remove( proc );
|
||||
if ( Utils.isOnUIThread() ) {
|
||||
initIfNot( context );
|
||||
synchronized( s_ifs ) {
|
||||
s_ifs.remove( proc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,8 +181,6 @@ public class NetStateCache {
|
|||
|
||||
public PvtBroadcastReceiver()
|
||||
{
|
||||
DbgUtils.assertOnUIThread();
|
||||
mHandler = new Handler();
|
||||
mLastStateSent = s_netAvail;
|
||||
}
|
||||
|
||||
|
@ -188,6 +189,11 @@ public class NetStateCache {
|
|||
{
|
||||
DbgUtils.assertOnUIThread();
|
||||
|
||||
if ( null == mHandler ) {
|
||||
DbgUtils.assertOnUIThread();
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
if ( intent.getAction().
|
||||
equals( ConnectivityManager.CONNECTIVITY_ACTION)) {
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ package org.eehouse.android.xw4;
|
|||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
@ -276,8 +275,10 @@ public class NetUtils {
|
|||
}
|
||||
result = new String( bas.toByteArray() );
|
||||
} else {
|
||||
Log.w( TAG, "runConn: responseCode: %d for url: %s",
|
||||
responseCode, conn.getURL() );
|
||||
Log.w( TAG, "runConn: responseCode: %d/%s for url: %s",
|
||||
responseCode, conn.getResponseMessage(),
|
||||
conn.getURL() );
|
||||
logErrorStream( conn.getErrorStream() );
|
||||
}
|
||||
} catch ( java.net.ProtocolException pe ) {
|
||||
Log.ex( TAG, pe );
|
||||
|
@ -289,6 +290,24 @@ public class NetUtils {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static void logErrorStream( InputStream is )
|
||||
{
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
for ( ; ; ) {
|
||||
int length = is.read( buffer );
|
||||
if ( length == -1 ) {
|
||||
break;
|
||||
}
|
||||
baos.write( buffer, 0, length );
|
||||
}
|
||||
Log.e( TAG, baos.toString() );
|
||||
} catch (Exception ex) {
|
||||
Log.e( TAG, ex.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
// This handles multiple params but only every gets passed one!
|
||||
private static String getPostDataString( Map<String, String> params )
|
||||
{
|
||||
|
|
|
@ -30,7 +30,6 @@ import android.support.v4.app.DialogFragment;
|
|||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ import android.support.v4.app.DialogFragment;
|
|||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
|
|
|
@ -30,10 +30,18 @@ public class OnBootReceiver extends BroadcastReceiver {
|
|||
@Override
|
||||
public void onReceive( Context context, Intent intent )
|
||||
{
|
||||
if ( null != intent && null != intent.getAction()
|
||||
&& intent.getAction().equals( Intent.ACTION_BOOT_COMPLETED ) ) {
|
||||
Log.d( TAG, "got ACTION_BOOT_COMPLETED" );
|
||||
startTimers( context );
|
||||
if ( null != intent ) {
|
||||
String action = intent.getAction();
|
||||
Log.d( TAG, "got %s", action );
|
||||
switch( action ) {
|
||||
case Intent.ACTION_BOOT_COMPLETED:
|
||||
case Intent.ACTION_MY_PACKAGE_REPLACED:
|
||||
startTimers( context );
|
||||
// Let's not put up the foreground service notification on
|
||||
// boot. Too likely to annoy.
|
||||
// BTService.onAppToBackground( context );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.eehouse.android.xw4;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
|
@ -30,6 +31,7 @@ import android.support.v4.content.ContextCompat;
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -38,20 +40,20 @@ import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
|
|||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class Perms23 {
|
||||
private static final String TAG = Perms23.class.getSimpleName();
|
||||
|
||||
public static enum Perm {
|
||||
READ_PHONE_STATE("android.permission.READ_PHONE_STATE"),
|
||||
STORAGE("android.permission.WRITE_EXTERNAL_STORAGE"),
|
||||
SEND_SMS("android.permission.SEND_SMS"),
|
||||
READ_CONTACTS("android.permission.READ_CONTACTS")
|
||||
;
|
||||
READ_PHONE_STATE(Manifest.permission.READ_PHONE_STATE),
|
||||
STORAGE(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
SEND_SMS(Manifest.permission.SEND_SMS),
|
||||
RECEIVE_SMS(Manifest.permission.RECEIVE_SMS),
|
||||
READ_CONTACTS(Manifest.permission.READ_CONTACTS);
|
||||
|
||||
private String m_str;
|
||||
private Perm(String str) { m_str = str; }
|
||||
|
||||
public String getString() { return m_str; }
|
||||
public static Perm getFor( String str ) {
|
||||
Perm result = null;
|
||||
|
@ -80,6 +82,12 @@ public class Perms23 {
|
|||
m_perms.addAll( perms );
|
||||
}
|
||||
|
||||
public Builder( Perm[] perms ) {
|
||||
for ( Perm perm : perms ) {
|
||||
m_perms.add( perm );
|
||||
}
|
||||
}
|
||||
|
||||
public Builder( Perm perm ) {
|
||||
m_perms.add( perm );
|
||||
}
|
||||
|
@ -128,7 +136,7 @@ public class Perms23 {
|
|||
|
||||
if ( haveAll ) {
|
||||
if ( null != cbck ) {
|
||||
Map<Perm, Boolean> map = new HashMap<Perm, Boolean>();
|
||||
Map<Perm, Boolean> map = new HashMap<>();
|
||||
for ( Perm perm : m_perms ) {
|
||||
map.put( perm, true );
|
||||
}
|
||||
|
@ -149,36 +157,37 @@ public class Perms23 {
|
|||
|
||||
private static class QueryInfo {
|
||||
private Action m_action;
|
||||
private Perm m_perm;
|
||||
private Perm[] m_perms;
|
||||
private DelegateBase m_delegate;
|
||||
private String m_rationaleMsg;
|
||||
private Object[] m_params;
|
||||
|
||||
private QueryInfo( DelegateBase delegate, Action action,
|
||||
Perm perm, String msg, Object[] params ) {
|
||||
Perm[] perms, String msg, Object[] params ) {
|
||||
m_delegate = delegate;
|
||||
m_action = action;
|
||||
m_perm = perm;
|
||||
m_perms = perms;
|
||||
m_rationaleMsg = msg;
|
||||
m_params = params;
|
||||
}
|
||||
|
||||
private QueryInfo( DelegateBase delegate, Object[] params )
|
||||
{
|
||||
this( delegate, (Action)params[0], (Perm)params[1], (String)params[2],
|
||||
this( delegate, (Action)params[0], (Perm[])params[1], (String)params[2],
|
||||
(Object[])params[3] );
|
||||
}
|
||||
|
||||
private Object[] getParams()
|
||||
{
|
||||
return new Object[] { m_action, m_perm, m_rationaleMsg, m_params };
|
||||
return new Object[] { m_action, m_perms, m_rationaleMsg, m_params };
|
||||
}
|
||||
|
||||
private void doIt( boolean showRationale )
|
||||
{
|
||||
Builder builder = new Builder( m_perm );
|
||||
Builder builder = new Builder( m_perms );
|
||||
if ( showRationale && null != m_rationaleMsg ) {
|
||||
builder.setOnShowRationale( new OnShowRationale() {
|
||||
@Override
|
||||
public void onShouldShowRationale( Set<Perm> perms ) {
|
||||
m_delegate.makeConfirmThenBuilder( m_rationaleMsg,
|
||||
Action.PERMS_QUERY )
|
||||
|
@ -191,9 +200,21 @@ public class Perms23 {
|
|||
} );
|
||||
}
|
||||
builder.asyncQuery( m_delegate.getActivity(), new PermCbck() {
|
||||
@Override
|
||||
public void onPermissionResult( Map<Perm, Boolean> perms ) {
|
||||
if ( Action.SKIP_CALLBACK != m_action ) {
|
||||
if ( perms.get( m_perm ) ) {
|
||||
Set<Perm> keys = perms.keySet();
|
||||
|
||||
// We need all the sought perms to have been granted
|
||||
boolean allGood = keys.size() == m_params.length;
|
||||
for ( Iterator<Perm> iter = keys.iterator();
|
||||
allGood && iter.hasNext(); ) {
|
||||
if ( !perms.get(iter.next()) ) {
|
||||
allGood = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( allGood ) {
|
||||
m_delegate.onPosButton( m_action, m_params );
|
||||
} else {
|
||||
m_delegate.onNegButton( m_action, m_params );
|
||||
|
@ -231,22 +252,36 @@ public class Perms23 {
|
|||
* Request permissions, giving rationale once, then call with action and
|
||||
* either positive or negative, the former if permission granted.
|
||||
*/
|
||||
public static void tryGetPerms( DelegateBase delegate, Perm perm, int rationaleId,
|
||||
public static void tryGetPerms( DelegateBase delegate, Perm[] perms, int rationaleId,
|
||||
final Action action, Object... params )
|
||||
{
|
||||
// Log.d( TAG, "tryGetPerms(%s)", perm.toString() );
|
||||
Context context = XWApp.getContext();
|
||||
String msg = LocUtils.getString( context, rationaleId );
|
||||
tryGetPerms( delegate, perm, msg, action, params );
|
||||
String msg = rationaleId == 0
|
||||
? null : LocUtils.getString( context, rationaleId );
|
||||
tryGetPerms( delegate, perms, msg, action, params );
|
||||
}
|
||||
|
||||
public static void tryGetPerms( DelegateBase delegate, Perm[] perms,
|
||||
String rationaleMsg, final Action action,
|
||||
Object... params )
|
||||
{
|
||||
// Log.d( TAG, "tryGetPerms(%s)", perm.toString() );
|
||||
new QueryInfo( delegate, action, perms, rationaleMsg, params )
|
||||
.doIt( true );
|
||||
}
|
||||
|
||||
public static void tryGetPerms( DelegateBase delegate, Perm perm,
|
||||
String rationaleMsg, final Action action,
|
||||
Object... params )
|
||||
{
|
||||
// Log.d( TAG, "tryGetPerms(%s)", perm.toString() );
|
||||
new QueryInfo( delegate, action, perm, rationaleMsg, params )
|
||||
.doIt( true );
|
||||
tryGetPerms( delegate, new Perm[]{ perm }, rationaleMsg, action, params );
|
||||
}
|
||||
|
||||
public static void tryGetPerms( DelegateBase delegate, Perm perm, int rationaleId,
|
||||
final Action action, Object... params )
|
||||
{
|
||||
tryGetPerms( delegate, new Perm[]{perm}, rationaleId, action, params );
|
||||
}
|
||||
|
||||
public static void onGotPermsAction( DelegateBase delegate, boolean positive,
|
||||
|
@ -262,6 +297,7 @@ public class Perms23 {
|
|||
{
|
||||
// Log.d( TAG, "gotPermissionResult(%s)", perms.toString() );
|
||||
Map<Perm, Boolean> result = new HashMap<Perm, Boolean>();
|
||||
boolean shouldResend = false;
|
||||
for ( int ii = 0; ii < perms.length; ++ii ) {
|
||||
Perm perm = Perm.getFor( perms[ii] );
|
||||
boolean granted = PackageManager.PERMISSION_GRANTED == granteds[ii];
|
||||
|
@ -270,9 +306,8 @@ public class Perms23 {
|
|||
// Hack. If SMS has been granted, resend all moves. This should be
|
||||
// replaced with an api allowing listeners to register
|
||||
// Perm-by-Perm, but I'm in a hurry.
|
||||
if ( granted && perm == Perm.SEND_SMS ) {
|
||||
GameUtils.resendAllIf( context, CommsConnType.COMMS_CONN_SMS,
|
||||
true, true );
|
||||
if ( granted && (perm == Perm.SEND_SMS || perm == Perm.RECEIVE_SMS) ) {
|
||||
shouldResend = true;
|
||||
}
|
||||
|
||||
// Log.d( TAG, "calling %s.onPermissionResult(%s, %b)",
|
||||
|
@ -280,6 +315,11 @@ public class Perms23 {
|
|||
// granted );
|
||||
}
|
||||
|
||||
if ( shouldResend ) {
|
||||
GameUtils.resendAllIf( context, CommsConnType.COMMS_CONN_SMS,
|
||||
true, true );
|
||||
}
|
||||
|
||||
PermCbck cbck = s_map.remove( code );
|
||||
if ( null != cbck ) {
|
||||
cbck.onPermissionResult( result );
|
||||
|
@ -291,15 +331,15 @@ public class Perms23 {
|
|||
String permString = perm.getString();
|
||||
boolean result = PackageManager.PERMISSION_GRANTED
|
||||
== ContextCompat.checkSelfPermission( XWApp.getContext(), permString );
|
||||
// Log.d( TAG, "havePermission(%s) => %b", permString, result );
|
||||
return result;
|
||||
}
|
||||
|
||||
// This is probably overkill as the OS only allows one permission request
|
||||
// at a time
|
||||
// If two permission requests are made in a row the map may contain more
|
||||
// than one entry.
|
||||
private static int s_nextRecord = 0;
|
||||
private static int register( PermCbck cbck )
|
||||
{
|
||||
Assert.assertTrue( !BuildConfig.DEBUG || 0 == s_map.size() );
|
||||
DbgUtils.assertOnUIThread();
|
||||
int code = ++s_nextRecord;
|
||||
s_map.put( code, cbck );
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.os.Bundle;
|
|||
import android.preference.PreferenceActivity;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ConfirmThenBuilder;
|
||||
|
|
|
@ -36,7 +36,6 @@ import android.preference.PreferenceScreen;
|
|||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
|
|
@ -52,7 +52,6 @@ import java.util.Set;
|
|||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
|
||||
|
@ -464,7 +463,7 @@ public class RelayInviteDelegate extends InviteDelegate {
|
|||
});
|
||||
|
||||
addSelf();
|
||||
updateListAdapter( m_devIDRecs.toArray( new DevIDRec[m_devIDRecs.size()] ) );
|
||||
updateListAdapter( m_devIDRecs );
|
||||
tryEnable();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,9 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.JobIntentService;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.GameUtils.BackMoveResult;
|
||||
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||
|
@ -63,7 +62,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class RelayService extends XWService
|
||||
public class RelayService extends JobIntentService
|
||||
implements NetStateCache.StateChangedIf {
|
||||
private static final String TAG = RelayService.class.getSimpleName();
|
||||
private static final int MAX_SEND = 1024;
|
||||
|
@ -103,6 +102,7 @@ public class RelayService extends XWService
|
|||
|
||||
private static List<PacketData> s_packetsSentUDP = new ArrayList<>();
|
||||
private static List<PacketData> s_packetsSentWeb = new ArrayList<>();
|
||||
private static final PacketData sEOQPacket = new PacketData();
|
||||
private static AtomicInteger s_nextPacketID = new AtomicInteger();
|
||||
private static boolean s_gcmWorking = false;
|
||||
private static boolean s_registered = false;
|
||||
|
@ -113,13 +113,15 @@ public class RelayService extends XWService
|
|||
static { resetBackoffTimer(); }
|
||||
|
||||
private Thread m_fetchThread = null; // no longer used
|
||||
private AtomicReference<UDPThreads> m_UDPThreadsRef = new AtomicReference<>();
|
||||
private static final AtomicReference<UDPThreads> sUDPThreadsRef = new AtomicReference<>();
|
||||
private Handler m_handler;
|
||||
private UDPThreads mThreads;
|
||||
private Runnable m_onInactivity;
|
||||
private int m_maxIntervalSeconds = 0;
|
||||
private long m_lastGamePacketReceived;
|
||||
private int m_nativeFailScore;
|
||||
private boolean m_skipUPDSet;
|
||||
private static AtomicInteger sNativeFailScore = new AtomicInteger();;
|
||||
private static boolean sSkipUPDSet;
|
||||
private RelayServiceHelper mHelper;
|
||||
private static DevIDType s_curType = DevIDType.ID_TYPE_NONE;
|
||||
private static long s_regStartTime = 0;
|
||||
|
||||
|
@ -190,15 +192,34 @@ public class RelayService extends XWService
|
|||
|
||||
public static void startService( Context context )
|
||||
{
|
||||
Log.i( TAG, "startService()" );
|
||||
Intent intent = getIntentTo( context, MsgCmds.UDP_CHANGED );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
// Must use the same lobID for all work enqueued for the same class
|
||||
private final static int sJobID = RelayService.class.hashCode();
|
||||
|
||||
private static void enqueueWork( Context context, Intent intent )
|
||||
{
|
||||
Log.d( TAG, "calling enqueueWork(cmd=%s)", cmdFrom( intent ) );
|
||||
enqueueWork( context, RelayService.class, sJobID, intent );
|
||||
}
|
||||
|
||||
private static MsgCmds cmdFrom( Intent intent )
|
||||
{
|
||||
MsgCmds cmd;
|
||||
try {
|
||||
cmd = MsgCmds.values()[intent.getIntExtra( CMD_STR, -1 )];
|
||||
} catch (Exception ex) { // OOB most likely
|
||||
cmd = null;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private static void stopService( Context context )
|
||||
{
|
||||
Intent intent = getIntentTo( context, MsgCmds.STOP );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static void inviteRemote( Context context, int destDevID,
|
||||
|
@ -206,7 +227,7 @@ public class RelayService extends XWService
|
|||
{
|
||||
int myDevID = DevID.getRelayDevIDInt( context );
|
||||
if ( 0 != myDevID ) {
|
||||
context.startService( getIntentTo( context, MsgCmds.INVITE )
|
||||
enqueueWork( context, getIntentTo( context, MsgCmds.INVITE )
|
||||
.putExtra( DEV_ID_SRC, myDevID )
|
||||
.putExtra( DEV_ID_DEST, destDevID )
|
||||
.putExtra( RELAY_ID, relayID )
|
||||
|
@ -217,13 +238,13 @@ public class RelayService extends XWService
|
|||
public static void reset( Context context )
|
||||
{
|
||||
Intent intent = getIntentTo( context, MsgCmds.RESET );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static void timerFired( Context context )
|
||||
{
|
||||
Intent intent = getIntentTo( context, MsgCmds.TIMER_FIRED );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static int sendPacket( Context context, long rowid, byte[] msg )
|
||||
|
@ -234,7 +255,7 @@ public class RelayService extends XWService
|
|||
Intent intent = getIntentTo( context, MsgCmds.SEND )
|
||||
.putExtra( ROWID, rowid )
|
||||
.putExtra( BINBUFFER, msg );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
result = msg.length;
|
||||
} else {
|
||||
Log.w( TAG, "sendPacket: network down" );
|
||||
|
@ -251,7 +272,7 @@ public class RelayService extends XWService
|
|||
.putExtra( ROWID, rowid )
|
||||
.putExtra( RELAY_ID, relayID )
|
||||
.putExtra( BINBUFFER, msg );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
result = msg.length;
|
||||
}
|
||||
return result;
|
||||
|
@ -266,18 +287,11 @@ public class RelayService extends XWService
|
|||
{
|
||||
Log.d( TAG, "receiveInvitation: got nli from %d: %s", srcDevID,
|
||||
nli.toString() );
|
||||
if ( !handleInvitation( nli, null, DictFetchOwner.OWNER_RELAY ) ) {
|
||||
if ( !mHelper.handleInvitation( nli, null, DictFetchOwner.OWNER_RELAY ) ) {
|
||||
Log.d( TAG, "handleInvitation() failed" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void postNotification( String device, int gameID, long rowid )
|
||||
{
|
||||
String body = LocUtils.getString( this, R.string.new_relay_body );
|
||||
GameUtils.postInvitedNotification( this, gameID, body, rowid );
|
||||
}
|
||||
|
||||
// Exists to get incoming data onto the main thread
|
||||
private static void postData( Context context, long rowid, byte[] msg )
|
||||
{
|
||||
|
@ -287,7 +301,7 @@ public class RelayService extends XWService
|
|||
Intent intent = getIntentTo( context, MsgCmds.RECEIVE )
|
||||
.putExtra( ROWID, rowid )
|
||||
.putExtra( BINBUFFER, msg );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
} else {
|
||||
Log.w( TAG, "postData(): Dropping message for rowid %d:"
|
||||
+ " not on device", rowid );
|
||||
|
@ -305,33 +319,29 @@ public class RelayService extends XWService
|
|||
Intent intent = getIntentTo( context, MsgCmds.PROCESS_GAME_MSGS )
|
||||
.putExtra( MSGS_ARR, msgs64 )
|
||||
.putExtra( RELAY_ID, relayId );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static void processDevMsgs( Context context, String[] msgs64 )
|
||||
{
|
||||
Intent intent = getIntentTo( context, MsgCmds.PROCESS_DEV_MSGS )
|
||||
.putExtra( MSGS_ARR, msgs64 );
|
||||
context.startService( intent );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
private static Intent getIntentTo( Context context, MsgCmds cmd )
|
||||
{
|
||||
Intent intent = new Intent( context, RelayService.class );
|
||||
intent.putExtra( CMD_STR, cmd.ordinal() );
|
||||
Intent intent = new Intent( context, RelayService.class )
|
||||
.putExtra( CMD_STR, cmd.ordinal() );
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return new RelayMsgSink().setRowID( rowid );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
Log.d( TAG, "%s.onCreate()", this );
|
||||
super.onCreate();
|
||||
mHelper = new RelayServiceHelper( this );
|
||||
m_lastGamePacketReceived =
|
||||
XWPrefs.getPrefsLong( this, R.string.key_last_packet,
|
||||
Utils.getCurSeconds() );
|
||||
|
@ -339,7 +349,7 @@ public class RelayService extends XWService
|
|||
m_handler = new Handler();
|
||||
m_onInactivity = new Runnable() {
|
||||
public void run() {
|
||||
Log.d( TAG, "m_onInactivity fired" );
|
||||
// Log.d( TAG, "m_onInactivity fired" );
|
||||
if ( !shouldMaintainConnection() ) {
|
||||
NetStateCache.unregister( RelayService.this,
|
||||
RelayService.this );
|
||||
|
@ -349,109 +359,20 @@ public class RelayService extends XWService
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
m_skipUPDSet = XWPrefs.getSkipToWebAPI( this );
|
||||
mThreads = startUDPThreadsOnce();
|
||||
if ( null == mThreads ) {
|
||||
stopSelf();
|
||||
}
|
||||
sSkipUPDSet = XWPrefs.getSkipToWebAPI( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand( Intent intent, int flags, int startId )
|
||||
public void onHandleWork( Intent intent )
|
||||
{
|
||||
Integer result = null;
|
||||
if ( null != intent ) {
|
||||
MsgCmds cmd;
|
||||
try {
|
||||
cmd = MsgCmds.values()[intent.getIntExtra( CMD_STR, -1 )];
|
||||
} catch (Exception ex) { // OOB most likely
|
||||
cmd = null;
|
||||
}
|
||||
if ( null != cmd ) {
|
||||
// Log.d( TAG, "onStartCommand(): cmd=%s", cmd.toString() );
|
||||
switch( cmd ) {
|
||||
case PROCESS_GAME_MSGS:
|
||||
String[] relayIDs = new String[1];
|
||||
relayIDs[0] = intent.getStringExtra( RELAY_ID );
|
||||
long[] rowIDs = DBUtils.getRowIDsFor( this, relayIDs[0] );
|
||||
if ( 0 < rowIDs.length ) {
|
||||
byte[][][] msgs = expandMsgsArray( intent );
|
||||
process( msgs, rowIDs, relayIDs );
|
||||
}
|
||||
break;
|
||||
case PROCESS_DEV_MSGS:
|
||||
byte[][][] msgss = expandMsgsArray( intent );
|
||||
for ( byte[][] msgs : msgss ) {
|
||||
for ( byte[] msg : msgs ) {
|
||||
gotPacket( msg, true, false );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UDP_CHANGED:
|
||||
startThreads();
|
||||
break;
|
||||
case RESET:
|
||||
stopThreads();
|
||||
startThreads();
|
||||
break;
|
||||
case UPGRADE:
|
||||
UpdateCheckReceiver.checkVersions( this, false );
|
||||
break;
|
||||
case GOT_INVITE:
|
||||
int srcDevID = intent.getIntExtra( INVITE_FROM, 0 );
|
||||
NetLaunchInfo nli
|
||||
= NetLaunchInfo.makeFrom( this, intent.getStringExtra(NLI_DATA) );
|
||||
receiveInvitation( srcDevID, nli );
|
||||
break;
|
||||
case SEND:
|
||||
case RECEIVE:
|
||||
case SENDNOCONN:
|
||||
startUDPThreadsIfNot();
|
||||
long rowid = intent.getLongExtra( ROWID, -1 );
|
||||
byte[] msg = intent.getByteArrayExtra( BINBUFFER );
|
||||
if ( MsgCmds.SEND == cmd ) {
|
||||
sendMessage( rowid, msg );
|
||||
} else if ( MsgCmds.SENDNOCONN == cmd ) {
|
||||
String relayID = intent.getStringExtra( RELAY_ID );
|
||||
sendNoConnMessage( rowid, relayID, msg );
|
||||
} else {
|
||||
receiveMessage( this, rowid, null, msg, s_addr );
|
||||
}
|
||||
break;
|
||||
case INVITE:
|
||||
startUDPThreadsIfNot();
|
||||
srcDevID = intent.getIntExtra( DEV_ID_SRC, 0 );
|
||||
int destDevID = intent.getIntExtra( DEV_ID_DEST, 0 );
|
||||
String relayID = intent.getStringExtra( RELAY_ID );
|
||||
String nliData = intent.getStringExtra( NLI_DATA );
|
||||
sendInvitation( srcDevID, destDevID, relayID, nliData );
|
||||
break;
|
||||
case TIMER_FIRED:
|
||||
if ( !NetStateCache.netAvail( this ) ) {
|
||||
Log.w( TAG, "not connecting: no network" );
|
||||
} else if ( startFetchThreadIfNotUDP() ) {
|
||||
// do nothing
|
||||
} else if ( registerWithRelayIfNot() ) {
|
||||
requestMessages();
|
||||
}
|
||||
RelayReceiver.setTimer( this );
|
||||
break;
|
||||
case STOP:
|
||||
stopThreads();
|
||||
stopSelf();
|
||||
break;
|
||||
default:
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
result = Service.START_STICKY;
|
||||
}
|
||||
}
|
||||
|
||||
if ( null == result ) {
|
||||
result = Service.START_STICKY_COMPATIBILITY;
|
||||
}
|
||||
|
||||
NetStateCache.register( this, this );
|
||||
DbgUtils.assertOnUIThread( false );
|
||||
Log.d( TAG, "%s.onHandleWork(cmd=%s)", this, cmdFrom( intent ) );
|
||||
handleCommand( intent );
|
||||
resetExitTimer();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -461,8 +382,11 @@ public class RelayService extends XWService
|
|||
long interval_millis = getMaxIntervalSeconds() * 1000;
|
||||
RelayReceiver.setTimer( this, interval_millis );
|
||||
}
|
||||
stopThreads();
|
||||
if ( null != mThreads ) {
|
||||
mThreads.unsetService();
|
||||
}
|
||||
super.onDestroy();
|
||||
Log.d( TAG, "%s.onDestroy() DONE", this );
|
||||
}
|
||||
|
||||
// NetStateCache.StateChangedIf interface
|
||||
|
@ -471,6 +395,88 @@ public class RelayService extends XWService
|
|||
startService( this ); // bad name: will *stop* threads too
|
||||
}
|
||||
|
||||
private void handleCommand( Intent intent )
|
||||
{
|
||||
MsgCmds cmd = cmdFrom( intent );
|
||||
if ( null != cmd ) {
|
||||
// Log.d( TAG, "handleCommand(): cmd=%s", cmd.toString() );
|
||||
switch( cmd ) {
|
||||
case PROCESS_GAME_MSGS:
|
||||
String[] relayIDs = new String[1];
|
||||
relayIDs[0] = intent.getStringExtra( RELAY_ID );
|
||||
long[] rowIDs = DBUtils.getRowIDsFor( this, relayIDs[0] );
|
||||
if ( 0 < rowIDs.length ) {
|
||||
byte[][][] msgs = expandMsgsArray( intent );
|
||||
process( msgs, rowIDs, relayIDs );
|
||||
}
|
||||
break;
|
||||
case PROCESS_DEV_MSGS:
|
||||
byte[][][] msgss = expandMsgsArray( intent );
|
||||
for ( byte[][] msgs : msgss ) {
|
||||
for ( byte[] msg : msgs ) {
|
||||
gotPacket( msg, true, false );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UDP_CHANGED:
|
||||
startThreads();
|
||||
break;
|
||||
case RESET:
|
||||
stopThreads();
|
||||
startThreads();
|
||||
break;
|
||||
case UPGRADE:
|
||||
UpdateCheckReceiver.checkVersions( this, false );
|
||||
break;
|
||||
case GOT_INVITE:
|
||||
int srcDevID = intent.getIntExtra( INVITE_FROM, 0 );
|
||||
NetLaunchInfo nli
|
||||
= NetLaunchInfo.makeFrom( this, intent.getStringExtra(NLI_DATA) );
|
||||
receiveInvitation( srcDevID, nli );
|
||||
break;
|
||||
case SEND:
|
||||
case RECEIVE:
|
||||
case SENDNOCONN:
|
||||
startUDPThreadsOnce();
|
||||
long rowid = intent.getLongExtra( ROWID, -1 );
|
||||
byte[] msg = intent.getByteArrayExtra( BINBUFFER );
|
||||
if ( MsgCmds.SEND == cmd ) {
|
||||
sendMessage( rowid, msg );
|
||||
} else if ( MsgCmds.SENDNOCONN == cmd ) {
|
||||
String relayID = intent.getStringExtra( RELAY_ID );
|
||||
sendNoConnMessage( rowid, relayID, msg );
|
||||
} else {
|
||||
mHelper.receiveMessage( this, rowid, null, msg, s_addr );
|
||||
}
|
||||
break;
|
||||
case INVITE:
|
||||
startUDPThreadsOnce();
|
||||
srcDevID = intent.getIntExtra( DEV_ID_SRC, 0 );
|
||||
int destDevID = intent.getIntExtra( DEV_ID_DEST, 0 );
|
||||
String relayID = intent.getStringExtra( RELAY_ID );
|
||||
String nliData = intent.getStringExtra( NLI_DATA );
|
||||
sendInvitation( srcDevID, destDevID, relayID, nliData );
|
||||
break;
|
||||
case TIMER_FIRED:
|
||||
if ( !NetStateCache.netAvail( this ) ) {
|
||||
Log.w( TAG, "not connecting: no network" );
|
||||
} else if ( startFetchThreadIfNotUDP() ) {
|
||||
// do nothing
|
||||
} else if ( registerWithRelayIfNot() ) {
|
||||
requestMessages();
|
||||
}
|
||||
RelayReceiver.setTimer( this );
|
||||
break;
|
||||
case STOP:
|
||||
stopThreads();
|
||||
stopSelf();
|
||||
break;
|
||||
default:
|
||||
Assert.assertFalse( BuildConfig.DEBUG );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupNotifications( String[] relayIDs, BackMoveResult[] bmrs,
|
||||
ArrayList<Boolean> locals )
|
||||
{
|
||||
|
@ -511,6 +517,7 @@ public class RelayService extends XWService
|
|||
{
|
||||
while ( null != m_fetchThread ) {
|
||||
Log.w( TAG, "2: m_fetchThread NOT NULL; WHAT TO DO???" );
|
||||
Assert.assertFalse( BuildConfig.DEBUG );
|
||||
try {
|
||||
Thread.sleep( 20 );
|
||||
} catch( java.lang.InterruptedException ie ) {
|
||||
|
@ -519,24 +526,38 @@ public class RelayService extends XWService
|
|||
}
|
||||
}
|
||||
|
||||
private void startUDPThreadsIfNot()
|
||||
private UDPThreads startUDPThreadsOnce()
|
||||
{
|
||||
UDPThreads threads = null;
|
||||
if ( XWApp.UDP_ENABLED && relayEnabled( this ) ) {
|
||||
synchronized ( m_UDPThreadsRef ) {
|
||||
if ( null == m_UDPThreadsRef.get() ) {
|
||||
UDPThreads threads = new UDPThreads();
|
||||
m_UDPThreadsRef.set( threads );
|
||||
synchronized ( sUDPThreadsRef ) {
|
||||
threads = sUDPThreadsRef.get();
|
||||
if ( null == threads ) {
|
||||
threads = new UDPThreads();
|
||||
sUDPThreadsRef.set( threads );
|
||||
threads.start();
|
||||
}
|
||||
threads.setService( this );
|
||||
}
|
||||
} else {
|
||||
Log.i( TAG, "startUDPThreadsIfNot(): UDP disabled" );
|
||||
Log.i( TAG, "startUDPThreadsOnce(): UDP disabled" );
|
||||
}
|
||||
} // startUDPThreadsIfNot
|
||||
return threads;
|
||||
} // startUDPThreadsOnce
|
||||
|
||||
private boolean skipNativeSend()
|
||||
private void stopUDPThreads()
|
||||
{
|
||||
boolean skip = m_nativeFailScore > UDP_FAIL_LIMIT || m_skipUPDSet;
|
||||
synchronized ( sUDPThreadsRef ) {
|
||||
UDPThreads threads = sUDPThreadsRef.getAndSet( null );
|
||||
if ( null != threads ) {
|
||||
threads.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean skipNativeSend()
|
||||
{
|
||||
boolean skip = sNativeFailScore.get() > UDP_FAIL_LIMIT || sSkipUPDSet;
|
||||
// Log.d( TAG, "skipNativeSend(score=%d)) => %b", m_nativeFailScore, skip );
|
||||
return skip;
|
||||
}
|
||||
|
@ -548,8 +569,8 @@ public class RelayService extends XWService
|
|||
|
||||
private void noteSent( PacketData packet, boolean fromUDP )
|
||||
{
|
||||
Log.d( TAG, "Sent (fromUDP=%b) packet: cmd=%s, id=%d",
|
||||
fromUDP, packet.m_cmd.toString(), packet.m_packetID );
|
||||
// Log.d( TAG, "Sent (fromUDP=%b) packet: cmd=%s, id=%d",
|
||||
// fromUDP, packet.m_cmd.toString(), packet.m_packetID );
|
||||
if ( fromUDP || packet.m_cmd != XWRelayReg.XWPDEV_ACK ) {
|
||||
List<PacketData> list = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
|
||||
synchronized( list ) {
|
||||
|
@ -562,25 +583,15 @@ public class RelayService extends XWService
|
|||
{
|
||||
long nowMS = System.currentTimeMillis();
|
||||
List<PacketData> map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
|
||||
Log.d( TAG, "noteSent(fromUDP=%b): adding %d; size before: %d",
|
||||
fromUDP, packets.size(), map.size() );
|
||||
// Log.d( TAG, "noteSent(fromUDP=%b): adding %d; size before: %d",
|
||||
// fromUDP, packets.size(), map.size() );
|
||||
for ( PacketData packet : packets ) {
|
||||
if ( fromUDP ) {
|
||||
packet.setSentMS( nowMS );
|
||||
}
|
||||
noteSent( packet, fromUDP );
|
||||
}
|
||||
Log.d( TAG, "noteSent(fromUDP=%b): size after: %d", fromUDP, map.size() );
|
||||
}
|
||||
|
||||
private void stopUDPThreadsIf()
|
||||
{
|
||||
DbgUtils.assertOnUIThread();
|
||||
|
||||
UDPThreads threads = m_UDPThreadsRef.getAndSet( null );
|
||||
if ( null != threads ) {
|
||||
threads.stop();
|
||||
}
|
||||
// Log.d( TAG, "noteSent(fromUDP=%b): size after: %d", fromUDP, map.size() );
|
||||
}
|
||||
|
||||
// MIGHT BE Running on reader thread
|
||||
|
@ -595,14 +606,14 @@ public class RelayService extends XWService
|
|||
if ( !skipAck ) {
|
||||
sendAckIf( header );
|
||||
}
|
||||
Log.d( TAG, "gotPacket(): cmd=%s", header.m_cmd.toString() );
|
||||
Log.d( TAG, "%s.gotPacket(): cmd=%s", this, header.m_cmd.toString() );
|
||||
switch ( header.m_cmd ) {
|
||||
case XWPDEV_UNAVAIL:
|
||||
int unavail = dis.readInt();
|
||||
Log.i( TAG, "relay unvailable for another %d seconds",
|
||||
unavail );
|
||||
String str = getVLIString( dis );
|
||||
postEvent( MultiEvent.RELAY_ALERT, str );
|
||||
mHelper.postEvent( MultiEvent.RELAY_ALERT, str );
|
||||
break;
|
||||
case XWPDEV_ALERT:
|
||||
str = getVLIString( dis );
|
||||
|
@ -647,20 +658,20 @@ public class RelayService extends XWService
|
|||
break;
|
||||
case XWPDEV_UPGRADE:
|
||||
intent = getIntentTo( this, MsgCmds.UPGRADE );
|
||||
startService( intent );
|
||||
enqueueWork( this, intent );
|
||||
break;
|
||||
case XWPDEV_GOTINVITE:
|
||||
resetBackoff = true;
|
||||
intent = getIntentTo( this, MsgCmds.GOT_INVITE );
|
||||
int srcDevID = dis.readInt();
|
||||
byte[] nliData = new byte[dis.readShort()];
|
||||
dis.readFully( nliData );
|
||||
NetLaunchInfo nli = XwJNI.nliFromStream( nliData );
|
||||
intent.putExtra( INVITE_FROM, srcDevID );
|
||||
String asStr = nli.toString();
|
||||
Log.d( TAG, "got invitation: %s", asStr );
|
||||
intent.putExtra( NLI_DATA, asStr );
|
||||
startService( intent );
|
||||
intent = getIntentTo( this, MsgCmds.GOT_INVITE )
|
||||
.putExtra( INVITE_FROM, srcDevID )
|
||||
.putExtra( NLI_DATA, asStr );
|
||||
enqueueWork( this, intent );
|
||||
break;
|
||||
case XWPDEV_ACK:
|
||||
noteAck( vli2un( dis ), fromUDP );
|
||||
|
@ -892,8 +903,7 @@ public class RelayService extends XWService
|
|||
|
||||
private void postPacket( ByteArrayOutputStream bas, XWRelayReg cmd )
|
||||
{
|
||||
startUDPThreadsIfNot();
|
||||
UDPThreads threads = m_UDPThreadsRef.get();
|
||||
UDPThreads threads = startUDPThreadsOnce();
|
||||
if ( threads != null ) {
|
||||
threads.add( new PacketData( bas, cmd ) );
|
||||
}
|
||||
|
@ -953,7 +963,7 @@ public class RelayService extends XWService
|
|||
long rowid = rowIDs[ii];
|
||||
sink.setRowID( rowid );
|
||||
for ( byte[] msg : forOne ) {
|
||||
receiveMessage( this, rowid, sink, msg, s_addr );
|
||||
mHelper.receiveMessage( this, rowid, sink, msg, s_addr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -961,37 +971,72 @@ public class RelayService extends XWService
|
|||
}
|
||||
}
|
||||
|
||||
private class UDPThreads {
|
||||
private static class UDPThreads {
|
||||
private DatagramSocket m_UDPSocket;
|
||||
private LinkedBlockingQueue<PacketData> m_queue =
|
||||
new LinkedBlockingQueue<PacketData>();
|
||||
private Thread m_UDPReadThread;
|
||||
private Thread m_UDPWriteThread;
|
||||
private RelayService[] mServiceHolder = {null};
|
||||
|
||||
UDPThreads() {}
|
||||
void setService( RelayService service )
|
||||
{
|
||||
synchronized ( mServiceHolder ) {
|
||||
mServiceHolder[0] = service;
|
||||
// unblock waiters for non-null Service
|
||||
mServiceHolder.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
// It will be a few milliseconds before all threads using the current
|
||||
// Service instance are done. Blocking the UI thread here until that
|
||||
// happened make for a laggy UI, so I'm going to see if we can
|
||||
// continue to use the instance for a short while after returning.
|
||||
void unsetService()
|
||||
{
|
||||
synchronized ( mServiceHolder ) {
|
||||
mServiceHolder[0] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private RelayService getService() throws InterruptedException
|
||||
{
|
||||
synchronized ( mServiceHolder ) {
|
||||
while ( null == mServiceHolder[0] ) {
|
||||
mServiceHolder.wait();
|
||||
}
|
||||
return mServiceHolder[0];
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
m_UDPReadThread = new Thread( null, new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
connectSocket(); // block until this is done
|
||||
startWriteThread();
|
||||
|
||||
connectSocket(); // block until this is done
|
||||
startWriteThread();
|
||||
|
||||
Log.i( TAG, "read thread running" );
|
||||
byte[] buf = new byte[1024];
|
||||
for ( ; ; ) {
|
||||
DatagramPacket packet =
|
||||
new DatagramPacket( buf, buf.length );
|
||||
try {
|
||||
m_UDPSocket.receive( packet );
|
||||
resetExitTimer();
|
||||
gotPacket( packet );
|
||||
} catch ( java.io.InterruptedIOException iioe ) {
|
||||
// DbgUtils.logf( "FYI: udp receive timeout" );
|
||||
} catch( java.io.IOException ioe ) {
|
||||
break;
|
||||
Log.i( TAG, "read thread running" );
|
||||
byte[] buf = new byte[1024];
|
||||
for ( ; ; ) {
|
||||
DatagramPacket packet =
|
||||
new DatagramPacket( buf, buf.length );
|
||||
try {
|
||||
m_UDPSocket.receive( packet );
|
||||
final RelayService service = getService();
|
||||
service.resetExitTimer();
|
||||
service.gotPacket( packet );
|
||||
} catch ( java.io.InterruptedIOException iioe ) {
|
||||
// DbgUtils.logf( "FYI: udp receive timeout" );
|
||||
} catch( java.io.IOException ioe ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} catch ( InterruptedException ie ) {
|
||||
Log.d( TAG, "exiting on interrupt: %s",
|
||||
ie.getMessage() );
|
||||
}
|
||||
Log.i( TAG, "read thread exiting" );
|
||||
}
|
||||
|
@ -1001,7 +1046,7 @@ public class RelayService extends XWService
|
|||
|
||||
void stop()
|
||||
{
|
||||
m_queue.add( new EOQPacketData() ); // will kill the writer thread
|
||||
m_queue.add( sEOQPacket ); // will kill the writer thread
|
||||
}
|
||||
|
||||
void add( PacketData packet )
|
||||
|
@ -1009,11 +1054,15 @@ public class RelayService extends XWService
|
|||
m_queue.add( packet );
|
||||
}
|
||||
|
||||
private void connectSocket()
|
||||
private void connectSocket() throws InterruptedException
|
||||
{
|
||||
if ( null == m_UDPSocket ) {
|
||||
int port = XWPrefs.getDefaultRelayPort( RelayService.this );
|
||||
String host = XWPrefs.getDefaultRelayHost( RelayService.this );
|
||||
int port;
|
||||
String host;
|
||||
final RelayService service = getService();
|
||||
port = XWPrefs.getDefaultRelayPort( service );
|
||||
host = XWPrefs.getDefaultRelayHost( service );
|
||||
|
||||
try {
|
||||
m_UDPSocket = new DatagramSocket();
|
||||
m_UDPSocket.setSoTimeout(30 * 1000); // timeout so we can log
|
||||
|
@ -1051,7 +1100,7 @@ public class RelayService extends XWService
|
|||
for ( outData = m_queue.poll(ts, TimeUnit.SECONDS);
|
||||
null != outData;
|
||||
outData = m_queue.poll() ) { // doesn't block
|
||||
if ( outData instanceof EOQPacketData ) {
|
||||
if ( outData == sEOQPacket ) {
|
||||
gotEOQ = true;
|
||||
break;
|
||||
} else if ( skipNativeSend() || outData.getForWeb() ) {
|
||||
|
@ -1060,18 +1109,19 @@ public class RelayService extends XWService
|
|||
dataListUDP.add( outData );
|
||||
}
|
||||
}
|
||||
|
||||
sendViaWeb( dataListWeb );
|
||||
sendViaUDP( dataListUDP );
|
||||
|
||||
getService().resetExitTimer();
|
||||
|
||||
runUDPAckTimer();
|
||||
|
||||
ConnStatusHandler.showSuccessOut();
|
||||
} catch ( InterruptedException ie ) {
|
||||
Log.w( TAG, "write thread killed" );
|
||||
break;
|
||||
}
|
||||
|
||||
sendViaWeb( dataListWeb );
|
||||
sendViaUDP( dataListUDP );
|
||||
|
||||
resetExitTimer();
|
||||
runUDPAckTimer();
|
||||
|
||||
ConnStatusHandler.showSuccessOut();
|
||||
}
|
||||
|
||||
Log.i( TAG, "write thread killing read thread" );
|
||||
|
@ -1090,19 +1140,22 @@ public class RelayService extends XWService
|
|||
m_UDPWriteThread.start();
|
||||
}
|
||||
|
||||
private int sendViaWeb( List<PacketData> packets )
|
||||
private int sendViaWeb( List<PacketData> packets ) throws InterruptedException
|
||||
{
|
||||
Log.d( TAG, "sendViaWeb(): sending %d at once", packets.size() );
|
||||
int sentLen = 0;
|
||||
if ( packets.size() > 0 ) {
|
||||
HttpURLConnection conn = NetUtils.makeHttpRelayConn( RelayService.this, "post" );
|
||||
Log.d( TAG, "sendViaWeb(): sending %d at once", packets.size() );
|
||||
|
||||
final RelayService service = getService();
|
||||
HttpURLConnection conn = NetUtils
|
||||
.makeHttpRelayConn( service, "post" );
|
||||
if ( null == conn ) {
|
||||
Log.e( TAG, "sendViaWeb(): null conn for POST" );
|
||||
} else {
|
||||
try {
|
||||
JSONArray dataArray = new JSONArray();
|
||||
for ( PacketData packet : packets ) {
|
||||
Assert.assertFalse( packet instanceof EOQPacketData );
|
||||
Assert.assertFalse( packet == sEOQPacket );
|
||||
byte[] datum = packet.assemble();
|
||||
dataArray.put( Utils.base64Encode(datum) );
|
||||
sentLen += datum.length;
|
||||
|
@ -1119,19 +1172,20 @@ public class RelayService extends XWService
|
|||
int nReplies = resData.length();
|
||||
// Log.d( TAG, "sendViaWeb(): got %d replies", nReplies );
|
||||
|
||||
noteSent( packets, false ); // before we process the acks below :-)
|
||||
service
|
||||
.noteSent( packets, false ); // before we process the acks below :-)
|
||||
|
||||
for ( int ii = 0; ii < nReplies; ++ii ) {
|
||||
byte[] datum = Utils.base64Decode( resData.getString( ii ) );
|
||||
// PENDING: skip ack or not
|
||||
gotPacket( datum, false, false );
|
||||
service.gotPacket( datum, false, false );
|
||||
}
|
||||
} else {
|
||||
Log.e( TAG, "sendViaWeb(): failed result for POST" );
|
||||
|
||||
}
|
||||
|
||||
ConnStatusHandler.updateStatus( RelayService.this, null,
|
||||
ConnStatusHandler.updateStatus( service, null,
|
||||
CommsConnType.COMMS_CONN_RELAY,
|
||||
succeeded );
|
||||
} catch ( JSONException ex ) {
|
||||
|
@ -1142,12 +1196,13 @@ public class RelayService extends XWService
|
|||
return sentLen;
|
||||
}
|
||||
|
||||
private int sendViaUDP( List<PacketData> packets )
|
||||
private int sendViaUDP( List<PacketData> packets ) throws InterruptedException
|
||||
{
|
||||
int sentLen = 0;
|
||||
|
||||
if ( packets.size() > 0 ) {
|
||||
noteSent( packets, true );
|
||||
final RelayService service = getService();
|
||||
service.noteSent( packets, true );
|
||||
for ( PacketData packet : packets ) {
|
||||
boolean getOut = true;
|
||||
byte[] data = packet.assemble();
|
||||
|
@ -1161,13 +1216,13 @@ public class RelayService extends XWService
|
|||
} catch ( java.net.SocketException se ) {
|
||||
Log.ex( TAG, se );
|
||||
Log.i( TAG, "Restarting threads to force new socket" );
|
||||
ConnStatusHandler.updateStatusOut( RelayService.this, null,
|
||||
ConnStatusHandler.updateStatusOut( service, null,
|
||||
CommsConnType.COMMS_CONN_RELAY,
|
||||
true );
|
||||
|
||||
m_handler.post( new Runnable() {
|
||||
service.m_handler.post( new Runnable() {
|
||||
public void run() {
|
||||
stopUDPThreadsIf();
|
||||
service.stopUDPThreads();
|
||||
}
|
||||
} );
|
||||
break;
|
||||
|
@ -1181,7 +1236,7 @@ public class RelayService extends XWService
|
|||
}
|
||||
}
|
||||
|
||||
ConnStatusHandler.updateStatus( RelayService.this, null,
|
||||
ConnStatusHandler.updateStatus( service, null,
|
||||
CommsConnType.COMMS_CONN_RELAY,
|
||||
sentLen > 0 );
|
||||
}
|
||||
|
@ -1216,7 +1271,7 @@ public class RelayService extends XWService
|
|||
forResend.add( packet );
|
||||
if ( packet.m_cmd != XWRelayReg.XWPDEV_ACK ) {
|
||||
foundNonAck = true;
|
||||
++m_nativeFailScore;
|
||||
sNativeFailScore.incrementAndGet();
|
||||
}
|
||||
iter.remove();
|
||||
}
|
||||
|
@ -1393,13 +1448,13 @@ public class RelayService extends XWService
|
|||
// Log.d( TAG, "noteAck(fromUDP=%b): removed for id %d: %s",
|
||||
// fromUDP, packetID, packet );
|
||||
if ( fromUDP ) {
|
||||
--m_nativeFailScore;
|
||||
sNativeFailScore.decrementAndGet();
|
||||
}
|
||||
} else {
|
||||
Log.w( TAG, "Weird: got ack %d but never sent", packetID );
|
||||
}
|
||||
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
if ( false && BuildConfig.DEBUG ) {
|
||||
ArrayList<String> pstrs = new ArrayList<>();
|
||||
for ( PacketData datum : map ) {
|
||||
if ( 0 != datum.m_packetID ) {
|
||||
|
@ -1422,6 +1477,7 @@ public class RelayService extends XWService
|
|||
// Called from any thread
|
||||
private void resetExitTimer()
|
||||
{
|
||||
// Log.d( TAG, "resetExitTimer()" );
|
||||
m_handler.removeCallbacks( m_onInactivity );
|
||||
|
||||
// UDP socket's no good as a return address after several
|
||||
|
@ -1437,10 +1493,11 @@ public class RelayService extends XWService
|
|||
stopThreads();
|
||||
} else if ( XWApp.UDP_ENABLED ) {
|
||||
stopFetchThreadIf();
|
||||
startUDPThreadsIfNot();
|
||||
startUDPThreadsOnce();
|
||||
registerWithRelay();
|
||||
} else {
|
||||
stopUDPThreadsIf();
|
||||
Assert.assertFalse( BuildConfig.DEBUG );
|
||||
stopUDPThreads();
|
||||
startFetchThreadIfNotUDP();
|
||||
}
|
||||
}
|
||||
|
@ -1449,7 +1506,7 @@ public class RelayService extends XWService
|
|||
{
|
||||
Log.d( TAG, "stopThreads()" );
|
||||
stopFetchThreadIf();
|
||||
stopUDPThreadsIf();
|
||||
stopUDPThreads();
|
||||
}
|
||||
|
||||
private static void un2vli( int nn, OutputStream os )
|
||||
|
@ -1528,7 +1585,8 @@ public class RelayService extends XWService
|
|||
result = figureBackoffSeconds();
|
||||
}
|
||||
|
||||
Log.d( TAG, "getMaxIntervalSeconds() => %d", result ); // WFT? went from 40 to 1000
|
||||
// WFT? went from 40 to 1000
|
||||
// Log.d( TAG, "getMaxIntervalSeconds() => %d", result );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1616,7 +1674,7 @@ public class RelayService extends XWService
|
|||
}
|
||||
}
|
||||
|
||||
private class PacketData {
|
||||
private static class PacketData {
|
||||
public ByteArrayOutputStream m_bas;
|
||||
public XWRelayReg m_cmd;
|
||||
public byte[] m_header;
|
||||
|
@ -1670,8 +1728,8 @@ public class RelayService extends XWService
|
|||
try {
|
||||
m_packetID = nextPacketID( m_cmd );
|
||||
DataOutputStream out = new DataOutputStream( bas );
|
||||
Log.d( TAG, "makeHeader(): building packet with cmd %s",
|
||||
m_cmd.toString() );
|
||||
// Log.d( TAG, "makeHeader(): building packet with cmd %s",
|
||||
// m_cmd.toString() );
|
||||
out.writeByte( XWPDevProto.XWPDEV_PROTO_VERSION_1.ordinal() );
|
||||
un2vli( m_packetID, out );
|
||||
out.writeByte( m_cmd.ordinal() );
|
||||
|
@ -1682,6 +1740,25 @@ public class RelayService extends XWService
|
|||
}
|
||||
}
|
||||
|
||||
// Exits only to exist, so instanceof can distinguish
|
||||
private class EOQPacketData extends PacketData {}
|
||||
private class RelayServiceHelper extends XWServiceHelper {
|
||||
|
||||
private Service mService;
|
||||
RelayServiceHelper( Service service ) {
|
||||
super( service );
|
||||
mService = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return new RelayMsgSink().setRowID( rowid );
|
||||
}
|
||||
|
||||
@Override
|
||||
void postNotification( String device, int gameID, long rowid )
|
||||
{
|
||||
String body = LocUtils.getString( mService, R.string.new_relay_body );
|
||||
GameUtils.postInvitedNotification( mService, gameID, body, rowid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ import android.view.View;
|
|||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
|
@ -299,7 +298,7 @@ public class SMSInviteDelegate extends InviteDelegate {
|
|||
}
|
||||
});
|
||||
|
||||
updateListAdapter( m_phoneRecs.toArray( new PhoneRec[m_phoneRecs.size()] ) );
|
||||
updateListAdapter( m_phoneRecs );
|
||||
tryEnable();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ import android.telephony.PhoneNumberUtils;
|
|||
import android.telephony.SmsManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||
|
@ -87,6 +86,7 @@ public class SMSService extends XWService {
|
|||
private BroadcastReceiver m_sentReceiver;
|
||||
private BroadcastReceiver m_receiveReceiver;
|
||||
private OnSharedPreferenceChangeListener m_prefsListener;
|
||||
private SMSServiceHelper mHelper;
|
||||
|
||||
private int m_nReceived = 0;
|
||||
private static int s_nSent = 0;
|
||||
|
@ -175,28 +175,28 @@ public class SMSService extends XWService {
|
|||
public static void stopService( Context context )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.STOP_SELF );
|
||||
context.startService( intent );
|
||||
startService( context, intent );
|
||||
}
|
||||
|
||||
// NBS case
|
||||
public static void handleFrom( Context context, byte[] buffer,
|
||||
String phone )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.HANDLEDATA );
|
||||
intent.putExtra( BUFFER, buffer );
|
||||
intent.putExtra( PHONE, phone );
|
||||
context.startService( intent );
|
||||
Intent intent = getIntentTo( context, SMSAction.HANDLEDATA )
|
||||
.putExtra( BUFFER, buffer )
|
||||
.putExtra( PHONE, phone );
|
||||
startService( context, intent );
|
||||
}
|
||||
|
||||
public static void inviteRemote( Context context, String phone,
|
||||
NetLaunchInfo nli )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.INVITE );
|
||||
intent.putExtra( PHONE, phone );
|
||||
Log.w( TAG, "inviteRemote(%s, '%s')", phone, nli );
|
||||
byte[] data = nli.asByteArray();
|
||||
intent.putExtra( GAMEDATA_BA, data );
|
||||
context.startService( intent );
|
||||
Intent intent = getIntentTo( context, SMSAction.INVITE )
|
||||
.putExtra( PHONE, phone )
|
||||
.putExtra( GAMEDATA_BA, data );
|
||||
startService( context, intent );
|
||||
}
|
||||
|
||||
public static int sendPacket( Context context, String phone,
|
||||
|
@ -204,11 +204,11 @@ public class SMSService extends XWService {
|
|||
{
|
||||
int nSent = -1;
|
||||
if ( XWPrefs.getSMSEnabled( context ) ) {
|
||||
Intent intent = getIntentTo( context, SMSAction.SEND );
|
||||
intent.putExtra( PHONE, phone );
|
||||
intent.putExtra( MultiService.GAMEID, gameID );
|
||||
intent.putExtra( BINBUFFER, binmsg );
|
||||
context.startService( intent );
|
||||
Intent intent = getIntentTo( context, SMSAction.SEND )
|
||||
.putExtra( PHONE, phone )
|
||||
.putExtra( MultiService.GAMEID, gameID )
|
||||
.putExtra( BINBUFFER, binmsg );
|
||||
startService( context, intent );
|
||||
nSent = binmsg.length;
|
||||
} else {
|
||||
Log.i( TAG, "sendPacket: dropping because SMS disabled" );
|
||||
|
@ -218,17 +218,17 @@ public class SMSService extends XWService {
|
|||
|
||||
public static void gameDied( Context context, int gameID, String phone )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.REMOVE );
|
||||
intent.putExtra( PHONE, phone );
|
||||
intent.putExtra( MultiService.GAMEID, gameID );
|
||||
context.startService( intent );
|
||||
Intent intent = getIntentTo( context, SMSAction.REMOVE )
|
||||
.putExtra( PHONE, phone )
|
||||
.putExtra( MultiService.GAMEID, gameID );
|
||||
startService( context, intent );
|
||||
}
|
||||
|
||||
public static void onGameDictDownload( Context context, Intent intentOld )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.ADDED_MISSING );
|
||||
intent.fillIn( intentOld, 0 );
|
||||
context.startService( intent );
|
||||
startService( context, intent );
|
||||
}
|
||||
|
||||
public static String fromPublicFmt( String msg )
|
||||
|
@ -256,10 +256,16 @@ public class SMSService extends XWService {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static void startService( Context context, Intent intent )
|
||||
{
|
||||
Log.d( TAG, "startService(%s)", intent );
|
||||
context.startService( intent );
|
||||
}
|
||||
|
||||
private static Intent getIntentTo( Context context, SMSAction cmd )
|
||||
{
|
||||
Intent intent = new Intent( context, SMSService.class );
|
||||
intent.putExtra( CMD_STR, cmd.ordinal() );
|
||||
Intent intent = new Intent( context, SMSService.class )
|
||||
.putExtra( CMD_STR, cmd.ordinal() );
|
||||
return intent;
|
||||
}
|
||||
|
||||
|
@ -272,15 +278,10 @@ public class SMSService extends XWService {
|
|||
return s_showToasts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return new SMSMsgSink( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
mHelper = new SMSServiceHelper( this );
|
||||
if ( Utils.deviceSupportsSMS( this ) ) {
|
||||
registerReceivers();
|
||||
} else {
|
||||
|
@ -312,6 +313,7 @@ public class SMSService extends XWService {
|
|||
@Override
|
||||
public int onStartCommand( Intent intent, int flags, int startId )
|
||||
{
|
||||
// Log.d( TAG, "onStartCommand(%s)", intent );
|
||||
int result = Service.START_NOT_STICKY;
|
||||
if ( null != intent ) {
|
||||
int ordinal = intent.getIntExtra( CMD_STR, -1 );
|
||||
|
@ -463,10 +465,10 @@ public class SMSService extends XWService {
|
|||
}
|
||||
break;
|
||||
case DEATH:
|
||||
postEvent( MultiEvent.MESSAGE_NOGAME, msg.gameID );
|
||||
mHelper.postEvent( MultiEvent.MESSAGE_NOGAME, msg.gameID );
|
||||
break;
|
||||
case ACK_INVITE:
|
||||
postEvent( MultiEvent.NEWGAME_SUCCESS, msg.gameID );
|
||||
mHelper.postEvent( MultiEvent.NEWGAME_SUCCESS, msg.gameID );
|
||||
break;
|
||||
default:
|
||||
Log.w( TAG, "unexpected cmd %s", msg.cmd );
|
||||
|
@ -483,26 +485,17 @@ public class SMSService extends XWService {
|
|||
for ( SMSProtoMsg msg : msgs ) {
|
||||
receive( msg, senderPhone );
|
||||
}
|
||||
postEvent( MultiEvent.SMS_RECEIVE_OK );
|
||||
mHelper.postEvent( MultiEvent.SMS_RECEIVE_OK );
|
||||
} else {
|
||||
Log.d( TAG, "receiveBuffer(): bogus or incomplete message from %s",
|
||||
senderPhone );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postNotification( String phone, int gameID, long rowid )
|
||||
{
|
||||
String owner = Utils.phoneToContact( this, phone, true );
|
||||
String body = LocUtils.getString( this, R.string.new_name_body_fmt,
|
||||
owner );
|
||||
GameUtils.postInvitedNotification( this, gameID, body, rowid );
|
||||
}
|
||||
|
||||
private void makeForInvite( String phone, NetLaunchInfo nli )
|
||||
{
|
||||
if ( nli != null ) {
|
||||
handleInvitation( nli, phone, DictFetchOwner.OWNER_SMS );
|
||||
mHelper.handleInvitation( nli, phone, DictFetchOwner.OWNER_SMS );
|
||||
ackInvite( phone, nli.gameID() );
|
||||
}
|
||||
}
|
||||
|
@ -548,7 +541,7 @@ public class SMSService extends XWService {
|
|||
} catch ( NullPointerException npe ) {
|
||||
Assert.fail(); // shouldn't be trying to do this!!!
|
||||
} catch ( java.lang.SecurityException se ) {
|
||||
postEvent( MultiEvent.SMS_SEND_FAILED_NOPERMISSION );
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_FAILED_NOPERMISSION );
|
||||
} catch ( Exception ee ) {
|
||||
Log.ex( TAG, ee );
|
||||
}
|
||||
|
@ -569,11 +562,12 @@ public class SMSService extends XWService {
|
|||
|
||||
private boolean feedMessage( int gameID, byte[] msg, CommsAddrRec addr )
|
||||
{
|
||||
ReceiveResult rslt = receiveMessage( this, gameID, null, msg, addr );
|
||||
if ( ReceiveResult.GAME_GONE == rslt ) {
|
||||
XWServiceHelper.ReceiveResult rslt = mHelper
|
||||
.receiveMessage( this, gameID, null, msg, addr );
|
||||
if ( XWServiceHelper.ReceiveResult.GAME_GONE == rslt ) {
|
||||
sendDiedPacket( addr.sms_phone, gameID );
|
||||
}
|
||||
return rslt == ReceiveResult.OK;
|
||||
return rslt == XWServiceHelper.ReceiveResult.OK;
|
||||
}
|
||||
|
||||
private void registerReceivers()
|
||||
|
@ -584,15 +578,15 @@ public class SMSService extends XWService {
|
|||
{
|
||||
switch ( getResultCode() ) {
|
||||
case Activity.RESULT_OK:
|
||||
postEvent( MultiEvent.SMS_SEND_OK );
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_OK );
|
||||
break;
|
||||
case SmsManager.RESULT_ERROR_RADIO_OFF:
|
||||
postEvent( MultiEvent.SMS_SEND_FAILED_NORADIO );
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_FAILED_NORADIO );
|
||||
break;
|
||||
case SmsManager.RESULT_ERROR_NO_SERVICE:
|
||||
default:
|
||||
Log.w( TAG, "FAILURE!!!" );
|
||||
postEvent( MultiEvent.SMS_SEND_FAILED );
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_FAILED );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -649,4 +643,28 @@ public class SMSService extends XWService {
|
|||
return sendPacket( addr.sms_phone, gameID, buf );
|
||||
}
|
||||
}
|
||||
|
||||
private class SMSServiceHelper extends XWServiceHelper {
|
||||
private Service mService;
|
||||
|
||||
SMSServiceHelper( Service service ) {
|
||||
super( service );
|
||||
mService = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return new SMSMsgSink( SMSService.this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postNotification( String phone, int gameID, long rowid )
|
||||
{
|
||||
String owner = Utils.phoneToContact( mService, phone, true );
|
||||
String body = LocUtils.getString( mService, R.string.new_name_body_fmt,
|
||||
owner );
|
||||
GameUtils.postInvitedNotification( mService, gameID, body, rowid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ import android.widget.ArrayAdapter;
|
|||
import android.widget.ListView;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.jni.GameSummary;
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.widget.Button;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
|
||||
|
|
|
@ -38,7 +38,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class TilePickView extends LinearLayout {
|
||||
private static final String TAG = TilePickView.class.getSimpleName();
|
||||
|
|
|
@ -35,7 +35,6 @@ import java.util.Iterator;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.HasDlgDelegate;
|
||||
|
|
|
@ -23,6 +23,7 @@ package org.eehouse.android.xw4;
|
|||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ContentResolver;
|
||||
|
@ -38,6 +39,8 @@ import android.database.Cursor;
|
|||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.FileProvider;
|
||||
|
@ -71,7 +74,6 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Perms23.Perm;
|
||||
import org.eehouse.android.xw4.jni.CommonPrefs;
|
||||
|
@ -254,7 +256,9 @@ public class Utils {
|
|||
defaults |= Notification.DEFAULT_VIBRATE;
|
||||
}
|
||||
|
||||
Notification notification = new NotificationCompat.Builder( context )
|
||||
String channelID = Channels.getChannelID( context, Channels.ID.GAME_EVENT );
|
||||
Notification notification =
|
||||
new NotificationCompat.Builder( context, channelID )
|
||||
.setContentIntent( pi )
|
||||
.setSmallIcon( R.drawable.notify )
|
||||
//.setTicker(body)
|
||||
|
@ -526,6 +530,11 @@ public class Utils {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static boolean isOnUIThread()
|
||||
{
|
||||
return Looper.getMainLooper().equals(Looper.myLooper());
|
||||
}
|
||||
|
||||
public static String base64Encode( byte[] in )
|
||||
{
|
||||
return Base64.encodeToString( in, Base64.NO_WRAP );
|
||||
|
@ -568,7 +577,7 @@ public class Utils {
|
|||
|
||||
public static void testSerialization( Serializable obj )
|
||||
{
|
||||
if ( BuildConfig.DEBUG ) {
|
||||
if ( false && BuildConfig.DEBUG ) {
|
||||
String as64 = serializableToString64( obj );
|
||||
Object other = string64ToSerializable( as64 );
|
||||
Assert.assertTrue( other.equals( obj ) );
|
||||
|
@ -610,5 +619,4 @@ public class Utils {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,10 +27,11 @@ import android.os.Bundle;
|
|||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WiDirInviteDelegate extends InviteDelegate
|
||||
implements WiDirService.DevSetListener {
|
||||
|
@ -76,11 +77,17 @@ public class WiDirInviteDelegate extends InviteDelegate
|
|||
WiDirService.unregisterDevSetListener( this );
|
||||
}
|
||||
|
||||
protected void onBarButtonClicked( int id )
|
||||
{
|
||||
// not implemented yet as there's no bar button
|
||||
Assert.assertFalse( BuildConfig.DEBUG );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChildAdded( View child, InviterItem data )
|
||||
{
|
||||
TwoStringPair pair = (TwoStringPair)data;
|
||||
((TwoStrsItem)child).setStrings( pair.str2, pair.str1 );
|
||||
((TwoStrsItem)child).setStrings( pair.str2, pair.getDev() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,7 +95,7 @@ public class WiDirInviteDelegate extends InviteDelegate
|
|||
{
|
||||
for ( int ii = 0; ii < selected.length; ++ii ) {
|
||||
TwoStringPair pair = (TwoStringPair)selected[ii];
|
||||
devs[ii] = pair.str1;
|
||||
devs[ii] = pair.getDev();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,13 +114,13 @@ public class WiDirInviteDelegate extends InviteDelegate
|
|||
private void rebuildList()
|
||||
{
|
||||
int count = m_macsToName.size();
|
||||
TwoStringPair[] pairs = new TwoStringPair[count];
|
||||
List<TwoStringPair> pairs = new ArrayList<>();
|
||||
// String[] names = new String[count];
|
||||
// String[] addrs = new String[count];
|
||||
Iterator<String> iter = m_macsToName.keySet().iterator();
|
||||
for ( int ii = 0; ii < count; ++ii ) {
|
||||
String mac = iter.next();
|
||||
pairs[ii] = new TwoStringPair(mac, m_macsToName.get(mac) );
|
||||
pairs.add( new TwoStringPair(mac, m_macsToName.get(mac) ) );
|
||||
// addrs[ii] = mac;
|
||||
// names[ii] = m_macsToName.get(mac);
|
||||
}
|
||||
|
|
|
@ -69,7 +69,6 @@ import org.eehouse.android.xw4.jni.CommsAddrRec;
|
|||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class WiDirService extends XWService {
|
||||
private static final String TAG = WiDirService.class.getSimpleName();
|
||||
|
@ -125,6 +124,7 @@ public class WiDirService extends XWService {
|
|||
private static Set<String> s_peersSet;
|
||||
|
||||
private P2pMsgSink m_sink;
|
||||
private WiDirServiceHelper mHelper;
|
||||
|
||||
public interface DevSetListener {
|
||||
void setChanged( Map<String, String> macToName );
|
||||
|
@ -134,6 +134,7 @@ public class WiDirService extends XWService {
|
|||
public void onCreate()
|
||||
{
|
||||
m_sink = new P2pMsgSink();
|
||||
mHelper = new WiDirServiceHelper(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -214,6 +215,8 @@ public class WiDirService extends XWService {
|
|||
sHavePermission = false;
|
||||
} catch ( SecurityException se ) { // perm not in manifest
|
||||
sHavePermission = false;
|
||||
} catch ( NullPointerException npe ) { // Seeing this on Oreo emulator
|
||||
sHavePermission = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -750,8 +753,9 @@ public class WiDirService extends XWService {
|
|||
CommsAddrRec addr = new CommsAddrRec( CommsConnType.COMMS_CONN_P2P )
|
||||
.setP2PParams( macAddress );
|
||||
|
||||
ReceiveResult rslt = receiveMessage( this, gameID, m_sink, data, addr );
|
||||
if ( ReceiveResult.GAME_GONE == rslt ) {
|
||||
XWServiceHelper.ReceiveResult rslt = mHelper
|
||||
.receiveMessage( this, gameID, m_sink, data, addr );
|
||||
if ( XWServiceHelper.ReceiveResult.GAME_GONE == rslt ) {
|
||||
sendNoGame( null, macAddress, gameID );
|
||||
}
|
||||
}
|
||||
|
@ -763,27 +767,15 @@ public class WiDirService extends XWService {
|
|||
NetLaunchInfo nli = NetLaunchInfo.makeFrom( this, nliData );
|
||||
String returnMac = intent.getStringExtra( KEY_SRC );
|
||||
|
||||
if ( !handleInvitation( nli, returnMac, DictFetchOwner.OWNER_P2P ) ) {
|
||||
if ( !mHelper.handleInvitation( nli, returnMac, DictFetchOwner.OWNER_P2P ) ) {
|
||||
Log.d( TAG, "handleInvitation() failed" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void postNotification( String device, int gameID, long rowid )
|
||||
{
|
||||
Log.e( TAG, "postNotification() doing nothing" );
|
||||
}
|
||||
|
||||
@Override
|
||||
MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return m_sink;
|
||||
}
|
||||
|
||||
private void handleGameGone( Intent intent )
|
||||
{
|
||||
int gameID = intent.getIntExtra( KEY_GAMEID, 0 );
|
||||
postEvent( MultiEvent.MESSAGE_NOGAME, gameID );
|
||||
mHelper.postEvent( MultiEvent.MESSAGE_NOGAME, gameID );
|
||||
}
|
||||
|
||||
private void makeGame( NetLaunchInfo nli, String senderMac )
|
||||
|
@ -793,7 +785,7 @@ public class WiDirService extends XWService {
|
|||
CommsAddrRec addr = nli.makeAddrRec( this );
|
||||
long rowid = GameUtils.makeNewMultiGame( this, nli,
|
||||
m_sink,
|
||||
getUtilCtxt() );
|
||||
mHelper.getUtilCtxt() );
|
||||
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
|
||||
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
|
||||
DBUtils.setName( this, rowid, nli.gameName );
|
||||
|
@ -1205,4 +1197,23 @@ public class WiDirService extends XWService {
|
|||
private class P2pMsgSink extends MultiMsgSink {
|
||||
public P2pMsgSink() { super( WiDirService.this ); }
|
||||
}
|
||||
|
||||
private class WiDirServiceHelper extends XWServiceHelper {
|
||||
|
||||
WiDirServiceHelper( Service service ) {
|
||||
super( service );
|
||||
}
|
||||
|
||||
@Override
|
||||
MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return m_sink;
|
||||
}
|
||||
|
||||
@Override
|
||||
void postNotification( String device, int gameID, long rowid )
|
||||
{
|
||||
Log.e( TAG, "postNotification() doing nothing" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ import android.widget.ListView;
|
|||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class XWActivity extends FragmentActivity
|
||||
implements Delegator, DlgDelegate.DlgClickNotify {
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
package org.eehouse.android.xw4;
|
||||
|
||||
import android.app.Application;
|
||||
import android.arch.lifecycle.Lifecycle;
|
||||
import android.arch.lifecycle.LifecycleObserver;
|
||||
import android.arch.lifecycle.LifecycleOwner;
|
||||
import android.arch.lifecycle.OnLifecycleEvent;
|
||||
import android.arch.lifecycle.ProcessLifecycleOwner;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
|
@ -30,9 +35,10 @@ import org.eehouse.android.xw4.jni.XwJNI;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class XWApp extends Application {
|
||||
import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
|
||||
|
||||
public class XWApp extends Application implements LifecycleObserver {
|
||||
private static final String TAG = XWApp.class.getSimpleName();
|
||||
|
||||
public static final boolean BTSUPPORTED = true;
|
||||
|
@ -44,8 +50,6 @@ public class XWApp extends Application {
|
|||
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
|
||||
|
@ -65,6 +69,8 @@ public class XWApp extends Application {
|
|||
Assert.assertTrue( s_context == s_context.getApplicationContext() );
|
||||
super.onCreate();
|
||||
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||
|
||||
// This one line should always get logged even if logging is
|
||||
// off -- because logging is on by default until logEnable is
|
||||
// called.
|
||||
|
@ -85,12 +91,25 @@ public class XWApp extends Application {
|
|||
}
|
||||
UpdateCheckReceiver.restartTimer( this );
|
||||
|
||||
BTService.startService( this );
|
||||
RelayService.startService( this );
|
||||
GCMIntentService.init( this );
|
||||
WiDirWrapper.init( this );
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(ON_ANY)
|
||||
public void onAny( LifecycleOwner source, Lifecycle.Event event )
|
||||
{
|
||||
Log.d( TAG, "onAny(%s)", event );
|
||||
switch( event ) {
|
||||
case ON_RESUME:
|
||||
BTService.onAppToForeground( this );
|
||||
break;
|
||||
case ON_STOP:
|
||||
BTService.onAppToBackground( this );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This is called on emulator only, but good for ensuring no memory leaks
|
||||
// by forcing JNI cleanup
|
||||
@Override
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.preference.DialogPreference;
|
|||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
|
|
|
@ -30,7 +30,6 @@ import android.view.View;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
abstract class XWDialogFragment extends DialogFragment {
|
||||
private static final String TAG = XWDialogFragment.class.getSimpleName();
|
||||
|
|
|
@ -23,7 +23,6 @@ package org.eehouse.android.xw4;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
|
|
@ -36,7 +36,6 @@ import android.widget.ListView;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
abstract class XWFragment extends Fragment implements Delegator {
|
||||
private static final String TAG = XWFragment.class.getSimpleName();
|
||||
|
|
|
@ -29,7 +29,6 @@ import android.text.TextUtils;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2010 - 2012 by Eric House (xwords@eehouse.org). All
|
||||
* Copyright 2010 - 2018 by Eric House (xwords@eehouse.org). All
|
||||
* rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -21,165 +21,15 @@
|
|||
package org.eehouse.android.xw4;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||
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;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
abstract class XWService extends Service {
|
||||
class XWService extends Service {
|
||||
private static final String TAG = XWService.class.getSimpleName();
|
||||
public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED };
|
||||
|
||||
protected static MultiService s_srcMgr = null;
|
||||
private static Set<String> s_seen = new HashSet<String>();
|
||||
|
||||
private UtilCtxt m_utilCtxt;
|
||||
|
||||
@Override
|
||||
public IBinder onBind( Intent intent )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public final static void setListener( MultiService.MultiEventListener li )
|
||||
{
|
||||
if ( null == s_srcMgr ) {
|
||||
// DbgUtils.logf( "XWService.setListener: registering %s", li.getClass().getName() );
|
||||
s_srcMgr = new MultiService();
|
||||
}
|
||||
s_srcMgr.setListener( li );
|
||||
}
|
||||
|
||||
protected void postEvent( MultiEvent event, Object ... args )
|
||||
{
|
||||
if ( null != s_srcMgr ) {
|
||||
s_srcMgr.postEvent( event, args );
|
||||
} else {
|
||||
Log.d( TAG, "postEvent(): dropping %s event",
|
||||
event.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we aren't already processing an invitation with this
|
||||
// inviteID.
|
||||
private boolean checkNotDupe( NetLaunchInfo nli )
|
||||
{
|
||||
String inviteID = nli.inviteID();
|
||||
boolean isDupe;
|
||||
synchronized( s_seen ) {
|
||||
isDupe = s_seen.contains( inviteID );
|
||||
if ( !isDupe ) {
|
||||
s_seen.add( inviteID );
|
||||
}
|
||||
}
|
||||
Log.d( TAG, "checkNotDupe('%s') => %b", inviteID, !isDupe );
|
||||
return !isDupe;
|
||||
}
|
||||
|
||||
abstract void postNotification( String device, int gameID, long rowid );
|
||||
|
||||
// Return true if able to start game only
|
||||
protected boolean handleInvitation( NetLaunchInfo nli, String device,
|
||||
DictFetchOwner dfo )
|
||||
{
|
||||
boolean success = false;
|
||||
long[] rowids = DBUtils.getRowIDsFor( this, nli.gameID() );
|
||||
if ( 0 == rowids.length
|
||||
|| ( rowids.length < nli.nPlayersT // will break for two-per-device game
|
||||
&& XWPrefs.getSecondInviteAllowed( this ) ) ) {
|
||||
|
||||
if ( nli.isValid() && checkNotDupe( nli ) ) {
|
||||
|
||||
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) {
|
||||
long rowid = GameUtils.makeNewMultiGame( this, nli,
|
||||
getSink( 0 ),
|
||||
getUtilCtxt() );
|
||||
|
||||
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
|
||||
DBUtils.setName( this, rowid, nli.gameName );
|
||||
}
|
||||
|
||||
postNotification( device, nli.gameID(), rowid );
|
||||
success = true;
|
||||
} else {
|
||||
Intent intent = MultiService
|
||||
.makeMissingDictIntent( this, nli, dfo );
|
||||
MultiService.postMissingDictNotification( this, intent,
|
||||
nli.gameID() );
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d( TAG, "handleInvitation() => %b", success );
|
||||
return success;
|
||||
}
|
||||
|
||||
protected UtilCtxt getUtilCtxt()
|
||||
{
|
||||
if ( null == m_utilCtxt ) {
|
||||
m_utilCtxt = new UtilCtxtImpl( this );
|
||||
}
|
||||
return m_utilCtxt;
|
||||
}
|
||||
|
||||
// Meant to be overridden
|
||||
abstract MultiMsgSink getSink( long rowid );
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2010 - 2018 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.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.CurGameInfo;
|
||||
import org.eehouse.android.xw4.jni.JNIThread;
|
||||
import org.eehouse.android.xw4.jni.UtilCtxt;
|
||||
import org.eehouse.android.xw4.jni.UtilCtxtImpl;
|
||||
import org.eehouse.android.xw4.jni.XwJNI.GamePtr;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
abstract class XWServiceHelper {
|
||||
private static final String TAG = XWServiceHelper.class.getSimpleName();
|
||||
private Service mService;
|
||||
private static MultiService s_srcMgr = null;
|
||||
|
||||
public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED };
|
||||
|
||||
XWServiceHelper( Service service )
|
||||
{
|
||||
mService = service;
|
||||
}
|
||||
|
||||
abstract MultiMsgSink getSink( long rowid );
|
||||
abstract void postNotification( String device, int gameID, long rowid );
|
||||
|
||||
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( context, rowid );
|
||||
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;
|
||||
}
|
||||
|
||||
public final static void setListener( MultiService.MultiEventListener li )
|
||||
{
|
||||
if ( null == s_srcMgr ) {
|
||||
// DbgUtils.logf( "XWService.setListener: registering %s",
|
||||
// li.getClass().getName() );
|
||||
s_srcMgr = new MultiService();
|
||||
}
|
||||
s_srcMgr.setListener( li );
|
||||
}
|
||||
|
||||
protected void postEvent( MultiEvent event, Object ... args )
|
||||
{
|
||||
if ( null != s_srcMgr ) {
|
||||
s_srcMgr.postEvent( event, args );
|
||||
} else {
|
||||
Log.d( TAG, "postEvent(): dropping %s event",
|
||||
event.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean handleInvitation( NetLaunchInfo nli, String device,
|
||||
DictFetchOwner dfo )
|
||||
{
|
||||
boolean success = nli.isValid() && checkNotInFlight( nli );
|
||||
if ( success ) {
|
||||
long[] rowids = DBUtils.getRowIDsFor( mService, nli.gameID() );
|
||||
if ( 0 == rowids.length ) {
|
||||
// cool: we're good
|
||||
} else if ( rowids.length < nli.nPlayersT ) {
|
||||
success = XWPrefs.getSecondInviteAllowed( mService );
|
||||
|
||||
// Allowing a second game allows the common testing action of
|
||||
// sending invitation to myself. But we still need to check
|
||||
// for duplicates! forceChannel's hard to dig up, but works
|
||||
for ( int ii = 0; success && ii < rowids.length; ++ii ) {
|
||||
long rowid = rowids[ii];
|
||||
CurGameInfo gi = null;
|
||||
try ( GameLock lock = GameLock.getFor( rowid ).tryLockRO() ) {
|
||||
// drop invite if can't open game; likely a dupe!
|
||||
if ( null != lock ) {
|
||||
gi = new CurGameInfo( mService );
|
||||
GamePtr gamePtr = GameUtils
|
||||
.loadMakeGame( mService, gi, lock );
|
||||
gamePtr.release();
|
||||
}
|
||||
}
|
||||
|
||||
if ( null == gi ) {
|
||||
// locked. Maybe it's open?
|
||||
JNIThread thread = JNIThread.getRetained( mService, rowid );
|
||||
if ( null != thread ) {
|
||||
gi = thread.getGI();
|
||||
thread.release( false );
|
||||
}
|
||||
}
|
||||
success = null != gi && gi.forceChannel != nli.forceChannel;
|
||||
}
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if ( success ) {
|
||||
if ( DictLangCache.haveDict( mService, nli.lang, nli.dict ) ) {
|
||||
long rowid = GameUtils.makeNewMultiGame( mService, nli,
|
||||
getSink( 0 ),
|
||||
getUtilCtxt() );
|
||||
|
||||
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
|
||||
DBUtils.setName( mService, rowid, nli.gameName );
|
||||
}
|
||||
|
||||
postNotification( device, nli.gameID(), rowid );
|
||||
} else {
|
||||
Intent intent = MultiService
|
||||
.makeMissingDictIntent( mService, nli, dfo );
|
||||
MultiService.postMissingDictNotification( mService, intent,
|
||||
nli.gameID() );
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d( TAG, "handleInvitation() => %b", success );
|
||||
return success;
|
||||
}
|
||||
|
||||
private UtilCtxt m_utilCtxt;
|
||||
protected UtilCtxt getUtilCtxt()
|
||||
{
|
||||
if ( null == m_utilCtxt ) {
|
||||
m_utilCtxt = new UtilCtxtImpl( mService );
|
||||
}
|
||||
return m_utilCtxt;
|
||||
}
|
||||
|
||||
// Check that we aren't already processing an invitation with this
|
||||
// inviteID.
|
||||
private static final long SEEN_INTERVAL_MS = 1000 * 2;
|
||||
private static Map<String, Long> s_seen = new HashMap<>();
|
||||
private boolean checkNotInFlight( NetLaunchInfo nli )
|
||||
{
|
||||
boolean inProcess;
|
||||
String inviteID = nli.inviteID();
|
||||
synchronized( s_seen ) {
|
||||
long now = System.currentTimeMillis();
|
||||
Long lastSeen = s_seen.get( inviteID );
|
||||
inProcess = null != lastSeen && lastSeen + SEEN_INTERVAL_MS > now;
|
||||
if ( !inProcess ) {
|
||||
s_seen.put( inviteID, now );
|
||||
}
|
||||
}
|
||||
Log.d( TAG, "checkNotInFlight('%s') => %b", inviteID, !inProcess );
|
||||
return !inProcess;
|
||||
}
|
||||
}
|
|
@ -23,8 +23,7 @@ package org.eehouse.android.xw4.jni;
|
|||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.BTService;
|
||||
import org.eehouse.android.xw4.GameUtils;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
|
|
|
@ -29,8 +29,8 @@ import java.util.HashSet;
|
|||
import java.util.Random;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.BuildConfig;
|
||||
import org.eehouse.android.xw4.DictLangCache;
|
||||
import org.eehouse.android.xw4.DictUtils;
|
||||
|
|
|
@ -23,8 +23,7 @@ package org.eehouse.android.xw4.jni;
|
|||
import android.content.Context;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.XWApp;
|
||||
import org.eehouse.android.xw4.DBUtils;
|
||||
import org.eehouse.android.xw4.DevID;
|
||||
|
|
|
@ -28,8 +28,7 @@ import java.util.Arrays;
|
|||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.BuildConfig;
|
||||
import org.eehouse.android.xw4.DBUtils;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
|
|
|
@ -26,8 +26,7 @@ import android.graphics.Bitmap;
|
|||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.BuildConfig;
|
||||
import org.eehouse.android.xw4.CommsTransport;
|
||||
import org.eehouse.android.xw4.ConnStatusHandler;
|
||||
|
@ -36,6 +35,7 @@ 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.Utils;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
import org.eehouse.android.xw4.R;
|
||||
import org.eehouse.android.xw4.XWPrefs;
|
||||
|
@ -217,23 +217,29 @@ public class JNIThread extends Thread {
|
|||
if ( null != m_jniGamePtr ) {
|
||||
Log.d( TAG, "configure(): m_jniGamePtr not null; that ok?" );
|
||||
}
|
||||
m_jniGamePtr = null;
|
||||
if ( null != stream ) {
|
||||
m_jniGamePtr = XwJNI.initFromStream( m_rowid, stream, m_gi,
|
||||
dictNames, pairs.m_bytes,
|
||||
pairs.m_paths,
|
||||
m_gi.langName( m_context ),
|
||||
utils, null, cp, m_xport );
|
||||
|
||||
synchronized ( this ) {
|
||||
m_jniGamePtr = null;
|
||||
if ( null != stream ) {
|
||||
m_jniGamePtr = XwJNI.initFromStream( m_rowid, stream, m_gi,
|
||||
dictNames, pairs.m_bytes,
|
||||
pairs.m_paths,
|
||||
m_gi.langName( m_context ),
|
||||
utils, null, cp, m_xport );
|
||||
}
|
||||
if ( null == m_jniGamePtr ) {
|
||||
m_jniGamePtr = XwJNI.initNew( m_gi, dictNames, pairs.m_bytes,
|
||||
pairs.m_paths,
|
||||
m_gi.langName(m_context),
|
||||
utils, null, cp, m_xport );
|
||||
}
|
||||
Assert.assertNotNull( m_jniGamePtr );
|
||||
notifyAll();
|
||||
}
|
||||
if ( null == m_jniGamePtr ) {
|
||||
m_jniGamePtr = XwJNI.initNew( m_gi, dictNames, pairs.m_bytes,
|
||||
pairs.m_paths,
|
||||
m_gi.langName(m_context),
|
||||
utils, null, cp, m_xport );
|
||||
}
|
||||
Assert.assertNotNull( m_jniGamePtr );
|
||||
|
||||
m_lastSavedState = Arrays.hashCode( stream );
|
||||
}
|
||||
Log.d( TAG, "configure() => %b", success );
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -418,13 +424,24 @@ public class JNIThread extends Thread {
|
|||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Log.d( TAG, "run() starting" );
|
||||
boolean[] barr = new boolean[2]; // scratch boolean
|
||||
for ( ; ; ) {
|
||||
synchronized ( this ) {
|
||||
if ( m_stopped ) {
|
||||
break;
|
||||
} else if ( null == m_jniGamePtr ) {
|
||||
try {
|
||||
wait();
|
||||
} catch ( InterruptedException iex ) {
|
||||
Log.d( TAG, "exiting run() on interrupt: %s",
|
||||
iex.getMessage() );
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -729,6 +746,7 @@ public class JNIThread extends Thread {
|
|||
m_jniGamePtr.release();
|
||||
m_jniGamePtr = null;
|
||||
}
|
||||
Log.d( TAG, "run() finished" );
|
||||
} // run
|
||||
|
||||
public void handleBkgrnd( JNICmd cmd, Object... args )
|
||||
|
@ -751,8 +769,8 @@ public class JNIThread extends Thread {
|
|||
public void handle( JNICmd cmd, Object... args )
|
||||
{
|
||||
if ( m_stopped && ! JNICmd.CMD_NONE.equals(cmd) ) {
|
||||
Log.w( TAG, "NOT adding %s to stopped thread!!!", cmd.toString() );
|
||||
DbgUtils.printStack( TAG );
|
||||
Log.w( TAG, "handle(%s): NOT adding to stopped thread!!!", cmd );
|
||||
// DbgUtils.printStack( TAG );
|
||||
} else {
|
||||
m_queue.add( new QueueElem( cmd, true, args ) );
|
||||
}
|
||||
|
@ -804,20 +822,27 @@ public class JNIThread extends Thread {
|
|||
}
|
||||
}
|
||||
|
||||
public static JNIThread getRetained( long rowid )
|
||||
public static JNIThread getRetained( Context context, long rowid )
|
||||
{
|
||||
return getRetained( rowid, false );
|
||||
return getRetained( context, rowid, false );
|
||||
}
|
||||
|
||||
public static JNIThread getRetained( long rowid, boolean makeNew )
|
||||
public static JNIThread getRetained( Context context, long rowid, boolean makeNew )
|
||||
{
|
||||
JNIThread result = null;
|
||||
synchronized( s_instances ) {
|
||||
result = s_instances.get( rowid );
|
||||
if ( null == result && makeNew ) {
|
||||
result = new JNIThread( new GameLock( rowid, true ).lock() );
|
||||
Assert.assertNotNull( result );
|
||||
s_instances.put( rowid, result );
|
||||
DbgUtils.assertOnUIThread(); // can't use GameLock.lock()
|
||||
if ( true /*test done*/ || (0 != Utils.nextRandomInt() % 3) ) {
|
||||
GameLock lock = GameLock.getFor( rowid ).tryLock();
|
||||
if ( lock != null ) {
|
||||
result = new JNIThread( lock );
|
||||
s_instances.put( rowid, result );
|
||||
} else {
|
||||
DbgUtils.toastNoLock( TAG, context, "getRetained(%d)", rowid );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( null != result ) {
|
||||
result.retain_sync();
|
||||
|
|
|
@ -22,8 +22,7 @@ package org.eehouse.android.xw4.jni;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.DBUtils;
|
||||
import org.eehouse.android.xw4.XWApp;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
|
|
|
@ -25,8 +25,7 @@ import android.text.TextUtils;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.BuildConfig;
|
||||
import org.eehouse.android.xw4.R;
|
||||
import org.eehouse.android.xw4.Utils;
|
||||
|
|
|
@ -67,7 +67,10 @@ public interface UtilCtxt {
|
|||
void notifyMove( String query );
|
||||
void notifyTrade( String[] tiles );
|
||||
|
||||
// These oughtto be an enum but then I'd have to cons one up in C.
|
||||
// These can't be an ENUM! The set is open-ended, with arbitrary values
|
||||
// added to ERR_RELAY_BASE, so no way to create an enum from an int in the
|
||||
// jni world. int has to be passed into userError(). Trust me: I
|
||||
// tried. :-)
|
||||
static final int ERR_NONE = 0;
|
||||
static final int ERR_TILES_NOT_IN_LINE = 1;
|
||||
static final int ERR_NO_EMPTIES_IN_TURN = 2;
|
||||
|
@ -82,11 +85,12 @@ public interface UtilCtxt {
|
|||
static final int ERR_REG_SERVER_SANS_REMOTE = 11;
|
||||
static final int STR_NEED_BT_HOST_ADDR = 12;
|
||||
static final int ERR_NO_EMPTY_TRADE = 13;
|
||||
static final int ERR_CANT_UNDO_TILEASSIGN = 14;
|
||||
static final int ERR_CANT_HINT_WHILE_DISABLED = 15;
|
||||
static final int ERR_NO_HINT_FOUND = 16;
|
||||
static final int ERR_TOO_MANY_TRADE = 14;
|
||||
static final int ERR_CANT_UNDO_TILEASSIGN = 15;
|
||||
static final int ERR_CANT_HINT_WHILE_DISABLED = 16;
|
||||
static final int ERR_NO_HINT_FOUND = 17;
|
||||
|
||||
static final int ERR_RELAY_BASE = 17;
|
||||
static final int ERR_RELAY_BASE = 18;
|
||||
void userError( int id );
|
||||
|
||||
void informMove( int turn, String expl, String words );
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.eehouse.android.xw4.jni;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Log;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||
|
|
|
@ -24,8 +24,7 @@ import android.graphics.Rect;
|
|||
|
||||
import java.util.Arrays;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
import org.eehouse.android.xw4.NetLaunchInfo;
|
||||
import org.eehouse.android.xw4.Utils;
|
||||
|
@ -59,6 +58,8 @@ public class XwJNI {
|
|||
return this;
|
||||
}
|
||||
|
||||
public long getRowid() { return m_rowid; }
|
||||
|
||||
// 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 synchronized void release()
|
||||
|
@ -365,6 +366,7 @@ public class XwJNI {
|
|||
public static native void server_handleUndo( GamePtr gamePtr );
|
||||
public static native boolean server_do( GamePtr gamePtr );
|
||||
public static native void server_tilesPicked( GamePtr gamePtr, int player, int[] tiles );
|
||||
public static native int server_countTilesInPool( GamePtr gamePtr );
|
||||
|
||||
public static native String server_formatDictCounts( GamePtr gamePtr, int nCols );
|
||||
public static native boolean server_getGameIsOver( GamePtr gamePtr );
|
||||
|
|
|
@ -42,8 +42,8 @@ import android.widget.Spinner;
|
|||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.Assert;
|
||||
import org.eehouse.android.xw4.DBUtils;
|
||||
import org.eehouse.android.xw4.DbgUtils;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
|
|
BIN
xwords4/android/app/src/main/res/drawable/notify_btservice.png
Normal file
BIN
xwords4/android/app/src/main/res/drawable/notify_btservice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 308 B |
|
@ -5,8 +5,8 @@
|
|||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<Button android:id="@+id/button_add"
|
||||
android:text="@string/bt_pick_addall_button"
|
||||
<Button android:id="@+id/button_scan"
|
||||
android:text="@string/button_scan"
|
||||
style="@style/evenly_spaced_horizontal"
|
||||
/>
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
<item android:id="@+id/games_game_copy"
|
||||
android:title="@string/list_item_copy"
|
||||
/>
|
||||
<item android:id="@+id/games_game_netstats"
|
||||
android:title="@string/list_item_netstats"
|
||||
/>
|
||||
<item android:id="@+id/games_game_select"
|
||||
android:title="@string/list_item_select"
|
||||
/>
|
||||
|
|
|
@ -92,8 +92,8 @@
|
|||
<string name="key_udp_interval">key_udp_interval</string>
|
||||
|
||||
<string name="key_notagain_sync">key_notagain_sync</string>
|
||||
<!-- <string name="key_notagain_sms_ready">key_notagain_sms_ready</string> -->
|
||||
<string name="key_notagain_newselect">key_notagain_newselect</string>
|
||||
<string name="key_notagain_emptybtscan">key_notagain_emptybtscan</string>
|
||||
<string name="key_notagain_backclears">key_notagain_backclears</string>
|
||||
<string name="key_notagain_chat">key_notagain_chat</string>
|
||||
<string name="key_notagain_relay">key_notagain_relay</string>
|
||||
|
@ -136,7 +136,6 @@
|
|||
<string name="key_invite_multi">key_invite_multi</string>
|
||||
<string name="key_na_rematch_two_only">key_notagain_rematch_two_only</string>
|
||||
<string name="key_notagain_dfltname">key_notagain_dfltname</string>
|
||||
|
||||
<string name="key_na_comms_bt">key_na_comms_bt</string>
|
||||
<string name="key_na_comms_p2p">key_na_comms_p2p</string>
|
||||
<string name="key_na_comms_sms">key_na_comms_sms</string>
|
||||
|
|
|
@ -148,6 +148,9 @@
|
|||
<!-- Title of dialog for renaming game (triggered by selecting
|
||||
list_item_rename) -->
|
||||
|
||||
<!-- long-tap game list menuitem for showing info about game's connectivity -->
|
||||
<string name="list_item_netstats">Connections…</string>
|
||||
|
||||
<!-- If you try to copy a networked game you get this error
|
||||
message. -->
|
||||
<string name="no_copy_network">Games that have already connected
|
||||
|
@ -1319,6 +1322,14 @@
|
|||
<string name="str_too_few_tiles_left_to_trade">Too few tiles left
|
||||
to exchange.</string>
|
||||
|
||||
<!-- In Spanish you can trade with fewer than 7 tiles in the
|
||||
pool, so you can wind up selecting more than are
|
||||
available. You get this error message in that case. -->
|
||||
<plurals name="too_many_trade_fmt">
|
||||
<item quantity="one">Too many tiles selected. Please select only one.</item>
|
||||
<item quantity="other">Too many tiles selected. Please select no more than %1$d.</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Displayed if you try to use the undo menuitem or button and
|
||||
there are no tiles on the board (no move has yet been made.)
|
||||
[If I'm being clever and disabling those features in this
|
||||
|
@ -1813,15 +1824,18 @@
|
|||
|
||||
<!-- <string name="newgame_enable_bt">Turn Bluetooth on</string> -->
|
||||
|
||||
<!-- In the Bluetooth invite device dialog -->
|
||||
<string name="bt_pick_addall_button">Add all Paired</string>
|
||||
<!-- -->
|
||||
<string name="bt_pick_clear_button">Remove checked</string>
|
||||
<!-- -->
|
||||
<!-- In invitation dialogs, button to remove checked items -->
|
||||
<string name="bt_pick_clear_button">Remove</string>
|
||||
|
||||
<!-- In the Bluetooth invite device dialog, to launch Andorid BT
|
||||
settings pair new device -->
|
||||
<string name="bt_pair_settings">Pair More</string>
|
||||
<!-- In the Bluetooth invite device dialog, kick off a new scan of
|
||||
nearby devices -->
|
||||
<string name="button_scan">Rescan</string>
|
||||
|
||||
<!-- -->
|
||||
<string name="invite_progress_title">Connecting...</string>
|
||||
<string name="invite_progress_title">Connecting…</string>
|
||||
<string name="invite_progress_fmt">Sending invitation to CrossWords on %1$s</string>
|
||||
<!-- -->
|
||||
<string name="summary_wait_host">Waiting for connection[s]</string>
|
||||
|
@ -1837,7 +1851,6 @@
|
|||
<string name="new_bt_body_fmt">A player on the device %1$s wants to start a game</string>
|
||||
<string name="new_relay_body">Tap to open the new game</string>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<string name="bt_bad_proto_fmt">The version of CrossWords on
|
||||
\"%1$s\" is incompatible with this one for play using
|
||||
|
@ -1850,13 +1863,20 @@
|
|||
continue.</string>
|
||||
|
||||
<!-- -->
|
||||
<plurals name="invite_bt_desc_fmt">
|
||||
<item quantity="one">Please check the device
|
||||
you want to include in this game.\n\nUse the \"%2$s\"
|
||||
button if you don\'t see the device you expect.</item>
|
||||
<item quantity="other">Please check up to %1$d device[s]
|
||||
you want to include in this game.\n\nUse the \"%2$s\"
|
||||
button if you don\'t see a device you expect.</item>
|
||||
<plurals name="invite_bt_desc_fmt_2">
|
||||
<item quantity="one">Please check the device you want to include
|
||||
in this game.</item>
|
||||
<item quantity="other">Please check up to %1$d device[s] you
|
||||
want to include in this game.</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Appended to above -->
|
||||
<string name="invite_bt_desc_postscript">\n\n(The list is of devices you\'ve
|
||||
paired and on which CrossWords has been detected.)</string>
|
||||
|
||||
<plurals name="bt_scan_progress_fmt">
|
||||
<item quantity="one">Scanning for CrossWords</item>
|
||||
<item quantity="other">Scanning for CrossWords on %1$d paired devices.</item>
|
||||
</plurals>
|
||||
<!-- -->
|
||||
<string name="bt_resend_fmt">Bluetooth send to %1$s failed; retry
|
||||
|
@ -2026,6 +2046,10 @@
|
|||
<string name="title_enable_p2p">Enable WiFi Direct</string>
|
||||
<string name="summary_enable_p2p">Experimental, uses lots of battery</string>
|
||||
|
||||
<string name="bkng_notify_text">Accepting Bluetooth messages…</string>
|
||||
<string name="bkng_stop_text">Stop</string>
|
||||
<string name="bkng_settings_text">Settings</string>
|
||||
|
||||
<!-- -->
|
||||
<string name="confirm_sms_title">Confirm your SMS plan</string>
|
||||
<!-- -->
|
||||
|
@ -2393,6 +2417,9 @@
|
|||
within Bluetooth range and that CrossWords is installed on
|
||||
it.</string>
|
||||
|
||||
<!-- How long ago did we last see a BT device via scan -->
|
||||
<string name="bt_scan_age_fmt">Last scan response: %1$s</string>
|
||||
|
||||
<!-- label within default wordlists in app preferences -->
|
||||
<string name="default_language">Default language</string>
|
||||
|
||||
|
@ -2447,7 +2474,7 @@
|
|||
<string name="waiting_invite_title">Waiting for response</string>
|
||||
<string name="waiting_rematch_title">Rematch in progress</string>
|
||||
<!-- Button for alert with title above -->
|
||||
<string name="button_wait">Wait</string>
|
||||
<string name="button_close">Close</string>
|
||||
<string name="button_reinvite">Re-invite</string>
|
||||
|
||||
<string name="invite_stays">(This dialog will stay up until all
|
||||
|
@ -2641,6 +2668,12 @@
|
|||
player name \"%1$s\". Would you like to personalize with your own
|
||||
name before you create this game?</string>
|
||||
|
||||
<string name="btservice_expl">This notification is present whenever
|
||||
CrossWords is running in the background to accept Bluetooth
|
||||
messages. It usually runs for about 15 minutes after CrossWords
|
||||
starts or a Bluetooth message is received.
|
||||
</string>
|
||||
|
||||
<string name="no_invites">This game has sent no invitations</string>
|
||||
|
||||
<string name="disable_dualpane">Disable side-by-side</string>
|
||||
|
@ -2715,4 +2748,14 @@
|
|||
<string name="perms_rationale_title">Android Permissions</string>
|
||||
<string name="toast_no_permission">Permission not granted</string>
|
||||
|
||||
<!-- Explanation in settings for always-on BT notification -->
|
||||
<string name="foreground_channel_expl">Accepting Bluetooth in background</string>
|
||||
<!-- Explanation in settings for traditional move-arrived notification -->
|
||||
<string name="gameevent_channel_expl">In-game events</string>
|
||||
|
||||
<string name="not_again_emptybtscan">If a scan doesn\'t find the device you expect:\n
|
||||
• First, just try again\n
|
||||
• Make sure Bluetooth is on on the other device\n
|
||||
• Launch CrossWords on the other device\n
|
||||
</string>
|
||||
</resources>
|
||||
|
|
|
@ -29,7 +29,6 @@ import com.google.android.gcm.GCMRegistrar;
|
|||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class GCMIntentService extends GCMBaseIntentService {
|
||||
private static final String TAG = GCMIntentService.class.getSimpleName();
|
||||
|
|
BIN
xwords4/android/app/src/xw4d/res/drawable/notify_btservice.png
Normal file
BIN
xwords4/android/app/src/xw4d/res/drawable/notify_btservice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 471 B |
|
@ -2,9 +2,9 @@
|
|||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
|
@ -17,6 +17,7 @@ buildscript {
|
|||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +30,7 @@ subprojects {
|
|||
afterEvaluate {project ->
|
||||
if (project.hasProperty("android")) {
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
compileSdkVersion 26
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,15 @@ and_util_getMD5SumForDict( JNIUtilCtxt* jniutil, const XP_UCHAR* name,
|
|||
jstring result =
|
||||
(*env)->CallObjectMethod( env, jniutil->jjniutil, mid, jname, jbytes );
|
||||
deleteLocalRefs( env, jname, jbytes, DELETE_NO_REF );
|
||||
|
||||
#ifdef DEBUG
|
||||
if ( !!result ) {
|
||||
const char* chars = (*env)->GetStringUTFChars( env, result, NULL );
|
||||
XP_LOGF( "%s(%s, len=%d) => %s", __func__, name, len, chars );
|
||||
(*env)->ReleaseStringUTFChars( env, result, chars );
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -702,10 +702,10 @@ and_dutil_deviceRegistered( XW_DUtilCtxt* duc, DevIDType typ,
|
|||
const XP_UCHAR* idRelay )
|
||||
{
|
||||
DUTIL_CBK_HEADER( "deviceRegistered",
|
||||
"(L" PKG_PATH("jni/UtilCtxt$DevIDType") ";Ljava/lang/String;)V" );
|
||||
"(L" PKG_PATH("jni/DUtilCtxt$DevIDType") ";Ljava/lang/String;)V" );
|
||||
jstring jstr = (*env)->NewStringUTF( env, idRelay );
|
||||
jobject jtyp = intToJEnum( env, typ,
|
||||
PKG_PATH("jni/UtilCtxt$DevIDType") );
|
||||
PKG_PATH("jni/DUtilCtxt$DevIDType") );
|
||||
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jtyp, jstr );
|
||||
deleteLocalRefs( env, jstr, jtyp, DELETE_NO_REF );
|
||||
DUTIL_CBK_TAIL();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue