mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-30 10:26:58 +01:00
Merge branch 'android_branch' into android_localize
Conflicts: xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java
This commit is contained in:
commit
716109e979
7 changed files with 931 additions and 689 deletions
|
@ -208,6 +208,14 @@
|
|||
<intent-filter android:priority="999" >
|
||||
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.DATA_SMS_RECEIVED" />
|
||||
<data android:scheme="sms" />
|
||||
<data android:port="3344" />
|
||||
<data android:host="*" />
|
||||
</intent-filter>
|
||||
|
||||
</receiver>
|
||||
|
||||
<service android:name="SMSService"/>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -41,6 +41,7 @@
|
|||
<string name="key_dict_host">key_dict_host3</string>
|
||||
<string name="key_logging_on">key_logging_on</string>
|
||||
<string name="key_show_sms">key_show_sms</string>
|
||||
<string name="key_send_data_sms">key_send_data_sms</string>
|
||||
<string name="key_init_hintsallowed">key_init_hintsallowed</string>
|
||||
<string name="key_init_nethintsallowed">key_init_nethintsallowed</string>
|
||||
<string name="key_init_autojuggle">key_init_autojuggle</string>
|
||||
|
@ -146,6 +147,8 @@
|
|||
<string name="debug_features">Enable debug features</string>
|
||||
<string name="debug_features_summary">Menuitems etc. (release builds
|
||||
only)</string>
|
||||
<string name="title_send_data_sms">Send SMS as data</string>
|
||||
<string name="summary_send_data_sms">(fails on CDMA phones)</string>
|
||||
<string name="board_menu_game_netstats">Network stats</string>
|
||||
<string name="netstats_title">Game network stats</string>
|
||||
|
||||
|
|
|
@ -344,6 +344,11 @@
|
|||
android:title="Show SMS sends, receives"
|
||||
android:defaultValue="false"
|
||||
/>
|
||||
<CheckBoxPreference android:key="@string/key_send_data_sms"
|
||||
android:title="@string/title_send_data_sms"
|
||||
android:summary="@string/summary_send_data_sms"
|
||||
android:defaultValue="true"
|
||||
/>
|
||||
<org.eehouse.android.xw4.XWEditTextPreference
|
||||
android:key="@string/key_relay_host"
|
||||
android:title="@string/relay_host"
|
||||
|
|
|
@ -33,28 +33,37 @@ public class SMSReceiver extends BroadcastReceiver {
|
|||
@Override
|
||||
public void onReceive( Context context, Intent intent )
|
||||
{
|
||||
String action = intent.getAction();
|
||||
Bundle bundle = intent.getExtras();
|
||||
DbgUtils.logf( "onReceive: action=%s", action );
|
||||
if ( null != bundle ) {
|
||||
boolean isData =
|
||||
action.equals("android.intent.action.DATA_SMS_RECEIVED");
|
||||
boolean isMine = false;
|
||||
Object[] pdus = (Object[])bundle.get( "pdus" );
|
||||
SmsMessage[] smses = new SmsMessage[pdus.length];
|
||||
|
||||
for ( int ii = 0; ii < pdus.length; ++ii ) {
|
||||
SmsMessage sms = SmsMessage.createFromPdu((byte[])pdus[ii]);
|
||||
String body = sms.getMessageBody();
|
||||
String postDetectable = SMSService.fromPublicFmt( body );
|
||||
isMine = null != postDetectable;
|
||||
if ( isMine ) {
|
||||
String phone = sms.getOriginatingAddress();
|
||||
DbgUtils.logf( "SMSReceiver: \"%s\" from %s",
|
||||
body, phone );
|
||||
SMSService.handleFrom( context, postDetectable, phone );
|
||||
String phone = sms.getOriginatingAddress();
|
||||
if ( isData ) {
|
||||
byte[] body = sms.getUserData();
|
||||
SMSService.handleFrom( context, body, phone );
|
||||
} else {
|
||||
String body = sms.getMessageBody();
|
||||
String postDetectable = SMSService.fromPublicFmt( body );
|
||||
isMine = null != postDetectable;
|
||||
if ( isMine ) {
|
||||
DbgUtils.logf( "SMSReceiver: \"%s\" from %s",
|
||||
body, phone );
|
||||
SMSService.handleFrom( context, postDetectable, phone );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isMine ) {
|
||||
DbgUtils.logf( "SMSReceiver: CONSUMING message" );
|
||||
abortBroadcast();
|
||||
if ( isMine ) {
|
||||
DbgUtils.logf( "SMSReceiver: CONSUMING message" );
|
||||
abortBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
@ -62,6 +63,7 @@ public class SMSService extends XWService {
|
|||
|
||||
private static final int SMS_PROTO_VERSION = 0;
|
||||
private static final int MAX_LEN_TEXT = 100;
|
||||
private static final int MAX_LEN_BINARY = 100;
|
||||
private static final int HANDLE = 1;
|
||||
private static final int INVITE = 2;
|
||||
private static final int SEND = 3;
|
||||
|
@ -70,6 +72,7 @@ public class SMSService extends XWService {
|
|||
private static final int CHECK_MSGDB = 6;
|
||||
private static final int ADDED_MISSING = 7;
|
||||
private static final int STOP_SELF = 8;
|
||||
private static final int HANDLEDATA = 9;
|
||||
|
||||
private static final String CMD_STR = "CMD";
|
||||
private static final String BUFFER = "BUFFER";
|
||||
|
@ -77,13 +80,15 @@ public class SMSService extends XWService {
|
|||
private static final String PHONE = "PHONE";
|
||||
|
||||
private static Boolean s_showToasts = null;
|
||||
|
||||
private static Boolean s_asData = null;
|
||||
|
||||
// All messages are base64-encoded byte arrays. The first byte is
|
||||
// always one of these. What follows depends.
|
||||
private enum SMS_CMD { NONE, INVITE, DATA, DEATH, ACK, };
|
||||
|
||||
private BroadcastReceiver m_sentReceiver;
|
||||
private BroadcastReceiver m_receiveReceiver;
|
||||
private OnSharedPreferenceChangeListener m_prefsListener;
|
||||
|
||||
private int m_nReceived = 0;
|
||||
private static int s_nSent = 0;
|
||||
|
@ -119,6 +124,15 @@ public class SMSService extends XWService {
|
|||
context.startService( intent );
|
||||
}
|
||||
|
||||
public static void handleFrom( Context context, byte[] buffer,
|
||||
String phone )
|
||||
{
|
||||
Intent intent = getIntentTo( context, HANDLEDATA );
|
||||
intent.putExtra( BUFFER, buffer );
|
||||
intent.putExtra( PHONE, phone );
|
||||
context.startService( intent );
|
||||
}
|
||||
|
||||
public static void inviteRemote( Context context, String phone,
|
||||
int gameID, String gameName,
|
||||
int lang, String dict,
|
||||
|
@ -201,10 +215,8 @@ public class SMSService extends XWService {
|
|||
private static Intent getIntentTo( Context context, int cmd )
|
||||
{
|
||||
if ( null == s_showToasts ) {
|
||||
SharedPreferences sp
|
||||
= PreferenceManager.getDefaultSharedPreferences( context );
|
||||
String key = LocUtils.getString( context, R.string.key_show_sms );
|
||||
s_showToasts = sp.getBoolean( key, false );
|
||||
s_showToasts =
|
||||
XWPrefs.getPrefsBoolean( context, R.string.key_show_sms, false );
|
||||
}
|
||||
|
||||
Intent intent = new Intent( context, SMSService.class );
|
||||
|
@ -233,6 +245,13 @@ public class SMSService extends XWService {
|
|||
unregisterReceiver( m_receiveReceiver );
|
||||
m_receiveReceiver = null;
|
||||
}
|
||||
if ( null != m_prefsListener ) {
|
||||
SharedPreferences sp
|
||||
= PreferenceManager.getDefaultSharedPreferences( this );
|
||||
sp.unregisterOnSharedPreferenceChangeListener( m_prefsListener );
|
||||
m_prefsListener = null;
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
@ -257,6 +276,7 @@ public class SMSService extends XWService {
|
|||
}
|
||||
break;
|
||||
case HANDLE:
|
||||
case HANDLEDATA:
|
||||
++m_nReceived;
|
||||
ConnStatusHandler.
|
||||
updateStatusIn( this, null,
|
||||
|
@ -264,9 +284,14 @@ public class SMSService extends XWService {
|
|||
if ( s_showToasts ) {
|
||||
DbgUtils.showf( this, "got %dth msg", m_nReceived );
|
||||
}
|
||||
String buffer = intent.getStringExtra( BUFFER );
|
||||
String phone = intent.getStringExtra( PHONE );
|
||||
receiveBuffer( buffer, phone );
|
||||
if ( HANDLE == cmd ) {
|
||||
String buffer = intent.getStringExtra( BUFFER );
|
||||
receiveBuffer( buffer, phone );
|
||||
} else {
|
||||
byte[] buffer = intent.getByteArrayExtra( BUFFER );
|
||||
receiveBuffer( buffer, phone );
|
||||
}
|
||||
break;
|
||||
case INVITE:
|
||||
case ADDED_MISSING:
|
||||
|
@ -388,13 +413,24 @@ public class SMSService extends XWService {
|
|||
das.write( bytes, 0, bytes.length );
|
||||
das.flush();
|
||||
|
||||
String as64 = XwJNI.base64Encode( bas.toByteArray() );
|
||||
String[] msgs = breakAndEncode( as64 );
|
||||
return sendBuffers( msgs, phone );
|
||||
byte[] data = bas.toByteArray();
|
||||
boolean result;
|
||||
if ( null == s_asData ) {
|
||||
boolean asData =
|
||||
XWPrefs.getPrefsBoolean( this, R.string.key_send_data_sms,
|
||||
true );
|
||||
s_asData = new Boolean( asData );
|
||||
}
|
||||
if ( s_asData ) {
|
||||
byte[][] msgs = breakAndEncode( data );
|
||||
result = sendBuffers( msgs, phone, data );
|
||||
} else {
|
||||
result = sendAsText( data, phone );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String[] breakAndEncode( String msg )
|
||||
throws java.io.IOException
|
||||
private String[] breakAndEncode( String msg ) throws java.io.IOException
|
||||
{
|
||||
// TODO: as optimization, truncate header when only one packet
|
||||
// required
|
||||
|
@ -418,6 +454,33 @@ public class SMSService extends XWService {
|
|||
return result;
|
||||
}
|
||||
|
||||
private byte[][] breakAndEncode( byte msg[] ) throws java.io.IOException
|
||||
{
|
||||
int count = (msg.length + (MAX_LEN_BINARY-1)) / MAX_LEN_BINARY;
|
||||
byte[][] result = new byte[count][];
|
||||
int msgID = ++s_nSent % 0x000000FF;
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for ( int ii = 0; ii < count; ++ii ) {
|
||||
int len = msg.length - end;
|
||||
if ( len > MAX_LEN_BINARY ) {
|
||||
len = MAX_LEN_BINARY;
|
||||
}
|
||||
end += len;
|
||||
byte[] part = new byte[4 + len];
|
||||
part[0] = (byte)0; // proto
|
||||
part[1] = (byte)msgID;
|
||||
part[2] = (byte)ii;
|
||||
part[3] = (byte)count;
|
||||
System.arraycopy( msg, start, part, 4, len );
|
||||
|
||||
result[ii] = part;
|
||||
start = end;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void receive( SMS_CMD cmd, byte[] data, String phone )
|
||||
{
|
||||
DataInputStream dis =
|
||||
|
@ -473,6 +536,19 @@ public class SMSService extends XWService {
|
|||
}
|
||||
}
|
||||
|
||||
private void receiveBuffer( byte[] buffer, String senderPhone )
|
||||
{
|
||||
byte proto = buffer[0];
|
||||
int id = buffer[1];
|
||||
int index = buffer[2];
|
||||
int count = buffer[3];
|
||||
byte[] rest = new byte[buffer.length - 4];
|
||||
System.arraycopy( buffer, 4, rest, 0, rest.length );
|
||||
tryAssemble( senderPhone, id, index, count, rest );
|
||||
|
||||
sendResult( MultiEvent.SMS_RECEIVE_OK );
|
||||
}
|
||||
|
||||
private void receiveBuffer( String as64, String senderPhone )
|
||||
{
|
||||
String[] parts = as64.split( ":" );
|
||||
|
@ -488,6 +564,34 @@ public class SMSService extends XWService {
|
|||
}
|
||||
}
|
||||
|
||||
private void tryAssemble( String senderPhone, int id, int index,
|
||||
int count, byte[] msg )
|
||||
{
|
||||
if ( index == 0 && count == 1 ) {
|
||||
disAssemble( senderPhone, msg );
|
||||
} else {
|
||||
// required? Should always be in main thread.
|
||||
synchronized( s_partialMsgs ) {
|
||||
HashMap<Integer, MsgStore> perPhone =
|
||||
s_partialMsgs.get( senderPhone );
|
||||
if ( null == perPhone ) {
|
||||
perPhone = new HashMap <Integer, MsgStore>();
|
||||
s_partialMsgs.put( senderPhone, perPhone );
|
||||
}
|
||||
MsgStore store = perPhone.get( id );
|
||||
if ( null == store ) {
|
||||
store = new MsgStore( id, count, false );
|
||||
perPhone.put( id, store );
|
||||
}
|
||||
|
||||
if ( store.add( index, msg ).isComplete() ) {
|
||||
disAssemble( senderPhone, store.messageData() );
|
||||
perPhone.remove( id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryAssemble( String senderPhone, int id, int index,
|
||||
int count, String msg )
|
||||
{
|
||||
|
@ -504,18 +608,41 @@ public class SMSService extends XWService {
|
|||
}
|
||||
MsgStore store = perPhone.get( id );
|
||||
if ( null == store ) {
|
||||
store = new MsgStore( id, count );
|
||||
store = new MsgStore( id, count, true );
|
||||
perPhone.put( id, store );
|
||||
}
|
||||
|
||||
if ( store.add( index, msg ).isComplete() ) {
|
||||
disAssemble( senderPhone, store.message() );
|
||||
disAssemble( senderPhone, store.messageText() );
|
||||
perPhone.remove( id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void disAssemble( String senderPhone, byte[] fullMsg )
|
||||
{
|
||||
DataInputStream dis =
|
||||
new DataInputStream( new ByteArrayInputStream(fullMsg) );
|
||||
try {
|
||||
byte proto = dis.readByte();
|
||||
if ( SMS_PROTO_VERSION != proto ) {
|
||||
DbgUtils.logf( "SMSService.disAssemble: bad proto %d; dropping",
|
||||
proto );
|
||||
} else {
|
||||
SMS_CMD cmd = SMS_CMD.values()[dis.readByte()];
|
||||
byte[] rest = new byte[dis.available()];
|
||||
dis.read( rest );
|
||||
receive( cmd, rest, senderPhone );
|
||||
}
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.loge( ioe );
|
||||
} catch ( ArrayIndexOutOfBoundsException oob ) {
|
||||
// enum this older code doesn't know about; drop it
|
||||
DbgUtils.logf( "disAssemble: dropping message with too-new enum" );
|
||||
}
|
||||
}
|
||||
|
||||
private void disAssemble( String senderPhone, String fullMsg )
|
||||
{
|
||||
byte[] data = XwJNI.base64Decode( fullMsg );
|
||||
|
@ -593,6 +720,39 @@ public class SMSService extends XWService {
|
|||
return success;
|
||||
}
|
||||
|
||||
private boolean sendBuffers( byte[][] fragments, String phone, byte[] data )
|
||||
{
|
||||
boolean success = false;
|
||||
try {
|
||||
SmsManager mgr = SmsManager.getDefault();
|
||||
PendingIntent sent = makeStatusIntent( MSG_SENT );
|
||||
PendingIntent delivery = makeStatusIntent( MSG_DELIVERED );
|
||||
for ( byte[] fragment : fragments ) {
|
||||
mgr.sendDataMessage( phone, null, (short)3344, fragment,
|
||||
sent, delivery );
|
||||
}
|
||||
if ( s_showToasts ) {
|
||||
DbgUtils.showf( this, "sent %dth msg", s_nSent );
|
||||
}
|
||||
success = true;
|
||||
} catch ( IllegalArgumentException iae ) {
|
||||
DbgUtils.logf( "sendBuffers(%s): %s", phone, iae.toString() );
|
||||
} catch ( NullPointerException npe ) {
|
||||
DbgUtils.showf( this, "Switching to regular SMS" );
|
||||
s_asData = new Boolean( false );
|
||||
XWPrefs.setPrefsBoolean( this, R.string.key_send_data_sms,
|
||||
false );
|
||||
success = sendAsText( data, phone );
|
||||
} catch ( Exception ee ) {
|
||||
DbgUtils.loge( ee );
|
||||
}
|
||||
|
||||
ConnStatusHandler.updateStatusOut( this, null,
|
||||
CommsConnType.COMMS_CONN_SMS,
|
||||
success );
|
||||
return success;
|
||||
}
|
||||
|
||||
private static void fillInviteIntent( Intent intent, String phone,
|
||||
int gameID, String gameName,
|
||||
int lang, String dict,
|
||||
|
@ -691,14 +851,39 @@ public class SMSService extends XWService {
|
|||
@Override
|
||||
public void onReceive(Context arg0, Intent arg1)
|
||||
{
|
||||
if ( Activity.RESULT_OK == getResultCode() ) {
|
||||
DbgUtils.logf( "SUCCESS!!!" );
|
||||
} else {
|
||||
DbgUtils.logf( "FAILURE!!!" );
|
||||
}
|
||||
DbgUtils.logf( "SMS delivery result: %s",
|
||||
Activity.RESULT_OK == getResultCode()
|
||||
? "SUCCESS" : "FAILURE" );
|
||||
}
|
||||
};
|
||||
registerReceiver( m_receiveReceiver, new IntentFilter(MSG_DELIVERED) );
|
||||
|
||||
m_prefsListener = new OnSharedPreferenceChangeListener() {
|
||||
public void onSharedPreferenceChanged( SharedPreferences sp,
|
||||
String key ) {
|
||||
if ( key.equals( getString( R.string.key_show_sms ) ) ) {
|
||||
s_showToasts = null;
|
||||
} else if ( key.equals( getString( R.string
|
||||
.key_send_data_sms ))) {
|
||||
s_asData = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
SharedPreferences sp
|
||||
= PreferenceManager.getDefaultSharedPreferences( this );
|
||||
sp.registerOnSharedPreferenceChangeListener( m_prefsListener );
|
||||
}
|
||||
|
||||
private boolean sendAsText( byte[] data, String phone )
|
||||
{
|
||||
boolean success = false;
|
||||
try {
|
||||
String[] msgs = breakAndEncode( XwJNI.base64Encode( data ) );
|
||||
success = sendBuffers( msgs, phone );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.loge( ioe );
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private class SMSMsgSink extends MultiMsgSink {
|
||||
|
@ -729,41 +914,68 @@ public class SMSService extends XWService {
|
|||
}
|
||||
|
||||
private class MsgStore {
|
||||
String[] m_msgs;
|
||||
String[] m_msgsText;
|
||||
byte[][] m_msgsData;
|
||||
int m_msgID;
|
||||
int m_haveCount;
|
||||
int m_fullLength;
|
||||
|
||||
public MsgStore( int id, int count )
|
||||
public MsgStore( int id, int count, boolean usingStrings )
|
||||
{
|
||||
m_msgID = id;
|
||||
m_msgs = new String[count];
|
||||
if ( usingStrings ) {
|
||||
m_msgsText = new String[count];
|
||||
} else {
|
||||
m_msgsData = new byte[count][];
|
||||
}
|
||||
m_fullLength = 0;
|
||||
}
|
||||
|
||||
public MsgStore add( int index, String msg )
|
||||
{
|
||||
if ( null == m_msgs[index] ) {
|
||||
if ( null == m_msgsText[index] ) {
|
||||
++m_haveCount;
|
||||
m_fullLength += msg.length();
|
||||
}
|
||||
m_msgs[index] = msg;
|
||||
m_msgsText[index] = msg;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MsgStore add( int index, byte[] msg )
|
||||
{
|
||||
if ( null == m_msgsData[index] ) {
|
||||
++m_haveCount;
|
||||
m_fullLength += msg.length;
|
||||
}
|
||||
m_msgsData[index] = msg;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isComplete()
|
||||
{
|
||||
boolean complete = m_msgs.length == m_haveCount;
|
||||
int count = null != m_msgsText ? m_msgsText.length : m_msgsData.length;
|
||||
boolean complete = count == m_haveCount;
|
||||
return complete;
|
||||
}
|
||||
|
||||
public String message()
|
||||
public String messageText()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(m_fullLength);
|
||||
for ( String msg : m_msgs ) {
|
||||
for ( String msg : m_msgsText ) {
|
||||
sb.append( msg );
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public byte[] messageData()
|
||||
{
|
||||
byte[] result = new byte[m_fullLength];
|
||||
int indx = 0;
|
||||
for ( byte[] msg : m_msgsData ) {
|
||||
System.arraycopy( msg, 0, result, indx, msg.length );
|
||||
indx += msg.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,8 +103,10 @@ public class Utils {
|
|||
{
|
||||
if ( null == s_deviceSupportSMS ) {
|
||||
boolean doesSMS = false;
|
||||
// TEMPORARY: disable SMS on KITKAT
|
||||
if ( 19 > Integer.valueOf( android.os.Build.VERSION.SDK ) ) {
|
||||
// TEMPORARY: disable SMS on KITKAT UNLESS use-text turned on
|
||||
if ( 19 > Integer.valueOf( android.os.Build.VERSION.SDK )
|
||||
|| XWPrefs.getPrefsBoolean( context, R.string.key_send_data_sms,
|
||||
false ) ) {
|
||||
TelephonyManager tm = (TelephonyManager)
|
||||
context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if ( null != tm ) {
|
||||
|
|
Loading…
Reference in a new issue