stop referring to this in AlertDialog buttons

There's a problem in dual-pane mode where activites outlive
DelegateBase instances that are tied to fragments. AlertDialogs, being
bound to the MainActivity, can sometimes outlive the delegates that
create them, meaning the 'this' referred to from closures bound to
onClick() handlers can come to be invalid (e.g. referencing a removed
fragment). So add a global registry of current DelegateBase instances
by class, and from onClick() handlers fetch and use the current
instance instead of the 'this' that's bound.
This commit is contained in:
Eric House 2016-07-22 10:14:59 -07:00
parent aef95ae498
commit 7634c425ef
7 changed files with 175 additions and 111 deletions

View file

@ -181,7 +181,7 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
handleViaThread( JNICmd.CMD_RESET );
curThis().handleViaThread( JNICmd.CMD_RESET );
}
};
ab.setNegativeButton( R.string.button_retry, lstnr );
@ -190,7 +190,7 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
doRematchIf();
curThis().doRematchIf();
}
};
ab.setNegativeButton( R.string.button_rematch, lstnr );
@ -215,14 +215,14 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
BoardDelegate self = curThis();
if ( DlgID.DLG_USEDICT == dlgID ) {
setGotGameDict( m_getDict );
self.setGotGameDict( m_getDict );
} else {
DwnldDelegate
.downloadDictInBack( m_activity,
m_gi.dictLang,
m_getDict,
BoardDelegate.this );
.downloadDictInBack( self.m_activity,
self.m_gi.dictLang,
self.m_getDict, self );
}
}
};
@ -241,7 +241,7 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
deleteAndClose();
curThis().deleteAndClose();
}
};
ab.setNegativeButton( R.string.button_delete, lstnr );
@ -259,7 +259,7 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = 1;
curThis().m_resultCode = 1;
}
};
ab.setPositiveButton( DlgID.QUERY_REQUEST_BLK == dlgID ?
@ -269,7 +269,7 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = 0;
curThis().m_resultCode = 0;
}
};
ab.setNegativeButton( R.string.button_no, lstnr );
@ -290,10 +290,11 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
showNotAgainDlgThen( R.string.not_again_lookup,
R.string.
key_na_lookup,
Action.LOOKUP_ACTION );
curThis().
showNotAgainDlgThen( R.string.not_again_lookup,
R.string.
key_na_lookup,
Action.LOOKUP_ACTION );
}
};
ab.setNegativeButton( buttonTxt, lstnr );
@ -309,7 +310,7 @@ public class BoardDelegate extends DelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
m_resultCode = item;
curThis().m_resultCode = item;
}
};
ab.setItems( m_texts, lstnr );
@ -322,8 +323,9 @@ public class BoardDelegate extends DelegateBase
OnClickListener undoClicked = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = UtilCtxt.PICKER_BACKUP;
removeDialog( dlgID );
BoardDelegate self = curThis();
self.m_resultCode = UtilCtxt.PICKER_BACKUP;
self.removeDialog( dlgID );
}
};
ab.setPositiveButton( R.string.tilepick_undo,
@ -332,8 +334,9 @@ public class BoardDelegate extends DelegateBase
OnClickListener doAllClicked = new OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = UtilCtxt.PICKER_PICKALL;
removeDialog( dlgID );
BoardDelegate self = curThis();
self.m_resultCode = UtilCtxt.PICKER_PICKALL;
self.removeDialog( dlgID );
}
};
ab.setNegativeButton( R.string.tilepick_all, doAllClicked );
@ -379,32 +382,34 @@ public class BoardDelegate extends DelegateBase
break;
case DLG_INVITE:
lstnr = new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
if ( !m_relayMissing ||
! m_connTypes.contains(CommsConnType.COMMS_CONN_RELAY) ) {
Assert.assertTrue( 0 < m_nMissing );
if ( m_summary.hasRematchInfo() ) {
tryRematchInvites( true );
public void onClick( DialogInterface dialog, int item ){
BoardDelegate self = curThis();
if ( !self.m_relayMissing ||
! self.m_connTypes.contains(CommsConnType.COMMS_CONN_RELAY) ) {
Assert.assertTrue( 0 < self.m_nMissing );
if ( self.m_summary.hasRematchInfo() ) {
self.tryRematchInvites( true );
} else {
showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION,
m_sentInfo );
self.showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION,
self.m_sentInfo );
}
} else {
askDropRelay();
self.askDropRelay();
}
}
};
OnClickListener lstnrWait = new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
finish();
curThis().finish();
}
};
OnClickListener lstnrMore = new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
showOKOnlyDialog( m_sentInfo.getAsText( m_activity ) );
BoardDelegate self = curThis();
self.showOKOnlyDialog( self.m_sentInfo
.getAsText( self.m_activity ) );
}
};
@ -1384,6 +1389,12 @@ public class BoardDelegate extends DelegateBase
return m_handler;
}
@Override
protected BoardDelegate curThis()
{
return (BoardDelegate)super.curThis();
}
private void deleteAndClose()
{
GameUtils.deleteGame( m_activity, m_gameLock, false );

View file

@ -38,7 +38,11 @@ import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
@ -60,6 +64,8 @@ public class DelegateBase implements DlgClickNotify,
private View m_rootView;
private boolean m_isVisible;
private ArrayList<Runnable> m_visibleProcs = new ArrayList<Runnable>();
private static Map<Class, WeakReference<DelegateBase>> s_instances
= new HashMap<Class, WeakReference<DelegateBase>>();
public DelegateBase( Delegator delegator, Bundle bundle, int layoutID )
{
@ -117,6 +123,8 @@ public class DelegateBase implements DlgClickNotify,
protected void onResume()
{
Assert.assertFalse( s_instances.containsKey(getClass()) );
s_instances.put( getClass(), new WeakReference<DelegateBase>(this) );
m_isVisible = true;
XWService.setListener( this );
runIfVisible();
@ -124,10 +132,19 @@ public class DelegateBase implements DlgClickNotify,
protected void onPause()
{
s_instances.remove( getClass() );
m_isVisible = false;
XWService.setListener( null );
}
protected DelegateBase curThis()
{
WeakReference<DelegateBase> ref = s_instances.get( getClass() );
DelegateBase result = ref.get();
DbgUtils.logf( "%s.curThis() => %s", this.toString(), result.toString() );
return result;
}
public boolean onCreateOptionsMenu( Menu menu, MenuInflater inflater )
{
boolean handled = 0 < m_optionsMenuID;

View file

@ -382,19 +382,20 @@ public class DictsDelegate extends ListDelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
DictLoc toLoc = itemToRealLoc( moveTo[0] );
DictsDelegate self = curThis();
DictLoc toLoc = self.itemToRealLoc( moveTo[0] );
for ( XWListItem selItem : selItems ) {
DictLoc fromLoc = (DictLoc)selItem.getCached();
String name = selItem.getText();
if ( fromLoc == toLoc ) {
DbgUtils.logf( "not moving %s: same loc", name );
} else if ( DictUtils.moveDict( m_activity,
name, fromLoc,
} else if ( DictUtils.moveDict( self.m_activity,
name, fromLoc,
toLoc ) ) {
selItem.setComment( m_locNames[toLoc.ordinal()] );
selItem.setComment( self.m_locNames[toLoc.ordinal()] );
selItem.setCached( toLoc );
selItem.invalidate();
DBUtils.dictsMoveInfo( m_activity, name,
DBUtils.dictsMoveInfo( self.m_activity, name,
fromLoc, toLoc );
} else {
DbgUtils.logf( "moveDict(%s) failed", name );
@ -416,15 +417,16 @@ public class DictsDelegate extends ListDelegateBase
final XWListItem row = m_selDicts.values().iterator().next();
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
DictsDelegate self = curThis();
if ( DialogInterface.BUTTON_NEGATIVE == item
|| DialogInterface.BUTTON_POSITIVE == item ) {
setDefault( row, R.string.key_default_dict,
R.string.key_default_robodict );
self.setDefault( row, R.string.key_default_dict,
R.string.key_default_robodict );
}
if ( DialogInterface.BUTTON_NEGATIVE == item
|| DialogInterface.BUTTON_NEUTRAL == item ) {
setDefault( row, R.string.key_default_robodict,
R.string.key_default_dict );
self.setDefault( row, R.string.key_default_robodict,
R.string.key_default_dict );
}
}
};
@ -444,18 +446,18 @@ public class DictsDelegate extends ListDelegateBase
case DICT_OR_DECLINE:
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
Intent intent = getIntent();
DictsDelegate self = curThis();
Intent intent = self.getIntent();
int lang = intent.getIntExtra( MultiService.LANG, -1 );
String name = intent.getStringExtra( MultiService.DICT );
m_launchedForMissing = true;
self.m_launchedForMissing = true;
DwnldDelegate
.downloadDictInBack( m_activity, lang,
name, DictsDelegate.this );
.downloadDictInBack( self.m_activity, lang, name, self );
}
};
lstnr2 = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
finish();
curThis().finish();
}
};
@ -1085,6 +1087,12 @@ public class DictsDelegate extends ListDelegateBase
return canHandle;
}
@Override
protected DictsDelegate curThis()
{
return (DictsDelegate)super.curThis();
}
//////////////////////////////////////////////////////////////////////
// XWListItem.ExpandedListener interface
//////////////////////////////////////////////////////////////////////

View file

@ -182,8 +182,9 @@ public class GameConfigDelegate extends DelegateBase
public void
onClick( DialogInterface dlg,
int button ) {
getPlayerSettings( dlg );
loadPlayersList();
GameConfigDelegate self = curThis();
self.getPlayerSettings( dlg );
self.loadPlayersList();
}
})
.setNegativeButton( android.R.string.cancel, null )
@ -211,7 +212,7 @@ public class GameConfigDelegate extends DelegateBase
dlpos = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
loadPlayersList();
curThis().loadPlayersList();
}
};
dialog = makeAlertBuilder()
@ -224,9 +225,10 @@ public class GameConfigDelegate extends DelegateBase
@Override
public void onDismiss( DialogInterface di )
{
if ( m_gi.forceRemoteConsistent() ) {
showToast( R.string.forced_consistent );
loadPlayersList();
GameConfigDelegate self = curThis();
if ( self.m_gi.forceRemoteConsistent() ) {
self.showToast( R.string.forced_consistent );
self.loadPlayersList();
}
}
};
@ -237,9 +239,10 @@ public class GameConfigDelegate extends DelegateBase
dlpos = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
applyChanges( true );
GameConfigDelegate self = curThis();
self.applyChanges( true );
if ( DlgID.CONFIRM_CHANGE_PLAY == dlgID ) {
launchGame();
self.launchGame();
}
}
};
@ -251,7 +254,7 @@ public class GameConfigDelegate extends DelegateBase
dlpos = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
launchGame();
curThis().launchGame();
}
};
} else {
@ -263,7 +266,7 @@ public class GameConfigDelegate extends DelegateBase
dialog.setOnDismissListener( new DialogInterface.
OnDismissListener() {
public void onDismiss( DialogInterface di ) {
finish();
curThis().finish();
}
});
break;
@ -289,16 +292,17 @@ public class GameConfigDelegate extends DelegateBase
final DialogInterface.OnClickListener lstnr =
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg, int button ) {
m_conTypes = items.getTypes();
GameConfigDelegate self = curThis();
self.m_conTypes = items.getTypes();
if ( cb.isChecked()) {
XWPrefs.setAddrTypes( m_activity, m_conTypes );
XWPrefs.setAddrTypes( self.m_activity, self.m_conTypes );
}
m_car.populate( m_activity, m_conTypes );
self.m_car.populate( self.m_activity, self.m_conTypes );
setConnLabel();
setupRelayStuffIf( false );
showHideRelayStuff();
self.setConnLabel();
self.setupRelayStuffIf( false );
self.showHideRelayStuff();
}
};
@ -794,6 +798,12 @@ public class GameConfigDelegate extends DelegateBase
return consumed;
}
@Override
protected GameConfigDelegate curThis()
{
return (GameConfigDelegate)super.curThis();
}
private void deleteGame()
{
GameUtils.deleteGame( m_activity, m_rowid, false );

View file

@ -620,18 +620,20 @@ public class GamesListDelegate extends ListDelegateBase
case WARN_NODICT_SUBST:
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
GamesListDelegate self = curThis();
// no name, so user must pick
if ( null == m_missingDictName ) {
DictsDelegate.downloadForResult( m_activity,
RequestCode
.REQUEST_LANG_GL,
m_missingDictLang );
DictsDelegate
.downloadForResult( self.m_activity,
RequestCode
.REQUEST_LANG_GL,
self.m_missingDictLang );
} else {
DwnldDelegate
.downloadDictInBack( m_activity,
m_missingDictLang,
m_missingDictName,
GamesListDelegate.this );
.downloadDictInBack( self.m_activity,
self.m_missingDictLang,
self.m_missingDictName,
self );
}
}
};
@ -660,7 +662,7 @@ public class GamesListDelegate extends ListDelegateBase
if ( DlgID.WARN_NODICT_SUBST == dlgID ) {
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
showDialog( DlgID.SHOW_SUBST );
curThis().showDialog( DlgID.SHOW_SUBST );
}
};
ab.setNeutralButton( R.string.button_substdict, lstnr );
@ -674,15 +676,16 @@ public class GamesListDelegate extends ListDelegateBase
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg,
int which ) {
GamesListDelegate self = curThis();
int pos = ((AlertDialog)dlg).getListView().
getCheckedItemPosition();
String dict = m_sameLangDicts[pos];
String dict = self.m_sameLangDicts[pos];
dict = DictLangCache.stripCount( dict );
if ( GameUtils.replaceDicts( m_activity,
m_missingDictRowId,
m_missingDictName,
if ( GameUtils.replaceDicts( self.m_activity,
self.m_missingDictRowId,
self.m_missingDictName,
dict ) ) {
launchGameIf();
self.launchGameIf();
}
}
};
@ -702,10 +705,11 @@ public class GamesListDelegate extends ListDelegateBase
case RENAME_GAME:
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = m_namer.getName();
DBUtils.setName( m_activity, m_rowid,
GamesListDelegate self = curThis();
String name = self.m_namer.getName();
DBUtils.setName( self.m_activity, self.m_rowid,
name );
m_adapter.invalName( m_rowid );
self.m_adapter.invalName( self.m_rowid );
}
};
GameSummary summary = DBUtils.getSummary( m_activity, m_rowid );
@ -719,11 +723,12 @@ public class GamesListDelegate extends ListDelegateBase
case RENAME_GROUP:
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = m_namer.getName();
DBUtils.setGroupName( m_activity,
m_groupid, name );
reloadGame( m_rowid );
mkListAdapter();
GamesListDelegate self = curThis();
String name = self.m_namer.getName();
DBUtils.setGroupName( self.m_activity,
self.m_groupid, name );
self.reloadGame( self.m_rowid );
self.mkListAdapter();
}
};
dialog = buildNamerDlg( m_adapter.groupName( m_groupid ),
@ -735,15 +740,16 @@ public class GamesListDelegate extends ListDelegateBase
case NEW_GROUP:
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = m_namer.getName();
DBUtils.addGroup( m_activity, name );
mkListAdapter();
showNewGroupIf();
GamesListDelegate self = curThis();
String name = self.m_namer.getName();
DBUtils.addGroup( self.m_activity, name );
self.mkListAdapter();
self.showNewGroupIf();
}
};
lstnr2 = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
showNewGroupIf();
curThis().showNewGroupIf();
}
};
dialog = buildNamerDlg( "", R.string.newgroup_label,
@ -758,13 +764,14 @@ public class GamesListDelegate extends ListDelegateBase
final int[] selItem = {-1}; // hack!!!!
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlgi, int item ) {
GamesListDelegate self = curThis();
selItem[0] = item;
AlertDialog dlg = (AlertDialog)dlgi;
Button btn =
dlg.getButton( AlertDialog.BUTTON_POSITIVE );
boolean enabled = startGroup == -1;
if ( !enabled ) {
long newGroup = m_adapter.getGroupIDFor( item );
long newGroup = self.m_adapter.getGroupIDFor( item );
enabled = newGroup != startGroup;
}
btn.setEnabled( enabled );
@ -772,20 +779,22 @@ public class GamesListDelegate extends ListDelegateBase
};
lstnr2 = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
GamesListDelegate self = curThis();
Assert.assertTrue( -1 != selItem[0] );
long gid = m_adapter.getGroupIDFor( selItem[0] );
for ( long rowid : m_rowids ) {
DBUtils.moveGame( m_activity, rowid, gid );
long gid = self.m_adapter.getGroupIDFor( selItem[0] );
for ( long rowid : self.m_rowids ) {
DBUtils.moveGame( self.m_activity, rowid, gid );
}
DBUtils.setGroupExpanded( m_activity, gid, true );
mkListAdapter();
DBUtils.setGroupExpanded( self.m_activity, gid, true );
self.mkListAdapter();
}
};
OnClickListener lstnr3 =
new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
m_moveAfterNewGroup = true;
showDialog( DlgID.NEW_GROUP );
GamesListDelegate self = curThis();
self.m_moveAfterNewGroup = true;
self.showDialog( DlgID.NEW_GROUP );
}
};
String[] groups = m_adapter.groupNames();
@ -833,12 +842,12 @@ public class GamesListDelegate extends ListDelegateBase
final EditText edit = (EditText)view.findViewById( R.id.edit );
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
makeThenLaunchOrConfigure( edit, true, false );
curThis().makeThenLaunchOrConfigure( edit, true, false );
}
};
lstnr2 = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
makeThenLaunchOrConfigure( edit, false, false );
curThis().makeThenLaunchOrConfigure( edit, false, false );
}
};
@ -862,7 +871,7 @@ public class GamesListDelegate extends ListDelegateBase
public void onClick( DialogInterface dlg, int item ) {
EditText edit = (EditText)((Dialog)dlg)
.findViewById( R.id.edit );
startRematchWithName( edit );
curThis().startRematchWithName( edit );
}
} )
.create();
@ -1050,6 +1059,12 @@ public class GamesListDelegate extends ListDelegateBase
}
}
@Override
protected GamesListDelegate curThis()
{
return (GamesListDelegate)super.curThis();
}
// OnItemLongClickListener interface
public boolean onItemLongClick( AdapterView<?> parent, View view,
int position, long id ) {
@ -1813,7 +1828,7 @@ public class GamesListDelegate extends ListDelegateBase
final boolean solo = isSolos[ii];
button.setOnClickListener( new View.OnClickListener() {
public void onClick( View view ) {
handleNewGameButton( solo );
curThis().handleNewGameButton( solo );
}
} );
}

