get phone numbers from contacts DB: factor some of BTInviteActivity

into superclass that NBSInviteActivity can share and use to make their
UIs similar: fetch mobile numbers from DB one-at-a-time, keep a list
there, and let you check then delete or return.  Rough, and doesn't
save state the way BT does, but works.
This commit is contained in:
Eric House 2012-03-21 06:17:09 -07:00
parent 45715ae1e0
commit 84496b3012
10 changed files with 440 additions and 112 deletions

View file

@ -35,6 +35,7 @@
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" />

View file

@ -28,3 +28,4 @@ relay_game_config.xml
rename_game.xml
select_dialog_item.xml
nbsinviter.xml
nbsinviter_item.xml

View file

@ -53,3 +53,5 @@ GameNamer.java
LookupActivity.java
NBSInviteActivity.java
NBSService.java
InviteActivity.java
NBSListItem.java

View file

@ -12,19 +12,58 @@
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<EditText android:id="@+id/phone_edit"
android:layout_height="wrap_content"
<ListView android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:phoneNumber="true"
android:singleLine="true"
android:maxLength="15"
android:selectAllOnFocus="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
android:layout_weight="1"
android:padding="8dp"
/>
<!-- <LinearLayout android:orientation="horizontal" -->
<!-- android:layout_width="fill_parent" -->
<!-- android:layout_height="wrap_content" -->
<!-- > -->
<!-- <EditText android:id="@+id/phone_edit" -->
<!-- android:layout_height="wrap_content" -->
<!-- android:layout_width="fill_parent" -->
<!-- android:layout_marginLeft="20dip" -->
<!-- android:layout_marginRight="20dip" -->
<!-- android:scrollHorizontally="true" -->
<!-- android:phoneNumber="true" -->
<!-- android:singleLine="true" -->
<!-- android:maxLength="15" -->
<!-- android:selectAllOnFocus="true" -->
<!-- android:textAppearance="?android:attr/textAppearanceMedium" -->
<!-- android:layout_weight="1" -->
<!-- android:hint="new number" -->
<!-- /> -->
<!-- <Button android:id="@+id/button_insert" -->
<!-- android:text="Insert" -->
<!-- android:layout_width="fill_parent" -->
<!-- android:layout_height="wrap_content" -->
<!-- android:layout_weight="1" -->
<!-- /> -->
<!-- </LinearLayout> -->
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button android:id="@+id/button_add"
android:text="@string/button_nbs_add"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<Button android:id="@+id/button_clear"
android:text="@string/bt_pick_clear_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
</LinearLayout>
<Button android:id="@+id/button_invite"
android:text="@string/button_invite"
android:layout_width="fill_parent"

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<org.eehouse.android.xw4.NBSListItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp"
>
<CheckBox android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView android:id="@+id/name"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView android:id="@+id/number"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
</org.eehouse.android.xw4.NBSListItem>

View file

