From 5bdc473e0293107cb44c1eba290e23ca3eb4456b Mon Sep 17 00:00:00 2001
From: Eric House
Date: Wed, 6 Mar 2019 12:39:08 -0800
Subject: [PATCH] use NBSProxy if available
Lots of work dealing with banned permissions (SEND_SMS and
RECEIVE_SMS). First, if they're banned and NBSProxy is installed, just
use it as if the permissions had been granted. When it's not there,
explain at various points where users will otherwise be confused: when
they try to invite using data sms, or when they open a game that already
uses it.)
---
xwords4/android/app/build.gradle | 16 +-
.../eehouse/android/xw4/BoardDelegate.java | 177 +++++++++++++-----
.../java/org/eehouse/android/xw4/DBUtils.java | 21 ++-
.../org/eehouse/android/xw4/DelegateBase.java | 3 +
.../org/eehouse/android/xw4/DictUtils.java | 3 +-
.../org/eehouse/android/xw4/DlgDelegate.java | 3 +-
.../android/xw4/GamesListDelegate.java | 30 ++-
.../org/eehouse/android/xw4/NetUtils.java | 15 +-
.../java/org/eehouse/android/xw4/Perms23.java | 129 ++++++++-----
.../org/eehouse/android/xw4/RelayService.java | 12 +-
.../org/eehouse/android/xw4/SMSService.java | 23 ++-
.../android/xw4/UpdateCheckReceiver.java | 41 ++--
.../java/org/eehouse/android/xw4/Utils.java | 6 +-
.../java/org/eehouse/android/xw4/XWApp.java | 30 ++-
.../eehouse/android/xw4/jni/DUtilCtxt.java | 6 +-
.../app/src/main/res/values/common_rsrc.xml | 3 +-
.../app/src/main/res/values/strings.xml | 13 +-
.../android/res_src/values-ba_CK/strings.xml | 2 +-
.../android/res_src/values-ca_PS/strings.xml | 2 +-
xwords4/android/res_src/values-de/strings.xml | 4 +-
xwords4/android/res_src/values-fr/strings.xml | 4 +-
xwords4/android/res_src/values-ja/strings.xml | 4 +-
.../android/res_src/values-nb-rNO/strings.xml | 4 +-
xwords4/android/scripts/info.py | 4 +
xwords4/android/website/sms.html | 60 ++++++
25 files changed, 445 insertions(+), 170 deletions(-)
create mode 100644 xwords4/android/website/sms.html
diff --git a/xwords4/android/app/build.gradle b/xwords4/android/app/build.gradle
index e02b2cf64..ecc997bb4 100644
--- a/xwords4/android/app/build.gradle
+++ b/xwords4/android/app/build.gradle
@@ -47,6 +47,8 @@ android {
// variant.buildConfigField "String", "FIELD_NAME", "\"my String\""
variant.buildConfigField "String", "FABRIC_API_KEY", "\"$FABRIC_API_KEY\""
+ // We need both of these because xwprefs.xml can't reference
+ // the BuildConfig constant
resValue "string", "git_rev", "$GITREV"
variant.buildConfigField "String", "GIT_REV", "\"$GITREV\""
@@ -68,9 +70,9 @@ android {
buildConfigField "String", "BUILD_INFO_NAME", "\"${BUILD_INFO_NAME}\""
buildConfigField "boolean", "IS_TAGGED_BUILD", "${CURTAG}" == '' ? "false" : "true"
resValue "string", "invite_prefix", "/and/"
- buildConfigField "int[]", "SMS_BANNED_EXPL", "null"
- buildConfigField "boolean", "UDP_ENABLED", "true"
- buildConfigField "boolean", "REPORT_LOCKS", "false"
+ buildConfigField "boolean", "SMS_BANNED", "false"
+ buildConfigField "boolean", "UDP_ENABLED", "true"
+ buildConfigField "boolean", "REPORT_LOCKS", "false"
}
xw4 {
@@ -82,8 +84,7 @@ android {
buildConfigField "boolean", "WIDIR_ENABLED", "false"
buildConfigField "boolean", "RELAYINVITE_SUPPORTED", "false"
buildConfigField "String", "VARIANT_NAME", "\"Google Play Store\""
- buildConfigField "int[]", "SMS_BANNED_EXPL", "null"
- // "{R.string.key_notagain_sms_banned, R.string.sms_banned_expl}"
+ buildConfigField "boolean", "SMS_BANNED", "false"
}
xw4fdroid {
@@ -117,8 +118,7 @@ android {
buildConfigField "boolean", "WIDIR_ENABLED", "true"
buildConfigField "boolean", "RELAYINVITE_SUPPORTED", "true"
buildConfigField "String", "VARIANT_NAME", "\"Dev/Debug sans SMS\""
- buildConfigField "int[]", "SMS_BANNED_EXPL", "null"
- // "{R.string.key_notagain_sms_banned, R.string.sms_banned_expl}"
+ buildConfigField "boolean", "SMS_BANNED", "true"
}
// WARNING: "all" breaks things. Seems to be a keyword. Need
@@ -246,7 +246,7 @@ dependencies {
implementation 'com.google.firebase:firebase-messaging:17.3.4' // rm-for-fdroid
implementation 'com.google.firebase:firebase-core:16.0.6' // rm-for-fdroid
- implementation 'com.github.eehouse:nbsproxy:v0.0.2'
+ implementation 'com.github.eehouse:nbsproxy:v0.0.5'
}
task mkImages(type: Exec) {
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java
index 9d8572e32..3fef34caf 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java
@@ -46,10 +46,8 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
-import java.util.Map;
import java.util.Set;
-
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
@@ -147,6 +145,16 @@ public class BoardDelegate extends DelegateBase
}
}
+
+ // Quick hack to manage a series of alerts meant to be presented
+ // one-at-a-time in order. Each tests whether it's its turn, and if so
+ // checks its conditions for being shown (e.g. NO_MEANS for no way to
+ // communicate). If the conditions aren't met (no need to show alert), it
+ // just sets the StartAlertOrder ivar to the next value and
+ // exits. Otherwise it needs to ensure that however the alert it's posting
+ // exits that ivar is incremented as well.
+ private static enum StartAlertOrder { NBS_PERMS, NO_MEANS, INVITE, DONE, };
+
private static class MySIS implements Serializable {
MySIS() { nMissing = -1; }
String toastStr;
@@ -155,9 +163,22 @@ public class BoardDelegate extends DelegateBase
int nMissing;
int nAlerts;
boolean inTrade;
+ StartAlertOrder mAlertOrder = StartAlertOrder.values()[0];
}
private MySIS m_mySIS;
+ private boolean alertOrderAt( StartAlertOrder ord )
+ {
+ return m_mySIS.mAlertOrder == ord;
+ }
+
+ private void alertOrderIncrIfAt( StartAlertOrder ord )
+ {
+ if ( alertOrderAt( ord ) ) {
+ m_mySIS.mAlertOrder = ord.values()[ord.ordinal() + 1];
+ }
+ }
+
@Override
protected Dialog makeDialog( DBAlert alert, Object[] params )
{
@@ -694,6 +715,7 @@ public class BoardDelegate extends DelegateBase
setBackgroundColor();
setKeepScreenOn();
} else {
+ warnIfNoTransport();
showInviteAlertIf();
}
}
@@ -1063,6 +1085,7 @@ public class BoardDelegate extends DelegateBase
deleteAndClose();
break;
case DROP_SMS_ACTION: // do nothing; work done in onNegButton case
+ alertOrderIncrIfAt( StartAlertOrder.NBS_PERMS );
break;
case INVITE_SMS_DATA:
@@ -1157,6 +1180,20 @@ public class BoardDelegate extends DelegateBase
case ASKED_PHONE_STATE:
showInviteChoicesThen( params );
break;
+ case INVITE_SMS_DATA:
+ Perms23.Perm[] perms = (Perms23.Perm[])params[2];
+ if ( Perms23.bannedWithWorkaround( m_activity, perms ) ) {
+ int nMissing = (Integer)params[0];
+ SentInvitesInfo info = (SentInvitesInfo)params[1];
+ launchPhoneNumberInvite( nMissing, info, InviteMeans.SMS_DATA,
+ RequestCode.SMS_DATA_INVITE_RESULT );
+ } else if ( Perms23.anyBanned( perms ) ) {
+ makeOkOnlyBuilder( R.string.sms_banned_ok_only )
+ .setActionPair(new ActionPair( Action.PERMS_BANNED_INFO,
+ R.string.button_more_info ) )
+ .show();
+ }
+ break;
default:
handled = super.onNegButton( action, params );
}
@@ -1222,10 +1259,10 @@ public class BoardDelegate extends DelegateBase
RequestCode.BT_INVITE_RESULT );
break;
case SMS_DATA:
- Perms23.tryGetPerms( this, new Perm[] { Perm.SEND_SMS,
- Perm.RECEIVE_SMS },
- R.string.sms_invite_rationale,
- Action.INVITE_SMS_DATA, m_mySIS.nMissing, info );
+ Perm[] perms = { Perm.SEND_SMS, Perm.RECEIVE_SMS };
+ Perms23.tryGetPerms( this, perms, R.string.sms_invite_rationale,
+ Action.INVITE_SMS_DATA, m_mySIS.nMissing,
+ info, perms );
break;
case SMS_USER: // like an email invite, but we want the phone #
launchPhoneNumberInvite( m_mySIS.nMissing, info, means,
@@ -2211,7 +2248,7 @@ public class BoardDelegate extends DelegateBase
Utils.cancelNotification( m_activity, (int)m_rowid );
- askPermissions();
+ askNBSPermissions();
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
warnIfNoTransport();
@@ -2221,23 +2258,51 @@ public class BoardDelegate extends DelegateBase
}
} // resumeGame
- private void askPermissions()
+ private void askNBSPermissions()
{
- if ( m_summary.conTypes.contains( CommsConnType.COMMS_CONN_SMS )
- && null == m_permCbck ) { // already asked?
- m_permCbck = new Perms23.PermCbck() {
- @Override
- public void onPermissionResult( Map perms ) {
- if ( ! perms.get(Perm.SEND_SMS) ) {
- makeConfirmThenBuilder( R.string.missing_perms,
- Action.DROP_SMS_ACTION )
- .setNegButton(R.string.remove_sms)
- .show();
+ final StartAlertOrder thisOrder = StartAlertOrder.NBS_PERMS;
+ if ( alertOrderAt( thisOrder ) // already asked?
+ && m_summary.conTypes.contains( CommsConnType.COMMS_CONN_SMS ) ) {
+ Perm[] nbsPerms = { Perm.SEND_SMS, Perm.RECEIVE_SMS };
+ if ( Perms23.havePermissions( m_activity, nbsPerms ) ) {
+ // We have them or a workaround; cool! proceed
+ alertOrderIncrIfAt( thisOrder );
+ } else {
+ // Make sure these can be treated the same!!!
+ Assert.assertTrue( nbsPerms.length == 2 &&
+ nbsPerms[0].isBanned() == nbsPerms[1].isBanned() );
+
+ m_permCbck = new Perms23.PermCbck() {
+ @Override
+ public void onPermissionResult( boolean allGood,
+ Map perms )
+ {
+ if ( allGood ) {
+ // Yay! nothing to do
+ alertOrderIncrIfAt( thisOrder );
+ } else {
+ Log.d( TAG, "askNBSPermissions(): posting alert" );
+ boolean banned = Perm.SEND_SMS.isBanned();
+ int explID = banned
+ ? R.string.banned_nbs_perms : R.string.missing_sms_perms;
+ DlgDelegate.ConfirmThenBuilder builder =
+ makeConfirmThenBuilder( explID, Action.DROP_SMS_ACTION );
+ if ( banned ) {
+ ActionPair pr = new ActionPair( Action.PERMS_BANNED_INFO,
+ R.string.button_more_info );
+ builder.setActionPair( pr );
+ }
+ builder.setNegButton( R.string.remove_sms )
+ .show();
+ Log.d( TAG, "askNBSPermissions(): DONE posting alert" );
+ }
}
- }
- };
- new Perms23.Builder(Perm.SEND_SMS)
- .asyncQuery( m_activity, m_permCbck );
+ };
+ new Perms23.Builder( nbsPerms )
+ .asyncQuery( m_activity, m_permCbck );
+ }
+ } else {
+ alertOrderIncrIfAt( thisOrder );
}
}
@@ -2385,17 +2450,21 @@ public class BoardDelegate extends DelegateBase
private void showInviteAlertIf()
{
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;
- ias.relayMissing = m_relayMissing;
- ias.connTypes = m_connTypes;
- ias.rowid = m_rowid;
- ias.nMissing = m_mySIS.nMissing;
- showDialogFragment( DlgID.DLG_INVITE, ias );
- m_haveStartedShowing = true;
+ if ( alertOrderAt( StartAlertOrder.INVITE ) ) {
+ if ( ! m_haveStartedShowing && null == m_inviteAlert
+ && m_mySIS.nMissing > 0 && !isFinishing() ) {
+ InviteAlertState ias = new InviteAlertState();
+ ias.summary = m_summary;
+ ias.gi = m_gi;
+ ias.relayMissing = m_relayMissing;
+ ias.connTypes = m_connTypes;
+ ias.rowid = m_rowid;
+ ias.nMissing = m_mySIS.nMissing;
+ showDialogFragment( DlgID.DLG_INVITE, ias );
+ m_haveStartedShowing = true;
+ } else {
+ alertOrderIncrIfAt( StartAlertOrder.INVITE );
+ }
}
}
@@ -2455,24 +2524,32 @@ public class BoardDelegate extends DelegateBase
private void warnIfNoTransport()
{
- if ( m_connTypes.contains( CommsConnType.COMMS_CONN_SMS ) ) {
- if ( !XWPrefs.getNBSEnabled( m_activity ) ) {
- makeConfirmThenBuilder( R.string.warn_sms_disabled,
- Action.ENABLE_NBS_ASK )
- .setPosButton( R.string.button_enable_sms )
- .setNegButton( R.string.button_later )
- .show();
+ if ( alertOrderAt( StartAlertOrder.NO_MEANS ) ) {
+ if ( m_connTypes.contains( CommsConnType.COMMS_CONN_SMS ) ) {
+ if ( !XWPrefs.getNBSEnabled( m_activity ) ) {
+ makeConfirmThenBuilder( R.string.warn_sms_disabled,
+ Action.ENABLE_NBS_ASK )
+ .setPosButton( R.string.button_enable_sms )
+ .setNegButton( R.string.button_later )
+ .show();
+ }
}
- }
- if ( m_connTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
- if ( !RelayService.relayEnabled( m_activity ) ) {
- m_dropRelayOnDismiss = false;
- String msg = getString( R.string.warn_relay_disabled )
- + "\n\n" + getString( R.string.warn_relay_remove );
- makeConfirmThenBuilder( msg, Action.ENABLE_RELAY_DO_OR )
- .setPosButton( R.string.button_enable_relay )
- .setNegButton( R.string.newgame_drop_relay )
- .show();
+ if ( m_connTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
+ if ( !RelayService.relayEnabled( m_activity ) ) {
+ m_dropRelayOnDismiss = false;
+ String msg = getString( R.string.warn_relay_disabled )
+ + "\n\n" + getString( R.string.warn_relay_remove );
+ makeConfirmThenBuilder( msg, Action.ENABLE_RELAY_DO_OR )
+ .setPosButton( R.string.button_enable_relay )
+ .setNegButton( R.string.newgame_drop_relay )
+ .show();
+ }
+ }
+
+ if ( m_connTypes.isEmpty() ) {
+ askNoAddrsDelete();
+ } else {
+ alertOrderIncrIfAt( StartAlertOrder.NO_MEANS );
}
}
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java
index 0842a6407..b4f0f2e26 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBUtils.java
@@ -433,7 +433,8 @@ public class DBUtils {
return result;
}
- public static int countOpenGamesUsingRelay( Context context )
+ private static int countOpenGamesUsing( Context context,
+ CommsConnType connTyp )
{
int result = 0;
String[] columns = { DBHelper.CONTYPE };
@@ -446,16 +447,28 @@ public class DBUtils {
int indx = cursor.getColumnIndex( DBHelper.CONTYPE );
while ( cursor.moveToNext() ) {
CommsConnTypeSet typs = new CommsConnTypeSet( cursor.getInt(indx) );
- if ( typs.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
+ if ( typs.contains( connTyp ) ) {
++result;
}
}
-
cursor.close();
}
- // Log.d( TAG, "countOpenGamesUsingRelay() => %d", result );
+ // Log.d( TAG, "countOpenGamesUsing(%s) => %d", connTyp, result );
+ return result;
+ }
+
+ public static int countOpenGamesUsingRelay( Context context )
+ {
+ int result = countOpenGamesUsing( context, CommsConnType.COMMS_CONN_RELAY );
+ return result;
+ }
+
+ public static int countOpenGamesUsingNBS( Context context )
+ {
+ int result = countOpenGamesUsing( context, CommsConnType.COMMS_CONN_SMS );
+ // Log.d( TAG, "countOpenGamesUsingNBS() => %d", result );
return result;
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
index e8e9b8d46..48ad74048 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
@@ -755,6 +755,9 @@ public class DelegateBase implements DlgClickNotify,
case PERMS_QUERY:
Perms23.onGotPermsAction( this, true, params );
break;
+ case PERMS_BANNED_INFO:
+ NetUtils.launchWebBrowserWith( m_activity, R.string.nbs_ban_url );
+ break;
default:
Log.d( TAG, "unhandled action %s", action.toString() );
// Assert.assertTrue( !BuildConfig.DEBUG );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictUtils.java
index f0696a49a..5b835f0c2 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DictUtils.java
@@ -181,7 +181,8 @@ public class DictUtils {
// Note: if STORAGE permission is changed the set being returned here
// will change. Might want to check for that and invalidate this list
// if it's changed.
- boolean haveStorage = Perms23.havePermission( Perms23.Perm.STORAGE );
+ boolean haveStorage = Perms23.havePermissions( context,
+ Perms23.Perm.STORAGE );
boolean permsChanged = null == s_hadStorage
|| haveStorage != s_hadStorage;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
index 1d70e699f..79cce8a34 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
@@ -124,11 +124,12 @@ public class DlgDelegate {
DISABLE_RELAY_DO,
ASKED_PHONE_STATE,
PERMS_QUERY,
+ PERMS_BANNED_INFO,
// Sent when not-again checkbox checked
SET_NA_DEFAULTNAME,
SET_GOT_LANGDICT,
- }
+ } // Action enum
public static class ActionPair implements Serializable {
public ActionPair( Action act, int str ) {
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
index e83c88e6b..e3df1d8b2 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
@@ -44,7 +44,6 @@ import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
-
import org.eehouse.android.xw4.DBUtils.GameChangeType;
import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
@@ -944,6 +943,7 @@ public class GamesListDelegate extends ListDelegateBase
@Override
protected void init( Bundle savedInstanceState )
{
+ boolean isFirstLaunch = null == savedInstanceState;
m_origTitle = getTitle();
m_handler = new Handler();
@@ -995,8 +995,12 @@ public class GamesListDelegate extends ListDelegateBase
// asking (OS will grant without user interaction) since they're in
// the same group. So just do it now. This code can be removed
// later...
- if ( Perms23.havePermission( Perm.SEND_SMS ) ) {
- Perms23.tryGetPerms( this, Perm.RECEIVE_SMS, 0, Action.SKIP_CALLBACK );
+ if ( !Perm.RECEIVE_SMS.isBanned() ) {
+ if ( Perms23.havePermissions( m_activity, Perm.SEND_SMS ) ) {
+ Perms23.tryGetPerms( this, Perm.RECEIVE_SMS, 0, Action.SKIP_CALLBACK );
+ }
+ } else if ( isFirstLaunch ) {
+ warnSMSBannedIf();
}
} // init
@@ -1060,6 +1064,23 @@ public class GamesListDelegate extends ListDelegateBase
}
}
+ private void warnSMSBannedIf()
+ {
+ if ( !Perms23.havePermissions( m_activity, Perm.SEND_SMS, Perm.RECEIVE_SMS )
+ && Perm.SEND_SMS.isBanned() ) {
+ int smsGameCount = DBUtils.countOpenGamesUsingNBS( m_activity );
+ if ( 0 < smsGameCount ) {
+ String msg = LocUtils.getString( m_activity,
+ R.string.not_again_nbsGamesOnUpgrade,
+ smsGameCount );
+ makeNotAgainBuilder( msg, R.string.key_notagain_nbsGamesOnUpgrade )
+ .setActionPair( new ActionPair( Action.PERMS_BANNED_INFO,
+ R.string.button_more_info ) )
+ .show();
+ }
+ }
+ }
+
private void moveGroup( long groupID, boolean moveUp )
{
m_adapter.moveGroup( groupID, moveUp );
@@ -1448,8 +1469,7 @@ public class GamesListDelegate extends ListDelegateBase
showItemsIf( ONEGAME_ITEMS, menu, 1 == nGamesSelected );
showItemsIf( ONEGROUP_ITEMS, menu, 1 == nGroupsSelected );
- boolean enable = showDbg && nothingSelected
- && UpdateCheckReceiver.haveToCheck( m_activity );
+ boolean enable = showDbg && nothingSelected;
Utils.setItemVisible( menu, R.id.games_menu_checkupdates, enable );
int selGroupPos = -1;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java
index f79df6abb..a7f4e458c 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetUtils.java
@@ -21,9 +21,10 @@
package org.eehouse.android.xw4;
import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
import android.text.TextUtils;
-
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -203,6 +204,18 @@ public class NetUtils {
return result;
}
+ public static void launchWebBrowserWith( Context context, int uriResID )
+ {
+ String uri = context.getString( uriResID );
+ launchWebBrowserWith( context, uri );
+ }
+
+ public static void launchWebBrowserWith( Context context, String uri )
+ {
+ Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse(uri) );
+ context.startActivity( intent );
+ }
+
protected static HttpsURLConnection makeHttpsRelayConn( Context context,
String proc )
{
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Perms23.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Perms23.java
index 442da6147..eb71df637 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Perms23.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Perms23.java
@@ -35,6 +35,8 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import org.eehouse.android.nbsplib.NBSProxy;
+
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
@@ -47,21 +49,20 @@ public class Perms23 {
public static enum Perm {
READ_PHONE_STATE(Manifest.permission.READ_PHONE_STATE),
STORAGE(Manifest.permission.WRITE_EXTERNAL_STORAGE),
- SEND_SMS(Manifest.permission.SEND_SMS, BuildConfig.SMS_BANNED_EXPL),
- RECEIVE_SMS(Manifest.permission.RECEIVE_SMS, BuildConfig.SMS_BANNED_EXPL),
+ SEND_SMS(Manifest.permission.SEND_SMS, BuildConfig.SMS_BANNED),
+ RECEIVE_SMS(Manifest.permission.RECEIVE_SMS, BuildConfig.SMS_BANNED),
READ_CONTACTS(Manifest.permission.READ_CONTACTS);
private String m_str;
- private int[] m_expl;
- private Perm(String str) { this(str, null); }
- private Perm(String str, int[] bannedExpl) {
+ private boolean m_banned;
+ private Perm(String str) { this(str, false); }
+ private Perm(String str, boolean banned) {
m_str = str;
- m_expl = bannedExpl;
+ m_banned = banned;
}
public String getString() { return m_str; }
- public boolean isBanned() { return m_expl != null; }
- public int[] getExpl() { Assert.assertTrue(isBanned()); return m_expl; }
+ public boolean isBanned() { return m_banned; }
public static Perm getFor( String str ) {
Perm result = null;
for ( Perm one : Perm.values() ) {
@@ -75,7 +76,7 @@ public class Perms23 {
}
public interface PermCbck {
- void onPermissionResult( Map perms );
+ void onPermissionResult( boolean allGood, Map perms );
}
public interface OnShowRationale {
void onShouldShowRationale( Set perms );
@@ -89,21 +90,12 @@ public class Perms23 {
m_perms.addAll( perms );
}
- public Builder( Perm[] perms ) {
+ public Builder( Perm... perms ) {
for ( Perm perm : perms ) {
m_perms.add( perm );
}
}
- public Builder( Perm perm ) {
- m_perms.add( perm );
- }
-
- public Builder add( Perm perm ) {
- m_perms.add( perm );
- return this;
- }
-
public Builder setOnShowRationale( OnShowRationale onShow )
{
m_onShow = onShow;
@@ -115,6 +107,11 @@ public class Perms23 {
asyncQuery( activity, null );
}
+ // We have set of permissions. For any of them that needs asking (not
+ // granted AND not banned) start an ask.
+ //
+ // PENDING: I suspect this'll crash if I ask for a banned and
+ // non-banned at the same time (and don't have either)
public void asyncQuery( Activity activity, PermCbck cbck )
{
Log.d( TAG, "asyncQuery(%s)", m_perms.toString() );
@@ -125,10 +122,13 @@ public class Perms23 {
ArrayList askStrings = new ArrayList();
for ( Perm perm : m_perms ) {
String permStr = perm.getString();
- boolean haveIt = PackageManager.PERMISSION_GRANTED
+ boolean haveIt = perm.isBanned() || PackageManager.PERMISSION_GRANTED
== ContextCompat.checkSelfPermission( activity, permStr );
if ( !haveIt ) {
+ // do not pass banned perms to the OS! They're not in
+ // AndroidManifest.xml so may crash on some devices
+ Assert.assertFalse( perm.isBanned() );
askStrings.add( permStr );
if ( null != m_onShow && ActivityCompat
@@ -144,10 +144,13 @@ public class Perms23 {
if ( haveAll ) {
if ( null != cbck ) {
Map map = new HashMap<>();
+ boolean allGood = true;
for ( Perm perm : m_perms ) {
- map.put( perm, true );
+ boolean banned = perm.isBanned();
+ map.put( perm, !banned );
+ allGood = allGood & !banned;
}
- cbck.onPermissionResult( map );
+ cbck.onPermissionResult( allGood, map );
}
} else if ( 0 < needShow.size() && null != m_onShow ) {
// Log.d( TAG, "calling onShouldShowRationale()" );
@@ -228,18 +231,10 @@ public class Perms23 {
}
builder.asyncQuery( m_delegate.getActivity(), new PermCbck() {
@Override
- public void onPermissionResult( Map permsMap ) {
+ public void onPermissionResult( boolean allGood,
+ Map permsMap ) {
if ( Action.SKIP_CALLBACK != m_action ) {
-
- // We need all the sought perms to have been granted
- boolean allGranted = true;
- Iterator iter = permsMap.keySet().iterator();
- while ( allGranted && iter.hasNext() ) {
- Perm perm = iter.next();
- allGranted = allGranted && permsMap.get( perm );
- }
-
- if ( allGranted ) {
+ if ( allGood ) {
m_delegate.onPosButton( m_action, m_params );
} else {
m_delegate.onNegButton( m_action, m_params );
@@ -252,16 +247,11 @@ public class Perms23 {
// Cons up a call with a "no" answer, and post it.
private void doItFail( Set bannedPerms )
{
- int resID = 0;
-
- final Perm[] perms = bannedPerms.toArray( new Perm[bannedPerms.size()] );
- int[] expls = perms[0].getExpl();
- m_delegate.makeNotAgainBuilder(expls[1], expls[0]).show();
-
m_delegate.post( new Runnable() {
@Override
public void run() {
- m_delegate.onNegButton( m_action, perms );
+ Log.d( TAG, "doItFail(); passing perms to onNegButton(%s)", m_action );
+ m_delegate.onNegButton( m_action, m_params );
}
} );
}
@@ -338,11 +328,14 @@ public class Perms23 {
String[] perms, int[] granteds )
{
// Log.d( TAG, "gotPermissionResult(%s)", perms.toString() );
- Map result = new HashMap();
+ Map result = new HashMap<>();
boolean shouldResend = false;
+ boolean allGood = true;
for ( int ii = 0; ii < perms.length; ++ii ) {
Perm perm = Perm.getFor( perms[ii] );
+ Assert.assertTrue( !perm.isBanned() || ! BuildConfig.DEBUG );
boolean granted = PackageManager.PERMISSION_GRANTED == granteds[ii];
+ allGood = allGood && granted;
result.put( perm, granted );
// Hack. If SMS has been granted, resend all moves. This should be
@@ -364,16 +357,58 @@ public class Perms23 {
PermCbck cbck = s_map.remove( code );
if ( null != cbck ) {
- cbck.onPermissionResult( result );
+ cbck.onPermissionResult( allGood, result );
}
}
- public static boolean havePermission( Perm perm )
+ public static boolean havePermissions( Context context, Perm... perms )
{
- String permString = perm.getString();
- boolean result = PackageManager.PERMISSION_GRANTED
- == ContextCompat.checkSelfPermission( XWApp.getContext(), permString );
- // Log.d( TAG, "havePermission(%s) => %b", permString, result );
+ boolean result = true;
+ for ( int ii = 0; result && ii < perms.length; ++ii ) {
+ Perm perm = perms[ii];
+ boolean thisResult;
+ if ( perm.isBanned() ) {
+ thisResult = bannedWithWorkaround( context, perm );
+ } else {
+ thisResult = PackageManager.PERMISSION_GRANTED
+ == ContextCompat.checkSelfPermission( XWApp.getContext(),
+ perm.getString() );
+ }
+ result = result && thisResult;
+ }
+ return result;
+ }
+
+ static boolean anyBanned( Perms23.Perm... perms )
+ {
+ boolean anyBanned = false;
+ for ( int ii = 0; !anyBanned && ii < perms.length; ++ii ) {
+ anyBanned = perms[ii].isBanned();
+ }
+ return anyBanned;
+ }
+
+ static boolean bannedWithWorkaround( Context context, Perms23.Perm... perms )
+ {
+ boolean allBanned = true;
+ boolean workaroundKnown = true;
+ for ( Perms23.Perm perm : perms ) {
+ allBanned = allBanned && perm.isBanned();
+
+ switch ( perm ) {
+ case SEND_SMS:
+ case RECEIVE_SMS:
+ workaroundKnown = workaroundKnown && NBSProxy.isInstalled( context );
+ break;
+ default:
+ Log.e( TAG, "bannedWithWorkaround(): unexpected perm %s", perm );
+ Assert.assertFalse( BuildConfig.DEBUG );
+ break;
+ }
+ }
+
+ boolean result = allBanned && workaroundKnown;
+ Log.d( TAG, "bannedWithWorkaround() => %b", result );
return result;
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
index 1cd5f1a94..0a82085de 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
@@ -557,8 +557,8 @@ public class RelayService extends XWJIService
for ( outData = s_queue.poll( ts, TimeUnit.MILLISECONDS );
null != outData;
outData = s_queue.poll() ) { // doesn't block
- Log.d( TAG, "removed packet from queue (%d left): %s",
- s_queue.size(), outData );
+ // Log.d( TAG, "removed packet from queue (%d left): %s",
+ // s_queue.size(), outData );
if ( outData == sEOQPacket ) {
shouldGoOn = false;
break;
@@ -691,7 +691,7 @@ public class RelayService extends XWJIService
int sentLen = 0;
if ( packets.size() > 0 ) {
- Log.d( TAG, "sendViaUDP(): sending %d at once", packets.size() );
+ // Log.d( TAG, "sendViaUDP(): sending %d at once", packets.size() );
final RelayService service = this;
service.noteSent( packets, true );
for ( PacketData packet : packets ) {
@@ -1155,9 +1155,9 @@ public class RelayService extends XWJIService
byte proto = dis.readByte();
if ( XWPDevProto.XWPDEV_PROTO_VERSION_1.ordinal() == proto ) {
int packetID = vli2un( dis );
- if ( 0 != packetID ) {
- Log.d( TAG, "readHeader(): got packetID %d", packetID );
- }
+ // if ( 0 != packetID ) {
+ // Log.d( TAG, "readHeader(): got packetID %d", packetID );
+ // }
byte ordinal = dis.readByte();
XWRelayReg cmd = XWRelayReg.values()[ordinal];
result = new PacketHeader( cmd, packetID );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java
index 142173ed9..60351ddf0 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java
@@ -376,8 +376,9 @@ public class SMSService extends XWJIService {
private void sendDiedPacket( String phone, int gameID )
{
- if ( !s_sentDied.contains(gameID) ) {
+ if ( !s_sentDied.contains( gameID ) ) {
resendFor( phone, SMS_CMD.DEATH, gameID, null );
+ s_sentDied.add( gameID );
}
}
@@ -515,19 +516,25 @@ public class SMSService extends XWJIService {
short nbsPort = getNBSPort();
try {
SmsManager mgr = SmsManager.getDefault();
- PendingIntent sent = makeStatusIntent( MSG_SENT );
- PendingIntent delivery = makeStatusIntent( MSG_DELIVERED );
+ boolean useProxy = Perms23.Perm.SEND_SMS.isBanned()
+ && NBSProxy.isInstalled( this );
+ PendingIntent sent = useProxy ? null : makeStatusIntent( MSG_SENT );
+ PendingIntent delivery = useProxy ? null : makeStatusIntent( MSG_DELIVERED );
for ( byte[] fragment : fragments ) {
- mgr.sendDataMessage( phone, null, nbsPort, fragment, sent,
- delivery );
- Log.i( TAG, "sendBuffers(): sent %d byte fragment to %s",
- fragment.length, phone );
+ if ( useProxy ) {
+ NBSProxy.send( this, phone, nbsPort, fragment );
+ } else {
+ mgr.sendDataMessage( phone, null, nbsPort, fragment,
+ sent, delivery );
+ }
+ // Log.i( TAG, "sendBuffers(): sent %d byte fragment to %s",
+ // fragment.length, phone );
}
success = true;
} catch ( IllegalArgumentException iae ) {
Log.w( TAG, "sendBuffers(%s): %s", phone, iae.toString() );
} catch ( NullPointerException npe ) {
- Assert.fail(); // shouldn't be trying to do this!!!
+ Assert.assertFalse( BuildConfig.DEBUG ); // shouldn't be trying to do this!!!
} catch ( java.lang.SecurityException se ) {
mHelper.postEvent( MultiEvent.SMS_SEND_FAILED_NOPERMISSION );
} catch ( Exception ee ) {
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/UpdateCheckReceiver.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/UpdateCheckReceiver.java
index 4e4d715b0..485cc7c42 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/UpdateCheckReceiver.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/UpdateCheckReceiver.java
@@ -52,6 +52,7 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
// constants that are also used in info.py
private static final String k_NAME = "name";
private static final String k_AVERS = "avers";
+ private static final String k_VARIANT = "variant";
private static final String k_GVERS = "gvers";
private static final String k_GHASH = "ghash";
private static final String k_INSTALLER = "installer";
@@ -66,6 +67,8 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
private static final String k_DEBUG = "dbg";
private static final String k_XLATEINFO = "xlatinfo";
private static final String k_STRINGSHASH = "strings";
+ private static final String k_UPGRADE_TITLE = "title";
+ private static final String k_UPGRADE_BODY = "body";
@Override
public void onReceive( Context context, Intent intent )
@@ -99,16 +102,6 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
interval_millis, pi );
}
- // Is app upgradeable OR have we installed any dicts?
- public static boolean haveToCheck( Context context )
- {
- boolean result = !Utils.isGooglePlayApp( context );
- if ( !result ) { // give another chance
- result = null != getDownloadedDicts( context );
- }
- return result;
- }
-
public static void checkVersions( Context context, boolean fromUI )
{
JSONObject params = new JSONObject();
@@ -123,17 +116,22 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
}
// App update
- if ( BuildConfig.FOR_FDROID || Utils.isGooglePlayApp( context ) ) {
- // Do nothing; can't or mustn't upgrade app
+ if ( BuildConfig.FOR_FDROID ) {
+ // Do nothing; can't upgrade app
} else {
String installer = pm.getInstallerPackageName( packageName );
+ if ( null == installer ) {
+ installer = "none";
+ }
try {
JSONObject appParams = new JSONObject();
+ appParams.put( k_VARIANT, BuildConfig.VARIANT_NAME );
appParams.put( k_AVERS, versionCode );
+ // Look at whether server needs these duplicates. PENDING....
appParams.put( k_GVERS, BuildConfig.GIT_REV );
- appParams.put( k_GHASH, context.getString( R.string.git_rev ) );
+ appParams.put( k_GHASH, BuildConfig.GIT_REV );
appParams.put( k_INSTALLER, installer );
if ( devOK( context ) ) {
appParams.put( k_DEVOK, true );
@@ -315,12 +313,17 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
.makeAppDownloadIntent( m_context, url );
}
- String title =
- LocUtils.getString( m_context, R.string.new_app_avail_fmt,
- label );
- String body =
- LocUtils.getString( m_context,
- R.string.new_app_avail );
+ // title and/or body might be in the reply
+ String title = app.optString( k_UPGRADE_TITLE, null );
+ if ( null == title ) {
+ title = LocUtils
+ .getString( m_context, R.string.new_app_avail_fmt, label );
+ }
+ String body = app.optString( k_UPGRADE_BODY, null );
+ if ( null == body ) {
+ body = LocUtils
+ .getString( m_context, R.string.new_app_avail );
+ }
Utils.postNotification( m_context, intent, title,
body, url.hashCode() );
gotOne = true;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
index a7c9d382c..dbdb08516 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
@@ -125,7 +125,7 @@ public class Utils {
public static boolean isGSMPhone( Context context )
{
boolean result = false;
- if ( Perms23.havePermission( Perm.READ_PHONE_STATE ) ) {
+ if ( Perms23.havePermissions( context, Perm.READ_PHONE_STATE ) ) {
SMSService.SMSPhoneInfo info = SMSService.getPhoneInfo( context );
result = null != info && info.isPhone && info.isGSM;
}
@@ -141,7 +141,7 @@ public class Utils {
public static boolean deviceSupportsNBS( Context context )
{
boolean result = false;
- if ( Perms23.havePermission( Perm.READ_PHONE_STATE ) ) {
+ if ( Perms23.havePermissions( context, Perm.READ_PHONE_STATE ) ) {
TelephonyManager tm = (TelephonyManager)
context.getSystemService( Context.TELEPHONY_SERVICE );
if ( null != tm ) {
@@ -314,7 +314,7 @@ public class Utils {
synchronized ( s_phonesHash ) {
if ( s_phonesHash.containsKey( phone ) ) {
name = s_phonesHash.get( phone );
- } else if ( Perms23.havePermission( Perm.READ_CONTACTS ) ) {
+ } else if ( Perms23.havePermissions( context, Perm.READ_CONTACTS ) ) {
try {
ContentResolver contentResolver = context
.getContentResolver();
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
index 2067ba783..869d6b7ce 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
@@ -40,7 +40,8 @@ import java.util.UUID;
import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
-public class XWApp extends Application implements LifecycleObserver {
+public class XWApp extends Application
+ implements LifecycleObserver, NBSProxy.Callbacks {
private static final String TAG = XWApp.class.getSimpleName();
public static final boolean BTSUPPORTED = true;
@@ -62,6 +63,8 @@ public class XWApp extends Application implements LifecycleObserver {
private static Boolean s_onEmulator = null;
private static Context s_context = null;
+ private short mPort;
+
@Override
public void onCreate()
{
@@ -72,7 +75,7 @@ public class XWApp extends Application implements LifecycleObserver {
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
android.util.Log.i( TAG, "onCreate(); git_rev="
- + getString( R.string.git_rev ) );
+ + BuildConfig.GIT_REV );
Log.enable( this );
OnBootReceiver.startTimers( this );
@@ -92,6 +95,11 @@ public class XWApp extends Application implements LifecycleObserver {
RelayService.startService( this );
FBMService.init( this );
WiDirWrapper.init( this );
+
+ mPort = Short.valueOf( getString( R.string.nbs_port ) );
+ if ( NBSProxy.isInstalled( this ) ) {
+ NBSProxy.register( mPort, BuildConfig.APPLICATION_ID, this );
+ }
}
@OnLifecycleEvent(ON_ANY)
@@ -119,6 +127,24 @@ public class XWApp extends Application implements LifecycleObserver {
super.onTerminate();
}
+ // NBSProxy.Callbacks
+ @Override
+ public void onDataReceived( short port, String fromPhone, byte[] data )
+ {
+ Assert.assertTrue( port == mPort || !BuildConfig.DEBUG );
+ SMSService.handleFrom( this, data, fromPhone );
+ }
+
+ // NBSProxy.Callbacks
+ @Override
+ public void onRegResponse( boolean appReached )
+ {
+ if ( !appReached ) {
+ String channelID = Channels.getChannelID( this, Channels.ID.FOREGROUND );
+ NBSProxy.postLaunchNotification( this, channelID, R.drawable.notify );
+ }
+ }
+
public static UUID getAppUUID()
{
if ( null == s_UUID ) {
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java
index bcfc2b549..36dcfd861 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/DUtilCtxt.java
@@ -216,10 +216,8 @@ public class DUtilCtxt {
public void store( String key, byte[] data )
{
- Log.d( TAG, "store(key=%s)", key );
-
- if ( null == data ) {
- } else {
+ // Log.d( TAG, "store(key=%s)", key );
+ if ( null != data ) {
DBUtils.setBytesFor( m_context, key, data );
}
}
diff --git a/xwords4/android/app/src/main/res/values/common_rsrc.xml b/xwords4/android/app/src/main/res/values/common_rsrc.xml
index c9d6bce5a..65e624f9e 100644
--- a/xwords4/android/app/src/main/res/values/common_rsrc.xml
+++ b/xwords4/android/app/src/main/res/values/common_rsrc.xml
@@ -136,7 +136,7 @@
key_invite_multi
key_notagain_rematch_two_only
key_notagain_dfltname
- key_notagain_sms_banned
+ key_notagain_nbsGamesOnUpgrade
key_na_comms_bt
key_na_comms_p2p
key_na_comms_sms
@@ -155,6 +155,7 @@
https://eehouse.org/and_wordlists
+ https://eehouse.org/sms.html
https://eehouse.org/xw4/info.py
https://eehouse.org/xw4/relay.py
diff --git a/xwords4/android/app/src/main/res/values/strings.xml b/xwords4/android/app/src/main/res/values/strings.xml
index ca5a515ab..24226a493 100644
--- a/xwords4/android/app/src/main/res/values/strings.xml
+++ b/xwords4/android/app/src/main/res/values/strings.xml
@@ -2700,12 +2700,23 @@
This change will take effect after you
restart CrossWords.
- This game is configured to communicate
+ This game is configured to communicate
via Data SMS but CrossWords does not have permission to do so. You
can still open the game, but it may not be able to send or receive
moves.\n\nYou can re-open it to be asked for permission again. Or
you can remove the Data SMS communication setting.
+ The Google Play Store
+ version of CrossWords is no longer allowed to support the
+ Play-by-Data-SMS feature.
+ \n\n
+ You have %1$d open games that use this feature. They will not be
+ able to send or receive moves via Data SMS as long as you are using
+ the Google Play version of CrossWords, or until I can figure out a
+ workaround that meets Store policies.
+ \n\n
+ You can read more using the button below.
+
CrossWords needs access to
temporary storage to keep what you\'re about to download.
diff --git a/xwords4/android/res_src/values-ba_CK/strings.xml b/xwords4/android/res_src/values-ba_CK/strings.xml
index 0f3430d77..0ed3d890b 100644
--- a/xwords4/android/res_src/values-ba_CK/strings.xml
+++ b/xwords4/android/res_src/values-ba_CK/strings.xml
@@ -2318,7 +2318,7 @@
Gnitixe ppa…
Siht egnahc lliw ton ekat tceffe litnu
uoy tratser Sdrowssorc.
- Siht emag si derugifnoc ot
+ Siht emag si derugifnoc ot
etacinummoc aiv SMS tub Sdrowssorc seod ton evah noissimrep ot od
os. Uoy nac llits nepo eht ,emag tub ti yam ton eb elba ot dnes ro
eviecer sevom.\n\nUoy nac nepo-er ti ot eb deksa rof noissimrep
diff --git a/xwords4/android/res_src/values-ca_PS/strings.xml b/xwords4/android/res_src/values-ca_PS/strings.xml
index 5254af79c..55e250206 100644
--- a/xwords4/android/res_src/values-ca_PS/strings.xml
+++ b/xwords4/android/res_src/values-ca_PS/strings.xml
@@ -2318,7 +2318,7 @@
EXITING APP…
THIS CHANGE WILL NOT TAKE EFFECT UNTIL
YOU RESTART CROSSWORDS.
- THIS GAME IS CONFIGURED TO
+ THIS GAME IS CONFIGURED TO
COMMUNICATE VIA SMS BUT CROSSWORDS DOES NOT HAVE PERMISSION TO DO
SO. YOU CAN STILL OPEN THE GAME, BUT IT MAY NOT BE ABLE TO SEND OR
RECEIVE MOVES.\n\nYOU CAN RE-OPEN IT TO BE ASKED FOR PERMISSION
diff --git a/xwords4/android/res_src/values-de/strings.xml b/xwords4/android/res_src/values-de/strings.xml
index 88d8d6bbf..359bf9032 100644
--- a/xwords4/android/res_src/values-de/strings.xml
+++ b/xwords4/android/res_src/values-de/strings.xml
@@ -1128,7 +1128,7 @@
Tablet-Layout deaktivieren
Diese Änderung wird aktiv, wenn Sie CrossWords neu starten.
- Diese Partie ist konfiguriert über SMS zu kommunizieren, aber CrossWords hat keine Berechtigung dazu. Sie können die Partie immer noch öffnen, aber möglicherweise keine Züge senden oder empfangen.
+ Diese Partie ist konfiguriert über SMS zu kommunizieren, aber CrossWords hat keine Berechtigung dazu. Sie können die Partie immer noch öffnen, aber möglicherweise keine Züge senden oder empfangen.
\n
\nSie können sie neu öffnen, um wieder nach der Berechtigung gefragt zu werden. Oder Sie können die SMS-Verbindung aus den Einstellungen entfernen.
@@ -1341,4 +1341,4 @@
\n • Starten Sie CrossWords auf dem anderen Gerät
\n • Wenn alles nichts hilft, booten Sie dieses Gerät
\n
-
\ No newline at end of file
+
diff --git a/xwords4/android/res_src/values-fr/strings.xml b/xwords4/android/res_src/values-fr/strings.xml
index 733db2b54..67187f360 100644
--- a/xwords4/android/res_src/values-fr/strings.xml
+++ b/xwords4/android/res_src/values-fr/strings.xml
@@ -3533,7 +3533,7 @@ Merci de me faire savoir si vous aimez cette fonctionnalité, de signaler les pl
Utiliser le WiFi Direct pour jouer contre un appareil faisant du WiFi Direct, sur lequel CrossWords est installé.
- Cette partie est configurée pour communiquer par SMS mais Crosswords n\'a pas la permission de le faire. Vous pouvez toujours ouvrir cette partie, mais elle pourrait ne pas être en capacité d\'envoyer ou recevoir des coups.
+ Cette partie est configurée pour communiquer par SMS mais Crosswords n\'a pas la permission de le faire. Vous pouvez toujours ouvrir cette partie, mais elle pourrait ne pas être en capacité d\'envoyer ou recevoir des coups.
Vous pouvez la ré-ouvrir pour que la permission soit redemandée. Ou vous pouvez retirer le réglage de communication par SMS.
@@ -3624,4 +3624,4 @@ Vous pouvez la ré-ouvrir pour que la permission soit redemandée. Ou vous pouve
Utiliser par défaut pour mon appareil
Fermer
Cette notification est présente dès que CrossWords tourne en tâche de fond pour recevoir des messages Bluetooth. Elle reste pendant environ 15 minutes après que CrossWords ait démarré ou qu\'un message Bluetooth ait été reçu.
-
\ No newline at end of file
+
diff --git a/xwords4/android/res_src/values-ja/strings.xml b/xwords4/android/res_src/values-ja/strings.xml
index 833b19e61..34c32063f 100644
--- a/xwords4/android/res_src/values-ja/strings.xml
+++ b/xwords4/android/res_src/values-ja/strings.xml
@@ -848,7 +848,9 @@
WiFi ダイレクト経由で接続可能なデバイスはありません。
WiFi ダイレクトを使用して、クロスワードがインストールされた
近くの WiFi ダイレクト対応デバイスに対してプレイします。
- このゲームは SMS 経由で通信するように構成されていますが、
+
+ このゲームは SMS 経由で通信するように構成されていますが、
+
クロスワードにそれを行うアクセス許可がありません。
まだ、ゲームを開くことができますが、移動を送信または受信できない場合があります。
diff --git a/xwords4/android/res_src/values-nb-rNO/strings.xml b/xwords4/android/res_src/values-nb-rNO/strings.xml
index 0ee52e182..7aaa3f6ba 100644
--- a/xwords4/android/res_src/values-nb-rNO/strings.xml
+++ b/xwords4/android/res_src/values-nb-rNO/strings.xml
@@ -1279,7 +1279,7 @@
Du bruker forvalgt spillernavn \"%1$s\". Ønsker du å personalisere med ditt eget navn før du oppretter dette spillet?
- Dette spillet er satt opp for kommunikasjon via SMS, men CrossWords mangler tilgangen. Du kan åpne spillet, men det kan hende du ikke vil kunne sende eller motta trekk.
+ Dette spillet er satt opp for kommunikasjon via SMS, men CrossWords mangler tilgangen. Du kan åpne spillet, men det kan hende du ikke vil kunne sende eller motta trekk.
\n
\nDu kan gjenåpne det for å bli spurt om tilgang igjen. Eller du kan fjerne SMS-kommunikasjonsinnstillingen.
@@ -1336,4 +1336,4 @@
\n • Kjør CrossWords på den andre enheten
\n • Hvis ingenting annet virker, start denne enheten på ny
\n
-
\ No newline at end of file
+
diff --git a/xwords4/android/scripts/info.py b/xwords4/android/scripts/info.py
index b38a65c55..c1ecf78e1 100755
--- a/xwords4/android/scripts/info.py
+++ b/xwords4/android/scripts/info.py
@@ -23,6 +23,7 @@ except ImportError:
VERBOSE = False
k_NAME = 'name'
k_AVERS = 'avers'
+k_VARIANT = 'variant'
k_GVERS = 'gvers'
k_INSTALLER = 'installer'
k_DEVOK = 'devOK'
@@ -35,6 +36,9 @@ k_LOCALE = 'locale'
k_XLATPROTO = 'proto'
k_XLATEVERS = 'xlatevers'
k_STRINGSHASH = 'strings'
+k_UPGRADE_TITLE = 'title'
+k_UPGRADE_BODY = 'body'
+
k_DICT_HEADER_MASK = 0x08
diff --git a/xwords4/android/website/sms.html b/xwords4/android/website/sms.html
new file mode 100644
index 000000000..784a6dbe8
--- /dev/null
+++ b/xwords4/android/website/sms.html
@@ -0,0 +1,60 @@
+
+
+ CrossWords and Play-via-Data-SMS
+ Play-via-Data-SMS is one of my favorite features of
+ CrossWords. It's super-reliable, and works between GSM phones
+ round-the-world without requiring any kind of server. Most apps
+ have servers in order to harvest saleable infomation about you
+ or to show ads, but CrossWords doesn't care about that and so
+ has always supported this feature.
+
+ Unfortunately, Google's decided there are security problems
+ with SMS (or at least that's their claimed motivation) and is
+ prohibiting most apps from continuing to send or receive SMS
+ (including Data SMS) if they want to be listed on the Play
+ Store. And so I've removed the feature from the CrossWords
+ "variant" that's available there. The feature remains, however,
+ in other variants, including those at:
+
+
+
+
+ (The GitHub and Sourceforge variants will install over the top
+ of the Play Store variant without problems. With the F-droid
+ variant, however, you'll have to uninstall your current
+ variant first, losing your games and settings.)
+
+ I've also starting working on a data-SMS-forwarding app called
+ NBSProxy. With NBSProxy installed, CrossWords can communicate
+ using Data SMS without the SMS permissions (since NBSProxy has
+ them, and is the one using Android's Data SMS functionality.)
+ Google says that apps whose core functionality requires SMS
+ permissions will be allowed on the Play Store, so NBSProxy
+ should be available there soon. In the meantime, you
+ can get
+ it from GitHub.
+
+
+ Side note: It'd be helpful to know how many people use the
+ Play-via-Data-SMS feature in deciding whether to keep these
+ separate variants going. Would you
+ mind emailing me and
+ letting me know whether you've found it useful?
+
+
+ Thanks!
+
+
+ --Eric
+
+
+
+