diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index c514c9fed..c48735d5a 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -22,7 +22,7 @@ to come from a domain that you own or have control over. --> diff --git a/xwords4/android/XWords4/build.xml b/xwords4/android/XWords4/build.xml index a8651456f..324e3e6ee 100644 --- a/xwords4/android/XWords4/build.xml +++ b/xwords4/android/XWords4/build.xml @@ -49,7 +49,7 @@ - + diff --git a/xwords4/android/XWords4/res/layout/game_list.xml b/xwords4/android/XWords4/res/layout/game_list.xml index e04e62741..9b2f54c92 100644 --- a/xwords4/android/XWords4/res/layout/game_list.xml +++ b/xwords4/android/XWords4/res/layout/game_list.xml @@ -6,10 +6,10 @@ android:layout_height="fill_parent" > - + diff --git a/xwords4/android/XWords4/res/layout/game_list_group.xml b/xwords4/android/XWords4/res/layout/game_list_group.xml index 7ab68cff1..f41cc9152 100644 --- a/xwords4/android/XWords4/res/layout/game_list_group.xml +++ b/xwords4/android/XWords4/res/layout/game_list_group.xml @@ -1,15 +1,27 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal" + android:background="#FF7F7F7F" + > + + + + + + diff --git a/xwords4/android/XWords4/res/layout/game_list_item.xml b/xwords4/android/XWords4/res/layout/game_list_item.xml index a0b283286..6aa2447a8 100644 --- a/xwords4/android/XWords4/res/layout/game_list_item.xml +++ b/xwords4/android/XWords4/res/layout/game_list_item.xml @@ -70,9 +70,7 @@ /> diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index d00c0b229..9ba430c0d 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -5,20 +5,16 @@ -Crosswords 4.4 beta 82 release +Crosswords 4.4 beta 83 release

New with this release

    -
  • Add ability to select studylist items so subsets can be removed or copied out
  • -
  • Enable studylists by default for new installs
  • -
  • Fix bug with Wordlist browser selections
  • -
  • Improve Lookup/Add-to-studylist dialog
  • -
  • Fix bug clearing pending move when you long-tap a letter to see words it's in
  • -
  • Other performance and minor UI improvements
  • +
  • Bug fix (obscure): don't mangle unicode model names

