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

This commit is contained in:
Weblate 2017-09-06 11:49:19 +02:00
commit 11368df086
20 changed files with 237 additions and 306 deletions

View file

@ -3,3 +3,5 @@
local.properties
.idea
app/src/main/assets/build-info.txt
obj-*
libs-*

View file

@ -1,6 +1,6 @@
def INITIAL_CLIENT_VERS = 8
def VERSION_CODE_BASE = 119
def VERSION_NAME = '4.4.123'
def VERSION_CODE_BASE = 121
def VERSION_NAME = '4.4.125'
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
boolean forFDroid = hasProperty('forFDroid')

View file

@ -6,8 +6,6 @@
<!-- BE SURE TO MODIFY project.properties AND the variable TARGET in
../scripts/setup_local_props.sh if targetSdkVersion changes!!!
-->
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="23" />
<supports-screens android:resizeable="true"
android:smallScreens="true"
android:normalScreens="true"
@ -48,6 +46,7 @@
<application android:icon="@drawable/icon48x48"
android:label="@string/app_name"
android:name=".XWApp"
android:theme="@style/AppTheme"
>
<activity android:name="MainActivity"
@ -84,9 +83,6 @@
<activity android:name="RelayInviteActivity"
android:label="@string/relay_invite_title"
/>
<activity android:name="WiDirInviteActivity"
android:label="@string/p2p_invite_title"
/>
<activity android:name="GameConfigActivity"
android:screenOrientation="sensor"

View file