View file

@ -82,7 +82,8 @@ public class PrefsDelegate extends DelegateBase
confirmID = R.string.confirm_revert_colors;
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
SharedPreferences sp = getSharedPreferences();
PrefsDelegate self = (PrefsDelegate)curThis();
SharedPreferences sp = self.getSharedPreferences();
SharedPreferences.Editor editor = sp.edit();
int[] colorKeys = {
R.string.key_player0,
@ -103,7 +104,7 @@ public class PrefsDelegate extends DelegateBase
editor.remove( getString(colorKey) );
}
editor.commit();
relaunch();
self.relaunch();
}
};
break;
@ -111,11 +112,12 @@ public class PrefsDelegate extends DelegateBase
confirmID = R.string.confirm_revert_all;
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
SharedPreferences sp = getSharedPreferences();
PrefsDelegate self = (PrefsDelegate)curThis();
SharedPreferences sp = self.getSharedPreferences();
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
relaunch();
self.relaunch();
}
};
break;
@ -149,13 +151,13 @@ public class PrefsDelegate extends DelegateBase
Button button = (Button)findViewById( R.id.revert_colors );
button.setOnClickListener( new View.OnClickListener() {
public void onClick( View v ) {
showDialog( DlgID.REVERT_COLORS );
curThis().showDialog( DlgID.REVERT_COLORS );
}
} );
button = (Button)findViewById( R.id.revert_all );
button.setOnClickListener(new View.OnClickListener() {
public void onClick( View v ) {
showDialog( DlgID.REVERT_ALL );
curThis().showDialog( DlgID.REVERT_ALL );
}
} );

View file

@ -147,13 +147,14 @@ public class SMSInviteDelegate extends InviteDelegate {
namerView.setKeyListener(DialerKeyListener.getInstance());
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
SMSInviteDelegate self = (SMSInviteDelegate)curThis();
String number = namerView.getName();
PhoneRec rec = new PhoneRec( number );
m_pendingNumber = number;
m_pendingName = null;
showConfirmThen( R.string.warn_unlimited,
R.string.button_yes,
Action.POST_WARNING_ACTION );
self.m_pendingNumber = number;
self.m_pendingName = null;
self.showConfirmThen( R.string.warn_unlimited,
R.string.button_yes,
Action.POST_WARNING_ACTION );
}
};
dialog = makeAlertBuilder()