Merge remote branch 'origin/android_branch' into android_branch

This commit is contained in:
Eric House 2014-04-23 07:40:46 -07:00
commit 6a4c599da5
25 changed files with 503 additions and 221 deletions

View file

@ -22,7 +22,7 @@
to come from a domain that you own or have control over. --> to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eehouse.android.xw4" package="org.eehouse.android.xw4"
android:versionCode="75" android:versionCode="76"
android:versionName="@string/app_version" android:versionName="@string/app_version"
> >

View file

@ -49,7 +49,7 @@
<!-- extension targets. Uncomment the ones where you want to do custom work <!-- extension targets. Uncomment the ones where you want to do custom work
in between standard targets --> in between standard targets -->
<property name="INITIAL_CLIENT_VERS" value="3"/> <property name="INITIAL_CLIENT_VERS" value="4"/>
<target name="-pre-clean"> <target name="-pre-clean">
<exec dir="." executable="../scripts/ndksetup.sh" output="/dev/null" <exec dir="." executable="../scripts/ndksetup.sh" output="/dev/null"
failonerror="true" > failonerror="true" >

View file

@ -6,10 +6,10 @@
android:layout_height="fill_parent" android:layout_height="fill_parent"
> >
<ExpandableListView android:id="@id/android:list" <ListView android:id="@id/android:list"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_weight="1" android:layout_weight="1"
android:drawSelectorOnTop="false" android:drawSelectorOnTop="false"
/> />
</LinearLayout> </LinearLayout>

View file

@ -1,15 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.eehouse.android.xw4.GameListGroup <org.eehouse.android.xw4.GameListGroup
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/game_name" android:layout_width="fill_parent"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_height="wrap_content" android:orientation="horizontal"
android:gravity="right" android:background="#FF7F7F7F"
android:singleLine="true" >
android:textAppearance="?android:attr/textAppearanceMedium"
android:paddingTop="3dp" <TextView android:id="@+id/game_name"
android:paddingBottom="3dp" android:layout_width="fill_parent"
android:textStyle="italic" android:layout_height="wrap_content"
android:background="#FF7F7F7F" android:gravity="left"
/> android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:textStyle="italic"
android:layout_weight="1"
/>
<ImageButton android:id="@+id/expander"
style="@style/expander_button"
/>
</org.eehouse.android.xw4.GameListGroup>

View file

@ -70,9 +70,7 @@
/> />
<ImageButton android:id="@+id/expander" <ImageButton android:id="@+id/expander"
android:layout_width="32dp" style="@style/expander_button"
android:layout_height="32dp"
android:src="@drawable/expander_ic_maximized"
/> />
</LinearLayout> </LinearLayout>

View file

@ -5,20 +5,16 @@
</style> </style>
</head> </head>
<body> <body>
<b>Crosswords 4.4 beta 82 release</b> <b>Crosswords 4.4 beta 83 release</b>
<h3>New with this release</h3> <h3>New with this release</h3>
<ul> <ul>
<li>Add ability to select studylist items so subsets can be removed or copied out</li> <li>Bug fix (obscure): don't mangle unicode model names</li>
<li>Enable studylists by default for new installs</li>
<li>Fix bug with Wordlist browser selections</li>
<li>Improve Lookup/Add-to-studylist dialog</li>
<li>Fix bug clearing pending move when you long-tap a letter to see words it's in</li>
<li>Other performance and minor UI improvements</li>
</ul> </ul>
<h3>Next up</h3> <h3>Next up</h3>
<ul> <ul>
<li>Much better support for localization</li>
<li>Look, again, at play via Bluetooth now that HTC phones aren't quite so common</li> <li>Look, again, at play via Bluetooth now that HTC phones aren't quite so common</li>
<li>Fix SMS play for KitKat</li> <li>Fix SMS play for KitKat</li>
<li>Offer "Rematch" when game's over</li> <li>Offer "Rematch" when game's over</li>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_version">4.4 beta 82</string> <string name="app_version">4.4 beta 83</string>
</resources> </resources>

View file

@ -66,5 +66,11 @@
<item name="android:layout_weight">1</item> <item name="android:layout_weight">1</item>
</style> </style>
<style name="expander_button">
<item name="android:layout_width">32dp</item>
<item name="android:layout_height">32dp</item>
<item name="android:src">@drawable/expander_ic_maximized</item>
</style>
</resources> </resources>

View file

@ -545,7 +545,7 @@ public class BoardDelegate extends DelegateBase
m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false );
m_overNotShown = true; m_overNotShown = true;
NFCUtils.register( m_activity ); // Don't seem to need to unregister... NFCUtils.register( m_activity, this ); // Don't seem to need to unregister...
setBackgroundColor(); setBackgroundColor();
setKeepScreenOn(); setKeepScreenOn();

View file