@ -13,9 +13,10 @@
</style>
</head>
<body>
<h2>CrossWords 4.4.123 release</h2>
<h2>CrossWords 4.4.125 release</h2>
<p>This is a quick bug-fix release.</p>
<p>This release fixes a problem inviting to new networked games, and
with title bars on some Samsung devices.</p>
<div id="survey">
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
@ -25,8 +26,10 @@
<h3>New with this release</h3>
<ul>
<li>Fix crash when invitation requires a wordlist you don't have</li>
<li>Include latest Portuguese translations</li>
<li>Fix delays bringing up the Invite dialog for new games</li>
<li>Explicitly specify application "theme" to fix a Samsung
"upgrade" turning the titlebar white and so making menu
icons disappear</li>
</ul>
<p>(The full changelog

View file

@ -126,6 +126,7 @@ public class BoardDelegate extends DelegateBase
private boolean m_overNotShown;
private boolean m_dropOnDismiss;
private DBAlert m_inviteAlert;
private boolean m_haveStartedShowing;
public class TimerRunnable implements Runnable {
private int m_why;
@ -2225,6 +2226,7 @@ public class BoardDelegate extends DelegateBase
// Assert.assertFalse( BuildConfig.DEBUG );
}
m_inviteAlert = null;
m_haveStartedShowing = false;
}
}
@ -2304,9 +2306,14 @@ public class BoardDelegate extends DelegateBase
showDialogFragment( dlgID, dlgTitle, txt );
}
// This is failing sometimes, and so the null == m_inviteAlert test means
// we never post it. BUT on a lot of devices without the test we wind up
// trying over and over to put the thing up.
private void showInviteAlertIf()
{
if ( /* null == m_inviteAlert && */m_mySIS.nMissing > 0 && !isFinishing() ) {
DbgUtils.assertOnUIThread();
if ( ! m_haveStartedShowing && null == m_inviteAlert
&& m_mySIS.nMissing > 0 && !isFinishing() ) {
InviteAlertState ias = new InviteAlertState();
ias.summary = m_summary;
ias.gi = m_gi;
@ -2315,6 +2322,7 @@ public class BoardDelegate extends DelegateBase
ias.rowid = m_rowid;
ias.nMissing = m_mySIS.nMissing;
showDialogFragment( DlgID.DLG_INVITE, ias );
m_haveStartedShowing = true;
}
}

View file

@ -84,14 +84,24 @@ public class ConnStatusHandler {
public String newerStr( Context context )
{
s_time.set( successNewer? lastSuccess : lastFailure );
return format( context, s_time );
String result = null;
long time = successNewer? lastSuccess : lastFailure;
if ( time > 0 ) {
s_time.set( time );
result = format( context, s_time );
}
return result;
}
public String olderStr( Context context )
{
s_time.set( successNewer? lastFailure : lastSuccess );
return format( context, s_time );
String result = null;
long time = successNewer? lastFailure : lastSuccess;
if ( time > 0 ) {
s_time.set( time );
result = format( context, s_time );
}
return result;
}
public void update( boolean success )
@ -175,13 +185,20 @@ public class ConnStatusHandler {
String did = addDebugInfo( context, typ );
sb.append( String.format( "\n\n*** %s %s***\n",
typ.longName( context ), did ) );
// For sends we list failures too.
SuccessRecord record = recordFor( typ, false );
tmp = LocUtils.getString( context, record.successNewer?
R.string.connstat_succ :
R.string.connstat_unsucc );
sb.append( LocUtils
.getString( context, R.string.connstat_lastsend_fmt,
tmp, record.newerStr( context ) ) );
String timeStr = record.newerStr( context );
if ( null != timeStr ) {
sb.append( LocUtils
.getString( context, R.string.connstat_lastsend_fmt,
tmp, timeStr ) )
.append( "\n" );
}
int fmtId = 0;
if ( record.successNewer ) {
@ -193,11 +210,12 @@ public class ConnStatusHandler {
fmtId = R.string.connstat_lastother_unsucc_fmt;
}
}
if ( 0 != fmtId ) {
sb.append( LocUtils.getString( context, fmtId,
record.olderStr( context )));
timeStr = record.olderStr( context );
if ( 0 != fmtId && null != timeStr ) {
sb.append( LocUtils.getString( context, fmtId, timeStr ))
.append( "\n" );
}
sb.append( "\n\n" );
sb.append( "\n" );
record = recordFor( typ, true );
if ( record.haveSuccess() ) {

View file

@ -290,7 +290,7 @@ public class DelegateBase implements DlgClickNotify,
if ( main.inDPMode() ) {
if ( !m_finishCalled ) {
m_finishCalled = true;
main.finishFragment();
main.finishFragment( (XWFragment)m_delegator );
}
handled = true;
}

View file

@ -32,6 +32,8 @@ import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import java.io.Serializable;
import junit.framework.Assert;
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
@ -126,13 +128,29 @@ public class DlgDelegate {
SET_GOT_LANGDICT,
}
public static class ActionPair {
public static class ActionPair implements Serializable {
public ActionPair( Action act, int str ) {
buttonStr = str; action = act;
}
public int buttonStr;
public Action action;
public Object[] params; // null for now
@Override
public boolean equals( Object obj )
{
boolean result;
if ( BuildConfig.DEBUG ) {
result = null != obj && obj instanceof ActionPair;
if ( result ) {
ActionPair other = (ActionPair)obj;
result = buttonStr == other.buttonStr
&& action == other.action;
}
} else {
result = super.equals( obj );
}
return result;
}
}
public abstract class DlgDelegateBuilder {

View file

@ -164,6 +164,7 @@ public class DlgState implements Parcelable {
out.writeInt( m_titleId );
out.writeString( m_msg );
out.writeSerializable( m_params );
out.writeSerializable( m_pair );
}
private void testCanParcelize()
@ -197,6 +198,7 @@ public class DlgState implements Parcelable {
int titleId = in.readInt();
String msg = in.readString();
Object[] params = (Object[])in.readSerializable();
ActionPair pair = (ActionPair)in.readSerializable();
DlgState state = new DlgState(id)
.setMsg( msg )
.setPosButton( posButton )
@ -206,6 +208,7 @@ public class DlgState implements Parcelable {
.setOnNA( onNA )
.setTitle(titleId)
.setParams(params)
.setActionPair(pair)
;
return state;
}

View file

@ -24,19 +24,20 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
@ -1926,8 +1927,19 @@ public class GamesListDelegate extends ListDelegateBase
{
m_mySIS.nextIsSolo = solo;
int count = m_adapter.getCount();
boolean skipOffer = 6 > count || XWPrefs.getHideNewgameButtons( m_activity );
boolean skipOffer = XWPrefs.getHideNewgameButtons( m_activity );
if ( ! skipOffer ) {
// If the API's availble, offer to hide buttons as soon as there
// are enough games that the list is scrollable. Otherwise fall
// back to there being at least four games.
if ( Build.VERSION.SDK_INT >= 19 ) {
ListView list = getListView();
skipOffer = !list.canScrollList( 1 ) && !list.canScrollList( -1 );
} else {
skipOffer = 4 > m_adapter.getCount();
}
}
if ( skipOffer ) {
handleNewGame( solo );
} else {

View file

@ -298,11 +298,12 @@ public class MainActivity extends XWActivity
resultCode, data );
}
protected void finishFragment()
protected void finishFragment( XWFragment fragment )
{
// Assert.assertTrue( fragment instanceof XWFragment );
// Log.d( TAG, "finishFragment()" );
getSupportFragmentManager().popBackStack/*Immediate*/();
int ID = fragment.getCommitID();
getSupportFragmentManager()
.popBackStack( ID, FragmentManager.POP_BACK_STACK_INCLUSIVE );
}
//////////////////////////////////////////////////////////////////////
@ -453,14 +454,14 @@ public class MainActivity extends XWActivity
return frag;
}
private void addFragmentImpl( Fragment fragment, Bundle bundle,
private void addFragmentImpl( XWFragment fragment, Bundle bundle,
String parentName )
{
fragment.setArguments( bundle );
addFragmentImpl( fragment, parentName );
}
private void addFragmentImpl( final Fragment fragment,
private void addFragmentImpl( final XWFragment fragment,
final String parentName )
{
if ( m_safeToCommit ) {
@ -497,7 +498,7 @@ public class MainActivity extends XWActivity
}
}
private void safeAddFragment( Fragment fragment, String parentName )
private void safeAddFragment( XWFragment fragment, String parentName )
{
Assert.assertTrue( m_safeToCommit );
String newName = fragment.getClass().getSimpleName();
@ -505,10 +506,11 @@ public class MainActivity extends XWActivity
popUnneeded( fm, newName, parentName );
fm.beginTransaction()
int ID = fm.beginTransaction()
.add( R.id.main_container, fragment, newName )
.addToBackStack( newName )
.commit();
fragment.setCommitID( ID );
// Don't do this. It causes an exception if e.g. from fragment.start()
// I wind up launching another fragment and calling into this code
// again. If I need executePendingTransactions() I'm doing something

View file

@ -75,6 +75,7 @@ public class RelayInviteDelegate extends InviteDelegate {
// private RelayDevsAdapter m_adapter;
private boolean m_immobileConfirmed;
private Activity m_activity;
private String m_devIDStr;
public static void launchForResult( Activity activity, int nMissing,
RequestCode requestCode )
@ -101,6 +102,8 @@ public class RelayInviteDelegate extends InviteDelegate {
super.init( msg, R.string.empty_relay_inviter );
addButtonBar( R.layout.relay_buttons, BUTTONIDS );
m_devIDStr = String.format( "%d", DevID.getRelayDevIDInt(m_activity) );
// getBundledData( savedInstanceState );
// m_addButton = (ImageButton)findViewById( R.id.manual_add_button );
@ -437,6 +440,21 @@ public class RelayInviteDelegate extends InviteDelegate {
// }
// } // addPhoneNumbers
private void addSelf()
{
boolean hasSelf = false;
for ( DevIDRec rec : m_devIDRecs ) {
if ( rec.m_devID.equals( m_devIDStr ) ) {
hasSelf = true;
break;
}
}
if ( !hasSelf ) {
DevIDRec rec = new DevIDRec( "me", m_devIDStr );
m_devIDRecs.add( rec );
}
}
private void rebuildList( boolean checkIfAll )
{
Collections.sort( m_devIDRecs, new Comparator<DevIDRec>() {
@ -444,15 +462,9 @@ public class RelayInviteDelegate extends InviteDelegate {
return rec1.m_opponent.compareTo(rec2.m_opponent);
}
});
addSelf();
updateListAdapter( m_devIDRecs.toArray( new DevIDRec[m_devIDRecs.size()] ) );
// m_adapter = new RelayDevsAdapter();
// setListAdapter( m_adapter );
// if ( checkIfAll && m_devIDRecs.size() <= m_nMissing ) {
// Iterator<DevIDRec> iter = m_devIDRecs.iterator();
// while ( iter.hasNext() ) {
// iter.next().m_isChecked = true;
// }
// }
tryEnable();
}

View file

@ -177,7 +177,7 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
params.put( k_STRINGSHASH, BuildConfig.STRINGS_HASH );
params.put( k_NAME, packageName );
params.put( k_AVERS, versionCode );
Log.d( TAG, "current update: %s", params.toString() );
// Log.d( TAG, "current update: %s", params.toString() );
new UpdateQueryTask( context, params, fromUI, pm,
packageName, dals ).execute();
} catch ( org.json.JSONException jse ) {
@ -278,6 +278,7 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
{
boolean gotOne = false;
try {
// Log.d( TAG, "makeNotificationsIf(response=%s)", jstr );
JSONObject jobj = new JSONObject( jstr );
if ( null != jobj ) {

View file

@ -511,8 +511,7 @@ public class Utils {
new ObjectInputStream( new ByteArrayInputStream(bytes) );
result = ois.readObject();
} catch ( Exception ex ) {
Log.ex( TAG, ex );
Assert.assertFalse( BuildConfig.DEBUG );
Log.d( TAG, ex.getMessage() );
}
return result;
}

View file

@ -41,10 +41,12 @@ import junit.framework.Assert;
abstract class XWFragment extends Fragment implements Delegator {
private static final String TAG = XWFragment.class.getSimpleName();
private static final String PARENT_NAME = "PARENT_NAME";
private static final String COMMIT_ID = "COMMIT_ID";
private DelegateBase m_dlgt;
private String m_parentName;
private boolean m_hasOptionsMenu = false;
private int m_commitID;
private static Set<XWFragment> sActiveFrags = new HashSet<XWFragment>();
public static XWFragment findOwnsView( View view )
@ -75,6 +77,9 @@ abstract class XWFragment extends Fragment implements Delegator {
return m_parentName;
}
public void setCommitID( int id ) { m_commitID = id; }
public int getCommitID() { return m_commitID; }
protected void onCreate( DelegateBase dlgt, Bundle sis, boolean hasOptionsMenu )
{
m_hasOptionsMenu = hasOptionsMenu;
@ -87,6 +92,7 @@ abstract class XWFragment extends Fragment implements Delegator {
Log.d( TAG, "%s.onSaveInstanceState() called", getClass().getSimpleName() );
Assert.assertNotNull( m_parentName );
outState.putString( PARENT_NAME, m_parentName );
outState.putInt( COMMIT_ID, m_commitID );
m_dlgt.onSaveInstanceState( outState );
super.onSaveInstanceState( outState );
}
@ -98,6 +104,7 @@ abstract class XWFragment extends Fragment implements Delegator {
if ( null != sis ) {
m_parentName = sis.getString( PARENT_NAME );
Assert.assertNotNull( m_parentName );
m_commitID = sis.getInt( COMMIT_ID );
}
Assert.assertNull( m_dlgt );
m_dlgt = dlgt;

View file

@ -749,10 +749,11 @@ public class JNIThread extends Thread {
public void handle( JNICmd cmd, Object... args )
{
m_queue.add( new QueueElem( cmd, true, args ) );
if ( m_stopped && ! JNICmd.CMD_NONE.equals(cmd) ) {
Log.w( TAG, "adding %s to stopped thread!!!", cmd.toString() );
Log.w( TAG, "NOT adding %s to stopped thread!!!", cmd.toString() );
DbgUtils.printStack( TAG );
} else {
m_queue.add( new QueueElem( cmd, true, args ) );
}
}

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="android:Theme.Material"/>
<style name="config_separator">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">fill_parent</item>

View file

@ -1,248 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Declare the contents of this Android application. The namespace
attribute brings in the Android platform namespace, and the package
supplies a unique name for the application. When writing your
own application, the package name must be changed from "com.example.*"
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eehouse.android.xw4"
>
<!-- BE SURE TO MODIFY project.properties AND the variable TARGET in
../scripts/setup_local_props.sh if targetSdkVersion changes!!!
-->
<supports-screens android:resizeable="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- Added for wifi-direct; don't ship until move to 23!!! -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-feature android:name="android.hardware.telephony"
android:required = "false"
/>
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<!-- GCM stuff -->
<permission android:name="${APP_ID}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${APP_ID}.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.NFC" />
<!-- for crittercism -->
<uses-permission android:name="android.permission.GET_TASKS"/>
<application android:icon="@drawable/icon48x48"
android:label="@string/app_name"
android:name=".XWApp"
android:theme="@style/AppTheme"
>
<activity android:name="MainActivity"
android:label="@string/app_name"
android:launchMode="standard"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="@string/xwords_nfc_mime" />
</intent-filter>
</activity>
<!-- NOT in the non-d version -->
<meta-data android:name="io.fabric.ApiKey" android:value="${FABRIC_API_KEY}" />
<activity android:name="DictsActivity"
android:label="@string/title_dicts_list"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="BTInviteActivity"
android:label="@string/bt_invite_title"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="SMSInviteActivity"
android:label="@string/sms_invite_title"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="sensor"
/>
<activity android:name="RelayInviteActivity"
android:label="@string/relay_invite_title"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="WiDirInviteActivity"
android:label="@string/p2p_invite_title"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="GameConfigActivity"
android:screenOrientation="sensor"
android:configChanges="keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden"
>
<intent-filter>
<action android:name="android.intent.action.EDIT" />
</intent-filter>
</activity>
<activity android:name="PrefsActivity"
android:label="@string/title_prefs"
android:screenOrientation="sensor"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="BoardActivity"
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
/>
<activity android:name="StudyListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<receiver android:name="OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<receiver android:name="RelayReceiver"/>
<receiver android:name="NagTurnReceiver"/>
<receiver android:name="UpdateCheckReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<activity android:name="DispatchNotify">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="newxwgame"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="@string/invite_host"
android:pathPrefix="@string/invite_prefix"
/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="@string/invite_mime" />
</intent-filter>
</activity>
<!-- downloading dicts -->
<activity android:name=".DwnldActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Dialog"
android:configChanges="keyboardHidden|orientation|screenSize"
>
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:scheme="file" android:host="*"
android:pathPattern=".*\\.xwd" />
<data android:scheme="http"
android:mimeType="application/x-xwordsdict"
android:host="*"
android:pathPattern=".*\\.xwd" />
</intent-filter>
</activity>
<activity android:name="DictBrowseActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name="ChatActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name=".loc.LocActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<activity android:name=".loc.LocItemEditActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
<service android:name="RelayService"/>
<service android:name="BTService"/>
<service android:name="WiDirService"/>
<service android:name="SMSService"/>
<receiver android:name=".MountEventReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<data android:scheme="file" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MEDIA_EJECT" />
<data android:scheme="file" />
</intent-filter>
</receiver>
<receiver android:name="BTReceiver">
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
</intent-filter>
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
</intent-filter>
</receiver>
<receiver android:name="SMSReceiver" >
<intent-filter>
<action android:name="android.intent.action.DATA_SMS_RECEIVED" />
<data android:scheme="sms" />
<data android:port="@string/nbs_port" />
<data android:host="*" />
</intent-filter>
</receiver>
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${APP_ID}" />
</intent-filter>
</receiver>
<service android:name=".GCMIntentService" />
</application>
</manifest>

View file

@ -1,7 +1,8 @@
#!/usr/bin/python
# Script meant to be installed on eehouse.org.
import logging, shelve, hashlib, sys, json, subprocess, glob, os, struct, random, string, psycopg2
import logging, shelve, hashlib, sys, re, json, subprocess, glob, os
import struct, random, string, psycopg2, zipfile
import mk_for_download, mygit
import xwconfig
@ -54,6 +55,10 @@ k_LANGSVERS = 'lvers'
# Version for those sticking with RELEASES
k_REL_REV = 'android_beta_98'
# newer build-info.txt file contain lines like this:
# git: android_beta_123
pat_git_tag = re.compile( 'git: (\S*)', re.DOTALL | re.MULTILINE )
# Version for those getting intermediate builds
k_suffix = '.xwd'
@ -164,20 +169,59 @@ def getDictSums():
openShelf()
return s_shelf[k_SUMS]
def getOrderedApks( path, debug ):
# logging.debug( "getOrderedApks(" + path + ")" )
apks = []
def getGitRevFor(file, repo):
result = None
zip = zipfile.ZipFile(file);
pattern = path
if debug: pattern += "/XWords4-debug-android_*.apk"
else: pattern += "/XWords4-release_*android_beta_*.apk"
try:
result = zip.read('assets/gitvers.txt').split("\n")[0]
except KeyError, err:
result = None
if not result:
try:
data = zip.read('assets/build-info.txt')
match = pat_git_tag.match(data)
if match:
tag = match.group(1)
if not 'dirty' in tag:
result = repo.tagToRev(tag)
except KeyError, err:
None
# print "getGitRevFor(", file, "->", result
return result
pat_badge_info = re.compile("package: name='([^']*)' versionCode='([^']*)' versionName='([^']*)'", re.DOTALL )
def getAAPTInfo(file):
result = None
test = subprocess.Popen(["aapt", "dump", "badging", file], shell = False, stdout = subprocess.PIPE)
for line in test.communicate():
if line:
match = pat_badge_info.match(line)
if match:
result = { 'appID' : match.group(1),
'versionCode' : int(match.group(2)),
'versionName' : match.group(3),
}
break
return result
def getOrderedApks( path, appID, debug ):
apkToCode = {}
apkToMtime = {}
if debug: pattern = path + "/*debug*.apk"
else: pattern = path + "/*release*.apk"
files = ((os.stat(apk).st_mtime, apk) for apk in glob.glob(pattern))
for mtime, file in sorted(files, reverse=True):
# logging.debug( file + ": " + str(mtime) )
apks.append( file )
return apks
info = getAAPTInfo(file)
if info['appID'] == appID:
apkToCode[file] = info['versionCode']
apkToMtime[file] = mtime
result = sorted(apkToCode.keys(), reverse=True, key=lambda file: (apkToCode[file], apkToMtime[file]))
return result
def getVariantDir( name ):
result = ''
@ -236,7 +280,7 @@ def getApp( params, name ):
# If we're a dev device, always push the latest
if k_DEBUG in params and params[k_DEBUG]:
dir = k_filebase + k_apkDir + variantDir
apks = getOrderedApks( dir, True )
apks = getOrderedApks( dir, name, True )
if 0 < len(apks):
apk = apks[0]
curApk = params[k_GVERS] + '.apk'
@ -247,7 +291,7 @@ def getApp( params, name ):
logging.debug("url: " + url)
result = {k_URL: url}
elif k_DEVOK in params and params[k_DEVOK]:
apks = getOrderedApks( k_filebase + k_apkDir, False )
apks = getOrderedApks( k_filebase + k_apkDir, name, False )
if 0 < len(apks):
apk = apks[0]
# Does path NOT contain name of installed file
@ -259,19 +303,15 @@ def getApp( params, name ):
result = {k_URL: url}
logging.debug( result )
elif k_AVERS in params and k_GVERS in params:
avers = params[k_AVERS]
elif k_GVERS in params:
gvers = params[k_GVERS]
if k_INSTALLER in params: installer = params[k_INSTALLER]
else: installer = ''
logging.debug( "name: %s; avers: %s; installer: %s; gvers: %s"
% (name, avers, installer, gvers) )
logging.debug( "name: %s; installer: %s; gvers: %s"
% (name, installer, gvers) )
if name in k_versions:
versForName = k_versions[name]
if versForName[k_AVERS] > int(avers):
result = {k_URL: k_urlbase + '/' + versForName[k_URL]}
elif k_GVERS in versForName and not gvers == versForName[k_GVERS]:
if k_GVERS in versForName and not gvers == versForName[k_GVERS]:
result = {k_URL: k_urlbase + '/' + versForName[k_URL]}
else:
logging.debug(name + " is up-to-date")
@ -521,23 +561,25 @@ def clearShelf():
for key in shelf: del shelf[key]
shelf.close()
def usage():
def usage(msg=None):
if msg: print "ERROR:", msg
print "usage:", sys.argv[0], '--get-sums [lang/dict]*'
print ' | --test-get-app app <org.eehouse.app.name> avers gvers'
print ' | --test-get-dicts name lang curSum'
print ' | --list-apks [path/to/apks]'
print ' | --list-apks [--path <path/to/apks>] [--debug] --appID org.something'
print ' | --list-dicts'
print ' | --opponent-ids-for'
print ' | --clear-shelf'
sys.exit(-1)
def main():
if 1 >= len(sys.argv): usage();
argc = len(sys.argv)
if 1 >= argc: usage();
arg = sys.argv[1]
if arg == '--clear-shelf':
clearShelf()
elif arg == '--list-dicts':
if 2 < len(sys.argv): lc = sys.argv[2]
if 2 < argc: lc = sys.argv[2]
else: lc = None
dictsJson = listDicts( lc )
print json.dumps( dictsJson )
@ -548,14 +590,13 @@ def main():
s_shelf[k_SUMS] = dictSums
closeShelf()
elif arg == '--test-get-app':
if not 5 == len(sys.argv): usage()
if not 4 == argc: usage()
params = { k_NAME: sys.argv[2],
k_AVERS: int(sys.argv[3]),
k_GVERS: sys.argv[4],
k_GVERS: sys.argv[3],
}
print getApp( params )
print getApp( params, sys.argv[2] )
elif arg == '--test-get-dicts':
if not 5 == len(sys.argv): usage()
if not 5 == argc: usage()
params = { k_NAME: sys.argv[2],
k_LANG : sys.argv[3],
k_MD5SUM : sys.argv[4],
@ -563,12 +604,19 @@ def main():
}
print getDicts( [params] )
elif arg == '--list-apks':
argc = len(sys.argv)
if argc >= 4: usage()
path = ""
if argc >= 3: path = sys.argv[2]
apks = getOrderedApks( path, False )
if 0 == len(apks): print "No apks in", path
debug = False
appID = ''
args = sys.argv[2:]
while len(args):
arg = args.pop(0)
if arg == '--appID': appID = args.pop(0)
elif arg == '--debug': debug = True
elif arg == '--path': path = args.pop(0)
if not appID: usage('--appID not optional')
apks = getOrderedApks( path, appID, debug )
if not len(apks): print "No apks in", path
else: print
for apk in apks:
print apk
elif arg == '--opponent-ids-for':

View file

@ -80,6 +80,24 @@ class GitRepo:
break
return result;
def getAllRevs( self ):
result = []
params = ['git', 'rev-list', '--reverse', 'HEAD']
out, err = self.__doProcess( params )
if err: self.__error('error from getRevsBetween')
result = None
if out:
result = out.splitlines()
return result
def tagToRev(self, tag):
result = None
params = ['git', 'rev-list', '-n', '1', tag]
out, err = self.__doProcess( params )
if err: self.__error('error from getRevsBetween')
if out: result = out.strip()
return result
def getHeadRev( self ):
return self.getRevsBetween( 'HEAD', 'HEAD' )[0]