Next up

    +
  • Much better support for localization
  • Look, again, at play via Bluetooth now that HTC phones aren't quite so common
  • Fix SMS play for KitKat
  • Offer "Rematch" when game's over
  • diff --git a/xwords4/android/XWords4/res/values/app_name.xml b/xwords4/android/XWords4/res/values/app_name.xml index 317d20e15..5a2c78447 100644 --- a/xwords4/android/XWords4/res/values/app_name.xml +++ b/xwords4/android/XWords4/res/values/app_name.xml @@ -1,5 +1,5 @@ - 4.4 beta 82 + 4.4 beta 83 diff --git a/xwords4/android/XWords4/res/values/styles.xml b/xwords4/android/XWords4/res/values/styles.xml index 37e4666f3..bbea3719b 100644 --- a/xwords4/android/XWords4/res/values/styles.xml +++ b/xwords4/android/XWords4/res/values/styles.xml @@ -66,5 +66,11 @@ 1 + + diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java index f1d77b032..4ce7a69bc 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java @@ -545,7 +545,7 @@ public class BoardDelegate extends DelegateBase m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); 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(); setKeepScreenOn(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 6635c26ca..a432fac34 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -205,7 +205,7 @@ public class DBUtils { int col = cursor.getColumnIndex( DBHelper.CONTYPE ); if ( 0 <= col ) { tmp = cursor.getInt( col ); - summary.conType = CommsConnType.values()[tmp]; + summary.conType = CommsAddrRec.CommsConnType.values()[tmp]; col = cursor.getColumnIndex( DBHelper.SEED ); if ( 0 < col ) { summary.seed = cursor.getInt( col ); @@ -964,14 +964,16 @@ public class DBUtils { // Groups stuff public static class GameGroupInfo { public String m_name; + public int m_count; public boolean m_expanded; public long m_lastMoveTime; public boolean m_hasTurn; 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_lastMoveTime = 0; + m_count = count; } } @@ -1010,44 +1012,58 @@ public class DBUtils { return thumb; } - // Return map of string (group name) to info about all games in - // that group. - public static HashMap getGroups( Context context ) + private static HashMap getGameCounts( SQLiteDatabase db ) { - return getGroups( context, 0 ); + HashMap result = new HashMap(); + 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 getGroups( Context context, - int nRows ) + // Map of groups rowid (= summaries.groupid) to group info record + protected static HashMap getGroups( Context context ) { if ( null == s_groupsCache ) { HashMap result = new HashMap(); - String[] columns = { ROW_ID, DBHelper.GROUPNAME, - DBHelper.EXPANDED }; - String limit = 0 == nRows ? null : String.format( "%d", nRows ); + + // Select all groups. For each group get the number of games in + // 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 ); synchronized( s_dbHelper ) { SQLiteDatabase db = s_dbHelper.getReadableDatabase(); - Cursor cursor = db.query( DBHelper.TABLE_NAME_GROUPS, columns, - null, // selection - null, // args - null, // groupBy - null, // having - null, //orderby - limit - ); - int idIndex = cursor.getColumnIndex( ROW_ID ); - int nameIndex = cursor.getColumnIndex( DBHelper.GROUPNAME ); - int expandedIndex = cursor.getColumnIndex( DBHelper.EXPANDED ); + + HashMap map = getGameCounts( db ); + + Cursor cursor = db.rawQuery( query, null ); + int idIndex = cursor.getColumnIndex( "rowid" ); + int nameIndex = cursor.getColumnIndex( "groups_groupname" ); + int expandedIndex = cursor.getColumnIndex( "groups_expanded" ); while ( cursor.moveToNext() ) { - String name = cursor.getString( nameIndex ); long id = cursor.getLong( idIndex ); + String name = cursor.getString( nameIndex ); Assert.assertNotNull( name ); 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(); @@ -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 ) { long[] result = null; @@ -1202,7 +1240,7 @@ public class DBUtils { public static long getAnyGroup( Context context ) { long result = GROUPID_UNSPEC; - HashMap groups = getGroups( context, 1 ); + HashMap groups = getGroups( context ); Iterator iter = groups.keySet().iterator(); if ( iter.hasNext() ) { result = iter.next(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DrawSelDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DrawSelDelegate.java new file mode 100644 index 000000000..3eb0338a8 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DrawSelDelegate.java @@ -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 ); + } + } +} diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java index 430800771..68f0b1474 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringDelegate.java @@ -52,12 +52,11 @@ public class ExpiringDelegate { private long m_startSecs; private Runnable m_runnable = null; private boolean m_selected; - private Drawable m_origDrawable; // these can be static as drawing's all in same thread. private static Rect s_rect; private static Paint s_paint; private static float[] s_points; - private static Drawable s_selDrawable; + private DrawSelDelegate m_dsdel; static { s_rect = new Rect(); @@ -65,14 +64,13 @@ public class ExpiringDelegate { s_paint.setStyle(Paint.Style.STROKE); s_paint.setStrokeWidth( 1 ); s_points = new float[4*6]; - s_selDrawable = new ColorDrawable( XWApp.SEL_COLOR ); } public ExpiringDelegate( Context context, View view ) { m_context = context; m_view = view; - m_origDrawable = view.getBackground(); + m_dsdel = new DrawSelDelegate( view ); } public void setHandler( Handler handler ) @@ -101,12 +99,7 @@ public class ExpiringDelegate { public void setSelected( boolean selected ) { m_selected = selected; - if ( selected ) { - m_origDrawable = m_view.getBackground(); - m_view.setBackgroundDrawable( s_selDrawable ); - } else { - m_view.setBackgroundDrawable( m_origDrawable ); - } + m_dsdel.showSelected( m_selected ); } public void onDraw( Canvas canvas ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringLinearLayout.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringLinearLayout.java index a95aed93d..4350f474a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringLinearLayout.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringLinearLayout.java @@ -49,6 +49,8 @@ public class ExpiringLinearLayout extends LinearLayout { protected void onDraw( Canvas canvas ) { super.onDraw( canvas ); - m_delegate.onDraw( canvas ); + if ( null != m_delegate ) { + m_delegate.onDraw( canvas ); + } } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringTextView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringTextView.java index 014e5af47..6c0ea2815 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringTextView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/ExpiringTextView.java @@ -28,7 +28,6 @@ import android.widget.TextView; class ExpiringTextView extends TextView { private ExpiringDelegate m_delegate = null; private Context m_context; - protected boolean m_selected = false; 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 protected void onDraw( Canvas canvas ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index d9deeff17..53f3ec56b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -24,8 +24,8 @@ import android.database.DataSetObserver; import android.os.Handler; import android.view.View; import android.view.ViewGroup; -import android.widget.ExpandableListAdapter; -import android.widget.ExpandableListView; +import android.widget.ListAdapter; +import android.widget.ListView; import java.util.Collections; 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.DBUtils.GameGroupInfo; -public class GameListAdapter implements ExpandableListAdapter { +public class GameListAdapter extends XWListAdapter + implements GameListGroup.GroupStateListener { + private Context m_context; - private ExpandableListView m_list; + private ListView m_list; private int m_fieldID; private Handler m_handler; private SelectableItem m_cb; private long[] m_positions; - public GameListAdapter( Context context, ExpandableListView list, + public GameListAdapter( Context context, ListView list, Handler handler, SelectableItem cb, long[] positions, String fieldName ) { + super( 0 ); m_context = context; m_list = list; m_handler = handler; @@ -61,6 +64,23 @@ public class GameListAdapter implements ExpandableListAdapter { m_fieldID = fieldToID( fieldName ); } + @Override + public int getCount() + { + HashMap 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() { Set keys = gameInfo().keySet(); // do not modify!!!! @@ -103,15 +123,16 @@ public class GameListAdapter implements ExpandableListAdapter { return success; } - public void expandGroups( ExpandableListView view ) + public void expandGroups( ListView view ) { - HashMap info = gameInfo(); - for ( int ii = 0; ii < info.size(); ++ii ) { - GameGroupInfo ggi = getInfoForGroup( ii ); - if ( ggi.m_expanded ) { - view.expandGroup( ii ); - } - } + DbgUtils.logf( "expandGroups not implemented" ); + // HashMap info = gameInfo(); + // for ( int ii = 0; ii < info.size(); ++ii ) { + // GameGroupInfo ggi = getInfoForGroup( ii ); + // if ( ggi.m_expanded ) { + // view.expandGroup( ii ); + // } + // } } public long getRowIDFor( int group, int child ) @@ -126,11 +147,13 @@ public class GameListAdapter implements ExpandableListAdapter { public long getRowIDFor( long packedPosition ) { - int childPosition = ExpandableListView. - getPackedPositionChild( packedPosition ); - int groupPosition = ExpandableListView. - getPackedPositionGroup( packedPosition ); - return getRowIDFor( groupPosition, childPosition ); + // int childPosition = ListView. + // getPackedPositionChild( packedPosition ); + // int groupPosition = ListView. + // getPackedPositionGroup( packedPosition ); + // return getRowIDFor( groupPosition, childPosition ); + Assert.fail(); + return 0; } public long getGroupIDFor( int groupPos ) @@ -156,121 +179,202 @@ public class GameListAdapter implements ExpandableListAdapter { 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 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 ////////////////////////////////////////////////////////////////////////// - public long getCombinedGroupId( long groupId ) - { - return groupId; - } + // public long getCombinedGroupId( long groupId ) + // { + // return groupId; + // } - public long getCombinedChildId( long groupId, long childId ) - { - return groupId << 16 | childId; - } + // public long getCombinedChildId( long groupId, long 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 ); DBUtils.setGroupExpanded( m_context, groupid, false ); long[] rowids = DBUtils.getGroupGames( m_context, groupid ); deselectGames( rowids ); + + notifyDataSetChanged(); } - public void onGroupExpanded( int groupPosition ) + private void onGroupExpanded( int groupPosition ) { long groupid = getGroupIDFor( groupPosition ); DBUtils.setGroupExpanded( m_context, groupid, true ); + + notifyDataSetChanged(); } - public boolean areAllItemsEnabled() { return true; } + // public boolean areAllItemsEnabled() { return true; } - public boolean isChildSelectable( int groupPosition, int childPosition ) - { return true; } + // public boolean isChildSelectable( int groupPosition, int childPosition ) + // { return true; } - public View getChildView( int groupPosition, int childPosition, - boolean isLastChild, View convertView, - ViewGroup parent) - { - View result = null; - if ( null != convertView ) { - // DbgUtils.logf( "getChildView gave non-null convertView" ); - if ( convertView instanceof GameListItem ) { - GameListItem child = (GameListItem)convertView; - long rowid = getRowIDFor( groupPosition, childPosition ); - if ( child.getRowID() == rowid ) { - child.setSelected( m_cb.getSelected( child ) ); - result = child; - } - } - } - if ( null == result ) { - result = getChildView( groupPosition, childPosition ); - } - return result; - } + // public View getChildView( int groupPosition, int childPosition, + // boolean isLastChild, View convertView, + // ViewGroup parent) + // { + // View result = null; + // if ( null != convertView ) { + // // DbgUtils.logf( "getChildView gave non-null convertView" ); + // if ( convertView instanceof GameListItem ) { + // GameListItem child = (GameListItem)convertView; + // long rowid = getRowIDFor( groupPosition, childPosition ); + // if ( child.getRowID() == rowid ) { + // child.setSelected( m_cb.getSelected( child ) ); + // result = child; + // } + // } + // } + // if ( null == result ) { + // result = getChildView( groupPosition, childPosition ); + // } + // return result; + // } - private View getChildView( int groupPosition, int childPosition ) - { - long rowid = getRowIDFor( groupPosition, childPosition ); - GameListItem result = - GameListItem.makeForRow( m_context, rowid, m_handler, - groupPosition, m_fieldID, m_cb ); - result.setSelected( m_cb.getSelected( result ) ); - return result; - } + // private View getChildView( int groupPosition, int childPosition ) + // { + // long rowid = getRowIDFor( groupPosition, childPosition ); + // GameListItem result = + // GameListItem.makeForRow( m_context, rowid, m_handler, + // groupPosition, m_fieldID, m_cb ); + // result.setSelected( m_cb.getSelected( result ) ); + // return result; + // } - public View getGroupView( int groupPosition, boolean isExpanded, - View convertView, ViewGroup parent ) - { - // if ( null != convertView ) { - // DbgUtils.logf( "getGroupView gave non-null convertView" ); - // } - long groupID = getGroupIDFor( groupPosition ); - GameListGroup view = - GameListGroup.makeForPosition( m_context, groupPosition, groupID, - m_cb ); + // public View getGroupView( int groupPosition, boolean isExpanded, + // View convertView, ViewGroup parent ) + // { + // // if ( null != convertView ) { + // // DbgUtils.logf( "getGroupView gave non-null convertView" ); + // // } + // long groupID = getGroupIDFor( groupPosition ); + // GameListGroup view = + // GameListGroup.makeForPosition( m_context, groupPosition, groupID, + // m_cb ); - if ( !isExpanded ) { - GameGroupInfo ggi = getInfoForGroup( groupPosition ); - view.setPct( m_handler, ggi.m_hasTurn, ggi.m_turnLocal, - ggi.m_lastMoveTime ); - } + // if ( !isExpanded ) { + // GameGroupInfo ggi = getInfoForGroup( groupPosition ); + // view.setPct( m_handler, ggi.m_hasTurn, ggi.m_turnLocal, + // ggi.m_lastMoveTime ); + // } - int nKids = getChildrenCount( groupPosition ); - String name = m_context.getString( R.string.group_namef, - groupNames()[groupPosition], nKids ); - view.setText( name ); + // int nKids = getChildrenCount( groupPosition ); + // String name = m_context.getString( R.string.group_namef, + // groupNames()[groupPosition], nKids ); + // 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 ) - { - return childPosition; - } + // public long getChildId( int groupPosition, int childPosition ) + // { + // return childPosition; + // } - public long getGroupId( int groupPosition ) - { - return groupPosition; - } + // public long getGroupId( int groupPosition ) + // { + // return groupPosition; + // } - public Object getChild( int groupPosition, int childPosition ) - { - return null; - } + // public Object getChild( int groupPosition, int childPosition ) + // { + // return null; + // } - public Object getGroup( int groupPosition ) - { - return null; - } + // public Object getGroup( int groupPosition ) + // { + // return null; + // } public int getChildrenCount( int groupPosition ) { @@ -283,13 +387,13 @@ public class GameListAdapter implements ExpandableListAdapter { return rows.length; } - public int getGroupCount() + protected int getGroupCount() { return gameInfo().size(); } - public void registerDataSetObserver( DataSetObserver obs ){} - public void unregisterDataSetObserver( DataSetObserver obs ){} + // public void registerDataSetObserver( DataSetObserver obs ){} + // public void unregisterDataSetObserver( DataSetObserver obs ){} public void inval( long rowid ) { @@ -474,7 +578,7 @@ public class GameListAdapter implements ExpandableListAdapter { GameListGroup group = getGroupItemFor( groupPosition ); if ( null != group ) { 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 ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListGroup.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListGroup.java index e630cdb62..e95bdd679 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListGroup.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListGroup.java @@ -24,27 +24,52 @@ import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; import org.eehouse.android.xw4.DBUtils.GameGroupInfo; -public class GameListGroup extends ExpiringTextView - implements SelectableItem.LongClickHandler +public class GameListGroup extends ExpiringLinearLayout + implements SelectableItem.LongClickHandler, + View.OnClickListener, + View.OnLongClickListener { + // Find me a home.... + interface GroupStateListener { + void onGroupExpandedChanged( int groupPosition, boolean expanded ); + } + private int m_groupPosition; private long m_groupID; private boolean m_expanded; 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, int groupPosition, long groupID, - SelectableItem cb ) + int nGames, + boolean expanded, + SelectableItem cb, + GroupStateListener gcb ) { GameListGroup result = (GameListGroup)Utils.inflate( context, R.layout.game_list_group ); result.m_cb = cb; + result.m_gcb = gcb; result.m_groupPosition = groupPosition; result.m_groupID = groupID; + result.m_nGames = nGames; + result.m_expanded = expanded; + + result.setButton(); // in case onFinishInflate already called + return result; } @@ -53,6 +78,23 @@ public class GameListGroup extends ExpiringTextView 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 ) { m_groupPosition = groupPosition; @@ -76,6 +118,11 @@ public class GameListGroup extends ExpiringTextView } } + protected void setText( String text ) + { + m_etv.setText( text ); + } + // GameListAdapter.ClickHandler interface public void longClicked() { @@ -84,8 +131,41 @@ public class GameListGroup extends ExpiringTextView protected void toggleSelected() { - super.toggleSelected(); + m_selected = !m_selected; + m_dsdel.showSelected( 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); + } + } + } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java index e27712633..b4a37a78d 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java @@ -66,8 +66,8 @@ public class GameListItem extends LinearLayout private int m_fieldID; private int m_loadingCount; private int m_groupPosition; - private Drawable m_origDrawable; private boolean m_selected = false; + private DrawSelDelegate m_dsdel; public GameListItem( Context cx, AttributeSet as ) { @@ -80,6 +80,7 @@ public class GameListItem extends LinearLayout m_rowid = DBUtils.ROWID_NOTFOUND; m_lastMoveTime = 0; m_loadingCount = 0; + m_dsdel = new DrawSelDelegate( this ); setOnClickListener( new View.OnClickListener() { @Override @@ -332,12 +333,7 @@ public class GameListItem extends LinearLayout private void toggleSelected() { m_selected = !m_selected; - if ( m_selected ) { - m_origDrawable = getBackground(); - setBackgroundColor( XWApp.SEL_COLOR ); - } else { - setBackgroundDrawable( m_origDrawable ); - } + m_dsdel.showSelected( m_selected ); m_cb.itemToggled( this, m_selected ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java index d8e2f3d24..fb128e185 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListActivity.java @@ -24,7 +24,7 @@ import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.app.ExpandableListActivity; +import android.app.ListActivity; import android.os.Bundle; import android.view.Menu; @@ -35,7 +35,7 @@ import org.eehouse.android.xw4.jni.CurGameInfo; 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 ROWID_EXTRA = "rowid"; @@ -49,7 +49,7 @@ public class GamesListActivity extends ExpandableListActivity { protected Dialog onCreateDialog( int id ) { Dialog dialog = super.onCreateDialog( id ); - if ( null == dialog ) { + if ( null == dialog && null != m_dlgt ) { dialog = m_dlgt.createDialog( id ); } return dialog; @@ -67,6 +67,7 @@ public class GamesListActivity extends ExpandableListActivity { { super.onCreate( savedInstanceState ); m_dlgt = new GamesListDelegate( this, savedInstanceState ); + m_dlgt.init( savedInstanceState ); } // onCreate // called when we're brought to the front (probably as a result of diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java index dcbac54da..889fdb74b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java @@ -124,7 +124,6 @@ public class GamesListDelegate extends DelegateBase { super( activity, savedInstanceState ); m_activity = activity; - init( savedInstanceState ); } 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 ); @@ -357,7 +356,7 @@ public class GamesListDelegate extends DelegateBase getBundledData( savedInstanceState ); m_activity.setContentView( R.layout.game_list ); - ExpandableListView listview = m_activity.getExpandableListView(); + ListView listview = m_activity.getListView(); DBUtils.setDBChangeListener( this ); boolean isUpgrade = Utils.firstBootThisVersion( m_activity ); @@ -604,7 +603,7 @@ public class GamesListDelegate extends DelegateBase protected void contentChanged() { 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 ); 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 ); m_menuPrepared = true; @@ -1351,7 +1351,7 @@ public class GamesListDelegate extends DelegateBase private GameListAdapter makeNewAdapter() { - ExpandableListView listview = m_activity.getExpandableListView(); + ListView listview = m_activity.getListView(); String field = CommonPrefs.getSummaryField( m_activity ); long[] positions = XWPrefs.getGroupPositions( m_activity ); GameListAdapter adapter = diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NFCUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NFCUtils.java index c5ce1a971..78e625b81 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NFCUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NFCUtils.java @@ -53,19 +53,17 @@ public class NFCUtils { } private static interface SafeNFC { - public void register( Activity activity ); + public void register( Activity activity, NFCActor actor ); } 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)activity.getSystemService( Context.NFC_SERVICE ); if ( null != manager ) { NfcAdapter adapter = manager.getDefaultAdapter(); if ( null != adapter ) { - final NFCActor actor = (NFCActor)activity; NfcAdapter.CreateNdefMessageCallback cb = new NfcAdapter.CreateNdefMessageCallback() { public NdefMessage createNdefMessage( NfcEvent evt ) @@ -117,10 +115,10 @@ public class NFCUtils { return result; } - public static void register( Activity activity ) + public static void register( Activity activity, NFCActor actor ) { if ( null != s_safeNFC ) { - s_safeNFC.register( activity ); + s_safeNFC.register( activity, actor ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java index 813991448..cdb41e565 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java @@ -696,6 +696,7 @@ public class RelayService extends XWService out.writeShort( BuildConstants.CLIENT_VERS_RELAY ); writeVLIString( out, BuildConstants.GIT_REV ); + // writeVLIString( out, String.format( "€%s", Build.MODEL) ); writeVLIString( out, Build.MODEL ); writeVLIString( out, Build.VERSION.RELEASE ); @@ -1152,9 +1153,10 @@ public class RelayService extends XWService if ( null == str ) { str = ""; } - int len = str.length(); + byte[] bytes = str.getBytes( "UTF-8" ); + int len = bytes.length; un2vli( len, os ); - os.writeBytes( str ); + os.write( bytes, 0, len ); } private void setMaxIntervalSeconds( int maxIntervalSeconds ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java index 1e9c4166d..0eb07c25d 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListAdapter.java @@ -24,11 +24,13 @@ import android.app.ListActivity; import android.widget.ListAdapter; import android.content.Context; import android.database.DataSetObserver; +import android.widget.BaseAdapter; /** * 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; public XWListAdapter( ) { @@ -49,6 +51,6 @@ public abstract class XWListAdapter implements ListAdapter { public int getViewTypeCount() { return 1; } public boolean hasStableIds() { return true; } public boolean isEmpty() { return getCount() == 0; } - public void registerDataSetObserver( DataSetObserver observer ) {} - public void unregisterDataSetObserver( DataSetObserver observer ) {} -} \ No newline at end of file + // public void registerDataSetObserver( DataSetObserver observer ) {} + // public void unregisterDataSetObserver( DataSetObserver observer ) {} +} diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java index 3e4273401..857086ce7 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListItem.java @@ -22,7 +22,6 @@ package org.eehouse.android.xw4; import android.content.Context; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.widget.ImageButton; @@ -38,10 +37,10 @@ public class XWListItem extends LinearLayout private Context m_context; private Object m_cached; private DeleteCallback m_delCb; - private Drawable m_origDrawable; private boolean m_selected = false; private SelectableItem m_selCb; private CheckBox m_checkbox; + private DrawSelDelegate m_dsdel; public interface DeleteCallback { void deleteCalled( XWListItem item ); @@ -50,6 +49,7 @@ public class XWListItem extends LinearLayout public XWListItem( Context cx, AttributeSet as ) { super( cx, as ); m_context = cx; + m_dsdel = new DrawSelDelegate( this ); } @Override @@ -152,12 +152,9 @@ public class XWListItem extends LinearLayout private void toggleSelected() { m_selected = !m_selected; - if ( m_selected ) { - m_origDrawable = getBackground(); - setBackgroundColor( XWApp.SEL_COLOR ); - } else { - setBackgroundDrawable( m_origDrawable ); - } + + m_dsdel.showSelected( m_selected ); + m_checkbox.setChecked( m_selected ); m_selCb.itemToggled( this, m_selected ); diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index a4fc61728..212454a65 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -819,7 +819,7 @@ DBMgr::execSql( const char* const query ) PGresult* result = PQexec( getThreadConn(), query ); ok = PGRES_COMMAND_OK == PQresultStatus(result); if ( !ok ) { - logf( XW_LOGERROR, "%s: PQexec=>%s;%s", __func__, + logf( XW_LOGERROR, "%s(%s): PQexec=>%s;%s", __func__, query, PQresStatus(PQresultStatus(result)), PQresultErrorMessage(result) ); clearThreadConn(); diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 3f84a246a..268cbef3f 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -327,6 +327,20 @@ vli2un( const uint8_t** bufpp, const uint8_t* end, uint32_t* out ) 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 getVLIString( const uint8_t** bufpp, const uint8_t* end, string& out ) @@ -1657,6 +1671,9 @@ handle_udp_packet( UdpThreadClosure* utc ) && getVLIString( &ptr, end, devDesc ) && getVLIString( &ptr, end, model ) && getVLIString( &ptr, end, osVers ) ) { + if ( 3 >= clientVers ) { + checkAllAscii( model, "bad model" ); + } registerDevice( relayID, &devID, utc->addr(), clientVers, devDesc, model, osVers ); }