mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-04 23:02:02 +01:00
perms: ask for the set needed to open a game
rewrite perms23 class to use Builder pattern, making it easier to pass more than one required permission. Use that (with hard-coded set for now) to check and ask for permissions a game needs to communicate. Offer to remove the comms methods the user doesn't want to permit (but that's not implemented yet.)
This commit is contained in:
parent
dfb22c13fa
commit
c398af2ac9
9 changed files with 941 additions and 836 deletions
File diff suppressed because it is too large
Load diff
|
@ -125,6 +125,7 @@
|
||||||
<string name="key_nag_intervals">key_nag_intervals</string>
|
<string name="key_nag_intervals">key_nag_intervals</string>
|
||||||
<string name="key_download_path">key_download_path</string>
|
<string name="key_download_path">key_download_path</string>
|
||||||
<string name="key_got_langdict">key_got_langdict</string>
|
<string name="key_got_langdict">key_got_langdict</string>
|
||||||
|
<string name="key_notagain_missing_perms">key_notagain_missing_perms</string>
|
||||||
<string name="key_xlations_locale">key_xlations_locale</string>
|
<string name="key_xlations_locale">key_xlations_locale</string>
|
||||||
<string name="key_xlations_enabled">key_xlations_enabled</string>
|
<string name="key_xlations_enabled">key_xlations_enabled</string>
|
||||||
<string name="key_invite_multi">key_invite_multi</string>
|
<string name="key_invite_multi">key_invite_multi</string>
|
||||||
|
|
|
@ -2718,4 +2718,13 @@
|
||||||
<string name="dualpane_restart">Exiting app…</string>
|
<string name="dualpane_restart">Exiting app…</string>
|
||||||
<string name="after_restart">This change will not take effect until
|
<string name="after_restart">This change will not take effect until
|
||||||
you restart Crosswords.</string>
|
you restart Crosswords.</string>
|
||||||
|
|
||||||
|
<string name="not_again_missing_perms">This game is configured to
|
||||||
|
communicate in at least one way that requires a permission that has
|
||||||
|
not been granted. You can still open it, but it may not be able to
|
||||||
|
send or receive moves.\n\nYou can re-open it to be asked again. Or
|
||||||
|
you can remove the settings that require missing permissions.</string>
|
||||||
|
|
||||||
|
<string name="remove_connvia">Remove</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2321,4 +2321,10 @@
|
||||||
<string name="dualpane_restart">Gnitixe ppa…</string>
|
<string name="dualpane_restart">Gnitixe ppa…</string>
|
||||||
<string name="after_restart">Siht egnahc lliw ton ekat tceffe litnu
|
<string name="after_restart">Siht egnahc lliw ton ekat tceffe litnu
|
||||||
uoy tratser Sdrowssorc.</string>
|
uoy tratser Sdrowssorc.</string>
|
||||||
|
<string name="not_again_missing_perms">Siht emag si derugifnoc ot
|
||||||
|
etacinummoc ni ta tsael eno yaw taht seriuqer a noissimrep taht sah
|
||||||
|
ton neeb detnarg. Uoy nac llits nepo ,ti tub ti yam ton eb elba ot
|
||||||
|
dnes ro eviecer sevom.\n\nUoy nac nepo-er ti ot eb deksa niaga. Ro
|
||||||
|
uoy nac evomer eht sgnittes taht eriuqer gnissim snoissimrep.</string>
|
||||||
|
<string name="remove_connvia">Evomer</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2321,4 +2321,10 @@
|
||||||
<string name="dualpane_restart">EXITING APP…</string>
|
<string name="dualpane_restart">EXITING APP…</string>
|
||||||
<string name="after_restart">THIS CHANGE WILL NOT TAKE EFFECT UNTIL
|
<string name="after_restart">THIS CHANGE WILL NOT TAKE EFFECT UNTIL
|
||||||
YOU RESTART CROSSWORDS.</string>
|
YOU RESTART CROSSWORDS.</string>
|
||||||
|
<string name="not_again_missing_perms">THIS GAME IS CONFIGURED TO
|
||||||
|
COMMUNICATE IN AT LEAST ONE WAY THAT REQUIRES A PERMISSION THAT HAS
|
||||||
|
NOT BEEN GRANTED. YOU CAN STILL OPEN IT, BUT IT MAY NOT BE ABLE TO
|
||||||
|
SEND OR RECEIVE MOVES.\n\nYOU CAN RE-OPEN IT TO BE ASKED AGAIN. OR
|
||||||
|
YOU CAN REMOVE THE SETTINGS THAT REQUIRE MISSING PERMISSIONS.</string>
|
||||||
|
<string name="remove_connvia">REMOVE</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -28,6 +28,8 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||||
|
@ -82,12 +84,13 @@ public class ConnViaViewLayout extends LinearLayout {
|
||||||
// ask for it here. When we get what we're getting, proceed to
|
// ask for it here. When we get what we're getting, proceed to
|
||||||
// actually check for what's supported. Those methods will return
|
// actually check for what's supported. Those methods will return
|
||||||
// false if they don't have the permission they need.
|
// false if they don't have the permission they need.
|
||||||
Perms23.doWithPermission( m_activity, Perms23.Perm.READ_PHONE_STATE,
|
new Perms23.Builder( Perms23.Perm.READ_PHONE_STATE )
|
||||||
new Perms23.PermCbck() {
|
.asyncQuery( m_activity, new Perms23.PermCbck() {
|
||||||
public void onPermissionResult( Perms23.Perm perm, boolean granted ) {
|
@Override
|
||||||
addConnectionsPostPermCheck();
|
public void onPermissionResult( Map<Perms23.Perm, Boolean> granted ) {
|
||||||
}
|
addConnectionsPostPermCheck();
|
||||||
} );
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addConnectionsPostPermCheck()
|
private void addConnectionsPostPermCheck()
|
||||||
|
|
|
@ -71,6 +71,8 @@ public class DlgDelegate {
|
||||||
NEW_GAME_DFLT_NAME,
|
NEW_GAME_DFLT_NAME,
|
||||||
ENABLE_DUALPANE,
|
ENABLE_DUALPANE,
|
||||||
ENABLE_DUALPANE_EXIT,
|
ENABLE_DUALPANE_EXIT,
|
||||||
|
LAUNCH_PERMS_REMOVE,
|
||||||
|
LAUNCH_PERMS,
|
||||||
|
|
||||||
// BoardDelegate
|
// BoardDelegate
|
||||||
UNDO_LAST_ACTION,
|
UNDO_LAST_ACTION,
|
||||||
|
|
|
@ -1315,6 +1315,15 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
m_newGameParams = params;
|
m_newGameParams = params;
|
||||||
askDefaultName();
|
askDefaultName();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LAUNCH_PERMS_REMOVE:
|
||||||
|
Assert.fail(); // not implemented
|
||||||
|
break;
|
||||||
|
case LAUNCH_PERMS:
|
||||||
|
long rowid = (Long)params[0];
|
||||||
|
launchGameAfterPerms( rowid );
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
|
@ -1559,33 +1568,33 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.games_menu_loaddb:
|
case R.id.games_menu_loaddb:
|
||||||
Perms23.doWithPermission( m_activity, Perms23.Perm.STORAGE,
|
new Perms23.Builder( Perms23.Perm.STORAGE )
|
||||||
new Perms23.PermCbck() {
|
.asyncQuery( m_activity, new Perms23.PermCbck() {
|
||||||
public void onPermissionResult( Perms23.Perm perm,
|
@Override
|
||||||
boolean granted )
|
public void onPermissionResult( Map<Perms23.Perm, Boolean> granted )
|
||||||
{
|
{
|
||||||
Assert.assertTrue( Perms23.Perm.STORAGE == perm );
|
Assert.assertTrue( granted.containsKey(Perms23.Perm.STORAGE) );
|
||||||
if ( granted ) {
|
if ( granted.get(Perms23.Perm.STORAGE) ) {
|
||||||
DBUtils.loadDB( m_activity );
|
DBUtils.loadDB( m_activity );
|
||||||
XWPrefs.clearGroupPositions( m_activity );
|
XWPrefs.clearGroupPositions( m_activity );
|
||||||
mkListAdapter();
|
mkListAdapter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
break;
|
break;
|
||||||
case R.id.games_menu_storedb:
|
case R.id.games_menu_storedb:
|
||||||
Perms23.doWithPermission( m_activity, Perms23.Perm.STORAGE,
|
new Perms23.Builder( Perms23.Perm.STORAGE )
|
||||||
new Perms23.PermCbck() {
|
.asyncQuery( m_activity, new Perms23.PermCbck() {
|
||||||
public void onPermissionResult( Perms23.Perm perm,
|
@Override
|
||||||
boolean granted )
|
public void onPermissionResult( Map<Perms23.Perm, Boolean> granted )
|
||||||
{
|
{
|
||||||
Assert.assertTrue( Perms23.Perm.STORAGE == perm );
|
Assert.assertTrue( granted.containsKey( Perms23.Perm.STORAGE ) );
|
||||||
if ( granted ) {
|
if ( granted.get( Perms23.Perm.STORAGE ) ) {
|
||||||
DBUtils.saveDB( m_activity );
|
DBUtils.saveDB( m_activity );
|
||||||
showToast( R.string.db_store_done );
|
showToast( R.string.db_store_done );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -2423,23 +2432,74 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
tryNFCIntent( intent );
|
tryNFCIntent( intent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<Perms23.Perm> getPermsNeededFor( GameSummary summary )
|
||||||
|
{
|
||||||
|
// PENDING: do this for real
|
||||||
|
Set<Perms23.Perm> perms = new HashSet<Perms23.Perm>();
|
||||||
|
perms.add( Perms23.Perm.READ_PHONE_STATE );
|
||||||
|
perms.add( Perms23.Perm.STORAGE );
|
||||||
|
perms.add( Perms23.Perm.SEND_SMS );
|
||||||
|
perms.add( Perms23.Perm.READ_CONTACTS );
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
|
||||||
private void doOpenGame( Object[] params )
|
private void doOpenGame( Object[] params )
|
||||||
{
|
{
|
||||||
GameSummary summary = (GameSummary)params[1];
|
GameSummary summary = (GameSummary)params[1];
|
||||||
long rowid = (Long)params[0];
|
final long rowid = (Long)params[0];
|
||||||
|
|
||||||
if ( summary.conTypes.contains( CommsAddrRec.CommsConnType.COMMS_CONN_RELAY )
|
if ( summary.conTypes.contains( CommsAddrRec.CommsConnType.COMMS_CONN_RELAY )
|
||||||
&& summary.roomName.length() == 0 ) {
|
&& summary.roomName.length() == 0 ) {
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
} else {
|
} else {
|
||||||
try {
|
final Set<Perms23.Perm> perms = getPermsNeededFor( summary );
|
||||||
if ( checkWarnNoDict( rowid ) ) {
|
new Perms23.Builder( perms )
|
||||||
launchGame( rowid );
|
.asyncQuery( m_activity, new Perms23.PermCbck() {
|
||||||
}
|
@Override
|
||||||
} catch ( GameLock.GameLockedException gle ) {
|
public void onPermissionResult( Map<Perms23.Perm,
|
||||||
DbgUtils.logex( TAG, gle );
|
Boolean> perms ) {
|
||||||
finish();
|
// If we get here, we were missing some
|
||||||
|
// permissions. The user may or may not have
|
||||||
|
// granted them. We'll go ahead and launch the
|
||||||
|
// game, but some stuff may fail (because we'll
|
||||||
|
// check for permissions later, e.g. when sending
|
||||||
|
// an SMS message, without allowing for asking
|
||||||
|
// again.
|
||||||
|
Set<Perms23.Perm> missing = new HashSet<Perms23.Perm>();
|
||||||
|
for ( Iterator<Perms23.Perm> iter = perms.keySet().iterator();
|
||||||
|
iter.hasNext(); ) {
|
||||||
|
Perms23.Perm perm = iter.next();
|
||||||
|
boolean granted = perms.get( perm );
|
||||||
|
if ( !granted ) {
|
||||||
|
missing.add( perm );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( 0 == missing.size() ) {
|
||||||
|
launchGameAfterPerms( rowid );
|
||||||
|
} else {
|
||||||
|
ActionPair pair = new ActionPair( Action.LAUNCH_PERMS_REMOVE,
|
||||||
|
R.string.remove_connvia );
|
||||||
|
makeNotAgainBuilder( R.string.not_again_missing_perms,
|
||||||
|
R.string.key_notagain_missing_perms,
|
||||||
|
Action.LAUNCH_PERMS )
|
||||||
|
.setActionPair( pair )
|
||||||
|
.setParams( rowid, missing )
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchGameAfterPerms( long rowid )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ( checkWarnNoDict( rowid ) ) {
|
||||||
|
launchGame( rowid );
|
||||||
}
|
}
|
||||||
|
} catch ( GameLock.GameLockedException gle ) {
|
||||||
|
DbgUtils.logex( TAG, gle );
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,20 @@ import android.content.pm.PackageManager;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class Perms23 {
|
public class Perms23 {
|
||||||
private static final String TAG = Perms23.class.getSimpleName();
|
private static final String TAG = Perms23.class.getSimpleName();
|
||||||
|
|
||||||
public static enum Perm {
|
public static enum Perm {
|
||||||
READ_PHONE_STATE("android.permission.READ_PHONE_STATE"),
|
READ_PHONE_STATE("android.permission.READ_PHONE_STATE"),
|
||||||
STORAGE("android.permission.WRITE_EXTERNAL_STORAGE")
|
STORAGE("android.permission.WRITE_EXTERNAL_STORAGE"),
|
||||||
|
SEND_SMS("android.permission.SEND_SMS"),
|
||||||
|
READ_CONTACTS("android.permission.READ_CONTACTS")
|
||||||
;
|
;
|
||||||
|
|
||||||
private String m_str;
|
private String m_str;
|
||||||
|
@ -51,56 +56,77 @@ public class Perms23 {
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface PermCbck {
|
public interface PermCbck {
|
||||||
void onPermissionResult( Perm perm, boolean granted );
|
void onPermissionResult( Map<Perm, Boolean> perms );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private Set<Perm> m_perms = new HashSet<Perm>();
|
||||||
|
|
||||||
|
public Builder(Set<Perm> perms) {
|
||||||
|
m_perms.addAll( perms );
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder( Perm perm ) {
|
||||||
|
m_perms.add( perm );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void asyncQuery( Activity activity, PermCbck cbck )
|
||||||
|
{
|
||||||
|
DbgUtils.logd( TAG, "asyncQuery()" );
|
||||||
|
boolean haveAll = true;
|
||||||
|
boolean shouldShow = false;
|
||||||
|
|
||||||
|
ArrayList<String> askStrings = new ArrayList<String>();
|
||||||
|
for ( Perm perm : m_perms ) {
|
||||||
|
String permStr = perm.getString();
|
||||||
|
boolean haveIt = PackageManager.PERMISSION_GRANTED
|
||||||
|
== ContextCompat.checkSelfPermission( activity, permStr );
|
||||||
|
|
||||||
|
// For research: ask the OS if we should be printing a rationale
|
||||||
|
if ( BuildConfig.DEBUG && !haveIt ) {
|
||||||
|
shouldShow = shouldShow || ActivityCompat
|
||||||
|
.shouldShowRequestPermissionRationale( activity, permStr );
|
||||||
|
}
|
||||||
|
|
||||||
|
haveAll = haveAll && haveIt;
|
||||||
|
if ( !haveIt ) {
|
||||||
|
askStrings.add( permStr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( shouldShow ) {
|
||||||
|
DbgUtils.showf( "Should show rationale!!!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( haveAll ) {
|
||||||
|
Map<Perm, Boolean> map = new HashMap<Perm, Boolean>();
|
||||||
|
for ( Perm perm : m_perms ) {
|
||||||
|
map.put( perm, true );
|
||||||
|
}
|
||||||
|
cbck.onPermissionResult( map );
|
||||||
|
} else {
|
||||||
|
String[] permsArray = askStrings.toArray( new String[askStrings.size()] );
|
||||||
|
int code = register( cbck );
|
||||||
|
ActivityCompat.requestPermissions( activity, permsArray, code );
|
||||||
|
}
|
||||||
|
|
||||||
|
DbgUtils.logd( TAG, "asyncQuery(%s) => %b", m_perms.toString(), haveAll );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Integer, PermCbck> s_map = new HashMap<Integer, PermCbck>();
|
||||||
public static void gotPermissionResult( int code, String[] perms, int[] granteds )
|
public static void gotPermissionResult( int code, String[] perms, int[] granteds )
|
||||||
{
|
{
|
||||||
CbckRecord record = s_map.get( code );
|
Map<Perm, Boolean> result = new HashMap<Perm, Boolean>();
|
||||||
for ( int ii = 0; ii < perms.length; ++ii ) {
|
for ( int ii = 0; ii < perms.length; ++ii ) {
|
||||||
Perm perm = Perm.getFor( perms[ii] );
|
Perm perm = Perm.getFor( perms[ii] );
|
||||||
boolean granted = PackageManager.PERMISSION_GRANTED == granteds[ii];
|
boolean granted = PackageManager.PERMISSION_GRANTED == granteds[ii];
|
||||||
DbgUtils.logd( TAG, "calling %s.onPermissionResult(%s, %b)",
|
result.put( perm, granted );
|
||||||
record.cbck.getClass().getSimpleName(), perm.toString(),
|
// DbgUtils.logd( TAG, "calling %s.onPermissionResult(%s, %b)",
|
||||||
granted );
|
// record.cbck.getClass().getSimpleName(), perm.toString(),
|
||||||
record.cbck.onPermissionResult( perm, granted );
|
// granted );
|
||||||
}
|
}
|
||||||
}
|
s_map.get( code ).onPermissionResult( result );
|
||||||
|
|
||||||
public static void doWithPermission( Activity activity, Perm perm, PermCbck cbck )
|
|
||||||
{
|
|
||||||
DbgUtils.logd( TAG, "doWithPermission()" );
|
|
||||||
String permStr = perm.getString();
|
|
||||||
|
|
||||||
if ( PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission( activity, permStr ) ) {
|
|
||||||
DbgUtils.logd( TAG, "doWithPermission(): already have it" );
|
|
||||||
cbck.onPermissionResult( perm, true );
|
|
||||||
} else {
|
|
||||||
// Should we show an explanation?
|
|
||||||
boolean shouldShow = ActivityCompat
|
|
||||||
.shouldShowRequestPermissionRationale( activity, permStr );
|
|
||||||
if ( shouldShow && BuildConfig.DEBUG ) {
|
|
||||||
DbgUtils.logd( TAG, "should show rationalle!!!" );
|
|
||||||
}
|
|
||||||
// Show an explanation to the user *asynchronously* -- don't
|
|
||||||
// block // this thread waiting for the user's
|
|
||||||
// response! After the user // sees the
|
|
||||||
// explanation, try again to request the
|
|
||||||
// permission.
|
|
||||||
|
|
||||||
String[] perms = new String[]{ permStr };
|
|
||||||
int code = register( perms, cbck );
|
|
||||||
ActivityCompat.requestPermissions( activity, perms, code );
|
|
||||||
}
|
|
||||||
|
|
||||||
// int check = ContextCompat.checkSelfPermission( activity, permStr );
|
|
||||||
// if ( PackageManager.PERMISSION_GRANTED == check ) {
|
|
||||||
// if ( null != onSuccess ) {
|
|
||||||
// onSuccess.run();
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Assert.assertTrue( PackageManager.PERMISSION_DENIED == check );
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean havePermission( Perm perm )
|
public static boolean havePermission( Perm perm )
|
||||||
|
@ -112,23 +138,12 @@ public class Perms23 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CbckRecord {
|
|
||||||
public PermCbck cbck;
|
|
||||||
public String[] perms;
|
|
||||||
public CbckRecord( String[] perms, PermCbck cbck ) {
|
|
||||||
this.perms = perms;
|
|
||||||
this.cbck = cbck;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int s_nextRecord;
|
private static int s_nextRecord;
|
||||||
private static Map<Integer, CbckRecord> s_map = new HashMap<Integer, CbckRecord>();
|
private static int register( PermCbck cbck )
|
||||||
private static int register( String[] perms, PermCbck cbck )
|
|
||||||
{
|
{
|
||||||
DbgUtils.assertOnUIThread();
|
DbgUtils.assertOnUIThread();
|
||||||
int code = ++s_nextRecord;
|
int code = ++s_nextRecord;
|
||||||
CbckRecord record = new CbckRecord( perms, cbck );
|
s_map.put( code, cbck );
|
||||||
s_map.put( code, record );
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue