Fix the look of tabbed multi-contact selector.
1) Updated to ActionBar style. 2) Split out into fragments. 3) Switch to cursor loaders.
|
@ -39,6 +39,11 @@
|
|||
android:windowSoftInputMode="stateVisible"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"/>
|
||||
|
||||
<activity android:name=".ContactSelectionActivity"
|
||||
android:label="Select Contacts"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"/>
|
||||
|
||||
|
||||
<activity android:name=".AutoInitiateActivity" android:theme="@android:style/Theme.Dialog" android:label="TextSecure Messaging Detected" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".ApplicationPreferencesActivity" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".PassphraseCreateActivity" android:theme="@android:style/Theme.Dialog" android:label="Create Passphrase" android:launchMode="singleInstance" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
|
@ -51,11 +56,6 @@
|
|||
<activity android:name=".ViewIdentityActivity" android:theme="@android:style/Theme.Dialog" android:label="Public Identity Key" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".SaveIdentityActivity" android:theme="@android:style/Theme.Dialog" android:label="Save Identity" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".ReviewIdentitiesActivity" android:theme="@android:style/Theme.Dialog" android:label="Manage Identity Keys (Long click)" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".ContactSelectionActivity" android:label="Contact Selection" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".ContactSelectionListActivity" android:label="Contact Selection" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".GroupSelectionListActivity" android:label="Group Selection" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
<activity android:name=".ContactSelectionRecentActivity" android:label="Recent Calls Selection" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout"></activity>
|
||||
|
||||
|
||||
<service android:enabled="true" android:name=".service.KeyCachingService"></service>
|
||||
<service android:enabled="true" android:name=".service.SendReceiveService"></service>
|
||||
|
|
BIN
res/drawable-hdpi/hairline_left.9.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-hdpi/hairline_right.9.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-hdpi/ic_menu_done_holo_dark.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
res/drawable-hdpi/ic_tab_contacts.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
res/drawable-hdpi/ic_tab_groups.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
res/drawable-hdpi/ic_tab_recent.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
res/drawable-mdpi/hairline_left.9.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-mdpi/hairline_right.9.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-mdpi/ic_menu_done_holo_dark.png
Normal file
After Width: | Height: | Size: 694 B |
BIN
res/drawable-mdpi/ic_tab_contacts.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
res/drawable-mdpi/ic_tab_groups.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-mdpi/ic_tab_recent.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-xhdpi/hairline_left.9.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-xhdpi/hairline_right.9.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-xhdpi/ic_menu_done_holo_dark.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-xhdpi/ic_tab_contacts.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
res/drawable-xhdpi/ic_tab_groups.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
res/drawable-xhdpi/ic_tab_recent.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
|
@ -1,42 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2006 The Android Open Source Project
|
||||
<LinearLayout android:layout_gravity="center"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/tabhost"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<TabWidget android:id="@android:id/tabs"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="68dip"
|
||||
android:paddingLeft="1dip"
|
||||
android:paddingRight="1dip"
|
||||
android:paddingTop="4dip"
|
||||
/>
|
||||
|
||||
<FrameLayout android:id="@android:id/tabcontent"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</TabHost>
|
||||
<LinearLayout android:id="@+id/fragment_container"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
9
res/menu/contact_selection.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:title="Finished"
|
||||
android:id="@+id/menu_selection_finished"
|
||||
android:icon="@drawable/ic_menu_done_holo_dark"
|
||||
android:showAsAction="ifRoom"
|
||||
/>
|
||||
|
||||
</menu>
|
8
res/menu/contact_selection_list.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:title="Select All"
|
||||
android:id="@+id/menu_select_all" />
|
||||
|
||||
<item android:title="Unselect All"
|
||||
android:id="@+id/menu_unselect_all" />
|
||||
</menu>
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
|
@ -10,54 +10,66 @@
|
|||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
|
||||
import android.app.TabActivity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Window;
|
||||
import android.widget.TabHost;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
|
||||
import com.actionbarsherlock.app.ActionBar;
|
||||
import com.actionbarsherlock.app.ActionBar.Tab;
|
||||
import com.actionbarsherlock.app.ActionBar.TabListener;
|
||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
||||
import com.actionbarsherlock.view.Menu;
|
||||
import com.actionbarsherlock.view.MenuInflater;
|
||||
import com.actionbarsherlock.view.MenuItem;
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
|
||||
/**
|
||||
* Activity container for selecting a list of contacts. Provides a tab frame for
|
||||
* contact, group, and "recent contact" activity tabs. Used by ComposeMessageActivity
|
||||
* when selecting a list of contacts to address a message to.
|
||||
*
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class ContactSelectionActivity extends SherlockFragmentActivity {
|
||||
|
||||
public class ContactSelectionActivity extends TabActivity implements TabHost.OnTabChangeListener {
|
||||
private ContactSelectionListFragment contactsFragment;
|
||||
private ContactSelectionGroupsFragment groupsFragment;
|
||||
private ContactSelectionRecentFragment recentFragment;
|
||||
|
||||
private TabHost tabHost;
|
||||
private Recipients recipients;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.contact_selection_activity);
|
||||
ActionBar actionBar = this.getSupportActionBar();
|
||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
tabHost = getTabHost();
|
||||
tabHost.setOnTabChangedListener(this);
|
||||
setContentView(R.layout.contact_selection_activity);
|
||||
|
||||
setupContactsTab();
|
||||
setupGroupsTab();
|
||||
setupRecentTab();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
registerPassphraseActivityStarted();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
@ -65,48 +77,102 @@ public class ContactSelectionActivity extends TabActivity implements TabHost.OnT
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onSearchRequested() {
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = this.getSupportMenuInflater();
|
||||
inflater.inflate(R.menu.contact_selection, menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_selection_finished:
|
||||
case android.R.id.home:
|
||||
handleSelectionFinished(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void handleSelectionFinished() {
|
||||
recipients = contactsFragment.getSelectedContacts();
|
||||
recipients.append(recentFragment.getSelectedContacts());
|
||||
recipients.append(groupsFragment.getSelectedContacts());
|
||||
|
||||
Intent resultIntent = getIntent();
|
||||
resultIntent.putExtra("recipients", this.recipients);
|
||||
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
private ActionBar.Tab constructTab(final Fragment fragment) {
|
||||
ActionBar actionBar = this.getSupportActionBar();
|
||||
ActionBar.Tab tab = actionBar.newTab();
|
||||
|
||||
tab.setTabListener(new TabListener(){
|
||||
@Override
|
||||
public void onTabSelected(Tab tab, FragmentTransaction ignore) {
|
||||
FragmentManager manager = ContactSelectionActivity.this.getSupportFragmentManager();
|
||||
FragmentTransaction ft = manager.beginTransaction();
|
||||
|
||||
ft.add(R.id.fragment_container, fragment);
|
||||
ft.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(Tab tab, FragmentTransaction ignore) {
|
||||
FragmentManager manager = ContactSelectionActivity.this.getSupportFragmentManager();
|
||||
FragmentTransaction ft = manager.beginTransaction();
|
||||
ft.remove(fragment);
|
||||
ft.commit();
|
||||
}
|
||||
@Override
|
||||
public void onTabReselected(Tab tab, FragmentTransaction ft) {}
|
||||
});
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
private void setupContactsTab() {
|
||||
contactsFragment = (ContactSelectionListFragment)Fragment.instantiate(this,
|
||||
ContactSelectionListFragment.class.getName());
|
||||
ActionBar.Tab contactsTab = constructTab(contactsFragment);
|
||||
contactsTab.setIcon(R.drawable.ic_tab_contacts);
|
||||
this.getSupportActionBar().addTab(contactsTab);
|
||||
}
|
||||
|
||||
private void setupGroupsTab() {
|
||||
groupsFragment = (ContactSelectionGroupsFragment)Fragment.instantiate(this,
|
||||
ContactSelectionGroupsFragment.class.getName());
|
||||
ActionBar.Tab groupsTab = constructTab(groupsFragment);
|
||||
groupsTab.setIcon(R.drawable.ic_tab_groups);
|
||||
this.getSupportActionBar().addTab(groupsTab);
|
||||
}
|
||||
|
||||
private void setupRecentTab() {
|
||||
recentFragment = (ContactSelectionRecentFragment)Fragment.instantiate(this,
|
||||
ContactSelectionRecentFragment.class.getName());
|
||||
|
||||
ActionBar.Tab recentTab = constructTab(recentFragment);
|
||||
recentTab.setIcon(R.drawable.ic_tab_recent);
|
||||
this.getSupportActionBar().addTab(recentTab);
|
||||
}
|
||||
|
||||
private void registerPassphraseActivityStarted() {
|
||||
Intent intent = new Intent(this, KeyCachingService.class);
|
||||
intent.setAction(KeyCachingService.ACTIVITY_START_EVENT);
|
||||
startService(intent);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
|
||||
private void registerPassphraseActivityStopped() {
|
||||
Intent intent = new Intent(this, KeyCachingService.class);
|
||||
intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
private void setupGroupsTab() {
|
||||
Intent intent = new Intent(this, GroupSelectionListActivity.class);
|
||||
|
||||
tabHost.addTab(tabHost.newTabSpec("groups")
|
||||
.setIndicator("Groups", getResources().getDrawable(android.R.drawable.ic_menu_share))
|
||||
.setContent(intent));
|
||||
}
|
||||
|
||||
private void setupRecentTab() {
|
||||
Intent intent = new Intent(this, ContactSelectionRecentActivity.class);
|
||||
|
||||
tabHost.addTab(tabHost.newTabSpec("recent")
|
||||
.setIndicator("Recent", getResources().getDrawable(android.R.drawable.ic_menu_call))
|
||||
.setContent(intent));
|
||||
}
|
||||
|
||||
private void setupContactsTab() {
|
||||
Intent intent = new Intent(this, ContactSelectionListActivity.class);
|
||||
|
||||
tabHost.addTab(tabHost.newTabSpec("contacts")
|
||||
.setIndicator("Contacts", getResources().getDrawable(android.R.drawable.ic_menu_agenda))
|
||||
.setContent(intent));
|
||||
}
|
||||
|
||||
public void onTabChanged(String tabId) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockListFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.GroupData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An activity for selecting a list of "contact groups." Displayed
|
||||
* by ContactSelectionActivity in a tabbed frame, and ultimately called
|
||||
* by ComposeMessageActivity for selecting a list of recipients.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class ContactSelectionGroupsFragment extends SherlockListFragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
|
||||
private final HashMap<Long, GroupData> selectedGroups = new HashMap<Long, GroupData>();
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle icicle) {
|
||||
super.onActivityCreated(icicle);
|
||||
|
||||
initializeResources();
|
||||
initializeCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.contact_selection_group_activity, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
((GroupItemView)v).selected();
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
setListAdapter(new GroupSelectionListAdapter(getActivity(), null));
|
||||
this.getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.getListView().setFocusable(true);
|
||||
}
|
||||
|
||||
public Recipients getSelectedContacts() {
|
||||
List<Recipient> recipientList = new LinkedList<Recipient>();
|
||||
|
||||
for (GroupData groupData : selectedGroups.values()) {
|
||||
List<ContactData> contactDataList = ContactAccessor.getInstance()
|
||||
.getGroupMembership(getActivity(), groupData.id);
|
||||
|
||||
Log.w("GroupSelectionListActivity", "Got contacts in group: " + contactDataList.size());
|
||||
|
||||
for (ContactData contactData : contactDataList) {
|
||||
for (NumberData numberData : contactData.numbers) {
|
||||
recipientList.add(new Recipient(contactData.name, numberData.number, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Recipients(recipientList);
|
||||
}
|
||||
|
||||
private void addGroup(GroupData groupData) {
|
||||
selectedGroups.put(groupData.id, groupData);
|
||||
}
|
||||
|
||||
private void removeGroup(GroupData groupData) {
|
||||
selectedGroups.remove(groupData.id);
|
||||
}
|
||||
|
||||
private class GroupSelectionListAdapter extends CursorAdapter {
|
||||
|
||||
public GroupSelectionListAdapter(Context context, Cursor cursor) {
|
||||
super(context, cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
GroupItemView view = new GroupItemView(context);
|
||||
bindView(view, context, cursor);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
GroupData groupData = ContactAccessor.getInstance().getGroupData(getActivity(), cursor);
|
||||
((GroupItemView)view).set(groupData);
|
||||
}
|
||||
}
|
||||
|
||||
private class GroupItemView extends LinearLayout {
|
||||
private GroupData groupData;
|
||||
private CheckedTextView name;
|
||||
|
||||
public GroupItemView(Context context) {
|
||||
super(context);
|
||||
|
||||
LayoutInflater li = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
li.inflate(R.layout.contact_selection_group_item, this, true);
|
||||
|
||||
this.name = (CheckedTextView)findViewById(R.id.name);
|
||||
}
|
||||
|
||||
public void selected() {
|
||||
name.toggle();
|
||||
|
||||
if (name.isChecked()) {
|
||||
addGroup(groupData);
|
||||
} else {
|
||||
removeGroup(groupData);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(GroupData groupData) {
|
||||
this.groupData = groupData;
|
||||
|
||||
if (selectedGroups.containsKey(groupData.id))
|
||||
this.name.setChecked(true);
|
||||
else
|
||||
this.name.setChecked(false);
|
||||
|
||||
this.name.setText(groupData.name);
|
||||
}
|
||||
}
|
||||
|
||||
// private class GroupAggregationHandler extends Handler implements Runnable {
|
||||
// private List<Recipient> recipientList = new LinkedList<Recipient>();
|
||||
// private ProgressDialog progressDialog;
|
||||
// private final Context context;
|
||||
//
|
||||
// public GroupAggregationHandler(Context context) {
|
||||
// this.context = context;
|
||||
// }
|
||||
//
|
||||
// public void run() {
|
||||
// recipientList.clear();
|
||||
//
|
||||
// for (GroupData groupData : selectedGroups.values()) {
|
||||
// List<ContactData> contactDataList = ContactAccessor.getInstance()
|
||||
// .getGroupMembership(getActivity(), groupData.id);
|
||||
//
|
||||
// Log.w("GroupSelectionListActivity", "Got contacts in group: " + contactDataList.size());
|
||||
//
|
||||
// for (ContactData contactData : contactDataList) {
|
||||
// for (NumberData numberData : contactData.numbers) {
|
||||
// recipientList.add(new Recipient(contactData.name, numberData.number, null));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.obtainMessage().sendToTarget();
|
||||
// }
|
||||
//
|
||||
// public void aggregateContacts() {
|
||||
// progressDialog = new ProgressDialog(context);
|
||||
// progressDialog.setTitle("Aggregating Contacts");
|
||||
// progressDialog.setMessage("Aggregating group contacts...");
|
||||
// progressDialog.setCancelable(false);
|
||||
// progressDialog.setIndeterminate(true);
|
||||
// progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
// progressDialog.show();
|
||||
// Log.w("GroupSelectionListActivity", "Showing group spinner...");
|
||||
// new Thread(this).start();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void handleMessage(Message message) {
|
||||
// progressDialog.dismiss();
|
||||
//
|
||||
// listener.groupAggregationComplete(new Recipients(recipientList));
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContactGroups(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
|
||||
((CursorAdapter)getListAdapter()).changeCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> arg0) {
|
||||
((CursorAdapter)getListAdapter()).changeCursor(null);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
|
@ -10,35 +10,22 @@
|
|||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ListActivity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckedTextView;
|
||||
|
@ -47,85 +34,97 @@ import android.widget.ListView;
|
|||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockListFragment;
|
||||
import com.actionbarsherlock.view.Menu;
|
||||
import com.actionbarsherlock.view.MenuInflater;
|
||||
import com.actionbarsherlock.view.MenuItem;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Activity for selecting a list of contacts. Displayed inside
|
||||
* a ContactSelectionActivity tab frame, and ultimately called by
|
||||
* ComposeMessageActivity for selecting a list of destination contacts.
|
||||
*
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
|
||||
public class ContactSelectionListActivity extends ListActivity {
|
||||
|
||||
private final HashMap<Long, ContactData> selectedContacts = new HashMap<Long, ContactData>();
|
||||
public class ContactSelectionListFragment extends SherlockListFragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
|
||||
private final HashMap<Long, ContactData> selectedContacts = new HashMap<Long, ContactData>();
|
||||
|
||||
private static final int MENU_OPTION_EXIT = 1;
|
||||
private static final int MENU_OPTION_SELECT_ALL = 2;
|
||||
private static final int MENU_OPTION_UNSELECT_ALL = 3;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
public void onActivityCreated(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
setContentView(R.layout.contact_selection_list_activity);
|
||||
initializeResources();
|
||||
displayContacts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menu.clear();
|
||||
|
||||
menu.add(0, MENU_OPTION_EXIT, Menu.NONE, "Finished!").setIcon(android.R.drawable.ic_menu_set_as);
|
||||
menu.add(0, MENU_OPTION_SELECT_ALL, Menu.NONE, "Select all").setIcon(android.R.drawable.ic_menu_add);
|
||||
menu.add(0, MENU_OPTION_UNSELECT_ALL, Menu.NONE, "Unselect all").setIcon(android.R.drawable.ic_menu_revert);
|
||||
|
||||
return true;
|
||||
initializeResources();
|
||||
initializeCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.contact_selection_list_activity, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.contact_selection_list, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case MENU_OPTION_EXIT: saveAndExit(); return true;
|
||||
case MENU_OPTION_SELECT_ALL: selectAll(); return true;
|
||||
case MENU_OPTION_UNSELECT_ALL: unselectAll(); return true;
|
||||
case R.id.menu_select_all: handleSelectAll(); return true;
|
||||
case R.id.menu_unselect_all: handleUnselectAll(); return true;
|
||||
}
|
||||
|
||||
|
||||
super.onOptionsItemSelected(item);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
|
||||
saveAndExit();
|
||||
return true;
|
||||
|
||||
public Recipients getSelectedContacts() {
|
||||
List<Recipient> recipientList = new LinkedList<Recipient>();
|
||||
|
||||
for (ContactData contactData : selectedContacts.values()) {
|
||||
for (NumberData numberData : contactData.numbers) {
|
||||
recipientList.add(new Recipient(contactData.name, numberData.number, null));
|
||||
}
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
return new Recipients(recipientList);
|
||||
}
|
||||
|
||||
private void unselectAll() {
|
||||
|
||||
|
||||
private void handleUnselectAll() {
|
||||
selectedContacts.clear();
|
||||
((CursorAdapter)getListView().getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void selectAll() {
|
||||
Log.w("ContactSelectionListActivity", "Selecting all...");
|
||||
|
||||
private void handleSelectAll() {
|
||||
selectedContacts.clear();
|
||||
|
||||
Cursor cursor = null;
|
||||
|
||||
|
||||
try {
|
||||
cursor = ContactAccessor.getInstance().getCursorForContactsWithNumbers(this);
|
||||
|
||||
cursor = ContactAccessor.getInstance().getCursorForContactsWithNumbers(getActivity());
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
ContactData contactData = ContactAccessor.getInstance().getContactData(this, cursor);
|
||||
|
||||
ContactData contactData = ContactAccessor.getInstance().getContactData(getActivity(), cursor);
|
||||
|
||||
if (contactData.numbers.isEmpty()) continue;
|
||||
else if (contactData.numbers.size() == 1) addSingleNumberContact(contactData);
|
||||
else addMultipleNumberContact(contactData, null);
|
||||
|
@ -137,66 +136,47 @@ public class ContactSelectionListActivity extends ListActivity {
|
|||
|
||||
((CursorAdapter)getListView().getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void saveAndExit() {
|
||||
List<Recipient> recipientList = new LinkedList<Recipient>();
|
||||
|
||||
for (ContactData contactData : selectedContacts.values()) {
|
||||
for (NumberData numberData : contactData.numbers) {
|
||||
recipientList.add(new Recipient(contactData.name, numberData.number, null));
|
||||
}
|
||||
}
|
||||
|
||||
Intent resultIntent = getIntent();
|
||||
resultIntent.putExtra("recipients", new Recipients(recipientList));
|
||||
|
||||
if (getParent() == null) setResult(RESULT_OK, resultIntent);
|
||||
else getParent().setResult(RESULT_OK, resultIntent);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
private void addSingleNumberContact(ContactData contactData) {
|
||||
selectedContacts.put(contactData.id, contactData);
|
||||
}
|
||||
|
||||
|
||||
private void removeContact(ContactData contactData) {
|
||||
selectedContacts.remove(contactData.id);
|
||||
}
|
||||
|
||||
|
||||
private void addMultipleNumberContact(ContactData contactData, CheckedTextView textView) {
|
||||
String[] options = new String[contactData.numbers.size()];
|
||||
int i = 0;
|
||||
|
||||
|
||||
for (NumberData option : contactData.numbers) {
|
||||
options[i++] = option.type + " " + option.number;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle("Select for " + contactData.name);
|
||||
builder.setMultiChoiceItems(options, null, new DiscriminatorClickedListener(contactData));
|
||||
builder.setPositiveButton("Ok", new DiscriminatorFinishedListener(contactData, textView));
|
||||
builder.setOnCancelListener(new DiscriminatorFinishedListener(contactData, textView));
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void displayContacts() {
|
||||
Cursor cursor = ContactAccessor.getInstance().getCursorForContactsWithNumbers(this);
|
||||
startManagingCursor(cursor);
|
||||
setListAdapter(new ContactSelectionListAdapter(this, cursor));
|
||||
|
||||
private void initializeCursor() {
|
||||
setListAdapter(new ContactSelectionListAdapter(getActivity(), null));
|
||||
this.getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
|
||||
private void initializeResources() {
|
||||
this.getListView().setFocusable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
((ContactItemView)v).selected();
|
||||
}
|
||||
|
||||
|
||||
private class ContactSelectionListAdapter extends CursorAdapter {
|
||||
|
||||
|
||||
public ContactSelectionListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
@ -213,9 +193,9 @@ public class ContactSelectionListActivity extends ListActivity {
|
|||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
ContactData contactData = ContactAccessor.getInstance().getContactData(context, cursor);
|
||||
((ContactItemView)view).set(contactData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class ContactItemView extends RelativeLayout {
|
||||
private ContactData contactData;
|
||||
private CheckedTextView name;
|
||||
|
@ -236,40 +216,40 @@ public class ContactSelectionListActivity extends ListActivity {
|
|||
|
||||
public void selected() {
|
||||
name.toggle();
|
||||
|
||||
|
||||
if (name.isChecked()) {
|
||||
if (contactData.numbers.size() == 1) addSingleNumberContact(contactData);
|
||||
else addMultipleNumberContact(contactData, name);
|
||||
} else {
|
||||
removeContact(contactData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void set(ContactData contactData) {
|
||||
this.contactData = contactData;
|
||||
|
||||
|
||||
if (selectedContacts.containsKey(contactData.id))
|
||||
this.name.setChecked(true);
|
||||
else
|
||||
this.name.setChecked(false);
|
||||
|
||||
|
||||
this.name.setText(contactData.name);
|
||||
|
||||
|
||||
if (contactData.numbers.isEmpty()) {
|
||||
this.name.setEnabled(false);
|
||||
this.number.setText("");
|
||||
this.label.setText("");
|
||||
} else {
|
||||
this.number.setText(contactData.numbers.get(0).number);
|
||||
this.label.setText(contactData.numbers.get(0).type);
|
||||
this.label.setText(contactData.numbers.get(0).type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class DiscriminatorFinishedListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
|
||||
private final ContactData contactData;
|
||||
private final CheckedTextView textView;
|
||||
|
||||
|
||||
public DiscriminatorFinishedListener(ContactData contactData, CheckedTextView textView) {
|
||||
this.contactData = contactData;
|
||||
this.textView = textView;
|
||||
|
@ -277,14 +257,14 @@ public class ContactSelectionListActivity extends ListActivity {
|
|||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
ContactData selected = selectedContacts.get(contactData.id);
|
||||
|
||||
|
||||
if (selected == null && textView != null) {
|
||||
if (textView != null) textView.setChecked(false);
|
||||
} else if (selected.numbers.size() == 0) {
|
||||
selectedContacts.remove(selected.id);
|
||||
if (textView != null) textView.setChecked(false);
|
||||
}
|
||||
|
||||
|
||||
if (textView == null)
|
||||
((CursorAdapter)getListView().getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
|
@ -293,38 +273,52 @@ public class ContactSelectionListActivity extends ListActivity {
|
|||
onClick(dialog, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class DiscriminatorClickedListener implements DialogInterface.OnMultiChoiceClickListener {
|
||||
private final ContactData contactData;
|
||||
|
||||
|
||||
public DiscriminatorClickedListener(ContactData contactData) {
|
||||
this.contactData = contactData;
|
||||
}
|
||||
|
||||
|
||||
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
|
||||
Log.w("ContactSelectionListActivity", "Got checked: " + isChecked);
|
||||
|
||||
|
||||
ContactData existing = selectedContacts.get(contactData.id);
|
||||
|
||||
|
||||
if (existing == null) {
|
||||
Log.w("ContactSelectionListActivity", "No existing contact data, creating...");
|
||||
|
||||
if (!isChecked)
|
||||
throw new AssertionError("We shouldn't be unchecking data that doesn't exist.");
|
||||
|
||||
|
||||
existing = new ContactData();
|
||||
existing.id = contactData.id;
|
||||
existing.name = contactData.name;
|
||||
existing.numbers = new LinkedList<NumberData>();
|
||||
|
||||
|
||||
selectedContacts.put(existing.id, existing);
|
||||
}
|
||||
|
||||
|
||||
NumberData selectedData = contactData.numbers.get(which);
|
||||
|
||||
|
||||
if (!isChecked) existing.numbers.remove(selectedData);
|
||||
else existing.numbers.add(selectedData);
|
||||
else existing.numbers.add(selectedData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContactsWithNumbers(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
|
||||
((CursorAdapter)getListAdapter()).changeCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> arg0) {
|
||||
((CursorAdapter)getListAdapter()).changeCursor(null);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
|
@ -10,35 +10,21 @@
|
|||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.RedPhoneCallTypes;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CallLog.Calls;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckedTextView;
|
||||
|
@ -48,102 +34,82 @@ import android.widget.ListView;
|
|||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockListFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.RedPhoneCallTypes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Displays a list of recently used contacts for multi-select. Displayed
|
||||
* by the ContactSelectionActivity in a tab frame, and ultimately used by
|
||||
* ComposeMessageActivity for selecting destination message contacts.
|
||||
*
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class ContactSelectionRecentActivity extends ListActivity {
|
||||
|
||||
private final HashMap<Long, ContactData> selectedContacts = new HashMap<Long, ContactData>();
|
||||
public class ContactSelectionRecentFragment extends SherlockListFragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
|
||||
private final HashMap<Long, ContactData> selectedContacts = new HashMap<Long, ContactData>();
|
||||
|
||||
private static final int MENU_OPTION_EXIT = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
setContentView(R.layout.contact_selection_recent_activity);
|
||||
public void onActivityCreated(Bundle icicle) {
|
||||
super.onActivityCreated(icicle);
|
||||
|
||||
initializeResources();
|
||||
displayContacts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menu.clear();
|
||||
menu.add(0, MENU_OPTION_EXIT, Menu.NONE, "Finished!").setIcon(android.R.drawable.ic_menu_set_as);
|
||||
return true;
|
||||
initializeCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case MENU_OPTION_EXIT: saveAndExit(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.contact_selection_recent_activity, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
|
||||
saveAndExit();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
((CallItemView)v).selected();
|
||||
}
|
||||
|
||||
private void saveAndExit() {
|
||||
|
||||
private void initializeCursor() {
|
||||
setListAdapter(new ContactSelectionListAdapter(getActivity(), null));
|
||||
this.getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
public Recipients getSelectedContacts() {
|
||||
List<Recipient> recipientList = new LinkedList<Recipient>();
|
||||
|
||||
|
||||
for (ContactData contactData : selectedContacts.values()) {
|
||||
for (NumberData numberData : contactData.numbers) {
|
||||
recipientList.add(new Recipient(contactData.name, numberData.number, null));
|
||||
}
|
||||
}
|
||||
|
||||
Intent resultIntent = getIntent();
|
||||
resultIntent.putExtra("recipients", new Recipients(recipientList));
|
||||
|
||||
if (getParent() == null) setResult(RESULT_OK, resultIntent);
|
||||
else getParent().setResult(RESULT_OK, resultIntent);
|
||||
|
||||
finish();
|
||||
|
||||
return new Recipients(recipientList);
|
||||
}
|
||||
|
||||
|
||||
private void addSingleNumberContact(ContactData contactData) {
|
||||
selectedContacts.put(contactData.id, contactData);
|
||||
}
|
||||
|
||||
|
||||
private void removeContact(ContactData contactData) {
|
||||
selectedContacts.remove(contactData.id);
|
||||
}
|
||||
|
||||
private void displayContacts() {
|
||||
Cursor cursor = getContentResolver().query(Calls.CONTENT_URI, null, null, null, Calls.DEFAULT_SORT_ORDER);
|
||||
startManagingCursor(cursor);
|
||||
|
||||
setListAdapter(new ContactSelectionListAdapter(this, cursor));
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.getListView().setFocusable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
((CallItemView)v).selected();
|
||||
}
|
||||
|
||||
private class ContactSelectionListAdapter extends CursorAdapter {
|
||||
|
||||
|
||||
public ContactSelectionListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
@ -164,11 +130,11 @@ public class ContactSelectionRecentActivity extends ListActivity {
|
|||
String number = cursor.getString(cursor.getColumnIndexOrThrow(Calls.NUMBER));
|
||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.TYPE));
|
||||
long date = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DATE));
|
||||
|
||||
|
||||
((CallItemView)view).set(id, name, label, number, type, date);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class CallItemView extends RelativeLayout {
|
||||
private ContactData contactData;
|
||||
private Context context;
|
||||
|
@ -176,7 +142,7 @@ public class ContactSelectionRecentActivity extends ListActivity {
|
|||
private TextView date;
|
||||
private TextView label;
|
||||
private TextView number;
|
||||
private CheckedTextView line1;
|
||||
private CheckedTextView line1;
|
||||
|
||||
public CallItemView(Context context) {
|
||||
super(context);
|
||||
|
@ -194,41 +160,59 @@ public class ContactSelectionRecentActivity extends ListActivity {
|
|||
|
||||
public void selected() {
|
||||
line1.toggle();
|
||||
|
||||
|
||||
if (line1.isChecked()) {
|
||||
addSingleNumberContact(contactData);
|
||||
} else {
|
||||
removeContact(contactData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set(long id, String name, String label, String number, int type, long date) {
|
||||
if( name == null ) {
|
||||
name = ContactAccessor.getInstance().getNameForNumber(ContactSelectionRecentActivity.this, number);
|
||||
name = ContactAccessor.getInstance().getNameForNumber(getActivity(), number);
|
||||
}
|
||||
|
||||
|
||||
this.line1.setText((name == null || name.equals("")) ? number : name);
|
||||
this.number.setText((name == null || name.equals("")) ? "" : number);
|
||||
this.label.setText(label);
|
||||
this.date.setText(DateUtils.getRelativeDateTimeString(context, date, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE));
|
||||
|
||||
|
||||
if (type == Calls.INCOMING_TYPE || type == RedPhoneCallTypes.INCOMING) callTypeIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_call_log_list_incoming_call));
|
||||
else if (type == Calls.OUTGOING_TYPE || type == RedPhoneCallTypes.OUTGOING) callTypeIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_call_log_list_outgoing_call));
|
||||
else if (type == Calls.MISSED_TYPE || type == RedPhoneCallTypes.MISSED) callTypeIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_call_log_list_missed_call));
|
||||
|
||||
|
||||
this.contactData = new ContactData();
|
||||
|
||||
|
||||
if (name != null)
|
||||
this.contactData.name = name;
|
||||
|
||||
|
||||
this.contactData.id = id;
|
||||
this.contactData.numbers = new LinkedList<ContactAccessor.NumberData>();
|
||||
this.contactData.numbers.add(new NumberData(null, number));
|
||||
|
||||
|
||||
if (selectedContacts.containsKey(id))
|
||||
this.line1.setChecked(true);
|
||||
else
|
||||
this.line1.setChecked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return new CursorLoader(getActivity(), Calls.CONTENT_URI,
|
||||
null, null, null,
|
||||
Calls.DEFAULT_SORT_ORDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
|
||||
((CursorAdapter)getListAdapter()).changeCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> arg0) {
|
||||
((CursorAdapter)getListAdapter()).changeCursor(null);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.GroupData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
|
||||
/**
|
||||
* An activity for selecting a list of "contact groups." Displayed
|
||||
* by ContactSelectionActivity in a tabbed frame, and ultimately called
|
||||
* by ComposeMessageActivity for selecting a list of recipients.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class GroupSelectionListActivity extends ListActivity {
|
||||
|
||||
private final HashMap<Long, GroupData> selectedGroups = new HashMap<Long, GroupData>();
|
||||
|
||||
private static final int MENU_OPTION_EXIT = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
setContentView(R.layout.contact_selection_group_activity);
|
||||
initializeResources();
|
||||
displayGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menu.clear();
|
||||
menu.add(0, MENU_OPTION_EXIT, Menu.NONE, "Finished!").setIcon(android.R.drawable.ic_menu_set_as);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case MENU_OPTION_EXIT: saveAndExit(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
|
||||
saveAndExit();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private void saveAndExit() {
|
||||
GroupAggregationHandler aggregator = new GroupAggregationHandler();
|
||||
aggregator.aggregateContactsAndExit();
|
||||
}
|
||||
|
||||
private void addGroup(GroupData groupData) {
|
||||
selectedGroups.put(groupData.id, groupData);
|
||||
}
|
||||
|
||||
private void removeGroup(GroupData groupData) {
|
||||
selectedGroups.remove(groupData.id);
|
||||
}
|
||||
|
||||
private void displayGroups() {
|
||||
Cursor cursor = ContactAccessor.getInstance().getCursorForContactGroups(this);
|
||||
|
||||
startManagingCursor(cursor);
|
||||
setListAdapter(new GroupSelectionListAdapter(this, cursor));
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.getListView().setFocusable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
((GroupItemView)v).selected();
|
||||
}
|
||||
|
||||
private class GroupSelectionListAdapter extends CursorAdapter {
|
||||
|
||||
public GroupSelectionListAdapter(Context context, Cursor cursor) {
|
||||
super(context, cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
GroupItemView view = new GroupItemView(context);
|
||||
bindView(view, context, cursor);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
GroupData groupData = ContactAccessor.getInstance().getGroupData(GroupSelectionListActivity.this, cursor);
|
||||
((GroupItemView)view).set(groupData);
|
||||
}
|
||||
}
|
||||
|
||||
private class GroupItemView extends LinearLayout {
|
||||
private GroupData groupData;
|
||||
private CheckedTextView name;
|
||||
|
||||
public GroupItemView(Context context) {
|
||||
super(context);
|
||||
|
||||
LayoutInflater li = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
li.inflate(R.layout.contact_selection_group_item, this, true);
|
||||
|
||||
this.name = (CheckedTextView)findViewById(R.id.name);
|
||||
}
|
||||
|
||||
public void selected() {
|
||||
name.toggle();
|
||||
|
||||
if (name.isChecked()) {
|
||||
addGroup(groupData);
|
||||
} else {
|
||||
removeGroup(groupData);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(GroupData groupData) {
|
||||
this.groupData = groupData;
|
||||
|
||||
if (selectedGroups.containsKey(groupData.id))
|
||||
this.name.setChecked(true);
|
||||
else
|
||||
this.name.setChecked(false);
|
||||
|
||||
this.name.setText(groupData.name);
|
||||
}
|
||||
}
|
||||
|
||||
private class GroupAggregationHandler extends Handler implements Runnable {
|
||||
private List<Recipient> recipientList = new LinkedList<Recipient>();
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
public GroupAggregationHandler() {}
|
||||
|
||||
public void run() {
|
||||
recipientList.clear();
|
||||
|
||||
for (GroupData groupData : selectedGroups.values()) {
|
||||
List<ContactData> contactDataList = ContactAccessor.getInstance().getGroupMembership(GroupSelectionListActivity.this, groupData.id);
|
||||
|
||||
Log.w("GroupSelectionListActivity", "Got contacts in group: " + contactDataList.size());
|
||||
|
||||
for (ContactData contactData : contactDataList) {
|
||||
for (NumberData numberData : contactData.numbers) {
|
||||
recipientList.add(new Recipient(contactData.name, numberData.number, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.obtainMessage().sendToTarget();
|
||||
}
|
||||
|
||||
public void aggregateContactsAndExit() {
|
||||
progressDialog = new ProgressDialog(GroupSelectionListActivity.this);
|
||||
progressDialog.setTitle("Aggregating Contacts");
|
||||
progressDialog.setMessage("Aggregating group contacts...");
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
progressDialog.show();
|
||||
Log.w("GroupSelectionListActivity", "Showing group spinner...");
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
progressDialog.dismiss();
|
||||
|
||||
Intent resultIntent = getIntent();
|
||||
resultIntent.putExtra("recipients", new Recipients(recipientList));
|
||||
|
||||
if (getParent() == null) setResult(RESULT_OK, resultIntent);
|
||||
else getParent().setResult(RESULT_OK, resultIntent);
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
|
@ -10,25 +10,25 @@
|
|||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Android changed their contacts API pretty heavily between
|
||||
|
@ -36,37 +36,17 @@ import android.os.Parcelable;
|
|||
* API operations, using a singleton pattern that will Class.forName
|
||||
* the correct one so we don't trigger NoClassDefFound exceptions on
|
||||
* old platforms.
|
||||
*
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
|
||||
public abstract class ContactAccessor {
|
||||
public abstract class ContactAccessor {
|
||||
public static final int UNIQUE_ID = 0;
|
||||
public static final int DISPLAY_NAME = 1;
|
||||
|
||||
private static ContactAccessor sInstance;
|
||||
|
||||
public static synchronized ContactAccessor getInstance() {
|
||||
if (sInstance == null) {
|
||||
String className;
|
||||
private static final ContactAccessor sInstance = new ContactAccessorNewApi();
|
||||
|
||||
if (Integer.parseInt(Build.VERSION.SDK) <= Build.VERSION_CODES.DONUT)
|
||||
className = "ContactAccessorOldApi";
|
||||
else
|
||||
className = "ContactAccessorNewApi";
|
||||
|
||||
try {
|
||||
Class<? extends ContactAccessor> clazz =
|
||||
Class.forName("org.thoughtcrime.securesms.contacts." + className )
|
||||
.asSubclass(ContactAccessor.class);
|
||||
|
||||
sInstance = clazz.newInstance();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized ContactAccessor getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
|
@ -78,6 +58,8 @@ public abstract class ContactAccessor {
|
|||
public abstract List<String> getNumbersForThreadSearchFilter(String constraint, ContentResolver contentResolver);
|
||||
public abstract List<ContactData> getGroupMembership(Context context, long groupId);
|
||||
public abstract Cursor getCursorForContactGroups(Context context);
|
||||
public abstract CursorLoader getCursorLoaderForContactGroups(Context context);
|
||||
public abstract CursorLoader getCursorLoaderForContactsWithNumbers(Context context);
|
||||
public abstract Cursor getCursorForContactsWithNumbers(Context context);
|
||||
public abstract GroupData getGroupData(Context context, Cursor cursor);
|
||||
public abstract ContactData getContactData(Context context, Cursor cursor);
|
||||
|
@ -85,9 +67,9 @@ public abstract class ContactAccessor {
|
|||
public abstract CharSequence phoneTypeToString(Context mContext, int type, CharSequence label);
|
||||
public abstract String getNameForNumber(Context context, String number);
|
||||
public abstract Uri getContactsUri();
|
||||
|
||||
|
||||
public static class NumberData implements Parcelable {
|
||||
|
||||
|
||||
public static final Parcelable.Creator<NumberData> CREATOR = new Parcelable.Creator<NumberData>() {
|
||||
public NumberData createFromParcel(Parcel in) {
|
||||
return new NumberData(in);
|
||||
|
@ -105,12 +87,12 @@ public abstract class ContactAccessor {
|
|||
this.type = type;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
|
||||
public NumberData(Parcel in) {
|
||||
number = in.readString();
|
||||
type = in.readString();
|
||||
}
|
||||
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
@ -120,14 +102,14 @@ public abstract class ContactAccessor {
|
|||
dest.writeString(type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class GroupData {
|
||||
public long id;
|
||||
public String name;
|
||||
}
|
||||
|
||||
|
||||
public static class ContactData implements Parcelable {
|
||||
|
||||
|
||||
public static final Parcelable.Creator<ContactData> CREATOR = new Parcelable.Creator<ContactData>() {
|
||||
public ContactData createFromParcel(Parcel in) {
|
||||
return new ContactData(in);
|
||||
|
@ -143,18 +125,18 @@ public abstract class ContactAccessor {
|
|||
public List<NumberData> numbers;
|
||||
|
||||
public ContactData() {}
|
||||
|
||||
|
||||
public ContactData(Parcel in) {
|
||||
id = in.readLong();
|
||||
name = in.readString();
|
||||
numbers = new LinkedList<NumberData>();
|
||||
in.readTypedList(numbers, NumberData.CREATOR);
|
||||
}
|
||||
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(id);
|
||||
dest.writeString(name);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
|
@ -10,21 +10,12 @@
|
|||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
|
@ -39,17 +30,27 @@ import android.provider.ContactsContract.Contacts;
|
|||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface into the Android 2.x+ contacts operations.
|
||||
*
|
||||
*
|
||||
* @author Stuart Anderson
|
||||
*/
|
||||
|
||||
public class ContactAccessorNewApi extends ContactAccessor {
|
||||
|
||||
|
||||
private static final String SORT_ORDER = Contacts.TIMES_CONTACTED + " DESC," + Contacts.DISPLAY_NAME + "," + Phone.TYPE;
|
||||
|
||||
private static final String[] PROJECTION_PHONE = {
|
||||
|
@ -65,22 +66,22 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
public List<String> getNumbersForThreadSearchFilter(String constraint, ContentResolver contentResolver) {
|
||||
LinkedList<String> numberList = new LinkedList<String>();
|
||||
Cursor cursor = null;
|
||||
|
||||
|
||||
try {
|
||||
cursor = contentResolver.query(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(constraint)),
|
||||
null, null, null, null);
|
||||
|
||||
cursor = contentResolver.query(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(constraint)),
|
||||
null, null, null, null);
|
||||
|
||||
while (cursor != null && cursor.moveToNext())
|
||||
numberList.add(cursor.getString(cursor.getColumnIndexOrThrow(Phone.NUMBER)));
|
||||
|
||||
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
|
||||
return numberList;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Cursor getCursorForRecipientFilter(CharSequence constraint, ContentResolver mContentResolver) {
|
||||
String phone = "";
|
||||
|
@ -97,22 +98,22 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Uri uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(cons));
|
||||
String selection = String.format("%s=%s OR %s=%s OR %s=%s",
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_MOBILE,
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_WORK_MOBILE,
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_MMS);
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_MOBILE,
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_WORK_MOBILE,
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_MMS);
|
||||
|
||||
Cursor phoneCursor = mContentResolver.query(uri,
|
||||
PROJECTION_PHONE,
|
||||
null,
|
||||
null,
|
||||
SORT_ORDER);
|
||||
|
||||
PROJECTION_PHONE,
|
||||
null,
|
||||
null,
|
||||
SORT_ORDER);
|
||||
|
||||
|
||||
|
||||
if (phone.length() > 0) {
|
||||
|
@ -139,9 +140,9 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
} else {
|
||||
return phoneCursor;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CharSequence phoneTypeToString( Context mContext, int type, CharSequence label ) {
|
||||
return Phone.getTypeLabel(mContext.getResources(), type, label);
|
||||
|
@ -156,50 +157,50 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
|
||||
private long getContactIdFromLookupUri(Context context, Uri uri) {
|
||||
Cursor cursor = null;
|
||||
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, new String[] {ContactsContract.Contacts._ID}, null, null, null);
|
||||
|
||||
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
return cursor.getLong(0);
|
||||
else
|
||||
return -1;
|
||||
|
||||
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ArrayList<Long> getRawContactIds(Context context, long contactId) {
|
||||
Cursor cursor = null;
|
||||
ArrayList<Long> rawContactIds = new ArrayList<Long>();
|
||||
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
|
||||
RawContacts.CONTACT_ID + " = ?", new String[] {contactId+""},
|
||||
null);
|
||||
|
||||
cursor = context.getContentResolver().query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
|
||||
RawContacts.CONTACT_ID + " = ?", new String[] {contactId+""},
|
||||
null);
|
||||
|
||||
if (cursor == null)
|
||||
return rawContactIds;
|
||||
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
rawContactIds.add(new Long(cursor.getLong(0)));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
|
||||
return rawContactIds;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void insertIdentityKey(Context context, Uri uri, IdentityKey identityKey) {
|
||||
long contactId = getContactIdFromLookupUri(context, uri);
|
||||
Log.w("ContactAccessorNewApi", "Got contact ID: " + contactId + " from uri: " + uri.toString());
|
||||
ArrayList<Long> rawContactIds = getRawContactIds(context, contactId);
|
||||
|
||||
|
||||
for (long rawContactId : rawContactIds) {
|
||||
Log.w("ContactAccessorNewApi", "Inserting data for raw contact id: " + rawContactId);
|
||||
ContentValues contentValues = new ContentValues();
|
||||
|
@ -208,9 +209,9 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
contentValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
|
||||
contentValues.put(Im.CUSTOM_PROTOCOL, "TextSecure-IdentityKey");
|
||||
contentValues.put(Im.DATA, Base64.encodeBytes(identityKey.serialize()));
|
||||
|
||||
context.getContentResolver().insert(Data.CONTENT_URI, contentValues);
|
||||
}
|
||||
|
||||
context.getContentResolver().insert(Data.CONTENT_URI, contentValues);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -218,16 +219,16 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
long contactId = getContactIdFromLookupUri(context, uri);
|
||||
String selection = Im.CONTACT_ID + " = ? AND " + Im.PROTOCOL + " = ? AND " + Im.CUSTOM_PROTOCOL + " = ?";
|
||||
String[] selectionArgs = new String[] {contactId+"", Im.PROTOCOL_CUSTOM+"", "TextSecure-IdentityKey"};
|
||||
|
||||
|
||||
Cursor cursor = context.getContentResolver().query(Data.CONTENT_URI, null, selection, selectionArgs, null);
|
||||
|
||||
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
String data = cursor.getString(cursor.getColumnIndexOrThrow(Im.DATA));
|
||||
|
||||
if (data != null)
|
||||
|
||||
if (data != null)
|
||||
return new IdentityKey(Base64.decode(data), 0);
|
||||
|
||||
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("ContactAccessorNewApi", e);
|
||||
|
@ -246,10 +247,10 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
@Override
|
||||
public String getNameFromContact(Context context, Uri uri) {
|
||||
Cursor cursor = null;
|
||||
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME}, null, null, null);
|
||||
|
||||
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
return cursor.getString(0);
|
||||
|
||||
|
@ -257,24 +258,24 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private String getMobileNumberForId(Context context, long id) {
|
||||
Cursor cursor = null;
|
||||
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + " = ? AND " + Phone.TYPE + " = ?",
|
||||
new String[] {id+"", Phone.TYPE_MOBILE+""}, null);
|
||||
|
||||
cursor = context.getContentResolver().query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + " = ? AND " + Phone.TYPE + " = ?",
|
||||
new String[] {id+"", Phone.TYPE_MOBILE+""}, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
return cursor.getString(cursor.getColumnIndexOrThrow(Phone.NUMBER));
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -282,29 +283,41 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
public NameAndNumber getNameAndNumberFromContact(Context context, Uri uri) {
|
||||
Log.w("ContactAccessorNewApi", "Get name and number from: " + uri.toString());
|
||||
Cursor cursor = null;
|
||||
|
||||
|
||||
try {
|
||||
NameAndNumber results = new NameAndNumber();
|
||||
cursor = context.getContentResolver().query(uri, new String[] {Contacts._ID, Contacts.DISPLAY_NAME}, null, null, null);
|
||||
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
results.name = cursor.getString(1);
|
||||
results.number = getMobileNumberForId(context, cursor.getLong(0));
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursorForContactsWithNumbers(Context context) {
|
||||
public CursorLoader getCursorLoaderForContactsWithNumbers(Context context) {
|
||||
Uri uri = ContactsContract.Contacts.CONTENT_URI;
|
||||
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = 1";
|
||||
return context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, selection, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
||||
|
||||
return new CursorLoader(context, uri, null, selection, null,
|
||||
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursorForContactsWithNumbers(Context context) {
|
||||
Uri uri = ContactsContract.Contacts.CONTENT_URI;
|
||||
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = 1";
|
||||
|
||||
return context.getContentResolver().query(uri, null, selection, null,
|
||||
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
||||
}
|
||||
|
||||
private ContactData getContactData(Context context, String displayName, long id) {
|
||||
|
@ -312,52 +325,58 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
contactData.id = id;
|
||||
contactData.name = displayName;
|
||||
contactData.numbers = new LinkedList<NumberData>();
|
||||
|
||||
|
||||
Cursor numberCursor = null;
|
||||
|
||||
|
||||
try {
|
||||
numberCursor = context.getContentResolver().query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + " = ?",
|
||||
new String[] {contactData.id + ""}, null);
|
||||
|
||||
numberCursor = context.getContentResolver().query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + " = ?",
|
||||
new String[] {contactData.id + ""}, null);
|
||||
|
||||
while (numberCursor != null && numberCursor.moveToNext())
|
||||
contactData.numbers.add(new NumberData(Phone.getTypeLabel(context.getResources(),
|
||||
numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE)),
|
||||
numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL))).toString(),
|
||||
numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.NUMBER))));
|
||||
contactData.numbers.add(new NumberData(Phone.getTypeLabel(context.getResources(),
|
||||
numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE)),
|
||||
numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL))).toString(),
|
||||
numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.NUMBER))));
|
||||
} finally {
|
||||
if (numberCursor != null)
|
||||
numberCursor.close();
|
||||
}
|
||||
|
||||
|
||||
return contactData;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ContactData getContactData(Context context, Cursor cursor) {
|
||||
return getContactData(context,
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME)),
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(Contacts._ID)));
|
||||
return getContactData(context,
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME)),
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(Contacts._ID)));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Cursor getCursorForContactGroups(Context context) {
|
||||
return context.getContentResolver().query(ContactsContract.Groups.CONTENT_URI, null, null, null, ContactsContract.Groups.TITLE + " ASC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorLoader getCursorLoaderForContactGroups(Context context) {
|
||||
return new CursorLoader(context, ContactsContract.Groups.CONTENT_URI,
|
||||
null, null, null, ContactsContract.Groups.TITLE + " ASC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ContactData> getGroupMembership(Context context, long groupId) {
|
||||
LinkedList<ContactData> contacts = new LinkedList<ContactData>();
|
||||
LinkedList<ContactData> contacts = new LinkedList<ContactData>();
|
||||
Cursor groupMembership = null;
|
||||
|
||||
|
||||
try {
|
||||
String selection = ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID + " = ? AND " +
|
||||
String selection = ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID + " = ? AND " +
|
||||
ContactsContract.CommonDataKinds.GroupMembership.MIMETYPE + " = ?";
|
||||
String[] args = new String[] {groupId+"",
|
||||
ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE};
|
||||
|
||||
|
||||
groupMembership = context.getContentResolver().query(Data.CONTENT_URI, null, selection, args, null);
|
||||
|
||||
|
||||
while (groupMembership != null && groupMembership.moveToNext()) {
|
||||
String displayName = groupMembership.getString(groupMembership.getColumnIndexOrThrow(Data.DISPLAY_NAME));
|
||||
long contactId = groupMembership.getLong(groupMembership.getColumnIndexOrThrow(Data.CONTACT_ID));
|
||||
|
@ -368,16 +387,16 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
if (groupMembership != null)
|
||||
groupMembership.close();
|
||||
}
|
||||
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GroupData getGroupData(Context context, Cursor cursor) {
|
||||
GroupData groupData = new GroupData();
|
||||
groupData.id = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.Groups._ID));
|
||||
groupData.name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Groups.TITLE));
|
||||
|
||||
|
||||
return groupData;
|
||||
}
|
||||
|
||||
|
@ -393,7 +412,7 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -401,5 +420,5 @@ public class ContactAccessorNewApi extends ContactAccessor {
|
|||
public Uri getContactsUri() {
|
||||
return ContactsContract.Contacts.CONTENT_URI;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,328 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.MergeCursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.Contacts;
|
||||
import android.provider.Contacts.GroupMembership;
|
||||
import android.provider.Contacts.People;
|
||||
import android.provider.Contacts.Phones;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
||||
/**
|
||||
* A contact interface into the 1.x API for older clients.
|
||||
*
|
||||
* @author Stuart Anderson
|
||||
*/
|
||||
|
||||
public class ContactAccessorOldApi extends ContactAccessor {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final String SORT_ORDER = Phones.NAME + "," + Phones.TYPE;
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final String[] PROJECTION_PHONE = {
|
||||
Phones._ID, // 0
|
||||
Phones.PERSON_ID, // 1
|
||||
Phones.TYPE, // 2
|
||||
Phones.NUMBER, // 3
|
||||
Phones.LABEL, // 4
|
||||
Phones.DISPLAY_NAME, // 5
|
||||
};
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Cursor getCursorForRecipientFilter(CharSequence constraint,
|
||||
ContentResolver mContentResolver) {
|
||||
String phone = "";
|
||||
String wherePhone = null;
|
||||
|
||||
String cons = null;
|
||||
if (constraint != null) {
|
||||
cons = constraint.toString();
|
||||
|
||||
if (RecipientsAdapter.usefulAsDigits(cons)) {
|
||||
phone = PhoneNumberUtils.convertKeypadLettersToDigits(cons);
|
||||
if (phone.equals(cons)) {
|
||||
phone = "";
|
||||
} else {
|
||||
phone = phone.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String filter = DatabaseUtils.sqlEscapeString(cons + '%');
|
||||
String filterLastName = DatabaseUtils.sqlEscapeString("% " + cons + '%');
|
||||
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("((name LIKE ");
|
||||
s.append(filter);
|
||||
s.append(") OR (name LIKE ");
|
||||
s.append(filterLastName);
|
||||
s.append(") OR (REPLACE(REPLACE(REPLACE(REPLACE(number, ' ', ''), '(', ''), ')', ''), '-', '') LIKE ");
|
||||
s.append(filter);
|
||||
s.append("))");
|
||||
|
||||
wherePhone = s.toString();
|
||||
|
||||
Cursor phoneCursor = mContentResolver.query(Phones.CONTENT_URI,
|
||||
PROJECTION_PHONE,
|
||||
wherePhone,
|
||||
null,
|
||||
SORT_ORDER);
|
||||
|
||||
//dumpCursor(phoneCursor);
|
||||
|
||||
|
||||
if (phone.length() > 0) {
|
||||
ArrayList result = new ArrayList();
|
||||
result.add(Integer.valueOf(-1)); // ID
|
||||
result.add(Long.valueOf(-1)); // CONTACT_ID
|
||||
result.add(Integer.valueOf(Phones.TYPE_CUSTOM)); // TYPE
|
||||
result.add(phone); // NUMBER
|
||||
|
||||
/*
|
||||
* The "\u00A0" keeps Phone.getDisplayLabel() from deciding
|
||||
* to display the default label ("Home") next to the transformation
|
||||
* of the letters into numbers.
|
||||
*/
|
||||
result.add("\u00A0"); // LABEL
|
||||
result.add(cons); // NAME
|
||||
|
||||
ArrayList<ArrayList> wrap = new ArrayList<ArrayList>();
|
||||
wrap.add(result);
|
||||
|
||||
ArrayListCursor translated = new ArrayListCursor(PROJECTION_PHONE, wrap);
|
||||
|
||||
return new MergeCursor(new Cursor[] { translated, phoneCursor });
|
||||
} else {
|
||||
return phoneCursor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public CharSequence phoneTypeToString(Context mContext, int type,
|
||||
CharSequence label) {
|
||||
return Phones.getDisplayLabel(mContext, type, label);
|
||||
}
|
||||
|
||||
public static void dumpCursor( Cursor c ) {
|
||||
c.moveToFirst();
|
||||
Log.d( "DC", "Begin:" );
|
||||
for( int i=0; i < c.getCount(); i++ ) {
|
||||
String rowStr = "";
|
||||
for( int j=0; j < c.getColumnCount(); j++ ) {
|
||||
rowStr = rowStr + c.getColumnName(j) + "=" + c.getString(j) +" ";
|
||||
}
|
||||
Log.d( "DC", rowStr + "\n" );
|
||||
c.moveToNext();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntentForContactSelection() {
|
||||
return new Intent(Intent.ACTION_PICK, People.CONTENT_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertIdentityKey(Context context, Uri uri, IdentityKey identityKey) {
|
||||
Toast.makeText(context, "Sorry, reading and writing identity keys to the contacts database is not supported on Android 1.X", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey importIdentityKey(Context context, Uri uri) {
|
||||
Toast.makeText(context, "Sorry, reading and writing identity keys to the contacts database is not supported on Android 1.X", Toast.LENGTH_LONG).show();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameFromContact(Context context, Uri uri) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNumbersForThreadSearchFilter(String constraint, ContentResolver contentResolver) {
|
||||
LinkedList<String> numberList = new LinkedList<String>();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = contentResolver.query(Uri.withAppendedPath(Contacts.People.CONTENT_FILTER_URI, Uri.encode(constraint)),
|
||||
null, null, null, null);
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(Contacts.Phones.NUMBER));
|
||||
if (number != null)
|
||||
numberList.add(number);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return numberList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameAndNumber getNameAndNumberFromContact(Context context, Uri uri) {
|
||||
Cursor cursor = null;
|
||||
NameAndNumber result = new NameAndNumber();
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
result.name = cursor.getString(cursor.getColumnIndexOrThrow(People.NAME));
|
||||
result.number = cursor.getString(cursor.getColumnIndexOrThrow(People.NUMBER));
|
||||
|
||||
return result;
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursorForContactsWithNumbers(Context context) {
|
||||
return context.getContentResolver().query(People.CONTENT_URI,new String[]{People._ID,People.DISPLAY_NAME},
|
||||
People.NUMBER + " NOT NULL", null, "UPPER( " + People.DISPLAY_NAME + " ) ASC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContactData getContactData(Context context, Cursor cursor) {
|
||||
ContactData contactData = new ContactData();
|
||||
contactData.id = cursor.getLong(cursor.getColumnIndexOrThrow(People._ID));
|
||||
contactData.name = cursor.getString(cursor.getColumnIndexOrThrow(People.DISPLAY_NAME));
|
||||
contactData.numbers = getNumberDataForPersonId(context, contactData.id);
|
||||
|
||||
return contactData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursorForContactGroups(Context context) {
|
||||
return context.getContentResolver().query(Contacts.Groups.CONTENT_URI, null, null, null, Contacts.Groups.NAME + " ASC");
|
||||
}
|
||||
|
||||
private LinkedList<NumberData> getNumberDataForPersonId(Context context, long personId) {
|
||||
LinkedList<NumberData> numbers = new LinkedList<NumberData>();
|
||||
Cursor numberCursor = context.getContentResolver().query(Phones.CONTENT_URI, null,
|
||||
Phones.PERSON_ID + " = ?",
|
||||
new String[] {personId+""}, null);
|
||||
try {
|
||||
while (numberCursor != null && numberCursor.moveToNext()) {
|
||||
numbers.add(new NumberData(Phones.getDisplayLabel(context, numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phones.TYPE)), "").toString(),
|
||||
numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phones.NUMBER))));
|
||||
}
|
||||
} finally {
|
||||
if (numberCursor != null)
|
||||
numberCursor.close();
|
||||
}
|
||||
|
||||
return numbers;
|
||||
}
|
||||
|
||||
private ContactData getContactDataFromGroupMembership(Context context, Cursor cursor) {
|
||||
ContactData contactData = new ContactData();
|
||||
contactData.id = cursor.getLong(cursor.getColumnIndexOrThrow(GroupMembership.PERSON_ID));
|
||||
|
||||
Cursor personCursor = context.getContentResolver().query(Uri.withAppendedPath(People.CONTENT_URI, contactData.id+""), null, null, null, null);
|
||||
|
||||
try {
|
||||
if (personCursor == null || !personCursor.moveToFirst())
|
||||
throw new AssertionError("Non-existent user in group?");
|
||||
|
||||
contactData.name = personCursor.getString(personCursor.getColumnIndexOrThrow(People.DISPLAY_NAME));
|
||||
contactData.numbers = getNumberDataForPersonId(context, contactData.id);
|
||||
|
||||
return contactData;
|
||||
|
||||
} finally {
|
||||
if (personCursor != null)
|
||||
personCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ContactData> getGroupMembership(Context context, long groupId) {
|
||||
LinkedList<ContactData> contacts = new LinkedList<ContactData>();
|
||||
Cursor groupMembershipCursor = context.getContentResolver().query(Contacts.GroupMembership.CONTENT_URI, null,
|
||||
GroupMembership.GROUP_ID + " = ?",
|
||||
new String[] {groupId+""}, null);
|
||||
|
||||
try {
|
||||
while (groupMembershipCursor != null && groupMembershipCursor.moveToNext()) {
|
||||
contacts.add(getContactDataFromGroupMembership(context, groupMembershipCursor));
|
||||
}
|
||||
} finally {
|
||||
if (groupMembershipCursor != null)
|
||||
groupMembershipCursor.close();
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupData getGroupData(Context context, Cursor cursor) {
|
||||
GroupData groupData = new GroupData();
|
||||
groupData.id = cursor.getLong(cursor.getColumnIndexOrThrow(Contacts.Groups._ID));
|
||||
groupData.name = cursor.getString(cursor.getColumnIndexOrThrow(Contacts.Groups.NAME));
|
||||
|
||||
return groupData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameForNumber(Context context, String number) {
|
||||
Cursor cursor = context.getContentResolver().query(Contacts.Phones.CONTENT_URI, null,
|
||||
Phones.NUMBER + " = ?",
|
||||
new String[] {number}, null);
|
||||
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
return cursor.getString(cursor.getColumnIndexOrThrow(Contacts.Phones.DISPLAY_NAME));
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getContactsUri() {
|
||||
return Contacts.People.CONTENT_URI;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
|
@ -10,20 +10,20 @@
|
|||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.recipients;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class Recipients implements Parcelable {
|
||||
|
||||
|
@ -37,73 +37,77 @@ public class Recipients implements Parcelable {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
private List<Recipient> recipients;
|
||||
|
||||
|
||||
public Recipients(List<Recipient> recipients) {
|
||||
this.recipients = recipients;
|
||||
}
|
||||
|
||||
|
||||
public Recipients(Parcel in) {
|
||||
this.recipients = new ArrayList<Recipient>();
|
||||
in.readTypedList(recipients, Recipient.CREATOR);
|
||||
}
|
||||
|
||||
|
||||
public void append(Recipients recipients) {
|
||||
this.recipients.addAll(recipients.getRecipientsList());
|
||||
}
|
||||
|
||||
public Recipients truncateToSingleRecipient() {
|
||||
assert(!this.recipients.isEmpty());
|
||||
this.recipients = this.recipients.subList(0, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEmailRecipient() {
|
||||
for (Recipient recipient : recipients) {
|
||||
if (NumberUtil.isValidEmail(recipient.getNumber()))
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.recipients.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
public boolean isSingleRecipient() {
|
||||
return this.recipients.size() == 1;
|
||||
}
|
||||
|
||||
|
||||
public Recipient getPrimaryRecipient() {
|
||||
if (!isEmpty())
|
||||
return this.recipients.get(0);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public List<Recipient> getRecipientsList() {
|
||||
return this.recipients;
|
||||
}
|
||||
|
||||
|
||||
public String[] toNumberStringArray() {
|
||||
String[] recipientsArray = new String[recipients.size()];
|
||||
Iterator<Recipient> iterator = recipients.iterator();
|
||||
int i = 0;
|
||||
|
||||
|
||||
while (iterator.hasNext())
|
||||
recipientsArray[i++] = iterator.next().getNumber();
|
||||
|
||||
|
||||
return recipientsArray;
|
||||
}
|
||||
|
||||
public String toShortString() {
|
||||
String fromString = "";
|
||||
|
||||
|
||||
for (int i=0;i<recipients.size();i++) {
|
||||
fromString += recipients.get(i).toShortString();
|
||||
|
||||
|
||||
if (i != recipients.size() -1 )
|
||||
fromString += ", ";
|
||||
fromString += ", ";
|
||||
}
|
||||
|
||||
|
||||
return fromString;
|
||||
}
|
||||
|
||||
|
|