From 75b976449ffcc4747341601e2288ee4e4101cfe1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 23 Nov 2013 19:02:57 -0800 Subject: [PATCH 01/29] turn SMS back on, and modify to send/receive "data" SMS rather than text. This works between two t-mobile devices without filling the kitkat one's message box. TBD: does it work on CDMA? --- xwords4/android/XWords4/AndroidManifest.xml | 8 + .../org/eehouse/android/xw4/SMSReceiver.java | 33 +-- .../org/eehouse/android/xw4/SMSService.java | 200 ++++++++++++++++-- .../src/org/eehouse/android/xw4/Utils.java | 13 +- 4 files changed, 219 insertions(+), 35 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 429b022a3..963a7c7f8 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -206,6 +206,14 @@ + + + + + + + + diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSReceiver.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSReceiver.java index 32161091d..009ec6246 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSReceiver.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSReceiver.java @@ -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(); + } } } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java index 773291d39..55be5587b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java @@ -55,12 +55,14 @@ public class SMSService extends XWService { private static final String INSTALL_URL = "http://eehouse.org/_/a.py/a "; private static final int MAX_SMS_LEN = 140; // ??? differs by network + private static final boolean s_asData = true; private static final String MSG_SENT = "MSG_SENT"; private static final String MSG_DELIVERED = "MSG_DELIVERED"; 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; @@ -69,6 +71,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"; @@ -118,6 +121,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, @@ -256,6 +268,7 @@ public class SMSService extends XWService { } break; case HANDLE: + case HANDLEDATA: ++m_nReceived; ConnStatusHandler. updateStatusIn( this, null, @@ -263,9 +276,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: @@ -387,9 +405,16 @@ 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 ( s_asData ) { + byte[][] msgs = breakAndEncode( data ); + result = sendBuffers( msgs, phone ); + } else { + String[] msgs = breakAndEncode( XwJNI.base64Encode( data ) ); + result = sendBuffers( msgs, phone ); + } + return result; } private String[] breakAndEncode( String msg ) @@ -417,6 +442,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 = @@ -472,6 +524,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( ":" ); @@ -487,6 +552,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 perPhone = + s_partialMsgs.get( senderPhone ); + if ( null == perPhone ) { + perPhone = new HashMap (); + 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 ) { @@ -503,18 +596,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 ); @@ -592,6 +708,33 @@ public class SMSService extends XWService { return success; } + private boolean sendBuffers( byte[][] fragments, String phone ) + { + 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 ( 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, @@ -728,41 +871,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; + } } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java index 1832493f0..3816f18f1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java @@ -100,14 +100,11 @@ public class Utils { { if ( null == s_deviceSupportSMS ) { boolean doesSMS = false; - // TEMPORARY: disable SMS on KITKAT - if ( 19 > Integer.valueOf( android.os.Build.VERSION.SDK ) ) { - TelephonyManager tm = (TelephonyManager) - context.getSystemService(Context.TELEPHONY_SERVICE); - if ( null != tm ) { - int type = tm.getPhoneType(); - doesSMS = TelephonyManager.PHONE_TYPE_NONE != type; - } + TelephonyManager tm = (TelephonyManager) + context.getSystemService(Context.TELEPHONY_SERVICE); + if ( null != tm ) { + int type = tm.getPhoneType(); + doesSMS = TelephonyManager.PHONE_TYPE_NONE != type; } s_deviceSupportSMS = new Boolean( doesSMS ); } From eab12200af8e0bc1fe687157637c9abc3c4b4ad7 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 26 Nov 2013 07:11:22 -0800 Subject: [PATCH 02/29] add debug preference to determine whether to use sendDataMessage() --- xwords4/android/XWords4/res/values/common_rsrc.xml | 3 +++ xwords4/android/XWords4/res/xml/xwprefs.xml | 5 +++++ .../XWords4/src/org/eehouse/android/xw4/SMSService.java | 6 ++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml index 72aa19275..d824161e9 100644 --- a/xwords4/android/XWords4/res/values/common_rsrc.xml +++ b/xwords4/android/XWords4/res/values/common_rsrc.xml @@ -39,6 +39,7 @@ key_dict_host3 key_logging_on key_show_sms + key_send_data_sms key_init_hintsallowed key_init_nethintsallowed key_init_autojuggle @@ -137,6 +138,8 @@ Enable debug features Menuitems etc. (release builds only) + Send SMS as data + (fails on CDMA phones) Source version id Relay game port diff --git a/xwords4/android/XWords4/res/xml/xwprefs.xml b/xwords4/android/XWords4/res/xml/xwprefs.xml index e6ad3a3e9..2b8a061d5 100644 --- a/xwords4/android/XWords4/res/xml/xwprefs.xml +++ b/xwords4/android/XWords4/res/xml/xwprefs.xml @@ -328,6 +328,11 @@ android:title="Show SMS sends, receives" android:defaultValue="false" /> + Date: Wed, 27 Nov 2013 07:06:14 -0800 Subject: [PATCH 03/29] fix crash: make thumbnail BEFORE closing game --- .../XWords4/src/org/eehouse/android/xw4/GameUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 536c8bc42..ac9d08e6c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -746,14 +746,15 @@ public class GameUtils { // update gi to reflect changes due to messages XwJNI.game_getGi( gamePtr, gi ); - saveGame( context, gamePtr, gi, lock, false ); - summarizeAndClose( context, lock, gamePtr, gi, feedImpl ); if ( draw && XWPrefs.getThumbEnabled( context ) ) { Bitmap bitmap = takeSnapshot( context, gamePtr, gi ); DBUtils.saveThumbnail( context, lock, bitmap ); } + saveGame( context, gamePtr, gi, lock, false ); + summarizeAndClose( context, lock, gamePtr, gi, feedImpl ); + int flags = setFromFeedImpl( feedImpl ); if ( GameSummary.MSG_FLAGS_NONE != flags ) { draw = true; From 605b45f6a069342aa6acc8e93f33a4c00ed40cb7 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 27 Nov 2013 07:06:27 -0800 Subject: [PATCH 04/29] remove logging --- xwords4/android/XWords4/jni/utilwrapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/jni/utilwrapper.c b/xwords4/android/XWords4/jni/utilwrapper.c index 1da05d0c9..d406ab7ba 100644 --- a/xwords4/android/XWords4/jni/utilwrapper.c +++ b/xwords4/android/XWords4/jni/utilwrapper.c @@ -536,7 +536,7 @@ static void and_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr, const CommsAddrRec* newAddr ) { - LOG_FUNC(); + // LOG_FUNC(); } static void From 72341a085a0b654377d3d4b62514f6e3f71f06cf Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 27 Nov 2013 07:08:58 -0800 Subject: [PATCH 05/29] start out sending as data, but on first NPE, the symptom of CDMAness, set the pref to send as text and do that from then on (unless the pref is reset manually) --- xwords4/android/XWords4/res/xml/xwprefs.xml | 2 +- .../org/eehouse/android/xw4/SMSService.java | 42 ++++++++++++++----- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/xwords4/android/XWords4/res/xml/xwprefs.xml b/xwords4/android/XWords4/res/xml/xwprefs.xml index 2b8a061d5..a3a8b9cea 100644 --- a/xwords4/android/XWords4/res/xml/xwprefs.xml +++ b/xwords4/android/XWords4/res/xml/xwprefs.xml @@ -331,7 +331,7 @@ Date: Mon, 2 Dec 2013 07:01:04 -0800 Subject: [PATCH 06/29] register for sms prefs changes so they can take effect immediately --- .../org/eehouse/android/xw4/SMSService.java | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java index adb5e7891..b6a654bb2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java @@ -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; @@ -86,6 +87,7 @@ public class SMSService extends XWService { private BroadcastReceiver m_sentReceiver; private BroadcastReceiver m_receiveReceiver; + private OnSharedPreferenceChangeListener m_prefsListener; private int m_nReceived = 0; private static int s_nSent = 0; @@ -212,10 +214,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 = context.getString( 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 ); @@ -244,6 +244,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(); } @@ -843,14 +850,27 @@ 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 ) From 6402ae56d22298d209844a6b856351673889b6de Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 2 Dec 2013 07:08:43 -0800 Subject: [PATCH 07/29] remove now-redundant response to prefs change --- .../XWords4/src/org/eehouse/android/xw4/PrefsActivity.java | 5 ----- .../XWords4/src/org/eehouse/android/xw4/SMSService.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java index b2a830a11..d30015767 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java @@ -42,13 +42,11 @@ public class PrefsActivity extends PreferenceActivity public static final int EXPLAIN_TITLE = 4; private String m_keyLogging; - private String m_smsToasting; private String m_smsEnable; private String m_downloadPath; private String m_thumbSize; private String m_hideTitle; - @Override protected Dialog onCreateDialog( int id ) { @@ -133,7 +131,6 @@ public class PrefsActivity extends PreferenceActivity setContentView( R.layout.prefs_w_buttons ); m_keyLogging = getString( R.string.key_logging_on ); - m_smsToasting = getString( R.string.key_show_sms ); m_smsEnable = getString( R.string.key_enable_sms ); m_downloadPath = getString( R.string.key_download_path ); m_thumbSize = getString( R.string.key_thumbsize ); @@ -174,8 +171,6 @@ public class PrefsActivity extends PreferenceActivity { if ( key.equals( m_keyLogging ) ) { DbgUtils.logEnable( sp.getBoolean( key, false ) ); - } else if ( key.equals( m_smsToasting ) ) { - SMSService.smsToastEnable( sp.getBoolean( key, false ) ); } else if ( key.equals( m_smsEnable ) ) { if ( sp.getBoolean( key, true ) ) { SMSService.checkForInvites( this ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java index b6a654bb2..55cc147dc 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java @@ -95,11 +95,6 @@ public class SMSService extends XWService { = new HashMap>(); private static HashSet s_sentDied = new HashSet(); - public static void smsToastEnable( boolean newVal ) - { - s_showToasts = newVal; - } - public static void checkForInvites( Context context ) { if ( XWApp.SMSSUPPORTED && Utils.deviceSupportsSMS( context ) ) { From ad60fcc61019a8f134ee6830c36504d20e8a5ac3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 6 Dec 2013 07:37:56 -0800 Subject: [PATCH 08/29] uninstall based on where called from --- xwords4/android/scripts/adb_uninstall.sh | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/xwords4/android/scripts/adb_uninstall.sh b/xwords4/android/scripts/adb_uninstall.sh index a20cbeb92..9f7e63434 100755 --- a/xwords4/android/scripts/adb_uninstall.sh +++ b/xwords4/android/scripts/adb_uninstall.sh @@ -10,6 +10,23 @@ usage() { exit 0 } +if [ ! -e build.xml ]; then + usage "No build.xml; please run me from the top android directory" +fi + +DIRNAME=$(basename $(pwd)) +case $DIRNAME in + XWords4-bt) + PKG=xw4bt + ;; + XWords4) + PKG=xw4 + ;; + *) + usage "running in unexpected directory $DIRNAME" + ;; +esac + while [ $# -ge 1 ]; do case $1 in -n) @@ -24,4 +41,4 @@ done SERIAL="$(adb devices | grep 'device$' | sed -n "$((1+INDEX)) p" | awk '{print $1}')" -adb -s $SERIAL uninstall org.eehouse.android.xw4 +adb -s $SERIAL uninstall org.eehouse.android.${PKG} From fa5649501ebe81162c0daa6a863b25cb5cd7c8d1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 6 Dec 2013 07:39:16 -0800 Subject: [PATCH 09/29] update send success status --- .../src/org/eehouse/android/xw4/BTService.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java index 5bc5988de..efa932caa 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java @@ -43,11 +43,12 @@ import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import junit.framework.Assert; - import org.eehouse.android.xw4.MultiService.MultiEvent; +import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec; +import junit.framework.Assert; + public class BTService extends XWService { private static final long RESEND_TIMEOUT = 5; // seconds @@ -357,6 +358,9 @@ public class BTService extends XWService { DbgUtils.logf( "unexpected msg %d", msg ); break; } + ConnStatusHandler. + updateStatusIn( BTService.this, null, + CommsConnType.COMMS_CONN_BT, true ); } } catch ( java.io.IOException ioe ) { DbgUtils.loge( ioe ); @@ -618,9 +622,15 @@ public class BTService extends XWService { sendInvite( elem ); break; case MESG_SEND: - if ( !doAnyResends( elem.m_addr ) || ! sendMsg( elem ) ) { + boolean success = doAnyResends( elem.m_addr ) + && sendMsg( elem ); + if ( !success ) { addToResends( elem ); } + ConnStatusHandler + .updateStatusOut( BTService.this, null, + CommsConnType.COMMS_CONN_BT, + success ); break; default: Assert.fail(); From 28c0862604e55b45432b92e3d6748b18bca59d77 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 24 Apr 2014 06:31:27 -0700 Subject: [PATCH 10/29] translations come in with ids rather than English strings as keys --- .../org/eehouse/android/xw4/loc/LocUtils.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java index 8b6c793fc..5857e6f93 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java @@ -55,8 +55,6 @@ import org.eehouse.android.xw4.Utils; import org.eehouse.android.xw4.XWPrefs; public class LocUtils { - // Keep this in sync with gen_loc_ids.py and what's used in the menu.xml - // files to mark me-localized strings. private static final int FMT_LEN = 4; private static final String k_LOCALE = "locale"; private static final String k_XLATPROTO = "proto"; @@ -262,7 +260,9 @@ public class LocUtils { Map newXlations = new HashMap( len ); for ( int ii = 0; ii < len; ++ii ) { JSONObject pair = pairs.getJSONObject( ii ); - String key = pair.getString( "en" ); + int id = pair.getInt( "id" ); + String key = context.getString( id ); + Assert.assertNotNull( key ); String txt = pair.getString( "loc" ); newXlations.put( key, txt ); } @@ -420,18 +420,22 @@ public class LocUtils { // formatters. private static String toUpperCase( String str ) { - String[] parts = str.split( "%[\\d]\\$[ds]" ); - StringBuilder sb = new StringBuilder(); - int offset = 0; - for ( String part : parts ) { - sb.append( part.toUpperCase() ); - offset += part.length(); - if ( offset < str.length() ) { - sb.append( str.substring( offset, offset + FMT_LEN ) ); - offset += FMT_LEN; + String result = null; + if ( UPPER_CASE ) { + String[] parts = str.split( "%[\\d]\\$[ds]" ); + StringBuilder sb = new StringBuilder(); + int offset = 0; + for ( String part : parts ) { + sb.append( part.toUpperCase() ); + offset += part.length(); + if ( offset < str.length() ) { + sb.append( str.substring( offset, offset + FMT_LEN ) ); + offset += FMT_LEN; + } } + result = sb.toString(); } - return sb.toString(); + return result; } public static AlertDialog.Builder makeAlertBuilder( Context context ) From 00ecf0d9d0b0772cfac788c181142428de3e2947 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 24 Apr 2014 06:53:13 -0700 Subject: [PATCH 11/29] be consistent using capital-U \U to indicate unicode --- .../android/XWords4/res/values/strings.xml | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 2427314c5..65653a3c8 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -235,7 +235,7 @@ wordlist delete confiration dialog in the case where the wordlist to be deleted is NOT the last in its language. The name of the language is substituted for %1$s. --> - \u0020One game (at least) + \U0020One game (at least) is using it, but there is another %1$s wordlist installed that can replace it. @@ -543,7 +543,7 @@ the above string to encourage the opener of a game missing players to invite, IF the number of missing players is greater than one this text is appended to the above. --> - \u0020(You are expecting multiple + \U0020(You are expecting multiple remote players. Be sure to address your invitation to that many people.) @@ -603,7 +603,7 @@ - \u0020Do you still want to accept + \U0020Do you still want to accept this move? @@ -683,7 +683,7 @@ --> - The robot made this move:\u0020 + The robot made this move:\U0020 exchanged %1$d tiles. Remote player %1$s made this - move:\u0020 + move:\U0020 @@ -738,8 +738,8 @@ indicate that the player is remote. --> %1$s (remote) - \u0020vs.\u0020 + line in a game summary. The \U0020 is a space in xml. --> + \U0020vs.\U0020 Bonus for using all tiles: 50\n @@ -886,7 +886,7 @@ Include in game listing - \u003cNothing\u003E + \U003cNothing\U003E Game language#1 @@ -1197,16 +1197,16 @@ - \u003ca href=\"%1$s\"\u003ETap - here\u003c/a\u003E (or tap the full link below, or, if you already + \U003ca href=\"%1$s\"\U003ETap + here\U003c/a\U003E (or tap the full link below, or, if you already have Crosswords installed, open the attachment) to accept my invitation and join this game. - \u003cbr \\\u003E - \u003cbr \\\u003E + \U003cbr \\\U003E + \U003cbr \\\U003E (full link: %1$s ) @@ -1246,9 +1246,9 @@ substituted for %1$s. --> %1$s message history - Me:\u0020 + Me:\U0020 - Not me:\u0020 + Not me:\U0020 Send @@ -2083,7 +2083,7 @@ You are using the wordlist %1$s but the game host is using %2$s. Would you like to use %3$s too? - \u0020(You will have to download it + \U0020(You will have to download it first.) Reloading game with %1$s @@ -2187,7 +2187,7 @@ can tap a nearby person\'s device to invite him/her to play – if he/she is also using NFC. - \u0020pct. + \U0020pct. If Android gives you a choice of browsers for viewing the wordlists downloads page, DO NOT choose From ed3370133f61818d4e2164aa1f0a2ad171ac8d80 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 24 Apr 2014 07:01:53 -0700 Subject: [PATCH 12/29] use lowercase \u instead; \U isn't acceptable in java --- .../android/XWords4/res/values/strings.xml | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 65653a3c8..6d1efaf14 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -235,7 +235,7 @@ wordlist delete confiration dialog in the case where the wordlist to be deleted is NOT the last in its language. The name of the language is substituted for %1$s. --> - \U0020One game (at least) + \u0020One game (at least) is using it, but there is another %1$s wordlist installed that can replace it. @@ -543,7 +543,7 @@ the above string to encourage the opener of a game missing players to invite, IF the number of missing players is greater than one this text is appended to the above. --> - \U0020(You are expecting multiple + \u0020(You are expecting multiple remote players. Be sure to address your invitation to that many people.) @@ -603,7 +603,7 @@ - \U0020Do you still want to accept + \u0020Do you still want to accept this move? @@ -683,7 +683,7 @@ --> - The robot made this move:\U0020 + The robot made this move:\u0020 exchanged %1$d tiles. Remote player %1$s made this - move:\U0020 + move:\u0020 @@ -738,8 +738,8 @@ indicate that the player is remote. --> %1$s (remote) - \U0020vs.\U0020 + line in a game summary. The \u0020 is a space in xml. --> + \u0020vs.\u0020 Bonus for using all tiles: 50\n @@ -886,7 +886,7 @@ Include in game listing - \U003cNothing\U003E + \u003CNothing\u003E Game language#1 @@ -1197,16 +1197,16 @@ - \U003ca href=\"%1$s\"\U003ETap - here\U003c/a\U003E (or tap the full link below, or, if you already + \u003Ca href=\"%1$s\"\u003ETap + here\u003C/a\u003E (or tap the full link below, or, if you already have Crosswords installed, open the attachment) to accept my invitation and join this game. - \U003cbr \\\U003E - \U003cbr \\\U003E + \u003Cbr \\\u003E + \u003Cbr \\\u003E (full link: %1$s ) @@ -1246,9 +1246,9 @@ substituted for %1$s. --> %1$s message history - Me:\U0020 + Me:\u0020 - Not me:\U0020 + Not me:\u0020 Send @@ -2083,7 +2083,7 @@ You are using the wordlist %1$s but the game host is using %2$s. Would you like to use %3$s too? - \U0020(You will have to download it + \u0020(You will have to download it first.) Reloading game with %1$s @@ -2187,7 +2187,7 @@ can tap a nearby person\'s device to invite him/her to play – if he/she is also using NFC. - \U0020pct. + \u0020pct. If Android gives you a choice of browsers for viewing the wordlists downloads page, DO NOT choose From 3b4c672492735aadfd99214db8da03fe4fce5ab2 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 24 Apr 2014 07:08:57 -0700 Subject: [PATCH 13/29] toward substituting what getString (or the resource compiler) normally does. --- .../XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java index 5857e6f93..e32146912 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java @@ -264,6 +264,7 @@ public class LocUtils { String key = context.getString( id ); Assert.assertNotNull( key ); String txt = pair.getString( "loc" ); + txt = replaceEscaped( txt ); newXlations.put( key, txt ); } @@ -438,6 +439,13 @@ public class LocUtils { return result; } + private static String replaceEscaped( String txt ) + { + String result = txt.replaceAll("\\n", "\n"); + // DbgUtils.logf( "replaceEscaped(<<%s>>) -> <<%s>>", txt, result ); + return result; + } + public static AlertDialog.Builder makeAlertBuilder( Context context ) { return new AlertBuilder( context ); From 28e81e3a74a804d2507dca125b624d7be5da581a Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 24 Apr 2014 07:56:27 -0700 Subject: [PATCH 14/29] translate preferences -- finally. --- .../eehouse/android/xw4/PrefsActivity.java | 12 +++++- .../org/eehouse/android/xw4/loc/LocUtils.java | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java index d056f70d9..8d00b7593 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/PrefsActivity.java @@ -25,6 +25,8 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceActivity; +import org.eehouse.android.xw4.loc.LocUtils; + public class PrefsActivity extends PreferenceActivity { private PrefsDelegate m_dlgt; @@ -38,12 +40,18 @@ public class PrefsActivity extends PreferenceActivity { @Override protected void onCreate( Bundle savedInstanceState ) { - super.onCreate( savedInstanceState ); - m_dlgt = new PrefsDelegate( this, savedInstanceState ); + super.onCreate( savedInstanceState ); m_dlgt.init( savedInstanceState ); } + @Override + protected void onStart() + { + LocUtils.xlatePreferences( this ); + super.onStart(); + } + @Override protected void onResume() { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java index e32146912..485fa0659 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java @@ -36,6 +36,9 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.Spinner; import android.widget.TextView; +import android.preference.Preference; +import android.preference.PreferenceGroup; +import android.preference.PreferenceActivity; import java.util.HashMap; import java.util.HashSet; @@ -119,6 +122,11 @@ public class LocUtils { xlateView( activity, Utils.getContentView( activity ) ); } + public static void xlatePreferences( PreferenceActivity activity ) + { + xlatePreferences( activity, activity.getPreferenceScreen(), 0 ); + } + public static void xlateView( Context context, View view ) { DbgUtils.logf( "xlateView() top level" ); @@ -130,6 +138,15 @@ public class LocUtils { xlateMenu( activity, menu, 0 ); } + private static String xlateString( Context context, CharSequence str ) + { + String result = null; + if ( null != str ) { + result = xlateString( context, str.toString() ); + } + return result; + } + public static String xlateString( Context context, String str ) { if ( LocIDs.getS_MAP( context ).containsKey( str ) ) { @@ -416,6 +433,30 @@ public class LocUtils { } } + public static void xlatePreferences( Context context, Preference pref, + int depth ) + { + // DbgUtils.logf( "xlatePreferences(depth=%d, view=%s, canRecurse=%b)", depth, + // pref.getClass().getName(), pref instanceof PreferenceGroup ); + + String str = xlateString( context, pref.getSummary() ); + if ( null != str ) { + pref.setSummary( str ); + } + str = xlateString( context, pref.getTitle() ); + if ( null != str ) { + pref.setTitle( str ); + } + + if ( pref instanceof PreferenceGroup ) { + PreferenceGroup group = (PreferenceGroup)pref; + int count = group.getPreferenceCount(); + for ( int ii = 0; ii < count; ++ii ) { + xlatePreferences( context, group.getPreference(ii), 1 + depth ); + } + } + } + // This is for testing, but the ability to pull the formatters will be // critical for validating local transations of strings containing // formatters. From 808e342cd4d74057a23ae63d009f3f3e4064442e Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 24 Apr 2014 19:58:18 -0700 Subject: [PATCH 15/29] clean up --- .../eehouse/android/xw4/BoardActivity.java | 51 ------------------- .../org/eehouse/android/xw4/ChatDelegate.java | 2 +- 2 files changed, 1 insertion(+), 52 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 6ba2dc2c2..396d5b90c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -21,68 +21,17 @@ package org.eehouse.android.xw4; import android.app.Activity; -import android.app.AlertDialog; import android.app.Dialog; -import android.app.ProgressDialog; - -import android.content.DialogInterface.OnDismissListener; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.res.Configuration; - -import android.graphics.Bitmap; -import android.graphics.Rect; - import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.text.TextUtils; - import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.View; -import android.view.Window; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.HashSet; -import java.util.concurrent.Semaphore; -import junit.framework.Assert; - -import org.eehouse.android.xw4.DlgDelegate.Action; -import org.eehouse.android.xw4.jni.*; -import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; -import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; -import org.eehouse.android.xw4.jni.JNIThread.*; - public class BoardActivity extends Activity { - public static final String INTENT_KEY_CHAT = "chat"; - - private static final int CHAT_REQUEST = 1; - private static final int BT_INVITE_RESULT = 2; - private static final int SMS_INVITE_RESULT = 3; - - private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins - - private static final String DLG_TITLE = "DLG_TITLE"; - private static final String DLG_TITLESTR = "DLG_TITLESTR"; - private static final String DLG_BYTES = "DLG_BYTES"; - private static final String ROOM = "ROOM"; - private static final String PWDNAME = "PWDNAME"; - private static final String TOASTSTR = "TOASTSTR"; - private static final String WORDS = "WORDS"; - private static final String GETDICT = "GETDICT"; - private BoardDelegate m_dlgt; @Override diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ChatDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ChatDelegate.java index aa07e2e8f..d8a54fc37 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ChatDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ChatDelegate.java @@ -104,7 +104,7 @@ public class ChatDelegate extends DelegateBase DBUtils.appendChatHistory( m_activity, m_rowid, text, true ); Intent result = new Intent(); - result.putExtra( BoardActivity.INTENT_KEY_CHAT, text ); + result.putExtra( BoardDelegate.INTENT_KEY_CHAT, text ); m_activity.setResult( Activity.RESULT_OK, result ); } m_activity.finish(); From 6e011ab36ffe2c13474cb4421c2cfc44cd3c277e Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 24 Apr 2014 19:59:20 -0700 Subject: [PATCH 16/29] allow board screen to be "upside down" on OS version new enough to support it (and make SMS Invite Activity go any way, just like everything else) --- xwords4/android/XWords4/AndroidManifest.xml | 2 +- .../XWords4/src/org/eehouse/android/xw4/BoardActivity.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index c48735d5a..9b0de0628 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -97,7 +97,7 @@ Date: Fri, 25 Apr 2014 07:41:58 -0700 Subject: [PATCH 17/29] fix crash when there's a "'" in model name --- xwords4/relay/dbmgr.cpp | 114 +++++++++++++++++++++++++++++++++------- xwords4/relay/dbmgr.h | 4 +- 2 files changed, 98 insertions(+), 20 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 212454a65..4ec559c9c 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -365,6 +365,18 @@ DBMgr::AllDevsAckd( const char* const connName ) return full; } +static void +getPtrs( vector& paramValues, const char* base, vector& offsets ) +{ + size_t ii; + for ( ii = 0; ii < offsets.size(); ++ii ) { + const char* ptr = offsets[ii] + base; + paramValues.push_back( ptr ); + logf( XW_LOGINFO, "%s: str[%d] points at: %s", __func__, ii, + paramValues[ii] ); + } +} + // Return DevIDRelay for device, adding it to devices table IFF it's not // already there. DevIDRelay @@ -397,13 +409,31 @@ DBMgr::RegisterDevice( const DevID* host, int clientVersion, } while ( DEVID_NONE == devID ); StrWPF query; + StrWPF paramBuf; + vector paramIndices; query.catf( "INSERT INTO " DEVICES_TABLE " (id, devTypes[1]," " devids[1], clntVers, versdesc, model, osvers)" - " VALUES( %d, %d, '%s', %d, '%s', '%s', '%s' )", - devID, host->m_devIDType, devidStr, clientVersion, - desc, model, osVers ); - logf( XW_LOGINFO, "%s: %s", __func__, query.c_str() ); - success = execSql( query ); + " VALUES( $1, $2, $3, $4, $5, $6, $7 )" ); + + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%d%c", devID, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%d%c", host->m_devIDType, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", devidStr, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%d%c", clientVersion, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", desc, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", model, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", osVers, '\0' ); + + vector paramValues; + getPtrs( paramValues, paramBuf.c_str(), paramIndices ); + + success = execParams( query, paramValues ); } } return devID; @@ -420,15 +450,24 @@ DBMgr::ReregisterDevice( DevIDRelay relayID, const DevID* host, const char* const desc, int clientVersion, const char* const model, const char* const osVers ) { - // First update the existing StrWPF query; - query.catf( "UPDATE " DEVICES_TABLE " SET " - "devTypes = array_prepend( %d, devTypes), " - "devids = array_prepend('%s', devids), ", - host->m_devIDType, host->m_devIDString.c_str() ); + StrWPF paramBuf; + vector paramIndices; - formatUpdate( query, true, desc, clientVersion, model, osVers, relayID ); - execSql( query ); + query.catf( "UPDATE " DEVICES_TABLE " SET " + "devTypes = array_prepend( $1, devTypes), " + "devids = array_prepend($2, devids), " ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%d%c", host->m_devIDType, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", host->m_devIDString.c_str(), '\0' ); + + formatUpdate( query, paramBuf, paramIndices, true, desc, clientVersion, + model, osVers, relayID ); + + vector paramValues; + getPtrs( paramValues, paramBuf.c_str(), paramIndices ); + execParams( query, paramValues ); } // Return true if the relayID exists in the DB already @@ -447,8 +486,15 @@ DBMgr::UpdateDevice( DevIDRelay relayID, const char* const desc, if ( exists ) { StrWPF query; query.catf( "UPDATE " DEVICES_TABLE " SET " ); - formatUpdate( query, false, desc, clientVersion, model, osVers, relayID ); - execSql( query ); + + StrWPF paramBuf; + vector paramIndices; + + formatUpdate( query, paramBuf, paramIndices, false, desc, + clientVersion, model, osVers, relayID ); + vector paramValues; + getPtrs( paramValues, paramBuf.c_str(), paramIndices ); + execParams( query, paramValues ); } return exists; } @@ -460,7 +506,8 @@ DBMgr::UpdateDevice( DevIDRelay relayID ) } void -DBMgr::formatUpdate( StrWPF& query, bool append, const char* const desc, +DBMgr::formatUpdate( StrWPF& query, StrWPF& paramBuf, vector& paramIndices, + bool append, const char* const desc, int clientVersion, const char* const model, const char* const osVers, DevIDRelay relayID ) { @@ -470,16 +517,29 @@ DBMgr::formatUpdate( StrWPF& query, bool append, const char* const desc, query.catf( "mtimes[1]='now'" ); } + int count = paramIndices.size(); if ( NULL != desc && '\0' != desc[0] ) { - query.catf( ", clntVers=%d, versDesc='%s'", clientVersion, desc ); + query.catf( ", clntVers=$%d, versDesc=$%d", 1 + count, 2 + count ); + count += 2; + + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%d%c", clientVersion, '\0' ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", desc, '\0' ); } if ( NULL != model && '\0' != model[0] ) { - query.catf( ", model='%s'", model ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", model, '\0' ); + query.catf( ", model=$%d", ++count ); } if ( NULL != osVers && '\0' != osVers[0] ) { - query.catf( ", osvers='%s'", osVers ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%s%c", osVers, '\0' ); + query.catf( ", osvers=$%d", ++count ); } - query.catf( " WHERE id = %d", relayID ); + paramIndices.push_back( paramBuf.size() ); + paramBuf.catf( "%d%c", relayID, '\0' ); + query.catf( " WHERE id = $%d", ++count ); } HostID @@ -831,6 +891,22 @@ DBMgr::execSql( const char* const query ) return ok; } +bool +DBMgr::execParams( const string& query, vector paramValues ) +{ + PGresult* result = PQexecParams( getThreadConn(), query.c_str(), + paramValues.size(), NULL, + ¶mValues[0], + NULL, NULL, 0 ); + bool success = PGRES_COMMAND_OK == PQresultStatus( result ); + if ( !success ) { + logf( XW_LOGERROR, "PQexecParams(%s)=>%s;%s", query.c_str(), + PQresStatus(PQresultStatus(result)), + PQresultErrorMessage(result) ); + } + return success; +} + void DBMgr::readArray( const char* const connName, const char* column, int arr[] ) /* len 4 */ { diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h index 6e5bcbf0e..c4fa0b691 100644 --- a/xwords4/relay/dbmgr.h +++ b/xwords4/relay/dbmgr.h @@ -149,6 +149,7 @@ class DBMgr { DBMgr(); bool execSql( const string& query ); bool execSql( const char* const query ); /* no-results query */ + bool execParams( const string& query, vector params ); void readArray( const char* const connName, const char* column, int arr[] ); DevIDRelay getDevID( const char* connName, int hid ); DevIDRelay getDevID( const DevID* devID ); @@ -160,7 +161,8 @@ class DBMgr { bool nullConnnameOK ); int CountStoredMessages( const char* const connName, int hid ); bool UpdateDevice( DevIDRelay relayID ); - void formatUpdate( StrWPF& query, bool append, const char* const desc, + void formatUpdate( StrWPF& query, StrWPF& prmBuf, vector& prmIndices, + bool append, const char* const desc, int clientVersion, const char* const model, const char* const osVers, DevIDRelay relayID ); From 2f3a7f4bd297aeb34f0d13baa9c25a15a0952983 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 25 Apr 2014 07:46:22 -0700 Subject: [PATCH 18/29] allow board screen to be "upside down" on OS version new enough to support it (and make SMS Invite Activity go any way, just like everything else) Conflicts: xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java --- xwords4/android/XWords4/AndroidManifest.xml | 2 +- .../XWords4/src/org/eehouse/android/xw4/BoardActivity.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 6d239b62f..c0835cdbf 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -97,7 +97,7 @@ Date: Fri, 25 Apr 2014 07:46:33 -0700 Subject: [PATCH 19/29] another change --- xwords4/android/XWords4/res/raw/changes | 1 + 1 file changed, 1 insertion(+) diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index 9ba430c0d..e56982a84 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -9,6 +9,7 @@

