From f3bb0c99a9228df0a62831b52e6397c997c2c709 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 20 Nov 2020 18:07:19 -0800 Subject: [PATCH] invite alert changes Create new class that owns the alert. Let it decide whether to post, remove, etc. Seems to work, but I've removed some of the "reinvite" options I'm not sure were helpful anyway. To be considered... --- .../eehouse/android/xw4/BoardDelegate.java | 383 +++--------------- .../eehouse/android/xw4/CommsTransport.java | 2 +- .../java/org/eehouse/android/xw4/DBAlert.java | 2 +- .../org/eehouse/android/xw4/DelegateBase.java | 7 +- .../org/eehouse/android/xw4/DlgDelegate.java | 3 +- .../android/xw4/InviteChoicesAlert.java | 7 +- .../org/eehouse/android/xw4/InviteView.java | 6 + .../android/xw4/InvitesNeededAlert.java | 199 +++++++++ .../android/xw4/jni/TransportProcs.java | 4 +- .../app/src/main/res/layout/invite_view.xml | 5 + .../app/src/main/res/values/strings.xml | 49 ++- xwords4/common/server.c | 9 +- 12 files changed, 311 insertions(+), 365 deletions(-) create mode 100644 xwords4/android/app/src/main/java/org/eehouse/android/xw4/InvitesNeededAlert.java 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 4c02e9b40..f15ed3bb6 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 @@ -81,7 +81,7 @@ public class BoardDelegate extends DelegateBase implements TransportProcs.TPMsgHandler, View.OnClickListener, DwnldDelegate.DownloadFinishedListener, ConnStatusHandler.ConnStatusCBacks, - Wrapper.Procs { + Wrapper.Procs, InvitesNeededAlert.Callbacks { private static final String TAG = BoardDelegate.class.getSimpleName(); private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins @@ -124,11 +124,9 @@ public class BoardDelegate extends DelegateBase private boolean m_startSkipped; private JNIThread.GameStateInfo m_gsi; - private int m_nGuestDevs = -1; private boolean m_showedReInvite; private boolean m_overNotShown; private boolean m_dropRelayOnDismiss; - private DBAlert m_inviteAlert; private boolean m_haveStartedShowing; private Wrapper mNFCWrapper; @@ -163,12 +161,11 @@ public class BoardDelegate extends DelegateBase private static enum StartAlertOrder { NBS_PERMS, NO_MEANS, INVITE, DONE, }; private static class MySIS implements Serializable { - MySIS() { nMissing = -1; } String toastStr; String[] words; String getDict; - int nMissing; - int nAlerts; + int nMissing = -1; + int nGuestDevs; boolean inTrade; StartAlertOrder mAlertOrder = StartAlertOrder.values()[0]; } @@ -177,7 +174,8 @@ public class BoardDelegate extends DelegateBase private boolean alertOrderAt( StartAlertOrder ord ) { boolean result = m_mySIS.mAlertOrder == ord; - // Log.d( TAG, "alertOrderAt(%s) => %b", ord, result ); + // Log.d( TAG, "alertOrderAt(%s) => %b (at %s)", ord, result, + // m_mySIS.mAlertOrder ); return result; } @@ -414,10 +412,8 @@ public class BoardDelegate extends DelegateBase .setNegativeButton( R.string.button_no, null ) .create(); break; - case DLG_INVITE: { - InviteAlertState state = (InviteAlertState)params[0]; - dialog = makeInviteDialog( alert, state ); - } + case DLG_INVITE: + dialog = InvitesNeededAlert.make( this, alert, params ); break; case ENABLE_NFC: @@ -447,160 +443,6 @@ public class BoardDelegate extends DelegateBase } } - private static class InviteAlertState implements Serializable { - GameSummary summary; - CurGameInfo gi; - boolean relayMissing; - CommsConnTypeSet connTypes; - long rowid; - int nMissing; - } - - private Dialog makeInviteDialog( final DBAlert alert, - final InviteAlertState state ) - { - Dialog dialog = null; - final Activity activity = alert.getActivity(); - if ( null != activity ) { - final SentInvitesInfo[] sentInfo = { null }; - String message; - int titleID; - boolean showInviteButton = true; - boolean showNeutButton = false; - - int buttonTxt = R.string.newgame_invite; - - if ( state.relayMissing ) { - titleID = R.string.seeking_relay; - // If relay is only means, don't allow at all - boolean relayOnly = 1 >= state.connTypes.size(); - showInviteButton = !relayOnly; - message = activity.getString( R.string.no_relay_conn ); - if ( NetStateCache.netAvail( activity ) - && NetStateCache.onWifi() ) { - message += LocUtils.getString( activity, R.string.wifi_warning ); - } - if ( !relayOnly ) { - CommsConnTypeSet without = (CommsConnTypeSet) - state.connTypes.clone(); - without.remove( CommsConnType.COMMS_CONN_RELAY ); - message += "\n\n" - + LocUtils.getString( activity, - R.string.drop_relay_warning_fmt, - without.toString( activity, true ) ); - buttonTxt = R.string.newgame_drop_relay; - } - } else { - sentInfo[0] = DBUtils.getInvitesFor( activity, state.rowid ); - int nSent = sentInfo[0].getMinPlayerCount(); - boolean invitesSent = nSent >= state.nMissing; - if ( invitesSent ) { - m_showedReInvite = true; - if ( state.summary.hasRematchInfo() ) { - titleID = R.string.waiting_rematch_title; - message = LocUtils.getString( activity, R.string.rematch_msg ); - } else { - titleID = R.string.waiting_invite_title; - message = LocUtils - .getQuantityString( activity, R.plurals.invite_sent_fmt, - nSent, nSent, state.nMissing ); - } - buttonTxt = R.string.button_reinvite; - showNeutButton = true; - } else if ( DeviceRole.SERVER_ISCLIENT == state.gi.serverRole ) { - Assert.assertFalse( state.summary.hasRematchInfo() ); - message = LocUtils.getString( activity, R.string.invited_msg ); - titleID = R.string.waiting_title; - showInviteButton = false; - } else { - titleID = R.string.waiting_title; - message = LocUtils - .getQuantityString( activity, R.plurals.invite_msg_fmt, - state.nMissing, state.nMissing ); - } - - if ( ! invitesSent && showInviteButton ) { - String ps = null; - if ( state.nMissing > 1 ) { - ps = LocUtils.getString( activity, R.string.invite_multiple ); - } else { - boolean[] avail = NFCUtils.nfcAvail( activity ); - if ( avail[1] ) { - ps = LocUtils.getString( activity, R.string.invite_if_nfc ); - } - } - if ( null != ps ) { - message += "\n\n" + ps; - } - } - - message += "\n\n" + LocUtils.getString( activity, R.string.invite_stays ); - } - - AlertDialog.Builder ab = makeAlertBuilder() - .setTitle( titleID ) - .setMessage( message ); - - OnClickListener lstnr = new OnClickListener() { - @Override - public void onClick( DialogInterface dialog, int item ){ - if ( !state.relayMissing || - ! state.connTypes.contains(CommsConnType.COMMS_CONN_RELAY) ) { - Assert.assertTrue( 0 < state.nMissing ); - if ( state.summary.hasRematchInfo() ) { - tryRematchInvites( true ); - } else if ( state.summary.hasInviteInfo() ) { - tryOtherInvites(); - } else { - callInviteChoices( sentInfo[0] ); - } - } else { - askDropRelay(); - } - } - }; - alert.setNoDismissListenerPos( ab, buttonTxt, lstnr ); - - if ( showNeutButton ) { - lstnr = new OnClickListener() { - @Override - public void onClick( DialogInterface dialog, - int item ) { - String msg = sentInfo[0].getAsText( activity ); - makeOkOnlyBuilder( msg ) - .setAction( Action.INVITE_INFO ) - .show(); - } - }; - alert.setNoDismissListenerNeut( ab, R.string.newgame_invite_more, - lstnr ); - } - if ( showInviteButton ) { - lstnr = new OnClickListener() { - @Override - public void onClick( DialogInterface di, int item ) { - alert.dismiss(); - finish(); - } - }; - alert.setNoDismissListenerNeg( ab, R.string.button_close, lstnr ); - } - - dialog = ab.create(); - - // Hack: I can't prevent screen rotation from duplicating this alert - dismissInviteAlert(); - m_inviteAlert = alert; - alert.setOnCancelListener( new DBAlert.OnCancelListener() { - @Override - public void onCancelled( XWDialogFragment frag ) { - finish(); - } - } ); - } - return dialog; - } // makeInviteDialog - public BoardDelegate( Delegator delegator, Bundle savedInstanceState ) { super( delegator, savedInstanceState, R.layout.board, R.menu.board_menu ); @@ -829,19 +671,18 @@ public class BoardDelegate extends DelegateBase // loop of showing the rationale over and over. Android will always tell // us to show the rationale, but if we've done it already we need to go // straight to asking for the permission. - private void callInviteChoices( final SentInvitesInfo info ) + private void callInviteChoices() { Perms23.tryGetPermsNA( this, Perm.READ_PHONE_STATE, R.string.phone_state_rationale, R.string.key_na_perms_phonestate, - Action.ASKED_PHONE_STATE, info ); + Action.ASKED_PHONE_STATE ); } - private void showInviteChoicesThen( Object[] params ) + private void showInviteChoicesThen() { - SentInvitesInfo info = (SentInvitesInfo)params[0]; NetLaunchInfo nli = nliForMe(); - showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION, info, nli ); + showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION, nli ); } @Override @@ -1221,7 +1062,7 @@ public class BoardDelegate extends DelegateBase break; case ASKED_PHONE_STATE: - showInviteChoicesThen( params ); + showInviteChoicesThen(); break; case BLANK_PICKED: @@ -1309,7 +1150,7 @@ public class BoardDelegate extends DelegateBase finish(); break; case ASKED_PHONE_STATE: - showInviteChoicesThen( params ); + showInviteChoicesThen(); break; case INVITE_SMS_DATA: Perms23.Perm[] perms = (Perms23.Perm[])params[2]; @@ -1411,8 +1252,9 @@ public class BoardDelegate extends DelegateBase case SMS_USER: case EMAIL: case CLIPBOARD: - NetLaunchInfo nli = new NetLaunchInfo( m_activity, m_summary, m_gi, 1, - 1 + m_nGuestDevs ); + NetLaunchInfo nli = new NetLaunchInfo( m_activity, m_summary, m_gi, + 1, // nPlayers + 1 + m_mySIS.nGuestDevs ); // fc if ( m_relayMissing ) { nli.removeAddress( CommsConnType.COMMS_CONN_RELAY ); } @@ -1526,17 +1368,6 @@ public class BoardDelegate extends DelegateBase ////////////////////////////////////////////////// // TransportProcs.TPMsgHandler interface ////////////////////////////////////////////////// - @Override - public void tpmRelayConnd( final String room, final int devOrder, - final boolean allHere, final int nMissing ) - { - runOnUiThread( new Runnable() { - @Override - public void run() { - handleConndMessage( room, devOrder, allHere, nMissing ); // from here too - } - } ); - } @Override public void tpmRelayErrorProc( TransportProcs.XWRELAY_ERROR relayErr ) @@ -1655,14 +1486,38 @@ public class BoardDelegate extends DelegateBase // Do we need this? } + //////////////////////////////////////////////////////////// + // InvitesNeededAlert.Callbacks + //////////////////////////////////////////////////////////// + @Override + public DelegateBase getDelegate() { return this; } + + @Override + public void onCloseClicked() + { + post( new Runnable() { + @Override + public void run() { + InvitesNeededAlert.dismiss(); + finish(); + } + } ); + } + + @Override + public void onInviteClicked() + { + callInviteChoices(); + } + private byte[] getInvite() { byte[] result = null; if ( 0 < m_mySIS.nMissing // Isn't there a better test?? && DeviceRole.SERVER_ISSERVER == m_gi.serverRole ) { NetLaunchInfo nli = new NetLaunchInfo( m_gi ); - Assert.assertTrue( 0 <= m_nGuestDevs ); - nli.forceChannel = 1 + m_nGuestDevs; + Assert.assertTrue( 0 <= m_mySIS.nGuestDevs ); + nli.forceChannel = 1 + m_mySIS.nGuestDevs; for ( Iterator iter = m_connTypes.iterator(); iter.hasNext(); ) { @@ -1774,68 +1629,6 @@ public class BoardDelegate extends DelegateBase return xpKey; } - private void handleConndMessage( String room, int devOrder, // <- hostID - boolean allHere, int nMissing ) - { - boolean skipDismiss = false; - - int naMsg = 0; - int naKey = 0; - String toastStr = null; - m_mySIS.nMissing = nMissing; - if ( allHere ) { - // All players have now joined the game. The device that - // created the room will assign tiles. Then it will be - // the first player's turn - - // Skip this until there's a way to show it only once per game - if ( false ) { - toastStr = getString( R.string.msg_relay_all_here_fmt, room ); - if ( devOrder > 1 ) { - naMsg = R.string.not_again_conndall; - naKey = R.string.key_notagain_conndall; - } - } - } else if ( nMissing > 0 ) { - if ( m_summary.hasRematchInfo() ) { - skipDismiss = !tryRematchInvites( false ); - } else if ( m_summary.hasInviteInfo() ) { - skipDismiss = !tryOtherInvites(); - } else if ( showInviteAlertIf() ) { - skipDismiss = true; - invalidateOptionsMenuIf(); - } else { - toastStr = getQuantityString( R.plurals.msg_relay_waiting_fmt, nMissing, - devOrder, room, nMissing ); - if ( devOrder == 1 ) { - naMsg = R.string.not_again_conndfirst; - naKey = R.string.key_notagain_conndfirst; - } else { - naMsg = R.string.not_again_conndmid; - naKey = R.string.key_notagain_conndmid; - } - } - } - - if ( null != toastStr ) { - Log.i( TAG, "handleConndMessage(): toastStr: %s", toastStr ); - m_mySIS.toastStr = toastStr; - if ( naMsg == 0 ) { - onPosButton( Action.SHOW_EXPL_ACTION, null ); - } else { - makeNotAgainBuilder( naMsg, naKey, Action.SHOW_EXPL_ACTION ) - .show(); - } - } - - if ( !skipDismiss ) { - dismissInviteAlert( nMissing, true ); // NO!!! - } - - invalidateOptionsMenuIf(); - setTitle(); - } // handleConndMessage - private class BoardUtilCtxt extends UtilCtxtImpl { public BoardUtilCtxt() @@ -2162,34 +1955,20 @@ public class BoardDelegate extends DelegateBase // missing or not. @Override public void informMissing( boolean isServer, CommsConnTypeSet connTypes, - int nDevs, final int nMissing ) + int nDevs, int nMissing ) { - boolean doDismiss = true; - m_connTypes = connTypes; - Assert.assertTrue( isServer || 0 == nMissing ); - // DbgUtils.logf( "BoardDelegate.informMissing(isServer=%b, nDevs=%d, nMissing=%d)", - // isServer, nDevs, nMissing ); - m_nGuestDevs = nDevs; - m_mySIS.nMissing = nMissing; // will be 0 unless isServer is true - setTitle(); + m_mySIS.nGuestDevs = nDevs; + m_connTypes = connTypes; - if ( null != connTypes && 0 == connTypes.size() ) { - askNoAddrsDelete(); - } else if ( 0 < nMissing && isServer ) { - doDismiss = false; - post( new Runnable() { + if ( isServer ) { + runOnUiThread( new Runnable() { @Override public void run() { showInviteAlertIf(); } } ); } - - // If we might have put up an alert earlier, take it down - if ( doDismiss ) { - dismissInviteAlert( nMissing, !m_relayMissing ); - } } @Override @@ -2570,42 +2349,6 @@ public class BoardDelegate extends DelegateBase } } - private void dismissInviteAlert( final int nMissing, final boolean connected ) - { - runOnUiThread( new Runnable() { - @Override - public void run() { - InviteChoicesAlert.dismissAny(); - - if ( m_relayMissing && connected ) { - m_relayMissing = false; - } - // Why this m_relayMissing thing? - if ( 0 == nMissing /* || !m_relayMissing*/ ) { - // Log.d( TAG, "dismissing invite alert %H", m_inviteAlert ); - dismissInviteAlert(); - } - } - } ); - } - - private void dismissInviteAlert() - { - if ( null != m_inviteAlert ) { - // Play Console reports a crash inside DialogFragment.dismiss() - // resulting from getFragmentManager() returning null. That - // probably means I'm on the way out and can safely drop? Let's - // see.... - try { - m_inviteAlert.dismiss(); - } catch ( NullPointerException | IllegalStateException ex ) { - Log.ex( TAG, ex ); - } - m_inviteAlert = null; - m_haveStartedShowing = false; - } - } - private void pingBTRemotes() { if ( null != m_connTypes @@ -2687,28 +2430,13 @@ public class BoardDelegate extends DelegateBase // 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 boolean showInviteAlertIf() + private void showInviteAlertIf() { - boolean success = false; - DbgUtils.assertOnUIThread(); - 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; - success = true; - } else { - alertOrderIncrIfAt( StartAlertOrder.INVITE ); - } + if ( alertOrderAt( StartAlertOrder.INVITE ) && ! isFinishing() ) { + boolean isRematch = null != m_summary && m_summary.hasRematchInfo(); + InvitesNeededAlert.showOrHide( this, m_mySIS.nGuestDevs, + m_mySIS.nMissing, isRematch ); } - return success; } private boolean doZoom( int zoomBy ) @@ -2814,8 +2542,8 @@ public class BoardDelegate extends DelegateBase for ( int ii = 0; ii < m_missingDevs.length; ++ii ) { String dev = m_missingDevs[ii]; int nPlayers = m_missingCounts[ii]; - Assert.assertTrue( 0 <= m_nGuestDevs ); - int forceChannel = ii + m_nGuestDevs + 1; + Assert.assertTrue( 0 <= m_mySIS.nGuestDevs ); + int forceChannel = ii + m_mySIS.nGuestDevs + 1; NetLaunchInfo nli = new NetLaunchInfo( m_activity, m_summary, m_gi, nPlayers, forceChannel ) .setRemotesAreRobots( m_remotesAreRobots ); @@ -3177,7 +2905,7 @@ public class BoardDelegate extends DelegateBase private NetLaunchInfo nliForMe() { int numHere = 1; - int forceChannel = 1; + int forceChannel = 1 + m_mySIS.nGuestDevs; NetLaunchInfo nli = new NetLaunchInfo( m_activity, m_summary, m_gi, numHere, forceChannel ); return nli; @@ -3314,7 +3042,6 @@ public class BoardDelegate extends DelegateBase DBUtils.recordInviteSent( m_activity, m_rowid, means, dev ); if ( !invitesSent ) { - dismissInviteAlert(); Log.d( TAG, "recordInviteSent(): redoing invite alert" ); showInviteAlertIf(); } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java index 055b4d067..a9caab875 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java @@ -395,7 +395,7 @@ public class CommsTransport implements TransportProcs, public void relayConnd( String room, int devOrder, boolean allHere, int nMissing ) { - m_tpHandler.tpmRelayConnd( room, devOrder, allHere, nMissing ); + // m_tpHandler.tpmRelayConnd( room, devOrder, allHere, nMissing ); } @Override diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBAlert.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBAlert.java index f7f4951b0..38e210050 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBAlert.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DBAlert.java @@ -99,7 +99,7 @@ public class DBAlert extends XWDialogFragment { if ( null == dialog ) { Log.e( TAG, "no dialog for %s from %s", getDlgID(), activity ); - Assert.failDbg(); + // Assert.failDbg(); // remove: better to see what users will see dialog = LocUtils.makeAlertBuilder( activity ) .setMessage( "Unable to create " + getDlgID() + " Alert" ) .setPositiveButton( "Bummer", null ) 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 e05c614d1..0fbfda295 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 @@ -519,7 +519,7 @@ public abstract class DelegateBase implements DlgClickNotify, protected void show( XWDialogFragment df ) { DbgUtils.assertOnUIThread(); - if ( m_activity instanceof XWActivity ) { + if ( null != df && m_activity instanceof XWActivity ) { ((XWActivity)m_activity).show( df ); } else { Assert.failDbg(); @@ -596,10 +596,9 @@ public abstract class DelegateBase implements DlgClickNotify, } protected void showInviteChoicesThen( Action action, - DBUtils.SentInvitesInfo info, - NetLaunchInfo nli) + NetLaunchInfo nli ) { - m_dlgDelegate.showInviteChoicesThen( action, info, nli ); + m_dlgDelegate.showInviteChoicesThen( action, nli ); } public Builder makeOkOnlyBuilder( int msgID ) 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 a0d741c71..948a7db67 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 @@ -401,12 +401,11 @@ public class DlgDelegate { } public void showInviteChoicesThen( final Action action, - SentInvitesInfo info, NetLaunchInfo nli ) { DlgState state = new DlgState( DlgID.INVITE_CHOICES_THEN ) .setAction( action ) - .setParams( info, nli ); + .setParams( nli ); m_dlgt.show( state ); } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteChoicesAlert.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteChoicesAlert.java index ccf77cc56..aa3ad85d5 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteChoicesAlert.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteChoicesAlert.java @@ -85,11 +85,8 @@ public class InviteChoicesAlert extends DlgDelegateAlert NetLaunchInfo nli = null; Object[] params = state.getParams(); if ( null != params ) { - if ( 0 < params.length && params[0] instanceof SentInvitesInfo ) { - lastMeans = ((SentInvitesInfo)params[0]).getLastMeans(); - } - if ( 1 < params.length && params[1] instanceof NetLaunchInfo ) { - nli = (NetLaunchInfo)params[1]; + if ( 0 < params.length && params[0] instanceof NetLaunchInfo ) { + nli = (NetLaunchInfo)params[0]; } } means.add( InviteMeans.EMAIL ); diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteView.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteView.java index 78a9799c8..a627e7d2c 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteView.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InviteView.java @@ -28,6 +28,7 @@ import android.widget.ImageView; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.ScrollView; +import android.widget.TextView; import java.util.HashMap; import java.util.List; @@ -238,6 +239,11 @@ public class InviteView extends ScrollView public void run() { ImageView iv = (ImageView)findViewById( R.id.qr_view ); iv.setImageBitmap( bitmap ); + if ( BuildConfig.NON_RELEASE ) { + TextView tv = (TextView)findViewById( R.id.qr_url ); + tv.setVisibility( View.VISIBLE ); + tv.setText( url ); + } post ( new Runnable() { @Override public void run() { diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InvitesNeededAlert.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InvitesNeededAlert.java new file mode 100644 index 000000000..fb6580a25 --- /dev/null +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/InvitesNeededAlert.java @@ -0,0 +1,199 @@ +/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */ +/* + * Copyright 2009 - 2020 by Eric House (xwords@eehouse.org). All rights + * reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.eehouse.android.xw4; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface; + +import java.io.Serializable; + +import org.eehouse.android.xw4.DlgDelegate.Action; +import org.eehouse.android.xw4.Perms23.Perm; +import org.eehouse.android.xw4.loc.LocUtils; + +class InvitesNeededAlert { + private static final String TAG = InvitesNeededAlert.class.getSimpleName(); + + private static InvitesNeededAlert[] sInstance = {null}; + + private DBAlert mAlert; + private State mState; + private Callbacks mCallbacks; + private DelegateBase mDelegate; + + private static class State implements Serializable { + int nDevsSeen; + int nPlayersMissing; + boolean isRematch; + + State( int nDevs, int nPlayers, boolean rematch ) + { + nDevsSeen = nDevs; + nPlayersMissing = nPlayers; + isRematch = rematch; + } + } + + interface Callbacks { + DelegateBase getDelegate(); + void onCloseClicked(); + void onInviteClicked(); + } + + static void showOrHide( Callbacks callbacks, int nDevsSeen, int nPlayersMissing, + boolean isRematch ) + { + DbgUtils.assertOnUIThread(); + InvitesNeededAlert self = sInstance[0]; + Log.d( TAG, "showOnceIf(nDevsSeen=%d, nPlayersMissing=%d); self: %s", + nDevsSeen, nPlayersMissing, self ); + + if ( null == self && 0 == nPlayersMissing ) { + // cool: need and have nothing, so do nothing + } else if ( 0 < nPlayersMissing && null == self ) { // Need but don't have + makeNew( callbacks, nDevsSeen, nPlayersMissing, isRematch ); + } else if ( 0 == nPlayersMissing && null != self ) { // Have and need to close + close( self ); + } else if ( null != self && nPlayersMissing != self.mState.nPlayersMissing ) { + close( self ); + makeNew( callbacks, nDevsSeen, nPlayersMissing, isRematch ); + } else if ( null != self && nPlayersMissing == self.mState.nPlayersMissing ) { + // nothing to do + } else { + Assert.failDbg(); + } + } + + static Dialog make( Callbacks callbacks, DBAlert alert, Object[] params ) + { + DbgUtils.assertOnUIThread(); + InvitesNeededAlert self = sInstance[0]; + return self.makeImpl( callbacks, alert, params ); + } + + static void dismiss() + { + Log.d( TAG, "dismiss()" ); + DbgUtils.assertOnUIThread(); + InvitesNeededAlert self = sInstance[0]; + if ( null != self ) { + close( self ); + } + } + + private static void makeNew( Callbacks callbacks, + int nDevsSeen, int nPlayersMissing, boolean isRematch ) + { + Log.d( TAG, "makeNew(nDevsSeen=%d, nPlayersMissing=%d)", nDevsSeen, nPlayersMissing ); + State state = new State( nDevsSeen, nPlayersMissing, isRematch ); + InvitesNeededAlert self = new InvitesNeededAlert( callbacks, state ); + callbacks.getDelegate().showDialogFragment( DlgID.DLG_INVITE, state ); + } + + private static void close( InvitesNeededAlert self ) + { + DbgUtils.assertOnUIThread(); + Assert.assertTrueNR( self == sInstance[0] ); + sInstance[0] = null; + if ( null != self.mAlert ) { + InviteChoicesAlert.dismissAny(); + self.mAlert.dismiss(); + } + } + + private InvitesNeededAlert( Callbacks callbacks, State state ) + { + mState = state; + mCallbacks = callbacks; + mDelegate = callbacks.getDelegate(); + DbgUtils.assertOnUIThread(); + Assert.assertTrueNR( null == sInstance[0] ); + sInstance[0] = this; + } + + private Dialog makeImpl( Callbacks callbacks, final DBAlert alert, + Object[] params ) + { + Dialog result = null; + State state = (State)params[0]; + mAlert = alert; + + Context context = mDelegate.getActivity(); + String title; + + boolean isRematch = state.isRematch; + if ( isRematch ) { + title = LocUtils.getString( context, R.string.waiting_rematch_title ); + } else { + title = LocUtils + .getQuantityString( context, R.plurals.waiting_title_fmt, + state.nPlayersMissing, state.nPlayersMissing ); + } + + String message = LocUtils + .getQuantityString( context, R.plurals.invite_msg_fmt, + state.nPlayersMissing, state.nPlayersMissing ); + message += "\n\n" + + LocUtils.getString( context, R.string.invite_msg_extra ); + + if ( isRematch ) { + message += "\n\n" + + LocUtils.getString( context, R.string.invite_msg_extra_rematch ); + } + + AlertDialog.Builder ab = mDelegate.makeAlertBuilder() + .setTitle( title ) + .setMessage( message ); + + alert.setNoDismissListenerPos( ab, R.string.newgame_invite, + new OnClickListener() { + @Override + public void onClick( DialogInterface dlg, int item ) { + onPosClick(); + } + } ); + + alert.setNoDismissListenerNeg( ab, R.string.button_close, + new OnClickListener() { + @Override + public void onClick( DialogInterface dlg, int item ) { + onNegClick(); + } + } ); + + result = ab.create(); + return result; + } + + private void onPosClick() + { + mCallbacks.onInviteClicked(); + } + + private void onNegClick() + { + Log.d( TAG, "onNegClick()" ); + mCallbacks.onCloseClicked(); + } +} diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java index 419d60a4a..6db4b7c3c 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java @@ -65,8 +65,8 @@ public interface TransportProcs { boolean relayNoConnProc( byte[] buf, String msgNo, String relayID ); public interface TPMsgHandler { - public void tpmRelayConnd( String room, int devOrder, boolean allHere, - int nMissing ); + // public void tpmRelayConnd( String room, int devOrder, boolean allHere, + // int nMissing ); public void tpmRelayErrorProc( XWRELAY_ERROR relayErr ); public void tpmCountChanged( int newCount ); } diff --git a/xwords4/android/app/src/main/res/layout/invite_view.xml b/xwords4/android/app/src/main/res/layout/invite_view.xml index 7afff687e..44f3ac4a4 100644 --- a/xwords4/android/app/src/main/res/layout/invite_view.xml +++ b/xwords4/android/app/src/main/res/layout/invite_view.xml @@ -91,6 +91,11 @@ android:layout_gravity="center_horizontal" android:paddingTop="10dp" /> + This game is waiting for one remote - player. Would you like to invite someone to join—assuming you - haven’t already? + player to respond to an invitation. This game is waiting for %1$d remote - players. Would you like to invite someone to join—assuming - you haven’t already? + players to repond to invitations. - - You have already invited a remote player to - this game. We are waiting for him/her to respond. Please - use the re-invite button if you think the invitation did not go - out. - You have already sent %1$d unique - invitations for this game. We are waiting for %2$d of the - recipients to respond. Please use the re-invite button if you - think the invitations did not go out. - - This game was created from an - invitation you received. As soon as it is able to connect to the - sender and any other invitees have arrived play will - begin. + + You will see this message until + all expected players have connected. If you’ve already sent an + invitation and suspect it was lost, there’s no harm in sending + another. + + With rematches, all + necessary invitations will have been sent automatically. But you + can always send new ones if an invitee is not responding. + + + + + + + + + + + + + + + Or just Tap to Invite -- if the other device also has Android Beaming and is nearby. @@ -2245,7 +2253,10 @@ to make more games visible.\n\n(If you later want to unhide the buttons go to the Appearance section of App settings). - Waiting for players + + Waiting for player + Waiting for %1$d players + Waiting for response Rematch in progress diff --git a/xwords4/common/server.c b/xwords4/common/server.c index da774ae1f..8143c3c8c 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -194,7 +194,8 @@ static XWStreamCtxt* messageStreamWithHeader( ServerCtxt* server, XWEnv xwe, XP_U16 devIndex, XW_Proto code ); static XP_Bool handleRegistrationMsg( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* stream ); -static XP_S8 registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ); +static XP_S8 registerRemotePlayer( ServerCtxt* server, XWEnv xwe, + XWStreamCtxt* stream ); static void sendInitialMessage( ServerCtxt* server, XWEnv xwe ); static void sendBadWordMsgs( ServerCtxt* server, XWEnv xwe ); static XP_Bool handleIllegalWord( ServerCtxt* server, XWEnv xwe, @@ -914,7 +915,7 @@ handleRegistrationMsg( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* stream ) XP_S8 prevIndex = -1; #endif for ( ; ii < playersInMsg; ++ii ) { - clientIndex = registerRemotePlayer( server, stream ); + clientIndex = registerRemotePlayer( server, xwe, stream ); if ( -1 == clientIndex ) { success = XP_FALSE; break; @@ -1790,7 +1791,7 @@ findFirstPending( ServerCtxt* server, ServerPlayer** playerP ) } /* findFirstPending */ static XP_S8 -registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) +registerRemotePlayer( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* stream ) { XP_S8 deviceIndex = -1; XP_PlayerAddr channelNo; @@ -1839,6 +1840,8 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) } player->deviceIndex = deviceIndex; + + informMissing( server, xwe ); } return deviceIndex; } /* registerRemotePlayer */