Читаем данные контактов в Android 2.x

Для тех, кто ранее работал с данными контактов в версиях системы ниже 2,0 переход на новую модель работы с данными старшей версии системы может показаться болезненным и не совсем логичным. Ниже я приведу фрагменты кода, которые делают примерно одно и тоже в разных версиях системы. Если раньше можно было читать данные достаточно коротким фрагментом кода, типа следующего:

// Работает в Android 1,6
	// Form an array specifying which columns to return. 
	String[] projection = new String[] {
			People._ID,
			People.NAME,
			People.NUMBER
	};

	// Get the base URI for the People table in the Contacts content provider.
	Uri contacts =  People.CONTENT_URI;

	// Make the query. 
	Cursor managedCursor = managedQuery(contacts,
			projection, // Which columns to return 
			null,       // Which rows to return (all rows)
			null,       // Selection arguments (none)
			// Put the results in ascending order by name
			People.NAME + " ASC");

	// Process contact data
	if (cur.moveToFirst()) {

		String name;
		String phoneNumber;
		int nameColumn = cur.getColumnIndex(People.NAME);
		int phoneColumn = cur.getColumnIndex(People.NUMBER);

		do {
			// Get the field values
			name = cur.getString(nameColumn);
			phoneNumber = cur.getString(phoneColumn);
			System.out.println("name: " + name + ", phone number: " + phoneNumber);

		} while (cur.moveToNext());
	}


то теперь для выполнения той же самой операции потребуется написать гораздо больше кода, который должен выполнить значительно больше работы:

// Работает в 2,0
    // © Ashwin Neurgaonkar
    // http://dev-chronicles.blogspot.com/2009/12/reading-contants-information-in-android.html
    // Доработано Бармалейкиным
    private void readNewStyle() {

    	String phoneTypes[] = {"Home", "Mobile", "Work", "Fax work", "Fax home", "Pager", "Other",
    						   "Callback", "Car", "Company main", "ISDN", "Main", "Other fax", "Radio",
    						   "Telex", "TTY TDD", "Work mobile", "Work pager", "Assistand", "MMS"};  

    	Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

    	while(cursor.moveToNext()) {
    		String ContactID = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
    		String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
    		System.out.println("Name: " + name + " _ID: " + ContactID);
    		String hasPhone = cursor.getString(
    				cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
    		
    		if(hasPhone.compareTo("1") == 0) {
    			
    			Cursor phoneCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
    					null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "='" + ContactID + "'",
    					null, null);

    			while(phoneCursor.moveToNext()) {
    				String number = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
    				String numberType = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
    				switch(Integer.parseInt(numberType)) {

    				// Отбираем знакомые типы телефонных номеров
    					case ContactsContract.CommonDataKinds.Phone.TYPE_HOME:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_WORK:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_CAR:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN:	
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_ISDN:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_MAIN:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX:	
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_RADIO:	
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_TELEX:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE:	
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT:
	    				case ContactsContract.CommonDataKinds.Phone.TYPE_MMS: {
	    					System.out.println("phone number: " + number + "("+phoneTypes[Integer.parseInt(numberType)]+")");
	    					break;
	    				}

	    				// Для всех остальных
	    				default: {
	    					System.out.println("phone number: " + number + "(" + numberType + ")");
	    					break;
	    				}
    				}
    			}
    			phoneCursor.close();
    		} else {
    			System.out.println("no phone number found");
    		}

    		Cursor emailCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
    				new String[] { ContactsContract.CommonDataKinds.Email.DATA,
    				ContactsContract.CommonDataKinds.Email.TYPE},
    				ContactsContract.CommonDataKinds.Email.CONTACT_ID + "='" + ContactID + "'", null, null);
    		
    		while(emailCursor.moveToNext()) {

    			String emailTypes[] = {"Home", "Work", "Other", "Mobile", "Custom"};
    			String email = emailCursor.getString(emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
    			String emailType = emailCursor.getString(emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
    		
    			switch(Integer.parseInt(emailType)) {
    			
    			// Отбираем знакомые типы электронных адресов
	    			case 1:	case 2:	case 3:	case 4:	case 5:
	    			{
	    				System.out.println(emailTypes[Integer.parseInt(emailType)] + email);
	    				break;
	    			}
	    			
	    			default: {
	    				System.out.println("Not listed type: " + email);
	    				break;
	    			}
    			}
    		}
    		emailCursor.close();
    	}
    	cursor.close();
    }


Стоит отметить, что проект для работы с контактами версии в стиле версии 2,0 может включать в себя код для работы в стиле 1,6 — Eclipse укажет вам на то что вызовы этих методов помечены как устаревшие и зачеркнёт их. Однако проект будет успешно собираться и работать. За вами лишь определение версии и вызов соответствующего метода длы выборки данных.

Комментарии (16)

RSS свернуть / развернуть
+
0
Спасибо, интересная статья, вывел на главную
avatar

Interfer0n

  • 20 мая 2010, 21:39
+
0
А можно обеспечить какой то минимальный функционал, который будет работать под все платформы? То есть, скомпилировать код в android 1.5 (SDK-3) и что б при запуске на все х последующих платформах он не падал?

Я здесь проводил некоторое исследование структуры БД контактов на разных версиях и как по мне все как то не однозначно. Я даже на хабр выложил, но статья не нашла сочувствующих :(
Может что то посоветуете?
avatar

Bosicc

  • 4 июня 2010, 03:48
+
0
Вопрос очень интересный и в тоже время насущный. В самом деле пока нет штатного способа получить из одного приложения доступ к данным контактов из разных версий системы. Разумеется, за исключением собственной реализации всех методов работы или полного включения кода из операционной системы. Если рассматривать этот вариант, то возможно имеет смысл попробовать написать собственный набор независимых версионных библиотек, которые можно было бы включать в свои проекты. Мне кажется они были бы востребованы еще достаточно долго, особенно в свете выпуска интернет-планшетов со старыми версиями системы (от 1,2).
avatar

Barmaleikin

  • 4 июня 2010, 07:44
+
0
как раз сегодня написал кусок кода, который в зависимости от версии выбирает тот или другой кусок кода для работы с контактами.
Но это только маленький кусочек, а впереди еще большой путь написания кросс платформенных контактов.
avatar

Bosicc

  • 4 июня 2010, 16:44
+
0
А можно из кода добраться до журнала звонков?
Скажем, сложный и простой варианты.
1 — сделать приложение запущенное постоянно, которое будет фиксировать звонки (кому, с… по ..) и накапливать статистику?
2 — по таймеру обращаться к журналу звонков и находя новые записи, обрабатывать их?
avatar

dmites

  • 21 июня 2010, 06:13
+
0
Запросто. Причём первый вариант предпочтительнее, т.к. система оповещает приложения, которые пожелали знать о поступлении звонка. Почитайте описание класса android.provider.CallLog.Calls, он как раз содержит необходимую вам информацию.

developer.android.com/reference/android/provider/CallLog.Calls.html
avatar

Barmaleikin

  • 21 июня 2010, 06:51
+
0
спс, я так понимаю, широкие возможности приведут скоро опять к мошенничеству.
Сделал какую-нить прогу бесплатную, распространилась она за пол года, а в ней код, который в час x отправляет программно смс на короткий номер. Пора писать фаерволы да интивирусы под android, ведь деньги с мобильника увести легче…
avatar

dmites

  • 21 июня 2010, 07:50
+
0
Ну, в целом вы мыслите верно, такая ситуация теоретически возможна. Но все это реализуется не так просто. Чтобы опубликовать свое приложение в Маркете, надо хоть как-то, но себя идентифицировать. Писать подобные программы, которые тут же будут обнаружены и удалены из Маркета, а списанные деньги возвращены — себе дороже выйдет. А файерволы и антивирусы для Андроида уже есть.
avatar

Barmaleikin

  • 21 июня 2010, 08:42
+
0
Возник вопрос, правда не совсем в тему. Как удалить отдельные поля контакты в API версии младше 5-й? Согласно документации, метод ContentResolver.update при передаче в качестве значение поля null — поле с указанным key должно удалиться, и метод возвращает кол-во обновленных записей.
Таким образом, конструкция вида:
ContentResolver resolver = getApplicationContext().getContentResolver();
ContentValues cv = new ContentValues();
cv.put(Contacts.People.NAME,"NAME");
int count = resolver.update(Contacts.People.CONTENT_URI, cv, "_ID = '1'", null);

отлично обновляет имя у контакта с _ID = 1, а вот после этого:
ContentResolver resolver = getApplicationContext().getContentResolver();
ContentValues cv = new ContentValues();
cv.putNull(Contacts.People.NAME);
int count = resolver.update(Contacts.People.CONTENT_URI, cv, "_ID = '1'", null);

ничего не происходит, хотя count = 1. Я предположил, что имя нельзя удалить и попробовал на Contacts.Organizations.COMPANY, результат — тот же. Может кто сталкивался?
avatar

lavel

  • 23 июня 2010, 10:24
+
0
вообще то метод update применяется для перезаписи данных. Для удаления записи используйте метод delete
String userID = "22";
String uri = People.CONTENT_URI + "/" + userID;
this.getContentResolver().delete(Uri.parse(uri), null, null);

Этот код полностью удалит строку из БД. У которой ID = 22;
avatar

Bosicc

  • 25 июня 2010, 05:10
+
0
Так мой вопрос в том, как удалить поле контакта, но не весь контакт. Согласно документации для этого используется метод update и значение null для этого поля, т.е. cv.putNull(Contacts.People.NAME) должен удалить имя у контакта, но сам контакт останется.
avatar

lavel

  • 25 июня 2010, 05:20
+
0
А как Вы представляете себе эту процедуру :) Есть таблица в базе данных. И Вы можете удалить либо столбец либо строку.
А удалить ячейку у одной записи не получится. Если очень интересно что же там происходит, то можете скачать БД с эмулятора на комп и с помощью утилиты посмотреть что там внутри происходит.
Потому что из документации не совсем очевидно чему именно присваивается null.
avatar

Bosicc

  • 25 июня 2010, 05:29
+
0
Ну если вчитаться в описание параметров метода update
«values — The new field values. The key is the column name for the field. A null value will remove an existing field value.» покажется очевидным, что именно удаляется, есть сущность — контакт, есть его свойства — имя, телефон, адрес вот их и нужно удалять. Зайдя в приложение Contacts вы сможете у кого-нибудь удалить например номер мобильного телефона? ;-) Вот мне нужно тоже самое, но в коде.
avatar

lavel

  • 25 июня 2010, 05:35
+
0
К сожалению у меня сейчас нет времени написать мелкий тестик. Но могу дать идею :)
Можете посмотреть мой пост про структуру контактов в 1.5 и 2.1. БД отличается. И если вам надо удалит один из телефонов пользователя, то нужно просто удалить запись из таблицы Phone (связь таблиц в 1.5). Примерно тоже самое надо делать и в 2.1, но там уже телефон хранится в другой базе (Data)
Ну а для примера моно глянуть опен сорс код google Contacts2.
avatar

Bosicc

  • 25 июня 2010, 05:54
+
0
Вы уверены, что из приложения будет доступ к бд другого приложения на уровне SQL, без ContentProvider? Я лично сомневаюсь. Да и хотелось бы сделать это стандартными методами, без работы с данными напрямую, мне казалось в это вся идеология Content.
avatar

lavel

  • 25 июня 2010, 06:09
+
0
А я и не говорю про прямой доступ к БД. Попробуйте это:
String uri = Phones.CONTENT_URI + "/"+ "13"; // 13 - id in Phone table
this.getContentResolver().delete(Uri.parse(uri), null, null);

Этот код удалит строку из таблицы телефонов у которого id=13. А этот id можно получить из таблицы People из поля People.ISPRIMARY ( это как удалить основной номер)
avatar

Bosicc

  • 25 июня 2010, 10:06

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.