New with this release

    +
  • Display board in upside-down portrait orientation too (where OS version allows)
  • Bug fix (obscure): don't mangle unicode model names
From 19573af533c8ac16f7021e1e9dd04b4336091b1d Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 25 Apr 2014 18:45:51 -0700 Subject: [PATCH 20/29] a more elegant way of building up params to pass to PQexecParams --- xwords4/relay/Makefile | 1 + xwords4/relay/dbmgr.cpp | 272 ++++++++++++++----------------------- xwords4/relay/dbmgr.h | 6 +- xwords4/relay/querybld.cpp | 78 +++++++++++ xwords4/relay/querybld.h | 47 +++++++ xwords4/relay/strwpf.cpp | 45 +++--- xwords4/relay/strwpf.h | 8 +- 7 files changed, 263 insertions(+), 194 deletions(-) create mode 100644 xwords4/relay/querybld.cpp create mode 100644 xwords4/relay/querybld.h diff --git a/xwords4/relay/Makefile b/xwords4/relay/Makefile index b85aeb906..b762735f0 100644 --- a/xwords4/relay/Makefile +++ b/xwords4/relay/Makefile @@ -38,6 +38,7 @@ SRC = \ udpager.cpp \ udpqueue.cpp \ xwrelay.cpp \ + querybld.cpp \ # STATIC ?= -static GITINFO = gitversion.txt diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 4ec559c9c..179b01dad 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -43,11 +43,8 @@ static DBMgr* s_instance = NULL; -#define DELIM "\1" #define MAX_NUM_PLAYERS 4 -static void formatParams( char* paramValues[], int nParams, const char* fmt, - char* buf, int bufLen, ... ); static int here_less_seed( const char* seeds, int perDeviceSum, unsigned short seed ); static void destr_function( void* conn ); @@ -105,20 +102,21 @@ DBMgr::AddNew( const char* cookie, const char* connName, CookieID cid, if ( !cookie ) cookie = ""; if ( !connName ) connName = ""; - const char* command = "INSERT INTO " GAMES_TABLE - " (cid, room, connName, nTotal, lang, pub)" - " VALUES( $1, $2, $3, $4, $5, $6 )"; - int nParams = 6; - char* paramValues[nParams]; - char buf[512]; - formatParams( paramValues, nParams, - "%d"DELIM"%s"DELIM"%s"DELIM"%d"DELIM"%d"DELIM"%s", - buf, sizeof(buf), cid, cookie, connName, nPlayersT, - langCode, isPublic?"TRUE":"FALSE" ); + QueryBuilder qb; + qb.appendQueryf( "INSERT INTO " GAMES_TABLE + " (cid, room, connName, nTotal, lang, pub)" + " VALUES( $$, $$, $$, $$, $$, $$ )" ) + .appendParam(cid) + .appendParam(cookie) + .appendParam(connName) + .appendParam(nPlayersT) + .appendParam(langCode) + .appendParam(isPublic?"TRUE":"FALSE" ) + .finish(); - PGresult* result = PQexecParams( getThreadConn(), command, - nParams, NULL, - paramValues, + PGresult* result = PQexecParams( getThreadConn(), qb.c_str(), + qb.paramCount(), NULL, + qb.paramValues(), NULL, NULL, 0 ); if ( PGRES_COMMAND_OK != PQresultStatus(result) ) { logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)), @@ -271,28 +269,27 @@ DBMgr::SeenSeed( const char* cookie, unsigned short seed, char* connNameBuf, int bufLen, int* nPlayersHP, CookieID* cid ) { - int nParams = 5; - char* paramValues[nParams]; - char buf[512]; - formatParams( paramValues, nParams, - "%s"DELIM"%d"DELIM"%d"DELIM"%d"DELIM"%s", buf, sizeof(buf), - cookie, langCode, nPlayersT, seed, - wantsPublic?"TRUE":"FALSE" ); + QueryBuilder qb; + qb.appendQueryf( "SELECT cid, connName, seeds, sum_array(nPerDevice) FROM " + GAMES_TABLE + " WHERE NOT dead" + " AND room ILIKE $$" + " AND lang = $$" + " AND nTotal = $$" + " AND $$ = ANY(seeds)" + " AND $$ = pub" + " ORDER BY ctime DESC" + " LIMIT 1") + .appendParam(cookie) + .appendParam(langCode) + .appendParam(nPlayersT) + .appendParam(seed) + .appendParam(wantsPublic?"TRUE":"FALSE" ) + .finish(); - const char* cmd = "SELECT cid, connName, seeds, sum_array(nPerDevice) FROM " - GAMES_TABLE - " WHERE NOT dead" - " AND room ILIKE $1" - " AND lang = $2" - " AND nTotal = $3" - " AND $4 = ANY(seeds)" - " AND $5 = pub" - " ORDER BY ctime DESC" - " LIMIT 1"; - - PGresult* result = PQexecParams( getThreadConn(), cmd, - nParams, NULL, - paramValues, + PGresult* result = PQexecParams( getThreadConn(), qb.c_str(), + qb.paramCount(), NULL, + qb.paramValues(), NULL, NULL, 0 ); bool found = 1 == PQntuples( result ); if ( found ) { @@ -312,31 +309,28 @@ DBMgr::FindOpen( const char* cookie, int lang, int nPlayersT, int nPlayersH, bool wantsPublic, char* connNameBuf, int bufLen, int* nPlayersHP ) { - CookieID cid = 0; + QueryBuilder qb; + qb.appendQueryf("SELECT cid, connName, sum_array(nPerDevice) FROM " + GAMES_TABLE + " WHERE NOT dead" + " AND room ILIKE $$" + " AND lang = $$" + " AND nTotal = $$" + " AND $$ <= nTotal-sum_array(nPerDevice)" + " AND $$ = pub" + " LIMIT 1") + .appendParam(cookie) + .appendParam(lang) + .appendParam(nPlayersT) + .appendParam(nPlayersH) + .appendParam(wantsPublic?"TRUE":"FALSE" ) + .finish(); - int nParams = 5; - char* paramValues[nParams]; - char buf[512]; - formatParams( paramValues, nParams, - "%s"DELIM"%d"DELIM"%d"DELIM"%d"DELIM"%s", buf, sizeof(buf), - cookie, lang, nPlayersT, nPlayersH, wantsPublic?"TRUE":"FALSE" ); - - /* NOTE: ILIKE, for case-insensitive comparison, is a postgres extension - to SQL. */ - const char* cmd = "SELECT cid, connName, sum_array(nPerDevice) FROM " - GAMES_TABLE - " WHERE NOT dead" - " AND room ILIKE $1" - " AND lang = $2" - " AND nTotal = $3" - " AND $4 <= nTotal-sum_array(nPerDevice)" - " AND $5 = pub" - " LIMIT 1"; - - PGresult* result = PQexecParams( getThreadConn(), cmd, - nParams, NULL, - paramValues, + PGresult* result = PQexecParams( getThreadConn(), qb.c_str(), + qb.paramCount(), NULL, + qb.paramValues(), NULL, NULL, 0 ); + CookieID cid = 0; if ( 1 == PQntuples( result ) ) { cid = atoi( PQgetvalue( result, 0, 0 ) ); snprintf( connNameBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) ); @@ -365,18 +359,6 @@ DBMgr::AllDevsAckd( const char* const connName ) return full; } -static void -getPtrs( vector& paramValues, const char* base, vector& offsets ) -{ - size_t ii; - for ( ii = 0; ii < offsets.size(); ++ii ) { - const char* ptr = offsets[ii] + base; - paramValues.push_back( ptr ); - logf( XW_LOGINFO, "%s: str[%d] points at: %s", __func__, ii, - paramValues[ii] ); - } -} - // Return DevIDRelay for device, adding it to devices table IFF it's not // already there. DevIDRelay @@ -408,32 +390,21 @@ DBMgr::RegisterDevice( const DevID* host, int clientVersion, devID = (DevIDRelay)random(); } while ( DEVID_NONE == devID ); - StrWPF query; - StrWPF paramBuf; - vector paramIndices; - query.catf( "INSERT INTO " DEVICES_TABLE " (id, devTypes[1]," - " devids[1], clntVers, versdesc, model, osvers)" - " VALUES( $1, $2, $3, $4, $5, $6, $7 )" ); + QueryBuilder qb; + qb.appendQueryf( "INSERT INTO " DEVICES_TABLE " (id, devTypes[1]," + " devids[1], clntVers, versdesc, model, osvers)" + " VALUES($$, $$, $$, $$, $$, $$, $$)" ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%d%c", devID, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%d%c", host->m_devIDType, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", devidStr, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%d%c", clientVersion, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", desc, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", model, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", osVers, '\0' ); + qb.appendParam( devID ) + .appendParam( host->m_devIDType ) + .appendParam( devidStr ) + .appendParam( clientVersion ) + .appendParam( desc ) + .appendParam( model ) + .appendParam( osVers ) + .finish(); - vector paramValues; - getPtrs( paramValues, paramBuf.c_str(), paramIndices ); - - success = execParams( query, paramValues ); + success = execParams( qb ); } } return devID; @@ -450,24 +421,17 @@ DBMgr::ReregisterDevice( DevIDRelay relayID, const DevID* host, const char* const desc, int clientVersion, const char* const model, const char* const osVers ) { - StrWPF query; - StrWPF paramBuf; - vector paramIndices; + QueryBuilder qb; + qb.appendQueryf( "UPDATE " DEVICES_TABLE " SET " + "devTypes = array_prepend($$, devTypes), " + "devids = array_prepend($$, devids), " ) - query.catf( "UPDATE " DEVICES_TABLE " SET " - "devTypes = array_prepend( $1, devTypes), " - "devids = array_prepend($2, devids), " ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%d%c", host->m_devIDType, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", host->m_devIDString.c_str(), '\0' ); + .appendParam( host->m_devIDType ) + .appendParam( host->m_devIDString.c_str() ); - formatUpdate( query, paramBuf, paramIndices, true, desc, clientVersion, - model, osVers, relayID ); - - vector paramValues; - getPtrs( paramValues, paramBuf.c_str(), paramIndices ); - execParams( query, paramValues ); + formatUpdate( qb, true, desc, clientVersion, model, osVers, relayID ); + qb.finish(); + execParams( qb ); } // Return true if the relayID exists in the DB already @@ -484,17 +448,11 @@ DBMgr::UpdateDevice( DevIDRelay relayID, const char* const desc, } if ( exists ) { - StrWPF query; - query.catf( "UPDATE " DEVICES_TABLE " SET " ); - - StrWPF paramBuf; - vector paramIndices; - - formatUpdate( query, paramBuf, paramIndices, false, desc, - clientVersion, model, osVers, relayID ); - vector paramValues; - getPtrs( paramValues, paramBuf.c_str(), paramIndices ); - execParams( query, paramValues ); + QueryBuilder qb; + qb.appendQueryf( "UPDATE " DEVICES_TABLE " SET " ); + formatUpdate( qb, false, desc, clientVersion, model, osVers, relayID ); + qb.finish(); + execParams( qb ); } return exists; } @@ -506,40 +464,33 @@ DBMgr::UpdateDevice( DevIDRelay relayID ) } void -DBMgr::formatUpdate( StrWPF& query, StrWPF& paramBuf, vector& paramIndices, +DBMgr::formatUpdate( QueryBuilder& qb, bool append, const char* const desc, int clientVersion, const char* const model, const char* const osVers, DevIDRelay relayID ) { if ( append ) { - query.catf( "mtimes=array_prepend('now', mtimes)" ); // FIXME: too many + qb.appendQueryf( "mtimes=array_prepend('now', mtimes)" ); // FIXME: too many } else { - query.catf( "mtimes[1]='now'" ); + qb.appendQueryf( "mtimes[1]='now'" ); } - int count = paramIndices.size(); if ( NULL != desc && '\0' != desc[0] ) { - query.catf( ", clntVers=$%d, versDesc=$%d", 1 + count, 2 + count ); - count += 2; - - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%d%c", clientVersion, '\0' ); - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", desc, '\0' ); + qb.appendQueryf( ", clntVers=$$" ) + .appendParam( clientVersion ) + .appendQueryf( ", versDesc=$$" ) + .appendParam( desc ); } if ( NULL != model && '\0' != model[0] ) { - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", model, '\0' ); - query.catf( ", model=$%d", ++count ); + qb.appendQueryf( ", model=$$" ) + .appendParam( model ); } if ( NULL != osVers && '\0' != osVers[0] ) { - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%s%c", osVers, '\0' ); - query.catf( ", osvers=$%d", ++count ); + qb.appendQueryf( ", osvers=$$" ) + .appendParam( osVers ); } - paramIndices.push_back( paramBuf.size() ); - paramBuf.catf( "%d%c", relayID, '\0' ); - query.catf( " WHERE id = $%d", ++count ); + qb.appendQueryf( " WHERE id = $$" ) + .appendParam( relayID ); } HostID @@ -892,15 +843,15 @@ DBMgr::execSql( const char* const query ) } bool -DBMgr::execParams( const string& query, vector paramValues ) +DBMgr::execParams( QueryBuilder& qb ) { - PGresult* result = PQexecParams( getThreadConn(), query.c_str(), - paramValues.size(), NULL, - ¶mValues[0], + PGresult* result = PQexecParams( getThreadConn(), qb.c_str(), + qb.paramCount(), NULL, + qb.paramValues(), NULL, NULL, 0 ); bool success = PGRES_COMMAND_OK == PQresultStatus( result ); if ( !success ) { - logf( XW_LOGERROR, "PQexecParams(%s)=>%s;%s", query.c_str(), + logf( XW_LOGERROR, "PQexecParams(%s)=>%s;%s", qb.c_str(), PQresStatus(PQresultStatus(result)), PQresultErrorMessage(result) ); } @@ -1331,31 +1282,6 @@ void DBMgr::clearHasNoMessages( DevIDRelay devid ) assert( !hasNoMessages( devid ) ); } -static void -formatParams( char* paramValues[], int nParams, const char* fmt, char* buf, - int bufLen, ... ) -{ - va_list ap; - va_start( ap, bufLen ); - - int len = vsnprintf( buf, bufLen, fmt, ap ); - assert( buf[len] == '\0' ); - - int pnum; - char* ptr = buf; - for ( pnum = 0; pnum < nParams; ++pnum ) { - paramValues[pnum] = ptr; - for ( ; *ptr != '\0' && *ptr != DELIM[0]; ++ptr ) { - // do nothing - assert( ptr < &buf[bufLen] ); - } - // we've found an end - *ptr = '\0'; - ++ptr; - } - va_end(ap); -} - static int here_less_seed( const char* seeds, int sumPerDevice, unsigned short seed ) { diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h index c4fa0b691..e0fafcaad 100644 --- a/xwords4/relay/dbmgr.h +++ b/xwords4/relay/dbmgr.h @@ -32,6 +32,7 @@ #include "xwrelay_priv.h" #include "devid.h" #include "strwpf.h" +#include "querybld.h" using namespace std; @@ -149,7 +150,7 @@ class DBMgr { DBMgr(); bool execSql( const string& query ); bool execSql( const char* const query ); /* no-results query */ - bool execParams( const string& query, vector params ); + bool execParams( QueryBuilder& qb ); void readArray( const char* const connName, const char* column, int arr[] ); DevIDRelay getDevID( const char* connName, int hid ); DevIDRelay getDevID( const DevID* devID ); @@ -161,8 +162,7 @@ class DBMgr { bool nullConnnameOK ); int CountStoredMessages( const char* const connName, int hid ); bool UpdateDevice( DevIDRelay relayID ); - void formatUpdate( StrWPF& query, StrWPF& prmBuf, vector& prmIndices, - bool append, const char* const desc, + void formatUpdate( QueryBuilder& qb, bool append, const char* const desc, int clientVersion, const char* const model, const char* const osVers, DevIDRelay relayID ); diff --git a/xwords4/relay/querybld.cpp b/xwords4/relay/querybld.cpp new file mode 100644 index 000000000..6334d1f9f --- /dev/null +++ b/xwords4/relay/querybld.cpp @@ -0,0 +1,78 @@ +/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/* + * Copyright 2014 by Eric House (xwords@eehouse.org). All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option. + * + * 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. + */ +#include + +#include "querybld.h" +#include "xwrelay_priv.h" + +QueryBuilder& +QueryBuilder::appendQueryf( const char* fmt, ... ) +{ + bool done; + do { + va_list ap; + va_start( ap, fmt ); + done = m_query.catf( fmt, ap ); + va_end( ap ); + } while ( !done ); + return *this; +} + +QueryBuilder& +QueryBuilder::appendParam( const char* value ) +{ + m_paramIndices.push_back( m_paramBuf.size() ); + m_paramBuf.catf( "%s%c", value, '\0' ); + return *this; +} + +QueryBuilder& +QueryBuilder::appendParam( int value ) +{ + m_paramIndices.push_back( m_paramBuf.size() ); + m_paramBuf.catf( "%d%c", value, '\0' ); + return *this; +} + +/* When done adding params, some of which contain $$, turn these into an order + * progression of $1, $2 .. $9. Note assumption that we don't go above 9 since + */ +void +QueryBuilder::finish() +{ + assert( 0 == m_paramValues.size() ); + + size_t ii; + const char* base = m_paramBuf.c_str(); + for ( ii = 0; ii < m_paramIndices.size(); ++ii ) { + const char* ptr = m_paramIndices[ii] + base; + m_paramValues.push_back( ptr ); + } + + for ( size_t count = 0; ; ++count ) { + const char* str = m_query.c_str(); + const char* ptr = strstr( str, "$$" ); + if ( !ptr ) { + assert( count == m_paramIndices.size() ); + break; + } + assert( count < 9 ); + m_query[1 + ptr - str] = '1' + count; + } +} diff --git a/xwords4/relay/querybld.h b/xwords4/relay/querybld.h new file mode 100644 index 000000000..1def83271 --- /dev/null +++ b/xwords4/relay/querybld.h @@ -0,0 +1,47 @@ +/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/* + * Copyright 2014 by Eric House (xwords@eehouse.org). All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option. + * + * 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. + */ + +#ifndef _QUERYBLD_H_ +#define _QUERYBLD_H_ + +#include + +#include "strwpf.h" + +using namespace std; + +class QueryBuilder { + + public: + QueryBuilder& appendQueryf( const char* fmt, ... ); + QueryBuilder& appendParam( const char* value ); + QueryBuilder& appendParam( int value ); + void finish(); + int paramCount() const { return m_paramValues.size(); } + const char* const* paramValues() const { return &m_paramValues[0]; } + const char* const c_str() const { return m_query.c_str(); } + + private: + StrWPF m_query; + StrWPF m_paramBuf; + vector m_paramIndices; + vector m_paramValues; +}; + +#endif diff --git a/xwords4/relay/strwpf.cpp b/xwords4/relay/strwpf.cpp index 9a8ab0a83..d6085755a 100644 --- a/xwords4/relay/strwpf.cpp +++ b/xwords4/relay/strwpf.cpp @@ -27,26 +27,37 @@ /* From stack overflow: snprintf with an expanding buffer. */ +bool +StrWPF::catf( const char* fmt, va_list ap ) +{ + bool success = false; + const int origsiz = size(); + resize( origsiz + m_addsiz ); + + int len = vsnprintf( (char*)c_str() + origsiz, m_addsiz, fmt, ap ); + + if ( len >= m_addsiz ) { // needs more space + m_addsiz = len + 1; + resize( origsiz ); + } else if ( -1 == len ) { + assert(0); // should be impossible + } else { + resize( origsiz + len ); + m_addsiz = 100; + success = true; + } + + return success; +} + void StrWPF::catf( const char* fmt, ... ) { - const int origsiz = size(); - int addsiz = 100; - va_list ap; - for ( ; ; ) { - resize( origsiz + addsiz ); - + bool done; + do { + va_list ap; va_start( ap, fmt ); - int len = vsnprintf( (char *)c_str() + origsiz, addsiz, fmt, ap ); + done = catf( fmt, ap ); va_end( ap ); - - if ( len >= addsiz ) { // needs more space - addsiz = len + 1; - } else if ( -1 == len ) { - assert(0); // should be impossible - } else { - resize( origsiz + len ); - break; - } - } + } while ( !done ); } diff --git a/xwords4/relay/strwpf.h b/xwords4/relay/strwpf.h index 950a9e817..e3973d2dc 100644 --- a/xwords4/relay/strwpf.h +++ b/xwords4/relay/strwpf.h @@ -21,10 +21,16 @@ #define _STRWPF_H_ #include +#include -class StrWPF : public std::string { +class StrWPF : public std::string { public: + StrWPF() : m_addsiz(100){} + void catf( const char* fmt, ... ); + bool catf( const char* fmt, va_list ap ); + private: + int m_addsiz; }; #endif From ae4c6e98f2845a818bb743b54001b6d9a573c05c Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 25 Apr 2014 20:58:14 -0700 Subject: [PATCH 21/29] replace escaped carraige returns and unicode from resources to get closer to what getString returns. If I can't find an Android API to do this it should be done on the server, but this is an improvement until I do. --- .../org/eehouse/android/xw4/loc/LocUtils.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java index 485fa0659..8ca2aa420 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java @@ -45,6 +45,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.json.JSONArray; import org.json.JSONObject; @@ -480,11 +482,33 @@ public class LocUtils { return result; } + private static Pattern s_patUnicode = Pattern.compile("(\\\\[Uu][0-9a-fA-F]{4})"); + private static Pattern s_patCr = Pattern.compile("\\\\n"); + private static String replaceEscaped( String txt ) { - String result = txt.replaceAll("\\n", "\n"); - // DbgUtils.logf( "replaceEscaped(<<%s>>) -> <<%s>>", txt, result ); - return result; + // String orig = txt; + + // Swap unicode escapes for real chars + Matcher matcher = s_patUnicode.matcher( txt ); + StringBuffer sb = new StringBuffer(); + while ( matcher.find() ) { + int start = matcher.start(); + int end = matcher.end(); + String match = txt.substring( start, end ); + char ch = (char)Integer.parseInt( match.substring(2), 16 ); + matcher.appendReplacement( sb, String.valueOf(ch) ); + } + matcher.appendTail(sb); + txt = sb.toString(); + + // Swap in real carriage returns + txt = s_patCr.matcher( txt ).replaceAll( "\n" ); + + // if ( ! orig.equals( txt ) ) { + // DbgUtils.logf( "replaceEscaped: <<%s>> -> <<%s>>", orig, txt ); + // } + return txt; } public static AlertDialog.Builder makeAlertBuilder( Context context ) From c82c59d985d9c566447a50ad5f0d7fd004c228a6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 26 Apr 2014 20:04:55 -0700 Subject: [PATCH 22/29] experimental: enable SMS on KitKat if debug setting to use Data SMS is on --- .../src/org/eehouse/android/xw4/Utils.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java index 858192f0d..2860986f4 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java @@ -100,11 +100,16 @@ public class Utils { { if ( null == s_deviceSupportSMS ) { boolean doesSMS = false; - TelephonyManager tm = (TelephonyManager) - context.getSystemService(Context.TELEPHONY_SERVICE); - if ( null != tm ) { - int type = tm.getPhoneType(); - doesSMS = TelephonyManager.PHONE_TYPE_NONE != type; + // 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 ) { + int type = tm.getPhoneType(); + doesSMS = TelephonyManager.PHONE_TYPE_NONE != type; + } } s_deviceSupportSMS = new Boolean( doesSMS ); } From f386cd5c1b05034dae394343d0b7c9392960a881 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 27 Apr 2014 13:17:48 -0700 Subject: [PATCH 23/29] for consistency, createDialog -> onCreateDialog --- .../XWords4/src/org/eehouse/android/xw4/BoardActivity.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/BoardDelegate.java | 4 ++-- .../XWords4/src/org/eehouse/android/xw4/DelegateBase.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/DictsActivity.java | 2 +- .../XWords4/src/org/eehouse/android/xw4/DictsDelegate.java | 6 +++--- .../src/org/eehouse/android/xw4/GamesListActivity.java | 2 +- .../src/org/eehouse/android/xw4/GamesListDelegate.java | 4 ++-- .../src/org/eehouse/android/xw4/StudyListActivity.java | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 7f165236b..264a322cc 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -39,7 +39,7 @@ public class BoardActivity extends Activity { { Dialog dialog = super.onCreateDialog( id ); if ( null == dialog ) { - dialog = m_dlgt.createDialog( id ); + dialog = m_dlgt.onCreateDialog( id ); } return dialog; } // onCreateDialog diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java index 78336ca40..d6a877543 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java @@ -236,9 +236,9 @@ public class BoardDelegate extends DelegateBase } } - protected Dialog createDialog( int id ) + protected Dialog onCreateDialog( int id ) { - Dialog dialog = super.createDialog( id ); + Dialog dialog = super.onCreateDialog( id ); if ( null == dialog ) { DialogInterface.OnClickListener lstnr; AlertDialog.Builder ab = makeAlertBuilder(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java index a4b514b45..014c835c3 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java @@ -122,7 +122,7 @@ public class DelegateBase implements DlgDelegate.DlgClickNotify, m_delegate.showDialog( dlgID ); } - protected Dialog createDialog( int id ) + protected Dialog onCreateDialog( int id ) { return m_delegate.createDialog( id ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index a069d8cc3..2d1b0257d 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -87,7 +87,7 @@ public class DictsActivity extends ExpandableListActivity { { Dialog dialog = super.onCreateDialog( id ); if ( null == dialog ) { - dialog = m_dlgt.createDialog( id ); + dialog = m_dlgt.onCreateDialog( id ); } return dialog; } // onCreateDialog diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsDelegate.java index 98d85934c..4bf482dcf 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsDelegate.java @@ -234,7 +234,7 @@ public class DictsDelegate extends DelegateBase m_activity = activity; } - protected Dialog createDialog( int id ) + protected Dialog onCreateDialog( int id ) { OnClickListener lstnr, lstnr2; Dialog dialog; @@ -341,7 +341,7 @@ public class DictsDelegate extends DelegateBase break; default: - dialog = super.createDialog( id ); + dialog = super.onCreateDialog( id ); doRemove = false; break; } @@ -351,7 +351,7 @@ public class DictsDelegate extends DelegateBase } return dialog; - } // createDialog + } // onCreateDialog protected void prepareDialog( int id, Dialog dialog ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java index 93640e078..a77d7ff13 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java @@ -48,7 +48,7 @@ public class GamesListActivity extends ListActivity { { Dialog dialog = super.onCreateDialog( id ); if ( null == dialog && null != m_dlgt ) { - dialog = m_dlgt.createDialog( id ); + dialog = m_dlgt.onCreateDialog( id ); } return dialog; } // onCreateDialog diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java index ae65d8372..ad9976104 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java @@ -127,7 +127,7 @@ public class GamesListDelegate extends DelegateBase m_activity = activity; } - protected Dialog createDialog( int id ) + protected Dialog onCreateDialog( int id ) { Dialog dialog = null; DialogInterface.OnClickListener lstnr; @@ -331,7 +331,7 @@ public class GamesListDelegate extends DelegateBase break; default: - dialog = super.createDialog( id ); + dialog = super.onCreateDialog( id ); break; } return dialog; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/StudyListActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/StudyListActivity.java index 1dd5d7dd0..83fbb9c8b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/StudyListActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/StudyListActivity.java @@ -75,7 +75,7 @@ public class StudyListActivity extends ListActivity { @Override protected Dialog onCreateDialog( int id ) { - Dialog dialog = m_dlgt.createDialog( id ); + Dialog dialog = m_dlgt.onCreateDialog( id ); if ( null == dialog ) { dialog = super.onCreateDialog( id ); } From 33a83b0e2fcf062f4f640ccab0785b2d2b439542 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 27 Apr 2014 13:34:09 -0700 Subject: [PATCH 24/29] toward use of data text messages for games: don't let CDMA phone users turn data messages on, and when checking if SMS is supported say yes for KitKat users if they've successfully turned data on. --- xwords4/android/XWords4/archive/R.java | 1 + .../android/XWords4/res/values/strings.xml | 2 + xwords4/android/XWords4/res/xml/xwprefs.xml | 2 +- .../eehouse/android/xw4/PrefsDelegate.java | 136 ++++++++++-------- .../src/org/eehouse/android/xw4/Utils.java | 30 +++- 5 files changed, 102 insertions(+), 69 deletions(-) diff --git a/xwords4/android/XWords4/archive/R.java b/xwords4/android/XWords4/archive/R.java index caeabc561..ecabfd403 100644 --- a/xwords4/android/XWords4/archive/R.java +++ b/xwords4/android/XWords4/archive/R.java @@ -854,6 +854,7 @@ public final class R { /** */ public static final int cur_tiles_fmt=0x7f0601fc; + public static final int data_gsm_only=0x7f0602ae; public static final int debug_features=0x7f06007c; public static final int debug_features_summary=0x7f06007d; /** dictionary used by default for human players when creating diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 6d1efaf14..e65cf0882 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -2235,5 +2235,7 @@ Enable local translating Add option to every screen menu + + SMS Data is only available on GSM phones. diff --git a/xwords4/android/XWords4/res/xml/xwprefs.xml b/xwords4/android/XWords4/res/xml/xwprefs.xml index 2b1a0f488..10eb90e02 100644 --- a/xwords4/android/XWords4/res/xml/xwprefs.xml +++ b/xwords4/android/XWords4/res/xml/xwprefs.xml @@ -347,7 +347,7 @@ Integer.valueOf( android.os.Build.VERSION.SDK ) - || XWPrefs.getPrefsBoolean( context, R.string.key_send_data_sms, - false ) ) { + boolean preKitkat = 19 > Integer.valueOf( android.os.Build.VERSION.SDK); + boolean usingData = + XWPrefs.getPrefsBoolean( context, R.string.key_send_data_sms, + false ); + if ( preKitkat || usingData ) { TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if ( null != tm ) { int type = tm.getPhoneType(); - doesSMS = TelephonyManager.PHONE_TYPE_NONE != type; + doesSMS = (usingData && !preKitkat) + ? TelephonyManager.PHONE_TYPE_GSM == type + : TelephonyManager.PHONE_TYPE_NONE != type; } } s_deviceSupportSMS = new Boolean( doesSMS ); From b87a7e808ca593ca500acb9dc43a3a0ceb79c19d Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 27 Apr 2014 19:08:04 -0700 Subject: [PATCH 25/29] don't fail building if scp fails --- xwords4/android/scripts/arelease.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/scripts/arelease.sh b/xwords4/android/scripts/arelease.sh index 642e33b87..cc8ff9e9d 100755 --- a/xwords4/android/scripts/arelease.sh +++ b/xwords4/android/scripts/arelease.sh @@ -77,7 +77,7 @@ for PACK_UNSIGNED in $FILES; do if [ -n "$XW_RELEASE_SCP_DEST" ]; then echo "running scp ${TARGET} $XW_RELEASE_SCP_DEST" - scp "${TARGET}" "$XW_RELEASE_SCP_DEST" + scp "${TARGET}" "$XW_RELEASE_SCP_DEST" || true else echo "XW_RELEASE_SCP_DEST not set; you're on your own" fi From 19badff420efc069e936c2a695a8fd625f87ce5b Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 27 Apr 2014 19:21:21 -0700 Subject: [PATCH 26/29] tweak text --- xwords4/android/XWords4/res/values/common_rsrc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml index 0a14a6edd..58216042d 100644 --- a/xwords4/android/XWords4/res/values/common_rsrc.xml +++ b/xwords4/android/XWords4/res/values/common_rsrc.xml @@ -148,7 +148,7 @@ Menuitems etc. (release builds only) Send SMS as data - (fails on CDMA phones) + (GSM phones only) Network stats Game network stats From be817feb55e2e0d8dea1d29c151e083a7e5cbb3e Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 27 Apr 2014 20:03:08 -0700 Subject: [PATCH 27/29] add git rev hash as constant (and usage()) --- xwords4/android/scripts/genvers.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/xwords4/android/scripts/genvers.sh b/xwords4/android/scripts/genvers.sh index 29c92f738..c6d33cf04 100755 --- a/xwords4/android/scripts/genvers.sh +++ b/xwords4/android/scripts/genvers.sh @@ -2,6 +2,13 @@ set -e -u +usage() { + echo "usage: $0 " + exit 1 +} + +[ $# -eq 4 ] || usage + VARIANT=$1 CLIENT_VERS_RELAY=$2 CHAT_SUPPORTED=$3 @@ -12,6 +19,11 @@ cd $(dirname $0) cd ../ GITVERSION=$(../scripts/gitversion.sh) +if git rev-parse $GITVERSION 2>/dev/null 1>/dev/null; then + GIT_HASH=$(git rev-parse $GITVERSION) +else + GIT_HASH=unknown +fi # TODO: deal with case where there's no hash available -- exported # code maybe? Better: gitversion.sh does that. @@ -34,6 +46,7 @@ cat < ${BUILD_DIR}/src/org/eehouse/android/${VARIANT}/BuildConstants.java package org.eehouse.android.${VARIANT}; class BuildConstants { public static final String GIT_REV = "$SHORTVERS"; + public static final String GIT_HASH = "$GIT_HASH"; public static final short CLIENT_VERS_RELAY = $CLIENT_VERS_RELAY; public static final boolean CHAT_SUPPORTED = $CHAT_SUPPORTED; public static final boolean THUMBNAIL_SUPPORTED = $THUMBNAIL_SUPPORTED; From b42c372c79334126eb6b347ad4fe70cd2bf599d4 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 27 Apr 2014 20:03:40 -0700 Subject: [PATCH 28/29] send git version code, app package name, and git hash with all messages --- .../android/xw4/UpdateCheckReceiver.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java index 8eabb20dc..268111b51 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java @@ -74,6 +74,7 @@ public class UpdateCheckReceiver extends BroadcastReceiver { private static final String k_PARAMS = "params"; private static final String k_DEVID = "did"; private static final String k_XLATEINFO = "xlatinfo"; + private static final String k_APPGITREV = "apprev"; @Override public void onReceive( Context context, Intent intent ) @@ -122,6 +123,13 @@ public class UpdateCheckReceiver extends BroadcastReceiver { JSONObject params = new JSONObject(); PackageManager pm = context.getPackageManager(); String packageName = context.getPackageName(); + int versionCode; + try { + versionCode = pm.getPackageInfo( packageName, 0 ).versionCode; + } catch ( PackageManager.NameNotFoundException nnfe ) { + DbgUtils.loge( nnfe ); + versionCode = 0; + } // App update if ( Utils.isGooglePlayApp( context ) ) { @@ -130,8 +138,6 @@ public class UpdateCheckReceiver extends BroadcastReceiver { String installer = pm.getInstallerPackageName( packageName ); try { - int versionCode = pm.getPackageInfo( packageName, 0 ).versionCode; - JSONObject appParams = new JSONObject(); appParams.put( k_NAME, packageName ); @@ -143,8 +149,6 @@ public class UpdateCheckReceiver extends BroadcastReceiver { } params.put( k_APP, appParams ); params.put( k_DEVID, XWPrefs.getDevID( context ) ); - } catch ( PackageManager.NameNotFoundException nnfe ) { - DbgUtils.loge( nnfe ); } catch ( org.json.JSONException jse ) { DbgUtils.loge( jse ); } @@ -176,8 +180,15 @@ public class UpdateCheckReceiver extends BroadcastReceiver { } if ( 0 < params.length() ) { - new UpdateQueryTask( context, params, fromUI, pm, - packageName, dals ).execute(); + try { + params.put( k_APPGITREV, BuildConstants.GIT_HASH ); + params.put( k_NAME, packageName ); + params.put( k_AVERS, versionCode ); + new UpdateQueryTask( context, params, fromUI, pm, + packageName, dals ).execute(); + } catch ( org.json.JSONException jse ) { + DbgUtils.loge( jse ); + } } } From 6c46ce22975fc19169f1fbe90ff5c60a9525f05e Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 28 Apr 2014 07:58:23 -0700 Subject: [PATCH 29/29] send and receive array of locales for translation info, one of which is the device's current default. Plan's to include a fake as well if set, but currently that replaces the default. --- .../android/xw4/UpdateCheckReceiver.java | 7 +- .../src/org/eehouse/android/xw4/XWPrefs.java | 2 +- .../org/eehouse/android/xw4/loc/LocUtils.java | 121 ++++++++++-------- 3 files changed, 72 insertions(+), 58 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java index 268111b51..a3a04411e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java @@ -170,7 +170,7 @@ public class UpdateCheckReceiver extends BroadcastReceiver { } // Xlations update - JSONObject xlationUpdate = LocUtils.makeForXlationUpdate( context ); + JSONArray xlationUpdate = LocUtils.makeForXlationUpdate( context ); if ( null != xlationUpdate ) { try { params.put( k_XLATEINFO, xlationUpdate ); @@ -184,6 +184,7 @@ public class UpdateCheckReceiver extends BroadcastReceiver { params.put( k_APPGITREV, BuildConstants.GIT_HASH ); params.put( k_NAME, packageName ); params.put( k_AVERS, versionCode ); + DbgUtils.logf( "current update: %s", params.toString() ); new UpdateQueryTask( context, params, fromUI, pm, packageName, dals ).execute(); } catch ( org.json.JSONException jse ) { @@ -398,8 +399,8 @@ public class UpdateCheckReceiver extends BroadcastReceiver { // translations info if ( jobj.has( k_XLATEINFO ) ) { - JSONObject data = jobj.getJSONObject( k_XLATEINFO ); - int nAdded = LocUtils.addXlation( m_context, data ); + JSONArray data = jobj.getJSONArray( k_XLATEINFO ); + int nAdded = LocUtils.addXlations( m_context, data ); if ( 0 < nAdded ) { gotOne = true; String msg = LocUtils.getString( m_context, R.string diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java index f61fb21c5..aa00f0e32 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWPrefs.java @@ -430,7 +430,7 @@ public class XWPrefs { setPrefsString( context, keyID, TextUtils.join( "\n", value ) ); } - public static String getLocale( Context context ) + public static String getFakeLocale( Context context ) { return getPrefsString( context, R.string.key_xlations_locale ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java index 8ca2aa420..89f75fae8 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/loc/LocUtils.java @@ -44,6 +44,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -62,8 +63,6 @@ import org.eehouse.android.xw4.XWPrefs; public class LocUtils { private static final int FMT_LEN = 4; private static final String k_LOCALE = "locale"; - private static final String k_XLATPROTO = "proto"; - private static final int XLATE_CUR_VERSION = 1; private static final String k_XLATEVERS = "xlatevers"; private static Map s_xlationsLocal = null; @@ -242,58 +241,77 @@ public class LocUtils { public static void saveLocalData( Context context ) { - DBUtils.saveXlations( context, s_curLocale, s_xlationsLocal, false ); + DBUtils.saveXlations( context, getCurLocale( context ), + s_xlationsLocal, false ); } - public static JSONObject makeForXlationUpdate( Context context ) + public static JSONArray makeForXlationUpdate( Context context ) { - JSONObject result = null; - if ( null != s_curLocale && 0 < s_curLocale.length() ) { - try { - String version = DBUtils.getStringFor( context, k_XLATEVERS, "0" ); - result = new JSONObject() - .put( k_XLATPROTO, XLATE_CUR_VERSION ) - .put( k_LOCALE, s_curLocale ) - .put( k_XLATEVERS, version ); - } catch ( org.json.JSONException jse ) { - DbgUtils.loge( jse ); - } + String locale = getCurLocale( context ); + String fake = XWPrefs.getFakeLocale( context ); + JSONArray result = new JSONArray() + .put( entryForLocale( context, locale ) ); + if ( null != fake && 0 < fake.length() && ! fake.equals(locale) ) { + result.put( entryForLocale( context, fake ) ); } return result; } + private static JSONObject entryForLocale( Context context, String locale ) + { + JSONObject result = null; + try { + String version = + DBUtils.getStringFor( context, localeKey(locale), "0" ); + result = new JSONObject() + .put( k_LOCALE, locale ) + .put( k_XLATEVERS, version ); + } catch ( org.json.JSONException jse ) { + DbgUtils.loge( jse ); + } + return result; + } + + private static String localeKey( String locale ) + { + return String.format( "%s:%s", k_XLATEVERS, locale ); + } + private static final String k_OLD = "old"; private static final String k_NEW = "new"; private static final String k_PAIRS = "pairs"; - public static int addXlation( Context context, JSONObject data ) + public static int addXlations( Context context, JSONArray data ) { int nAdded = 0; try { - int newVersion = data.getInt( k_NEW ); - JSONArray pairs = data.getJSONArray( k_PAIRS ); - DbgUtils.logf( "got pairs of len %d, version %d", pairs.length(), - newVersion ); + int nLocales = data.length(); + for ( int ii = 0; ii < nLocales; ++ii ) { + JSONObject entry = data.getJSONObject( ii ); + String locale = entry.getString( k_LOCALE ); + String newVersion = entry.getString( k_NEW ); + JSONArray pairs = entry.getJSONArray( k_PAIRS ); + DbgUtils.logf( "addXlations: locale %s: got pairs of len %d, version %s", locale, + pairs.length(), newVersion ); - int len = pairs.length(); - Map newXlations = new HashMap( len ); - for ( int ii = 0; ii < len; ++ii ) { - JSONObject pair = pairs.getJSONObject( ii ); - int id = pair.getInt( "id" ); - String key = context.getString( id ); - Assert.assertNotNull( key ); - String txt = pair.getString( "loc" ); - txt = replaceEscaped( txt ); - newXlations.put( key, txt ); + int len = pairs.length(); + Map newXlations = new HashMap( len ); + for ( int jj = 0; jj < len; ++jj ) { + JSONObject pair = pairs.getJSONObject( jj ); + int id = pair.getInt( "id" ); + String key = context.getString( id ); + Assert.assertNotNull( key ); + String txt = pair.getString( "loc" ); + txt = replaceEscaped( txt ); + newXlations.put( key, txt ); + } + + DBUtils.saveXlations( context, locale, newXlations, true ); + DBUtils.setStringFor( context, localeKey(locale), newVersion ); + nAdded += len; } - - DBUtils.saveXlations( context, s_curLocale, newXlations, true ); - DBUtils.setStringFor( context, k_XLATEVERS, - String.format( "%d", newVersion ) ); - s_xlationsBlessed = null; loadXlations( context ); - nAdded = len; } catch ( org.json.JSONException jse ) { DbgUtils.loge( jse ); } @@ -354,13 +372,23 @@ public class LocUtils { } } - private static void loadXlations( Context context ) + private static String getCurLocale( Context context ) { if ( null == s_curLocale ) { - s_curLocale = XWPrefs.getLocale( context ); + String locale = XWPrefs.getFakeLocale( context ); + if ( null == locale || 0 == locale.length() ) { + locale = Locale.getDefault().toString(); + } + s_curLocale = locale; } + return s_curLocale; + } + + private static void loadXlations( Context context ) + { if ( null == s_xlationsLocal || null == s_xlationsBlessed ) { - Object[] asObjs = DBUtils.getXlations( context, s_curLocale ); + Object[] asObjs = DBUtils.getXlations( context, + getCurLocale( context ) ); s_xlationsLocal = (Map)asObjs[0]; s_xlationsBlessed = (Map)asObjs[1]; DbgUtils.logf( "loadXlations: got %d local strings, %d blessed strings", @@ -387,16 +415,6 @@ public class LocUtils { return s_idsToKeys.get( id ); } - private static boolean isEnabled( Context context ) - { - if ( null == s_enabled ) { - s_curLocale = XWPrefs.getLocale( context ); - s_enabled = new Boolean( null != s_curLocale && - 0 < s_curLocale.length() ); - } - return s_enabled; - } - private static void xlateView( Context context, View view, int depth ) { // DbgUtils.logf( "xlateView(depth=%d, view=%s, canRecurse=%b)", depth, @@ -408,11 +426,6 @@ public class LocUtils { } else if ( view instanceof TextView ) { TextView tv = (TextView)view; tv.setText( xlateString( context, tv.getText().toString() ) ); - // } else if ( view instanceof CheckBox ) { - // CheckBox box = (CheckBox)view; - // String str = box.getText().toString(); - // str = xlateString( context, str ); - // box.setText( str ); } else if ( view instanceof Spinner ) { Spinner sp = (Spinner)view; CharSequence prompt = sp.getPrompt();