@ -1860,7 +1860,7 @@
<string name="bt_devs_missing">Missing connections? Invite BT
devs? (I won\'t ask again until you reopen this game.)</string>
<string name="invite_descf">Please select the %d device[s] you
<string name="invite_bt_descf">Please select the %d device[s] you
want to include in this game. Use the Rescan button if you
don\'t see a device you expect.</string>
@ -1887,6 +1887,12 @@
<string name="new_nbs_title">New game via SMS</string>
<string name="new_nbs_bodyf">Phone with number %s has invited you to
play</string>
<string name="new_nbsmove_title">New move via SMS</string>
play</string>
<string name="new_nbsmove_title">New move via SMS</string>
<string name="button_nbs_add">Add contact</string>
<string name="invite_nbs_descf">Please select the %d phone numbers you
want to include in this game. Use the Add contact button if you
don\'t see a number you expect.</string>
</resources>

View file

@ -39,82 +39,22 @@ import android.os.Handler;
import junit.framework.Assert;
public class BTInviteActivity extends XWListActivity
implements View.OnClickListener,
CompoundButton.OnCheckedChangeListener {
public class BTInviteActivity extends InviteActivity {
public static final String DEVS = "DEVS";
public static final String INTENT_KEY_NMISSING = "NMISSING";
private Button m_okButton;
private Button m_rescanButton;
private Button m_clearButton;
private int m_nMissing;
private int m_checkCount = 0;
private boolean m_firstScan;
@Override
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
Intent intent = getIntent();
m_nMissing = intent.getIntExtra( INTENT_KEY_NMISSING, -1 );
setContentView( R.layout.btinviter );
TextView desc = (TextView)findViewById( R.id.invite_desc );
desc.setText( Utils.format( this, R.string.invite_descf, m_nMissing ) );
m_okButton = (Button)findViewById( R.id.button_invite );
m_okButton.setOnClickListener( this );
m_rescanButton = (Button)findViewById( R.id.button_rescan );
m_rescanButton.setOnClickListener( this );
m_clearButton = (Button)findViewById( R.id.button_clear );
m_clearButton.setOnClickListener( this );
m_checkCount = 0;
tryEnable();
super.onCreate( savedInstanceState, R.layout.btinviter,
R.id.button_invite, R.id.button_rescan,
R.id.button_clear, R.id.invite_desc,
R.string.invite_bt_descf );
m_firstScan = true;
BTService.clearDevices( this, null ); // will return names
}
public void onClick( View view )
{
if ( m_okButton == view ) {
Intent intent = new Intent();
String[] devs = listSelected();
intent.putExtra( DEVS, devs );
setResult( Activity.RESULT_OK, intent );
finish();
} else if ( m_rescanButton == view ) {
scan();
} else if ( m_clearButton == view ) {
BTService.clearDevices( this, listSelected() );
}
}
// /* AdapterView.OnItemClickListener */
// public void onItemClick( AdapterView<?> parent, View view,
// int position, long id )
// {
// DbgUtils.logf( "BTInviteActivity.onItemClick(position=%d)", position );
// }
public void onCheckedChanged( CompoundButton buttonView,
boolean isChecked )
{
DbgUtils.logf( "BTInviteActivity.onCheckedChanged( isChecked=%b )",
isChecked );
if ( isChecked ) {
++m_checkCount;
} else {
--m_checkCount;
}
tryEnable();
}
// BTService.BTEventListener interface
@Override
public void eventOccurred( BTService.BTEvent event, final Object ... args )
@ -151,13 +91,18 @@ public class BTInviteActivity extends XWListActivity
}
}
private void scan()
protected void scan()
{
startProgress( R.string.scan_progress );
BTService.scan( this );
}
private String[] listSelected()
protected void clearSelected()
{
BTService.clearDevices( this, listSelected() );
}
protected String[] listSelected()
{
ListView list = (ListView)findViewById( android.R.id.list );
String[] result = new String[m_checkCount];
@ -172,12 +117,6 @@ public class BTInviteActivity extends XWListActivity
return result;
}
private void tryEnable()
{
m_okButton.setEnabled( m_checkCount == m_nMissing );
m_clearButton.setEnabled( 0 < m_checkCount );
}
private class BTDevsAdapter extends XWListAdapter {
private String[] m_devs;
public BTDevsAdapter( String[] devs )

View file

@ -0,0 +1,115 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2009-2011 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.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.TextView;
import android.os.Handler;
import junit.framework.Assert;
abstract class InviteActivity extends XWListActivity
implements View.OnClickListener,
CompoundButton.OnCheckedChangeListener {
public static final String DEVS = "DEVS";
public static final String INTENT_KEY_NMISSING = "NMISSING";
protected int m_nMissing;
protected Button m_okButton;
protected Button m_rescanButton;
protected Button m_clearButton;
protected int m_checkCount = 0;
protected void onCreate( Bundle savedInstanceState, int view_id,
int button_invite, int button_rescan,
int button_clear, int desc_id, int desc_strf )
{
super.onCreate( savedInstanceState );
setContentView( view_id );
Intent intent = getIntent();
m_nMissing = intent.getIntExtra( INTENT_KEY_NMISSING, -1 );
m_okButton = (Button)findViewById( button_invite );
m_okButton.setOnClickListener( this );
m_rescanButton = (Button)findViewById( button_rescan );
m_rescanButton.setOnClickListener( this );
m_clearButton = (Button)findViewById( button_clear );
m_clearButton.setOnClickListener( this );
TextView desc = (TextView)findViewById( desc_id );
desc.setText( Utils.format( this, desc_strf, m_nMissing ) );
m_checkCount = 0;
tryEnable();
}
public void onClick( View view )
{
if ( m_okButton == view ) {
Intent intent = new Intent();
String[] devs = listSelected();
intent.putExtra( DEVS, devs );
setResult( Activity.RESULT_OK, intent );
finish();
} else if ( m_rescanButton == view ) {
scan();
} else if ( m_clearButton == view ) {
clearSelected();
}
}
public void onCheckedChanged( CompoundButton buttonView,
boolean isChecked )
{
DbgUtils.logf( "InviteActivity.onCheckedChanged( isChecked=%b )",
isChecked );
if ( isChecked ) {
++m_checkCount;
} else {
--m_checkCount;
}
tryEnable();
}
protected void tryEnable()
{
m_okButton.setEnabled( m_checkCount == m_nMissing );
m_clearButton.setEnabled( 0 < m_checkCount );
}
abstract String[] listSelected();
abstract void scan();
abstract void clearSelected();
}

View file

@ -21,52 +21,182 @@
package org.eehouse.android.xw4;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import java.util.ArrayList;
import junit.framework.Assert;
public class NBSInviteActivity extends XWActivity
public class NBSInviteActivity extends InviteActivity
implements View.OnClickListener {
public static final String DEVS = "DEVS";
public static final String INTENT_KEY_NMISSING = "NMISSING";
private Button m_okButton;
private EditText m_edit;
private int m_nMissing;
private static final int GET_CONTACT = 1;
private ArrayList<String> m_names;
private ArrayList<String> m_phones;
private NBSPhonesAdapter m_adapter;
@Override
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
super.onCreate( savedInstanceState, R.layout.nbsinviter,
R.id.button_invite, R.id.button_add,
R.id.button_clear, R.id.invite_desc,
R.string.invite_nbs_descf );
Intent intent = getIntent();
m_nMissing = intent.getIntExtra( INTENT_KEY_NMISSING, -1 );
Assert.assertTrue( 1 == m_nMissing );
setContentView( R.layout.nbsinviter );
m_okButton = (Button)findViewById( R.id.button_invite );
m_okButton.setOnClickListener( this );
m_names = new ArrayList<String>();
m_phones = new ArrayList<String>();
}
public void onClick( View view )
@Override
protected void onActivityResult( int requestCode, int resultCode,
Intent data )
{
if ( m_okButton == view ) {
EditText edit = (EditText)findViewById( R.id.phone_edit );
String phone = edit.getText().toString();
if ( null != phone && 0 < phone.length() ) {
Intent intent = new Intent();
String[] devs = { phone };
intent.putExtra( DEVS, devs );
setResult( Activity.RESULT_OK, intent );
finish();
} else {
showOKOnlyDialog( R.string.err_no_phone );
super.onActivityResult(requestCode, resultCode, data);
if ( Activity.RESULT_CANCELED != resultCode && data != null ) {
switch (requestCode) {
case GET_CONTACT:
addPhoneNumbers( data );
break;
}
}
}
protected void scan() {
Intent intent = new Intent( Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI );
startActivityForResult( intent, GET_CONTACT );
}
protected void clearSelected()
{
DbgUtils.logf( "NBSInviteActivity.clearSelected()" );
ListView list = (ListView)findViewById( android.R.id.list );
int count = list.getChildCount();
for ( int ii = count - 1; ii >= 0; --ii ) {
NBSListItem item = (NBSListItem)list.getChildAt( ii );
if ( item.isChecked() ) {
m_phones.remove( ii );
m_names.remove( ii );
}
}
rebuildList();
// int index = m_phones.size();
// while ( index-- > 0 ) {
// if ( m_adapter.isChecked( index ) ) {
// m_phones.remove( index );
// m_names.remove( index );
// }
// }
}
protected String[] listSelected() {
DbgUtils.logf( "NBSInviteActivity.listSelected()" );
ListView list = (ListView)findViewById( android.R.id.list );
String[] result = new String[m_checkCount];
int count = list.getChildCount();
int index = 0;
for ( int ii = 0; ii < count; ++ii ) {
NBSListItem item = (NBSListItem)list.getChildAt( ii );
if ( item.isChecked() ) {
result[index++] = item.getNumber();
}
}
return result;
}
private void addPhoneNumbers( Intent intent )
{
Uri data = intent.getData();
Cursor cursor = managedQuery( data, null, null, null, null );
if ( cursor.moveToFirst() ) {
int len_before = m_phones.size();
int index = cursor.getColumnIndex(ContactsContract.Contacts._ID );
if ( 0 <= index ) {
String id = cursor.getString( index );
ContentResolver resolver = getContentResolver();
Cursor pc =
resolver.query( CommonDataKinds.Phone.CONTENT_URI,
null,
CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[] { id }, null );
while ( pc.moveToNext() ) {
String name =
pc.getString( pc.getColumnIndex( CommonDataKinds.
Phone.DISPLAY_NAME));
String number =
pc.getString( pc.getColumnIndex( CommonDataKinds.
Phone.NUMBER ) );
int type =
pc.getInt( pc.getColumnIndex( CommonDataKinds.
Phone.TYPE ) );
if ( Phone.TYPE_MOBILE == type && 0 < number.length() ) {
m_names.add( name );
m_phones.add( number );
}
}
if ( len_before != m_phones.size() ) {
rebuildList();
}
}
}
}
private void rebuildList()
{
m_adapter = new NBSPhonesAdapter();
setListAdapter( m_adapter );
m_checkCount = 0;
tryEnable();
}
private class NBSPhonesAdapter extends XWListAdapter {
private NBSListItem[] m_items;
public NBSPhonesAdapter()
{
super( m_phones.size() );
m_items = new NBSListItem[m_phones.size()];
}
public Object getItem( int position )
{
DbgUtils.logf( "getItem called (phone %s)", m_phones.get(position) );
NBSListItem item =
(NBSListItem)Utils.inflate( NBSInviteActivity.this,
R.layout.nbsinviter_item );
item.setOnCheckedChangeListener( NBSInviteActivity.this );
item.setContents( m_names.get(position), m_phones.get(position) );
m_items[position] = item;
return item;
}
public View getView( final int position, View convertView,
ViewGroup parent ) {
return (View)getItem( position );
}
public boolean isChecked( int index )
{
boolean checked = m_items[index].isChecked();
DbgUtils.logf( "item %d (for %s) checked: %b",
index, m_items[index].getNumber(), checked );
return checked;
}
}
}

View file

@ -0,0 +1,67 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2012 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.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.CompoundButton.OnCheckedChangeListener;
public class NBSListItem extends LinearLayout {
private Context m_context;
public NBSListItem( Context cx, AttributeSet as )
{
super( cx, as );
m_context = cx;
}
public void setContents( String name, String number )
{
TextView tv = (TextView)findViewById( R.id.name );
tv.setText( name );
tv = (TextView)findViewById( R.id.number );
tv.setText( number );
}
public void setOnCheckedChangeListener( OnCheckedChangeListener lstnr )
{
CheckBox cb = (CheckBox)findViewById( R.id.checkbox );
cb.setOnCheckedChangeListener( lstnr );
}
public String getNumber()
{
TextView tv = (TextView)findViewById( R.id.number );
return tv.getText().toString();
}
public boolean isChecked()
{
CheckBox cb = (CheckBox)findViewById( R.id.checkbox );
boolean isChecked = cb.isChecked();
DbgUtils.logf( "isChecked(%s)=>%b", getNumber(), isChecked );
return isChecked;
}
}