snapshot on the way to working tile picker

New classes implement custom alert and its view, where most of the logic
for putting up one button per tile, hiding and showing buttons based on
what's left, etc. lives. Rough, but works well until rotated, when gets
redrawn without spaces for the buttons that could come back.
This commit is contained in:
Eric House 2017-03-14 07:23:30 -07:00
parent a804983b8e
commit 58fc0e9b81
11 changed files with 468 additions and 54 deletions

View file

@ -74,6 +74,7 @@ import org.eehouse.android.xw4.jni.UtilCtxtImpl;
import org.eehouse.android.xw4.jni.XwJNI.GamePtr;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.TilePickAlert.TilePickState;
public class BoardDelegate extends DelegateBase
implements TransportProcs.TPMsgHandler, View.OnClickListener,
@ -341,23 +342,24 @@ public class BoardDelegate extends DelegateBase
}
break;
case PICK_TILE_REQUESTBLANK: {
final int turn = (Integer)params[0];
final int col = (Integer)params[1];
final int row = (Integer)params[2];
String[] texts = (String[])params[3];
dialog = ab.setItems( texts, new OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
handleViaThread( JNICmd.CMD_SET_BLANK, turn, col,
row, item );
}
})
.setNegativeButton( android.R.string.cancel, null )
.setTitle( R.string.title_tile_picker )
.create();
}
break;
// case PICK_TILE_REQUESTBLANK: {
// final int turn = (Integer)params[0];
// final int col = (Integer)params[1];
// final int row = (Integer)params[2];
// String[] texts = (String[])params[3];
// dialog = ab
// .setItems( texts, new OnClickListener() {
// public void onClick( DialogInterface dialog,
// int item ) {
// handleViaThread( JNICmd.CMD_SET_BLANK, turn, col,
// row, item );
// }
// })
// .setNegativeButton( android.R.string.cancel, null )
// .setTitle( R.string.title_tile_picker )
// .create();
// }
// break;
// case PICK_TILE_REQUESTTRAY_BLK: {
// String[] texts = (String[])params[0];
@ -1149,6 +1151,24 @@ public class BoardDelegate extends DelegateBase
showInviteChoicesThen( params );
break;
case BLANK_PICKED:
TilePickAlert.TilePickState tps
= (TilePickAlert.TilePickState)params[0];
int[] newTiles = (int[])params[1];
handleViaThread( JNICmd.CMD_SET_BLANK, tps.playerNum,
tps.col, tps.row, newTiles[0] );
break;
case TRAY_PICKED:
tps = (TilePickAlert.TilePickState)params[0];
newTiles = (int[])params[1];
if ( tps.isInitial ) {
handleViaThread( JNICmd.CMD_TILES_PICKED, tps.playerNum, newTiles );
} else {
handleViaThread( JNICmd.CMD_COMMIT, true, true, newTiles );
}
break;
case ENABLE_SMS_DO:
post( new Runnable() {
public void run() {
@ -1787,41 +1807,21 @@ public class BoardDelegate extends DelegateBase
@Override
public void notifyPickTileBlank( int playerNum, int col, int row, String[] texts )
{
showDialogFragment( DlgID.PICK_TILE_REQUESTBLANK, playerNum, col,
row, texts );
TilePickAlert.TilePickState tps =
new TilePickAlert.TilePickState( playerNum, texts, col, row );
show( TilePickAlert.newInstance( Action.BLANK_PICKED, tps ) );
}
@Override
public void informNeedPickTiles( final boolean isInitial,
final int playerNum, final int nToPick,
public void informNeedPickTiles( boolean isInitial,
int playerNum, int nToPick,
String[] texts, int[] counts )
{
post( new Runnable() {
@Override
public void run() {
String msg = String.format( "Picked %d tiles for player #%d",
nToPick, playerNum + 1 );
makeOkOnlyBuilder( msg )
.show();
int[] noNewTiles = new int[0];
if ( isInitial ) {
handleViaThread( JNICmd.CMD_TILES_PICKED, playerNum, noNewTiles );
} else {
handleViaThread( JNICmd.CMD_COMMIT, true, true, noNewTiles );
}
}
} );
TilePickAlert.TilePickState tps
= new TilePickAlert.TilePickState( isInitial, playerNum, nToPick,
texts, counts );
show( TilePickAlert.newInstance( Action.TRAY_PICKED, tps ) );
}
// public int userPickTileTray( int playerNum, String[] texts,
// String[] curTiles, int nPicked )
// {
// String curTilesStr = TextUtils.join( ", ", curTiles );
// boolean canUndoTiles = 0 < nPicked;
// waitBlockingDialog( DlgID.PICK_TILE_REQUESTTRAY_BLK,
// UtilCtxt.PICKER_PICKALL, texts, curTilesStr,
// canUndoTiles );
// return m_resultCode;
// }
@Override
public void informNeedPassword( int player, String name )

View file

@ -95,6 +95,8 @@ public class DlgDelegate {
DROP_RELAY_ACTION,
DROP_SMS_ACTION,
INVITE_SMS,
BLANK_PICKED,
TRAY_PICKED,
// Dict Browser
FINISH_ACTION,
@ -295,9 +297,9 @@ public class DlgDelegate {
public static enum InviteMeans {
SMS, EMAIL, NFC, BLUETOOTH, CLIPBOARD, RELAY, WIFIDIRECT,
};
boolean onPosButton( Action action, Object[] params );
boolean onNegButton( Action action, Object[] params );
boolean onDismissed( Action action, Object[] params );
boolean onPosButton( Action action, Object... params );
boolean onNegButton( Action action, Object... params );
boolean onDismissed( Action action, Object... params );
void inviteChoiceMade( Action action, InviteMeans means, Object[] params );
}

View file

@ -38,7 +38,7 @@ import org.eehouse.android.xw4.loc.LocUtils;
/** Abstract superclass for Alerts that have moved from and are still created
* inside DlgDelegate
*/
public class DlgDelegateAlert extends XWDialogFragment {
abstract class DlgDelegateAlert extends XWDialogFragment {
private static final String TAG = DlgDelegateAlert.class.getSimpleName();
private static final String STATE_KEY = "STATE_KEY";
private DlgState m_state;
@ -88,7 +88,7 @@ public class DlgDelegateAlert extends XWDialogFragment {
true );
} else if ( null != state.m_onNAChecked ) {
DlgClickNotify notify = (DlgClickNotify)getActivity();
notify.onPosButton( m_state.m_onNAChecked, null );
notify.onPosButton( m_state.m_onNAChecked );
}
}
}

View file

@ -61,7 +61,6 @@ public enum DlgID {
, NOTIFY_BADWORDS
, QUERY_MOVE
, QUERY_TRADE
, PICK_TILE_REQUESTBLANK
, ASK_PASSWORD
, DLG_RETRY
, DLG_SCORES

View file

@ -89,9 +89,9 @@ class HostDelegate extends DelegateBase {
{
Action action = Action.values()[data.getIntExtra(ACTION, -1)];
if ( data.getBooleanExtra( IS_POS_BUTTON, false ) ) {
target.onPosButton( action, null );
target.onPosButton( action );
} else {
target.onNegButton( action, null );
target.onNegButton( action );
}
}
}

View file

@ -0,0 +1,150 @@
/* -*- compile-command: "cd ../../../../../../../../ && ./gradlew installXw4Debug"; -*- */
/*
* Copyright 2017 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.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.widget.Button;
import java.io.Serializable;
import junit.framework.Assert;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
import org.eehouse.android.xw4.loc.LocUtils;
public class TilePickAlert extends XWDialogFragment
implements TilePickView.TilePickListener {
private static final String TPS = "TPS";
private static final String ACTION = "ACTION";
private TilePickView m_view;
private TilePickState m_state;
private Action m_action;
private AlertDialog m_dialog;
private int[] m_selTiles;
public static class TilePickState implements Serializable {
public int col;
public int row;
public int playerNum;
public int[] counts;
public String[] faces;
public boolean isInitial;
public int nToPick;
public TilePickState( int player, String[] faces, int col, int row ) {
this.col = col; this.row = row; this.playerNum = player;
this.faces = faces;
this.nToPick = 1;
}
public TilePickState( boolean isInitial, int playerNum, int nToPick,
String[] faces, int[] counts ) {
this.playerNum = playerNum;
this.isInitial = isInitial;
this.nToPick = nToPick;
this.faces = faces;
this.counts = counts;
}
}
public static TilePickAlert newInstance( Action action, TilePickState state )
{
TilePickAlert result = new TilePickAlert();
Bundle args = new Bundle();
args.putSerializable( ACTION, action );
args.putSerializable( TPS, state );
result.setArguments( args );
return result;
}
public TilePickAlert() {}
@Override
public void onSaveInstanceState( Bundle bundle )
{
super.onSaveInstanceState( bundle );
bundle.putSerializable( TPS, m_state );
bundle.putSerializable( ACTION, m_action );
m_view.saveInstanceState( bundle );
}
@Override
public Dialog onCreateDialog( Bundle sis )
{
if ( null == sis ) {
sis = getArguments();
}
m_state = (TilePickState)sis.getSerializable( TPS );
m_action = (Action)sis.getSerializable( ACTION );
Activity activity = getActivity();
Assert.assertNotNull( activity );
m_view = (TilePickView)LocUtils.inflate( activity, R.layout.tile_picker );
m_view.init( this, m_state, sis );
DialogInterface.OnClickListener lstnr =
new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ) {
onDonePressed();
}
};
m_dialog = LocUtils.makeAlertBuilder( activity )
.setTitle( String.format( "Pick %d", m_state.nToPick ) )
.setView( m_view )
.setPositiveButton( R.string.tilepick_all, lstnr )
.create();
return m_dialog;
}
// TilePickView.TilePickListener
@Override
public void onDonePressed()
{
Activity activity = getActivity();
if ( activity instanceof DlgClickNotify ) {
DlgClickNotify notify = (DlgClickNotify)activity;
notify.onPosButton( m_action, m_state, m_selTiles );
} else {
Assert.assertTrue( !BuildConfig.DEBUG );
}
dismiss();
}
@Override
public void onTilesChanged( int nToPick, int[] newTiles )
{
m_selTiles = newTiles;
if ( null != m_dialog ) {
boolean done = nToPick == newTiles.length;
int msgID = done ? android.R.string.ok : R.string.tilepick_all;
Button button = m_dialog.getButton( AlertDialog.BUTTON_POSITIVE );
button.setText( LocUtils.getString( getContext(), msgID ) );
}
}
}

View file

@ -0,0 +1,201 @@
/* -*- compile-command: "cd ../../../../../../../../ && ./gradlew insXw4Deb"; -*- */
/*
* Copyright 2009-2017 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.text.TextUtils;
// import android.app.Dialog;
import android.content.Context;
// import android.content.DialogInterface;
// import android.content.Intent;
// import android.net.Uri;
import android.os.Bundle;
import android.util.AttributeSet;
// import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
// import android.widget.AdapterView;
// import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
// import android.widget.ListView;
import android.widget.TextView;
// import junit.framework.Assert;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.TilePickAlert.TilePickState;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
public class TilePickView extends LinearLayout {
private static final String TAG = TilePickView.class.getSimpleName();
private static final String NEW_TILES = "NEW_TILES";
private static final boolean SHOW_UNAVAIL = false;
public interface TilePickListener {
void onDonePressed();
void onTilesChanged( int nToPick, int[] newTiles );
}
private ArrayList<Integer> m_pendingTiles;
private TilePickListener m_listner;
private TilePickState m_state;
private List<Button> m_pendingButtons;
public TilePickView( Context context, AttributeSet as ) {
super( context, as );
}
protected void init( TilePickListener lstn, TilePickState state,
Bundle bundle )
{
m_state = state;
m_listner = lstn;
m_pendingTiles = (ArrayList<Integer>)bundle.getSerializable( NEW_TILES );
if ( null == m_pendingTiles ) {
DbgUtils.logd( TAG, "creating new m_pendingTiles" );
m_pendingTiles = new ArrayList<Integer>();
}
showPending();
addTileButtons();
updateDelButton();
findViewById( R.id.del ).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View view ) {
removePending();
updateDelButton();
m_listner.onTilesChanged( m_state.nToPick, getPending() );
}
} );
}
// NOT @Override!!!
protected void saveInstanceState( Bundle bundle )
{
bundle.putSerializable( NEW_TILES, m_pendingTiles );
}
private int[] getPending()
{
int[] result = new int[m_pendingTiles.size()];
for ( int ii = 0; ii < result.length; ++ii ) {
result[ii] = m_pendingTiles.get(ii);
}
return result;
}
private void addTileButtons()
{
Context context = getContext();
LinearLayout container = (LinearLayout)
findViewById( R.id.button_bar_container );
m_pendingButtons = new ArrayList<Button>();
LinearLayout bar = null;
int barLen = 0;
int nShown = 0;
for ( int ii = 0; ii < m_state.faces.length; ++ii ) {
if ( null != m_state.counts && m_state.counts[ii] == 0 && !SHOW_UNAVAIL ) {
continue;
}
final int dataIndex = ii;
final int visIndex = nShown++;
if ( null == bar || 0 == (visIndex % barLen) ) {
bar = (LinearLayout)
LocUtils.inflate( context, R.layout.tile_picker_bar );
container.addView( bar );
barLen = bar.getChildCount();
}
Button button = (Button)bar.getChildAt( visIndex % barLen );
button.setVisibility( View.VISIBLE );
updateButton( button, dataIndex, 0 );
button.setOnClickListener( new OnClickListener() {
@Override
public void onClick( View view ) {
onTileClicked( view, dataIndex );
}
} );
}
}
private void onTileClicked( View view, int index ) {
if ( m_pendingTiles.size() == m_state.nToPick ) {
removePending();
}
Button button = (Button)view;
m_pendingTiles.add( index );
m_pendingButtons.add( button );
updateDelButton();
updateButton( button, index, -1 );
showPending();
m_listner.onTilesChanged( m_state.nToPick, getPending() );
}
private void showPending()
{
TextView text = (TextView)findViewById( R.id.pending_desc );
List<String> faces = new ArrayList<String>();
for ( int indx : m_pendingTiles ) {
faces.add( m_state.faces[indx] );
}
String msg = "Current selection: " + TextUtils.join( ",", faces );
text.setText( msg );
}
private void updateButton( Button button, int index, int adjust )
{
Context context = getContext();
String face = m_state.faces[index];
if ( null != m_state.counts ) {
m_state.counts[index] += adjust;
int count = m_state.counts[index];
face = LocUtils.getString( context, R.string.tile_button_txt_fmt,
face, count );
int vis = count == 0 ? View.INVISIBLE : View.VISIBLE;
button.setVisibility( vis );
}
button.setText( face );
}
private void removePending()
{
int tile = m_pendingTiles.remove( m_pendingTiles.size() - 1 );
Button button = m_pendingButtons.remove( m_pendingButtons.size() - 1 );
updateButton( button, tile, 1 );
showPending();
}
private void updateDelButton()
{
int vis = m_pendingTiles.size() == 0 ? View.INVISIBLE : View.VISIBLE;
findViewById( R.id.del ).setVisibility( vis );
}
}

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<org.eehouse.android.xw4.TilePickView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="@+id/pending_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<ImageButton android:id="@+id/del"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@android:drawable/ic_delete"
android:visibility="invisible"
/>
</LinearLayout>
<ScrollView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<LinearLayout android:id="@+id/button_bar_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</ScrollView>
</org.eehouse.android.xw4.TilePickView>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button style="@style/evenly_spaced_horizontal"
android:visibility="invisible"
/>
<Button style="@style/evenly_spaced_horizontal"
android:visibility="invisible"
/>
<Button style="@style/evenly_spaced_horizontal"
android:visibility="invisible"
/>
<Button style="@style/evenly_spaced_horizontal"
android:visibility="invisible"
/>
</LinearLayout>

View file

@ -1537,6 +1537,8 @@
feature is not yet supported on Android.) -->
<string name="title_tile_picker">Letter for blank</string>
<string name="tile_button_txt_fmt">%1$s (%2$d)</string>
<!-- title of dialog brought up in response to the
board_menu_game_left menu. The dialog lists all tiles
remaining in the pool, i.e. not on the board or in the rack

View file

@ -2456,6 +2456,7 @@ server_commitMove( ServerCtxt* server, TrayTileSet* newTilesP )
XP_ASSERT( turn >= 0 );
nTilesMoved = model_getCurrentMoveCount( model, turn );
pool_removeTiles( server->pool, &newTiles );
fetchTiles( server, turn, nTilesMoved, NULL, &newTiles );
#ifndef XWFEATURE_STANDALONE_ONLY