@ -205,7 +205,7 @@ public class DBUtils {
int col = cursor.getColumnIndex( DBHelper.CONTYPE ); int col = cursor.getColumnIndex( DBHelper.CONTYPE );
if ( 0 <= col ) { if ( 0 <= col ) {
tmp = cursor.getInt( col ); tmp = cursor.getInt( col );
summary.conType = CommsConnType.values()[tmp]; summary.conType = CommsAddrRec.CommsConnType.values()[tmp];
col = cursor.getColumnIndex( DBHelper.SEED ); col = cursor.getColumnIndex( DBHelper.SEED );
if ( 0 < col ) { if ( 0 < col ) {
summary.seed = cursor.getInt( col ); summary.seed = cursor.getInt( col );
@ -964,14 +964,16 @@ public class DBUtils {
// Groups stuff // Groups stuff
public static class GameGroupInfo { public static class GameGroupInfo {
public String m_name; public String m_name;
public int m_count;
public boolean m_expanded; public boolean m_expanded;
public long m_lastMoveTime; public long m_lastMoveTime;
public boolean m_hasTurn; public boolean m_hasTurn;
public boolean m_turnLocal; public boolean m_turnLocal;
public GameGroupInfo( String name, boolean expanded ) { public GameGroupInfo( String name, int count, boolean expanded ) {
m_name = name; m_expanded = expanded; m_name = name; m_expanded = expanded;
m_lastMoveTime = 0; m_lastMoveTime = 0;
m_count = count;
} }
} }
@ -1010,44 +1012,58 @@ public class DBUtils {
return thumb; return thumb;
} }
// Return map of string (group name) to info about all games in private static HashMap<Long, Integer> getGameCounts( SQLiteDatabase db )
// that group.
public static HashMap<Long,GameGroupInfo> getGroups( Context context )
{ {
return getGroups( context, 0 ); HashMap<Long, Integer> result = new HashMap<Long, Integer>();
String query = "SELECT %s, count(%s) as cnt FROM %s GROUP BY %s";
query = String.format( query, DBHelper.GROUPID, DBHelper.GROUPID,
DBHelper.TABLE_NAME_SUM, DBHelper.GROUPID );
Cursor cursor = db.rawQuery( query, null );
int rowIndex = cursor.getColumnIndex( DBHelper.GROUPID );
int cntIndex = cursor.getColumnIndex( "cnt" );
while ( cursor.moveToNext() ) {
long row = cursor.getLong(rowIndex);
int count = cursor.getInt(cntIndex);
result.put( row, count );
}
cursor.close();
return result;
} }
private static HashMap<Long,GameGroupInfo> getGroups( Context context, // Map of groups rowid (= summaries.groupid) to group info record
int nRows ) protected static HashMap<Long,GameGroupInfo> getGroups( Context context )
{ {
if ( null == s_groupsCache ) { if ( null == s_groupsCache ) {
HashMap<Long,GameGroupInfo> result = HashMap<Long,GameGroupInfo> result =
new HashMap<Long,GameGroupInfo>(); new HashMap<Long,GameGroupInfo>();
String[] columns = { ROW_ID, DBHelper.GROUPNAME,
DBHelper.EXPANDED }; // Select all groups. For each group get the number of games in
String limit = 0 == nRows ? null : String.format( "%d", nRows ); // that group. There should be a way to do that with one query
// but I can't figure it out.
String query = "SELECT rowid, groupname as groups_groupname, "
+ " groups.expanded as groups_expanded FROM groups";
DbgUtils.logf( "query: %s", query );
initDB( context ); initDB( context );
synchronized( s_dbHelper ) { synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase(); SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_GROUPS, columns,
null, // selection HashMap<Long, Integer> map = getGameCounts( db );
null, // args
null, // groupBy Cursor cursor = db.rawQuery( query, null );
null, // having int idIndex = cursor.getColumnIndex( "rowid" );
null, //orderby int nameIndex = cursor.getColumnIndex( "groups_groupname" );
limit int expandedIndex = cursor.getColumnIndex( "groups_expanded" );
);
int idIndex = cursor.getColumnIndex( ROW_ID );
int nameIndex = cursor.getColumnIndex( DBHelper.GROUPNAME );
int expandedIndex = cursor.getColumnIndex( DBHelper.EXPANDED );
while ( cursor.moveToNext() ) { while ( cursor.moveToNext() ) {
String name = cursor.getString( nameIndex );
long id = cursor.getLong( idIndex ); long id = cursor.getLong( idIndex );
String name = cursor.getString( nameIndex );
Assert.assertNotNull( name ); Assert.assertNotNull( name );
boolean expanded = 0 != cursor.getInt( expandedIndex ); boolean expanded = 0 != cursor.getInt( expandedIndex );
result.put( id, new GameGroupInfo( name, expanded ) ); int count = map.containsKey( id ) ? map.get( id ) : 0;
result.put( id, new GameGroupInfo( name, count, expanded ) );
} }
cursor.close(); cursor.close();
@ -1136,6 +1152,28 @@ public class DBUtils {
} }
} }
public static int countGames( Context context )
{
int result = 0;
String[] columns = { ROW_ID };
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
null, // selection
null, // args
null, // groupBy
null, // having
null
);
result = cursor.getCount();
cursor.close();
db.close();
}
DbgUtils.logf( "DBUtils.countGames()=>%d", result );
return result;
}
public static long[] getGroupGames( Context context, long groupID ) public static long[] getGroupGames( Context context, long groupID )
{ {
long[] result = null; long[] result = null;
@ -1202,7 +1240,7 @@ public class DBUtils {
public static long getAnyGroup( Context context ) public static long getAnyGroup( Context context )
{ {
long result = GROUPID_UNSPEC; long result = GROUPID_UNSPEC;
HashMap<Long,GameGroupInfo> groups = getGroups( context, 1 ); HashMap<Long,GameGroupInfo> groups = getGroups( context );
Iterator<Long> iter = groups.keySet().iterator(); Iterator<Long> iter = groups.keySet().iterator();
if ( iter.hasNext() ) { if ( iter.hasNext() ) {
result = iter.next(); result = iter.next();

View file

@ -0,0 +1,47 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009-2014 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.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
public class DrawSelDelegate {
private View m_view;
private Drawable m_origDrawable;
private static ColorDrawable s_selDrawable =
new ColorDrawable( XWApp.SEL_COLOR );
protected DrawSelDelegate( View view )
{
m_view = view;
}
protected void showSelected( boolean selected )
{
if ( selected ) {
m_origDrawable = m_view.getBackground();
m_view.setBackgroundDrawable( s_selDrawable );
} else {
m_view.setBackgroundDrawable( m_origDrawable );
}
}
}

View file

@ -52,12 +52,11 @@ public class ExpiringDelegate {
private long m_startSecs; private long m_startSecs;
private Runnable m_runnable = null; private Runnable m_runnable = null;
private boolean m_selected; private boolean m_selected;
private Drawable m_origDrawable;
// these can be static as drawing's all in same thread. // these can be static as drawing's all in same thread.
private static Rect s_rect; private static Rect s_rect;
private static Paint s_paint; private static Paint s_paint;
private static float[] s_points; private static float[] s_points;
private static Drawable s_selDrawable; private DrawSelDelegate m_dsdel;
static { static {
s_rect = new Rect(); s_rect = new Rect();
@ -65,14 +64,13 @@ public class ExpiringDelegate {
s_paint.setStyle(Paint.Style.STROKE); s_paint.setStyle(Paint.Style.STROKE);
s_paint.setStrokeWidth( 1 ); s_paint.setStrokeWidth( 1 );
s_points = new float[4*6]; s_points = new float[4*6];
s_selDrawable = new ColorDrawable( XWApp.SEL_COLOR );
} }
public ExpiringDelegate( Context context, View view ) public ExpiringDelegate( Context context, View view )
{ {
m_context = context; m_context = context;
m_view = view; m_view = view;
m_origDrawable = view.getBackground(); m_dsdel = new DrawSelDelegate( view );
} }
public void setHandler( Handler handler ) public void setHandler( Handler handler )
@ -101,12 +99,7 @@ public class ExpiringDelegate {
public void setSelected( boolean selected ) public void setSelected( boolean selected )
{ {
m_selected = selected; m_selected = selected;
if ( selected ) { m_dsdel.showSelected( m_selected );
m_origDrawable = m_view.getBackground();
m_view.setBackgroundDrawable( s_selDrawable );
} else {
m_view.setBackgroundDrawable( m_origDrawable );
}
} }
public void onDraw( Canvas canvas ) public void onDraw( Canvas canvas )

View file

@ -49,6 +49,8 @@ public class ExpiringLinearLayout extends LinearLayout {
protected void onDraw( Canvas canvas ) protected void onDraw( Canvas canvas )
{ {
super.onDraw( canvas ); super.onDraw( canvas );
m_delegate.onDraw( canvas ); if ( null != m_delegate ) {
m_delegate.onDraw( canvas );
}
} }
} }

View file

@ -28,7 +28,6 @@ import android.widget.TextView;
class ExpiringTextView extends TextView { class ExpiringTextView extends TextView {
private ExpiringDelegate m_delegate = null; private ExpiringDelegate m_delegate = null;
private Context m_context; private Context m_context;
protected boolean m_selected = false;
public ExpiringTextView( Context context, AttributeSet attrs ) public ExpiringTextView( Context context, AttributeSet attrs )
{ {
@ -53,12 +52,6 @@ class ExpiringTextView extends TextView {
} }
} }
protected void toggleSelected()
{
m_selected = !m_selected;
getDelegate().setSelected( m_selected );
}
@Override @Override
protected void onDraw( Canvas canvas ) protected void onDraw( Canvas canvas )
{ {

View file

@ -24,8 +24,8 @@ import android.database.DataSetObserver;
import android.os.Handler; import android.os.Handler;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ExpandableListAdapter; import android.widget.ListAdapter;
import android.widget.ExpandableListView; import android.widget.ListView;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; // class is not synchronized import java.util.HashMap; // class is not synchronized
@ -40,18 +40,21 @@ import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.DBUtils.GameGroupInfo; import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
public class GameListAdapter implements ExpandableListAdapter { public class GameListAdapter extends XWListAdapter
implements GameListGroup.GroupStateListener {
private Context m_context; private Context m_context;
private ExpandableListView m_list; private ListView m_list;
private int m_fieldID; private int m_fieldID;
private Handler m_handler; private Handler m_handler;
private SelectableItem m_cb; private SelectableItem m_cb;
private long[] m_positions; private long[] m_positions;
public GameListAdapter( Context context, ExpandableListView list, public GameListAdapter( Context context, ListView list,
Handler handler, SelectableItem cb, Handler handler, SelectableItem cb,
long[] positions, String fieldName ) long[] positions, String fieldName )
{ {
super( 0 );
m_context = context; m_context = context;
m_list = list; m_list = list;
m_handler = handler; m_handler = handler;
@ -61,6 +64,23 @@ public class GameListAdapter implements ExpandableListAdapter {
m_fieldID = fieldToID( fieldName ); m_fieldID = fieldToID( fieldName );
} }
@Override
public int getCount()
{
HashMap<Long,GameGroupInfo> groups = DBUtils.getGroups( m_context );
int count = groups.size();
for ( GameGroupInfo ggi : groups.values() ) {
if ( ggi.m_expanded ) {
count += ggi.m_count;
}
}
// DbgUtils.logf( "GameListAdapter.getCount() => %d", count );
return count;
}
@Override
public int getViewTypeCount() { return 2; }
public long[] getGroupPositions() public long[] getGroupPositions()
{ {
Set<Long> keys = gameInfo().keySet(); // do not modify!!!! Set<Long> keys = gameInfo().keySet(); // do not modify!!!!
@ -103,15 +123,16 @@ public class GameListAdapter implements ExpandableListAdapter {
return success; return success;
} }
public void expandGroups( ExpandableListView view ) public void expandGroups( ListView view )
{ {
HashMap<Long,GameGroupInfo> info = gameInfo(); DbgUtils.logf( "expandGroups not implemented" );
for ( int ii = 0; ii < info.size(); ++ii ) { // HashMap<Long,GameGroupInfo> info = gameInfo();
GameGroupInfo ggi = getInfoForGroup( ii ); // for ( int ii = 0; ii < info.size(); ++ii ) {
if ( ggi.m_expanded ) { // GameGroupInfo ggi = getInfoForGroup( ii );
view.expandGroup( ii ); // if ( ggi.m_expanded ) {
} // view.expandGroup( ii );
} // }
// }
} }
public long getRowIDFor( int group, int child ) public long getRowIDFor( int group, int child )
@ -126,11 +147,13 @@ public class GameListAdapter implements ExpandableListAdapter {
public long getRowIDFor( long packedPosition ) public long getRowIDFor( long packedPosition )
{ {
int childPosition = ExpandableListView. // int childPosition = ListView.
getPackedPositionChild( packedPosition ); // getPackedPositionChild( packedPosition );
int groupPosition = ExpandableListView. // int groupPosition = ListView.
getPackedPositionGroup( packedPosition ); // getPackedPositionGroup( packedPosition );
return getRowIDFor( groupPosition, childPosition ); // return getRowIDFor( groupPosition, childPosition );
Assert.fail();
return 0;
} }
public long getGroupIDFor( int groupPos ) public long getGroupIDFor( int groupPos )
@ -156,121 +179,202 @@ public class GameListAdapter implements ExpandableListAdapter {
deselectGroups( groupIDs ); deselectGroups( groupIDs );
} }
//////////////////////////////////////////////////////////////////////
// GroupStateListener interface
//////////////////////////////////////////////////////////////////////
public void onGroupExpandedChanged( int groupPosition, boolean expanded )
{
if ( expanded ) {
onGroupExpanded( groupPosition );
} else {
onGroupCollapsed( groupPosition );
}
}
//////////////////////////////////////////////////////////////////////////
// ListAdapter interface
//////////////////////////////////////////////////////////////////////////
public View getView( final int position, View convertView, ViewGroup parent )
{
DbgUtils.logf( "getView(convertView=%H)", convertView );
View result = null;
HashMap<Long,GameGroupInfo> info = gameInfo();
int groupPosition = 0;
int indx = position;
long[] groupPosns = getGroupPositions();
for ( long groupID : groupPosns ) {
GameGroupInfo groupInfo = info.get( groupID );
if ( indx == 0 ) {
int nKids = getChildrenCount( groupPosition );
GameListGroup group =
GameListGroup.makeForPosition( m_context, groupPosition,
groupID, nKids,
groupInfo.m_expanded,
m_cb, this );
if ( !groupInfo.m_expanded ) {
GameGroupInfo ggi = getInfoForGroup( groupPosition );
group.setPct( m_handler, ggi.m_hasTurn, ggi.m_turnLocal,
ggi.m_lastMoveTime );
}
String name = m_context.getString( R.string.group_namef,
groupNames()[groupPosition],
nKids );
group.setText( name );
group.setSelected( m_cb.getSelected( group ) );
result = group;
break;
} else {
int count = groupInfo.m_expanded ? groupInfo.m_count : 0;
// int count = groupInfo.m_count;
// DbgUtils.logf( "group[%d] visible count: %d", groupPosition, count );
if ( indx <= count ) {
long[] rows = DBUtils.getGroupGames( m_context, groupID );
long rowid = rows[indx - 1];
result =
GameListItem.makeForRow( m_context, rowid, m_handler,
groupPosition, m_fieldID, m_cb );
result.setVisibility( groupInfo.m_expanded ?
View.VISIBLE : View.GONE );
break;
}
indx -= 1 + count;
++groupPosition;
}
}
DbgUtils.logf( "GameListAdapter.getView(pos=%d, group=%d)=>%H",
position, groupPosition, result );
return result;
}
@Override
public Object getItem( int position )
{
return getView( position, null, null );
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// ExpandableListAdapter interface // ExpandableListAdapter interface
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
public long getCombinedGroupId( long groupId ) // public long getCombinedGroupId( long groupId )
{ // {
return groupId; // return groupId;
} // }
public long getCombinedChildId( long groupId, long childId ) // public long getCombinedChildId( long groupId, long childId )
{ // {
return groupId << 16 | childId; // return groupId << 16 | childId;
} // }
public boolean isEmpty() { return false; } // public boolean isEmpty() { return false; }
public void onGroupCollapsed( int groupPosition )
private void onGroupCollapsed( int groupPosition )
{ {
long groupid = getGroupIDFor( groupPosition ); long groupid = getGroupIDFor( groupPosition );
DBUtils.setGroupExpanded( m_context, groupid, false ); DBUtils.setGroupExpanded( m_context, groupid, false );
long[] rowids = DBUtils.getGroupGames( m_context, groupid ); long[] rowids = DBUtils.getGroupGames( m_context, groupid );
deselectGames( rowids ); deselectGames( rowids );
notifyDataSetChanged();
} }
public void onGroupExpanded( int groupPosition ) private void onGroupExpanded( int groupPosition )
{ {
long groupid = getGroupIDFor( groupPosition ); long groupid = getGroupIDFor( groupPosition );
DBUtils.setGroupExpanded( m_context, groupid, true ); DBUtils.setGroupExpanded( m_context, groupid, true );
notifyDataSetChanged();
} }
public boolean areAllItemsEnabled() { return true; } // public boolean areAllItemsEnabled() { return true; }
public boolean isChildSelectable( int groupPosition, int childPosition ) // public boolean isChildSelectable( int groupPosition, int childPosition )
{ return true; } // { return true; }
public View getChildView( int groupPosition, int childPosition, // public View getChildView( int groupPosition, int childPosition,
boolean isLastChild, View convertView, // boolean isLastChild, View convertView,
ViewGroup parent) // ViewGroup parent)
{ // {
View result = null; // View result = null;
if ( null != convertView ) { // if ( null != convertView ) {
// DbgUtils.logf( "getChildView gave non-null convertView" ); // // DbgUtils.logf( "getChildView gave non-null convertView" );
if ( convertView instanceof GameListItem ) { // if ( convertView instanceof GameListItem ) {
GameListItem child = (GameListItem)convertView; // GameListItem child = (GameListItem)convertView;
long rowid = getRowIDFor( groupPosition, childPosition ); // long rowid = getRowIDFor( groupPosition, childPosition );
if ( child.getRowID() == rowid ) { // if ( child.getRowID() == rowid ) {
child.setSelected( m_cb.getSelected( child ) ); // child.setSelected( m_cb.getSelected( child ) );
result = child; // result = child;
} // }
} // }
} // }
if ( null == result ) { // if ( null == result ) {
result = getChildView( groupPosition, childPosition ); // result = getChildView( groupPosition, childPosition );
} // }
return result; // return result;
} // }
private View getChildView( int groupPosition, int childPosition ) // private View getChildView( int groupPosition, int childPosition )
{ // {
long rowid = getRowIDFor( groupPosition, childPosition ); // long rowid = getRowIDFor( groupPosition, childPosition );
GameListItem result = // GameListItem result =
GameListItem.makeForRow( m_context, rowid, m_handler, // GameListItem.makeForRow( m_context, rowid, m_handler,
groupPosition, m_fieldID, m_cb ); // groupPosition, m_fieldID, m_cb );
result.setSelected( m_cb.getSelected( result ) ); // result.setSelected( m_cb.getSelected( result ) );
return result; // return result;
} // }
public View getGroupView( int groupPosition, boolean isExpanded, // public View getGroupView( int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent ) // View convertView, ViewGroup parent )
{ // {
// if ( null != convertView ) { // // if ( null != convertView ) {
// DbgUtils.logf( "getGroupView gave non-null convertView" ); // // DbgUtils.logf( "getGroupView gave non-null convertView" );
// } // // }
long groupID = getGroupIDFor( groupPosition ); // long groupID = getGroupIDFor( groupPosition );
GameListGroup view = // GameListGroup view =
GameListGroup.makeForPosition( m_context, groupPosition, groupID, // GameListGroup.makeForPosition( m_context, groupPosition, groupID,
m_cb ); // m_cb );
if ( !isExpanded ) { // if ( !isExpanded ) {
GameGroupInfo ggi = getInfoForGroup( groupPosition ); // GameGroupInfo ggi = getInfoForGroup( groupPosition );
view.setPct( m_handler, ggi.m_hasTurn, ggi.m_turnLocal, // view.setPct( m_handler, ggi.m_hasTurn, ggi.m_turnLocal,
ggi.m_lastMoveTime ); // ggi.m_lastMoveTime );
} // }
int nKids = getChildrenCount( groupPosition ); // int nKids = getChildrenCount( groupPosition );
String name = m_context.getString( R.string.group_namef, // String name = m_context.getString( R.string.group_namef,
groupNames()[groupPosition], nKids ); // groupNames()[groupPosition], nKids );
view.setText( name ); // view.setText( name );
view.setSelected( m_cb.getSelected( view ) ); // view.setSelected( m_cb.getSelected( view ) );
return view; // return view;
} // }
public boolean hasStableIds() { return false; } // public boolean hasStableIds() { return false; }
public long getChildId( int groupPosition, int childPosition ) // public long getChildId( int groupPosition, int childPosition )
{ // {
return childPosition; // return childPosition;
} // }
public long getGroupId( int groupPosition ) // public long getGroupId( int groupPosition )
{ // {
return groupPosition; // return groupPosition;
} // }
public Object getChild( int groupPosition, int childPosition ) // public Object getChild( int groupPosition, int childPosition )
{ // {
return null; // return null;
} // }
public Object getGroup( int groupPosition ) // public Object getGroup( int groupPosition )
{ // {
return null; // return null;
} // }
public int getChildrenCount( int groupPosition ) public int getChildrenCount( int groupPosition )
{ {
@ -283,13 +387,13 @@ public class GameListAdapter implements ExpandableListAdapter {
return rows.length; return rows.length;
} }
public int getGroupCount() protected int getGroupCount()
{ {
return gameInfo().size(); return gameInfo().size();
} }
public void registerDataSetObserver( DataSetObserver obs ){} // public void registerDataSetObserver( DataSetObserver obs ){}
public void unregisterDataSetObserver( DataSetObserver obs ){} // public void unregisterDataSetObserver( DataSetObserver obs ){}
public void inval( long rowid ) public void inval( long rowid )
{ {
@ -474,7 +578,7 @@ public class GameListAdapter implements ExpandableListAdapter {
GameListGroup group = getGroupItemFor( groupPosition ); GameListGroup group = getGroupItemFor( groupPosition );
if ( null != group ) { if ( null != group ) {
GameGroupInfo ggi = getInfoForGroup( groupPosition ); GameGroupInfo ggi = getInfoForGroup( groupPosition );
group.setPct( ggi.m_hasTurn, ggi.m_turnLocal, ggi.m_lastMoveTime ); group.setPct( m_handler, ggi.m_hasTurn, ggi.m_turnLocal, ggi.m_lastMoveTime );
} }
} }

View file

@ -24,27 +24,52 @@ import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.eehouse.android.xw4.DBUtils.GameGroupInfo; import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
public class GameListGroup extends ExpiringTextView public class GameListGroup extends ExpiringLinearLayout
implements SelectableItem.LongClickHandler implements SelectableItem.LongClickHandler,
View.OnClickListener,
View.OnLongClickListener
{ {
// Find me a home....
interface GroupStateListener {
void onGroupExpandedChanged( int groupPosition, boolean expanded );
}
private int m_groupPosition; private int m_groupPosition;
private long m_groupID; private long m_groupID;
private boolean m_expanded; private boolean m_expanded;
private SelectableItem m_cb; private SelectableItem m_cb;
private GroupStateListener m_gcb;
private TextView m_etv;
private boolean m_selected;
private int m_nGames;
private DrawSelDelegate m_dsdel;
private ImageButton m_expandButton;
public static GameListGroup makeForPosition( Context context, public static GameListGroup makeForPosition( Context context,
int groupPosition, int groupPosition,
long groupID, long groupID,
SelectableItem cb ) int nGames,
boolean expanded,
SelectableItem cb,
GroupStateListener gcb )
{ {
GameListGroup result = GameListGroup result =
(GameListGroup)Utils.inflate( context, R.layout.game_list_group ); (GameListGroup)Utils.inflate( context, R.layout.game_list_group );
result.m_cb = cb; result.m_cb = cb;
result.m_gcb = gcb;
result.m_groupPosition = groupPosition; result.m_groupPosition = groupPosition;
result.m_groupID = groupID; result.m_groupID = groupID;
result.m_nGames = nGames;
result.m_expanded = expanded;
result.setButton(); // in case onFinishInflate already called
return result; return result;
} }
@ -53,6 +78,23 @@ public class GameListGroup extends ExpiringTextView
super( cx, as ); super( cx, as );
} }
@Override
protected void onFinishInflate()
{
super.onFinishInflate();
m_etv = (TextView)findViewById( R.id.game_name );
m_expandButton = (ImageButton)findViewById( R.id.expander );
// click on me OR the button expands/contracts...
setOnClickListener( this );
m_expandButton.setOnClickListener( this );
m_dsdel = new DrawSelDelegate( this );
setOnLongClickListener( this );
setButton();
}
public void setGroupPosition( int groupPosition ) public void setGroupPosition( int groupPosition )
{ {
m_groupPosition = groupPosition; m_groupPosition = groupPosition;
@ -76,6 +118,11 @@ public class GameListGroup extends ExpiringTextView
} }
} }
protected void setText( String text )
{
m_etv.setText( text );
}
// GameListAdapter.ClickHandler interface // GameListAdapter.ClickHandler interface
public void longClicked() public void longClicked()
{ {
@ -84,8 +131,41 @@ public class GameListGroup extends ExpiringTextView
protected void toggleSelected() protected void toggleSelected()
{ {
super.toggleSelected(); m_selected = !m_selected;
m_dsdel.showSelected( m_selected );
m_cb.itemToggled( this, m_selected ); m_cb.itemToggled( this, m_selected );
} }
//////////////////////////////////////////////////
// View.OnLongClickListener interface
//////////////////////////////////////////////////
public boolean onLongClick( View view )
{
longClicked();
return true;
}
//////////////////////////////////////////////////
// View.OnClickListener interface
//////////////////////////////////////////////////
public void onClick( View view )
{
if ( 0 < m_nGames ) {
m_expanded = !m_expanded;
m_gcb.onGroupExpandedChanged( m_groupPosition, m_expanded );
setButton();
}
}
private void setButton()
{
if ( null != m_expandButton ) {
m_expandButton.setVisibility( 0 == m_nGames ?
View.GONE : View.VISIBLE );
m_expandButton.setImageResource( m_expanded ?
R.drawable.expander_ic_maximized :
R.drawable.expander_ic_minimized);
}
}
} }

View file

@ -66,8 +66,8 @@ public class GameListItem extends LinearLayout
private int m_fieldID; private int m_fieldID;
private int m_loadingCount; private int m_loadingCount;
private int m_groupPosition; private int m_groupPosition;
private Drawable m_origDrawable;
private boolean m_selected = false; private boolean m_selected = false;
private DrawSelDelegate m_dsdel;
public GameListItem( Context cx, AttributeSet as ) public GameListItem( Context cx, AttributeSet as )
{ {
@ -80,6 +80,7 @@ public class GameListItem extends LinearLayout
m_rowid = DBUtils.ROWID_NOTFOUND; m_rowid = DBUtils.ROWID_NOTFOUND;
m_lastMoveTime = 0; m_lastMoveTime = 0;
m_loadingCount = 0; m_loadingCount = 0;
m_dsdel = new DrawSelDelegate( this );
setOnClickListener( new View.OnClickListener() { setOnClickListener( new View.OnClickListener() {
@Override @Override
@ -332,12 +333,7 @@ public class GameListItem extends LinearLayout
private void toggleSelected() private void toggleSelected()
{ {
m_selected = !m_selected; m_selected = !m_selected;
if ( m_selected ) { m_dsdel.showSelected( m_selected );
m_origDrawable = getBackground();
setBackgroundColor( XWApp.SEL_COLOR );
} else {
setBackgroundDrawable( m_origDrawable );
}
m_cb.itemToggled( this, m_selected ); m_cb.itemToggled( this, m_selected );
} }

View file

@ -24,7 +24,7 @@ import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.app.ExpandableListActivity; import android.app.ListActivity;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
@ -35,7 +35,7 @@ import org.eehouse.android.xw4.jni.CurGameInfo;
import junit.framework.Assert; import junit.framework.Assert;
public class GamesListActivity extends ExpandableListActivity { public class GamesListActivity extends ListActivity {
// private static final String RELAYIDS_EXTRA = "relayids"; // private static final String RELAYIDS_EXTRA = "relayids";
private static final String ROWID_EXTRA = "rowid"; private static final String ROWID_EXTRA = "rowid";
@ -49,7 +49,7 @@ public class GamesListActivity extends ExpandableListActivity {
protected Dialog onCreateDialog( int id ) protected Dialog onCreateDialog( int id )
{ {
Dialog dialog = super.onCreateDialog( id ); Dialog dialog = super.onCreateDialog( id );
if ( null == dialog ) { if ( null == dialog && null != m_dlgt ) {
dialog = m_dlgt.createDialog( id ); dialog = m_dlgt.createDialog( id );
} }
return dialog; return dialog;
@ -67,6 +67,7 @@ public class GamesListActivity extends ExpandableListActivity {
{ {
super.onCreate( savedInstanceState ); super.onCreate( savedInstanceState );
m_dlgt = new GamesListDelegate( this, savedInstanceState ); m_dlgt = new GamesListDelegate( this, savedInstanceState );
m_dlgt.init( savedInstanceState );
} // onCreate } // onCreate
// called when we're brought to the front (probably as a result of // called when we're brought to the front (probably as a result of

View file

@ -124,7 +124,6 @@ public class GamesListDelegate extends DelegateBase
{ {
super( activity, savedInstanceState ); super( activity, savedInstanceState );
m_activity = activity; m_activity = activity;
init( savedInstanceState );
} }
protected Dialog createDialog( int id ) protected Dialog createDialog( int id )
@ -348,7 +347,7 @@ public class GamesListDelegate extends DelegateBase
} }
} }
private void init( Bundle savedInstanceState ) protected void init( Bundle savedInstanceState )
{ {
CrashTrack.init( m_activity ); CrashTrack.init( m_activity );
@ -357,7 +356,7 @@ public class GamesListDelegate extends DelegateBase
getBundledData( savedInstanceState ); getBundledData( savedInstanceState );
m_activity.setContentView( R.layout.game_list ); m_activity.setContentView( R.layout.game_list );
ExpandableListView listview = m_activity.getExpandableListView(); ListView listview = m_activity.getListView();
DBUtils.setDBChangeListener( this ); DBUtils.setDBChangeListener( this );
boolean isUpgrade = Utils.firstBootThisVersion( m_activity ); boolean isUpgrade = Utils.firstBootThisVersion( m_activity );
@ -604,7 +603,7 @@ public class GamesListDelegate extends DelegateBase
protected void contentChanged() protected void contentChanged()
{ {
if ( null != m_adapter ) { if ( null != m_adapter ) {
m_adapter.expandGroups( m_activity.getExpandableListView() ); m_adapter.expandGroups( m_activity.getListView() );
} }
} }
@ -694,7 +693,8 @@ public class GamesListDelegate extends DelegateBase
enable = nothingSelected && XWPrefs.getStudyEnabled( m_activity ); enable = nothingSelected && XWPrefs.getStudyEnabled( m_activity );
Utils.setItemVisible( menu, R.id.games_menu_study, enable ); Utils.setItemVisible( menu, R.id.games_menu_study, enable );
enable = 0 < DBUtils.getGamesWithSendsPending( m_activity ).size(); enable = nothingSelected &&
0 < DBUtils.getGamesWithSendsPending( m_activity ).size();
Utils.setItemVisible( menu, R.id.games_menu_resend, enable ); Utils.setItemVisible( menu, R.id.games_menu_resend, enable );
m_menuPrepared = true; m_menuPrepared = true;
@ -1351,7 +1351,7 @@ public class GamesListDelegate extends DelegateBase
private GameListAdapter makeNewAdapter() private GameListAdapter makeNewAdapter()
{ {
ExpandableListView listview = m_activity.getExpandableListView(); ListView listview = m_activity.getListView();
String field = CommonPrefs.getSummaryField( m_activity ); String field = CommonPrefs.getSummaryField( m_activity );
long[] positions = XWPrefs.getGroupPositions( m_activity ); long[] positions = XWPrefs.getGroupPositions( m_activity );
GameListAdapter adapter = GameListAdapter adapter =

View file

@ -53,19 +53,17 @@ public class NFCUtils {
} }
private static interface SafeNFC { private static interface SafeNFC {
public void register( Activity activity ); public void register( Activity activity, NFCActor actor );
} }
private static class SafeNFCImpl implements SafeNFC { private static class SafeNFCImpl implements SafeNFC {
public void register( final Activity activity ) public void register( final Activity activity, final NFCActor actor )
{ {
Assert.assertTrue( activity instanceof NFCActor );
NfcManager manager = NfcManager manager =
(NfcManager)activity.getSystemService( Context.NFC_SERVICE ); (NfcManager)activity.getSystemService( Context.NFC_SERVICE );
if ( null != manager ) { if ( null != manager ) {
NfcAdapter adapter = manager.getDefaultAdapter(); NfcAdapter adapter = manager.getDefaultAdapter();
if ( null != adapter ) { if ( null != adapter ) {
final NFCActor actor = (NFCActor)activity;
NfcAdapter.CreateNdefMessageCallback cb = NfcAdapter.CreateNdefMessageCallback cb =
new NfcAdapter.CreateNdefMessageCallback() { new NfcAdapter.CreateNdefMessageCallback() {
public NdefMessage createNdefMessage( NfcEvent evt ) public NdefMessage createNdefMessage( NfcEvent evt )
@ -117,10 +115,10 @@ public class NFCUtils {
return result; return result;
} }
public static void register( Activity activity ) public static void register( Activity activity, NFCActor actor )
{ {
if ( null != s_safeNFC ) { if ( null != s_safeNFC ) {
s_safeNFC.register( activity ); s_safeNFC.register( activity, actor );
} }
} }

View file

@ -696,6 +696,7 @@ public class RelayService extends XWService
out.writeShort( BuildConstants.CLIENT_VERS_RELAY ); out.writeShort( BuildConstants.CLIENT_VERS_RELAY );
writeVLIString( out, BuildConstants.GIT_REV ); writeVLIString( out, BuildConstants.GIT_REV );
// writeVLIString( out, String.format( "€%s", Build.MODEL) );
writeVLIString( out, Build.MODEL ); writeVLIString( out, Build.MODEL );
writeVLIString( out, Build.VERSION.RELEASE ); writeVLIString( out, Build.VERSION.RELEASE );
@ -1152,9 +1153,10 @@ public class RelayService extends XWService
if ( null == str ) { if ( null == str ) {
str = ""; str = "";
} }
int len = str.length(); byte[] bytes = str.getBytes( "UTF-8" );
int len = bytes.length;
un2vli( len, os ); un2vli( len, os );
os.writeBytes( str ); os.write( bytes, 0, len );
} }
private void setMaxIntervalSeconds( int maxIntervalSeconds ) private void setMaxIntervalSeconds( int maxIntervalSeconds )

View file

@ -24,11 +24,13 @@ import android.app.ListActivity;
import android.widget.ListAdapter; import android.widget.ListAdapter;
import android.content.Context; import android.content.Context;
import android.database.DataSetObserver; import android.database.DataSetObserver;
import android.widget.BaseAdapter;
/** /**
* Let's see if we can implement a few of these methods just once. * Let's see if we can implement a few of these methods just once.
*/ */
public abstract class XWListAdapter implements ListAdapter { public abstract class XWListAdapter extends BaseAdapter
implements ListAdapter {
private int m_count; private int m_count;
public XWListAdapter( ) { public XWListAdapter( ) {
@ -49,6 +51,6 @@ public abstract class XWListAdapter implements ListAdapter {
public int getViewTypeCount() { return 1; } public int getViewTypeCount() { return 1; }
public boolean hasStableIds() { return true; } public boolean hasStableIds() { return true; }
public boolean isEmpty() { return getCount() == 0; } public boolean isEmpty() { return getCount() == 0; }
public void registerDataSetObserver( DataSetObserver observer ) {} // public void registerDataSetObserver( DataSetObserver observer ) {}
public void unregisterDataSetObserver( DataSetObserver observer ) {} // public void unregisterDataSetObserver( DataSetObserver observer ) {}
} }

View file

@ -22,7 +22,6 @@ package org.eehouse.android.xw4;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
@ -38,10 +37,10 @@ public class XWListItem extends LinearLayout
private Context m_context; private Context m_context;
private Object m_cached; private Object m_cached;
private DeleteCallback m_delCb; private DeleteCallback m_delCb;
private Drawable m_origDrawable;
private boolean m_selected = false; private boolean m_selected = false;
private SelectableItem m_selCb; private SelectableItem m_selCb;
private CheckBox m_checkbox; private CheckBox m_checkbox;
private DrawSelDelegate m_dsdel;
public interface DeleteCallback { public interface DeleteCallback {
void deleteCalled( XWListItem item ); void deleteCalled( XWListItem item );
@ -50,6 +49,7 @@ public class XWListItem extends LinearLayout
public XWListItem( Context cx, AttributeSet as ) { public XWListItem( Context cx, AttributeSet as ) {
super( cx, as ); super( cx, as );
m_context = cx; m_context = cx;
m_dsdel = new DrawSelDelegate( this );
} }
@Override @Override
@ -152,12 +152,9 @@ public class XWListItem extends LinearLayout
private void toggleSelected() private void toggleSelected()
{ {
m_selected = !m_selected; m_selected = !m_selected;
if ( m_selected ) {
m_origDrawable = getBackground(); m_dsdel.showSelected( m_selected );
setBackgroundColor( XWApp.SEL_COLOR );
} else {
setBackgroundDrawable( m_origDrawable );
}
m_checkbox.setChecked( m_selected ); m_checkbox.setChecked( m_selected );
m_selCb.itemToggled( this, m_selected ); m_selCb.itemToggled( this, m_selected );

View file

@ -819,7 +819,7 @@ DBMgr::execSql( const char* const query )
PGresult* result = PQexec( getThreadConn(), query ); PGresult* result = PQexec( getThreadConn(), query );
ok = PGRES_COMMAND_OK == PQresultStatus(result); ok = PGRES_COMMAND_OK == PQresultStatus(result);
if ( !ok ) { if ( !ok ) {
logf( XW_LOGERROR, "%s: PQexec=>%s;%s", __func__, logf( XW_LOGERROR, "%s(%s): PQexec=>%s;%s", __func__, query,
PQresStatus(PQresultStatus(result)), PQresStatus(PQresultStatus(result)),
PQresultErrorMessage(result) ); PQresultErrorMessage(result) );
clearThreadConn(); clearThreadConn();

View file

@ -327,6 +327,20 @@ vli2un( const uint8_t** bufpp, const uint8_t* end, uint32_t* out )
return success; return success;
} }
static void
checkAllAscii( string& str, const char* ifBad )
{
const char* strp = str.c_str();
while ( '\0' != *strp ) {
if ( 0 != (0x80 & *strp) ) {
logf( XW_LOGERROR, "%s: replacing string %s", __func__, str.c_str(), ifBad );
str.assign( ifBad );
break;
}
++strp;
}
}
static bool static bool
getVLIString( const uint8_t** bufpp, const uint8_t* end, getVLIString( const uint8_t** bufpp, const uint8_t* end,
string& out ) string& out )
@ -1657,6 +1671,9 @@ handle_udp_packet( UdpThreadClosure* utc )
&& getVLIString( &ptr, end, devDesc ) && getVLIString( &ptr, end, devDesc )
&& getVLIString( &ptr, end, model ) && getVLIString( &ptr, end, model )
&& getVLIString( &ptr, end, osVers ) ) { && getVLIString( &ptr, end, osVers ) ) {
if ( 3 >= clientVers ) {
checkAllAscii( model, "bad model" );
}
registerDevice( relayID, &devID, utc->addr(), registerDevice( relayID, &devID, utc->addr(),
clientVers, devDesc, model, osVers ); clientVers, devDesc, model, osVers );
} }