am a7c86fc5: am 15fc0e5f: Reconcile with ics-mr1-release
* commit 'a7c86fc53dd15b79dbe6304d7bbb405169e33349':
diff --git a/proguard.flags b/proguard.flags
index f56ffff..308513b 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -7,7 +7,10 @@
int getCount();
}
-# Any methods whose name is '*ForTest' are preserved.
--keep class ** {
- *** *ForTest(...);
+# Any class or method annotated with NeededForTesting.
+-keep @com.android.providers.contacts.util.NeededForTesting class *
+-keepclassmembers class * {
+@com.android.providers.contacts.util.NeededForTesting *;
}
+
+-verbose
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index df3e1f6..2f6562b 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android-kernprogramme"</string>
<string name="app_label" msgid="3389954322874982620">"Kontakte-berging"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakte"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Gradeer kontaktedatabasis op."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Kontakopgradering benodig meer berging"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Gradeer kontakberging op"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Kies om die opgradering te voltooi."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Gradeer kontaktedatabasis op."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kontakte-opgradering benodig meer geheue."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Gradeer berging vir kontakte op"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Raak om die opgradering te voltooi."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakte"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Ander"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Kry toegang tot alle stemboodskappe"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Laat die program alle stemboodskappe stoor en ophaal wat hierdie toestel kan lees."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Laat die program toe om alle stemboodskappe te stoor en op te haal wat hierdie toestel kan lees."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Stemboodskap van "</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index ce2da2f..6aa55ec 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"የAndroid ኮር ትግበራ"</string>
<string name="app_label" msgid="3389954322874982620">"የዕውቂያ ማከማቻ"</string>
<string name="provider_label" msgid="6012150850819899907">"እውቅያዎች"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"የእውቂያዎችን ውሂብ ጎታ በማሻሻል ላይ፡፡"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"የእውቅያ አልቅ ተጨማሪ ማህደረ ትውስታ ይፈልጋል"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"የእውቂያ ማከማቻን ማላቅ"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"አልቁን ለማጠናቀቅ ምረጥ።"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"የእውቂያዎችን ውሂብ ጎታ በማሻሻል ላይ፡፡"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"የእውቅያዎች አሻሽል ተጨማሪ ማህደረ ትውስታ ይፈልጋል"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"ለእውቅያዎች ማከማቻ በማሻሻል ላይ"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"አሻሽሉን ለማጠናቀቅ ንካ፡፡"</string>
<string name="default_directory" msgid="93961630309570294">"እውቅያዎች"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"ሌላ"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"ሁሉንም የድምፅ መልዕክቶች ድረስ"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"ትግበራ ይህ መሣሪያ መድረስ የሚችለውን የድምፅ መልዕክቶች በሙሉ ለማከማቸት እና ሰርስሮ ለማውጣት ይፈቅዳል።"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"ትግበራ ይህ መሣሪያ መድረስ የሚችለውን የድምፅ መልዕክቶች በሙሉ ለማከማቸት እና ሰርስሮ ለማውጣት ይፈቅዳል።"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"ከ....የድምፅ መልዕክት "</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 9e0ac4e..3cd298d 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"تطبيقات Android المركزية"</string>
<string name="app_label" msgid="3389954322874982620">"تخزين جهات الاتصال"</string>
<string name="provider_label" msgid="6012150850819899907">"جهات الاتصال"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"جارٍ ترقية قاعدة بيانات جهات الاتصال."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"تحتاج ترقية جهة الاتصال إلى مزيد من الذاكرة"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"سعة تخزين ترقية جهة الاتصال"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"حدد لإكمال الترقية."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"جارٍ ترقية قاعدة بيانات جهات الاتصال."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"تتطلب ترقية جهات الاتصال مزيدًا من الذاكرة."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"ترقية وحدة التخزين لجهات الاتصال"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"المس لإكمال عملية الترقية."</string>
<string name="default_directory" msgid="93961630309570294">"جهات الاتصال"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"غير ذلك"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"الوصول إلى جميع رسائل البريد الصوتي"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"للسماح للتطبيق بتخزين واسترداد جميع رسائل البريد الصوتي التي يمكن الوصول إليها عبر هذا الجهاز."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"للسماح للتطبيق بتخزين واسترداد جميع رسائل البريد الصوتي التي يمكن الوصول إليها عبر هذا الجهاز."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"بريد صوتي من "</string>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 7f24194..44357c3 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Асноўныя праграмы для Android"</string>
<string name="app_label" msgid="3389954322874982620">"Сховішча кантактаў"</string>
<string name="provider_label" msgid="6012150850819899907">"Кантакты"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Абнаўленне базы дадзеных кантактаў."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Для абнаўлення кантакту патрабуецца больш памяці"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Абнаўленне сховішча кантактаў"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Выберыце, каб завяршыць абнаўленне."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Абнаўленне базы дадзеных кантактаў."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Для абнаўлення кантактаў патрабуецца больш памяці."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Каб абнавiць кантакты, патрабуецца больш памяцi"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Націсніце, каб завершыць абнаўленне."</string>
<string name="default_directory" msgid="93961630309570294">"Кантакты"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Іншае"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Доступ да ўсіх галасавых паведамленняў"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Дазваляе прыкладанням захоўваць і ўзнаўляць усе галасавыя паведамленні, да якіх можа даступіцца гэта прылада"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Дазваляе прыкладанню захоўваць і прайграваць усе даступныя для гэтай прылады паведамленні галасавой пошты."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Галасавое паведамленне ад "</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 13df956..dee34a7 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Основни приложения на Android"</string>
<string name="app_label" msgid="3389954322874982620">"Хранилище на контакти"</string>
<string name="provider_label" msgid="6012150850819899907">"Контакти"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Базата от данни на контактите се надгражда."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Надстройването на контакта се нуждае от още памет"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Надстройка на хранилището за контакти"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Изберете за завършване на надстройването."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Базата от данни на контактите се надстройва."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"За надстройването на контактите е необходима още памет."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Хранилището за контакти се надстройва"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Докоснете, за да завършите надстройването."</string>
<string name="default_directory" msgid="93961630309570294">"Контакти"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Други"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Достъп до всички гласови съобщения"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Разрешава на приложението да съхранява и извлича всички гласови съобщения, до които това устройство има достъп."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Разрешава на приложението да съхранява и извлича всички гласови съобщения, до които това устройство има достъп."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Гласова поща от "</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 8df6be3..ef073fa 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Aplicacions bàsiques d\'Android"</string>
<string name="app_label" msgid="3389954322874982620">"Emmagatzematge de contactes"</string>
<string name="provider_label" msgid="6012150850819899907">"Contactes"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"S\'està actualitzant la base de dades de Contactes."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Per actualitzar els contactes cal tenir més memòria"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"S\'està actualitzant l\'emmagatzematge dels contactes"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Seleccioneu-ho per completar l\'actualització."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"S\'està actualitzant la base de dades dels contactes."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Per actualitzar els contactes cal tenir més memòria."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"S\'està actualitzant l\'emmagatzematge per als contactes"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Toca-ho per completar l\'actualització."</string>
<string name="default_directory" msgid="93961630309570294">"Contactes"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Altres"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accés a tots els missatges de veu"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Permet que l\'aplicació emmagatzemi i recuperi tots els missatges de veu als quals pot accedir aquest dispositiu."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permet que l\'aplicació emmagatzemi i recuperi totes les bústies de veu a les quals pot accedir aquest dispositiu."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Missatge de veu de "</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 3da1f3d..76335a0 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Úložiště kontaktů"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakty"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Aktualizace databáze kontaktů."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Upgrade kontaktu vyžaduje více paměti"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Nedostatek paměti pro upgrade kontaktu"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Výběrem této možnosti dokončíte upgrade."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Probíhá upgrade databáze kontaktů."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Upgrade kontaktů vyžaduje více paměti."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Probíhá upgrade úložiště kontaktů"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Dotykem dokončíte upgrade."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakty"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Jiné"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Přístup ke všem hlasovým zprávám"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Umožňuje aplikaci ukládat a načítat všechny hlasové zprávy, ke kterým má toto zařízení přístup."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Umožňuje aplikaci ukládat a načítat všechny hlasové zprávy, ke kterým má toto zařízení přístup."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Hlasová zpráva od uživatele "</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 76bb37f..7b708fb 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android-kerneapplikationer"</string>
<string name="app_label" msgid="3389954322874982620">"Lagring af kontakter"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakter"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Opgraderer databasen med kontaktpersoner."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Opgradering af kontaktpersoner kræver mere hukommelse"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Opgraderer lagerplads til kontaktpersoner"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Vælg for at fuldføre opgraderingen."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Opgraderer databasen med kontaktpersoner."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Opgradering af kontaktpersoner kræver mere hukommelse."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Opgraderer lagring af kontaktpersoner"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Tryk for at fuldføre opgraderingen."</string>
<string name="default_directory" msgid="93961630309570294">"Kontaktpersoner"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Andre"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Adgang til alle telefonsvarerbeskeder"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Tillader applikationen at gemme og hente alle telefonsvarerbeskeder, som denne enhed kan få adgang til."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Tillader, at applikationen gemmer og henter alle telefonsvarerbeskeder, som denne enhed kan få adgang til."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Telefonsvarerbesked fra "</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 6827f15..1f10b19 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Kontakte-Speicher"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakte"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Kontaktdatenbank wird aktualisiert..."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Kontaktupgrade erfordert mehr Speicher"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Kontaktspeicher wird aktualisiert"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Zum Abschluss des Upgrades auswählen"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Kontaktdatenbank wird aktualisiert..."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kontakte-Upgrade erfordert mehr Speicher."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Speicher für Kontakte wird aktualisiert..."</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Zum Abschluss des Upgrades berühren"</string>
<string name="default_directory" msgid="93961630309570294">"Kontakte"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Sonstige"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Zugriff auf alle Mailbox-Nachrichten"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Ermöglicht der Anwendung das Speichern und Abrufen aller Mailbox-Nachrichten, auf die dieses Gerät zugreifen kann"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Ermöglicht der App das Speichern und Abrufen aller Mailbox-Nachrichten, auf die dieses Gerät zugreifen kann"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Mailbox-Nachricht von "</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 4fb21c9..2622515 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Βασικές εφαρμογές Android"</string>
<string name="app_label" msgid="3389954322874982620">"Χώρος αποθήκευσης επαφών"</string>
<string name="provider_label" msgid="6012150850819899907">"Επαφές"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Αναβάθμιση βάσης δεδομένων επαφών"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Η αναβάθμιση των επαφών απαιτεί περισσότερη μνήμη"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Αναβάθμιση χώρου αποθήκευσης επαφών"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Επιλέξτε για την ολοκλήρωση της αναβάθμισης."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Αναβάθμιση βάσης δεδομένων επαφών"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Απαιτείται περισσότερη μνήμη για την αναβάθμιση των επαφών."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Αναβάθμιση χώρου αποθήκευσης για επαφές"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Αγγίξτε για να ολοκληρώσετε την αναβάθμιση."</string>
<string name="default_directory" msgid="93961630309570294">"Επαφές"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Άλλο"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Πρόσβαση σε όλα τα μηνύματα αυτόματου τηλεφωνητή"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Επιτρέπει στην εφαρμογή την αποθήκευση και ανάκτηση όλων των μηνυμάτων αυτόματου τηλεφωνητή, στα οποία έχει πρόσβαση αυτή η συσκευή."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Επιτρέπει στην εφαρμογή την αποθήκευση και ανάκτηση όλων των μηνυμάτων αυτόματου τηλεφωνητή, στα οποία μπορεί να έχει πρόσβαση αυτή η συσκευή."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Μήνυμα αυτόματου τηλεφωνητή από "</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index efa2d60..9cbc783 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Contacts Storage"</string>
<string name="provider_label" msgid="6012150850819899907">"Contacts"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Upgrading Contacts database."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Contact upgrade needs more memory"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Upgrading contact storage"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Select to complete the upgrade."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Upgrading contacts database."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Contact upgrade needs more memory."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Upgrading storage for contacts"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Touch to complete the upgrade."</string>
<string name="default_directory" msgid="93961630309570294">"Contacts"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Other"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Access all voicemails"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Allows the application to store and retrieve all voicemails that this device can access."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Allows the app to store and retrieve all voicemails that this device can access."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Voicemail from "</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index b09e262..a3cddf2 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Aplicaciones básicas de Android"</string>
<string name="app_label" msgid="3389954322874982620">"Almacenamiento para contactos"</string>
<string name="provider_label" msgid="6012150850819899907">"Contactos"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Actualizando la base de datos de los contactos"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"La actualización de los contactos necesita más memoria."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Actualizando almacenamiento de contactos"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Selecciona para completar la actualización."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Actualizando la base de datos de los contactos"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"La actualización de contactos necesita más memoria."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Actualizando el espacio de almacenamiento de los contactos"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Toca para completar la actualización."</string>
<string name="default_directory" msgid="93961630309570294">"Contactos"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Otro"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acceder a todos los mensajes de voz"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Permite que la aplicación almacene y recupere todos los mensajes de voz a los que este dispositivo puede acceder."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite que la aplicación almacene y recupere todos los mensajes de voz a los que este dispositivo puede acceder."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Mensaje de voz de "</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index b901512..d4f7edf 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Aplicaciones básicas de Android"</string>
<string name="app_label" msgid="3389954322874982620">"Información de los contactos"</string>
<string name="provider_label" msgid="6012150850819899907">"Contactos"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Actualizando base de datos de contactos"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"La actualización del contacto necesita más memoria."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Espacio de almacenamiento de actualización de contactos"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Selecciona esta opción para completar la actualización."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Actualizando la base de datos de contactos..."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"La actualización de contactos necesita más memoria."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Actualizando el almacenamiento para contactos..."</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Toca para completar la actualización."</string>
<string name="default_directory" msgid="93961630309570294">"Contactos"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Otro"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acceder a todos los mensajes de voz"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Permite que la aplicación almacene y recupere todos los mensajes de voz a los que puede acceder este dispositivo."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite que la aplicación almacene y recupere todos los mensajes de voz a los que puede acceder este dispositivo."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Mensaje de voz de "</string>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 5cac543..6fda0d5 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Androidi tuumrakendused"</string>
<string name="app_label" msgid="3389954322874982620">"Kontaktiruum"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontaktid"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Kontaktide andmebaasi uuendamine."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Kontakti uuendamiseks on vaja rohkem mälu"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Kontakti mäluruumi uuendamine"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Valige uuenduse lõpule viimiseks."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Kontaktide andmebaasi uuendamine."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kontaktisikute uuendamiseks on vaja rohkem mälu"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Kontaktide salvestusruumi uuendamine"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Puudutage uuendamise lõpuleviimiseks."</string>
<string name="default_directory" msgid="93961630309570294">"Kontaktid"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Muu"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Juurdepääs kõigile kõnepostisõnumitele"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Võimaldab rakendusel hoida ja vastu võtta kõik kõnepostisõnumid, millele see seade juurde pääseb."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Võimaldab rakendusel salvestada ja vastu võtta kõik kõnepostisõnumid, mille juurde seade pääseb."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Kõnepost kontaktilt "</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 687b0c4..bcdf710 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"حافظه مخاطبین"</string>
<string name="provider_label" msgid="6012150850819899907">"مخاطبین"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"ارتقاء پایگاه داده مخاطبین."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"برای ارتقاء مخاطب به حافظه بیشتری نیاز است"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"ارتقا حافظه مخاطب"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"برای کامل کردن ارتقا انتخاب کنید."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"ارتقا پایگاه داده مخاطبین."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"برای ارتقای مخاطبین به حافظه بیشتری نیاز است."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"ارتقا حافظه برای مخاطبین"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"برای تکمیل ارتقا لمس کنید."</string>
<string name="default_directory" msgid="93961630309570294">"مخاطبین"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"سایر موارد"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"دسترسی به تمام پستهای صوتی"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"به برنامه اجازه ذخیره و بازیابی تمام پستهای صوتی قابل دسترس برای این دستگاه را میدهد."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"به برنامه اجازه ذخیره و بازیابی تمام پستهای صوتی قابل دسترس برای این دستگاه را میدهد."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"پست صوتی از "</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index e39fc54..d9c341f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Androidin ydinsovellukset"</string>
<string name="app_label" msgid="3389954322874982620">"Yhteystietojen tallennus"</string>
<string name="provider_label" msgid="6012150850819899907">"Yhteystiedot"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Päivitetään yhteystietojen tietokantaa."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Yhteystietojen päivittämiseen tarvitaan lisää muistia"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Päivitetään yhteystietojen tallennustilaa"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Suorita päivitys loppuun valitsemalla."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Päivitetään yhteystietojen tietokantaa."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Yhteystietojen päivittämiseen tarvitaan enemmän muistia."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Päivitetään yhteystietojen tallennustilaa"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Suorita päivitys loppuun koskettamalla."</string>
<string name="default_directory" msgid="93961630309570294">"Yhteystiedot"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Muu"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Kaikkien vastaajaviestien käyttäminen"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Antaa sovelluksen tallentaa ja hakea kaikki vastaajaviestit, jotka ovat laitteen käytettävissä."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Antaa sovelluksen tallentaa ja hakea kaikki vastaajaviestit, jotka ovat laitteen käytettävissä."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Vastaajaviesti henkilöltä "</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 6d794f2..b25b763 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Applications de base Android"</string>
<string name="app_label" msgid="3389954322874982620">"Liste des contacts"</string>
<string name="provider_label" msgid="6012150850819899907">"Contacts"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Mise à jour de la base de données Contacts."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"La mise à niveau des contacts requiert plus de mémoire."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Mise à niveau du stockage des contacts"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Sélectionnez pour effectuer la mise à niveau."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Mise à jour de la base de données des contacts en cours…"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"La mise à jour des contacts requiert plus de mémoire."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Mise à jour du stockage des contacts"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Appuyez pour terminer la mise à jour."</string>
<string name="default_directory" msgid="93961630309570294">"Contacts"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Autre"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accéder à tous les messages vocaux"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Permet à l\'application de stocker et de récupérer tous les messages vocaux auxquels ce service peut accéder."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permet à l\'application de stocker et de récupérer tous les messages vocaux auxquels cet appareil peut accéder."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Message vocal de "</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 153767b..f2d706a 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"संपर्क संग्रहण"</string>
<string name="provider_label" msgid="6012150850819899907">"संपर्क"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"संपर्क डेटाबेस अपग्रेड हो रहा है."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"संपर्क अपग्रेड के लिए अधिक स्मृति की आवश्यकता होती है"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"संपर्क संग्रहण अपग्रेड किया जा रहा है"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"अपग्रेड पूर्ण करने के लिए चयन करें."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"संपर्क डेटाबेस अपग्रेड हो रहा है."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"संपर्क अपग्रेड के लिए अधिक स्मृति की आवश्यकता होती है."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"संपर्कों के लिए संग्रहण अपग्रेड करना"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"अपग्रेड पूर्ण करने के लिए स्पर्श करें."</string>
<string name="default_directory" msgid="93961630309570294">"संपर्क"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"अन्य"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"सभी ध्वनिमेल पर पहुंचें"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"एप्लिकेशन को ऐसे सभी ध्वनिमेल संग्रहीत और पुनर्प्राप्त करने देता है जिन पर यह उपकरण पहुंच सकता है."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"एप्लिकेशन को ऐसे सभी ध्वनिमेल संग्रहीत और पुनर्प्राप्त करने देता है जिन पर यह उपकरण पहुंच सकता है."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"इनका ध्वनिमेल: "</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index f00b088..2b53781 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Matične aplikacije za Android"</string>
<string name="app_label" msgid="3389954322874982620">"Prostor za pohranu kontakata"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakti"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Nadogradnja baze podataka kontakata."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Za nadogradnju kontakta potrebno je više memorije"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Nadogradnja pohrane kontakata"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Odaberite da biste dovršili nadogradnju."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Nadogradnja baze podataka kontakata."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Za nadogradnju kontakata potrebno je više memorije."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Nadogradnja pohrane za kontakte"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Dodirnite da biste dovršili nadogradnju."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakti"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Drugo"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Pristup svoj govornoj pošti"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Omogućuje aplikaciji pohranu i dohvat sve govorne pošte kojoj uređaj može pristupiti."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Aplikaciji omogućuje pohranjivanje i dohvaćanje svih poruka govorne pošte kojoj ovaj uređaj može pristupati."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Govorna pošta od "</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 934bd73..7f477e5 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Alap Android-alkalmazások"</string>
<string name="app_label" msgid="3389954322874982620">"Névjegytár"</string>
<string name="provider_label" msgid="6012150850819899907">"Címtár"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Névjegyek adatbázisának frissítése"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"A névjegy frissítéséhez több memóriára van szükség"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"A névjegy-tárhely frissítése"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Válassza ezt a verziófrissítéshez."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Névjegyek adatbázisának frissítése."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"A névjegyek frissítéséhez több memóriára van szükség."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Névjegyek tárolójának frissítése"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Érintse meg a frissítés befejezéséhez."</string>
<string name="default_directory" msgid="93961630309570294">"Címtár"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Egyéb"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Hozzáférés az összes hangüzenethez"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Lehetővé teszi, hogy az alkalmazás tárolja és lekérje azokat a hangüzeneteket, amelyekhez ez a készülék hozzáférhet."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Lehetővé teszi, hogy az alkalmazás tárolja és lekérje azokat a hangüzeneteket, amelyekhez ez a készülék hozzáférhet."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Hangüzenet tőle: "</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 34600a8..f958b12 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Apl Inti Android"</string>
<string name="app_label" msgid="3389954322874982620">"Penyimpanan Kenalan"</string>
<string name="provider_label" msgid="6012150850819899907">"Kenalan"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Meningkatkan versi basis data Kenalan."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Peningkatan versi kenalan memerlukan lebih banyak memori"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Meningkatkan penyimpanan kenalan"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Pilih untuk menyelesaikan peningkatan versi."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Meningkatkan versi basis data kenalan."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Peningkatan versi kenalan memerlukan lebih banyak memori."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Meningkatkan versi penyimpanan untuk kenalan"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Sentuh untuk menyelesaikan peningkatan versi."</string>
<string name="default_directory" msgid="93961630309570294">"Kenalan"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Lainnya"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Akses semua pesan suara"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Mengizinkan aplikasi menyimpan dan mengambil semua pesan suara yang dapat diakses oleh perangkat ini."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Mengizinkan apl menyimpan dan mengambil semua pesan suara yang dapat diakses oleh perangkat ini."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Kotak pesan dari "</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 719dc98..dfea4d4 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Applicazioni di base Android"</string>
<string name="app_label" msgid="3389954322874982620">"Archiviazione contatti"</string>
<string name="provider_label" msgid="6012150850819899907">"Contatti"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Upgrade del database di Contatti."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"L\'aggiornamento dei contatti richiede più memoria"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Aggiornamento dell\'archivio contatti"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Seleziona per completare l\'aggiornamento."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Upgrade del database dei contatti."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"L\'upgrade dei contatti richiede più memoria."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Upgrade dell\'archivio dei contatti"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Tocca per completare l\'upgrade."</string>
<string name="default_directory" msgid="93961630309570294">"Contatti"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Altro"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accesso a tutti i messaggi vocali"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Consente all\'applicazione di archiviare e recuperare tutti i messaggi vocali a cui questo dispositivo può accedere."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Consente all\'applicazione di archiviare recuperare tutti i messaggi vocali a cui questo dispositivo può accedere."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Messaggio vocale da "</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 0840371..c87a2f9 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"אחסון אנשי קשר"</string>
<string name="provider_label" msgid="6012150850819899907">"אנשי קשר"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"משדרג את מסד הנתונים של אנשי הקשר."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"שדרוג אנשי קשר דורש זיכרון נוסף"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"משדרג אחסון של אנשי קשר"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"בחר כדי להשלים את השדרוג."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"משדרג את מסד הנתונים של אנשי הקשר."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"שדרוג אנשי הקשר מחייב זיכרון נוסף."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"משדרג את האחסון של אנשי קשר"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"גע כדי לבצע את השדרוג."</string>
<string name="default_directory" msgid="93961630309570294">"אנשי קשר"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"אחר"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"גישה לכל הודעות הדואר הקולי"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"מאפשר ליישום לאחסן ולאחזר את כל הודעות הדואר הקולי שמכשיר זה יכול לגשת אליהן."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"מאפשר ליישום לאחסן ולאחזר את כל הודעות הדואר הקולי שלמכשיר זה יש גישה אליהן."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"הודעה קולית מאת "</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index ec64581..74044d8 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"アドレス帳"</string>
<string name="provider_label" msgid="6012150850819899907">"連絡先"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"連絡先データベースをアップグレードしています。"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"連絡先のアップグレードに必要なメモリが不足しています"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"連絡先ストレージのアップグレード"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"アップグレードを完了するには選択してください。"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"連絡先データベースをアップグレードしています。"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"連絡先のアップグレードに必要なメモリが不足しています。"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"連絡先用ストレージのアップグレード"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"アップグレードを完了するにはタップしてください。"</string>
<string name="default_directory" msgid="93961630309570294">"連絡先"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"その他"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"すべてのボイスメールにアクセス"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"この端末でアクセス可能なすべてのボイスメールを保存、取得することをアプリケーションに許可します。"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"この端末でアクセス可能なすべてのボイスメールを保存、取得することをアプリに許可します。"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"受信ボイスメール: "</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index a1a1b91..041cc0b 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core 앱"</string>
<string name="app_label" msgid="3389954322874982620">"주소록 저장소"</string>
<string name="provider_label" msgid="6012150850819899907">"주소록"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"주소록 데이터베이스 업그레이드 중"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"연락처를 업그레이드하려면 메모리가 더 필요합니다."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"연락처 저장소 업그레이드"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"업그레이드를 완료하려면 선택하세요."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"주소록 데이터베이스 업그레이드 중"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"주소록을 업그레이드하려면 메모리가 더 필요합니다."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"주소록을 위한 저장소 업그레이드 중"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"업그레이드를 완료하려면 터치하세요."</string>
<string name="default_directory" msgid="93961630309570294">"주소록"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"기타"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"모든 음성사서함에 액세스"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"애플리케이션을 사용하면 이 기기에서 액세스할 수 있는 모든 음성사서함을 저장하고 검색할 수 있습니다."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"앱이 이 기기에서 액세스할 수 있는 모든 음성사서함을 저장하고 검색하도록 허용합니다."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"음성사서함 발신자 "</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index b0f2cf2..b873d1f 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Pagrindinės „Android“ programos"</string>
<string name="app_label" msgid="3389954322874982620">"Adresatų saugykla"</string>
<string name="provider_label" msgid="6012150850819899907">"Adresinė"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Naujovinama Kontaktų duomenų bazė."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Norint naujovinti adresatą, reikia daugiau atminties"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Naujovinama adresatų saugykla"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Pasirinkite ir užbaikite naujovinimą."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Naujovinami kontaktų duomenys."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Norint naujovinti kontaktus, reikia daugiau atminties."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Naujovinama kontaktų atmintinė"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Palieskite, kad baigtumėte naujovinti."</string>
<string name="default_directory" msgid="93961630309570294">"Kontaktai"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Kita"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Pasiekti visus balso pašto pranešimus"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Leidžiama programai saugoti ir nuskaityti visus balso pašto pranešimus, kuriuos gali pasiekti šis įrenginys."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Leidžiama programai saugoti ir nuskaityti visus balso pašto pranešimus, kuriuos gali pasiekti šis įrenginys."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Balso pašto pranešimas nuo "</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 5c11d72..fa1e89e 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Kontaktpersonu krātuve"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontaktpersonas"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Notiek kontaktpersonu datu bāzes jaunināšana."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Lai jauninātu kontaktpersonas, nepieciešams vairāk atmiņas."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Kontaktpersonu krātuves jaunināšana"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Atlasiet, lai pabeigtu jaunināšanu."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Notiek kontaktpersonu datu bāzes jaunināšana."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Lai jauninātu kontaktpersonas, nepieciešams vairāk vietas atmiņā."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Notiek kontaktpersonu krātuves jaunināšana"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Pieskarieties, lai pabeigtu jaunināšanu."</string>
<string name="default_directory" msgid="93961630309570294">"Kontaktpersonas"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Cits"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Piekļuve visiem balss pasta ziņojumiem"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Ļauj lietojumprogrammai glabāt un izgūt visus balss pasta ziņojumus, kuriem var piekļūt šajā ierīcē."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Ļauj lietotnei glabāt un izgūt visus balss pasta ziņojumus, kuriem var piekļūt šajā ierīcē."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Balss pasta ziņojums no "</string>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index d00395e..653a8d9 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Apl Teras Android"</string>
<string name="app_label" msgid="3389954322874982620">"Storan Kenalan"</string>
<string name="provider_label" msgid="6012150850819899907">"Kenalan"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Menaiktaraf pangkalan data Kenalan."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Kemas kini kenalan memerlukan lagi memori"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Meningkatkan storan kenalan"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Pilih untuk menyelesaikan pengemaskinian."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Menaik taraf pangkalan data kenalan."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Peningkatan kenalan memerlukan lebih banyak memori."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Meningkatkan storan untuk kenalan"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Sentuh untuk menyelesaikan peningkatan."</string>
<string name="default_directory" msgid="93961630309570294">"Kenalan"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Lain-lain"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Akses semua mel suara"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Membenarkan aplikasi menyimpan dan mengambil semula semua mel suara yang boleh diakses peranti ini."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Membenarkan aplikasi menyimpan dan mengambil semula semua mel suara yang boleh diakses peranti ini."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Mel suara daripada "</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 72bb3e9..5775854 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Kjerneapper for Android"</string>
<string name="app_label" msgid="3389954322874982620">"Kontaktlager"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakter"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Oppgraderer kontaktdatabase."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Det er ikke nok minne for å oppgradere kontaktene"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Oppgraderer lagrede kontakter"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Velg for å fullføre oppgraderingen."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Oppgraderer kontaktdatabasen."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Du har ikke nok minne til å oppgradere kontaktene."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Oppgraderer lagring for kontakter"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Trykk for å fullføre oppgraderingen."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakter"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Andre"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Tilgang til alle talemeldinger"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Gjør at applikasjonen kan lagre og hente alle talepostmeldinger som denne enheten har tilgang til."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Lar appen lagre og hente alle talepostmeldinger som denne enheten har tilgang til."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Talemelding fra "</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 82e7e70..1a09288 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Kernapps van Android"</string>
<string name="app_label" msgid="3389954322874982620">"Opslag contacten"</string>
<string name="provider_label" msgid="6012150850819899907">"Contacten"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Contactendatabase bijwerken."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Voor het bijwerken van contacten is meer geheugen nodig"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Contactopslag bijwerken"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Selecteer om de upgrade te voltooien."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Contactendatabase bijwerken."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Voor het bijwerken van contacten is meer geheugen nodig."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Opslag voor contacten bijwerken"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Raak aan om de upgrade te voltooien."</string>
<string name="default_directory" msgid="93961630309570294">"Contacten"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Overig"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Toegang tot alle voicemails"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Hiermee kan de app alle voicemails opslaan en ophalen waartoe dit apparaat toegang heeft."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Toestaan dat de app alle voicemails opslaat en ophaalt waartoe dit apparaat toegang heeft."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Voicemail van "</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index dfcf8fd..b598221 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Aplikacje główne Androida"</string>
<string name="app_label" msgid="3389954322874982620">"Spis kontaktów"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakty"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Uaktualnianie bazy danych Kontaktów."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Uaktualnienie kontaktów wymaga więcej pamięci"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Uaktualnianie magazynu kontaktów"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Wybierz, aby dokończyć uaktualnianie."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Uaktualnianie bazy danych kontaktów"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Uaktualnienie kontaktów wymaga więcej pamięci."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Pamięć na uaktualnienie kontaktów"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Dotknij, aby dokończyć uaktualnianie."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakty"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Inne"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Dostęp do wszystkich wiadomości głosowych"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Zezwala aplikacji na przechowywanie i pobieranie wszystkich wiadomości głosowych, do których ma dostęp to urządzenie."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Zezwala aplikacji na przechowywanie i pobieranie wszystkich wiadomości głosowych, do których ma dostęp to urządzenie."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Poczta głosowa od "</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index e84a22e..acdb0d2 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Aplicações Principais do Android"</string>
<string name="app_label" msgid="3389954322874982620">"Armazenamento de contactos"</string>
<string name="provider_label" msgid="6012150850819899907">"Contactos"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"A atualizar a base de dados de Contactos."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"A actualização de contactos necessita de mais memória"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"A actualizar armazenamento de contactos"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Seleccione para concluir a actualização."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"A atualizar a base de dados de contactos."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"A atualização de contactos necessita de mais memória."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"A atualizar armazenamento de contactos"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Toque para concluir a atualização."</string>
<string name="default_directory" msgid="93961630309570294">"Contactos"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Outro"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acesso a todas as mensagens de correio de voz"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Permite à aplicação guardar e recuperar todas as mensagens de correio de voz a que este aparelho pode aceder."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite à aplicação guardar e recuperar todas as mensagens de correio de voz a que este aparelho pode aceder."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Correio de voz de "</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index ec9efae..5d471a1 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Principais aplicativos do Android"</string>
<string name="app_label" msgid="3389954322874982620">"Armazenamento de contatos"</string>
<string name="provider_label" msgid="6012150850819899907">"Contatos"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Atualizando o banco de dados de contatos."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"A atualização de contatos precisa de mais memória"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Armazenamento de atualização de contatos"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Selecione para concluir a atualização."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Atualizando o banco de dados de contatos."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"A atualização de contatos precisa de mais memória."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Atualização do armazenamento para contatos"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Toque para concluir a atualização."</string>
<string name="default_directory" msgid="93961630309570294">"Contatos"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Outros"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acessar todas as mensagens de voz"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Permite que o aplicativo armazene e recupere todas as mensagens de voz que esse dispositivo acessa."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite que o aplicativo armazene e recupere todas as mensagens de voz que esse dispositivo acessa."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Correio de voz de "</string>
</resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index e975b42..b0e2898 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -20,16 +20,19 @@
<skip />
<string name="app_label" msgid="3389954322874982620">"Glista da contacts"</string>
<string name="provider_label" msgid="6012150850819899907">"Contacts"</string>
- <!-- no translation found for upgrade_msg (6174884195179549239) -->
+ <!-- no translation found for upgrade_msg (8640807392794309950) -->
<skip />
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Ina actualisaziun da contacts basegna dapli memoria"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Actualisar la memoria da contacts"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Tscherner per cumplettar l\'actualisaziun"</string>
+ <!-- no translation found for upgrade_out_of_memory_notification_ticker (7638747231223520477) -->
+ <skip />
+ <!-- no translation found for upgrade_out_of_memory_notification_title (8888171924684998531) -->
+ <skip />
+ <!-- no translation found for upgrade_out_of_memory_notification_text (8438179450336437626) -->
+ <skip />
<string name="default_directory" msgid="93961630309570294">"Contacts"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Auter"</string>
<!-- no translation found for read_write_all_voicemail_label (4557216100818257560) -->
<skip />
- <!-- no translation found for read_write_all_voicemail_description (2249895806470926882) -->
+ <!-- no translation found for read_write_all_voicemail_description (8029809937805761356) -->
<skip />
<!-- no translation found for voicemail_from_column (435732568832121444) -->
<skip />
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 7ceb2ff..110f166 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Stocarea datelor din Agendă"</string>
<string name="provider_label" msgid="6012150850819899907">"Agendă"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Se actualizează baza de date a Agendei."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Actualizarea datelor despre persoanele din agendă necesită mai multă memorie"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Se trece la o versiune superioară a memoriei pentru persoanele din agendă"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Selectaţi pentru a finaliza trecerea la o versiune superioară."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Se actualizează baza de date a Agendei."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Actualizarea agendei necesită mai multă memorie."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Măriţi spaţiul de stocare pentru Agendă"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Atingeţi pentru a finaliza actualizarea."</string>
<string name="default_directory" msgid="93961630309570294">"Agendă"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Altul"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accesaţi toate mesajele vocale"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Permite aplicaţiei să stocheze şi să preia toate mesajele vocale pe care acest dispozitiv le poate accesa."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite aplicaţiei să stocheze şi să preia toate mesajele vocale pe care acest dispozitiv le poate accesa."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Mesaj vocal de la "</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 60ae7b9..45b28a0 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Основные приложения Android"</string>
<string name="app_label" msgid="3389954322874982620">"Хранилище контактов"</string>
<string name="provider_label" msgid="6012150850819899907">"Контакты"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Обновление базы данных контактов..."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Необходимо больше памяти для обновления контактов"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Обновление памяти контактов"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Нажмите, чтобы завершить обновление."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Обновление базы данных контактов..."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Для обновления контактов нужно больше памяти."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Недостаточно места для обновления контактов"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Нажмите, чтобы завершить обновление."</string>
<string name="default_directory" msgid="93961630309570294">"Контакты"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Другое"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Доступ к голосовым сообщениям"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Разрешить приложению сохранять и извлекать все голосовые сообщения, к которым есть доступ на этом устройстве."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Приложение сможет сохранять и загружать все голосовые сообщения, к которым есть доступ на этом устройстве."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Голосовое сообщение от абонента "</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index ec716f6..e8d82c6 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Úložisko kontaktov"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakty"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Prebieha inovácia databázy kontaktov."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Inovácia kontaktu vyžaduje viac pamäte"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Prebieha inovácia ukladacieho priestoru kontaktov"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Výberom tejto možnosti dokončíte inováciu"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Prebieha inovácia databázy kontaktov."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Inovácia kontaktov vyžaduje viac pamäte."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Prebieha inovácia úložiska pre kontakty"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Dotykom na túto možnosť dokončíte inováciu."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakty"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Iné"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Prístup ku všetkým hlasovým správam"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Umožňuje aplikácii uchovávať a načítavať všetky hlasové správy, ku ktorým má zariadenie prístup."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Umožňuje aplikácii uchovávať a načítavať všetky hlasové správy, ku ktorým má zariadenie prístup."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Hlasová správa od "</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index ec1fe50..af43531 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Osrednji programi sistema Android"</string>
<string name="app_label" msgid="3389954322874982620">"Shramba za stike"</string>
<string name="provider_label" msgid="6012150850819899907">"Stiki"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Nadgradnja zbirke podatkov stikov."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Za nadgradnjo stika je potrebnega več pomnilnika"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Nadgradnja shrambe za stike"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Izberite, da dokončate nadgradnjo."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Nadgradnja zbirke podatkov stikov."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Za nadgradnjo stikov je potrebno več pomnilnika"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Nadgradnja shrambe za stike"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Dotaknite se, da končate nadgradnjo."</string>
<string name="default_directory" msgid="93961630309570294">"Stiki"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Drugo"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Dostop do glasovne pošte"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Programu dovoli shranjevanje in prenos sporočil glasovne pošte, do katerih lahko dostopa ta naprava."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Programu dovoli shranjevanje in prenos sporočil v odzivniku, do katerih lahko dostopa ta naprava."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Govorna pošta s številke "</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index fca0af9..c8888db 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core апликације"</string>
<string name="app_label" msgid="3389954322874982620">"Складиште контаката"</string>
<string name="provider_label" msgid="6012150850819899907">"Контакти"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Надограђивање базе података о контактима."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"За ажурирање контаката потребно је више меморије"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Ажурирање складишта контаката"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Изаберите опцију за довршетак надоградње."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Надограђивање базе података о контактима."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"За ажурирање контаката потребно је више меморије."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Надограђивање меморије за контакте"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Додирните да бисте довршили надоградњу."</string>
<string name="default_directory" msgid="93961630309570294">"Контакти"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Други"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Приступ свим порукама говорне поште"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Омогућава апликацији да складишти и преузима све поруке говорне поште којима овај уређај може да приступи."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Дозвољава апликацији да складишти и преузима све поруке говорне поште којима овај уређај може да приступи."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Говорна пошта од "</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 897fbff..06ecf9a 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Kontakter"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontakter"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Uppgraderar kontaktdatabas."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Kontaktuppgradering kräver mer minne"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Uppgradera kontaktminne"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Välj för att slutföra uppgraderingen."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Kontaktdatabasen uppgraderas."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kontaktuppgradering kräver mer minne."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Lagringsutrymmet för kontakter uppgraderas"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Tryck om du vill slutföra uppgraderingen."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakter"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Övrigt"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Åtkomst till alla röstmeddelanden"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Tillåter att programmet sparar och hämtar alla röstmeddelanden som enheten har åtkomst till."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Tillåter att appen sparar och hämtar alla röstmeddelanden som enheten har åtkomst till."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Röstmeddelande från "</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 1c8ebd1..b83c636 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Programu Msingi za Android"</string>
<string name="app_label" msgid="3389954322874982620">"Hifadhi ya Anwani"</string>
<string name="provider_label" msgid="6012150850819899907">"Anwani"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Inapandisha daraja la hifadhidata ya Anwani."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Kupandisha gredi kwa anwani kunahitaji kumbukumbu zaidi"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Upandishaji gredi wa hifadhi ya anwani"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Chagua ili kukamilisha kupandisha daraja."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Inapandisha gredi hifadhidata ya Anwani."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kupandishwa gredi kwa anwani kunahitaji kumbukumbu zaidi."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Inapandisha gredi hifadhi ya anwani"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Gusa ili kukamilisha kupandisha gredi."</string>
<string name="default_directory" msgid="93961630309570294">"Anwani"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Nyingineyo"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Fikia barua zote za sauti"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Huruhusu programu kuhifadhi na kuepua barua zote za sauti ambazo kifaa hiki kinaweza kufikia."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Inaruhusu programu kuhifadhi na kutoa jumbe zote za sauti ambazo kifaa hiki kinaweza kufikia."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Barua ya sauti kutoka "</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index d9b996c..0cb6adc 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"แอปพลิเคชันหลักของ Android"</string>
<string name="app_label" msgid="3389954322874982620">"ที่จัดเก็บรายชื่อ"</string>
<string name="provider_label" msgid="6012150850819899907">"สมุดโทรศัพท์"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"กำลังอัปเกรดฐานข้อมูลรายชื่อติดต่อ"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"ต้องใช้หน่วยความจำเพิ่มเพื่ออัปเกรดสมุดโทรศัพท์"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"กำลังอัปเกรดที่เก็บรายชื่อในสมุดโทรศัพท์"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"เลือกเพื่อทำการอัปเกรดให้สมบูรณ์"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"กำลังอัปเกรดฐานข้อมูลสมุดโทรศัพท์"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"ต้องใช้หน่วยความจำเพิ่มเพื่ออัปเกรดสมุดโทรศัพท์"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"กำลังอัปเกรดที่จัดเก็บข้อมูลสำหรับสมุดโทรศัพท์"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"แตะเพื่อทำการอัปเกรดให้สมบูรณ์"</string>
<string name="default_directory" msgid="93961630309570294">"สมุดโทรศัพท์"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"อื่นๆ"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"เข้าถึงข้อความเสียงทั้งหมด"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"อนุญาตให้แอปพลิเคชันจัดเก็บและเรียกข้อความเสียงทั้งหมดที่อุปกรณ์นี้สามารถเข้าถึงได้"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"อนุญาตให้แอปพลิเคชันจัดเก็บและเรียกข้อความเสียงทั้งหมดที่อุปกรณ์นี้สามารถเข้าถึงได้"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"ข้อความเสียงจาก "</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index eb88576..44f6b73 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Core Apps ng Android"</string>
<string name="app_label" msgid="3389954322874982620">"Imbakan ng Mga Contact"</string>
<string name="provider_label" msgid="6012150850819899907">"Mga Contact"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Ina-upgrade ang database ng Mga Contact."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Nangangailangan ang pag-upgrade sa contact nang higit pang memory"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Pag-upgrade ng imbakan ng contact"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Piliing kumpletuhin ang pag-upgrade."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Ina-upgrade ang database ng mga contact."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Nangangailangan ng higit pang memory ang pag-upgrade sa mga contact."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Ina-upgrade ang storage para sa mga contact"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Pindutin upang kumpletuhin ang pag-upgrade."</string>
<string name="default_directory" msgid="93961630309570294">"Mga Contact"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Iba pa"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"I-access ang lahat ng voicemail"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Binibigyang-daan ang application upang maimbak at mabawi ang lahat ng voicemail na naa-access ng device na ito."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Binibigyang-daan ang app upang iimbak at kuning muli ang lahat ng voicemail na naa-access ng device na ito."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Voicemail mula sa/kay "</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 3cc061a..672288a 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Çekirdek Uygulamaları"</string>
<string name="app_label" msgid="3389954322874982620">"Kişi Deposu"</string>
<string name="provider_label" msgid="6012150850819899907">"Kişiler"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Kişiler veritabanı yeni sürüme geçiriliyor."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Kişiyi yeni sürüme geçirmek için daha fazla bellek gerekiyor"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Kişi deposu yükseltiliyor"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Yeni sürüme geçmeyi tamamlamak için seçin."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Kişiler veritabanı yeni sürüme geçiriliyor."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kişileri yeni sürüme geçirmek için daha fazla bellek gerekiyor."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Kişiler için depolama alanı yeni sürüme geçiriliyor"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Yeni sürüme geçmeyi tamamlamak için dokunun."</string>
<string name="default_directory" msgid="93961630309570294">"Kişiler"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Diğer"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Tüm sesli mesajlara erişim"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Uygulamaya, bu cihazın erişebileceği tüm sesli mesajları depolama ve alma izni verir."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Uygulamaya, bu cihazın erişebileceği tüm sesli mesajları depolama ve alma izni verir."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Sesli mesaj gönderen: "</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 28e6148..1829219 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Пам\'ять контактів"</string>
<string name="provider_label" msgid="6012150850819899907">"Контакти"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Оновлення бази даних контактів."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Оновл. контакту потреб. більше пам\'яті"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Оновлення пам\'яті контактів"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Виберіть для заверш. оновлення."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Оновлення бази даних контактів."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Оновлення контактів потребує більше пам’яті."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Оновлення пам’яті для контактів"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Торкніться, щоб завершити оновлення."</string>
<string name="default_directory" msgid="93961630309570294">"Контакти"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Інші"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Доступ до всієї голосової пошти"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Дозволяє програмі зберігати й отримувати всю голосову пошту, доступ до якої має цей пристрій."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Дозволяє програмі зберігати й отримувати всю голосову пошту, доступ до якої має цей пристрій."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Голосова пошта від "</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index d793fa9..431a403 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Ứng dụng lõi Android"</string>
<string name="app_label" msgid="3389954322874982620">"Bộ nhớ Danh bạ"</string>
<string name="provider_label" msgid="6012150850819899907">"Danh bạ"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Đang nâng cấp cơ sở dữ liệu Danh bạ."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Nâng cấp liên hệ cần thêm bộ nhớ"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Nâng cấp bộ nhớ liên hệ"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Chọn để hoàn tất nâng cấp."</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Đang nâng cấp cơ sở dữ liệu danh bạ."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Nâng cấp danh bạ cần thêm bộ nhớ."</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Đang nâng cấp dung lượng cho danh bạ"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Chạm để hoàn tất nâng cấp."</string>
<string name="default_directory" msgid="93961630309570294">"Danh bạ"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Khác"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Truy cập tất cả các thư thoại"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Cho phép ứng dụng lưu trữ và truy xuất tất cả thư thoại mà thiết bị này có thể truy cập."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Cho phép ứng dụng lưu trữ và truy xuất tất cả thư thoại mà thiết bị này có thể truy cập."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Thư thoại từ "</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 554e401..5dd88e4 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android 核心应用程序"</string>
<string name="app_label" msgid="3389954322874982620">"联系人存储"</string>
<string name="provider_label" msgid="6012150850819899907">"联系人"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"正在升级联系人数据库。"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"联系人升级需要更多的存储空间"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"升级联系人时存储空间不足"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"请选择以完成升级。"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"正在升级联系人数据库。"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"联系人升级需要更多的存储空间。"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"正在升级存储器以容纳更多联系人"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"请触摸以完成升级。"</string>
<string name="default_directory" msgid="93961630309570294">"联系人"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"其他"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"访问所有语音邮件"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"允许应用程序存储和检索此设备可访问的所有语音邮件。"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"允许应用程序存储和检索此设备可访问的所有语音邮件。"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"语音邮件发件人 "</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 2d8761d..ba929c3 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android 核心應用程式"</string>
<string name="app_label" msgid="3389954322874982620">"聯絡人儲存空間"</string>
<string name="provider_label" msgid="6012150850819899907">"聯絡人"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"正在升級聯絡人資料庫。"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"需要更多記憶體才能將聯絡人升級"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"升級聯絡人儲存空間"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"選取以完成升級。"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"正在升級聯絡人資料庫。"</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"需要更多記憶體才能將聯絡人升級。"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"正在升級聯絡人儲存空間"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"輕觸即可完成升級。"</string>
<string name="default_directory" msgid="93961630309570294">"聯絡人"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"其他"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"存取所有語音留言"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"允許應用程式儲存和擷取這個裝置可存取的所有語音留言。"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"允許應用程式儲存及擷取這個裝置可存取的所有語音留言。"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"語音郵件寄件者: "</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index b170638..76de636 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -19,13 +19,13 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Izinhlelo Zokusebenza ze-Android Core"</string>
<string name="app_label" msgid="3389954322874982620">"Isitoreji Sothintana Nabo"</string>
<string name="provider_label" msgid="6012150850819899907">"Othintana nabo"</string>
- <string name="upgrade_msg" msgid="6174884195179549239">"Ukufaka ezakamuva kwimininingo egciniwe yothintana naye."</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="4089605622758004662">"Ukuthuthukisa othintana naye kudinga enye imemori"</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="7849508493764133004">"Ithuthukisa isitoreji sothintana naye"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="3967762223137708403">"Khetha ukuqedela ukuthuthukisa"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Ukufaka ezakamuva kwimininingo egciniwe yothintana naye."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Ukuthuthukisa othintana naye kudinga enye imemori"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Ukuthuthukiswa kwesilondolozi soxhumana nabo"</string>
+ <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Thinta ukuqedela ukuthuthukisa."</string>
<string name="default_directory" msgid="93961630309570294">"Othintana nabo"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Okunye"</string>
<string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Finyelela kuwo wonke amavoyisimeyili"</string>
- <string name="read_write_all_voicemail_description" msgid="2249895806470926882">"Ivumela uhlelo olusebenzayo lulonde futhi lulande wonke ama-imeyli ezwi ledivayisi engakwazi ukuwafinyelela."</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Ivumela uhlelo olusebenzayo lulonde futhi lulande wonke ama-imeyli ezwi ledivayisi engakwazi ukuwafinyelela."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Imeyili yezwi kusuka "</string>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bc14294..aaa7f44 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -27,16 +27,16 @@
<string name="provider_label">Contacts</string>
<!-- [CHAR LIMIT=NONE] Boot message while upgrading contacts. -->
- <string name="upgrade_msg">Upgrading Contacts database.</string>
+ <string name="upgrade_msg">Upgrading contacts database.</string>
<!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
- <string name="upgrade_out_of_memory_notification_ticker">Contact upgrade needs more memory</string>
+ <string name="upgrade_out_of_memory_notification_ticker">Contacts upgrade needs more memory.</string>
<!-- Title for the notification shown when updating contacts fails because of memory shortage -->
- <string name="upgrade_out_of_memory_notification_title">Upgrading contact storage</string>
+ <string name="upgrade_out_of_memory_notification_title">Upgrading storage for contacts</string>
<!-- Text for the notification shown when updating contacts fails because of memory shortage -->
- <string name="upgrade_out_of_memory_notification_text">Select to complete the upgrade.</string>
+ <string name="upgrade_out_of_memory_notification_text">Touch to complete the upgrade.</string>
<!-- The name of the default contact directory -->
<string name="default_directory">Contacts</string>
@@ -49,7 +49,7 @@
<string name="read_write_all_voicemail_label">Access all voicemails</string>
<!-- Description of an application permission, listed so the user can choose whether
they want to allow the application to do this. [CHAR LIMIT=NONE] -->
- <string name="read_write_all_voicemail_description">Allows the application to store and retrieve
+ <string name="read_write_all_voicemail_description">Allows the app to store and retrieve
all voicemails that this device can access.</string>
<!-- The prefix string before the number used for the display name for the voicemail table.
diff --git a/src/com/android/providers/contacts/AccountWithDataSet.java b/src/com/android/providers/contacts/AccountWithDataSet.java
index d667432..3fea8a6 100644
--- a/src/com/android/providers/contacts/AccountWithDataSet.java
+++ b/src/com/android/providers/contacts/AccountWithDataSet.java
@@ -18,18 +18,35 @@
import com.android.internal.util.Objects;
+import android.accounts.Account;
+import android.text.TextUtils;
+
/**
* Account information that includes the data set, if any.
*/
public class AccountWithDataSet {
+ public static final AccountWithDataSet LOCAL = new AccountWithDataSet(null, null, null);
+
private final String mAccountName;
private final String mAccountType;
private final String mDataSet;
public AccountWithDataSet(String accountName, String accountType, String dataSet) {
- mAccountName = accountName;
- mAccountType = accountType;
- mDataSet = dataSet;
+ mAccountName = emptyToNull(accountName);
+ mAccountType = emptyToNull(accountType);
+ mDataSet = emptyToNull(dataSet);
+ }
+
+ private static final String emptyToNull(String text) {
+ return TextUtils.isEmpty(text) ? null : text;
+ }
+
+ public static AccountWithDataSet get(String accountName, String accountType, String dataSet) {
+ return new AccountWithDataSet(accountName, accountType, dataSet);
+ }
+
+ public static AccountWithDataSet get(Account account, String dataSet) {
+ return new AccountWithDataSet(account.name, account.type, null);
}
public String getAccountName() {
@@ -44,6 +61,10 @@
return mDataSet;
}
+ public boolean isLocalAccount() {
+ return (mAccountName == null) && (mAccountType == null);
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof AccountWithDataSet) {
@@ -68,4 +89,20 @@
return "AccountWithDataSet {name=" + mAccountName + ", type=" + mAccountType + ", dataSet="
+ mDataSet + "}";
}
+
+ /**
+ * @return {@code true} if the owning {@link Account} is in the passed array.
+ */
+ public boolean inSystemAccounts(Account[] systemAccounts) {
+ // Note we don't want to create a new Account object from this instance, as it may contain
+ // null account name/type, which Account wouldn't accept. So we need to compare field by
+ // field.
+ for (Account systemAccount : systemAccounts) {
+ if (Objects.equal(systemAccount.name, getAccountName())
+ && Objects.equal(systemAccount.type, getAccountType())) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 3c39625..89ae591 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -46,7 +46,7 @@
public class CallLogProvider extends ContentProvider {
/** Selection clause to use to exclude voicemail records. */
private static final String EXCLUDE_VOICEMAIL_SELECTION = getInequalityClause(
- Calls.TYPE, Integer.toString(Calls.VOICEMAIL_TYPE));
+ Calls.TYPE, Calls.VOICEMAIL_TYPE);
private static final int CALLS = 1;
@@ -320,10 +320,9 @@
* matches CALLS_ID. For other uri types the behaviour is undefined.
* @throws IllegalArgumentException if the id included in the Uri is not a valid long value.
*/
- private String parseCallIdFromUri(Uri uri) {
+ private long parseCallIdFromUri(Uri uri) {
try {
- Long id = Long.valueOf(uri.getPathSegments().get(1));
- return id.toString();
+ return Long.parseLong(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid call id in uri: " + uri, e);
}
diff --git a/src/com/android/providers/contacts/ContactAggregator.java b/src/com/android/providers/contacts/ContactAggregator.java
index cad5d50..d6b2abc 100644
--- a/src/com/android/providers/contacts/ContactAggregator.java
+++ b/src/com/android/providers/contacts/ContactAggregator.java
@@ -17,6 +17,7 @@
package com.android.providers.contacts;
import com.android.providers.contacts.ContactMatcher.MatchScore;
+import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
@@ -372,16 +373,13 @@
private interface AggregationQuery {
String SQL =
"SELECT " + RawContacts._ID + "," + RawContacts.CONTACT_ID +
- ", " + RawContacts.ACCOUNT_TYPE + "," + RawContacts.ACCOUNT_NAME +
- ", " + RawContacts.DATA_SET +
+ ", " + RawContactsColumns.ACCOUNT_ID +
" FROM " + Tables.RAW_CONTACTS +
" WHERE " + RawContacts._ID + " IN(";
int _ID = 0;
int CONTACT_ID = 1;
- int ACCOUNT_TYPE = 2;
- int ACCOUNT_NAME = 3;
- int DATA_SET = 4;
+ int ACCOUNT_ID = 2;
}
/**
@@ -418,9 +416,7 @@
long rawContactIds[] = new long[count];
long contactIds[] = new long[count];
- String accountTypes[] = new String[count];
- String accountNames[] = new String[count];
- String dataSets[] = new String[count];
+ long accountIds[] = new long[count];
Cursor c = db.rawQuery(mSb.toString(), selectionArgs);
try {
count = c.getCount();
@@ -428,9 +424,7 @@
while (c.moveToNext()) {
rawContactIds[index] = c.getLong(AggregationQuery._ID);
contactIds[index] = c.getLong(AggregationQuery.CONTACT_ID);
- accountTypes[index] = c.getString(AggregationQuery.ACCOUNT_TYPE);
- accountNames[index] = c.getString(AggregationQuery.ACCOUNT_NAME);
- dataSets[index] = c.getString(AggregationQuery.DATA_SET);
+ accountIds[index] = c.getLong(AggregationQuery.ACCOUNT_ID);
index++;
}
} finally {
@@ -438,8 +432,8 @@
}
for (int i = 0; i < count; i++) {
- aggregateContact(txContext, db, rawContactIds[i], accountTypes[i], accountNames[i],
- dataSets[i], contactIds[i], mCandidates, mMatcher);
+ aggregateContact(txContext, db, rawContactIds[i], accountIds[i], contactIds[i],
+ mCandidates, mMatcher);
}
long elapsedTime = System.currentTimeMillis() - start;
@@ -565,16 +559,14 @@
public static final String TABLE = Tables.RAW_CONTACTS;
public static final String[] COLUMNS = {
- RawContacts.CONTACT_ID, RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_NAME,
- RawContacts.DATA_SET
+ RawContacts.CONTACT_ID,
+ RawContactsColumns.ACCOUNT_ID
};
public static final String SELECTION = RawContacts._ID + "=?";
public static final int CONTACT_ID = 0;
- public static final int ACCOUNT_TYPE = 1;
- public static final int ACCOUNT_NAME = 2;
- public static final int DATA_SET = 3;
+ public static final int ACCOUNT_ID = 1;
}
public void aggregateContact(
@@ -587,9 +579,7 @@
ContactMatcher matcher = new ContactMatcher();
long contactId = 0;
- String accountName = null;
- String accountType = null;
- String dataSet = null;
+ long accountId = 0;
mSelectionArgs1[0] = String.valueOf(rawContactId);
Cursor cursor = db.query(RawContactIdAndAccountQuery.TABLE,
RawContactIdAndAccountQuery.COLUMNS, RawContactIdAndAccountQuery.SELECTION,
@@ -597,15 +587,13 @@
try {
if (cursor.moveToFirst()) {
contactId = cursor.getLong(RawContactIdAndAccountQuery.CONTACT_ID);
- accountType = cursor.getString(RawContactIdAndAccountQuery.ACCOUNT_TYPE);
- accountName = cursor.getString(RawContactIdAndAccountQuery.ACCOUNT_NAME);
- dataSet = cursor.getString(RawContactIdAndAccountQuery.DATA_SET);
+ accountId = cursor.getLong(RawContactIdAndAccountQuery.ACCOUNT_ID);
}
} finally {
cursor.close();
}
- aggregateContact(txContext, db, rawContactId, accountType, accountName, dataSet, contactId,
+ aggregateContact(txContext, db, rawContactId, accountId, contactId,
candidates, matcher);
}
@@ -644,8 +632,8 @@
* with the highest match score. If no such contact is found, creates a new contact.
*/
private synchronized void aggregateContact(TransactionContext txContext, SQLiteDatabase db,
- long rawContactId, String accountType, String accountName, String dataSet,
- long currentContactId, MatchCandidateList candidates, ContactMatcher matcher) {
+ long rawContactId, long accountId, long currentContactId, MatchCandidateList candidates,
+ ContactMatcher matcher) {
int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
@@ -675,8 +663,7 @@
// the same account, not only will we not join it, but also we will split
// that other aggregate
if (contactId != -1 && contactId != currentContactId &&
- containsRawContactsFromAccount(db, contactId, accountType, accountName,
- dataSet)) {
+ containsRawContactsFromAccount(db, contactId, accountId)) {
contactIdToSplit = contactId;
contactId = -1;
}
@@ -739,41 +726,13 @@
* Returns true if the aggregate contains has any raw contacts from the specified account.
*/
private boolean containsRawContactsFromAccount(
- SQLiteDatabase db, long contactId, String accountType, String accountName,
- String dataSet) {
- String query;
- String[] args;
- if (accountType == null) {
- query = "SELECT count(_id) FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.CONTACT_ID + "=?" +
- " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL " +
- " AND " + RawContacts.ACCOUNT_NAME + " IS NULL " +
- " AND " + RawContacts.DATA_SET + " IS NULL";
- args = mSelectionArgs1;
- args[0] = String.valueOf(contactId);
- } else if (dataSet == null) {
- query = "SELECT count(_id) FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.CONTACT_ID + "=?" +
- " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
- " AND " + RawContacts.ACCOUNT_NAME + "=?" +
- " AND " + RawContacts.DATA_SET + " IS NULL";
- args = mSelectionArgs3;
- args[0] = String.valueOf(contactId);
- args[1] = accountType;
- args[2] = accountName;
- } else {
- query = "SELECT count(_id) FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.CONTACT_ID + "=?" +
- " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
- " AND " + RawContacts.ACCOUNT_NAME + "=?" +
- " AND " + RawContacts.DATA_SET + "=?";
- args = mSelectionArgs4;
- args[0] = String.valueOf(contactId);
- args[1] = accountType;
- args[2] = accountName;
- args[3] = dataSet;
- }
- Cursor cursor = db.rawQuery(query, args);
+ SQLiteDatabase db, long contactId, long accountId) {
+ final String query = "SELECT count(_id) FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + RawContacts.CONTACT_ID + "=?" +
+ " AND " + RawContactsColumns.ACCOUNT_ID + "=?";
+ Cursor cursor = db.rawQuery(query, new String[] {
+ Long.toString(contactId), Long.toString(accountId)
+ });
try {
cursor.moveToFirst();
return cursor.getInt(0) != 0;
@@ -1540,9 +1499,9 @@
+ RawContactsColumns.CONCRETE_ID + ","
+ RawContactsColumns.DISPLAY_NAME + ","
+ RawContactsColumns.DISPLAY_NAME_SOURCE + ","
- + RawContacts.ACCOUNT_TYPE + ","
- + RawContacts.ACCOUNT_NAME + ","
- + RawContacts.DATA_SET + ","
+ + AccountsColumns.CONCRETE_ACCOUNT_TYPE + ","
+ + AccountsColumns.CONCRETE_ACCOUNT_NAME + ","
+ + AccountsColumns.CONCRETE_DATA_SET + ","
+ RawContacts.SOURCE_ID + ","
+ RawContacts.CUSTOM_RINGTONE + ","
+ RawContacts.SEND_TO_VOICEMAIL + ","
@@ -1555,6 +1514,9 @@
+ Data.IS_SUPER_PRIMARY + ","
+ Photo.PHOTO_FILE_ID +
" FROM " + Tables.RAW_CONTACTS +
+ " JOIN " + Tables.ACCOUNTS + " ON ("
+ + AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
+ + ")" +
" LEFT OUTER JOIN " + Tables.DATA +
" ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
+ " AND ((" + DataColumns.MIMETYPE_ID + "=%d"
@@ -1848,7 +1810,7 @@
private interface PhotoIdQuery {
final String[] COLUMNS = new String[] {
- RawContacts.ACCOUNT_TYPE,
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE,
DataColumns.CONCRETE_ID,
Data.IS_SUPER_PRIMARY,
Photo.PHOTO_FILE_ID,
@@ -1873,7 +1835,11 @@
long photoMimeType = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
- String tables = Tables.RAW_CONTACTS + " JOIN " + Tables.DATA + " ON("
+ String tables = Tables.RAW_CONTACTS
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
+ + ")"
+ + " JOIN " + Tables.DATA + " ON("
+ DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
+ " AND (" + DataColumns.MIMETYPE_ID + "=" + photoMimeType + " AND "
+ Photo.PHOTO + " NOT NULL))";
@@ -2082,6 +2048,7 @@
}
private interface LookupKeyQuery {
+ String TABLE = Views.RAW_CONTACTS;
String[] COLUMNS = new String[] {
RawContacts._ID,
RawContactsColumns.DISPLAY_NAME,
@@ -2122,7 +2089,7 @@
protected String computeLookupKeyForContact(SQLiteDatabase db, long contactId) {
StringBuilder sb = new StringBuilder();
mSelectionArgs1[0] = String.valueOf(contactId);
- final Cursor c = db.query(Views.RAW_CONTACTS, LookupKeyQuery.COLUMNS,
+ final Cursor c = db.query(LookupKeyQuery.TABLE, LookupKeyQuery.COLUMNS,
RawContacts.CONTACT_ID + "=?", mSelectionArgs1, null, null, RawContacts._ID);
try {
while (c.moveToNext()) {
diff --git a/src/com/android/providers/contacts/ContactDirectoryManager.java b/src/com/android/providers/contacts/ContactDirectoryManager.java
index 1473679..25494f8 100644
--- a/src/com/android/providers/contacts/ContactDirectoryManager.java
+++ b/src/com/android/providers/contacts/ContactDirectoryManager.java
@@ -16,6 +16,7 @@
package com.android.providers.contacts;
+import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
import com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import com.google.android.collect.Lists;
@@ -53,7 +54,6 @@
private static final String TAG = "ContactDirectoryManager";
private static final boolean DEBUG = false; // DON'T SUBMIT WITH TRUE
- public static final String PROPERTY_DIRECTORY_SCAN_COMPLETE = "directoryScanComplete";
public static final String CONTACT_DIRECTORY_META_DATA = "android.content.ContactDirectory";
public class DirectoryInfo {
@@ -176,21 +176,21 @@
*/
public void scanAllPackages(boolean rescan) {
if (rescan || !areTypeResourceIdsValid()) {
- getDbHelper().setProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "0");
+ getDbHelper().setProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "0");
}
scanAllPackagesIfNeeded();
}
private void scanAllPackagesIfNeeded() {
- String scanComplete = getDbHelper().getProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "0");
+ String scanComplete = getDbHelper().getProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "0");
if (!"0".equals(scanComplete)) {
return;
}
long start = SystemClock.currentThreadTimeMillis();
int count = scanAllPackages();
- getDbHelper().setProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "1");
+ getDbHelper().setProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "1");
long end = SystemClock.currentThreadTimeMillis();
Log.i(TAG, "Discovered " + count + " contact directories in " + (end - start) + "ms");
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index f5f1a6a..68dc42f 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -17,6 +17,8 @@
package com.android.providers.contacts;
import com.android.common.content.SyncStateContentProviderHelper;
+import com.android.providers.contacts.util.NeededForTesting;
+import com.google.android.collect.Sets;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -80,6 +82,9 @@
import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Database helper for contacts. Designed as a singleton to make sure that all
@@ -103,7 +108,7 @@
* 600-699 Ice Cream Sandwich
* </pre>
*/
- static final int DATABASE_VERSION = 625;
+ static final int DATABASE_VERSION = 626;
private static final String DATABASE_NAME = "contacts2.db";
private static final String DATABASE_PRESENCE = "presence_db";
@@ -160,27 +165,47 @@
public static final String DATA_JOIN_RAW_CONTACTS = "data "
+ "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)";
+ // NOTE: If you want to refer to account name/type/data_set, AccountsColumns.CONCRETE_XXX
+ // MUST be used, as upgraded raw_contacts may have the account info columns too.
public static final String DATA_JOIN_MIMETYPE_RAW_CONTACTS = "data "
+ "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
- + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)";
+ + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)"
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")";
// NOTE: This requires late binding of GroupMembership MIME-type
- public static final String RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS = "raw_contacts "
- + "LEFT OUTER JOIN settings ON ("
- + "raw_contacts.account_name = settings.account_name AND "
- + "raw_contacts.account_type = settings.account_type AND "
- + "((raw_contacts.data_set IS NULL AND settings.data_set IS NULL) "
- + "OR (raw_contacts.data_set = settings.data_set))) "
+ // TODO Consolidate settings and accounts
+ public static final String RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS = Tables.RAW_CONTACTS
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")"
+ + "LEFT OUTER JOIN " + Tables.SETTINGS + " ON ("
+ + AccountsColumns.CONCRETE_ACCOUNT_NAME + "="
+ + SettingsColumns.CONCRETE_ACCOUNT_NAME + " AND "
+ + AccountsColumns.CONCRETE_ACCOUNT_TYPE + "="
+ + SettingsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
+ + "((" + AccountsColumns.CONCRETE_DATA_SET + " IS NULL AND "
+ + SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
+ + AccountsColumns.CONCRETE_DATA_SET + "="
+ + SettingsColumns.CONCRETE_DATA_SET + "))) "
+ "LEFT OUTER JOIN data ON (data.mimetype_id=? AND "
+ "data.raw_contact_id = raw_contacts._id) "
+ "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID
+ ")";
// NOTE: This requires late binding of GroupMembership MIME-type
+ // TODO Add missing DATA_SET join -- or just consolidate settings and accounts
public static final String SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS = "settings "
+ "LEFT OUTER JOIN raw_contacts ON ("
- + "raw_contacts.account_name = settings.account_name AND "
- + "raw_contacts.account_type = settings.account_type) "
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=(SELECT "
+ + AccountsColumns.CONCRETE_ID
+ + " FROM " + Tables.ACCOUNTS
+ + " WHERE "
+ + "(" + AccountsColumns.CONCRETE_ACCOUNT_NAME
+ + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME + ") AND "
+ + "(" + AccountsColumns.CONCRETE_ACCOUNT_TYPE
+ + "=" + SettingsColumns.CONCRETE_ACCOUNT_TYPE + ")))"
+ "LEFT OUTER JOIN data ON (data.mimetype_id=? AND "
+ "data.raw_contact_id = raw_contacts._id) "
+ "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
@@ -205,18 +230,19 @@
+ ")"
+ ")";
+ // NOTE: If you want to refer to account name/type/data_set, AccountsColumns.CONCRETE_XXX
+ // MUST be used, as upgraded raw_contacts may have the account info columns too.
public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS = "data "
+ "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
+ "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")"
+ "LEFT OUTER JOIN packages ON (data.package_id = packages._id) "
+ "LEFT OUTER JOIN groups "
+ " ON (mimetypes.mimetype='" + GroupMembership.CONTENT_ITEM_TYPE + "' "
+ " AND groups._id = data." + GroupMembership.GROUP_ROW_ID + ") ";
- public static final String GROUPS_JOIN_PACKAGES = "groups "
- + "LEFT OUTER JOIN packages ON (groups.package_id = packages._id)";
-
-
public static final String ACTIVITIES = "activities";
public static final String ACTIVITIES_JOIN_MIMETYPES = "activities "
@@ -233,6 +259,11 @@
public static final String NAME_LOOKUP_JOIN_RAW_CONTACTS = "name_lookup "
+ "INNER JOIN view_raw_contacts ON (name_lookup.raw_contact_id = "
+ "view_raw_contacts._id)";
+
+ public static final String RAW_CONTACTS_JOIN_ACCOUNTS = Tables.RAW_CONTACTS
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
+ + ")";
}
public interface Joins {
@@ -272,9 +303,16 @@
final String GROUP_BY_ACCOUNT_CONTACT_ID = SettingsColumns.CONCRETE_ACCOUNT_NAME + ","
+ SettingsColumns.CONCRETE_ACCOUNT_TYPE + "," + RawContacts.CONTACT_ID;
- final String RAW_CONTACT_IS_LOCAL = RawContactsColumns.CONCRETE_ACCOUNT_NAME
- + " IS NULL AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL AND "
- + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL";
+ String LOCAL_ACCOUNT_ID =
+ "(SELECT " + AccountsColumns._ID +
+ " FROM " + Tables.ACCOUNTS +
+ " WHERE " +
+ AccountsColumns.ACCOUNT_NAME + " IS NULL AND " +
+ AccountsColumns.ACCOUNT_TYPE + " IS NULL AND " +
+ AccountsColumns.DATA_SET + " IS NULL)";
+
+ final String RAW_CONTACT_IS_LOCAL = RawContactsColumns.CONCRETE_ACCOUNT_ID
+ + "=" + LOCAL_ACCOUNT_ID;
final String ZERO_GROUP_MEMBERSHIPS = "COUNT(" + GroupsColumns.CONCRETE_ID + ")=0";
@@ -298,12 +336,7 @@
" GROUP BY " + RawContacts.CONTACT_ID;
final String GROUP_HAS_ACCOUNT_AND_SOURCE_ID = Groups.SOURCE_ID + "=? AND "
- + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND "
- + Groups.DATA_SET + " IS NULL";
-
- final String GROUP_HAS_ACCOUNT_AND_DATA_SET_AND_SOURCE_ID = Groups.SOURCE_ID + "=? AND "
- + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND "
- + Groups.DATA_SET + "=?";
+ + GroupsColumns.ACCOUNT_ID + "=?";
public static final String CONTACT_VISIBLE =
"EXISTS (SELECT _id FROM " + Tables.VISIBLE_CONTACTS
@@ -334,14 +367,9 @@
public interface RawContactsColumns {
public static final String CONCRETE_ID =
Tables.RAW_CONTACTS + "." + BaseColumns._ID;
- public static final String CONCRETE_ACCOUNT_NAME =
- Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME;
- public static final String CONCRETE_ACCOUNT_TYPE =
- Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE;
- public static final String CONCRETE_DATA_SET =
- Tables.RAW_CONTACTS + "." + RawContacts.DATA_SET;
- public static final String CONCRETE_ACCOUNT_TYPE_AND_DATA_SET =
- Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE_AND_DATA_SET;
+
+ public static final String ACCOUNT_ID = "account_id";
+ public static final String CONCRETE_ACCOUNT_ID = Tables.RAW_CONTACTS + "." + ACCOUNT_ID;
public static final String CONCRETE_SOURCE_ID =
Tables.RAW_CONTACTS + "." + RawContacts.SOURCE_ID;
public static final String CONCRETE_VERSION =
@@ -381,6 +409,12 @@
Tables.RAW_CONTACTS + "." + RawContacts.NAME_VERIFIED;
}
+ public interface ViewRawContactsColumns {
+ String CONCRETE_ACCOUNT_NAME = Views.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME;
+ String CONCRETE_ACCOUNT_TYPE = Views.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE;
+ String CONCRETE_DATA_SET = Views.RAW_CONTACTS + "." + RawContacts.DATA_SET;
+ }
+
public interface DataColumns {
public static final String PACKAGE_ID = "package_id";
public static final String MIMETYPE_ID = "mimetype_id";
@@ -422,23 +456,21 @@
public static final String GROUP_ROW_ID = GroupMembership.GROUP_ROW_ID;
}
- public interface PhoneColumns {
- public static final String NORMALIZED_NUMBER = Data.DATA4;
- public static final String CONCRETE_NORMALIZED_NUMBER = DataColumns.CONCRETE_DATA4;
- }
-
public interface GroupsColumns {
public static final String PACKAGE_ID = "package_id";
+ public static final String CONCRETE_PACKAGE_ID = Tables.GROUPS + "." + PACKAGE_ID;
public static final String CONCRETE_ID = Tables.GROUPS + "." + BaseColumns._ID;
public static final String CONCRETE_SOURCE_ID = Tables.GROUPS + "." + Groups.SOURCE_ID;
- public static final String CONCRETE_ACCOUNT_NAME =
- Tables.GROUPS + "." + Groups.ACCOUNT_NAME;
- public static final String CONCRETE_ACCOUNT_TYPE =
- Tables.GROUPS + "." + Groups.ACCOUNT_TYPE;
- public static final String CONCRETE_DATA_SET = Tables.GROUPS + "." + Groups.DATA_SET;
- public static final String CONCRETE_ACCOUNT_TYPE_AND_DATA_SET = Tables.GROUPS + "." +
- Groups.ACCOUNT_TYPE_AND_DATA_SET;
+
+ public static final String ACCOUNT_ID = "account_id";
+ public static final String CONCRETE_ACCOUNT_ID = Tables.GROUPS + "." + ACCOUNT_ID;
+ }
+
+ public interface ViewGroupsColumns {
+ String CONCRETE_ACCOUNT_NAME = Views.GROUPS + "." + Groups.ACCOUNT_NAME;
+ String CONCRETE_ACCOUNT_TYPE = Views.GROUPS + "." + Groups.ACCOUNT_TYPE;
+ String CONCRETE_DATA_SET = Views.GROUPS + "." + Groups.DATA_SET;
}
public interface ActivitiesColumns {
@@ -592,10 +624,16 @@
String PROPERTY_VALUE = "property_value";
}
- public interface AccountsColumns {
+ public interface AccountsColumns extends BaseColumns {
+ String CONCRETE_ID = Tables.ACCOUNTS + "." + BaseColumns._ID;
+
String ACCOUNT_NAME = RawContacts.ACCOUNT_NAME;
String ACCOUNT_TYPE = RawContacts.ACCOUNT_TYPE;
String DATA_SET = RawContacts.DATA_SET;
+
+ String CONCRETE_ACCOUNT_NAME = Tables.ACCOUNTS + "." + ACCOUNT_NAME;
+ String CONCRETE_ACCOUNT_TYPE = Tables.ACCOUNTS + "." + ACCOUNT_TYPE;
+ String CONCRETE_DATA_SET = Tables.ACCOUNTS + "." + DATA_SET;
}
public static final class DirectoryColumns {
@@ -646,10 +684,29 @@
public static final int USAGE_TYPE_INT_SHORT_TEXT = 2;
}
+ public interface Projections {
+ String[] ID = new String[] {BaseColumns._ID};
+ String[] LITERAL_ONE = new String[] {"1"};
+ }
+
+ /**
+ * Property names for {@link ContactsDatabaseHelper#getProperty} and
+ * {@link ContactsDatabaseHelper#setProperty}.
+ */
+ public interface DbProperties {
+ String DIRECTORY_SCAN_COMPLETE = "directoryScanComplete";
+ String AGGREGATION_ALGORITHM = "aggregation_v2";
+ String KNOWN_ACCOUNTS = "known_accounts";
+ }
+
/** In-memory cache of previously found MIME-type mappings */
+ // TODO Use ConcurrentHashMap?
private final HashMap<String, Long> mMimetypeCache = new HashMap<String, Long>();
/** In-memory cache of previously found package name mappings */
+ // TODO Use ConcurrentHashMap?
private final HashMap<String, Long> mPackageCache = new HashMap<String, Long>();
+ private final Map<AccountWithDataSet, Long> mAccountCache =
+ new ConcurrentHashMap<AccountWithDataSet, Long>();
private long mMimeTypeIdEmail;
private long mMimeTypeIdIm;
@@ -731,7 +788,15 @@
com.android.internal.R.bool.config_use_strict_phone_number_comparation);
}
- private void refreshDatabaseCaches(SQLiteDatabase db) {
+ /**
+ * Clear all the cached database information and re-initialize it.
+ *
+ * @param db target database
+ * @param accountTableHasId {@code true} if the "accounts" table exists and has the ID column.
+ * This is normally {@code true}, but needs to be false during database upgrade until
+ * step 626, where the account ID was introduced.
+ */
+ private void refreshDatabaseCaches(SQLiteDatabase db, boolean accountTableHasId) {
mStatusUpdateDelete = null;
mStatusUpdateReplace = null;
mStatusUpdateInsert = null;
@@ -750,12 +815,21 @@
mAggregationModeQuery = null;
mContactInDefaultDirectoryQuery = null;
- populateMimeTypeCache(db);
+ initializeCache(db, accountTableHasId);
}
- private void populateMimeTypeCache(SQLiteDatabase db) {
+ /**
+ * (Re-)initialize the cached database information.
+ *
+ * @param db target database
+ * @param accountTableHasId See {@link #refreshDatabaseCaches}.
+ */
+ private void initializeCache(SQLiteDatabase db, boolean accountTableHasId) {
mMimetypeCache.clear();
mPackageCache.clear();
+ if (accountTableHasId) {
+ refreshAccountCache(db);
+ }
// TODO: This could be optimized into one query instead of 7
// Also: We shouldn't have those fields in the first place. This should just be
@@ -772,7 +846,7 @@
@Override
public void onOpen(SQLiteDatabase db) {
- refreshDatabaseCaches(db);
+ refreshDatabaseCaches(db, true);
mSyncState.onDatabaseOpened(db);
@@ -864,6 +938,13 @@
mSyncState.createDatabase(db);
+ db.execSQL("CREATE TABLE " + Tables.ACCOUNTS + " (" +
+ AccountsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+ AccountsColumns.ACCOUNT_NAME + " TEXT, " +
+ AccountsColumns.ACCOUNT_TYPE + " TEXT, " +
+ AccountsColumns.DATA_SET + " TEXT" +
+ ");");
+
// One row per group of contacts corresponding to the same person
db.execSQL("CREATE TABLE " + Tables.CONTACTS + " (" +
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
@@ -888,12 +969,11 @@
Contacts.NAME_RAW_CONTACT_ID +
");");
- // Contacts table
+ // Raw_contacts table
db.execSQL("CREATE TABLE " + Tables.RAW_CONTACTS + " (" +
RawContacts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
- RawContacts.ACCOUNT_NAME + " STRING DEFAULT NULL, " +
- RawContacts.ACCOUNT_TYPE + " STRING DEFAULT NULL, " +
- RawContacts.DATA_SET + " STRING DEFAULT NULL, " +
+ RawContactsColumns.ACCOUNT_ID + " INTEGER REFERENCES " +
+ Tables.ACCOUNTS + "(" + AccountsColumns._ID + ")," +
RawContacts.SOURCE_ID + " TEXT," +
RawContacts.RAW_CONTACT_IS_READ_ONLY + " INTEGER NOT NULL DEFAULT 0," +
RawContacts.VERSION + " INTEGER NOT NULL DEFAULT 1," +
@@ -929,19 +1009,11 @@
RawContacts.CONTACT_ID +
");");
- db.execSQL("CREATE INDEX raw_contacts_source_id_index ON " + Tables.RAW_CONTACTS + " (" +
- RawContacts.SOURCE_ID + ", " +
- RawContacts.ACCOUNT_TYPE + ", " +
- RawContacts.ACCOUNT_NAME +
- ");");
-
- db.execSQL("CREATE INDEX raw_contacts_source_id_data_set_index ON " +
+ db.execSQL("CREATE INDEX raw_contacts_source_id_account_id_index ON " +
Tables.RAW_CONTACTS + " (" +
- RawContacts.SOURCE_ID + ", " +
- RawContacts.ACCOUNT_TYPE + ", " +
- RawContacts.ACCOUNT_NAME + ", " +
- RawContacts.DATA_SET +
- ");");
+ RawContacts.SOURCE_ID + ", " +
+ RawContactsColumns.ACCOUNT_ID +
+ ");");
db.execSQL("CREATE TABLE " + Tables.STREAM_ITEMS + " (" +
StreamItems._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
@@ -1099,9 +1171,8 @@
db.execSQL("CREATE TABLE " + Tables.GROUPS + " (" +
Groups._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
GroupsColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," +
- Groups.ACCOUNT_NAME + " STRING DEFAULT NULL, " +
- Groups.ACCOUNT_TYPE + " STRING DEFAULT NULL, " +
- Groups.DATA_SET + " STRING DEFAULT NULL, " +
+ GroupsColumns.ACCOUNT_ID + " INTEGER REFERENCES " +
+ Tables.ACCOUNTS + "(" + AccountsColumns._ID + ")," +
Groups.SOURCE_ID + " TEXT," +
Groups.VERSION + " INTEGER NOT NULL DEFAULT 1," +
Groups.DIRTY + " INTEGER NOT NULL DEFAULT 0," +
@@ -1121,17 +1192,9 @@
Groups.SYNC4 + " TEXT " +
");");
- db.execSQL("CREATE INDEX groups_source_id_index ON " + Tables.GROUPS + " (" +
+ db.execSQL("CREATE INDEX groups_source_id_account_id_index ON " + Tables.GROUPS + " (" +
Groups.SOURCE_ID + ", " +
- Groups.ACCOUNT_TYPE + ", " +
- Groups.ACCOUNT_NAME +
- ");");
-
- db.execSQL("CREATE INDEX groups_source_id_data_set_index ON " + Tables.GROUPS + " (" +
- Groups.SOURCE_ID + ", " +
- Groups.ACCOUNT_TYPE + ", " +
- Groups.ACCOUNT_NAME + ", " +
- Groups.DATA_SET +
+ GroupsColumns.ACCOUNT_ID +
");");
db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.AGGREGATION_EXCEPTIONS + " (" +
@@ -1242,16 +1305,6 @@
PropertiesColumns.PROPERTY_VALUE + " TEXT " +
");");
- db.execSQL("CREATE TABLE " + Tables.ACCOUNTS + " (" +
- AccountsColumns.ACCOUNT_NAME + " TEXT, " +
- AccountsColumns.ACCOUNT_TYPE + " TEXT, " +
- AccountsColumns.DATA_SET + " TEXT" +
- ");");
-
- // Allow contacts without any account to be created for now. Achieve that
- // by inserting a fake account with both type and name as NULL.
- db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " VALUES(NULL, NULL, NULL)");
-
createDirectoriesTable(db);
createSearchIndexTable(db);
@@ -1323,7 +1376,7 @@
");");
// Trigger a full scan of directories in the system
- setProperty(db, ContactDirectoryManager.PROPERTY_DIRECTORY_SCAN_COMPLETE, "0");
+ setProperty(db, DbProperties.DIRECTORY_SCAN_COMPLETE, "0");
}
public void createSearchIndexTable(SQLiteDatabase db) {
@@ -1433,8 +1486,8 @@
" INSERT OR IGNORE INTO " + Tables.DEFAULT_DIRECTORY +
" SELECT " + RawContacts.CONTACT_ID +
" FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL " +
- " AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL; ");
+ " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_ID +
+ "=" + Clauses.LOCAL_ACCOUNT_ID + ";");
final String insertContactsWithAccountNoDefaultGroup = (
" INSERT OR IGNORE INTO " + Tables.DEFAULT_DIRECTORY +
" SELECT " + RawContacts.CONTACT_ID +
@@ -1442,10 +1495,8 @@
" WHERE NOT EXISTS" +
" (SELECT " + Groups._ID +
" FROM " + Tables.GROUPS +
- " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " = " +
- GroupsColumns.CONCRETE_ACCOUNT_NAME +
- " AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " = " +
- GroupsColumns.CONCRETE_ACCOUNT_TYPE +
+ " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_ID + " = " +
+ GroupsColumns.CONCRETE_ACCOUNT_ID +
" AND " + Groups.AUTO_ADD + " != 0" + ");");
final String insertContactsWithAccountDefaultGroup = (
" INSERT OR IGNORE INTO " + Tables.DEFAULT_DIRECTORY +
@@ -1461,10 +1512,8 @@
" AND EXISTS" +
" (SELECT " + Groups._ID +
" FROM " + Tables.GROUPS +
- " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " = " +
- GroupsColumns.CONCRETE_ACCOUNT_NAME +
- " AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " = " +
- GroupsColumns.CONCRETE_ACCOUNT_TYPE +
+ " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_ID + " = " +
+ GroupsColumns.CONCRETE_ACCOUNT_ID +
" AND " + Groups.AUTO_ADD + " != 0" + ");");
db.execSQL("DROP TRIGGER IF EXISTS " + Tables.GROUPS + "_auto_add_updated1;");
@@ -1537,14 +1586,15 @@
+ Data.SYNC4;
String syncColumns =
- RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AS " + RawContacts.ACCOUNT_NAME + ","
- + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + RawContacts.ACCOUNT_TYPE + ","
- + RawContactsColumns.CONCRETE_DATA_SET + " AS " + RawContacts.DATA_SET + ","
- + "(CASE WHEN " + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL THEN "
- + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
- + " ELSE " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "||'/'||"
- + RawContactsColumns.CONCRETE_DATA_SET + " END) AS "
- + RawContacts.ACCOUNT_TYPE_AND_DATA_SET + ","
+ RawContactsColumns.CONCRETE_ACCOUNT_ID + ","
+ + AccountsColumns.CONCRETE_ACCOUNT_NAME + " AS " + RawContacts.ACCOUNT_NAME + ","
+ + AccountsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + RawContacts.ACCOUNT_TYPE + ","
+ + AccountsColumns.CONCRETE_DATA_SET + " AS " + RawContacts.DATA_SET + ","
+ + "(CASE WHEN " + AccountsColumns.CONCRETE_DATA_SET + " IS NULL THEN "
+ + AccountsColumns.CONCRETE_ACCOUNT_TYPE
+ + " ELSE " + AccountsColumns.CONCRETE_ACCOUNT_TYPE + "||'/'||"
+ + AccountsColumns.CONCRETE_DATA_SET + " END) AS "
+ + RawContacts.ACCOUNT_TYPE_AND_DATA_SET + ","
+ RawContactsColumns.CONCRETE_SOURCE_ID + " AS " + RawContacts.SOURCE_ID + ","
+ RawContactsColumns.CONCRETE_NAME_VERIFIED + " AS "
+ RawContacts.NAME_VERIFIED + ","
@@ -1613,6 +1663,9 @@
+ DataColumns.CONCRETE_MIMETYPE_ID + "=" + MimetypesColumns.CONCRETE_ID + ")"
+ " JOIN " + Tables.RAW_CONTACTS + " ON ("
+ DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID + ")"
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")"
+ " JOIN " + Tables.CONTACTS + " ON ("
+ RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")"
+ " JOIN " + Tables.RAW_CONTACTS + " AS name_raw_contact ON("
@@ -1649,7 +1702,10 @@
+ dbForProfile() + " AS " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + ", "
+ rawContactOptionColumns + ", "
+ syncColumns
- + " FROM " + Tables.RAW_CONTACTS;
+ + " FROM " + Tables.RAW_CONTACTS
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")";
db.execSQL("CREATE VIEW " + Views.RAW_CONTACTS + " AS " + rawContactsSelect);
@@ -1695,6 +1751,9 @@
+ dbForProfile() + " AS " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + ","
+ Tables.GROUPS + "." + Groups.SOURCE_ID + " AS " + GroupMembership.GROUP_SOURCE_ID
+ " FROM " + Tables.RAW_CONTACTS
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")"
+ " LEFT OUTER JOIN " + Tables.DATA + " ON ("
+ DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID + ")"
+ " LEFT OUTER JOIN " + Tables.PACKAGES + " ON ("
@@ -1729,6 +1788,9 @@
+ DataColumns.CONCRETE_ID + " AS " + Contacts.Entity.DATA_ID + ","
+ Tables.GROUPS + "." + Groups.SOURCE_ID + " AS " + GroupMembership.GROUP_SOURCE_ID
+ " FROM " + Tables.RAW_CONTACTS
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")"
+ " JOIN " + Tables.CONTACTS + " ON ("
+ RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")"
+ " JOIN " + Tables.RAW_CONTACTS + " AS name_raw_contact ON("
@@ -1771,9 +1833,9 @@
ContactsColumns.CONCRETE_ID + " AS " + StreamItems.CONTACT_ID + ", " +
ContactsColumns.CONCRETE_LOOKUP_KEY +
" AS " + StreamItems.CONTACT_LOOKUP_KEY + ", " +
- RawContactsColumns.CONCRETE_ACCOUNT_NAME + ", " +
- RawContactsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
- RawContactsColumns.CONCRETE_DATA_SET + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
+ AccountsColumns.CONCRETE_DATA_SET + ", " +
StreamItemsColumns.CONCRETE_RAW_CONTACT_ID +
" as " + StreamItems.RAW_CONTACT_ID + ", " +
RawContactsColumns.CONCRETE_SOURCE_ID +
@@ -1791,7 +1853,11 @@
" FROM " + Tables.STREAM_ITEMS
+ " JOIN " + Tables.RAW_CONTACTS + " ON ("
+ StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
- + ") JOIN " + Tables.CONTACTS + " ON ("
+ + ")"
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ + ")"
+ + " JOIN " + Tables.CONTACTS + " ON ("
+ RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")";
db.execSQL("CREATE VIEW " + Views.STREAM_ITEMS + " AS " + streamItemSelect);
@@ -1831,13 +1897,17 @@
private void createGroupsView(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS " + Views.GROUPS + ";");
+
String groupsColumns =
- Groups.ACCOUNT_NAME + ","
- + Groups.ACCOUNT_TYPE + ","
- + Groups.DATA_SET + ","
- + "(CASE WHEN " + Groups.DATA_SET + " IS NULL THEN " + Groups.ACCOUNT_TYPE
- + " ELSE " + Groups.ACCOUNT_TYPE + "||" + Groups.DATA_SET + " END) AS "
- + Groups.ACCOUNT_TYPE_AND_DATA_SET + ","
+ GroupsColumns.CONCRETE_ACCOUNT_ID + " AS " + GroupsColumns.ACCOUNT_ID + ","
+ + AccountsColumns.CONCRETE_ACCOUNT_NAME + " AS " + Groups.ACCOUNT_NAME + ","
+ + AccountsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + Groups.ACCOUNT_TYPE + ","
+ + AccountsColumns.CONCRETE_DATA_SET + " AS " + Groups.DATA_SET + ","
+ + "(CASE WHEN " + AccountsColumns.CONCRETE_DATA_SET
+ + " IS NULL THEN " + AccountsColumns.CONCRETE_ACCOUNT_TYPE
+ + " ELSE " + AccountsColumns.CONCRETE_ACCOUNT_TYPE
+ + "||'/'||" + AccountsColumns.CONCRETE_DATA_SET + " END) AS "
+ + Groups.ACCOUNT_TYPE_AND_DATA_SET + ","
+ Groups.SOURCE_ID + ","
+ Groups.VERSION + ","
+ Groups.DIRTY + ","
@@ -1860,7 +1930,11 @@
String groupsSelect = "SELECT "
+ GroupsColumns.CONCRETE_ID + " AS " + Groups._ID + ","
+ groupsColumns
- + " FROM " + Tables.GROUPS_JOIN_PACKAGES;
+ + " FROM " + Tables.GROUPS
+ + " JOIN " + Tables.ACCOUNTS + " ON ("
+ + GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")"
+ + " LEFT OUTER JOIN " + Tables.PACKAGES + " ON ("
+ + GroupsColumns.CONCRETE_PACKAGE_ID + "=" + PackagesColumns.CONCRETE_ID + ")";
db.execSQL("CREATE VIEW " + Views.GROUPS + " AS " + groupsSelect);
}
@@ -2303,6 +2377,12 @@
oldVersion = 625;
}
+ if (oldVersion < 626) {
+ upgradeToVersion626(db);
+ upgradeViewsAndTriggers = true;
+ oldVersion = 626;
+ }
+
if (upgradeViewsAndTriggers) {
createContactsViews(db);
createGroupsView(db);
@@ -3328,7 +3408,7 @@
}
private void upgradeToVersion504(SQLiteDatabase db) {
- populateMimeTypeCache(db);
+ initializeCache(db, false);
// Find all names with prefixes and recreate display name
Cursor cursor = db.rawQuery(
@@ -3533,6 +3613,81 @@
db.execSQL("ALTER TABLE calls ADD formatted_number TEXT DEFAULT NULL;");
}
+ private void upgradeToVersion626(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS accounts");
+
+ db.execSQL("CREATE TABLE accounts (" +
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ "account_name TEXT, " +
+ "account_type TEXT, " +
+ "data_set TEXT" +
+ ");");
+
+ // Add "account_id" column to groups and raw_contacts
+ db.execSQL("ALTER TABLE raw_contacts ADD " +
+ "account_id INTEGER REFERENCES accounts(_id)");
+ db.execSQL("ALTER TABLE groups ADD " +
+ "account_id INTEGER REFERENCES accounts(_id)");
+
+ // Update indexes.
+ db.execSQL("DROP INDEX IF EXISTS raw_contacts_source_id_index");
+ db.execSQL("DROP INDEX IF EXISTS raw_contacts_source_id_data_set_index");
+ db.execSQL("DROP INDEX IF EXISTS groups_source_id_index");
+ db.execSQL("DROP INDEX IF EXISTS groups_source_id_data_set_index");
+
+ db.execSQL("CREATE INDEX raw_contacts_source_id_account_id_index ON raw_contacts ("
+ + "sourceid, account_id);");
+ db.execSQL("CREATE INDEX groups_source_id_account_id_index ON groups ("
+ + "sourceid, account_id);");
+
+ // Migrate account_name/account_type/data_set to accounts table
+
+ final Set<AccountWithDataSet> accountsWithDataSets = Sets.newHashSet();
+ upgradeToVersion626_findAccountsWithDataSets(accountsWithDataSets, db, "raw_contacts");
+ upgradeToVersion626_findAccountsWithDataSets(accountsWithDataSets, db, "groups");
+
+ for (AccountWithDataSet accountWithDataSet : accountsWithDataSets) {
+ db.execSQL("INSERT INTO accounts (account_name,account_type,data_set)VALUES(?, ?, ?)",
+ new String[] {
+ accountWithDataSet.getAccountName(),
+ accountWithDataSet.getAccountType(),
+ accountWithDataSet.getDataSet()
+ });
+ }
+ upgradeToVersion626_fillAccountId(db, "raw_contacts");
+ upgradeToVersion626_fillAccountId(db, "groups");
+ }
+
+ private static void upgradeToVersion626_findAccountsWithDataSets(
+ Set<AccountWithDataSet> result, SQLiteDatabase db, String table) {
+ Cursor c = db.rawQuery(
+ "SELECT DISTINCT account_name, account_type, data_set FROM " + table, null);
+ try {
+ while (c.moveToNext()) {
+ result.add(AccountWithDataSet.get(c.getString(0), c.getString(1), c.getString(2)));
+ }
+ } finally {
+ c.close();
+ }
+ }
+
+ private static void upgradeToVersion626_fillAccountId(SQLiteDatabase db, String table) {
+ StringBuilder sb = new StringBuilder();
+
+ // Set account_id and null out account_name, account_type and data_set
+
+ sb.append("UPDATE " + table + " SET account_id = (SELECT _id FROM accounts WHERE ");
+
+ addJoinExpressionAllowingNull(sb, table + ".account_name", "accounts.account_name");
+ sb.append("AND");
+ addJoinExpressionAllowingNull(sb, table + ".account_type", "accounts.account_type");
+ sb.append("AND");
+ addJoinExpressionAllowingNull(sb, table + ".data_set", "accounts.data_set");
+
+ sb.append("), account_name = null, account_type = null, data_set = null");
+ db.execSQL(sb.toString());
+ }
+
public String extractHandleFromEmailAddress(String email) {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
if (tokens.length == 0) {
@@ -3586,6 +3741,17 @@
}
/**
+ * Add a string like "(((column1) = (column2)) OR ((column1) IS NULL AND (column2) IS NULL))"
+ */
+ private static StringBuilder addJoinExpressionAllowingNull(StringBuilder sb,
+ String column1, String column2) {
+ sb.append("(((").append(column1).append(")=(").append(column2);
+ sb.append("))OR((");
+ sb.append(column1).append(") IS NULL AND (").append(column2).append(") IS NULL))");
+ return sb;
+ }
+
+ /**
* Adds index stats into the SQLite database to force it to always use the lookup indexes.
*/
private void updateSqliteStats(SQLiteDatabase db) {
@@ -3594,6 +3760,9 @@
// Important here are relative sizes. Raw-Contacts is slightly bigger than Contacts
// Warning: Missing tables in here will make SQLite assume to contain 1000000 rows,
// which can lead to catastrophic query plans for small tables
+
+ // See the latest of version of http://www.sqlite.org/cgi/src/finfo?name=src/analyze.c
+ // for what these numbers mean.
try {
db.execSQL("DELETE FROM sqlite_stat1");
updateIndexStats(db, Tables.CONTACTS,
@@ -3602,15 +3771,13 @@
"contacts_name_raw_contact_id_index", "9000 1");
updateIndexStats(db, Tables.RAW_CONTACTS,
- "raw_contacts_source_id_index", "10000 1 1 1");
- updateIndexStats(db, Tables.RAW_CONTACTS,
"raw_contacts_contact_id_index", "10000 2");
updateIndexStats(db, Tables.RAW_CONTACTS,
"raw_contact_sort_key2_index", "10000 2");
updateIndexStats(db, Tables.RAW_CONTACTS,
"raw_contact_sort_key1_index", "10000 2");
updateIndexStats(db, Tables.RAW_CONTACTS,
- "raw_contacts_source_id_data_set_index", "10000 1 1 1 1");
+ "raw_contacts_source_id_account_id_index", "10000 1 1 1 1");
updateIndexStats(db, Tables.NAME_LOOKUP,
"name_lookup_raw_contact_id_index", "35000 4");
@@ -3632,9 +3799,7 @@
"data_raw_contact_id", "60000 10");
updateIndexStats(db, Tables.GROUPS,
- "groups_source_id_index", "50 2 2 1");
- updateIndexStats(db, Tables.GROUPS,
- "groups_source_id_data_set_index", "50 2 2 1 1");
+ "groups_source_id_account_id_index", "50 2 2 1 1");
updateIndexStats(db, Tables.NICKNAME_LOOKUP,
"nickname_lookup_index", "500 2 1");
@@ -3749,8 +3914,6 @@
SQLiteDatabase db = getWritableDatabase();
db.execSQL("DELETE FROM " + Tables.ACCOUNTS + ";");
- db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " VALUES(NULL, NULL, NULL)");
-
db.execSQL("DELETE FROM " + Tables.CONTACTS + ";");
db.execSQL("DELETE FROM " + Tables.RAW_CONTACTS + ";");
db.execSQL("DELETE FROM " + Tables.STREAM_ITEMS + ";");
@@ -3767,6 +3930,8 @@
db.execSQL("DELETE FROM " + Tables.DIRECTORIES + ";");
db.execSQL("DELETE FROM " + Tables.SEARCH_INDEX + ";");
+ initializeCache(db, true);
+
// Note: we are not removing reference data from Tables.NICKNAME_LOOKUP
}
@@ -3968,6 +4133,79 @@
}
}
+ /** Refresh {@link #mAccountCache}. */
+ public void refreshAccountCache() {
+ refreshAccountCache(getReadableDatabase());
+ }
+
+ /** Refresh {@link #mAccountCache}. */
+ private void refreshAccountCache(SQLiteDatabase db) {
+ mAccountCache.clear();
+ Cursor c = db.rawQuery(
+ "SELECT DISTINCT " + AccountsColumns._ID + "," + AccountsColumns.ACCOUNT_NAME +
+ "," + AccountsColumns.ACCOUNT_TYPE + "," + AccountsColumns.DATA_SET +
+ " FROM " + Tables.ACCOUNTS, null);
+ try {
+ while (c.moveToNext()) {
+ mAccountCache.put(
+ AccountWithDataSet.get(c.getString(1), c.getString(2), c.getString(3)),
+ c.getLong(0));
+ }
+ } finally {
+ c.close();
+ }
+ }
+
+ /**
+ * Gets all accounts in the accounts table.
+ */
+ public Set<AccountWithDataSet> getAllAccountsWithDataSets() {
+ return mAccountCache.keySet();
+ }
+
+ /**
+ * @return ID of the specified account, or null if the account doesn't exist.
+ */
+ public Long getAccountIdOrNull(AccountWithDataSet accountWithDataSet) {
+ if (accountWithDataSet == null) {
+ accountWithDataSet = AccountWithDataSet.LOCAL;
+ }
+ return mAccountCache.get(accountWithDataSet);
+ }
+
+ /**
+ * @return ID of the specified account. This method will create a record in the accounts table
+ * if the account doesn't exist in the accounts table.
+ *
+ * This must be used in a transaction, so there's no need for synchronization.
+ */
+ public long getOrCreateAccountIdInTransaction(AccountWithDataSet accountWithDataSet) {
+ if (accountWithDataSet == null) {
+ accountWithDataSet = AccountWithDataSet.LOCAL;
+ }
+ Long id = getAccountIdOrNull(accountWithDataSet);
+ if (id != null) {
+ return id;
+ }
+ final SQLiteStatement insert = getWritableDatabase().compileStatement(
+ "INSERT INTO " + Tables.ACCOUNTS +
+ " (" + AccountsColumns.ACCOUNT_NAME + ", " +
+ AccountsColumns.ACCOUNT_TYPE + ", " +
+ AccountsColumns.DATA_SET + ") VALUES (?, ?, ?)");
+ try {
+ DatabaseUtils.bindObjectToProgram(insert, 1, accountWithDataSet.getAccountName());
+ DatabaseUtils.bindObjectToProgram(insert, 2, accountWithDataSet.getAccountType());
+ DatabaseUtils.bindObjectToProgram(insert, 3, accountWithDataSet.getDataSet());
+ id = insert.executeInsert();
+ } finally {
+ insert.close();
+ }
+
+ mAccountCache.put(accountWithDataSet, id);
+
+ return id;
+ }
+
/**
* Update {@link Contacts#IN_VISIBLE_GROUP} for all contacts.
*/
@@ -4008,38 +4246,29 @@
" JOIN " + Tables.DATA +
" ON (" + RawContactsColumns.CONCRETE_ID + "="
+ Data.RAW_CONTACT_ID + ")" +
- " WHERE " + RawContacts.CONTACT_ID + "=?" +
- " AND " + DataColumns.MIMETYPE_ID + "=?" +
+ " WHERE " + RawContacts.CONTACT_ID + "=?1" +
+ " AND " + DataColumns.MIMETYPE_ID + "=?2" +
") OR EXISTS (" +
"SELECT " + RawContacts._ID +
" FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.CONTACT_ID + "=?" +
+ " WHERE " + RawContacts.CONTACT_ID + "=?1" +
" AND NOT EXISTS" +
" (SELECT " + Groups._ID +
" FROM " + Tables.GROUPS +
- " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " = "
- + GroupsColumns.CONCRETE_ACCOUNT_NAME +
- " AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " = "
- + GroupsColumns.CONCRETE_ACCOUNT_TYPE +
- " AND (" + RawContactsColumns.CONCRETE_DATA_SET + " = "
- + GroupsColumns.CONCRETE_DATA_SET
- + " OR " + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL AND "
- + GroupsColumns.CONCRETE_DATA_SET + " IS NULL)" +
+ " WHERE " + RawContactsColumns.CONCRETE_ACCOUNT_ID + " = "
+ + GroupsColumns.CONCRETE_ACCOUNT_ID +
" AND " + Groups.AUTO_ADD + " != 0" +
")" +
") OR EXISTS (" +
"SELECT " + RawContacts._ID +
" FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.CONTACT_ID + "=?" +
- " AND " + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL " +
- " AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL" +
- " AND " + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL" +
+ " WHERE " + RawContacts.CONTACT_ID + "=?1" +
+ " AND " + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" +
+ Clauses.LOCAL_ACCOUNT_ID +
")",
new String[] {
contactIdAsString,
- String.valueOf(mimetype),
- contactIdAsString,
- contactIdAsString
+ String.valueOf(mimetype)
}) != 0;
if (onlyIfChanged) {
@@ -4377,21 +4606,31 @@
}
/**
+ * Test if the given column appears in the given projection.
+ */
+ public static boolean isInProjection(String[] projection, String column) {
+ if (projection == null) {
+ return true; // Null means "all columns". We can't really tell if it's in there...
+ }
+ for (String test : projection) {
+ if (column.equals(test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Test if any of the columns appear in the given projection.
*/
- public boolean isInProjection(String[] projection, String... columns) {
+ public static boolean isInProjection(String[] projection, String... columns) {
if (projection == null) {
return true;
}
// Optimized for a single-column test
if (columns.length == 1) {
- String column = columns[0];
- for (String test : projection) {
- if (column.equals(test)) {
- return true;
- }
- }
+ return isInProjection(projection, columns[0]);
} else {
for (String test : projection) {
for (String column : columns) {
@@ -4958,6 +5197,7 @@
return mCountryMonitor.getCountryIso();
}
+ @NeededForTesting
/* package */ String querySearchIndexContentForTest(long contactId) {
return DatabaseUtils.stringForQuery(getReadableDatabase(),
"SELECT " + SearchIndexColumns.CONTENT +
@@ -4966,6 +5206,7 @@
new String[] { String.valueOf(contactId) });
}
+ @NeededForTesting
/* package */ String querySearchIndexTokensForTest(long contactId) {
return DatabaseUtils.stringForQuery(getReadableDatabase(),
"SELECT " + SearchIndexColumns.TOKENS +
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 59b2170..ee1958d 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -16,9 +16,11 @@
package com.android.providers.contacts;
+import com.android.common.content.ProjectionMap;
import com.android.common.content.SyncStateContentProviderHelper;
import com.android.providers.contacts.ContactAggregator.AggregationSuggestionParameter;
import com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
+import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
@@ -26,14 +28,15 @@
import com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Joins;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
-import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Projections;
import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
@@ -41,9 +44,11 @@
import com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.ContactsDatabaseHelper.ViewGroupsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Views;
import com.android.providers.contacts.SearchIndexManager.FtsQueryBuilder;
import com.android.providers.contacts.util.DbQueryUtils;
+import com.android.providers.contacts.util.NeededForTesting;
import com.android.vcard.VCardComposer;
import com.android.vcard.VCardConfig;
import com.google.android.collect.Lists;
@@ -54,10 +59,8 @@
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.app.SearchManager;
+import android.content.CancelationSignal;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
@@ -65,7 +68,6 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.IContentService;
-import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.SyncAdapterType;
@@ -77,10 +79,7 @@
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.database.AbstractCursor;
-import android.database.CrossProcessCursor;
import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.CursorWrapper;
import android.database.DatabaseUtils;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
@@ -128,7 +127,6 @@
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.DisplayPhoto;
import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.PhotoFiles;
import android.provider.ContactsContract.Profile;
@@ -182,7 +180,6 @@
private static final int BACKGROUND_TASK_INITIALIZE = 0;
private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
- private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
@@ -212,16 +209,8 @@
*/
private static final String PREAUTHORIZED_URI_TOKEN = "perm_token";
- /**
- * Property key for the legacy contact import version. The need for a version
- * as opposed to a boolean flag is that if we discover bugs in the contact import process,
- * we can trigger re-import by incrementing the import version.
- */
- private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
- private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
private static final String PREF_LOCALE = "locale";
- private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
@@ -283,8 +272,8 @@
private static final int RAW_CONTACTS = 2002;
private static final int RAW_CONTACTS_ID = 2003;
- private static final int RAW_CONTACTS_DATA = 2004;
- private static final int RAW_CONTACT_ENTITY_ID = 2005;
+ private static final int RAW_CONTACTS_ID_DATA = 2004;
+ private static final int RAW_CONTACT_ID_ENTITY = 2005;
private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
private static final int RAW_CONTACTS_ID_STREAM_ITEMS_ID = 2008;
@@ -300,6 +289,9 @@
private static final int EMAILS_FILTER = 3008;
private static final int POSTALS = 3009;
private static final int POSTALS_ID = 3010;
+ private static final int CALLABLES = 3011;
+ private static final int CALLABLES_ID = 3012;
+ private static final int CALLABLES_FILTER = 3013;
private static final int PHONE_LOOKUP = 4000;
@@ -357,7 +349,7 @@
private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
private static final int STREAM_ITEMS_LIMIT = 21005;
- private static final int DISPLAY_PHOTO = 22000;
+ private static final int DISPLAY_PHOTO_ID = 22000;
private static final int PHOTO_DIMENSIONS = 22001;
// Inserts into URIs in this map will direct to the profile database if the parent record's
@@ -366,7 +358,7 @@
private static final Map<Integer, String> INSERT_URI_ID_VALUE_MAP = Maps.newHashMap();
static {
INSERT_URI_ID_VALUE_MAP.put(DATA, Data.RAW_CONTACT_ID);
- INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_DATA, Data.RAW_CONTACT_ID);
+ INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_DATA, Data.RAW_CONTACT_ID);
INSERT_URI_ID_VALUE_MAP.put(STATUS_UPDATES, StatusUpdates.DATA_ID);
INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
@@ -392,27 +384,13 @@
private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
RawContactsColumns.CONCRETE_ID + "=? AND "
- + GroupsColumns.CONCRETE_ACCOUNT_NAME
- + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
- + GroupsColumns.CONCRETE_ACCOUNT_TYPE
- + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
- + GroupsColumns.CONCRETE_DATA_SET
- + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR "
- + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
- + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
- + " AND " + Groups.FAVORITES + " != 0";
+ + GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
+ + " AND " + Groups.FAVORITES + " != 0";
private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
RawContactsColumns.CONCRETE_ID + "=? AND "
- + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
- + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
- + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
- + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
- + GroupsColumns.CONCRETE_DATA_SET + "="
- + RawContactsColumns.CONCRETE_DATA_SET + " OR "
- + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
- + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
- + " AND " + Groups.AUTO_ADD + " != 0";
+ + GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
+ + " AND " + Groups.AUTO_ADD + " != 0";
private static final String[] PROJECTION_GROUP_ID
= new String[]{Tables.GROUPS + "." + Groups._ID};
@@ -428,13 +406,16 @@
private interface DataContactsQuery {
public static final String TABLE = "data "
+ "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
+ + "JOIN " + Tables.ACCOUNTS + " ON ("
+ + AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
+ + ")"
+ "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
public static final String[] PROJECTION = new String[] {
RawContactsColumns.CONCRETE_ID,
- RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
- RawContactsColumns.CONCRETE_ACCOUNT_NAME,
- RawContactsColumns.CONCRETE_DATA_SET,
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE,
+ AccountsColumns.CONCRETE_ACCOUNT_NAME,
+ AccountsColumns.CONCRETE_DATA_SET,
DataColumns.CONCRETE_ID,
ContactsColumns.CONCRETE_ID
};
@@ -448,19 +429,21 @@
}
interface RawContactsQuery {
- String TABLE = Tables.RAW_CONTACTS;
+ String TABLE = Tables.RAW_CONTACTS_JOIN_ACCOUNTS;
String[] COLUMNS = new String[] {
RawContacts.DELETED,
- RawContacts.ACCOUNT_TYPE,
- RawContacts.ACCOUNT_NAME,
- RawContacts.DATA_SET,
+ RawContactsColumns.ACCOUNT_ID,
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE,
+ AccountsColumns.CONCRETE_ACCOUNT_NAME,
+ AccountsColumns.CONCRETE_DATA_SET,
};
int DELETED = 0;
- int ACCOUNT_TYPE = 1;
- int ACCOUNT_NAME = 2;
- int DATA_SET = 3;
+ int ACCOUNT_ID = 1;
+ int ACCOUNT_TYPE = 2;
+ int ACCOUNT_NAME = 3;
+ int DATA_SET = 4;
}
public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
@@ -502,22 +485,22 @@
/*
* Sorting order for email address suggestions: first starred, then the rest.
- * second in_visible_group, then the rest.
- * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
- * - three buckets: very recently contacted, then fairly
- * recently contacted, then the rest. Within each of the bucket - descending count
- * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
+ * Within the two groups:
+ * - three buckets: very recently contacted, then fairly recently contacted, then the rest.
+ * Within each of the bucket - descending count of times contacted (both for data row and for
+ * contact row).
+ * If all else fails, in_visible_group, alphabetical.
* (Super)primary email address is returned before other addresses for the same contact.
*/
private static final String EMAIL_FILTER_SORT_ORDER =
Contacts.STARRED + " DESC, "
- + Contacts.IN_VISIBLE_GROUP + " DESC, "
+ "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
+ " THEN 0 "
+ " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
+ " THEN 1 "
+ " ELSE 2 END), "
+ DataUsageStatColumns.TIMES_USED + " DESC, "
+ + Contacts.IN_VISIBLE_GROUP + " DESC, "
+ Contacts.DISPLAY_NAME + ", "
+ Data.CONTACT_ID + ", "
+ Data.IS_SUPER_PRIMARY + " DESC, "
@@ -887,6 +870,8 @@
* Note {@link Groups#SUMMARY_COUNT} doesn't exist in groups/view_groups.
* When we detect this column being requested, we join {@link Joins#GROUP_MEMBER_COUNT} to
* generate it.
+ *
+ * TODO Support SUMMARY_GROUP_COUNT_PER_ACCOUNT too. See also queryLocal().
*/
private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
.addAll(sGroupsProjectionMap)
@@ -895,27 +880,7 @@
"(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
+ Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
+ " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
- .build();
-
- // This is only exposed as hidden API for the contacts app, so we can be very specific in
- // the filtering
- private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
- ProjectionMap.builder()
- .addAll(sGroupsSummaryProjectionMap)
- .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
- "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
- + "(" + Groups.ACCOUNT_NAME + "="
- + GroupsColumns.CONCRETE_ACCOUNT_NAME
- + " AND "
- + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
- + " AND "
- + Groups.DELETED + "=0 AND "
- + Groups.FAVORITES + "=0 AND "
- + Groups.AUTO_ADD + "=0"
- + ")"
- + " GROUP BY "
- + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
- + ")")
+ .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, "0") // Always returns 0 for now.
.build();
/** Contains the agg_exceptions columns */
@@ -940,14 +905,14 @@
+ " THEN 1"
+ " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
+ " END)"
- + " FROM " + Tables.GROUPS
- + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
+ + " FROM " + Views.GROUPS
+ + " WHERE " + ViewGroupsColumns.CONCRETE_ACCOUNT_NAME + "="
+ SettingsColumns.CONCRETE_ACCOUNT_NAME
- + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
+ + " AND " + ViewGroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
+ SettingsColumns.CONCRETE_ACCOUNT_TYPE
- + " AND ((" + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
+ + " AND ((" + ViewGroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
+ SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
- + GroupsColumns.CONCRETE_DATA_SET + "="
+ + ViewGroupsColumns.CONCRETE_DATA_SET + "="
+ SettingsColumns.CONCRETE_DATA_SET + "))))=0"
+ " THEN 1"
+ " ELSE 0"
@@ -1063,13 +1028,13 @@
private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
- private boolean sIsPhoneInitialized;
- private boolean sIsPhone;
+ private boolean mIsPhoneInitialized;
+ private boolean mIsPhone;
- private StringBuilder mSb = new StringBuilder();
- private String[] mSelectionArgs1 = new String[1];
- private String[] mSelectionArgs2 = new String[2];
- private ArrayList<String> mSelectionArgs = Lists.newArrayList();
+ private final StringBuilder mSb = new StringBuilder();
+ private final String[] mSelectionArgs1 = new String[1];
+ private final String[] mSelectionArgs2 = new String[2];
+ private final ArrayList<String> mSelectionArgs = Lists.newArrayList();
private Account mAccount;
@@ -1129,10 +1094,10 @@
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
- matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
+ matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
RAW_CONTACTS_ID_DISPLAY_PHOTO);
- matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
+ matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ID_ENTITY);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
RAW_CONTACTS_ID_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
@@ -1156,6 +1121,10 @@
matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
/** "*" is in CSV form with data ids ("123,456,789") */
matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
+ matcher.addURI(ContactsContract.AUTHORITY, "data/callables/", CALLABLES);
+ matcher.addURI(ContactsContract.AUTHORITY, "data/callables/#", CALLABLES_ID);
+ matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter", CALLABLES_FILTER);
+ matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter/*", CALLABLES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
@@ -1222,7 +1191,7 @@
STREAM_ITEMS_ID_PHOTOS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
- matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO);
+ matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO_ID);
matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
@@ -1247,20 +1216,23 @@
private boolean mDirectoryCacheValid = false;
/**
- * An entry in group id cache. It maps the combination of (account type, account name, data set,
- * and source id) to group row id.
+ * An entry in group id cache.
+ *
+ * TODO: Move this and {@link #mGroupIdCache} to {@link DataRowHandlerForGroupMembership}.
*/
public static class GroupIdCacheEntry {
- String accountType;
- String accountName;
- String dataSet;
+ long accountId;
String sourceId;
long groupId;
}
- // We don't need a soft cache for groups - the assumption is that there will only
- // be a small number of contact groups. The cache is keyed off source id. The value
- // is a list of groups with this group id.
+ /**
+ * Map from group source IDs to lists of {@link GroupIdCacheEntry}s.
+ *
+ * We don't need a soft cache for groups - the assumption is that there will only
+ * be a small number of contact groups. The cache is keyed off source id. The value
+ * is a list of groups with this group id.
+ */
private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
/**
@@ -1335,8 +1307,8 @@
// The active transaction context will switch depending on the operation being performed.
// Both transaction contexts will be cleared out when a batch transaction is started, and
// each will be processed separately when a batch transaction completes.
- private TransactionContext mContactTransactionContext = new TransactionContext(false);
- private TransactionContext mProfileTransactionContext = new TransactionContext(true);
+ private final TransactionContext mContactTransactionContext = new TransactionContext(false);
+ private final TransactionContext mProfileTransactionContext = new TransactionContext(true);
private final ThreadLocal<TransactionContext> mTransactionContext =
new ThreadLocal<TransactionContext>();
@@ -1344,18 +1316,18 @@
private long mPreAuthorizedUriDuration;
// Map of single-use pre-authorized URIs to expiration times.
- private Map<Uri, Long> mPreAuthorizedUris = Maps.newHashMap();
+ private final Map<Uri, Long> mPreAuthorizedUris = Maps.newHashMap();
// Random number generator.
- private SecureRandom mRandom = new SecureRandom();
+ private final SecureRandom mRandom = new SecureRandom();
private LegacyApiSupport mLegacyApiSupport;
private GlobalSearchSupport mGlobalSearchSupport;
private CommonNicknameCache mCommonNicknameCache;
private SearchIndexManager mSearchIndexManager;
- private ContentValues mValues = new ContentValues();
- private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
+ private final ContentValues mValues = new ContentValues();
+ private final HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
private boolean mProviderStatusUpdateNeeded;
@@ -1387,6 +1359,12 @@
return initialize();
} catch (RuntimeException e) {
Log.e(TAG, "Cannot start provider", e);
+ // In production code we don't want to throw here, so that phone will still work
+ // in low storage situations.
+ // See I5c88a3024ff1c5a06b5756b29a2d903f8f6a2531
+ if (shouldThrowExceptionForInitializationError()) {
+ throw e;
+ }
return false;
} finally {
if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
@@ -1395,6 +1373,10 @@
}
}
+ protected boolean shouldThrowExceptionForInitializationError() {
+ return false;
+ }
+
private boolean initialize() {
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
@@ -1444,7 +1426,6 @@
DEFAULT_PREAUTHORIZED_URI_EXPIRATION);
scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
- scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
@@ -1521,10 +1502,8 @@
new DataRowHandlerForNote(context, dbHelper, contactAggregator));
}
- /**
- * Visible for testing.
- */
- /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
+ @VisibleForTesting
+ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
return new PhotoPriorityResolver(context);
}
@@ -1553,13 +1532,6 @@
break;
}
- case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
- if (isLegacyContactImportNeeded()) {
- importLegacyContactsInBackground();
- }
- break;
- }
-
case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
Context context = getContext();
if (!mAccountUpdateListenerRegistered) {
@@ -1725,7 +1697,7 @@
}
}
- /* Visible for testing */
+ @VisibleForTesting
protected void cleanupPhotoStore() {
SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
mActiveDb.set(db);
@@ -1752,32 +1724,23 @@
// Also query for all social stream item photos.
c = db.query(Tables.STREAM_ITEM_PHOTOS + " JOIN " + Tables.STREAM_ITEMS
- + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID
- + " JOIN " + Tables.RAW_CONTACTS
- + " ON " + StreamItems.RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID,
+ + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID,
new String[]{
StreamItemPhotosColumns.CONCRETE_ID,
StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID,
- StreamItemPhotos.PHOTO_FILE_ID,
- RawContacts.ACCOUNT_TYPE,
- RawContacts.ACCOUNT_NAME
+ StreamItemPhotos.PHOTO_FILE_ID
},
null, null, null, null, null);
Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
- Map<Long, Account> streamItemPhotoIdToAccount = Maps.newHashMap();
try {
while (c.moveToNext()) {
long streamItemPhotoId = c.getLong(0);
long streamItemId = c.getLong(1);
long photoFileId = c.getLong(2);
- String accountType = c.getString(3);
- String accountName = c.getString(4);
usedPhotoFileIds.add(photoFileId);
photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
- Account account = new Account(accountName, accountType);
- streamItemPhotoIdToAccount.put(photoFileId, account);
}
} finally {
c.close();
@@ -1857,12 +1820,12 @@
return mNameLookupBuilder;
}
- /* Visible for testing */
+ @VisibleForTesting
public ContactDirectoryManager getContactDirectoryManagerForTest() {
return mContactDirectoryManager;
}
- /* Visible for testing */
+ @VisibleForTesting
protected Locale getLocale() {
return Locale.getDefault();
}
@@ -1872,99 +1835,11 @@
return profileMode != null && profileMode;
}
- protected boolean isLegacyContactImportNeeded() {
- int version = Integer.parseInt(
- mContactsHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
- return version < PROPERTY_CONTACTS_IMPORT_VERSION;
- }
-
- protected LegacyContactImporter getLegacyContactImporter() {
- return new LegacyContactImporter(getContext(), this);
- }
-
- /**
- * Imports legacy contacts as a background task.
- */
- private void importLegacyContactsInBackground() {
- Log.v(TAG, "Importing legacy contacts");
- setProviderStatus(ProviderStatus.STATUS_UPGRADING);
-
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
- mContactsHelper.setLocale(this, mCurrentLocale);
- prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
-
- LegacyContactImporter importer = getLegacyContactImporter();
- if (importLegacyContacts(importer)) {
- onLegacyContactImportSuccess();
- } else {
- onLegacyContactImportFailure();
- }
- }
-
- /**
- * Unlocks the provider and declares that the import process is complete.
- */
- private void onLegacyContactImportSuccess() {
- NotificationManager nm =
- (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
-
- // Store a property in the database indicating that the conversion process succeeded
- mContactsHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
- String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
- setProviderStatus(ProviderStatus.STATUS_NORMAL);
- Log.v(TAG, "Completed import of legacy contacts");
- }
-
- /**
- * Announces the provider status and keeps the provider locked.
- */
- private void onLegacyContactImportFailure() {
- Context context = getContext();
- NotificationManager nm =
- (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
-
- // Show a notification
- Notification n = new Notification(android.R.drawable.stat_notify_error,
- context.getString(R.string.upgrade_out_of_memory_notification_ticker),
- System.currentTimeMillis());
- n.setLatestEventInfo(context,
- context.getString(R.string.upgrade_out_of_memory_notification_title),
- context.getString(R.string.upgrade_out_of_memory_notification_text),
- PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
- n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
-
- nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
-
- setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
- Log.v(TAG, "Failed to import legacy contacts");
-
- // Do not let any database changes until this issue is resolved.
- mOkToOpenAccess = false;
- }
-
- /* Visible for testing */
- /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
- boolean aggregatorEnabled = mContactAggregator.isEnabled();
- mContactAggregator.setEnabled(false);
- try {
- if (importer.importContacts()) {
-
- // TODO aggregate all newly added raw contacts
- mContactAggregator.setEnabled(aggregatorEnabled);
- return true;
- }
- } catch (Throwable e) {
- Log.e(TAG, "Legacy contact import failed", e);
- }
- mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
- return false;
- }
-
/**
* Wipes all data from the contacts database.
*/
- /* package */ void wipeData() {
+ @NeededForTesting
+ void wipeData() {
mContactsHelper.wipeData();
mProfileHelper.wipeData();
mContactsPhotoStore.clear();
@@ -2082,18 +1957,11 @@
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
if (mWriteAccessLatch != null) {
- // We are stuck trying to upgrade contacts db. The only update request
- // allowed in this case is an update of provider status, which will trigger
- // an attempt to upgrade contacts again.
+ // Update on PROVIDER_STATUS used to be used as a trigger to re-start legacy contact
+ // import. Now that we no longer support it, we just ignore it.
int match = sUriMatcher.match(uri);
if (match == PROVIDER_STATUS) {
- Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
- if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
- scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
- return 1;
- } else {
- return 0;
- }
+ return 0;
}
}
waitForAccess(mWriteAccessLatch);
@@ -2416,9 +2284,9 @@
break;
}
- case RAW_CONTACTS_DATA:
+ case RAW_CONTACTS_ID_DATA:
case PROFILE_RAW_CONTACTS_ID_DATA: {
- int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
+ int segment = match == RAW_CONTACTS_ID_DATA ? 1 : 2;
values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(segment));
id = insertData(values, callerIsSyncAdapter);
mSyncToNetwork |= !callerIsSyncAdapter;
@@ -2569,12 +2437,23 @@
} else {
values.put(RawContacts.DATA_SET, dataSet);
}
- accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
+ accountWithDataSet = AccountWithDataSet.get(account.name, account.type, dataSet);
}
return accountWithDataSet;
}
/**
+ * Same as {@link #resolveAccountWithDataSet}, but returns the account id for the
+ * {@link AccountWithDataSet}. Used for insert.
+ *
+ * May update the account cache; must be used only in a transaction.
+ */
+ private long resolveAccountIdInTransaction(Uri uri, ContentValues values) {
+ return mDbHelper.get().getOrCreateAccountIdInTransaction(
+ resolveAccountWithDataSet(uri, mValues));
+ }
+
+ /**
* Inserts an item in the contacts table
*
* @param values the values for the new row
@@ -2597,7 +2476,11 @@
mValues.putAll(values);
mValues.putNull(RawContacts.CONTACT_ID);
- AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
+ final long accountId = resolveAccountIdInTransaction(uri, mValues);
+ mValues.remove(RawContacts.ACCOUNT_NAME);
+ mValues.remove(RawContacts.ACCOUNT_TYPE);
+ mValues.remove(RawContacts.DATA_SET);
+ mValues.put(RawContactsColumns.ACCOUNT_ID, accountId);
if (values.containsKey(RawContacts.DELETED)
&& values.getAsInteger(RawContacts.DELETED) != 0) {
@@ -2613,7 +2496,7 @@
mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
// Trigger creation of a Contact based on this RawContact at the end of transaction
- mTransactionContext.get().rawContactInserted(rawContactId, accountWithDataSet);
+ mTransactionContext.get().rawContactInserted(rawContactId, accountId);
if (!callerIsSyncAdapter) {
addAutoAddMembership(rawContactId);
@@ -2735,10 +2618,6 @@
long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
- // Ensure that the raw contact exists and belongs to the caller's account.
- Account account = resolveAccount(uri, mValues);
- enforceModifyingAccount(account, rawContactId);
-
// Don't attempt to insert accounts params - they don't exist in the stream items table.
mValues.remove(RawContacts.ACCOUNT_NAME);
mValues.remove(RawContacts.ACCOUNT_TYPE);
@@ -2776,10 +2655,6 @@
if (streamItemId != 0) {
long rawContactId = lookupRawContactIdForStreamId(streamItemId);
- // Ensure that the raw contact exists and belongs to the caller's account.
- Account account = resolveAccount(uri, mValues);
- enforceModifyingAccount(account, rawContactId);
-
// Don't attempt to insert accounts params - they don't exist in the stream item
// photos table.
mValues.remove(RawContacts.ACCOUNT_NAME);
@@ -2880,104 +2755,6 @@
}
/**
- * Checks whether the given raw contact ID is owned by the given account.
- * If the resolved account is null, this will return true iff the raw contact
- * is also associated with the "null" account.
- *
- * If the resolved account does not match, this will throw a security exception.
- * @param account The resolved account (may be null).
- * @param rawContactId The raw contact ID to check for.
- */
- private void enforceModifyingAccount(Account account, long rawContactId) {
- String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
- + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
- + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
- String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
- + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
- + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
- Cursor c;
- if (account != null) {
- c = mActiveDb.get().query(Tables.RAW_CONTACTS,
- new String[]{RawContactsColumns.CONCRETE_ID}, accountSelection,
- new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
- null, null, null);
- } else {
- c = mActiveDb.get().query(Tables.RAW_CONTACTS,
- new String[]{RawContactsColumns.CONCRETE_ID}, noAccountSelection,
- new String[]{String.valueOf(rawContactId)},
- null, null, null);
- }
- try {
- if(c.getCount() == 0) {
- throw new SecurityException("Caller account does not match raw contact ID "
- + rawContactId);
- }
- } finally {
- c.close();
- }
- }
-
- /**
- * Checks whether the given selection of stream items matches up with the given
- * account. If any of the raw contacts fail the account check, this will throw a
- * security exception.
- * @param account The resolved account (may be null).
- * @param selection The selection.
- * @param selectionArgs The selection arguments.
- * @return The list of stream item IDs that would be included in this selection.
- */
- private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
- String[] selectionArgs) {
- List<Long> streamItemIds = Lists.newArrayList();
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- setTablesAndProjectionMapForStreamItems(qb);
- Cursor c = qb.query(mActiveDb.get(),
- new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
- selection, selectionArgs, null, null, null);
- try {
- while (c.moveToNext()) {
- streamItemIds.add(c.getLong(0));
-
- // Throw a security exception if the account doesn't match the raw contact's.
- enforceModifyingAccount(account, c.getLong(1));
- }
- } finally {
- c.close();
- }
- return streamItemIds;
- }
-
- /**
- * Checks whether the given selection of stream item photos matches up with the given
- * account. If any of the raw contacts fail the account check, this will throw a
- * security exception.
- * @param account The resolved account (may be null).
- * @param selection The selection.
- * @param selectionArgs The selection arguments.
- * @return The list of stream item photo IDs that would be included in this selection.
- */
- private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
- String[] selectionArgs) {
- List<Long> streamItemPhotoIds = Lists.newArrayList();
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- setTablesAndProjectionMapForStreamItemPhotos(qb);
- Cursor c = qb.query(mActiveDb.get(),
- new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
- selection, selectionArgs, null, null, null);
- try {
- while (c.moveToNext()) {
- streamItemPhotoIds.add(c.getLong(0));
-
- // Throw a security exception if the account doesn't match the raw contact's.
- enforceModifyingAccount(account, c.getLong(1));
- }
- } finally {
- c.close();
- }
- return streamItemPhotoIds;
- }
-
- /**
* Queries the database for stream items under the given raw contact. If there are
* more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
* the oldest entries (as determined by timestamp) will be deleted.
@@ -3089,7 +2866,12 @@
mValues.clear();
mValues.putAll(values);
- final AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
+ final long accountId = mDbHelper.get().getOrCreateAccountIdInTransaction(
+ resolveAccountWithDataSet(uri, mValues));
+ mValues.remove(Groups.ACCOUNT_NAME);
+ mValues.remove(Groups.ACCOUNT_TYPE);
+ mValues.remove(Groups.DATA_SET);
+ mValues.put(GroupsColumns.ACCOUNT_ID, accountId);
// Replace package with internal mapping
final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
@@ -3109,35 +2891,12 @@
long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
if (!callerIsSyncAdapter && isFavoritesGroup) {
- // add all starred raw contacts to this group
- String selection;
- String[] selectionArgs;
- if (accountWithDataSet == null) {
- selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
- + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
- + RawContacts.DATA_SET + " IS NULL";
- selectionArgs = null;
- } else if (accountWithDataSet.getDataSet() == null) {
- selection = RawContacts.ACCOUNT_NAME + "=? AND "
- + RawContacts.ACCOUNT_TYPE + "=? AND "
- + RawContacts.DATA_SET + " IS NULL";
- selectionArgs = new String[] {
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType()
- };
- } else {
- selection = RawContacts.ACCOUNT_NAME + "=? AND "
- + RawContacts.ACCOUNT_TYPE + "=? AND "
- + RawContacts.DATA_SET + "=?";
- selectionArgs = new String[] {
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType(),
- accountWithDataSet.getDataSet()
- };
- }
+ // If the inserted group is a favorite group, add all starred raw contacts to it.
+ mSelectionArgs1[0] = Long.toString(accountId);
Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
new String[]{RawContacts._ID, RawContacts.STARRED},
- selection, selectionArgs, null, null, null);
+ RawContactsColumns.CONCRETE_ACCOUNT_ID + "=?", mSelectionArgs1,
+ null, null, null);
try {
while (c.moveToNext()) {
if (c.getLong(1) != 0) {
@@ -3175,7 +2934,7 @@
if (dataSet != null) {
settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
}
- Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0);
+ Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0, null);
try {
if (c.getCount() > 0) {
// If a record was found, replace it with the new values.
@@ -3405,7 +3164,7 @@
Cursor c = queryLocal(streamUri, new String[]{StreamItems._ID},
StreamItems.RAW_CONTACT_ID + "=?",
new String[]{String.valueOf(rawContactId)},
- null, -1 /* directory ID */);
+ null, -1 /* directory ID */, null);
try {
if (c.getCount() > 0) {
c.moveToFirst();
@@ -3529,7 +3288,7 @@
args[1] = Uri.encode(lookupKey);
lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
- null);
+ null, null);
try {
if (c.getCount() == 1) {
// contact was unmodified so go ahead and delete it
@@ -3549,7 +3308,8 @@
int numDeletes = 0;
Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
- appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
+ appendAccountIdToSelection(uri, selection), selectionArgs,
+ null, null, null);
try {
while (c.moveToNext()) {
final long rawContactId = c.getLong(0);
@@ -3580,6 +3340,7 @@
case DATA_ID:
case PHONES_ID:
case EMAILS_ID:
+ case CALLABLES_ID:
case POSTALS_ID:
case PROFILE_DATA_ID: {
long dataId = ContentUris.parseId(uri);
@@ -3595,8 +3356,9 @@
case GROUPS: {
int numDeletes = 0;
- Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups._ID},
- appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
+ Cursor c = mActiveDb.get().query(Tables.GROUPS, Projections.ID,
+ appendAccountIdToSelection(uri, selection), selectionArgs,
+ null, null, null);
try {
while (c.moveToNext()) {
numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
@@ -3751,16 +3513,12 @@
* Returns whether the given raw contact ID is local (i.e. has no account associated with it).
*/
private boolean rawContactIsLocal(long rawContactId) {
- Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
- new String[] {
- RawContacts.ACCOUNT_NAME,
- RawContacts.ACCOUNT_TYPE,
- RawContacts.DATA_SET
- },
- RawContacts._ID + "=?",
+ Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, Projections.LITERAL_ONE,
+ RawContactsColumns.CONCRETE_ID + "=? AND " +
+ RawContactsColumns.ACCOUNT_ID + "=" + Clauses.LOCAL_ACCOUNT_ID,
new String[] {String.valueOf(rawContactId)}, null, null, null);
try {
- return c.moveToFirst() && c.isNull(0) && c.isNull(1) && c.isNull(2);
+ return c.getCount() > 0;
} finally {
c.close();
}
@@ -3779,23 +3537,21 @@
private int deleteStreamItems(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
- // First query for the stream items to be deleted, and check that they belong
- // to the account.
- Account account = resolveAccount(uri, values);
- List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
- account, selection, selectionArgs);
-
- // If no security exception has been thrown, we're fine to delete.
- for (long streamItemId : streamItemIds) {
- deleteStreamItem(streamItemId);
+ int count = 0;
+ final Cursor c = mActiveDb.get().query(Views.STREAM_ITEMS, Projections.ID,
+ selection, selectionArgs, null, null, null);
+ try {
+ c.moveToPosition(-1);
+ while (c.moveToNext()) {
+ count += deleteStreamItem(c.getLong(0));
+ }
+ } finally {
+ c.close();
}
-
- mVisibleTouched = true;
- return streamItemIds.size();
+ return count;
}
private int deleteStreamItem(long streamItemId) {
- // Note that this does not enforce the modifying account.
deleteStreamItemPhotos(streamItemId);
return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
new String[]{String.valueOf(streamItemId)});
@@ -3803,12 +3559,6 @@
private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
- // First query for the stream item photos to be deleted, and check that they
- // belong to the account.
- Account account = resolveAccount(uri, values);
- enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
-
- // If no security exception has been thrown, we're fine to delete.
return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
}
@@ -3904,9 +3654,9 @@
break;
}
- case RAW_CONTACTS_DATA:
+ case RAW_CONTACTS_ID_DATA:
case PROFILE_RAW_CONTACTS_ID_DATA: {
- int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
+ int segment = match == RAW_CONTACTS_ID_DATA ? 1 : 2;
final String rawContactId = uri.getPathSegments().get(segment);
String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
+ (selection == null ? "" : " AND " + selection);
@@ -3929,6 +3679,7 @@
case DATA_ID:
case PHONES_ID:
case EMAILS_ID:
+ case CALLABLES_ID:
case POSTALS_ID: {
count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
if (count > 0) {
@@ -3939,7 +3690,7 @@
case RAW_CONTACTS:
case PROFILE_RAW_CONTACTS: {
- selection = appendAccountToSelection(uri, selection);
+ selection = appendAccountIdToSelection(uri, selection);
count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
break;
}
@@ -3960,7 +3711,7 @@
}
case GROUPS: {
- count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
+ count = updateGroups(uri, values, appendAccountIdToSelection(uri, selection),
selectionArgs, callerIsSyncAdapter);
if (count > 0) {
mSyncToNetwork |= !callerIsSyncAdapter;
@@ -4094,10 +3845,6 @@
// Stream items can't be moved to a new raw contact.
values.remove(StreamItems.RAW_CONTACT_ID);
- // Check that the stream items being updated belong to the account.
- Account account = resolveAccount(uri, values);
- enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
-
// Don't attempt to update accounts params - they don't exist in the stream items table.
values.remove(RawContacts.ACCOUNT_NAME);
values.remove(RawContacts.ACCOUNT_TYPE);
@@ -4111,10 +3858,6 @@
// Stream item photos can't be moved to a new stream item.
values.remove(StreamItemPhotos.STREAM_ITEM_ID);
- // Check that the stream item photos being updated belong to the account.
- Account account = resolveAccount(uri, values);
- enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
-
// Don't attempt to update accounts params - they don't exist in the stream item
// photos table.
values.remove(RawContacts.ACCOUNT_NAME);
@@ -4164,53 +3907,106 @@
return mValues;
}
- private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
- String[] selectionArgs, boolean callerIsSyncAdapter) {
+ private interface GroupAccountQuery {
+ String TABLE = Views.GROUPS;
+ String[] COLUMNS = new String[] {
+ Groups._ID,
+ Groups.ACCOUNT_TYPE,
+ Groups.ACCOUNT_NAME,
+ Groups.DATA_SET,
+ };
+ int ID = 0;
+ int ACCOUNT_TYPE = 1;
+ int ACCOUNT_NAME = 2;
+ int DATA_SET = 3;
+ }
+
+ private int updateGroups(Uri uri, ContentValues originalValues, String selectionWithId,
+ String[] selectionArgs, boolean callerIsSyncAdapter) {
mGroupIdCache.clear();
- ContentValues updatedValues;
- if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
- updatedValues = mValues;
- updatedValues.clear();
- updatedValues.putAll(values);
- updatedValues.put(Groups.DIRTY, 1);
- } else {
- updatedValues = values;
- }
+ final SQLiteDatabase db = mActiveDb.get();
+ final ContactsDatabaseHelper dbHelper = mDbHelper.get();
- int count = mActiveDb.get().update(Tables.GROUPS, updatedValues, selectionWithId,
- selectionArgs);
+ final ContentValues updatedValues = new ContentValues();
+ updatedValues.putAll(originalValues);
+
+ if (!callerIsSyncAdapter && !updatedValues.containsKey(Groups.DIRTY)) {
+ updatedValues.put(Groups.DIRTY, 1);
+ }
if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
mVisibleTouched = true;
}
+ // Prepare for account change
+ final boolean isAccountNameChanging = updatedValues.containsKey(Groups.ACCOUNT_NAME);
+ final boolean isAccountTypeChanging = updatedValues.containsKey(Groups.ACCOUNT_TYPE);
+ final boolean isDataSetChanging = updatedValues.containsKey(Groups.DATA_SET);
+ final boolean isAccountChanging = isAccountNameChanging || isAccountTypeChanging
+ || isDataSetChanging;
+ final String updatedAccountName = updatedValues.getAsString(Groups.ACCOUNT_NAME);
+ final String updatedAccountType = updatedValues.getAsString(Groups.ACCOUNT_TYPE);
+ final String updatedDataSet = updatedValues.getAsString(Groups.DATA_SET);
+
+ updatedValues.remove(Groups.ACCOUNT_NAME);
+ updatedValues.remove(Groups.ACCOUNT_TYPE);
+ updatedValues.remove(Groups.DATA_SET);
+
+ // We later call requestSync() on all affected accounts.
+ final Set<Account> affectedAccounts = Sets.newHashSet();
+
+ // Look for all affected rows, and change them row by row.
+ final Cursor c = db.query(GroupAccountQuery.TABLE, GroupAccountQuery.COLUMNS,
+ selectionWithId, selectionArgs, null, null, null);
+ int returnCount = 0;
+ try {
+ c.moveToPosition(-1);
+ while (c.moveToNext()) {
+ final long groupId = c.getLong(GroupAccountQuery.ID);
+
+ mSelectionArgs1[0] = Long.toString(groupId);
+
+ final String accountName = isAccountNameChanging
+ ? updatedAccountName : c.getString(GroupAccountQuery.ACCOUNT_NAME);
+ final String accountType = isAccountTypeChanging
+ ? updatedAccountType : c.getString(GroupAccountQuery.ACCOUNT_TYPE);
+ final String dataSet = isDataSetChanging
+ ? updatedDataSet : c.getString(GroupAccountQuery.DATA_SET);
+
+ if (isAccountChanging) {
+ final long accountId = dbHelper.getOrCreateAccountIdInTransaction(
+ AccountWithDataSet.get(accountName, accountType, dataSet));
+ updatedValues.put(GroupsColumns.ACCOUNT_ID, accountId);
+ }
+
+ // Finally do the actual update.
+ final int count = db.update(Tables.GROUPS, updatedValues,
+ GroupsColumns.CONCRETE_ID + "=?", mSelectionArgs1);
+
+ if ((count > 0)
+ && !TextUtils.isEmpty(accountName)
+ && !TextUtils.isEmpty(accountType)) {
+ affectedAccounts.add(new Account(accountName, accountType));
+ }
+
+ returnCount += count;
+ }
+ } finally {
+ c.close();
+ }
+
// TODO: This will not work for groups that have a data set specified, since the content
// resolver will not be able to request a sync for the right source (unless it is updated
// to key off account with data set).
+ // i.e. requestSync only takes Account, not AccountWithDataSet.
if (updatedValues.containsKey(Groups.SHOULD_SYNC)
&& updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
- Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
- Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
- null, null);
- String accountName;
- String accountType;
- try {
- while (c.moveToNext()) {
- accountName = c.getString(0);
- accountType = c.getString(1);
- if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
- Account account = new Account(accountName, accountType);
- ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
- new Bundle());
- break;
- }
- }
- } finally {
- c.close();
+ for (Account account : affectedAccounts) {
+ ContentResolver.requestSync(account, ContactsContract.AUTHORITY, new Bundle());
}
}
- return count;
+ return returnCount;
}
private int updateSettings(Uri uri, ContentValues values, String selection,
@@ -4236,7 +4032,7 @@
int count = 0;
Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
- new String[] { RawContacts._ID }, selection,
+ Projections.ID, selection,
selectionArgs, null, null, null);
try {
while (cursor.moveToNext()) {
@@ -4253,27 +4049,68 @@
private int updateRawContact(long rawContactId, ContentValues values,
boolean callerIsSyncAdapter) {
- final String selection = RawContacts._ID + " = ?";
+ final String selection = RawContactsColumns.CONCRETE_ID + " = ?";
mSelectionArgs1[0] = Long.toString(rawContactId);
+
+ final ContactsDatabaseHelper dbHelper = mDbHelper.get();
+
final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
&& values.getAsInteger(RawContacts.DELETED) == 0);
+
+ final boolean isAccountNameChanging = values.containsKey(RawContacts.ACCOUNT_NAME);
+ final boolean isAccountTypeChanging = values.containsKey(RawContacts.ACCOUNT_TYPE);
+ final boolean isDataSetChanging = values.containsKey(RawContacts.DATA_SET);
+ final boolean isAccountChanging = isAccountNameChanging || isAccountTypeChanging
+ || isDataSetChanging;
+
int previousDeleted = 0;
- String accountType = null;
- String accountName = null;
- String dataSet = null;
- if (requestUndoDelete) {
+ long accountId = 0;
+ String oldAccountType = null;
+ String oldAccountName = null;
+ String oldDataSet = null;
+
+ if (requestUndoDelete || isAccountChanging) {
Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
selection, mSelectionArgs1, null, null, null);
try {
if (cursor.moveToFirst()) {
previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
- accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
- accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
- dataSet = cursor.getString(RawContactsQuery.DATA_SET);
+ accountId = cursor.getLong(RawContactsQuery.ACCOUNT_ID);
+ oldAccountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
+ oldAccountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
+ oldDataSet = cursor.getString(RawContactsQuery.DATA_SET);
}
} finally {
cursor.close();
}
+ if (isAccountChanging) {
+ // We can't change the original ContentValues, as it'll be re-used over all
+ // updateRawContact invocations in a transaction, so we need to create a new one.
+ // (However we don't want to use mValues here, because mValues may be used in some
+ // other methods that are called by this method.)
+ final ContentValues originalValues = values;
+ values = new ContentValues();
+ values.clear();
+ values.putAll(originalValues);
+
+ final AccountWithDataSet newAccountWithDataSet = AccountWithDataSet.get(
+ isAccountNameChanging
+ ? values.getAsString(RawContacts.ACCOUNT_NAME) : oldAccountName,
+ isAccountTypeChanging
+ ? values.getAsString(RawContacts.ACCOUNT_TYPE) : oldAccountType,
+ isDataSetChanging
+ ? values.getAsString(RawContacts.DATA_SET) : oldDataSet
+ );
+ accountId = dbHelper.getOrCreateAccountIdInTransaction(newAccountWithDataSet);
+
+ values.put(RawContactsColumns.ACCOUNT_ID, accountId);
+
+ values.remove(RawContacts.ACCOUNT_NAME);
+ values.remove(RawContacts.ACCOUNT_TYPE);
+ values.remove(RawContacts.DATA_SET);
+ }
+ }
+ if (requestUndoDelete) {
values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
}
@@ -4300,7 +4137,7 @@
// favorites group membership based on whether or not this contact is starred.
// If it is starred, add a group membership, if one doesn't already exist
// otherwise delete any matching group memberships.
- if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
+ if (!callerIsSyncAdapter && isAccountChanging) {
boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
SELECTION_STARRED_FROM_RAW_CONTACTS,
new String[]{Long.toString(rawContactId)});
@@ -4310,7 +4147,7 @@
// if this raw contact is being associated with an account, then add a
// group membership to the group marked as AutoAdd, if any.
- if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
+ if (!callerIsSyncAdapter && isAccountChanging) {
addAutoAddMembership(rawContactId);
}
@@ -4327,8 +4164,11 @@
mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
}
if (requestUndoDelete && previousDeleted == 1) {
- mTransactionContext.get().rawContactInserted(rawContactId,
- new AccountWithDataSet(accountName, accountType, dataSet));
+ // Note before the accounts refactoring, we used to use the *old* account here,
+ // which doesn't make sense, so now we pass the *new* account.
+ // (In practice it doesn't matter because there's probably no apps that undo-delete
+ // and change accounts at the same time.)
+ mTransactionContext.get().rawContactInserted(rawContactId, accountId);
}
}
return count;
@@ -4359,7 +4199,7 @@
// so we don't need to worry about updating data we don't have permission to read.
Cursor c = queryLocal(uri,
DataRowHandler.DataUpdateQuery.COLUMNS,
- selection, selectionArgs, null, -1 /* directory ID */);
+ selection, selectionArgs, null, -1 /* directory ID */, null);
try {
while(c.moveToNext()) {
count += updateData(mValues, c, callerIsSyncAdapter);
@@ -4523,10 +4363,77 @@
scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
}
- protected boolean updateAccountsInBackground(Account[] accounts) {
- // TODO : Check the unit test.
- boolean accountsChanged = false;
- SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+ private static final String ACCOUNT_STRING_SEPARATOR_OUTER = "\u0001";
+ private static final String ACCOUNT_STRING_SEPARATOR_INNER = "\u0002";
+
+ /** return serialized version of {@code accounts} */
+ @VisibleForTesting
+ static String accountsToString(Set<Account> accounts) {
+ final StringBuilder sb = new StringBuilder();
+ for (Account account : accounts) {
+ if (sb.length() > 0) {
+ sb.append(ACCOUNT_STRING_SEPARATOR_OUTER);
+ }
+ sb.append(account.name);
+ sb.append(ACCOUNT_STRING_SEPARATOR_INNER);
+ sb.append(account.type);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * de-serialize string returned by {@link #accountsToString} and return it.
+ * If {@code accountsString} is malformed it'll throw {@link IllegalArgumentException}.
+ */
+ @VisibleForTesting
+ static Set<Account> stringToAccounts(String accountsString) {
+ final Set<Account> ret = Sets.newHashSet();
+ if (accountsString.length() == 0) return ret; // no accounts
+ try {
+ for (String accountString : accountsString.split(ACCOUNT_STRING_SEPARATOR_OUTER)) {
+ String[] nameAndType = accountString.split(ACCOUNT_STRING_SEPARATOR_INNER);
+ ret.add(new Account(nameAndType[0], nameAndType[1]));
+ }
+ return ret;
+ } catch (RuntimeException ex) {
+ throw new IllegalArgumentException("Malformed string", ex);
+ }
+ }
+
+ /**
+ * @return {@code true} if the given {@code currentSystemAccounts} are different from the
+ * accounts we know, which are stored in the {@link DbProperties#KNOWN_ACCOUNTS} property.
+ */
+ @VisibleForTesting
+ boolean haveAccountsChanged(Account[] currentSystemAccounts) {
+ final ContactsDatabaseHelper dbHelper = mDbHelper.get();
+ final Set<Account> knownAccountSet;
+ try {
+ knownAccountSet = stringToAccounts(
+ dbHelper.getProperty(DbProperties.KNOWN_ACCOUNTS, ""));
+ } catch (IllegalArgumentException e) {
+ // Failed to get the last known accounts for an unknown reason. Let's just
+ // treat as if accounts have changed.
+ return true;
+ }
+ final Set<Account> currentAccounts = Sets.newHashSet(currentSystemAccounts);
+ return !knownAccountSet.equals(currentAccounts);
+ }
+
+ @VisibleForTesting
+ void saveAccounts(Account[] systemAccounts) {
+ final ContactsDatabaseHelper dbHelper = mDbHelper.get();
+ dbHelper.setProperty(DbProperties.KNOWN_ACCOUNTS,
+ accountsToString(Sets.newHashSet(systemAccounts)));
+ }
+
+ private boolean updateAccountsInBackground(Account[] systemAccounts) {
+ if (!haveAccountsChanged(systemAccounts)) {
+ return false;
+ }
+ Log.i(TAG, "Accounts changed");
+ final ContactsDatabaseHelper dbHelper = mDbHelper.get();
+ final SQLiteDatabase db = dbHelper.getWritableDatabase();
mActiveDb.set(db);
db.beginTransaction();
@@ -4534,117 +4441,68 @@
// absolutely imperative that no calls be made inside the following try block that can
// interact with the contacts DB. Otherwise it is quite possible for a deadlock to occur.
try {
- Set<AccountWithDataSet> existingAccountsWithDataSets =
- findValidAccountsWithDataSets(Tables.ACCOUNTS);
+ // First, remove stale rows from raw_contacts, groups, and related tables.
- // Add a row to the ACCOUNTS table (with no data set) for each new account.
- for (Account account : accounts) {
- AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
- account.name, account.type, null);
- if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
- accountsChanged = true;
+ // All accounts that are used in raw_contacts and/or groups.
+ final Set<AccountWithDataSet> knownAccountsWithDataSets
+ = dbHelper.getAllAccountsWithDataSets();
- // Add an account entry with an empty data set to match the account.
- db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
- + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
- + ") VALUES (?, ?, ?)",
- new String[] {
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType(),
- accountWithDataSet.getDataSet()
- });
+ // Find the accounts that have been removed.
+ final List<AccountWithDataSet> accountsWithDataSetsToDelete = Lists.newArrayList();
+ for (AccountWithDataSet knownAccountWithDataSet : knownAccountsWithDataSets) {
+ if (knownAccountWithDataSet.isLocalAccount()
+ || knownAccountWithDataSet.inSystemAccounts(systemAccounts)) {
+ continue;
}
- }
-
- // Check each of the existing sub-accounts against the account list. If the owning
- // account no longer exists, the sub-account and all its data should be deleted.
- List<AccountWithDataSet> accountsWithDataSetsToDelete =
- new ArrayList<AccountWithDataSet>();
- List<Account> accountList = Arrays.asList(accounts);
- for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
- Account owningAccount = new Account(
- accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
- if (!accountList.contains(owningAccount)) {
- accountsWithDataSetsToDelete.add(accountWithDataSet);
- }
+ accountsWithDataSetsToDelete.add(knownAccountWithDataSet);
}
if (!accountsWithDataSetsToDelete.isEmpty()) {
- accountsChanged = true;
for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
Log.d(TAG, "removing data for removed account " + accountWithDataSet);
- String[] accountParams = new String[] {
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType()
- };
- String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
- ? accountParams
- : new String[] {
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType(),
- accountWithDataSet.getDataSet()
- };
- String groupsDataSetClause = " AND " + Groups.DATA_SET
- + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
- String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
- + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
- String settingsDataSetClause = " AND " + Settings.DATA_SET
- + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
+ final Long accountIdOrNull = dbHelper.getAccountIdOrNull(accountWithDataSet);
- db.execSQL(
- "DELETE FROM " + Tables.GROUPS +
- " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
- " AND " + Groups.ACCOUNT_TYPE + " = ?" +
- groupsDataSetClause, accountWithDataSetParams);
- db.execSQL(
- "DELETE FROM " + Tables.PRESENCE +
- " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
- "SELECT " + RawContacts._ID +
- " FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
- " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
- rawContactsDataSetClause + ")", accountWithDataSetParams);
- db.execSQL(
- "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
- " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
- "SELECT " + StreamItems._ID +
- " FROM " + Tables.STREAM_ITEMS +
- " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
- "SELECT " + RawContacts._ID +
- " FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
- " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
- rawContactsDataSetClause + "))",
- accountWithDataSetParams);
- db.execSQL(
- "DELETE FROM " + Tables.STREAM_ITEMS +
- " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
- "SELECT " + RawContacts._ID +
- " FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
- " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
- rawContactsDataSetClause + ")",
- accountWithDataSetParams);
- db.execSQL(
- "DELETE FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
- " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
- rawContactsDataSetClause, accountWithDataSetParams);
- db.execSQL(
- "DELETE FROM " + Tables.SETTINGS +
- " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
- " AND " + Settings.ACCOUNT_TYPE + " = ?" +
- settingsDataSetClause, accountWithDataSetParams);
- db.execSQL(
- "DELETE FROM " + Tables.ACCOUNTS +
- " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
- " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
- rawContactsDataSetClause, accountWithDataSetParams);
- db.execSQL(
- "DELETE FROM " + Tables.DIRECTORIES +
- " WHERE " + Directory.ACCOUNT_NAME + "=?" +
- " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
- resetDirectoryCache();
+ // getAccountIdOrNull() really shouldn't return null here, but just in case...
+ if (accountIdOrNull != null) {
+ final String[] accountIdParams =
+ new String[] {Long.toString(accountIdOrNull)};
+ db.execSQL(
+ "DELETE FROM " + Tables.GROUPS +
+ " WHERE " + GroupsColumns.ACCOUNT_ID + " = ?",
+ accountIdParams);
+ db.execSQL(
+ "DELETE FROM " + Tables.PRESENCE +
+ " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
+ "SELECT " + RawContacts._ID +
+ " FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + RawContactsColumns.ACCOUNT_ID + " = ?)",
+ accountIdParams);
+ db.execSQL(
+ "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
+ " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
+ "SELECT " + StreamItems._ID +
+ " FROM " + Tables.STREAM_ITEMS +
+ " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
+ "SELECT " + RawContacts._ID +
+ " FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + RawContactsColumns.ACCOUNT_ID + "=?))",
+ accountIdParams);
+ db.execSQL(
+ "DELETE FROM " + Tables.STREAM_ITEMS +
+ " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
+ "SELECT " + RawContacts._ID +
+ " FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + RawContactsColumns.ACCOUNT_ID + " = ?)",
+ accountIdParams);
+ db.execSQL(
+ "DELETE FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + RawContactsColumns.ACCOUNT_ID + " = ?",
+ accountIdParams);
+ db.execSQL(
+ "DELETE FROM " + Tables.ACCOUNTS +
+ " WHERE " + AccountsColumns._ID + "=?",
+ accountIdParams);
+ }
}
// Find all aggregated contacts that used to contain the raw contacts
@@ -4672,55 +4530,44 @@
for (Long contactId : orphanContactIds) {
mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
}
- mDbHelper.get().updateAllVisible();
+ dbHelper.updateAllVisible();
// Don't bother updating the search index if we're in profile mode - there is no
// search index for the profile DB, and updating it for the contacts DB in this case
// makes no sense and risks a deadlock.
if (!inProfileMode()) {
+ // TODO Fix it. It only updates index for contacts/raw_contacts that the
+ // current transaction context knows updated, but here in this method we don't
+ // update that information, so effectively it's no-op.
+ // We can probably just schedule BACKGROUND_TASK_UPDATE_SEARCH_INDEX.
+ // (But make sure it's not scheduled yet. We schedule this task in initialize()
+ // too.)
updateSearchIndexInTransaction();
}
}
- // Now that we've done the account-based additions and subtractions from the Accounts
- // table, check for raw contacts that have been added with a data set and add Accounts
- // entries for those if necessary.
- existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
- Set<AccountWithDataSet> rawContactAccountsWithDataSets =
- findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
- rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
+ // Second, remove stale rows from Tables.SETTINGS and Tables.DIRECTORIES
+ removeStaleAccountRows(Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE,
+ systemAccounts);
+ removeStaleAccountRows(Tables.DIRECTORIES, Directory.ACCOUNT_NAME,
+ Directory.ACCOUNT_TYPE, systemAccounts);
- // Any remaining raw contact sub-accounts need to be added to the Accounts table.
- for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
- accountsChanged = true;
+ // Third, remaining tasks that must be done in a transaction.
+ // TODO: Should sync state take data set into consideration?
+ dbHelper.getSyncState().onAccountsChanged(db, systemAccounts);
- // Add an account entry to match the raw contact.
- db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
- + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
- + ") VALUES (?, ?, ?)",
- new String[] {
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType(),
- accountWithDataSet.getDataSet()
- });
- }
+ saveAccounts(systemAccounts);
- if (accountsChanged) {
- // TODO: Should sync state take data set into consideration?
- mDbHelper.get().getSyncState().onAccountsChanged(db, accounts);
- }
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
mAccountWritability.clear();
- if (accountsChanged) {
- updateContactsAccountCount(accounts);
- updateProviderStatus();
- }
-
- return accountsChanged;
+ dbHelper.refreshAccountCache();
+ updateContactsAccountCount(systemAccounts);
+ updateProviderStatus();
+ return true;
}
private void updateContactsAccountCount(Account[] accounts) {
@@ -4747,31 +4594,44 @@
scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
}
- /**
- * Finds all distinct account types and data sets present in the specified table.
- */
- private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
- Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
- Cursor c = mActiveDb.get().rawQuery(
- "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
- "," + RawContacts.DATA_SET +
+ public void removeStaleAccountRows(String table, String accountNameColumn,
+ String accountTypeColumn, Account[] systemAccounts) {
+ final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+ final Cursor c = db.rawQuery(
+ "SELECT DISTINCT " + accountNameColumn +
+ "," + accountTypeColumn +
" FROM " + table, null);
try {
+ c.moveToPosition(-1);
while (c.moveToNext()) {
- if (!c.isNull(0) && !c.isNull(1)) {
- accountsWithDataSets.add(
- new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
+ final AccountWithDataSet accountWithDataSet = AccountWithDataSet.get(
+ c.getString(0), c.getString(1), null);
+ if (accountWithDataSet.isLocalAccount()
+ || accountWithDataSet.inSystemAccounts(systemAccounts)) {
+ // Account still exists.
+ continue;
}
+
+ db.execSQL("DELETE FROM " + table +
+ " WHERE " + accountNameColumn + "=? AND " +
+ accountTypeColumn + "=?",
+ new String[] {accountWithDataSet.getAccountName(),
+ accountWithDataSet.getAccountType()});
}
} finally {
c.close();
}
- return accountsWithDataSets;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
+ return query(uri, projection, selection, selectionArgs, sortOrder, null);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder, CancelationSignal cancelationSignal) {
waitForAccess(mReadAccessLatch);
@@ -4781,7 +4641,8 @@
// Query the profile DB if appropriate.
if (mapsToProfileDb(uri)) {
switchToProfileMode();
- return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder);
+ return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder,
+ cancelationSignal);
}
// Otherwise proceed with a normal query against the contacts DB.
@@ -4790,15 +4651,16 @@
String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
if (directory == null) {
return addSnippetExtrasToCursor(uri,
- queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
+ queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1,
+ cancelationSignal));
} else if (directory.equals("0")) {
return addSnippetExtrasToCursor(uri,
queryLocal(uri, projection, selection, selectionArgs, sortOrder,
- Directory.DEFAULT));
+ Directory.DEFAULT, cancelationSignal));
} else if (directory.equals("1")) {
return addSnippetExtrasToCursor(uri,
queryLocal(uri, projection, selection, selectionArgs, sortOrder,
- Directory.LOCAL_INVISIBLE));
+ Directory.LOCAL_INVISIBLE, cancelationSignal));
}
DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
@@ -4836,11 +4698,14 @@
return null;
}
- CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
- if (crossProcessCursor != null) {
- return addSnippetExtrasToCursor(uri, cursor);
- } else {
- return matrixCursorFromCursor(addSnippetExtrasToCursor(uri, cursor));
+ // Load the cursor contents into a memory cursor (backed by a cursor window) and close the
+ // underlying cursor.
+ try {
+ MemoryCursor memCursor = new MemoryCursor(null, cursor.getColumnNames());
+ memCursor.fillFromCursor(cursor);
+ return memCursor;
+ } finally {
+ cursor.close();
}
}
@@ -4851,23 +4716,7 @@
return cursor;
}
- // Parse out snippet arguments for use when snippets are retrieved from the cursor.
- String[] args = null;
- String snippetArgs =
- getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
- if (snippetArgs != null) {
- args = snippetArgs.split(",");
- }
-
String query = uri.getLastPathSegment();
- String startMatch = args != null && args.length > 0 ? args[0]
- : DEFAULT_SNIPPET_ARG_START_MATCH;
- String endMatch = args != null && args.length > 1 ? args[1]
- : DEFAULT_SNIPPET_ARG_END_MATCH;
- String ellipsis = args != null && args.length > 2 ? args[2]
- : DEFAULT_SNIPPET_ARG_ELLIPSIS;
- int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
- : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
// Snippet data is needed for the snippeting on the client side, so store it in the cursor
if (cursor instanceof AbstractCursor && deferredSnippetingRequested(uri)){
@@ -4896,31 +4745,6 @@
return cursor;
}
- private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
- Cursor c = cursor;
- if (c instanceof CrossProcessCursor) {
- return (CrossProcessCursor) c;
- } else if (c instanceof CursorWindow) {
- return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
- } else {
- return null;
- }
- }
-
- public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
- MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
- int numColumns = cursor.getColumnCount();
- String data[] = new String[numColumns];
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- for (int i = 0; i < numColumns; i++) {
- data[i] = cursor.getString(i);
- }
- newCursor.addRow(data);
- }
- return newCursor;
- }
-
private static final class DirectoryQuery {
public static final String[] COLUMNS = new String[] {
Directory._ID,
@@ -4971,21 +4795,11 @@
}
}
- private boolean hasColumn(String[] projection, String column) {
- if (projection == null) {
- return true; // Null projection means "all columns".
- }
-
- for (int i = 0; i < projection.length; i++) {
- if (column.equalsIgnoreCase(projection[i])) return true;
- }
- return false;
- }
-
protected Cursor queryLocal(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, long directoryId) {
+ String[] selectionArgs, String sortOrder, long directoryId,
+ CancelationSignal cancelationSignal) {
if (VERBOSE_LOGGING) {
- Log.v(TAG, "query: " + uri);
+ Log.v(TAG, "query=" + uri + " selection=" + selection);
}
// Default active DB to the contacts DB if none has been set.
@@ -5010,7 +4824,7 @@
case CONTACTS: {
setTablesAndProjectionMapForContacts(qb, uri, projection);
- appendLocalDirectorySelectionIfNeeded(qb, directoryId);
+ appendLocalDirectoryAndAccountSelectionIfNeeded(qb, directoryId, uri);
break;
}
@@ -5039,7 +4853,8 @@
Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
projection, selection, selectionArgs, sortOrder, groupBy, limit,
- Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
+ Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey,
+ cancelationSignal);
if (c != null) {
return c;
}
@@ -5073,7 +4888,8 @@
lookupQb.appendWhere(" AND ");
Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
projection, selection, selectionArgs, sortOrder, groupBy, limit,
- Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
+ Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey,
+ cancelationSignal);
if (c != null) {
return c;
}
@@ -5116,7 +4932,8 @@
Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
projection, selection, selectionArgs, sortOrder, groupBy, limit,
StreamItems.CONTACT_ID, contactId,
- StreamItems.CONTACT_LOOKUP_KEY, lookupKey);
+ StreamItems.CONTACT_LOOKUP_KEY, lookupKey,
+ cancelationSignal);
if (c != null) {
return c;
}
@@ -5156,11 +4973,14 @@
if (uri.getPathSegments().size() > 2) {
filterParam = uri.getLastPathSegment();
}
+
+ // If the query consists of a single word, we can do snippetizing after-the-fact for
+ // a performance boost. Otherwise, we can't defer.
+ snippetDeferred = isSingleWordQuery(filterParam)
+ && deferredSnipRequested && snippetNeeded(projection);
setTablesAndProjectionMapForContactsWithSnippet(
qb, uri, projection, filterParam, directoryId,
- deferredSnipRequested);
- snippetDeferred = isSingleWordQuery(filterParam) &&
- deferredSnipRequested && snippetNeeded(projection);
+ snippetDeferred);
break;
}
@@ -5370,7 +5190,8 @@
Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
projection, selection, selectionArgs, sortOrder, groupBy, limit,
Contacts.Entity.CONTACT_ID, contactId,
- Contacts.Entity.LOOKUP_KEY, lookupKey);
+ Contacts.Entity.LOOKUP_KEY, lookupKey,
+ cancelationSignal);
if (c != null) {
return c;
}
@@ -5433,10 +5254,19 @@
return cursor;
}
- case PHONES: {
+ case PHONES:
+ case CALLABLES: {
+ final String mimeTypeIsPhoneExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
+ final String mimeTypeIsSipExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
setTablesAndProjectionMapForData(qb, uri, projection, false);
- qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + "=" +
- mDbHelper.get().getMimeTypeIdForPhone());
+ if (match == CALLABLES) {
+ qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
+ ") OR (" + mimeTypeIsSipExpression + "))");
+ } else {
+ qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
+ }
final boolean removeDuplicates = readBooleanQueryParameter(
uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
@@ -5456,31 +5286,50 @@
break;
}
- case PHONES_ID: {
+ case PHONES_ID:
+ case CALLABLES_ID: {
+ final String mimeTypeIsPhoneExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
+ final String mimeTypeIsSipExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
setTablesAndProjectionMapForData(qb, uri, projection, false);
selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
- qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
- + mDbHelper.get().getMimeTypeIdForPhone());
+ if (match == CALLABLES_ID) {
+ qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
+ ") OR (" + mimeTypeIsSipExpression + "))");
+ } else {
+ qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
+ }
qb.appendWhere(" AND " + Data._ID + "=?");
break;
}
- case PHONES_FILTER: {
+ case PHONES_FILTER:
+ case CALLABLES_FILTER: {
+ final String mimeTypeIsPhoneExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
+ final String mimeTypeIsSipExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
+
String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
Integer typeInt = sDataUsageTypeMap.get(typeParam);
if (typeInt == null) {
typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
}
setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
- qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
- + mDbHelper.get().getMimeTypeIdForPhone());
+ if (match == CALLABLES_FILTER) {
+ qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
+ ") OR (" + mimeTypeIsSipExpression + "))");
+ } else {
+ qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
+ }
+
if (uri.getPathSegments().size() > 2) {
String filterParam = uri.getLastPathSegment();
StringBuilder sb = new StringBuilder();
sb.append(" AND (");
boolean hasCondition = false;
- boolean orNeeded = false;
final String ftsMatchQuery = SearchIndexManager.getFtsMatchQuery(
filterParam, FtsQueryBuilder.UNSCOPED_NORMALIZING);
if (ftsMatchQuery.length() > 0) {
@@ -5493,13 +5342,12 @@
" WHERE " + SearchIndexColumns.NAME + " MATCH '");
sb.append(ftsMatchQuery);
sb.append("')");
- orNeeded = true;
hasCondition = true;
}
String number = PhoneNumberUtils.normalizeNumber(filterParam);
if (!TextUtils.isEmpty(number)) {
- if (orNeeded) {
+ if (hasCondition) {
sb.append(" OR ");
}
sb.append(Data._ID +
@@ -5511,6 +5359,23 @@
hasCondition = true;
}
+ if (!TextUtils.isEmpty(filterParam) && match == CALLABLES_FILTER) {
+ // If the request is via Callable uri, Sip addresses matching the filter
+ // parameter should be returned.
+ if (hasCondition) {
+ sb.append(" OR ");
+ }
+ sb.append("(");
+ sb.append(mimeTypeIsSipExpression);
+ sb.append(" AND ((" + Data.DATA1 + " LIKE ");
+ DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
+ sb.append(") OR (" + Data.DATA1 + " LIKE ");
+ // Users may want SIP URIs starting from "sip:"
+ DatabaseUtils.appendEscapedSQLString(sb, "sip:"+ filterParam + '%');
+ sb.append(")))");
+ hasCondition = true;
+ }
+
if (!hasCondition) {
// If it is neither a phone number nor a name, the query should return
// an empty cursor. Let's ensure that.
@@ -5519,9 +5384,21 @@
sb.append(")");
qb.appendWhere(sb);
}
- groupBy = "(CASE WHEN " + PhoneColumns.NORMALIZED_NUMBER
- + " IS NOT NULL THEN " + PhoneColumns.NORMALIZED_NUMBER
+ if (match == CALLABLES_FILTER) {
+ // If the row is for a phone number that has a normalized form, we should use
+ // the normalized one as PHONES_FILTER does, while we shouldn't do that
+ // if the row is for a sip address.
+ String isPhoneAndHasNormalized = "("
+ + mimeTypeIsPhoneExpression + " AND "
+ + Phone.NORMALIZED_NUMBER + " IS NOT NULL)";
+ groupBy = "(CASE WHEN " + isPhoneAndHasNormalized
+ + " THEN " + Phone.NORMALIZED_NUMBER
+ " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
+ } else {
+ groupBy = "(CASE WHEN " + Phone.NORMALIZED_NUMBER
+ + " IS NOT NULL THEN " + Phone.NORMALIZED_NUMBER
+ + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
+ }
if (sortOrder == null) {
final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
@@ -5680,9 +5557,9 @@
break;
}
- case RAW_CONTACTS_DATA:
+ case RAW_CONTACTS_ID_DATA:
case PROFILE_RAW_CONTACTS_ID_DATA: {
- int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
+ int segment = match == RAW_CONTACTS_ID_DATA ? 1 : 2;
long rawContactId = Long.parseLong(uri.getPathSegments().get(segment));
setTablesAndProjectionMapForData(qb, uri, projection, false);
selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
@@ -5777,7 +5654,7 @@
qb.setStrict(true);
boolean foundResult = false;
Cursor cursor = query(mActiveDb.get(), qb, projection, selection, selectionArgs,
- sortOrder, groupBy, limit);
+ sortOrder, groupBy, limit, cancelationSignal);
try {
if (cursor.getCount() > 0) {
foundResult = true;
@@ -5801,7 +5678,7 @@
case GROUPS: {
qb.setTables(Views.GROUPS);
qb.setProjectionMap(sGroupsProjectionMap);
- appendAccountFromParameter(qb, uri);
+ appendAccountIdFromParameter(qb, uri);
break;
}
@@ -5814,18 +5691,19 @@
}
case GROUPS_SUMMARY: {
- final boolean returnGroupCountPerAccount =
- readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
- false);
String tables = Views.GROUPS + " AS " + Tables.GROUPS;
- if (hasColumn(projection, Groups.SUMMARY_COUNT)) {
+ if (ContactsDatabaseHelper.isInProjection(projection, Groups.SUMMARY_COUNT)) {
tables = tables + Joins.GROUP_MEMBER_COUNT;
}
+ if (ContactsDatabaseHelper.isInProjection(projection,
+ Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT)) {
+ // TODO Add join for this column too (and update the projection map)
+ // TODO Also remove Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT when it works.
+ Log.w(TAG, Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT + " is not supported yet");
+ }
qb.setTables(tables);
- qb.setProjectionMap(returnGroupCountPerAccount ?
- sGroupsSummaryProjectionMapWithGroupCountPerAccount
- : sGroupsSummaryProjectionMap);
- appendAccountFromParameter(qb, uri);
+ qb.setProjectionMap(sGroupsSummaryProjectionMap);
+ appendAccountIdFromParameter(qb, uri);
groupBy = GroupsColumns.CONCRETE_ID;
break;
}
@@ -5925,7 +5803,7 @@
break;
}
- case RAW_CONTACT_ENTITY_ID: {
+ case RAW_CONTACT_ID_ENTITY: {
long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
setTablesAndProjectionMapForRawEntities(qb, uri);
selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
@@ -5965,26 +5843,28 @@
Cursor cursor =
query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
- limit);
+ limit, cancelationSignal);
+
if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
cursor = bundleLetterCountExtras(cursor, mActiveDb.get(), qb, selection,
- selectionArgs, sortOrder, addressBookIndexerCountExpression);
+ selectionArgs, sortOrder, addressBookIndexerCountExpression, cancelationSignal);
}
if (snippetDeferred) {
cursor = addDeferredSnippetingExtra(cursor);
}
+
return cursor;
}
private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
String selection, String[] selectionArgs, String sortOrder, String groupBy,
- String limit) {
+ String limit, CancelationSignal cancelationSignal) {
if (projection != null && projection.length == 1
&& BaseColumns._COUNT.equals(projection[0])) {
qb.setProjectionMap(sCountProjectionMap);
}
final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
- sortOrder, limit);
+ sortOrder, limit, cancelationSignal);
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
}
@@ -6011,12 +5891,14 @@
* Runs the query with the supplied contact ID and lookup ID. If the query succeeds,
* it returns the resulting cursor, otherwise it returns null and the calling
* method needs to resolve the lookup key and rerun the query.
+ * @param cancelationSignal
*/
private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
SQLiteDatabase db, Uri uri,
String[] projection, String selection, String[] selectionArgs,
String sortOrder, String groupBy, String limit,
- String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
+ String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey,
+ CancelationSignal cancelationSignal) {
String[] args;
if (selectionArgs == null) {
args = new String[2];
@@ -6028,7 +5910,7 @@
args[1] = Uri.encode(lookupKey);
lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
- groupBy, limit);
+ groupBy, limit, cancelationSignal);
if (c.getCount() != 0) {
return c;
}
@@ -6062,7 +5944,7 @@
*/
private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder,
- String countExpression) {
+ String countExpression, CancelationSignal cancelationSignal) {
if (!(cursor instanceof AbstractCursor)) {
Log.w(TAG, "Unable to bundle extras. Cursor is not AbstractCursor.");
return cursor;
@@ -6113,7 +5995,8 @@
Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
AddressBookIndexQuery.ORDER_BY, null /* having */,
- AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
+ AddressBookIndexQuery.ORDER_BY + sortOrderSuffix,
+ null, cancelationSignal);
try {
int groupCount = indexCursor.getCount();
@@ -6477,7 +6360,7 @@
* contact and joins that with other contacts tables.
*/
private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
- String[] projection, String filter, long directoryId, boolean deferredSnippeting) {
+ String[] projection, String filter, long directoryId, boolean deferSnippeting) {
StringBuilder sb = new StringBuilder();
sb.append(Views.CONTACTS);
@@ -6489,7 +6372,7 @@
if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
} else {
- appendSearchIndexJoin(sb, uri, projection, filter, deferredSnippeting);
+ appendSearchIndexJoin(sb, uri, projection, filter, deferSnippeting);
}
appendContactPresenceJoin(sb, projection, Contacts._ID);
appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
@@ -6499,7 +6382,7 @@
private void appendSearchIndexJoin(
StringBuilder sb, Uri uri, String[] projection, String filter,
- boolean deferredSnippeting) {
+ boolean deferSnippeting) {
if (snippetNeeded(projection)) {
String[] args = null;
@@ -6520,7 +6403,7 @@
appendSearchIndexJoin(
sb, filter, true, startMatch, endMatch, ellipsis, maxTokens,
- deferredSnippeting);
+ deferSnippeting);
} else {
appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
}
@@ -6528,16 +6411,13 @@
public void appendSearchIndexJoin(StringBuilder sb, String filter,
boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
- int maxTokens, boolean deferredSnippeting) {
+ int maxTokens, boolean deferSnippeting) {
boolean isEmailAddress = false;
String emailAddress = null;
boolean isPhoneNumber = false;
String phoneNumber = null;
String numberE164 = null;
- // If the query consists of a single word, we can do snippetizing after-the-fact for a
- // performance boost.
- boolean singleTokenSearch = isSingleWordQuery(filter);
if (filter.indexOf('@') != -1) {
emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
@@ -6557,18 +6437,24 @@
sb.append(", ");
if (isEmailAddress) {
sb.append("ifnull(");
- DatabaseUtils.appendEscapedSQLString(sb, startMatch);
- sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
+ if (!deferSnippeting) {
+ // Add the snippet marker only when we're really creating snippet.
+ DatabaseUtils.appendEscapedSQLString(sb, startMatch);
+ sb.append("||");
+ }
+ sb.append("(SELECT MIN(" + Email.ADDRESS + ")");
sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
sb.append(" WHERE " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
- sb.append(")||");
- DatabaseUtils.appendEscapedSQLString(sb, endMatch);
+ sb.append(")");
+ if (!deferSnippeting) {
+ sb.append("||");
+ DatabaseUtils.appendEscapedSQLString(sb, endMatch);
+ }
sb.append(",");
- // Optimization for single-token search (do only if requested).
- if (singleTokenSearch && deferredSnippeting) {
+ if (deferSnippeting) {
sb.append(SearchIndexColumns.CONTENT);
} else {
appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
@@ -6576,8 +6462,12 @@
sb.append(")");
} else if (isPhoneNumber) {
sb.append("ifnull(");
- DatabaseUtils.appendEscapedSQLString(sb, startMatch);
- sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
+ if (!deferSnippeting) {
+ // Add the snippet marker only when we're really creating snippet.
+ DatabaseUtils.appendEscapedSQLString(sb, startMatch);
+ sb.append("||");
+ }
+ sb.append("(SELECT MIN(" + Phone.NUMBER + ")");
sb.append(" FROM " +
Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
sb.append(" ON " + DataColumns.CONCRETE_ID);
@@ -6592,12 +6482,14 @@
sb.append(numberE164);
sb.append("%'");
}
- sb.append(")||");
- DatabaseUtils.appendEscapedSQLString(sb, endMatch);
+ sb.append(")");
+ if (! deferSnippeting) {
+ sb.append("||");
+ DatabaseUtils.appendEscapedSQLString(sb, endMatch);
+ }
sb.append(",");
- // Optimization for single-token search (do only if requested).
- if (singleTokenSearch && deferredSnippeting) {
+ if (deferSnippeting) {
sb.append(SearchIndexColumns.CONTENT);
} else {
appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
@@ -6606,8 +6498,7 @@
} else {
final String normalizedFilter = NameNormalizer.normalize(filter);
if (!TextUtils.isEmpty(normalizedFilter)) {
- // Optimization for single-token search (do only if requested)..
- if (singleTokenSearch && deferredSnippeting) {
+ if (deferSnippeting) {
sb.append(SearchIndexColumns.CONTENT);
} else {
sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
@@ -6696,13 +6587,13 @@
sb.append(Views.RAW_CONTACTS);
qb.setTables(sb.toString());
qb.setProjectionMap(sRawContactsProjectionMap);
- appendAccountFromParameter(qb, uri);
+ appendAccountIdFromParameter(qb, uri);
}
private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
qb.setTables(Views.RAW_ENTITIES);
qb.setProjectionMap(sRawEntityProjectionMap);
- appendAccountFromParameter(qb, uri);
+ appendAccountIdFromParameter(qb, uri);
}
private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
@@ -6754,7 +6645,7 @@
}
qb.setProjectionMap(projectionMap);
- appendAccountFromParameter(qb, uri);
+ appendAccountIdFromParameter(qb, uri);
}
private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
@@ -6801,7 +6692,7 @@
qb.setTables(sb.toString());
qb.setProjectionMap(sEntityProjectionMap);
- appendAccountFromParameter(qb, uri);
+ appendAccountIdFromParameter(qb, uri);
}
private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
@@ -6857,50 +6748,83 @@
}
}
- private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
+ private void appendLocalDirectoryAndAccountSelectionIfNeeded(SQLiteQueryBuilder qb,
+ long directoryId, Uri uri) {
+ final StringBuilder sb = new StringBuilder();
if (directoryId == Directory.DEFAULT) {
- qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
- return true;
+ sb.append("(" + Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY + ")");
} else if (directoryId == Directory.LOCAL_INVISIBLE){
- qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
- return true;
+ sb.append("(" + Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY + ")");
+ } else {
+ sb.append("(1)");
}
- return false;
+
+ final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
+ // Accounts are valid by only checking one parameter, since we've
+ // already ruled out partial accounts.
+ final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
+ if (validAccount) {
+ final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
+ if (accountId == null) {
+ // No such account.
+ sb.setLength(0);
+ sb.append("(1=2)");
+ } else {
+ sb.append(
+ " AND (" + Contacts._ID + " IN (" +
+ "SELECT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + RawContactsColumns.ACCOUNT_ID + "=" + accountId.toString() +
+ "))");
+ }
+ }
+ qb.appendWhere(sb.toString());
}
private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
- final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
- final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
- final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
-
- final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
- if (partialUri) {
- // Throw when either account is incomplete
- throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
- "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
- }
+ final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
// Accounts are valid by only checking one parameter, since we've
// already ruled out partial accounts.
- final boolean validAccount = !TextUtils.isEmpty(accountName);
+ final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
if (validAccount) {
- String toAppend = RawContacts.ACCOUNT_NAME + "="
- + DatabaseUtils.sqlEscapeString(accountName) + " AND "
+ String toAppend = "(" + RawContacts.ACCOUNT_NAME + "="
+ + DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountName()) + " AND "
+ RawContacts.ACCOUNT_TYPE + "="
- + DatabaseUtils.sqlEscapeString(accountType);
- if (dataSet == null) {
+ + DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountType());
+ if (accountWithDataSet.getDataSet() == null) {
toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
} else {
toAppend += " AND " + RawContacts.DATA_SET + "=" +
- DatabaseUtils.sqlEscapeString(dataSet);
+ DatabaseUtils.sqlEscapeString(accountWithDataSet.getDataSet());
}
+ toAppend += ")";
qb.appendWhere(toAppend);
} else {
qb.appendWhere("1");
}
}
- private String appendAccountToSelection(Uri uri, String selection) {
+ private void appendAccountIdFromParameter(SQLiteQueryBuilder qb, Uri uri) {
+ final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
+
+ // Accounts are valid by only checking one parameter, since we've
+ // already ruled out partial accounts.
+ final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
+ if (validAccount) {
+ final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
+ if (accountId == null) {
+ // No such account.
+ qb.appendWhere("(1=2)");
+ } else {
+ qb.appendWhere(
+ "(" + RawContactsColumns.ACCOUNT_ID + "=" + accountId.toString() + ")");
+ }
+ } else {
+ qb.appendWhere("1");
+ }
+ }
+
+ private AccountWithDataSet getAccountWithDataSetFromUri(Uri uri) {
final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
@@ -6911,20 +6835,25 @@
throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
"Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
}
+ return AccountWithDataSet.get(accountName, accountType, dataSet);
+ }
+
+ private String appendAccountToSelection(Uri uri, String selection) {
+ final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
// Accounts are valid by only checking one parameter, since we've
// already ruled out partial accounts.
- final boolean validAccount = !TextUtils.isEmpty(accountName);
+ final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
if (validAccount) {
- StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
- + DatabaseUtils.sqlEscapeString(accountName) + " AND "
- + RawContacts.ACCOUNT_TYPE + "="
- + DatabaseUtils.sqlEscapeString(accountType));
- if (dataSet == null) {
+ StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "=");
+ selectionSb.append(DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountName()));
+ selectionSb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
+ selectionSb.append(DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountType()));
+ if (accountWithDataSet.getDataSet() == null) {
selectionSb.append(" AND " + RawContacts.DATA_SET + " IS NULL");
} else {
selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
- .append(DatabaseUtils.sqlEscapeString(dataSet));
+ .append(DatabaseUtils.sqlEscapeString(accountWithDataSet.getDataSet()));
}
if (!TextUtils.isEmpty(selection)) {
selectionSb.append(" AND (");
@@ -6937,6 +6866,34 @@
}
}
+ private String appendAccountIdToSelection(Uri uri, String selection) {
+ final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
+
+ // Accounts are valid by only checking one parameter, since we've
+ // already ruled out partial accounts.
+ final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
+ if (validAccount) {
+ final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
+ if (accountId == null) {
+ // No such account in the accounts table. This means, there's no rows to be
+ // selected.
+ return "(1=2)";
+ }
+ final StringBuilder selectionSb = new StringBuilder(
+ RawContactsColumns.ACCOUNT_ID + "=");
+ selectionSb.append(Long.toString(accountId));
+
+ if (!TextUtils.isEmpty(selection)) {
+ selectionSb.append(" AND (");
+ selectionSb.append(selection);
+ selectionSb.append(')');
+ }
+ return selectionSb.toString();
+ } else {
+ return selection;
+ }
+ }
+
/**
* Gets the value of the "limit" URI query parameter.
*
@@ -7068,7 +7025,7 @@
setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
projection, null, null, null, null, null,
- Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
+ Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey, null);
if (c != null) {
try {
c.moveToFirst();
@@ -7142,7 +7099,7 @@
}
}
- case DISPLAY_PHOTO: {
+ case DISPLAY_PHOTO_ID: {
long photoFileId = ContentUris.parseId(uri);
if (!mode.equals("r")) {
throw new IllegalArgumentException(
@@ -7463,7 +7420,7 @@
case CONTACTS_LOOKUP_DISPLAY_PHOTO:
case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
case RAW_CONTACTS_ID_DISPLAY_PHOTO:
- case DISPLAY_PHOTO:
+ case DISPLAY_PHOTO_ID:
return "image/jpeg";
case RAW_CONTACTS:
case PROFILE_RAW_CONTACTS:
@@ -7612,19 +7569,35 @@
" IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
}
- public boolean isPhoneNumber(String filter) {
- boolean atLeastOneDigit = false;
- int len = filter.length();
+ public boolean isPhoneNumber(String query) {
+ if (TextUtils.isEmpty(query)) {
+ return false;
+ }
+ // assume a phone number if it has at least 1 digit
+ return countPhoneNumberDigits(query) > 0;
+ }
+
+ /**
+ * Returns the number of digitis in a phone number ignoring special characters such as '-'.
+ * If the string is not a valid phone number, 0 is returned.
+ */
+ public static int countPhoneNumberDigits(String query) {
+ int numDigits = 0;
+ int len = query.length();
for (int i = 0; i < len; i++) {
- char c = filter.charAt(i);
- if (c >= '0' && c <= '9') {
- atLeastOneDigit = true;
- } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
- && c != '-' && c != '(' && c != ')' && c != ' ') {
- return false;
+ char c = query.charAt(i);
+ if (Character.isDigit(c)) {
+ numDigits ++;
+ } else if (c == '*' || c == '#' || c == 'N' || c == '.' || c == ';'
+ || c == '-' || c == '(' || c == ')' || c == ' ') {
+ // carry on
+ } else if (c == '+' && numDigits == 0) {
+ // plus before any digits is ok
+ } else {
+ return 0; // not a phone number
}
}
- return atLeastOneDigit;
+ return numDigits;
}
/**
@@ -7836,14 +7809,11 @@
}
int version = Integer.parseInt(mContactsHelper.getProperty(
- PROPERTY_AGGREGATION_ALGORITHM, "1"));
+ DbProperties.AGGREGATION_ALGORITHM, "1"));
return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
}
protected void upgradeAggregationAlgorithmInBackground() {
- // This upgrade will affect very few contacts, so it can be performed on the
- // main thread during the initial boot after an OTA
-
Log.i(TAG, "Upgrading aggregation algorithm");
int count = 0;
long start = SystemClock.currentThreadTimeMillis();
@@ -7858,9 +7828,8 @@
new String[]{"r1." + RawContacts._ID},
"r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
" AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
- " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
- " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
- " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
+ " AND r1." + RawContactsColumns.ACCOUNT_ID +
+ "=r2." + RawContactsColumns.ACCOUNT_ID,
null, null, null, null, null);
try {
while (cursor.moveToNext()) {
@@ -7875,7 +7844,7 @@
mContactAggregator.aggregateInTransaction(mTransactionContext.get(), db);
updateSearchIndexInTransaction();
db.setTransactionSuccessful();
- mContactsHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
+ mContactsHelper.setProperty(DbProperties.AGGREGATION_ALGORITHM,
String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
} finally {
if (db != null) {
@@ -7887,13 +7856,27 @@
}
}
- /* Visible for testing */
+ @VisibleForTesting
boolean isPhone() {
- if (!sIsPhoneInitialized) {
- sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
- sIsPhoneInitialized = true;
+ if (!mIsPhoneInitialized) {
+ mIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
+ mIsPhoneInitialized = true;
}
- return sIsPhone;
+ return mIsPhone;
+ }
+
+ boolean isVoiceCapable() {
+ // this copied from com.android.phone.PhoneApp.onCreate():
+
+ // "voice capable" flag.
+ // This flag currently comes from a resource (which is
+ // overrideable on a per-product basis):
+ return getContext().getResources()
+ .getBoolean(com.android.internal.R.bool.config_voice_capable);
+ // ...but this might eventually become a PackageManager "system
+ // feature" instead, in which case we'd do something like:
+ // return
+ // getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
}
private boolean handleDataUsageFeedback(Uri uri) {
@@ -8043,6 +8026,14 @@
* @return a boolean indicating if a snippet is needed or not.
*/
private boolean snippetNeeded(String [] projection) {
- return mDbHelper.get().isInProjection(projection, SearchSnippetColumns.SNIPPET);
+ return ContactsDatabaseHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET);
+ }
+
+ /**
+ * @return the currently active {@link ContactsDatabaseHelper} for the current thread.
+ */
+ @NeededForTesting
+ public ContactsDatabaseHelper getThreadActiveDatabaseHelperForTest() {
+ return mDbHelper.get();
}
}
diff --git a/src/com/android/providers/contacts/CrossProcessCursorWrapper.java b/src/com/android/providers/contacts/CrossProcessCursorWrapper.java
deleted file mode 100644
index 76baa96..0000000
--- a/src/com/android/providers/contacts/CrossProcessCursorWrapper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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
- */
-
-package com.android.providers.contacts;
-
-import android.database.CrossProcessCursor;
-import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.CursorWrapper;
-
-/**
- * Cursor wrapper that implements {@link CrossProcessCursor}, but will only behave as such if the
- * cursor it is wrapping is itself a {@link CrossProcessCursor} or another wrapper around the same.
- */
-public class CrossProcessCursorWrapper extends CursorWrapper implements CrossProcessCursor {
-
- // The cross process cursor. Only non-null if the wrapped cursor was a cross-process cursor.
- private final CrossProcessCursor mCrossProcessCursor;
-
- public CrossProcessCursorWrapper(Cursor cursor) {
- super(cursor);
- mCrossProcessCursor = getCrossProcessCursor(cursor);
- }
-
- private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
- if (cursor instanceof CrossProcessCursor) {
- return (CrossProcessCursor) cursor;
- } else if (cursor instanceof CursorWrapper) {
- return getCrossProcessCursor(((CursorWrapper) cursor).getWrappedCursor());
- } else {
- return null;
- }
- }
-
- @Override
- public void fillWindow(int pos, CursorWindow window) {
- if (mCrossProcessCursor != null) {
- mCrossProcessCursor.fillWindow(pos, window);
- } else {
- throw new UnsupportedOperationException("Wrapped cursor is not a cross-process cursor");
- }
- }
-
- @Override
- public CursorWindow getWindow() {
- if (mCrossProcessCursor != null) {
- return mCrossProcessCursor.getWindow();
- } else {
- throw new UnsupportedOperationException("Wrapped cursor is not a cross-process cursor");
- }
- }
-
- @Override
- public boolean onMove(int oldPosition, int newPosition) {
- if (mCrossProcessCursor != null) {
- return mCrossProcessCursor.onMove(oldPosition, newPosition);
- } else {
- throw new UnsupportedOperationException("Wrapped cursor is not a cross-process cursor");
- }
- }
-
-}
diff --git a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
index 1b7e475..542e5a0 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
@@ -15,10 +15,11 @@
*/
package com.android.providers.contacts;
-import com.android.internal.util.Objects;
import com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Projections;
+import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import com.android.providers.contacts.ContactsProvider2.GroupIdCacheEntry;
@@ -30,7 +31,6 @@
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
-import android.text.TextUtils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -45,15 +45,11 @@
String[] COLUMNS = new String[] {
RawContacts.DELETED,
- RawContacts.ACCOUNT_TYPE,
- RawContacts.ACCOUNT_NAME,
- RawContacts.DATA_SET,
+ RawContactsColumns.ACCOUNT_ID,
};
int DELETED = 0;
- int ACCOUNT_TYPE = 1;
- int ACCOUNT_NAME = 2;
- int DATA_SET = 3;
+ int ACCOUNT_ID = 1;
}
private static final String SELECTION_RAW_CONTACT_ID = RawContacts._ID + "=?";
@@ -170,7 +166,7 @@
if (containsGroupSourceId) {
final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
- txContext.getAccountWithDataSetForRawContact(rawContactId));
+ txContext.getAccountIdOrNullForRawContact(rawContactId));
values.remove(GroupMembership.GROUP_SOURCE_ID);
values.put(GroupMembership.GROUP_ROW_ID, groupId);
}
@@ -178,41 +174,37 @@
/**
* Returns the group id of the group with sourceId and the same account as rawContactId.
- * If the group doesn't already exist then it is first created,
+ * If the group doesn't already exist then it is first created.
+ *
* @param db SQLiteDatabase to use for this operation
- * @param rawContactId the contact this group is associated with
- * @param sourceId the sourceIf of the group to query or create
+ * @param rawContactId the raw contact this group is associated with
+ * @param sourceId the source ID of the group to query or create
+ * @param accountIdOrNull the account ID for the raw contact. If null it'll be queried from
+ * the raw_contacts table.
* @return the group id of the existing or created group
* @throws IllegalArgumentException if the contact is not associated with an account
* @throws IllegalStateException if a group needs to be created but the creation failed
*/
private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
- AccountWithDataSet accountWithDataSet) {
+ Long accountIdOrNull) {
- if (accountWithDataSet == null) {
+ if (accountIdOrNull == null) {
mSelectionArgs1[0] = String.valueOf(rawContactId);
Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
- RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
+ RawContactsColumns.CONCRETE_ID + "=?", mSelectionArgs1, null, null, null);
try {
if (c.moveToFirst()) {
- String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
- String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
- String dataSet = c.getString(RawContactsQuery.DATA_SET);
- if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
- accountWithDataSet = new AccountWithDataSet(
- accountName, accountType, dataSet);
- }
+ accountIdOrNull = c.getLong(RawContactsQuery.ACCOUNT_ID);
}
} finally {
c.close();
}
}
- if (accountWithDataSet == null) {
- throw new IllegalArgumentException("if the groupmembership only "
- + "has a sourceid the the contact must be associated with "
- + "an account");
+ if (accountIdOrNull == null) {
+ throw new IllegalArgumentException("Raw contact not found for _ID=" + rawContactId);
}
+ final long accountId = accountIdOrNull;
ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
if (entries == null) {
@@ -223,51 +215,30 @@
int count = entries.size();
for (int i = 0; i < count; i++) {
GroupIdCacheEntry entry = entries.get(i);
- if (entry.accountName.equals(accountWithDataSet.getAccountName())
- && entry.accountType.equals(accountWithDataSet.getAccountType())
- && Objects.equal(entry.dataSet, accountWithDataSet.getDataSet())) {
+ if (entry.accountId == accountId) {
return entry.groupId;
}
}
GroupIdCacheEntry entry = new GroupIdCacheEntry();
- entry.accountName = accountWithDataSet.getAccountName();
- entry.accountType = accountWithDataSet.getAccountType();
- entry.dataSet = accountWithDataSet.getDataSet();
+ entry.accountId = accountId;
entry.sourceId = sourceId;
entries.add(0, entry);
- // look up the group that contains this sourceId and has the same account name, type, and
- // data set as the contact refered to by rawContactId
- Cursor c;
- if (accountWithDataSet.getDataSet() == null) {
- c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
+ // look up the group that contains this sourceId and has the same account as the contact
+ // referred to by rawContactId
+ Cursor c = db.query(Tables.GROUPS, Projections.ID,
Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
- new String[]{
- sourceId,
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType()
- }, null, null, null);
- } else {
- c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
- Clauses.GROUP_HAS_ACCOUNT_AND_DATA_SET_AND_SOURCE_ID,
- new String[]{
- sourceId,
- accountWithDataSet.getAccountName(),
- accountWithDataSet.getAccountType(),
- accountWithDataSet.getDataSet()
- }, null, null, null);
- }
+ new String[]{sourceId, Long.toString(accountId)}, null, null, null);
+
try {
if (c.moveToFirst()) {
entry.groupId = c.getLong(0);
} else {
ContentValues groupValues = new ContentValues();
- groupValues.put(Groups.ACCOUNT_NAME, accountWithDataSet.getAccountName());
- groupValues.put(Groups.ACCOUNT_TYPE, accountWithDataSet.getAccountType());
- groupValues.put(Groups.DATA_SET, accountWithDataSet.getDataSet());
+ groupValues.put(GroupsColumns.ACCOUNT_ID, accountId);
groupValues.put(Groups.SOURCE_ID, sourceId);
- long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
+ long groupId = db.insert(Tables.GROUPS, null, groupValues);
if (groupId < 0) {
throw new IllegalStateException("unable to create a new group with "
+ "this sourceid: " + groupValues);
diff --git a/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java b/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
index 008d9cf..88e432e 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
@@ -15,7 +15,6 @@
*/
package com.android.providers.contacts;
-import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
@@ -48,7 +47,7 @@
String numberE164 =
PhoneNumberUtils.formatNumberToE164(number, mDbHelper.getCurrentCountryIso());
if (numberE164 != null) {
- values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
+ values.put(Phone.NORMALIZED_NUMBER, numberE164);
}
dataId = super.insert(db, txContext, rawContactId, values);
@@ -76,7 +75,7 @@
mDbHelper.getCurrentCountryIso());
}
if (numberE164 != null) {
- values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
+ values.put(Phone.NORMALIZED_NUMBER, numberE164);
}
}
diff --git a/src/com/android/providers/contacts/GlobalSearchSupport.java b/src/com/android/providers/contacts/GlobalSearchSupport.java
index 5d4d273..2acae60 100644
--- a/src/com/android/providers/contacts/GlobalSearchSupport.java
+++ b/src/com/android/providers/contacts/GlobalSearchSupport.java
@@ -17,10 +17,13 @@
package com.android.providers.contacts;
import android.app.SearchManager;
+import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
+import android.location.Country;
+import android.location.CountryDetector;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
@@ -30,40 +33,30 @@
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.SearchSnippetColumns;
import android.provider.ContactsContract.StatusUpdates;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
-import com.android.i18n.phonenumbers.NumberParseException;
import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import com.android.providers.contacts.ContactsDatabaseHelper.Views;
import java.util.ArrayList;
-import java.util.Locale;
/**
* Support for global search integration for Contacts.
*/
public class GlobalSearchSupport {
- private static final String[] SEARCH_SUGGESTIONS_BASED_ON_PHONE_NUMBER_COLUMNS = {
- "_id",
- SearchManager.SUGGEST_COLUMN_TEXT_1,
- SearchManager.SUGGEST_COLUMN_TEXT_2,
- SearchManager.SUGGEST_COLUMN_ICON_1,
- SearchManager.SUGGEST_COLUMN_INTENT_DATA,
- SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
- SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
- };
-
- private static final String[] SEARCH_SUGGESTIONS_BASED_ON_NAME_COLUMNS = {
+ private static final String[] SEARCH_SUGGESTIONS_COLUMNS = {
"_id",
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_ICON_1,
SearchManager.SUGGEST_COLUMN_ICON_2,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+ SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA,
SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT,
@@ -113,15 +106,19 @@
String text2;
String icon1;
String icon2;
+ String intentData;
+ String intentAction;
String filter;
String lastAccessTime;
@SuppressWarnings({"unchecked"})
public ArrayList asList(String[] projection) {
- if (photoUri != null) {
- icon1 = photoUri.toString();
- } else {
- icon1 = String.valueOf(com.android.internal.R.drawable.ic_contact_picture);
+ if (icon1 == null) {
+ if (photoUri != null) {
+ icon1 = photoUri.toString();
+ } else {
+ icon1 = String.valueOf(com.android.internal.R.drawable.ic_contact_picture);
+ }
}
if (presence != -1) {
@@ -130,15 +127,16 @@
ArrayList<Object> list = new ArrayList<Object>();
if (projection == null) {
- list.add(contactId);
- list.add(text1);
- list.add(text2);
- list.add(icon1);
- list.add(icon2);
- list.add(buildUri());
- list.add(lookupKey);
- list.add(filter);
- list.add(lastAccessTime);
+ list.add(contactId); // _id
+ list.add(text1); // text1
+ list.add(text2); // text2
+ list.add(icon1); // icon1
+ list.add(icon2); // icon2
+ list.add(intentData == null ? buildUri() : intentData); // intent data
+ list.add(intentAction); // intentAction
+ list.add(lookupKey); // shortcut id
+ list.add(filter); // extra data
+ list.add(lastAccessTime); // last access hint
} else {
for (int i = 0; i < projection.length; i++) {
addColumnValue(list, projection[i]);
@@ -159,7 +157,7 @@
} else if (SearchManager.SUGGEST_COLUMN_ICON_2.equals(column)) {
list.add(icon2);
} else if (SearchManager.SUGGEST_COLUMN_INTENT_DATA.equals(column)) {
- list.add(buildUri());
+ list.add(intentData == null ? buildUri() : intentData);
} else if (SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID.equals(column)) {
list.add(lookupKey);
} else if (SearchManager.SUGGEST_COLUMN_SHORTCUT_ID.equals(column)) {
@@ -179,12 +177,24 @@
}
private final ContactsProvider2 mContactsProvider;
- private final PhoneNumberUtil mPhoneNumberUtil;
+ private PhoneNumberUtil mPhoneNumberUtil;
+ private CountryDetector mCountryDetector;
+ private String mSimCountryIso;
@SuppressWarnings("all")
public GlobalSearchSupport(ContactsProvider2 contactsProvider) {
mContactsProvider = contactsProvider;
+
mPhoneNumberUtil = PhoneNumberUtil.getInstance();
+ mCountryDetector = (CountryDetector)
+ mContactsProvider.getContext().getSystemService(Context.COUNTRY_DETECTOR);
+ TelephonyManager telman = (TelephonyManager)
+ mContactsProvider.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ // assuming here that the SIM never changes while the phone is booted. ok?
+ mSimCountryIso = telman == null ? null : telman.getSimCountryIso();
+ if (mSimCountryIso != null) {
+ mSimCountryIso = mSimCountryIso.toUpperCase();
+ }
// To ensure the data column position. This is dead code if properly configured.
if (Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
@@ -194,9 +204,39 @@
}
}
+ private boolean isPossibleByPhoneNumberUtil(String query) {
+ String currentCountry = null;
+ Country current = mCountryDetector.detectCountry();
+ if (current != null) {
+ currentCountry = current.getCountryIso().toUpperCase();
+ }
+ if (currentCountry != null && mPhoneNumberUtil.isPossibleNumber(query, currentCountry)) {
+ return true;
+ }
+ if (mSimCountryIso != null && !TextUtils.equals(currentCountry, mSimCountryIso)) {
+ // use the SIM country if it's different, so we can add contacts for home numbers
+ // while roaming
+ return mPhoneNumberUtil.isPossibleNumber(query, mSimCountryIso);
+ }
+ return false;
+ }
+
+ private boolean isPhoneNumber(String query) {
+ if (TextUtils.isEmpty(query)) {
+ return false;
+ }
+ if (ContactsProvider2.countPhoneNumberDigits(query) > 2) {
+ // 3 or more digits matching the basic pattern
+ return true;
+ }
+ // more advanced check, for 1800-FLOWERS style numbers and the like
+ return isPossibleByPhoneNumberUtil(query);
+ }
+
public Cursor handleSearchSuggestionsQuery(
SQLiteDatabase db, Uri uri, String[] projection, String limit) {
final String searchClause;
+
final String selection;
if (uri.getPathSegments().size() <= 1) {
searchClause = null;
@@ -206,16 +246,13 @@
selection = null;
}
- if (mContactsProvider.isPhone() && isPhoneNumber(searchClause)) {
- return buildCursorForSearchSuggestionsBasedOnPhoneNumber(searchClause);
- } else {
- return buildCursorForSearchSuggestionsBasedOnFilter(
- db, projection, selection, searchClause, limit);
+ MatrixCursor cursor = new MatrixCursor(
+ projection == null ? SEARCH_SUGGESTIONS_COLUMNS : projection);
+ addSearchSuggestionsBasedOnFilter(cursor, db, projection, selection, searchClause, limit);
+ if (isPhoneNumber(searchClause)) {
+ addSearchSuggestionsBasedOnPhoneNumber(cursor, searchClause, projection);
}
- }
-
- private boolean isPhoneNumber(String query) {
- return mPhoneNumberUtil.isPossibleNumber(query, Locale.getDefault().getCountry());
+ return cursor;
}
/**
@@ -236,73 +273,61 @@
} catch (IllegalArgumentException e) {
contactId = -1L;
}
- return buildCursorForSearchSuggestionsBasedOnFilter(
+ MatrixCursor cursor = new MatrixCursor(
+ projection == null ? SEARCH_SUGGESTIONS_COLUMNS : projection);
+ return addSearchSuggestionsBasedOnFilter(cursor,
db, projection, ContactsColumns.CONCRETE_ID + "=" + contactId, filter, null);
}
- private boolean isVoiceCapable() {
- // this copied from com.android.phone.PhoneApp.onCreate():
-
- // "voice capable" flag.
- // This flag currently comes from a resource (which is
- // overrideable on a per-product basis):
- return mContactsProvider.getContext().getResources()
- .getBoolean(com.android.internal.R.bool.config_voice_capable);
- // ...but this might eventually become a PackageManager "system
- // feature" instead, in which case we'd do something like:
- // return
- // getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
- }
-
- private Cursor buildCursorForSearchSuggestionsBasedOnPhoneNumber(String searchClause) {
- MatrixCursor cursor = new MatrixCursor(SEARCH_SUGGESTIONS_BASED_ON_PHONE_NUMBER_COLUMNS);
+ private Cursor addSearchSuggestionsBasedOnPhoneNumber(MatrixCursor cursor,
+ String searchClause, String[] projection) {
Resources r = mContactsProvider.getContext().getResources();
String s;
int i;
- if (isVoiceCapable()) {
- ArrayList<Object> dialNumber = new ArrayList<Object>();
- dialNumber.add(0); // _id
+ if (mContactsProvider.isPhone() && mContactsProvider.isVoiceCapable()) {
+ SearchSuggestion dialNumber = new SearchSuggestion();
+ dialNumber.contactId = -1;
s = r.getString(com.android.internal.R.string.dial_number_using, searchClause);
i = s.indexOf('\n');
if (i < 0) {
- dialNumber.add(s);
- dialNumber.add("");
+ dialNumber.text1 = s;
+ dialNumber.text2 = "";
} else {
- dialNumber.add(s.substring(0, i));
- dialNumber.add(s.substring(i + 1));
+ dialNumber.text1 = s.substring(0, i);
+ dialNumber.text2 = s.substring(i + 1);
}
- dialNumber.add(String.valueOf(com.android.internal.R.drawable.call_contact));
- dialNumber.add("tel:" + searchClause);
- dialNumber.add(ContactsContract.Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED);
- dialNumber.add(null);
- cursor.addRow(dialNumber);
+ dialNumber.icon1 = String.valueOf(com.android.internal.R.drawable.call_contact);
+ dialNumber.intentData = "tel:" + searchClause;
+ dialNumber.intentAction =
+ ContactsContract.Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED;
+ dialNumber.lookupKey = SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT; // shortcut id
+ cursor.addRow(dialNumber.asList(projection));
}
- ArrayList<Object> createContact = new ArrayList<Object>();
- createContact.add(1); // _id
+ SearchSuggestion createContact = new SearchSuggestion();
+ createContact.contactId = -2;
s = r.getString(com.android.internal.R.string.create_contact_using, searchClause);
i = s.indexOf('\n');
if (i < 0) {
- createContact.add(s);
- createContact.add("");
+ createContact.text1 = s;
+ createContact.text2 = "";
} else {
- createContact.add(s.substring(0, i));
- createContact.add(s.substring(i + 1));
+ createContact.text1 = s.substring(0, i);
+ createContact.text2 = s.substring(i + 1);
}
- createContact.add(String.valueOf(com.android.internal.R.drawable.create_contact));
- createContact.add("tel:" + searchClause);
- createContact.add(ContactsContract.Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED);
- createContact.add(SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT);
- cursor.addRow(createContact);
+ createContact.icon1 = String.valueOf(com.android.internal.R.drawable.create_contact);
+ createContact.intentData = "tel:" + searchClause;
+ createContact.intentAction =
+ ContactsContract.Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED;
+ createContact.lookupKey = SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT; // shortcut id
+ cursor.addRow(createContact.asList(projection));
return cursor;
}
- private Cursor buildCursorForSearchSuggestionsBasedOnFilter(SQLiteDatabase db,
+ private Cursor addSearchSuggestionsBasedOnFilter(MatrixCursor cursor, SQLiteDatabase db,
String[] projection, String selection, String filter, String limit) {
- MatrixCursor cursor = new MatrixCursor(
- projection != null ? projection : SEARCH_SUGGESTIONS_BASED_ON_NAME_COLUMNS);
StringBuilder sb = new StringBuilder();
final boolean haveFilter = !TextUtils.isEmpty(filter);
sb.append("SELECT "
diff --git a/src/com/android/providers/contacts/Hex.java b/src/com/android/providers/contacts/Hex.java
index 991f095..090764a 100644
--- a/src/com/android/providers/contacts/Hex.java
+++ b/src/com/android/providers/contacts/Hex.java
@@ -61,7 +61,7 @@
int j = 0;
for (int i = 0; i < array.length; i++) {
int index = array[i] & 0xFF;
- if (index == 0 && zeroTerminated) {
+ if (zeroTerminated && index == 0 && i == array.length-1) {
break;
}
diff --git a/src/com/android/providers/contacts/InstrumentedCursorWrapper.java b/src/com/android/providers/contacts/InstrumentedCursorWrapper.java
deleted file mode 100644
index c412810..0000000
--- a/src/com/android/providers/contacts/InstrumentedCursorWrapper.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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
- */
-
-package com.android.providers.contacts;
-
-import com.google.android.collect.Lists;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * Cursor wrapper that handles tracking time taken before query result came back and how long
- * the cursor was open before it was closed.
- */
-public class InstrumentedCursorWrapper extends CrossProcessCursorWrapper {
-
- /**
- * Static list of active cursors.
- */
- private static List<InstrumentedCursorWrapper> mActiveCursors = Lists.newArrayList();
-
- /**
- * Time (ms since epoch) when the cursor was created.
- */
- private long mCreationTime;
-
- /**
- * Milliseconds after creation at which the query completed (triggered by a getCount or
- * any method that moves the cursor).
- */
- private long mTimeToQuery;
-
- /**
- * The URI being queried in this cursor.
- */
- private Uri mUri;
-
- /**
- * Log tag to use.
- */
- private String mTag;
-
- public InstrumentedCursorWrapper(Cursor cursor, Uri uri, String tag) {
- super(cursor);
- mCreationTime = System.currentTimeMillis();
- mUri = uri;
- mTag = tag;
- mActiveCursors.add(this);
- }
-
- @Override
- public int getCount() {
- int count = super.getCount();
- logQueryTime();
- return count;
- }
-
- @Override
- public boolean moveToFirst() {
- boolean result = super.moveToFirst();
- logQueryTime();
- return result;
- }
-
- @Override
- public boolean moveToLast() {
- boolean result = super.moveToLast();
- logQueryTime();
- return result;
- }
-
- @Override
- public boolean move(int offset) {
- boolean result = super.move(offset);
- logQueryTime();
- return result;
- }
-
- @Override
- public boolean moveToPosition(int position) {
- boolean result = super.moveToPosition(position);
- logQueryTime();
- return result;
- }
-
- @Override
- public boolean moveToNext() {
- boolean result = super.moveToNext();
- logQueryTime();
- return result;
- }
-
- @Override
- public boolean moveToPrevious() {
- boolean result = super.moveToPrevious();
- logQueryTime();
- return result;
- }
-
- @Override
- public void close() {
- super.close();
- long timeToClose = System.currentTimeMillis() - mCreationTime;
- Log.v(mTag, timeToClose + "ms to close for URI " + mUri
- + " (" + (timeToClose - mTimeToQuery) + "ms since query complete)");
- mActiveCursors.remove(this);
- Log.v(mTag, mActiveCursors.size() + " cursors still open");
- }
-
- private void logQueryTime() {
- if (mTimeToQuery == 0) {
- mTimeToQuery = System.currentTimeMillis() - mCreationTime;
- Log.v(mTag, mTimeToQuery + "ms to query URI " + mUri);
- }
- }
-}
diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java
index 0c1cc32..78cbc9d 100644
--- a/src/com/android/providers/contacts/LegacyApiSupport.java
+++ b/src/com/android/providers/contacts/LegacyApiSupport.java
@@ -15,6 +15,7 @@
*/
package com.android.providers.contacts;
+import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
@@ -118,7 +119,9 @@
private static final int SETTINGS = 44;
private static final String PEOPLE_JOINS =
- " LEFT OUTER JOIN data name ON (raw_contacts._id = name.raw_contact_id"
+ " JOIN " + Tables.ACCOUNTS + " ON ("
+ + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")"
+ + " LEFT OUTER JOIN data name ON (raw_contacts._id = name.raw_contact_id"
+ " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = name.mimetype_id)"
+ "='" + StructuredName.CONTENT_ITEM_TYPE + "')"
+ " LEFT OUTER JOIN data organization ON (raw_contacts._id = organization.raw_contact_id"
@@ -508,9 +511,9 @@
private boolean mDefaultAccountKnown;
private Account mAccount;
- private long mMimetypeEmail;
- private long mMimetypeIm;
- private long mMimetypePostal;
+ private final long mMimetypeEmail;
+ private final long mMimetypeIm;
+ private final long mMimetypePostal;
public LegacyApiSupport(Context context, ContactsDatabaseHelper contactsDatabaseHelper,
@@ -563,8 +566,8 @@
+ " AS " + People.PHONETIC_NAME + " , " +
"note." + Note.NOTE
+ " AS " + People.NOTES + ", " +
- RawContacts.ACCOUNT_NAME + ", " +
- RawContacts.ACCOUNT_TYPE + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
+ " AS " + People.TIMES_CONTACTED + ", " +
Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
@@ -606,8 +609,8 @@
+ " AS " + android.provider.Contacts.Organizations.PERSON_ID + ", " +
Data.IS_PRIMARY
+ " AS " + android.provider.Contacts.Organizations.ISPRIMARY + ", " +
- RawContacts.ACCOUNT_NAME + ", " +
- RawContacts.ACCOUNT_TYPE + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
Organization.COMPANY
+ " AS " + android.provider.Contacts.Organizations.COMPANY + ", " +
Organization.TYPE
@@ -680,8 +683,8 @@
+ " AS " + android.provider.Contacts.Extensions._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.Extensions.PERSON_ID + ", " +
- RawContacts.ACCOUNT_NAME + ", " +
- RawContacts.ACCOUNT_TYPE + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
ExtensionsColumns.NAME
+ " AS " + android.provider.Contacts.Extensions.NAME + ", " +
ExtensionsColumns.VALUE
@@ -695,12 +698,14 @@
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUPS + ";");
db.execSQL("CREATE VIEW " + LegacyTables.GROUPS + " AS SELECT " +
GroupsColumns.CONCRETE_ID + " AS " + android.provider.Contacts.Groups._ID + ", " +
- Groups.ACCOUNT_NAME + ", " +
- Groups.ACCOUNT_TYPE + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
Groups.TITLE + " AS " + android.provider.Contacts.Groups.NAME + ", " +
Groups.NOTES + " AS " + android.provider.Contacts.Groups.NOTES + " , " +
Groups.SYSTEM_ID + " AS " + android.provider.Contacts.Groups.SYSTEM_ID +
" FROM " + Tables.GROUPS +
+ " JOIN " + Tables.ACCOUNTS + " ON (" +
+ GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")" +
";");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUP_MEMBERSHIP + ";");
@@ -709,10 +714,8 @@
+ " AS " + android.provider.Contacts.GroupMembership._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.GroupMembership.PERSON_ID + ", " +
- Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME
- + " AS " + RawContacts.ACCOUNT_NAME + ", " +
- Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE
- + " AS " + RawContacts.ACCOUNT_TYPE + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
GroupMembership.GROUP_ROW_ID
+ " AS " + android.provider.Contacts.GroupMembership.GROUP_ID + ", " +
Groups.TITLE
@@ -724,10 +727,10 @@
GroupsColumns.CONCRETE_SOURCE_ID
+ " AS "
+ android.provider.Contacts.GroupMembership.GROUP_SYNC_ID + ", " +
- GroupsColumns.CONCRETE_ACCOUNT_NAME
+ AccountsColumns.CONCRETE_ACCOUNT_NAME
+ " AS "
+ android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT + ", " +
- GroupsColumns.CONCRETE_ACCOUNT_TYPE
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE
+ " AS "
+ android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE +
" FROM " + Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS +
@@ -742,8 +745,8 @@
+ " AS " + android.provider.Contacts.Photos._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.Photos.PERSON_ID + ", " +
- RawContacts.ACCOUNT_NAME + ", " +
- RawContacts.ACCOUNT_TYPE + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
+ AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
Tables.DATA + "." + Photo.PHOTO
+ " AS " + android.provider.Contacts.Photos.DATA + ", " +
"legacy_photo." + LegacyPhotoData.EXISTS_ON_SERVER
diff --git a/src/com/android/providers/contacts/LegacyContactImporter.java b/src/com/android/providers/contacts/LegacyContactImporter.java
deleted file mode 100644
index 239f08d..0000000
--- a/src/com/android/providers/contacts/LegacyContactImporter.java
+++ /dev/null
@@ -1,1310 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * 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.
- */
-
-package com.android.providers.contacts;
-
-import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteStatement;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.File;
-
-public class LegacyContactImporter {
-
- public static final String TAG = "LegacyContactImporter";
-
- private static final int MAX_ATTEMPTS = 5;
- private static final int DELAY_BETWEEN_ATTEMPTS = 2000;
-
- public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
- private static final String DATABASE_NAME = "contacts.db";
-
- private static final int INSERT_BATCH_SIZE = 200;
-
- /**
- * Estimated increase in database size after import.
- */
- private static final long DATABASE_SIZE_MULTIPLIER = 4;
-
- /**
- * Estimated minimum database size in megabytes.
- */
- private static final long DATABASE_MIN_SIZE = 5;
-
- private final Context mContext;
- private final ContactsProvider2 mContactsProvider;
- private final NameLookupBuilder mNameLookupBuilder;
-
- private ContactsDatabaseHelper mDbHelper;
- private ContentValues mValues = new ContentValues();
- private ContentResolver mResolver;
- private boolean mPhoneticNameAvailable = true;
-
- private SQLiteDatabase mSourceDb;
- private SQLiteDatabase mTargetDb;
-
- private NameSplitter mNameSplitter;
- private int mBatchCounter;
-
- private int mContactCount;
-
- private long mStructuredNameMimetypeId;
- private long mNoteMimetypeId;
- private long mOrganizationMimetypeId;
- private long mPhoneMimetypeId;
- private long mEmailMimetypeId;
- private long mImMimetypeId;
- private long mPostalMimetypeId;
- private long mPhotoMimetypeId;
- private long mGroupMembershipMimetypeId;
-
- private long mEstimatedStorageRequirement = DATABASE_MIN_SIZE;
-
- public LegacyContactImporter(Context context, ContactsProvider2 contactsProvider) {
- mContext = context;
- mContactsProvider = contactsProvider;
- mResolver = mContactsProvider.getContext().getContentResolver();
- mNameLookupBuilder = mContactsProvider.getNameLookupBuilder();
- }
-
- public boolean importContacts() throws Exception {
- String path = mContext.getDatabasePath(DATABASE_NAME).getPath();
- File file = new File(path);
- if (!file.exists()) {
- Log.i(TAG, "Legacy contacts database does not exist at " + path);
- return true;
- }
-
- Log.w(TAG, "Importing contacts from " + path);
-
- for (int i = 0; i < MAX_ATTEMPTS; i++) {
- try {
- mSourceDb = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
- importContactsFromLegacyDb();
- Log.i(TAG, "Imported legacy contacts: " + mContactCount);
- mContactsProvider.notifyChange();
- return true;
-
- } catch (SQLiteException e) {
- Log.e(TAG, "Database import exception. Will retry in " + DELAY_BETWEEN_ATTEMPTS
- + "ms", e);
-
- // We could get a "database locked" exception here, in which
- // case we should retry
- Thread.sleep(DELAY_BETWEEN_ATTEMPTS);
-
- } finally {
- if (mSourceDb != null) {
- mSourceDb.close();
- }
- }
- }
-
- long oldDatabaseSize = file.length();
- mEstimatedStorageRequirement = oldDatabaseSize * DATABASE_SIZE_MULTIPLIER / 1024 / 1024;
- if (mEstimatedStorageRequirement < DATABASE_MIN_SIZE) {
- mEstimatedStorageRequirement = DATABASE_MIN_SIZE;
- }
-
- return false;
- }
-
- public long getEstimatedStorageRequirement() {
- return mEstimatedStorageRequirement;
- }
-
- private void importContactsFromLegacyDb() {
- int version = mSourceDb.getVersion();
-
- // Upgrade to version 78 was the latest that wiped out data. Might as well follow suit
- // and ignore earlier versions.
- if (version < 78) {
- return;
- }
-
- if (version < 80) {
- mPhoneticNameAvailable = false;
- }
-
- mDbHelper = (ContactsDatabaseHelper)mContactsProvider.getDatabaseHelper();
- mTargetDb = mDbHelper.getWritableDatabase();
-
- mStructuredNameMimetypeId = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
- mNoteMimetypeId = mDbHelper.getMimeTypeId(Note.CONTENT_ITEM_TYPE);
- mOrganizationMimetypeId = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
- mPhoneMimetypeId = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
- mEmailMimetypeId = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
- mImMimetypeId = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
- mPostalMimetypeId = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE);
- mPhotoMimetypeId = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
- mGroupMembershipMimetypeId =
- mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
-
- mNameSplitter = mContactsProvider.getNameSplitter();
-
- mTargetDb.beginTransaction();
- try {
- checkForImportFailureTest();
-
- /*
- * At this point there should be no data in the contacts provider, but in case
- * some was inserted by mistake, we should remove it. The main reason for this
- * is that we will be preserving original contact IDs and don't want to run into
- * any collisions.
- */
- mContactsProvider.wipeData();
-
- importGroups();
- importPeople();
- importOrganizations();
- importPhones();
- importContactMethods();
- importPhotos();
- importGroupMemberships();
- updateDisplayNamesAndLookupKeys();
-
- // Deleted contacts should be inserted after everything else, because
- // the legacy table does not provide an _ID field - the _ID field
- // will be autoincremented
- importDeletedPeople();
-
- mDbHelper.updateAllVisible();
-
- mTargetDb.setTransactionSuccessful();
- } finally {
- mTargetDb.endTransaction();
- }
-
- importCalls();
- }
-
- /**
- * This is used for simulating an import failure. Insert a row into the "settings"
- * table with key='TEST' and then proceed with the upgrade. Remove the record
- * after verifying the failure handling.
- */
- private void checkForImportFailureTest() {
- long isTest = DatabaseUtils.longForQuery(mSourceDb,
- "SELECT COUNT(*) FROM settings WHERE key='TEST'", null);
- if (isTest != 0) {
- throw new SQLiteException("Testing import failure.");
- }
- }
-
- private interface GroupsQuery {
- String TABLE = "groups";
-
- String[] COLUMNS = {
- "_id", "name", "notes", "should_sync", "system_id", "_sync_account", "_sync_id",
- "_sync_dirty",
- };
-
- static int ID = 0;
- static int NAME = 1;
- static int NOTES = 2;
- static int SHOULD_SYNC = 3; // TODO add this feature to Groups
- static int SYSTEM_ID = 4;
-
- static int _SYNC_ACCOUNT = 5;
- static int _SYNC_ID = 6;
- static int _SYNC_DIRTY = 7;
- }
-
- private interface GroupsInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.GROUPS + "(" +
- Groups._ID + "," +
- Groups.TITLE + "," +
- Groups.NOTES + "," +
- Groups.SYSTEM_ID + "," +
- Groups.DIRTY + "," +
- Groups.GROUP_VISIBLE + "," +
- Groups.ACCOUNT_NAME + "," +
- Groups.ACCOUNT_TYPE + "," +
- Groups.SOURCE_ID +
- ") VALUES (?,?,?,?,?,?,?,?,?)";
-
- int ID = 1;
- int TITLE = 2;
- int NOTES = 3;
- int SYSTEM_ID = 4;
- int DIRTY = 5;
- int GROUP_VISIBLE = 6;
- int ACCOUNT_NAME = 7;
- int ACCOUNT_TYPE = 8;
- int SOURCE_ID = 9;
- }
-
- private void importGroups() {
- SQLiteStatement insert = mTargetDb.compileStatement(GroupsInsert.INSERT_SQL);
- Cursor c = mSourceDb.query(GroupsQuery.TABLE, GroupsQuery.COLUMNS, null, null,
- null, null, null);
- try {
- while (c.moveToNext()) {
- insertGroup(c, insert);
- }
- } finally {
- c.close();
- insert.close();
- }
- }
-
- private void insertGroup(Cursor c, SQLiteStatement insert) {
- long id = c.getLong(GroupsQuery.ID);
-
- insert.bindLong(GroupsInsert.ID, id);
- bindString(insert, GroupsInsert.TITLE, c.getString(GroupsQuery.NAME));
- bindString(insert, GroupsInsert.NOTES, c.getString(GroupsQuery.NOTES));
- bindString(insert, GroupsInsert.SYSTEM_ID, c.getString(GroupsQuery.SYSTEM_ID));
- insert.bindLong(GroupsInsert.DIRTY, c.getLong(GroupsQuery._SYNC_DIRTY));
- insert.bindLong(GroupsInsert.GROUP_VISIBLE, 1);
-
- String account = c.getString(GroupsQuery._SYNC_ACCOUNT);
- if (!TextUtils.isEmpty(account)) {
- bindString(insert, GroupsInsert.ACCOUNT_NAME, account);
- bindString(insert, GroupsInsert.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE);
- bindString(insert, GroupsInsert.SOURCE_ID, c.getString(GroupsQuery._SYNC_ID));
- } else {
- insert.bindNull(GroupsInsert.ACCOUNT_NAME);
- insert.bindNull(GroupsInsert.ACCOUNT_TYPE);
- insert.bindNull(GroupsInsert.SOURCE_ID);
- }
- insert(insert);
- }
-
- private interface PeopleQuery {
- String TABLE = "people";
-
- String NAME_SQL =
- "(CASE WHEN (name IS NOT NULL AND name != '') "
- + "THEN name "
- + "ELSE "
- + "(CASE WHEN primary_organization is NOT NULL THEN "
- + "(SELECT company FROM organizations WHERE "
- + "organizations._id = primary_organization) "
- + "ELSE "
- + "(CASE WHEN primary_phone IS NOT NULL THEN "
- +"(SELECT number FROM phones WHERE phones._id = primary_phone) "
- + "ELSE "
- + "(CASE WHEN primary_email IS NOT NULL THEN "
- + "(SELECT data FROM contact_methods WHERE "
- + "contact_methods._id = primary_email) "
- + "ELSE "
- + "null "
- + "END) "
- + "END) "
- + "END) "
- + "END) ";
-
-
- String[] COLUMNS_WITH_DISPLAY_NAME_WITHOUT_PHONETIC_NAME = {
- "_id", NAME_SQL, "notes", "times_contacted", "last_time_contacted", "starred",
- "primary_phone", "primary_organization", "primary_email", "custom_ringtone",
- "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id",
- "_sync_dirty",
- };
-
- String[] COLUMNS_WITH_DISPLAY_NAME_WITH_PHONETIC_NAME = {
- "_id", NAME_SQL, "notes", "times_contacted", "last_time_contacted", "starred",
- "primary_phone", "primary_organization", "primary_email", "custom_ringtone",
- "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id",
- "_sync_dirty", "phonetic_name",
- };
-
- String[] COLUMNS_WITHOUT_PHONETIC_NAME = {
- "_id", "name", "notes", "times_contacted", "last_time_contacted", "starred",
- "primary_phone", "primary_organization", "primary_email", "custom_ringtone",
- "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id",
- "_sync_dirty",
- };
-
- String[] COLUMNS_WITH_PHONETIC_NAME = {
- "_id", "name", "notes", "times_contacted", "last_time_contacted", "starred",
- "primary_phone", "primary_organization", "primary_email", "custom_ringtone",
- "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id",
- "_sync_dirty", "phonetic_name",
- };
-
- static int _ID = 0;
- static int NAME = 1;
- static int NOTES = 2;
- static int TIMES_CONTACTED = 3;
- static int LAST_TIME_CONTACTED = 4;
- static int STARRED = 5;
- static int PRIMARY_PHONE = 6;
- static int PRIMARY_ORGANIZATION = 7;
- static int PRIMARY_EMAIL = 8;
- static int CUSTOM_RINGTONE = 9;
- static int SEND_TO_VOICEMAIL = 10;
-
- static int _SYNC_ACCOUNT = 11;
- static int _SYNC_ID = 12;
- static int _SYNC_TIME = 13;
- static int _SYNC_LOCAL_ID = 14;
- static int _SYNC_DIRTY = 15;
-
- static int PHONETIC_NAME = 16;
- }
-
-
- private interface RawContactsInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.RAW_CONTACTS + "(" +
- RawContacts._ID + "," +
- RawContacts.CONTACT_ID + "," +
- RawContacts.CUSTOM_RINGTONE + "," +
- RawContacts.DIRTY + "," +
- RawContacts.LAST_TIME_CONTACTED + "," +
- RawContacts.SEND_TO_VOICEMAIL + "," +
- RawContacts.STARRED + "," +
- RawContacts.TIMES_CONTACTED + "," +
- RawContacts.SYNC1 + "," +
- RawContacts.SYNC2 + "," +
- RawContacts.ACCOUNT_NAME + "," +
- RawContacts.ACCOUNT_TYPE + "," +
- RawContacts.SOURCE_ID + "," +
- RawContactsColumns.DISPLAY_NAME +
- ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
-
- int ID = 1;
- int CONTACT_ID = 2;
- int CUSTOM_RINGTONE = 3;
- int DIRTY = 4;
- int LAST_TIME_CONTACTED = 5;
- int SEND_TO_VOICEMAIL = 6;
- int STARRED = 7;
- int TIMES_CONTACTED = 8;
- int SYNC1 = 9;
- int SYNC2 = 10;
- int ACCOUNT_NAME = 11;
- int ACCOUNT_TYPE = 12;
- int SOURCE_ID = 13;
- int DISPLAY_NAME = 14;
- }
-
- private interface ContactsInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.CONTACTS + "(" +
- Contacts._ID + "," +
- Contacts.CUSTOM_RINGTONE + "," +
- Contacts.LAST_TIME_CONTACTED + "," +
- Contacts.SEND_TO_VOICEMAIL + "," +
- Contacts.STARRED + "," +
- Contacts.TIMES_CONTACTED + "," +
- Contacts.NAME_RAW_CONTACT_ID +
- ") VALUES (?,?,?,?,?,?,?)";
-
- int ID = 1;
- int CUSTOM_RINGTONE = 2;
- int LAST_TIME_CONTACTED = 3;
- int SEND_TO_VOICEMAIL = 4;
- int STARRED = 5;
- int TIMES_CONTACTED = 6;
- int NAME_RAW_CONTACT_ID = 7;
- }
-
- private interface StructuredNameInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- StructuredName.DISPLAY_NAME + "," +
- StructuredName.PREFIX + "," +
- StructuredName.GIVEN_NAME + "," +
- StructuredName.MIDDLE_NAME + "," +
- StructuredName.FAMILY_NAME + "," +
- StructuredName.SUFFIX + "," +
- StructuredName.FULL_NAME_STYLE + "," +
- StructuredName.PHONETIC_FAMILY_NAME + "," +
- StructuredName.PHONETIC_MIDDLE_NAME + "," +
- StructuredName.PHONETIC_GIVEN_NAME + "," +
- StructuredName.PHONETIC_NAME_STYLE +
- ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int DISPLAY_NAME = 3;
- int PREFIX = 4;
- int GIVEN_NAME = 5;
- int MIDDLE_NAME = 6;
- int FAMILY_NAME = 7;
- int SUFFIX = 8;
- int FULL_NAME_STYLE = 9;
- int PHONETIC_FAMILY_NAME = 10;
- int PHONETIC_MIDDLE_NAME = 11;
- int PHONETIC_GIVEN_NAME = 12;
- int PHONETIC_NAME_STYLE = 13;
- }
-
- private interface NoteInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- Note.NOTE +
- ") VALUES (?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int NOTE = 3;
- }
-
- private void importPeople() {
- SQLiteStatement rawContactInsert = mTargetDb.compileStatement(RawContactsInsert.INSERT_SQL);
- SQLiteStatement contactInsert = mTargetDb.compileStatement(ContactsInsert.INSERT_SQL);
- SQLiteStatement structuredNameInsert =
- mTargetDb.compileStatement(StructuredNameInsert.INSERT_SQL);
- SQLiteStatement noteInsert = mTargetDb.compileStatement(NoteInsert.INSERT_SQL);
- try {
- String[] columns = mPhoneticNameAvailable
- ? PeopleQuery.COLUMNS_WITH_DISPLAY_NAME_WITH_PHONETIC_NAME
- : PeopleQuery.COLUMNS_WITH_DISPLAY_NAME_WITHOUT_PHONETIC_NAME;
- Cursor c = mSourceDb.query(PeopleQuery.TABLE, columns, "name IS NULL", null, null,
- null, null);
- try {
- while (c.moveToNext()) {
- insertRawContact(c, rawContactInsert);
- insertContact(c, contactInsert);
- insertNote(c, noteInsert);
- mContactCount++;
- }
- } finally {
- c.close();
- }
-
- columns = mPhoneticNameAvailable
- ? PeopleQuery.COLUMNS_WITH_PHONETIC_NAME
- : PeopleQuery.COLUMNS_WITHOUT_PHONETIC_NAME;
- c = mSourceDb.query(PeopleQuery.TABLE, columns, "name IS NOT NULL", null, null, null,
- null);
- try {
- while (c.moveToNext()) {
- long id = insertRawContact(c, rawContactInsert);
- insertContact(c, contactInsert);
- insertStructuredName(c, structuredNameInsert);
- insertNote(c, noteInsert);
- mContactCount++;
- }
- } finally {
- c.close();
- }
- } finally {
- rawContactInsert.close();
- contactInsert.close();
- structuredNameInsert.close();
- noteInsert.close();
- }
- }
-
- private long insertRawContact(Cursor c, SQLiteStatement insert) {
- long id = c.getLong(PeopleQuery._ID);
- insert.bindLong(RawContactsInsert.ID, id);
- insert.bindLong(RawContactsInsert.CONTACT_ID, id);
- bindString(insert, RawContactsInsert.CUSTOM_RINGTONE,
- c.getString(PeopleQuery.CUSTOM_RINGTONE));
- bindString(insert, RawContactsInsert.DIRTY,
- c.getString(PeopleQuery._SYNC_DIRTY));
- insert.bindLong(RawContactsInsert.LAST_TIME_CONTACTED,
- c.getLong(PeopleQuery.LAST_TIME_CONTACTED));
- insert.bindLong(RawContactsInsert.SEND_TO_VOICEMAIL,
- c.getLong(PeopleQuery.SEND_TO_VOICEMAIL));
- insert.bindLong(RawContactsInsert.STARRED,
- c.getLong(PeopleQuery.STARRED));
- insert.bindLong(RawContactsInsert.TIMES_CONTACTED,
- c.getLong(PeopleQuery.TIMES_CONTACTED));
- bindString(insert, RawContactsInsert.SYNC1,
- c.getString(PeopleQuery._SYNC_TIME));
- bindString(insert, RawContactsInsert.SYNC2,
- c.getString(PeopleQuery._SYNC_LOCAL_ID));
- bindString(insert, RawContactsInsert.DISPLAY_NAME,
- c.getString(PeopleQuery.NAME));
-
- String account = c.getString(PeopleQuery._SYNC_ACCOUNT);
- if (!TextUtils.isEmpty(account)) {
- bindString(insert, RawContactsInsert.ACCOUNT_NAME, account);
- bindString(insert, RawContactsInsert.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE);
- bindString(insert, RawContactsInsert.SOURCE_ID, c.getString(PeopleQuery._SYNC_ID));
- } else {
- insert.bindNull(RawContactsInsert.ACCOUNT_NAME);
- insert.bindNull(RawContactsInsert.ACCOUNT_TYPE);
- insert.bindNull(RawContactsInsert.SOURCE_ID);
- }
- insert(insert);
- return id;
- }
-
- private void insertContact(Cursor c, SQLiteStatement insert) {
- long id = c.getLong(PeopleQuery._ID);
- insert.bindLong(ContactsInsert.ID, id);
- bindString(insert, ContactsInsert.CUSTOM_RINGTONE,
- c.getString(PeopleQuery.CUSTOM_RINGTONE));
- insert.bindLong(ContactsInsert.LAST_TIME_CONTACTED,
- c.getLong(PeopleQuery.LAST_TIME_CONTACTED));
- insert.bindLong(ContactsInsert.SEND_TO_VOICEMAIL,
- c.getLong(PeopleQuery.SEND_TO_VOICEMAIL));
- insert.bindLong(ContactsInsert.STARRED,
- c.getLong(PeopleQuery.STARRED));
- insert.bindLong(ContactsInsert.TIMES_CONTACTED,
- c.getLong(PeopleQuery.TIMES_CONTACTED));
- insert.bindLong(ContactsInsert.NAME_RAW_CONTACT_ID, id);
-
- insert(insert);
- }
-
- private void insertStructuredName(Cursor c, SQLiteStatement insert) {
- String name = c.getString(PeopleQuery.NAME);
- if (TextUtils.isEmpty(name)) {
- return;
- }
-
- long id = c.getLong(PeopleQuery._ID);
-
- insert.bindLong(StructuredNameInsert.RAW_CONTACT_ID, id);
- insert.bindLong(StructuredNameInsert.MIMETYPE_ID, mStructuredNameMimetypeId);
- bindString(insert, StructuredNameInsert.DISPLAY_NAME, name);
-
- NameSplitter.Name splitName = new NameSplitter.Name();
- mNameSplitter.split(splitName, name);
-
- bindString(insert, StructuredNameInsert.PREFIX,
- splitName.getPrefix());
- bindString(insert, StructuredNameInsert.GIVEN_NAME,
- splitName.getGivenNames());
- bindString(insert, StructuredNameInsert.MIDDLE_NAME,
- splitName.getMiddleName());
- bindString(insert, StructuredNameInsert.FAMILY_NAME,
- splitName.getFamilyName());
- bindString(insert, StructuredNameInsert.SUFFIX,
- splitName.getSuffix());
- final String joined = mNameSplitter.join(splitName, true, true);
- bindString(insert, StructuredNameInsert.DISPLAY_NAME, joined);
-
- if (mPhoneticNameAvailable) {
- String phoneticName = c.getString(PeopleQuery.PHONETIC_NAME);
- if (phoneticName != null) {
- int index = phoneticName.indexOf(' ');
- if (index == -1) {
- splitName.phoneticFamilyName = phoneticName;
- } else {
- splitName.phoneticFamilyName = phoneticName.substring(0, index).trim();
- splitName.phoneticGivenName = phoneticName.substring(index + 1).trim();
- }
- }
- }
-
- mNameSplitter.guessNameStyle(splitName);
-
- int fullNameStyle = splitName.getFullNameStyle();
- insert.bindLong(StructuredNameInsert.FULL_NAME_STYLE,
- fullNameStyle);
- bindString(insert, StructuredNameInsert.PHONETIC_FAMILY_NAME,
- splitName.phoneticFamilyName);
- bindString(insert, StructuredNameInsert.PHONETIC_MIDDLE_NAME,
- splitName.phoneticMiddleName);
- bindString(insert, StructuredNameInsert.PHONETIC_GIVEN_NAME,
- splitName.phoneticGivenName);
- insert.bindLong(StructuredNameInsert.PHONETIC_NAME_STYLE,
- splitName.phoneticNameStyle);
-
- long dataId = insert(insert);
-
- mNameLookupBuilder.insertNameLookup(id, dataId, name,
- mNameSplitter.getAdjustedFullNameStyle(fullNameStyle));
-
- if (splitName.phoneticFamilyName != null
- || splitName.phoneticMiddleName != null
- || splitName.phoneticGivenName != null) {
- mDbHelper.insertNameLookupForPhoneticName(id, dataId,
- splitName.phoneticFamilyName,
- splitName.phoneticMiddleName,
- splitName.phoneticGivenName);
- }
- }
-
- private void insertNote(Cursor c, SQLiteStatement insert) {
- String notes = c.getString(PeopleQuery.NOTES);
-
- if (TextUtils.isEmpty(notes)) {
- return;
- }
-
- long id = c.getLong(PeopleQuery._ID);
- insert.bindLong(NoteInsert.RAW_CONTACT_ID, id);
- insert.bindLong(NoteInsert.MIMETYPE_ID, mNoteMimetypeId);
- bindString(insert, NoteInsert.NOTE, notes);
- insert(insert);
- }
-
- private interface OrganizationsQuery {
- String TABLE = "organizations";
-
- String[] COLUMNS = {
- "person", "company", "title", "isprimary", "type", "label",
- };
-
- static int PERSON = 0;
- static int COMPANY = 1;
- static int TITLE = 2;
- static int ISPRIMARY = 3;
- static int TYPE = 4;
- static int LABEL = 5;
- }
-
- private interface OrganizationInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- Data.IS_PRIMARY + "," +
- Data.IS_SUPER_PRIMARY + "," +
- Organization.COMPANY + "," +
- Organization.TITLE + "," +
- Organization.TYPE + "," +
- Organization.LABEL +
- ") VALUES (?,?,?,?,?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int IS_PRIMARY = 3;
- int IS_SUPER_PRIMARY = 4;
- int COMPANY = 5;
- int TITLE = 6;
- int TYPE = 7;
- int LABEL = 8;
- }
-
- private void importOrganizations() {
- SQLiteStatement insert = mTargetDb.compileStatement(OrganizationInsert.INSERT_SQL);
- Cursor c = mSourceDb.query(OrganizationsQuery.TABLE, OrganizationsQuery.COLUMNS, null, null,
- null, null, null);
- try {
- while (c.moveToNext()) {
- insertOrganization(c, insert);
- }
- } finally {
- c.close();
- insert.close();
- }
- }
-
- private void insertOrganization(Cursor c, SQLiteStatement insert) {
- long id = c.getLong(OrganizationsQuery.PERSON);
- insert.bindLong(OrganizationInsert.RAW_CONTACT_ID, id);
- insert.bindLong(OrganizationInsert.MIMETYPE_ID, mOrganizationMimetypeId);
- bindString(insert, OrganizationInsert.IS_PRIMARY, c.getString(OrganizationsQuery.ISPRIMARY));
- bindString(insert, OrganizationInsert.IS_SUPER_PRIMARY,
- c.getString(OrganizationsQuery.ISPRIMARY));
- bindString(insert, OrganizationInsert.COMPANY, c.getString(OrganizationsQuery.COMPANY));
- bindString(insert, OrganizationInsert.TITLE, c.getString(OrganizationsQuery.TITLE));
- bindString(insert, OrganizationInsert.TYPE, c.getString(OrganizationsQuery.TYPE));
- bindString(insert, OrganizationInsert.LABEL, c.getString(OrganizationsQuery.LABEL));
- insert(insert);
- }
-
- private interface ContactMethodsQuery {
- String TABLE = "contact_methods";
-
- String[] COLUMNS = {
- "person", "kind", "data", "aux_data", "type", "label", "isprimary",
- };
-
- static int PERSON = 0;
- static int KIND = 1;
- static int DATA = 2;
- static int AUX_DATA = 3;
- static int TYPE = 4;
- static int LABEL = 5;
- static int ISPRIMARY = 6;
- }
-
- private interface EmailInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- Data.IS_PRIMARY + "," +
- Data.IS_SUPER_PRIMARY + "," +
- Email.DATA + "," +
- Email.TYPE + "," +
- Email.LABEL + "," +
- Data.DATA14 +
- ") VALUES (?,?,?,?,?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int IS_PRIMARY = 3;
- int IS_SUPER_PRIMARY = 4;
- int DATA = 5;
- int TYPE = 6;
- int LABEL = 7;
- int AUX_DATA = 8;
- }
-
- private interface ImInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- Data.IS_PRIMARY + "," +
- Data.IS_SUPER_PRIMARY + "," +
- Im.DATA + "," +
- Im.TYPE + "," +
- Im.LABEL + "," +
- Data.DATA14 +
- ") VALUES (?,?,?,?,?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int IS_PRIMARY = 3;
- int IS_SUPER_PRIMARY = 4;
- int DATA = 5;
- int TYPE = 6;
- int LABEL = 7;
- int AUX_DATA = 8;
- }
-
- private interface PostalInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- Data.IS_PRIMARY + "," +
- Data.IS_SUPER_PRIMARY + "," +
- StructuredPostal.FORMATTED_ADDRESS + "," +
- StructuredPostal.TYPE + "," +
- StructuredPostal.LABEL + "," +
- Data.DATA14 +
- ") VALUES (?,?,?,?,?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int IS_PRIMARY = 3;
- int IS_SUPER_PRIMARY = 4;
- int DATA = 5;
- int TYPE = 6;
- int LABEL = 7;
- int AUX_DATA = 8;
- }
-
- private void importContactMethods() {
- SQLiteStatement emailInsert = mTargetDb.compileStatement(EmailInsert.INSERT_SQL);
- SQLiteStatement imInsert = mTargetDb.compileStatement(ImInsert.INSERT_SQL);
- SQLiteStatement postalInsert = mTargetDb.compileStatement(PostalInsert.INSERT_SQL);
- Cursor c = mSourceDb.query(ContactMethodsQuery.TABLE, ContactMethodsQuery.COLUMNS, null,
- null, null, null, null);
- try {
- while (c.moveToNext()) {
- int kind = c.getInt(ContactMethodsQuery.KIND);
- switch (kind) {
- case android.provider.Contacts.KIND_EMAIL:
- insertEmail(c, emailInsert);
- break;
-
- case android.provider.Contacts.KIND_IM:
- insertIm(c, imInsert);
- break;
-
- case android.provider.Contacts.KIND_POSTAL:
- insertPostal(c, postalInsert);
- break;
- }
- }
- } finally {
- c.close();
- emailInsert.close();
- imInsert.close();
- postalInsert.close();
- }
-
- }
-
- private void insertEmail(Cursor c, SQLiteStatement insert) {
- long personId = c.getLong(ContactMethodsQuery.PERSON);
- String email = c.getString(ContactMethodsQuery.DATA);
-
- insert.bindLong(EmailInsert.RAW_CONTACT_ID, personId);
- insert.bindLong(EmailInsert.MIMETYPE_ID, mEmailMimetypeId);
- bindString(insert, EmailInsert.IS_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY));
- bindString(insert, EmailInsert.IS_SUPER_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY));
- bindString(insert, EmailInsert.DATA, email);
- bindString(insert, EmailInsert.AUX_DATA, c.getString(ContactMethodsQuery.AUX_DATA));
- bindString(insert, EmailInsert.TYPE, c.getString(ContactMethodsQuery.TYPE));
- bindString(insert, EmailInsert.LABEL, c.getString(ContactMethodsQuery.LABEL));
-
- long dataId = insert(insert);
- mDbHelper.insertNameLookupForEmail(personId, dataId, email);
- }
-
- private void insertIm(Cursor c, SQLiteStatement insert) {
- long personId = c.getLong(ContactMethodsQuery.PERSON);
-
- insert.bindLong(ImInsert.RAW_CONTACT_ID, personId);
- insert.bindLong(ImInsert.MIMETYPE_ID, mImMimetypeId);
- bindString(insert, ImInsert.IS_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY));
- bindString(insert, ImInsert.IS_SUPER_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY));
- bindString(insert, ImInsert.DATA, c.getString(ContactMethodsQuery.DATA));
- bindString(insert, ImInsert.AUX_DATA, c.getString(ContactMethodsQuery.AUX_DATA));
- bindString(insert, ImInsert.TYPE, c.getString(ContactMethodsQuery.TYPE));
- bindString(insert, ImInsert.LABEL, c.getString(ContactMethodsQuery.LABEL));
- insert(insert);
- }
-
- private void insertPostal(Cursor c, SQLiteStatement insert) {
- long personId = c.getLong(ContactMethodsQuery.PERSON);
-
- insert.bindLong(PostalInsert.RAW_CONTACT_ID, personId);
- insert.bindLong(PostalInsert.MIMETYPE_ID, mPostalMimetypeId);
- bindString(insert, PostalInsert.IS_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY));
- bindString(insert, PostalInsert.IS_SUPER_PRIMARY,
- c.getString(ContactMethodsQuery.ISPRIMARY));
- bindString(insert, PostalInsert.DATA, c.getString(ContactMethodsQuery.DATA));
- bindString(insert, PostalInsert.AUX_DATA, c.getString(ContactMethodsQuery.AUX_DATA));
- bindString(insert, PostalInsert.TYPE, c.getString(ContactMethodsQuery.TYPE));
- bindString(insert, PostalInsert.LABEL, c.getString(ContactMethodsQuery.LABEL));
- insert(insert);
- }
-
- private interface PhonesQuery {
- String TABLE = "phones";
-
- String[] COLUMNS = {
- "person", "type", "number", "label", "isprimary",
- };
-
- static int PERSON = 0;
- static int TYPE = 1;
- static int NUMBER = 2;
- static int LABEL = 3;
- static int ISPRIMARY = 4;
- }
-
- private interface PhoneInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- Data.IS_PRIMARY + "," +
- Data.IS_SUPER_PRIMARY + "," +
- Phone.NUMBER + "," +
- Phone.TYPE + "," +
- Phone.LABEL + "," +
- PhoneColumns.NORMALIZED_NUMBER +
- ") VALUES (?,?,?,?,?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int IS_PRIMARY = 3;
- int IS_SUPER_PRIMARY = 4;
- int NUMBER = 5;
- int TYPE = 6;
- int LABEL = 7;
- int NORMALIZED_NUMBER = 8;
- }
-
- private interface PhoneLookupInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.PHONE_LOOKUP + "(" +
- PhoneLookupColumns.RAW_CONTACT_ID + "," +
- PhoneLookupColumns.DATA_ID + "," +
- PhoneLookupColumns.NORMALIZED_NUMBER + "," +
- PhoneLookupColumns.MIN_MATCH +
- ") VALUES (?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int DATA_ID = 2;
- int NORMALIZED_NUMBER = 3;
- int MIN_MATCH = 4;
- }
-
- private interface HasPhoneNumberUpdate {
- String UPDATE_SQL = "UPDATE " + Tables.CONTACTS +
- " SET " + Contacts.HAS_PHONE_NUMBER + "=1 WHERE " + Contacts._ID + "=?";
-
- int CONTACT_ID = 1;
- }
-
- private void importPhones() {
- SQLiteStatement phoneInsert = mTargetDb.compileStatement(PhoneInsert.INSERT_SQL);
- SQLiteStatement phoneLookupInsert =
- mTargetDb.compileStatement(PhoneLookupInsert.INSERT_SQL);
- SQLiteStatement hasPhoneUpdate =
- mTargetDb.compileStatement(HasPhoneNumberUpdate.UPDATE_SQL);
- Cursor c = mSourceDb.query(PhonesQuery.TABLE, PhonesQuery.COLUMNS, null, null,
- null, null, null);
- try {
- while (c.moveToNext()) {
- insertPhone(c, phoneInsert, phoneLookupInsert, hasPhoneUpdate);
- }
- } finally {
- c.close();
- phoneInsert.close();
- phoneLookupInsert.close();
- hasPhoneUpdate.close();
- }
- }
-
- private void insertPhone(Cursor c, SQLiteStatement phoneInsert,
- SQLiteStatement phoneLookupInsert, SQLiteStatement hasPhoneUpdate) {
- long lastUpdatedContact = -1;
- long id = c.getLong(PhonesQuery.PERSON);
- String number = c.getString(PhonesQuery.NUMBER);
- String normalizedNumber = null;
- if (number != null) {
- normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
- }
- phoneInsert.bindLong(PhoneInsert.RAW_CONTACT_ID, id);
- phoneInsert.bindLong(PhoneInsert.MIMETYPE_ID, mPhoneMimetypeId);
- bindString(phoneInsert, PhoneInsert.IS_PRIMARY, c.getString(PhonesQuery.ISPRIMARY));
- bindString(phoneInsert, PhoneInsert.IS_SUPER_PRIMARY, c.getString(PhonesQuery.ISPRIMARY));
- bindString(phoneInsert, PhoneInsert.NUMBER, number);
- bindString(phoneInsert, PhoneInsert.TYPE, c.getString(PhonesQuery.TYPE));
- bindString(phoneInsert, PhoneInsert.LABEL, c.getString(PhonesQuery.LABEL));
- bindString(phoneInsert, PhoneInsert.NORMALIZED_NUMBER, normalizedNumber);
-
- long dataId = insert(phoneInsert);
- if (normalizedNumber != null) {
- phoneLookupInsert.bindLong(PhoneLookupInsert.RAW_CONTACT_ID, id);
- phoneLookupInsert.bindLong(PhoneLookupInsert.DATA_ID, dataId);
- phoneLookupInsert.bindString(PhoneLookupInsert.NORMALIZED_NUMBER, normalizedNumber);
- phoneLookupInsert.bindString(PhoneLookupInsert.MIN_MATCH,
- PhoneNumberUtils.toCallerIDMinMatch(number));
- insert(phoneLookupInsert);
-
- if (lastUpdatedContact != id) {
- lastUpdatedContact = id;
- hasPhoneUpdate.bindLong(HasPhoneNumberUpdate.CONTACT_ID, id);
- hasPhoneUpdate.execute();
- }
- }
- }
-
- private interface PhotosQuery {
- String TABLE = "photos";
-
- String[] COLUMNS = {
- "person", "data", "_sync_id", "_sync_account"
- };
-
- static int PERSON = 0;
- static int DATA = 1;
- static int _SYNC_ID = 2;
- static int _SYNC_ACCOUNT = 3;
- }
-
- private interface PhotoInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- Photo.PHOTO + "," +
- Data.SYNC1 +
- ") VALUES (?,?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int PHOTO = 3;
- int SYNC1 = 4;
- }
-
- private interface PhotoIdUpdate {
- String UPDATE_SQL = "UPDATE " + Tables.CONTACTS +
- " SET " + Contacts.PHOTO_ID + "=? WHERE " + Contacts._ID + "=?";
-
- int PHOTO_ID = 1;
- int CONTACT_ID = 2;
- }
-
- private void importPhotos() {
- SQLiteStatement insert = mTargetDb.compileStatement(PhotoInsert.INSERT_SQL);
- SQLiteStatement photoIdUpdate = mTargetDb.compileStatement(PhotoIdUpdate.UPDATE_SQL);
- Cursor c = mSourceDb.query(PhotosQuery.TABLE, PhotosQuery.COLUMNS, null, null,
- null, null, null);
- try {
- while (c.moveToNext()) {
- insertPhoto(c, insert, photoIdUpdate);
- }
- } finally {
- c.close();
- insert.close();
- photoIdUpdate.close();
- }
- }
-
- private void insertPhoto(Cursor c, SQLiteStatement insert, SQLiteStatement photoIdUpdate) {
- if (c.isNull(PhotosQuery.DATA)) {
- return;
- }
-
- long personId = c.getLong(PhotosQuery.PERSON);
-
- insert.bindLong(PhotoInsert.RAW_CONTACT_ID, personId);
- insert.bindLong(PhotoInsert.MIMETYPE_ID, mPhotoMimetypeId);
- insert.bindBlob(PhotoInsert.PHOTO, c.getBlob(PhotosQuery.DATA));
-
- String account = c.getString(PhotosQuery._SYNC_ACCOUNT);
- if (!TextUtils.isEmpty(account)) {
- bindString(insert, PhotoInsert.SYNC1, c.getString(PhotosQuery._SYNC_ID));
- } else {
- insert.bindNull(PhotoInsert.SYNC1);
- }
-
- long rowId = insert(insert);
- photoIdUpdate.bindLong(PhotoIdUpdate.PHOTO_ID, rowId);
- photoIdUpdate.bindLong(PhotoIdUpdate.CONTACT_ID, personId);
- photoIdUpdate.execute();
- }
-
- private interface GroupMembershipQuery {
- String TABLE = "groupmembership";
-
- String[] COLUMNS = {
- "person", "group_id", "group_sync_account", "group_sync_id"
- };
-
- static int PERSON_ID = 0;
- static int GROUP_ID = 1;
- static int GROUP_SYNC_ACCOUNT = 2;
- static int GROUP_SYNC_ID = 3;
- }
-
- private interface GroupMembershipInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" +
- Data.RAW_CONTACT_ID + "," +
- DataColumns.MIMETYPE_ID + "," +
- GroupMembership.GROUP_ROW_ID +
- ") VALUES (?,?,?)";
-
- int RAW_CONTACT_ID = 1;
- int MIMETYPE_ID = 2;
- int GROUP_ROW_ID = 3;
- }
-
- private void importGroupMemberships() {
- SQLiteStatement insert = mTargetDb.compileStatement(GroupMembershipInsert.INSERT_SQL);
- Cursor c = mSourceDb.query(GroupMembershipQuery.TABLE, GroupMembershipQuery.COLUMNS, null,
- null, null, null, null);
- try {
- while (c.moveToNext()) {
- insertGroupMembership(c, insert);
- }
- } finally {
- c.close();
- insert.close();
- }
- }
-
- private void insertGroupMembership(Cursor c, SQLiteStatement insert) {
- long personId = c.getLong(GroupMembershipQuery.PERSON_ID);
-
- long groupId = 0;
- if (c.isNull(GroupMembershipQuery.GROUP_ID)) {
- String account = c.getString(GroupMembershipQuery.GROUP_SYNC_ACCOUNT);
- if (!TextUtils.isEmpty(account)) {
- String syncId = c.getString(GroupMembershipQuery.GROUP_SYNC_ID);
-
- Cursor cursor = mTargetDb.query(Tables.GROUPS,
- new String[]{Groups._ID}, Groups.SOURCE_ID + "=?", new String[]{syncId},
- null, null, null);
- try {
- if (cursor.moveToFirst()) {
- groupId = cursor.getLong(0);
- }
- } finally {
- cursor.close();
- }
-
- if (groupId == 0) {
- ContentValues values = new ContentValues();
- values.put(Groups.ACCOUNT_NAME, account);
- values.put(Groups.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE);
- values.put(Groups.GROUP_VISIBLE, true);
- values.put(Groups.SOURCE_ID, syncId);
- groupId = mTargetDb.insert(Tables.GROUPS, null, values);
- }
- }
- } else {
- groupId = c.getLong(GroupMembershipQuery.GROUP_ID);
- }
-
- insert.bindLong(GroupMembershipInsert.RAW_CONTACT_ID, personId);
- insert.bindLong(GroupMembershipInsert.MIMETYPE_ID, mGroupMembershipMimetypeId);
- insert.bindLong(GroupMembershipInsert.GROUP_ROW_ID, groupId);
- insert(insert);
- }
-
- private interface CallsQuery {
- String TABLE = "calls";
-
- String[] COLUMNS = {
- "_id", "number", "date", "duration", "type", "new", "name", "numbertype",
- "numberlabel"
- };
-
- static int ID = 0;
- static int NUMBER = 1;
- static int DATE = 2;
- static int DURATION = 3;
- static int TYPE = 4;
- static int NEW = 5;
- static int NAME = 6;
- static int NUMBER_TYPE = 7;
- static int NUMBER_LABEL = 8;
- }
-
- private void importCalls() {
- Cursor c = mSourceDb.query(CallsQuery.TABLE, CallsQuery.COLUMNS, null, null,
- null, null, null);
- try {
- while (c.moveToNext()) {
- insertCall(c);
- }
- } finally {
- c.close();
- }
- }
-
- private void insertCall(Cursor c) {
-
- // Cannot use batch operations here, because call log is serviced by a separate provider
- mValues.clear();
- mValues.put(Calls._ID, c.getLong(CallsQuery.ID));
- mValues.put(Calls.NUMBER, c.getString(CallsQuery.NUMBER));
- mValues.put(Calls.DATE, c.getLong(CallsQuery.DATE));
- mValues.put(Calls.DURATION, c.getLong(CallsQuery.DURATION));
- mValues.put(Calls.NEW, c.getLong(CallsQuery.NEW));
- mValues.put(Calls.TYPE, c.getLong(CallsQuery.TYPE));
- mValues.put(Calls.CACHED_NAME, c.getString(CallsQuery.NAME));
- mValues.put(Calls.CACHED_NUMBER_LABEL, c.getString(CallsQuery.NUMBER_LABEL));
- mValues.put(Calls.CACHED_NUMBER_TYPE, c.getString(CallsQuery.NUMBER_TYPE));
-
- // TODO: confirm that we can use the CallLogProvider at this point, that it is guaranteed
- // to have been registered.
- mResolver.insert(Calls.CONTENT_URI, mValues);
- }
-
- private void updateDisplayNamesAndLookupKeys() {
- // Compute display names, sort keys, lookup key, etc. for all Raw Cont
- Cursor cursor = mResolver.query(RawContacts.CONTENT_URI,
- new String[] { RawContacts._ID }, null, null, null);
- try {
- while (cursor.moveToNext()) {
- long rawContactId = cursor.getLong(0);
- mDbHelper.updateRawContactDisplayName(mTargetDb, rawContactId);
- mContactsProvider.updateLookupKeyForRawContact(mTargetDb, rawContactId);
- }
- } finally {
- cursor.close();
- }
- }
-
- private interface DeletedPeopleQuery {
- String TABLE = "_deleted_people";
-
- String[] COLUMNS = {
- "_sync_id", "_sync_account"
- };
-
- static int _SYNC_ID = 0;
- static int _SYNC_ACCOUNT = 1;
- }
-
- private interface DeletedRawContactInsert {
- String INSERT_SQL = "INSERT INTO " + Tables.RAW_CONTACTS + "(" +
- RawContacts.ACCOUNT_NAME + "," +
- RawContacts.ACCOUNT_TYPE + "," +
- RawContacts.SOURCE_ID + "," +
- RawContacts.DELETED + "," +
- RawContacts.AGGREGATION_MODE +
- ") VALUES (?,?,?,?,?)";
-
-
- int ACCOUNT_NAME = 1;
- int ACCOUNT_TYPE = 2;
- int SOURCE_ID = 3;
- int DELETED = 4;
- int AGGREGATION_MODE = 5;
- }
-
- private void importDeletedPeople() {
- SQLiteStatement insert = mTargetDb.compileStatement(DeletedRawContactInsert.INSERT_SQL);
- Cursor c = mSourceDb.query(DeletedPeopleQuery.TABLE, DeletedPeopleQuery.COLUMNS, null, null,
- null, null, null);
- try {
- while (c.moveToNext()) {
- insertDeletedPerson(c, insert);
- }
- } finally {
- c.close();
- insert.close();
- }
- }
-
- private void insertDeletedPerson(Cursor c, SQLiteStatement insert) {
- String account = c.getString(DeletedPeopleQuery._SYNC_ACCOUNT);
- if (account == null) {
- return;
- }
-
- insert.bindString(DeletedRawContactInsert.ACCOUNT_NAME, account);
- insert.bindString(DeletedRawContactInsert.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE);
- bindString(insert, DeletedRawContactInsert.SOURCE_ID,
- c.getString(DeletedPeopleQuery._SYNC_ID));
- insert.bindLong(DeletedRawContactInsert.DELETED, 1);
- insert.bindLong(DeletedRawContactInsert.AGGREGATION_MODE,
- RawContacts.AGGREGATION_MODE_DISABLED);
- insert(insert);
- }
-
- private void bindString(SQLiteStatement insert, int index, String string) {
- if (string == null) {
- insert.bindNull(index);
- } else {
- insert.bindString(index, string);
- }
- }
-
- private long insert(SQLiteStatement insertStatement) {
- long rowId = insertStatement.executeInsert();
- if (rowId == 0) {
- throw new RuntimeException("Insert failed");
- }
-
- mBatchCounter++;
- if (mBatchCounter >= INSERT_BATCH_SIZE) {
- mTargetDb.setTransactionSuccessful();
- mTargetDb.endTransaction();
- mTargetDb.beginTransaction();
- mBatchCounter = 0;
- }
- return rowId;
- }
-}
diff --git a/src/com/android/providers/contacts/MemoryCursor.java b/src/com/android/providers/contacts/MemoryCursor.java
new file mode 100644
index 0000000..23402a6
--- /dev/null
+++ b/src/com/android/providers/contacts/MemoryCursor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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
+ */
+package com.android.providers.contacts;
+
+import android.database.AbstractWindowedCursor;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.database.DatabaseUtils;
+
+/**
+ * Implementation of an in-memory cursor backed by a cursor window.
+ */
+public class MemoryCursor extends AbstractWindowedCursor {
+
+ private final String[] mColumnNames;
+
+ public MemoryCursor(String name, String[] columnNames) {
+ setWindow(new CursorWindow(name));
+ mColumnNames = columnNames;
+ }
+
+ public void fillFromCursor(Cursor cursor) {
+ DatabaseUtils.cursorFillWindow(cursor, 0, getWindow());
+ }
+
+ @Override
+ public int getCount() {
+ return getWindow().getNumRows();
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return mColumnNames;
+ }
+}
diff --git a/src/com/android/providers/contacts/NameSplitter.java b/src/com/android/providers/contacts/NameSplitter.java
index 27e5056..fd5b096 100644
--- a/src/com/android/providers/contacts/NameSplitter.java
+++ b/src/com/android/providers/contacts/NameSplitter.java
@@ -15,6 +15,8 @@
*/
package com.android.providers.contacts;
+import com.android.providers.contacts.util.NeededForTesting;
+
import android.content.ContentValues;
import android.provider.ContactsContract.FullNameStyle;
import android.provider.ContactsContract.PhoneticNameStyle;
@@ -106,6 +108,7 @@
this.suffix = suffix;
}
+ @NeededForTesting
public String getPrefix() {
return prefix;
}
@@ -122,6 +125,7 @@
return familyName;
}
+ @NeededForTesting
public String getSuffix() {
return suffix;
}
diff --git a/src/com/android/providers/contacts/ProfileProvider.java b/src/com/android/providers/contacts/ProfileProvider.java
index d40f1fd..ca77620 100644
--- a/src/com/android/providers/contacts/ProfileProvider.java
+++ b/src/com/android/providers/contacts/ProfileProvider.java
@@ -15,6 +15,7 @@
*/
package com.android.providers.contacts;
+import android.content.CancelationSignal;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -73,9 +74,16 @@
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
+ return query(uri, projection, selection, selectionArgs, sortOrder, null);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder, CancelationSignal cancelationSignal) {
enforceReadPermission(uri);
mDelegate.substituteDb(getDatabaseHelper().getReadableDatabase());
- return mDelegate.queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1);
+ return mDelegate.queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1,
+ cancelationSignal);
}
@Override
diff --git a/src/com/android/providers/contacts/ProjectionMap.java b/src/com/android/providers/contacts/ProjectionMap.java
deleted file mode 100644
index f4c76d6..0000000
--- a/src/com/android/providers/contacts/ProjectionMap.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * 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
- */
-
-package com.android.providers.contacts;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A convenience wrapper for a projection map. Makes it easier to create and use projection maps.
- */
-public class ProjectionMap extends HashMap<String, String> {
-
- public static class Builder {
-
- private ProjectionMap mMap = new ProjectionMap();
-
- public Builder add(String column) {
- mMap.putColumn(column, column);
- return this;
- }
-
- public Builder add(String alias, String expression) {
- mMap.putColumn(alias, expression + " AS " + alias);
- return this;
- }
-
- public Builder addAll(ProjectionMap map) {
- for (Map.Entry<String, String> entry : map.entrySet()) {
- mMap.putColumn(entry.getKey(), entry.getValue());
- }
- return this;
- }
-
- public ProjectionMap build() {
- String[] columns = new String[mMap.size()];
- mMap.keySet().toArray(columns);
- Arrays.sort(columns);
- mMap.mColumns = columns;
- return mMap;
- }
-
- }
-
- private String[] mColumns;
-
- public static Builder builder() {
- return new Builder();
- }
-
- /**
- * Returns a sorted array of all column names in the projection map.
- */
- public String[] getColumnNames() {
- return mColumns;
- }
-
- private void putColumn(String alias, String column) {
- super.put(alias, column);
- }
-
- @Override
- public String put(String key, String value) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void putAll(Map<? extends String, ? extends String> map) {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/src/com/android/providers/contacts/SearchIndexManager.java b/src/com/android/providers/contacts/SearchIndexManager.java
index c989fdc..2174f69 100644
--- a/src/com/android/providers/contacts/SearchIndexManager.java
+++ b/src/com/android/providers/contacts/SearchIndexManager.java
@@ -17,6 +17,7 @@
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
@@ -30,6 +31,7 @@
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.ProviderStatus;
+import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.util.Log;
@@ -240,7 +242,7 @@
int count = 0;
try {
mDbHelper.createSearchIndexTable(db);
- count = buildIndex(db, null, false);
+ count = buildAndInsertIndex(db, null);
} finally {
mContactsProvider.setProviderStatus(ProviderStatus.STATUS_NORMAL);
@@ -254,7 +256,7 @@
mSb.setLength(0);
mSb.append("(");
if (!contactIds.isEmpty()) {
- mSb.append(Data.CONTACT_ID + " IN (");
+ mSb.append(RawContacts.CONTACT_ID + " IN (");
for (Long contactId : contactIds) {
mSb.append(contactId).append(",");
}
@@ -266,7 +268,7 @@
if (!contactIds.isEmpty()) {
mSb.append(" OR ");
}
- mSb.append(Data.RAW_CONTACT_ID + " IN (");
+ mSb.append(RawContactsColumns.CONCRETE_ID + " IN (");
for (Long rawContactId : rawContactIds) {
mSb.append(rawContactId).append(",");
}
@@ -275,10 +277,25 @@
}
mSb.append(")");
- buildIndex(mDbHelper.getWritableDatabase(), mSb.toString(), true);
+
+ // The selection to select raw_contacts.
+ final String rawContactsSelection = mSb.toString();
+
+ // Remove affected search_index rows.
+ final SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ final int deleted = db.delete(Tables.SEARCH_INDEX,
+ SearchIndexColumns.CONTACT_ID + " IN (SELECT " +
+ RawContacts.CONTACT_ID +
+ " FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + rawContactsSelection +
+ ")"
+ , null);
+
+ // Then rebuild index for them.
+ buildAndInsertIndex(db, rawContactsSelection);
}
- private int buildIndex(SQLiteDatabase db, String selection, boolean replace) {
+ private int buildAndInsertIndex(SQLiteDatabase db, String selection) {
mSb.setLength(0);
mSb.append(Data.CONTACT_ID + ", ");
mSb.append("(CASE WHEN " + DataColumns.MIMETYPE_ID + "=");
@@ -307,7 +324,7 @@
long contactId = cursor.getLong(0);
if (contactId != currentContactId) {
if (currentContactId != -1) {
- saveContactIndex(db, currentContactId, mIndexBuilder, replace);
+ insertIndexRow(db, currentContactId, mIndexBuilder);
count++;
}
currentContactId = contactId;
@@ -321,7 +338,7 @@
}
}
if (currentContactId != -1) {
- saveContactIndex(db, currentContactId, mIndexBuilder, replace);
+ insertIndexRow(db, currentContactId, mIndexBuilder);
count++;
}
} finally {
@@ -330,24 +347,13 @@
return count;
}
- private void saveContactIndex(
- SQLiteDatabase db, long contactId, IndexBuilder builder, boolean replace) {
+ private void insertIndexRow(SQLiteDatabase db, long contactId, IndexBuilder builder) {
mValues.clear();
mValues.put(SearchIndexColumns.CONTENT, builder.getContent());
mValues.put(SearchIndexColumns.NAME, builder.getName());
mValues.put(SearchIndexColumns.TOKENS, builder.getTokens());
- if (replace) {
- mSelectionArgs1[0] = String.valueOf(contactId);
- int count = db.update(Tables.SEARCH_INDEX, mValues,
- SearchIndexColumns.CONTACT_ID + "=CAST(? AS int)", mSelectionArgs1);
- if (count == 0) {
- mValues.put(SearchIndexColumns.CONTACT_ID, contactId);
- db.insert(Tables.SEARCH_INDEX, null, mValues);
- }
- } else {
- mValues.put(SearchIndexColumns.CONTACT_ID, contactId);
- db.insert(Tables.SEARCH_INDEX, null, mValues);
- }
+ mValues.put(SearchIndexColumns.CONTACT_ID, contactId);
+ db.insert(Tables.SEARCH_INDEX, null, mValues);
}
private int getSearchIndexVersion() {
return Integer.parseInt(mDbHelper.getProperty(PROPERTY_SEARCH_INDEX_VERSION, "0"));
diff --git a/src/com/android/providers/contacts/TransactionContext.java b/src/com/android/providers/contacts/TransactionContext.java
index aee2fe2..4f880f7 100644
--- a/src/com/android/providers/contacts/TransactionContext.java
+++ b/src/com/android/providers/contacts/TransactionContext.java
@@ -32,7 +32,8 @@
public class TransactionContext {
private final boolean mForProfile;
- private HashMap<Long, AccountWithDataSet> mInsertedRawContacts = Maps.newHashMap();
+ /** Map from raw contact id to account Id */
+ private HashMap<Long, Long> mInsertedRawContactsAccounts = Maps.newHashMap();
private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
private HashSet<Long> mStaleSearchIndexRawContacts = Sets.newHashSet();
@@ -47,8 +48,8 @@
return mForProfile;
}
- public void rawContactInserted(long rawContactId, AccountWithDataSet accountWithDataSet) {
- mInsertedRawContacts.put(rawContactId, accountWithDataSet);
+ public void rawContactInserted(long rawContactId, long accountId) {
+ mInsertedRawContactsAccounts.put(rawContactId, accountId);
}
public void rawContactUpdated(long rawContactId) {
@@ -72,7 +73,7 @@
}
public Set<Long> getInsertedRawContactIds() {
- return mInsertedRawContacts.keySet();
+ return mInsertedRawContactsAccounts.keySet();
}
public Set<Long> getUpdatedRawContactIds() {
@@ -95,16 +96,16 @@
return mUpdatedSyncStates.entrySet();
}
- public AccountWithDataSet getAccountWithDataSetForRawContact(long rawContactId) {
- return mInsertedRawContacts.get(rawContactId);
+ public Long getAccountIdOrNullForRawContact(long rawContactId) {
+ return mInsertedRawContactsAccounts.get(rawContactId);
}
public boolean isNewRawContact(long rawContactId) {
- return mInsertedRawContacts.containsKey(rawContactId);
+ return mInsertedRawContactsAccounts.containsKey(rawContactId);
}
public void clear() {
- mInsertedRawContacts.clear();
+ mInsertedRawContactsAccounts.clear();
mUpdatedRawContacts.clear();
mUpdatedSyncStates.clear();
mDirtyRawContacts.clear();
diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java
index ec1fa38..79d8f92 100644
--- a/src/com/android/providers/contacts/VoicemailContentProvider.java
+++ b/src/com/android/providers/contacts/VoicemailContentProvider.java
@@ -49,8 +49,6 @@
*/
public class VoicemailContentProvider extends ContentProvider
implements VoicemailTable.DelegateHelper {
- private static final String TAG = "VoicemailContentProvider";
-
private VoicemailPermissions mVoicemailPermissions;
private VoicemailTable.Delegate mVoicemailContentTable;
private VoicemailTable.Delegate mVoicemailStatusTable;
diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java
index 4d23531..3b72653 100644
--- a/src/com/android/providers/contacts/VoicemailContentTable.java
+++ b/src/com/android/providers/contacts/VoicemailContentTable.java
@@ -19,6 +19,7 @@
import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
+import com.android.common.content.ProjectionMap;
import com.android.providers.contacts.VoicemailContentProvider.UriData;
import com.android.providers.contacts.util.CloseUtils;
@@ -245,7 +246,7 @@
/** Creates a clause to restrict the selection to only voicemail call type.*/
private String getCallTypeClause() {
- return getEqualityClause(Calls.TYPE, String.valueOf(Calls.VOICEMAIL_TYPE));
+ return getEqualityClause(Calls.TYPE, Calls.VOICEMAIL_TYPE);
}
private DatabaseModifier getDatabaseModifier(SQLiteDatabase db) {
diff --git a/src/com/android/providers/contacts/VoicemailStatusTable.java b/src/com/android/providers/contacts/VoicemailStatusTable.java
index 24714ed..a0a61ba 100644
--- a/src/com/android/providers/contacts/VoicemailStatusTable.java
+++ b/src/com/android/providers/contacts/VoicemailStatusTable.java
@@ -18,6 +18,7 @@
import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
+import com.android.common.content.ProjectionMap;
import com.android.providers.contacts.VoicemailContentProvider.UriData;
import android.content.ContentUris;
diff --git a/src/com/android/providers/contacts/util/DbQueryUtils.java b/src/com/android/providers/contacts/util/DbQueryUtils.java
index 58c8bb1..c853a96 100644
--- a/src/com/android/providers/contacts/util/DbQueryUtils.java
+++ b/src/com/android/providers/contacts/util/DbQueryUtils.java
@@ -34,8 +34,13 @@
return getClauseWithOperator(field, "=", value);
}
+ /** Returns a WHERE clause asserting equality of a field to a value. */
+ public static String getEqualityClause(String field, long value) {
+ return getClauseWithOperator(field, "=", value);
+ }
+
/** Returns a WHERE clause asserting in-equality of a field to a value. */
- public static String getInequalityClause(String field, String value) {
+ public static String getInequalityClause(String field, long value) {
return getClauseWithOperator(field, "!=", value);
}
@@ -49,6 +54,16 @@
return clause.toString();
}
+ private static String getClauseWithOperator(String field, String operator, long value) {
+ StringBuilder clause = new StringBuilder();
+ clause.append("(");
+ clause.append(field);
+ clause.append(" ").append(operator).append(" ");
+ clause.append(value);
+ clause.append(")");
+ return clause.toString();
+ }
+
/** Concatenates any number of clauses using "AND". */
public static String concatenateClauses(String... clauses) {
StringBuilder builder = new StringBuilder();
diff --git a/src/com/android/providers/contacts/util/NeededForTesting.java b/src/com/android/providers/contacts/util/NeededForTesting.java
new file mode 100644
index 0000000..0eb418e
--- /dev/null
+++ b/src/com/android/providers/contacts/util/NeededForTesting.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.providers.contacts.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the class, constructor, method or field is used by tests and therefore cannot be
+ * removed by tools like ProGuard.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
+public @interface NeededForTesting {}
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 33a39ec..b3b3c28 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -41,6 +41,7 @@
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Contacts;
@@ -337,6 +338,23 @@
return resultUri;
}
+ protected Uri insertSipAddress(long rawContactId, String sipAddress) {
+ return insertSipAddress(rawContactId, sipAddress, false);
+ }
+
+ protected Uri insertSipAddress(long rawContactId, String sipAddress, boolean primary) {
+ ContentValues values = new ContentValues();
+ values.put(Data.RAW_CONTACT_ID, rawContactId);
+ values.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
+ values.put(SipAddress.SIP_ADDRESS, sipAddress);
+ if (primary) {
+ values.put(Data.IS_PRIMARY, 1);
+ }
+
+ Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
+ return resultUri;
+ }
+
protected Uri insertNickname(long rawContactId, String nickname) {
ContentValues values = new ContentValues();
values.put(Data.RAW_CONTACT_ID, rawContactId);
@@ -1055,9 +1073,7 @@
private boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues,
StringBuilder msgBuffer) {
- Set<Map.Entry<String, Object>> entries = expectedValues.valueSet();
- for (Map.Entry<String, Object> entry : entries) {
- String column = entry.getKey();
+ for (String column : expectedValues.keySet()) {
int index = cursor.getColumnIndex(column);
if (index == -1) {
msgBuffer.append("No such column: ").append(column);
diff --git a/tests/src/com/android/providers/contacts/ContactAggregatorPerformanceTest.java b/tests/src/com/android/providers/contacts/ContactAggregatorPerformanceTest.java
deleted file mode 100644
index 013a706..0000000
--- a/tests/src/com/android/providers/contacts/ContactAggregatorPerformanceTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * 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.
- */
-
-package com.android.providers.contacts;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
-import android.os.Debug;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.IsolatedContext;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
-import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-
-/**
- * Performance test for {@link ContactAggregator}.
- *
- * Run the test like this:
- * <code>
- * adb push <large contacts2.db> \
- * data/data/com.android.providers.contacts/databases/perf.contacts2.db
- * adb shell am instrument \
- * -e class com.android.providers.contacts.ContactAggregatorPerformanceTest \
- * -w com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
- * </code>
- */
-@LargeTest
-public class ContactAggregatorPerformanceTest extends AndroidTestCase {
-
- private static final String TAG = "ContactAggregatorPerformanceTest";
- private static final boolean TRACE = false;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- SynchronousContactsProvider2.resetOpenHelper();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- SynchronousContactsProvider2.resetOpenHelper();
- }
-
- public void testPerformance() {
- final Context targetContext = getContext();
- MockContentResolver resolver = new MockContentResolver();
- MockContext context = new MockContext() {
- @Override
- public Resources getResources() {
- return targetContext.getResources();
- }
-
- @Override
- public String getPackageName() {
- return "no.package";
- }
-
- @Override
- public ApplicationInfo getApplicationInfo() {
- ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = "contactsTestPackage";
- return ai;
- }
- };
- RenamingDelegatingContext targetContextWrapper =
- new RenamingDelegatingContext(context, targetContext, "perf.");
- targetContextWrapper.makeExistingFilesAndDbsAccessible();
- IsolatedContext providerContext = new IsolatedContext(resolver, targetContextWrapper);
- SynchronousContactsProvider2 provider = new SynchronousContactsProvider2();
- provider.setDataWipeEnabled(false);
- provider.attachInfo(providerContext, null);
- resolver.addProvider(ContactsContract.AUTHORITY, provider);
-
- long rawContactCount = provider.getRawContactCount();
- if (rawContactCount == 0) {
- Log.w(TAG, "The test has not been set up. Use this command to copy a contact db"
- + " to the device:\nadb push <large contacts2.db> "
- + "data/data/com.android.providers.contacts/databases/perf.contacts2.db");
- return;
- }
-
- provider.prepareForFullAggregation(500);
- rawContactCount = provider.getRawContactCount();
- long start = System.currentTimeMillis();
- if (TRACE) {
- Debug.startMethodTracing("aggregation");
- }
-
- // TODO
-// provider.aggregate();
-
- if (TRACE) {
- Debug.stopMethodTracing();
- }
- long end = System.currentTimeMillis();
- long contactCount = provider.getContactCount();
-
- Log.i(TAG, String.format("Aggregated contacts in %d ms.\n" +
- "Raw contacts: %d\n" +
- "Aggregated contacts: %d\n" +
- "Per raw contact: %.3f",
- end-start,
- rawContactCount,
- contactCount,
- ((double)(end-start)/rawContactCount)));
-
- provider.getDatabaseHelper().close();
- }
-}
-
diff --git a/tests/src/com/android/providers/contacts/ContactsDatabaseHelperTest.java b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperTest.java
new file mode 100644
index 0000000..bbfba1e
--- /dev/null
+++ b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.providers.contacts;
+
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.google.android.collect.Sets;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.Set;
+
+@SmallTest
+public class ContactsDatabaseHelperTest extends BaseContactsProvider2Test {
+ private ContactsDatabaseHelper mDbHelper;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDbHelper = getContactsProvider().getDatabaseHelper(getContext());
+ }
+
+ public void testGetOrCreateAccountId() {
+ final AccountWithDataSet a1 = null;
+ final AccountWithDataSet a2 = new AccountWithDataSet("a", null, null);
+ final AccountWithDataSet a3 = new AccountWithDataSet(null, "b", null);
+ final AccountWithDataSet a4 = new AccountWithDataSet(null, null, "c");
+ final AccountWithDataSet a5 = new AccountWithDataSet("a", "b", "c");
+
+ // First, there's no accounts. getAccountIdOrNull() always returns null.
+ assertNull(mDbHelper.getAccountIdOrNull(a1));
+ assertNull(mDbHelper.getAccountIdOrNull(a2));
+ assertNull(mDbHelper.getAccountIdOrNull(a3));
+ assertNull(mDbHelper.getAccountIdOrNull(a4));
+ assertNull(mDbHelper.getAccountIdOrNull(a5));
+
+ // getOrCreateAccountId should create accounts.
+ final long a1id = mDbHelper.getOrCreateAccountIdInTransaction(a1);
+ final long a2id = mDbHelper.getOrCreateAccountIdInTransaction(a2);
+ final long a3id = mDbHelper.getOrCreateAccountIdInTransaction(a3);
+ final long a4id = mDbHelper.getOrCreateAccountIdInTransaction(a4);
+ final long a5id = mDbHelper.getOrCreateAccountIdInTransaction(a5);
+
+ // The IDs should be all positive and unique.
+ assertTrue(a1id > 0);
+ assertTrue(a2id > 0);
+ assertTrue(a3id > 0);
+ assertTrue(a4id > 0);
+ assertTrue(a5id > 0);
+
+ final Set<Long> ids = Sets.newHashSet();
+ ids.add(a1id);
+ ids.add(a2id);
+ ids.add(a3id);
+ ids.add(a4id);
+ ids.add(a5id);
+ assertEquals(5, ids.size());
+
+ // Second call: This time getOrCreateAccountId will return the existing IDs.
+ assertEquals(a1id, mDbHelper.getOrCreateAccountIdInTransaction(a1));
+ assertEquals(a2id, mDbHelper.getOrCreateAccountIdInTransaction(a2));
+ assertEquals(a3id, mDbHelper.getOrCreateAccountIdInTransaction(a3));
+ assertEquals(a4id, mDbHelper.getOrCreateAccountIdInTransaction(a4));
+ assertEquals(a5id, mDbHelper.getOrCreateAccountIdInTransaction(a5));
+
+ // Now getAccountIdOrNull() returns IDs too.
+ assertEquals((Long) a1id, mDbHelper.getAccountIdOrNull(a1));
+ assertEquals((Long) a2id, mDbHelper.getAccountIdOrNull(a2));
+ assertEquals((Long) a3id, mDbHelper.getAccountIdOrNull(a3));
+ assertEquals((Long) a4id, mDbHelper.getAccountIdOrNull(a4));
+ assertEquals((Long) a5id, mDbHelper.getAccountIdOrNull(a5));
+
+ // null and AccountWithDataSet.NULL should be treated as the same thing.
+ assertEquals(a1id, mDbHelper.getOrCreateAccountIdInTransaction(AccountWithDataSet.LOCAL));
+ assertEquals((Long) a1id, mDbHelper.getAccountIdOrNull(AccountWithDataSet.LOCAL));
+
+ // Clear the table, but until we refresh the cache getAccountIdOrNull() keeps returning
+ // cached values.
+ mDbHelper.getWritableDatabase().execSQL("delete from " + Tables.ACCOUNTS);
+ assertEquals((Long) a1id, mDbHelper.getAccountIdOrNull(AccountWithDataSet.LOCAL));
+
+ // refreshAccountCache() clears the cache.
+ mDbHelper.refreshAccountCache();
+
+ assertNull(mDbHelper.getAccountIdOrNull(AccountWithDataSet.LOCAL));
+ assertNull(mDbHelper.getAccountIdOrNull(a1));
+ assertNull(mDbHelper.getAccountIdOrNull(a2));
+ assertNull(mDbHelper.getAccountIdOrNull(a3));
+ assertNull(mDbHelper.getAccountIdOrNull(a4));
+ assertNull(mDbHelper.getAccountIdOrNull(a5));
+
+ // Logically same as a5, but physically different object.
+ final AccountWithDataSet a5b = new AccountWithDataSet("a", "b", "c");
+ // a5 and a5b should have the same ID.
+ assertEquals(
+ mDbHelper.getOrCreateAccountIdInTransaction(a5),
+ mDbHelper.getOrCreateAccountIdInTransaction(a5b));
+ }
+}
diff --git a/tests/src/com/android/providers/contacts/ContactsMockResources.java b/tests/src/com/android/providers/contacts/ContactsMockResources.java
index d1ec817..248d6da 100644
--- a/tests/src/com/android/providers/contacts/ContactsMockResources.java
+++ b/tests/src/com/android/providers/contacts/ContactsMockResources.java
@@ -16,9 +16,8 @@
package com.android.providers.contacts;
-import com.google.common.collect.Maps;
+import com.google.android.collect.Maps;
-import android.content.res.Resources.NotFoundException;
import android.test.mock.MockResources;
import java.util.Map;
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index cbb1c49..046dec8 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -19,9 +19,11 @@
import com.android.internal.util.ArrayUtils;
import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
import com.android.providers.contacts.tests.R;
import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
import android.accounts.Account;
import android.content.ContentProviderOperation;
@@ -36,12 +38,14 @@
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.CommonDataKinds.Callable;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.ContactCounts;
@@ -78,6 +82,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
/**
* Unit tests for {@link ContactsProvider2}.
@@ -556,6 +561,7 @@
Groups.SYNC4,
Groups.SUMMARY_COUNT,
Groups.SUMMARY_WITH_PHONES,
+ Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
});
}
@@ -619,6 +625,7 @@
values.put(RawContacts.ACCOUNT_NAME, "a");
values.put(RawContacts.ACCOUNT_TYPE, "b");
+ values.put(RawContacts.DATA_SET, "ds");
values.put(RawContacts.SOURCE_ID, "c");
values.put(RawContacts.VERSION, 42);
values.put(RawContacts.DIRTY, 1);
@@ -917,15 +924,33 @@
}
public void testPhonesFilterQuery() {
- long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1);
+ testPhonesFilterQueryInter(Phone.CONTENT_FILTER_URI);
+ }
+
+ /**
+ * A convenient method for {@link #testPhonesFilterQuery()} and
+ * {@link #testCallablesFilterQuery()}.
+ *
+ * This confirms if both URIs return identical results for phone-only contacts and
+ * appropriately different results for contacts with sip addresses.
+ *
+ * @param baseFilterUri Either {@link Phone#CONTENT_FILTER_URI} or
+ * {@link Callable#CONTENT_FILTER_URI}.
+ */
+ private void testPhonesFilterQueryInter(Uri baseFilterUri) {
+ assertTrue("Unsupported Uri (" + baseFilterUri + ")",
+ Phone.CONTENT_FILTER_URI.equals(baseFilterUri)
+ || Callable.CONTENT_FILTER_URI.equals(baseFilterUri));
+
+ final long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1);
insertPhoneNumber(rawContactId1, "1-800-466-4411");
- long rawContactId2 = createRawContactWithName("Chilled", "Guacamole", ACCOUNT_2);
+ final long rawContactId2 = createRawContactWithName("Chilled", "Guacamole", ACCOUNT_2);
insertPhoneNumber(rawContactId2, "1-800-466-5432");
insertPhoneNumber(rawContactId2, "0@example.com", false, Phone.TYPE_PAGER);
insertPhoneNumber(rawContactId2, "1@example.com", false, Phone.TYPE_PAGER);
- Uri filterUri1 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "tamale");
+ final Uri filterUri1 = Uri.withAppendedPath(baseFilterUri, "tamale");
ContentValues values = new ContentValues();
values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
@@ -934,16 +959,16 @@
values.putNull(Phone.LABEL);
assertStoredValuesWithProjection(filterUri1, values);
- Uri filterUri2 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-GOOG-411");
+ final Uri filterUri2 = Uri.withAppendedPath(baseFilterUri, "1-800-GOOG-411");
assertStoredValues(filterUri2, values);
- Uri filterUri3 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "18004664");
+ final Uri filterUri3 = Uri.withAppendedPath(baseFilterUri, "18004664");
assertStoredValues(filterUri3, values);
- Uri filterUri4 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "encilada");
+ final Uri filterUri4 = Uri.withAppendedPath(baseFilterUri, "encilada");
assertEquals(0, getCount(filterUri4, null, null));
- Uri filterUri5 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "*");
+ final Uri filterUri5 = Uri.withAppendedPath(baseFilterUri, "*");
assertEquals(0, getCount(filterUri5, null, null));
ContentValues values1 = new ContentValues();
@@ -967,7 +992,42 @@
values3.put(Phone.TYPE, Phone.TYPE_PAGER);
values3.putNull(Phone.LABEL);
- Uri filterUri6 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "Chilled");
+ final Uri filterUri6 = Uri.withAppendedPath(baseFilterUri, "Chilled");
+ assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} );
+
+ // Insert a SIP address. From here, Phone URI and Callable URI may return different results
+ // than each other.
+ insertSipAddress(rawContactId1, "sip_hot_tamale@example.com");
+ insertSipAddress(rawContactId1, "sip:sip_hot@example.com");
+
+ final Uri filterUri7 = Uri.withAppendedPath(baseFilterUri, "sip_hot");
+ final Uri filterUri8 = Uri.withAppendedPath(baseFilterUri, "sip_hot_tamale");
+ if (Callable.CONTENT_FILTER_URI.equals(baseFilterUri)) {
+ ContentValues values4 = new ContentValues();
+ values4.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+ values4.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
+ values4.put(SipAddress.SIP_ADDRESS, "sip_hot_tamale@example.com");
+
+ ContentValues values5 = new ContentValues();
+ values5.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+ values5.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
+ values5.put(SipAddress.SIP_ADDRESS, "sip:sip_hot@example.com");
+ assertStoredValues(filterUri1, new ContentValues[] {values, values4, values5});
+
+ assertStoredValues(filterUri7, new ContentValues[] {values4, values5});
+ assertStoredValues(filterUri8, values4);
+ } else {
+ // Sip address should not affect Phone URI.
+ assertStoredValuesWithProjection(filterUri1, values);
+ assertEquals(0, getCount(filterUri7, null, null));
+ }
+
+ // Sanity test. Run tests for "Chilled Guacamole" again and see nothing changes
+ // after the Sip address being inserted.
+ assertStoredValues(filterUri2, values);
+ assertStoredValues(filterUri3, values);
+ assertEquals(0, getCount(filterUri4, null, null));
+ assertEquals(0, getCount(filterUri5, null, null));
assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} );
}
@@ -1171,6 +1231,43 @@
assertNetworkNotified(true);
}
+ /** Tests if {@link Callable#CONTENT_URI} returns both phones and sip addresses. */
+ public void testCallablesQuery() {
+ long rawContactId1 = createRawContactWithName("Meghan", "Knox");
+ long phoneId1 = ContentUris.parseId(insertPhoneNumber(rawContactId1, "18004664411"));
+ long contactId1 = queryContactId(rawContactId1);
+
+ long rawContactId2 = createRawContactWithName("John", "Doe");
+ long sipAddressId2 = ContentUris.parseId(
+ insertSipAddress(rawContactId2, "sip@example.com"));
+ long contactId2 = queryContactId(rawContactId2);
+
+ ContentValues values1 = new ContentValues();
+ values1.put(Data._ID, phoneId1);
+ values1.put(Data.RAW_CONTACT_ID, rawContactId1);
+ values1.put(RawContacts.CONTACT_ID, contactId1);
+ values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ values1.put(Phone.NUMBER, "18004664411");
+ values1.put(Phone.TYPE, Phone.TYPE_HOME);
+ values1.putNull(Phone.LABEL);
+ values1.put(Contacts.DISPLAY_NAME, "Meghan Knox");
+
+ ContentValues values2 = new ContentValues();
+ values2.put(Data._ID, sipAddressId2);
+ values2.put(Data.RAW_CONTACT_ID, rawContactId2);
+ values2.put(RawContacts.CONTACT_ID, contactId2);
+ values2.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
+ values2.put(SipAddress.SIP_ADDRESS, "sip@example.com");
+ values2.put(Contacts.DISPLAY_NAME, "John Doe");
+
+ assertEquals(2, getCount(Callable.CONTENT_URI, null, null));
+ assertStoredValues(Callable.CONTENT_URI, new ContentValues[] { values1, values2 });
+ }
+
+ public void testCallablesFilterQuery() {
+ testPhonesFilterQueryInter(Callable.CONTENT_FILTER_URI);
+ }
+
public void testEmailsQuery() {
ContentValues values = new ContentValues();
values.put(RawContacts.CUSTOM_RINGTONE, "d");
@@ -2173,6 +2270,7 @@
values.put(Groups.ACCOUNT_NAME, "a");
values.put(Groups.ACCOUNT_TYPE, "b");
+ values.put(Groups.DATA_SET, "ds");
values.put(Groups.SOURCE_ID, "c");
values.put(Groups.VERSION, 42);
values.put(Groups.GROUP_VISIBLE, 1);
@@ -2295,19 +2393,29 @@
assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
- final Uri uri = Groups.CONTENT_SUMMARY_URI.buildUpon()
- .appendQueryParameter(Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT, "true")
- .build();
- v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1);
- v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
- v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
+ final Uri uri = Groups.CONTENT_SUMMARY_URI;
+
+ // TODO Once SUMMARY_GROUP_COUNT_PER_ACCOUNT is supported remove all the if(false).
+ if (false) {
+ v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1);
+ v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
+ v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
+ } else {
+ v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
+ v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
+ v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
+ }
assertStoredValues(uri, new ContentValues[] { v1, v2, v3 });
// Introduce another group in account1, testing SUMMARY_GROUP_COUNT_PER_ACCOUNT correctly
// reflects the change.
final long groupId4 = createGroup(account1, "sourceId4", "title4");
- v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
- v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT) + 1);
+ if (false) {
+ v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
+ v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT) + 1);
+ } else {
+ v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
+ }
ContentValues v4 = new ContentValues();
v4.put(Groups._ID, groupId4);
v4.put(Groups.TITLE, "title4");
@@ -2316,8 +2424,12 @@
v4.put(Groups.ACCOUNT_TYPE, account1.type);
v4.put(Groups.SUMMARY_COUNT, 0);
v4.put(Groups.SUMMARY_WITH_PHONES, 0);
- v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
- v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT));
+ if (false) {
+ v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
+ v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT));
+ } else {
+ v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
+ }
assertStoredValues(uri, new ContentValues[] { v1, v2, v3, v4 });
// We change the tables dynamically according to the requested projection.
@@ -2763,6 +2875,19 @@
assertStoredValues(filterUri, values);
}
+ public void testCountPhoneNumberDigits() {
+ assertEquals(10, ContactsProvider2.countPhoneNumberDigits("86 (0) 5-55-12-34"));
+ assertEquals(10, ContactsProvider2.countPhoneNumberDigits("860 555-1234"));
+ assertEquals(3, ContactsProvider2.countPhoneNumberDigits("860"));
+ assertEquals(10, ContactsProvider2.countPhoneNumberDigits("8605551234"));
+ assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860555"));
+ assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860 555"));
+ assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860-555"));
+ assertEquals(12, ContactsProvider2.countPhoneNumberDigits("+441234098765"));
+ assertEquals(0, ContactsProvider2.countPhoneNumberDigits("44+1234098765"));
+ assertEquals(0, ContactsProvider2.countPhoneNumberDigits("+441234098foo"));
+ }
+
public void testSearchSnippetPhone() throws Exception {
long rawContactId = createRawContact();
long contactId = queryContactId(rawContactId);
@@ -3458,17 +3583,6 @@
// Stream item insertion test cases.
- public void testInsertStreamItemIntoOtherAccount() {
- long rawContactId = createRawContact(mAccount);
- ContentValues values = buildGenericStreamItemValues();
- try {
- insertStreamItem(rawContactId, values, mAccountTwo);
- fail("Stream insertion was allowed in another account's raw contact.");
- } catch (SecurityException expected) {
- // Trying to insert stream items into account one's raw contact is forbidden.
- }
- }
-
public void testInsertStreamItemInProfileRequiresWriteProfileAccess() {
long profileRawContactId = createBasicProfileContact(new ContentValues());
@@ -3637,22 +3751,6 @@
RawContacts.StreamItems.CONTENT_DIRECTORY), values);
}
- public void testUpdateStreamItemFromOtherAccount() {
- long rawContactId = createRawContact(mAccount);
- ContentValues values = buildGenericStreamItemValues();
- Uri resultUri = insertStreamItem(rawContactId, values, mAccount);
- long streamItemId = ContentUris.parseId(resultUri);
- values.put(StreamItems._ID, streamItemId);
- values.put(StreamItems.TEXT, "Goodbye world");
- try {
- mResolver.update(maybeAddAccountQueryParameters(StreamItems.CONTENT_URI, mAccountTwo),
- values, null, null);
- fail("Should not be able to update stream items inserted by another account");
- } catch (SecurityException expected) {
- // Can't update the stream items from another account.
- }
- }
-
// Stream item photo update test cases.
public void testUpdateStreamItemPhotoById() throws IOException {
@@ -3708,32 +3806,6 @@
mResolver.openInputStream(Uri.parse(displayPhotoUri)));
}
- public void testUpdateStreamItemPhotoFromOtherAccount() {
- long rawContactId = createRawContact(mAccount);
- ContentValues values = buildGenericStreamItemValues();
- Uri resultUri = insertStreamItem(rawContactId, values, mAccount);
- long streamItemId = ContentUris.parseId(resultUri);
- ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
- resultUri = insertStreamItemPhoto(streamItemId, photoValues, mAccount);
- long streamItemPhotoId = ContentUris.parseId(resultUri);
-
- photoValues.put(StreamItemPhotos._ID, streamItemPhotoId);
- photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
- R.drawable.galaxy, PhotoSize.ORIGINAL));
- Uri photoUri =
- maybeAddAccountQueryParameters(
- Uri.withAppendedPath(
- ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
- StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
- mAccountTwo);
- try {
- mResolver.update(photoUri, photoValues, null, null);
- fail("Should not be able to update stream item photos inserted by another account");
- } catch (SecurityException expected) {
- // Can't update a stream item photo inserted by another account.
- }
- }
-
// Stream item deletion test cases.
public void testDeleteStreamItemById() {
@@ -3775,21 +3847,6 @@
RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues);
}
- public void testDeleteStreamItemFromOtherAccount() {
- long rawContactId = createRawContact(mAccount);
- long streamItemId = ContentUris.parseId(
- insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount));
- try {
- mResolver.delete(
- maybeAddAccountQueryParameters(
- ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
- mAccountTwo), null, null);
- fail("Should not be able to delete stream item inserted by another account");
- } catch (SecurityException expected) {
- // Can't delete a stream item from another account.
- }
- }
-
// Stream item photo deletion test cases.
public void testDeleteStreamItemPhotoById() {
@@ -3833,23 +3890,6 @@
assertStoredValues(photoUri, firstPhotoValues);
}
- public void testDeleteStreamItemPhotoFromOtherAccount() {
- long rawContactId = createRawContact(mAccount);
- long streamItemId = ContentUris.parseId(
- insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount));
- insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), mAccount);
- try {
- mResolver.delete(maybeAddAccountQueryParameters(
- Uri.withAppendedPath(
- ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
- StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
- mAccountTwo), null, null);
- fail("Should not be able to delete stream item photo inserted by another account");
- } catch (SecurityException expected) {
- // Can't delete a stream item photo from another account.
- }
- }
-
public void testDeleteStreamItemsWhenRawContactDeleted() {
long rawContactId = createRawContact(mAccount);
Uri streamItemUri = insertStreamItem(rawContactId,
@@ -4486,6 +4526,103 @@
assertStoredValue(uri, RawContacts.DELETED, "1");
}
+ /**
+ * Test for {@link ContactsProvider2#stringToAccounts} and
+ * {@link ContactsProvider2#accountsToString}.
+ */
+ public void testAccountsToString() {
+ final Set<Account> EXPECTED_0 = Sets.newHashSet();
+ final Set<Account> EXPECTED_1 = Sets.newHashSet(ACCOUNT_1);
+ final Set<Account> EXPECTED_2 = Sets.newHashSet(ACCOUNT_2);
+ final Set<Account> EXPECTED_1_2 = Sets.newHashSet(ACCOUNT_1, ACCOUNT_2);
+
+ final Set<Account> ACTUAL_0 = Sets.newHashSet();
+ final Set<Account> ACTUAL_1 = Sets.newHashSet(ACCOUNT_1);
+ final Set<Account> ACTUAL_2 = Sets.newHashSet(ACCOUNT_2);
+ final Set<Account> ACTUAL_1_2 = Sets.newHashSet(ACCOUNT_2, ACCOUNT_1);
+
+ assertTrue(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_0)));
+ assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_1)));
+ assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_2)));
+ assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_1_2)));
+
+ assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_0)));
+ assertTrue(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_1)));
+ assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_2)));
+ assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_1_2)));
+
+ assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_0)));
+ assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_1)));
+ assertTrue(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_2)));
+ assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_1_2)));
+
+ assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_0)));
+ assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_1)));
+ assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_2)));
+ assertTrue(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_1_2)));
+
+ try {
+ ContactsProvider2.stringToAccounts("x");
+ fail("Didn't throw for malformed input");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private static final Set<Account> accountsToStringToAccounts(Set<Account> accounts) {
+ return ContactsProvider2.stringToAccounts(ContactsProvider2.accountsToString(accounts));
+ }
+
+ /**
+ * Test for {@link ContactsProvider2#haveAccountsChanged} and
+ * {@link ContactsProvider2#saveAccounts}.
+ */
+ public void testHaveAccountsChanged() {
+ final ContactsProvider2 cp = (ContactsProvider2) getProvider();
+
+ final Account[] ACCOUNTS_0 = new Account[] {};
+ final Account[] ACCOUNTS_1 = new Account[] {ACCOUNT_1};
+ final Account[] ACCOUNTS_2 = new Account[] {ACCOUNT_2};
+ final Account[] ACCOUNTS_1_2 = new Account[] {ACCOUNT_1, ACCOUNT_2};
+ final Account[] ACCOUNTS_2_1 = new Account[] {ACCOUNT_2, ACCOUNT_1};
+
+ // Add ACCOUNT_1
+
+ assertTrue(cp.haveAccountsChanged(ACCOUNTS_1));
+ cp.saveAccounts(ACCOUNTS_1);
+ assertFalse(cp.haveAccountsChanged(ACCOUNTS_1));
+
+ // Add ACCOUNT_2
+
+ assertTrue(cp.haveAccountsChanged(ACCOUNTS_1_2));
+ // (try with reverse order)
+ assertTrue(cp.haveAccountsChanged(ACCOUNTS_2_1));
+ cp.saveAccounts(ACCOUNTS_1_2);
+ assertFalse(cp.haveAccountsChanged(ACCOUNTS_1_2));
+ // (try with reverse order)
+ assertFalse(cp.haveAccountsChanged(ACCOUNTS_2_1));
+
+ // Remove ACCOUNT_1
+
+ assertTrue(cp.haveAccountsChanged(ACCOUNTS_2));
+ cp.saveAccounts(ACCOUNTS_2);
+ assertFalse(cp.haveAccountsChanged(ACCOUNTS_2));
+
+ // Remove ACCOUNT_2
+
+ assertTrue(cp.haveAccountsChanged(ACCOUNTS_0));
+ cp.saveAccounts(ACCOUNTS_0);
+ assertFalse(cp.haveAccountsChanged(ACCOUNTS_0));
+
+ // Test with malformed DB property.
+
+ final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+ dbHelper.setProperty(DbProperties.KNOWN_ACCOUNTS, "x");
+
+ // With malformed property the method always return true.
+ assertTrue(cp.haveAccountsChanged(ACCOUNTS_0));
+ assertTrue(cp.haveAccountsChanged(ACCOUNTS_1));
+ }
+
public void testAccountsUpdated() {
// This is to ensure we do not delete contacts with null, null (account name, type)
// accidentally.
diff --git a/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java b/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java
index 066c47e..9bfe9d6 100644
--- a/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java
+++ b/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java
@@ -113,46 +113,60 @@
GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").photo(
loadTestPhoto()).phone("1-800-4664-411").build();
new SuggestionTesterBuilder(contact).query("1800").expectIcon1Uri(true).expectedText1(
- "Deer Dough").expectedText2("1-800-4664-411").build().test();
+ "Deer Dough").expectedText2("1-800-4664-411").expectedCreateContactNumber("1800")
+ .build().test();
}
- public void testSearchSuggestionsByPhoneNumberOnPhone() throws Exception {
- getContactsProvider().setIsPhone(true);
-
+ public void assertCreateContactSuggestion(Cursor c, String number) {
ContentValues values = new ContentValues();
-
- Uri searchUri = new Uri.Builder().scheme("content").authority(ContactsContract.AUTHORITY)
- .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath("12345678").build();
-
- Cursor c = mResolver.query(searchUri, null, null, null, null);
- assertEquals(2, c.getCount());
- c.moveToFirst();
-
- values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Dial number");
- values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "using 12345678");
- values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
- String.valueOf(com.android.internal.R.drawable.call_contact));
- values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
- Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED);
- values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:12345678");
- values.putNull(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
- assertCursorValues(c, values);
-
- c.moveToNext();
- values.clear();
values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Create contact");
- values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "using 12345678");
+ values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "using "+ number);
values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
String.valueOf(com.android.internal.R.drawable.create_contact));
values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED);
- values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:12345678");
+ values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:" + number);
values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT);
assertCursorValues(c, values);
+ }
+
+ public void searchSuggestionsByPhoneNumberOnPhoneTest(String number) {
+ getContactsProvider().setIsPhone(true);
+ getContactsProvider().setIsVoiceCapable(true);
+
+ ContentValues values = new ContentValues();
+
+ Uri searchUri = new Uri.Builder().scheme("content").authority(ContactsContract.AUTHORITY)
+ .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath(number).build();
+
+ Cursor c = mResolver.query(searchUri, null, null, null, null);
+ assertEquals(2, c.getCount());
+ c.moveToFirst();
+ values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Dial number");
+ values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "using " + number);
+ values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
+ String.valueOf(com.android.internal.R.drawable.call_contact));
+ values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+ Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED);
+ values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:" + number);
+ values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
+ SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT);
+ assertCursorValues(c, values);
+
+ c.moveToNext();
+ assertCreateContactSuggestion(c, number);
c.close();
}
+ public void testSearchSuggestionsByPhoneNumberOnPhone() throws Exception {
+ searchSuggestionsByPhoneNumberOnPhoneTest("12345678");
+ }
+
+ public void testSearchSuggestionsByAlphnumericPhoneNumberOnPhone() throws Exception {
+ searchSuggestionsByPhoneNumberOnPhoneTest("1800-flowers");
+ }
+
/**
* Tests that the quick search suggestion returns the expected contact
* information.
@@ -171,6 +185,8 @@
private final String expectedText2;
+ private final String expectedCreateContactNumber;
+
public SuggestionTester(SuggestionTesterBuilder builder) {
contact = builder.contact;
query = builder.query;
@@ -178,6 +194,7 @@
expectedIcon2 = builder.expectedIcon2;
expectedText1 = builder.expectedText1;
expectedText2 = builder.expectedText2;
+ expectedCreateContactNumber = builder.expectedCreateContactNumber;
}
/**
@@ -205,7 +222,7 @@
.appendPath(query).build();
Cursor c = mResolver.query(searchUri, null, null, null, null);
- assertEquals(1, c.getCount());
+ assertEquals(expectedCreateContactNumber == null ? 1 : 2, c.getCount());
c.moveToFirst();
String icon1 = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1));
@@ -219,6 +236,12 @@
// SearchManager does not declare a constant for _id
ContentValues values = getContactValues();
assertCursorValues(c, values);
+
+ if (expectedCreateContactNumber != null) {
+ c.moveToNext();
+ assertCreateContactSuggestion(c, expectedCreateContactNumber);
+ }
+
c.close();
}
@@ -337,6 +360,8 @@
private String expectedText2;
+ private String expectedCreateContactNumber;
+
public SuggestionTesterBuilder(GoldenContact contact) {
this.contact = contact;
}
@@ -393,5 +418,10 @@
expectedText2 = value;
return this;
}
+
+ public SuggestionTesterBuilder expectedCreateContactNumber(String number) {
+ expectedCreateContactNumber = number;
+ return this;
+ }
}
}
diff --git a/tests/src/com/android/providers/contacts/GroupsTest.java b/tests/src/com/android/providers/contacts/GroupsTest.java
index 56b7100..3d85064 100644
--- a/tests/src/com/android/providers/contacts/GroupsTest.java
+++ b/tests/src/com/android/providers/contacts/GroupsTest.java
@@ -46,6 +46,7 @@
* com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
* </code>
*/
+@MediumTest
public class GroupsTest extends BaseContactsProvider2Test {
private static final String GROUP_GREY = "Grey";
@@ -64,7 +65,6 @@
private static final String PHONE_CHARLIE_1 = "555-4444";
private static final String PHONE_CHARLIE_2 = "555-5555";
- @LargeTest
public void testGroupSummary() {
// Clear any existing data before starting
@@ -144,7 +144,6 @@
cursor.close();
}
- @MediumTest
public void testGroupDirtySetOnChange() {
Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI,
createGroup(mAccount, "gsid1", "title1"));
@@ -153,7 +152,6 @@
assertDirty(uri, false);
}
- @MediumTest
public void testMarkAsDirtyParameter() {
Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI,
createGroup(mAccount, "gsid1", "title1"));
@@ -166,7 +164,6 @@
assertDirty(uri, false);
}
- @MediumTest
public void testGroupDirtyClearedWhenSetExplicitly() {
Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI,
createGroup(mAccount, "gsid1", "title1"));
@@ -180,7 +177,6 @@
assertDirty(uri, false);
}
- @MediumTest
public void testGroupDeletion1() {
long groupId = createGroup(mAccount, "g1", "gt1");
Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI, groupId);
@@ -195,7 +191,6 @@
assertEquals(0, getCount(uri, null, null));
}
- @MediumTest
public void testGroupDeletion2() {
long groupId = createGroup(mAccount, "g1", "gt1");
Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI, groupId);
@@ -206,7 +201,6 @@
assertEquals(0, getCount(uri, null, null));
}
- @MediumTest
public void testGroupVersionUpdates() {
Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI,
createGroup(mAccount, "gsid1", "title1"));
@@ -254,7 +248,6 @@
+ (visible ? 1 : 0), null).withExpectedCount(1).build();
}
- @LargeTest
public void testDelayVisibleTransaction() throws RemoteException, OperationApplicationException {
final ContentValues values = new ContentValues();
diff --git a/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java b/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java
deleted file mode 100644
index d78193b..0000000
--- a/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * 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.
- */
-
-package com.android.providers.contacts;
-
-import android.content.ContentProvider;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Debug;
-import android.provider.CallLog;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.IsolatedContext;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
-import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import java.io.File;
-
-/**
- * Performance test for {@link ContactAggregator}. Run the test like this:
- * <code>
- * adb push <large contacts.db> \
- * data/data/com.android.providers.contacts/databases/perf_imp.contacts.db
- * adb shell am instrument \
- * -e class com.android.providers.contacts.LegacyContactImporterPerformanceTest \
- * -w com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
- * </code>
- *
- * Note that this SHOULD be a large test, but had to be bumped down to medium due to a bug in the
- * SQLite cleanup code.
- */
-@MediumTest
-public class LegacyContactImporterPerformanceTest extends AndroidTestCase {
-
- private static final String TAG = "LegacyContactImporterPerformanceTest";
-
- private static final boolean TRACE = false;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- SynchronousContactsProvider2.resetOpenHelper();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- SynchronousContactsProvider2.resetOpenHelper();
- }
-
- public void testPerformance() {
- final Context targetContext = getContext();
- MockContentResolver resolver = new MockContentResolver();
- MockContext context = new MockContext() {
- @Override
- public Resources getResources() {
- return targetContext.getResources();
- }
-
- @Override
- public String getPackageName() {
- return "no.package";
- }
-
- @Override
- public ApplicationInfo getApplicationInfo() {
- ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = "contactsTestPackage";
- return ai;
- }
-
- @Override
- public PackageManager getPackageManager() {
- return new ContactsMockPackageManager();
- }
- };
-
- RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(context,
- targetContext, "perf_imp.");
- targetContextWrapper.makeExistingFilesAndDbsAccessible();
- IsolatedContext providerContext = new IsolatedContext(resolver, targetContextWrapper) {
- @Override
- public File getFilesDir() {
- // TODO: Need to figure out something more graceful than this.
- return new File("/data/data/com.android.providers.contacts.tests/files");
- }
- };
- SynchronousContactsProvider2 provider = new SynchronousContactsProvider2();
- provider.setDataWipeEnabled(false);
- provider.attachInfo(providerContext, null);
- resolver.addProvider(ContactsContract.AUTHORITY, provider);
-
- ContentProvider callLogProvider = new TestCallLogProvider();
- callLogProvider.attachInfo(providerContext, null);
- resolver.addProvider(CallLog.AUTHORITY, callLogProvider);
-
- LegacyContactImporter importer = new LegacyContactImporter(providerContext, provider);
-
- provider.wipeData();
-
- long start = System.currentTimeMillis();
- if (TRACE) {
- Debug.startMethodTracing("import");
- }
- provider.importLegacyContacts(importer);
- if (TRACE) {
- Debug.stopMethodTracing();
- }
- long end = System.currentTimeMillis();
- long contactCount = provider.getRawContactCount();
- Log.i(TAG, String.format("Imported contacts in %d ms.\n"
- + "Contacts: %d\n"
- + "Per contact: %.3f",
- end - start,
- contactCount,
- ((double)(end - start) / contactCount)));
- }
-
- public static class TestCallLogProvider extends CallLogProvider {
- private static ContactsDatabaseHelper mDbHelper;
-
- @Override
- protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
- if (mDbHelper == null) {
- mDbHelper = new ContactsDatabaseHelper(context);
- }
- return mDbHelper;
- }
- }
-}
diff --git a/tests/src/com/android/providers/contacts/LegacyContactImporterTest.java b/tests/src/com/android/providers/contacts/LegacyContactImporterTest.java
deleted file mode 100644
index 61108af..0000000
--- a/tests/src/com/android/providers/contacts/LegacyContactImporterTest.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * 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.
- */
-
-package com.android.providers.contacts;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Unit tests for {@link LegacyContactImporter}.
- *
- * Run the test like this:
- * <code>
- * adb shell am instrument -e class com.android.providers.contacts.LegacyContactImporterTest -w \
- * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
- * </code>
- *
- * Note that this SHOULD be a large test, but had to be bumped down to medium due to a bug in the
- * SQLite cleanup code.
- */
-@MediumTest
-public class LegacyContactImporterTest extends BaseContactsProvider2Test {
-
- private static class LegacyMockContext extends MockContext {
-
- private String mFileName;
-
- public LegacyMockContext(String fileName) {
- mFileName = fileName;
- }
-
- @Override
- public SQLiteDatabase openOrCreateDatabase(String file, int mode,
- SQLiteDatabase.CursorFactory factory) {
- return SQLiteDatabase.openDatabase(mFileName, factory, SQLiteDatabase.OPEN_READONLY);
- }
-
- @Override
- public File getDatabasePath(String name) {
- return new File(mFileName);
- }
- }
-
- private LegacyMockContext createLegacyMockContext(String folder) throws IOException {
- Context context = getTestContext();
- File tempDb = new File(context.getFilesDir(), "legacy_contacts.db");
- if (tempDb.exists()) {
- tempDb.delete();
- }
- createSQLiteDatabaseFromDumpFile(tempDb.getPath(),
- new File(folder, "legacy_contacts.sql").getPath());
- return new LegacyMockContext(tempDb.getPath());
- }
-
- private void createSQLiteDatabaseFromDumpFile(String tempDbPath, String dumpFileAssetPath)
- throws IOException {
-
- final String[] ignoredTables = new String[] {"android_metadata", "sqlite_sequence"};
-
- Context context = getTestContext();
- SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(tempDbPath, null);
- try {
- String data = readAssetAsString(dumpFileAssetPath);
- String[] commands = data.split(";\r|;\n|;\r\n");
- for (String command : commands) {
- boolean ignore = false;
- for (String ignoredTable : ignoredTables) {
- if (command.contains(ignoredTable)) {
- ignore = true;
- break;
- }
- }
- if (!ignore) {
- database.execSQL(command);
- }
- }
-
- assertTrue(
- "Database Version not set. Be sure to add " +
- "'PRAGMA user_version = <number>;' to the SQL Script",
- database.getVersion() != 0);
- } finally {
- database.close();
- }
- }
-
- @Override
- protected void setUp() throws Exception {
- SynchronousContactsProvider2.resetOpenHelper();
- super.setUp();
- addProvider(TestCallLogProvider.class, CallLog.AUTHORITY);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- SynchronousContactsProvider2.resetOpenHelper();
- }
-
- public void testContactUpgrade1() throws Exception {
- testAssetSet("test1");
- }
-
- public void testSyncedContactsUpgrade() throws Exception {
- testAssetSet("testSynced");
- }
-
- public void testUnsyncedContactsUpgrade() throws Exception {
- testAssetSet("testUnsynced");
- }
-
- private void testAssetSet(String folder) throws Exception {
- ContactsProvider2 provider = (ContactsProvider2)getProvider();
- LegacyContactImporter importer =
- new LegacyContactImporter(createLegacyMockContext(folder), provider);
- provider.importLegacyContacts(importer);
-
- assertQueryResults(folder + "/expected_groups.txt", Groups.CONTENT_URI, new String[]{
- Groups._ID,
- Groups.ACCOUNT_NAME,
- Groups.ACCOUNT_TYPE,
- Groups.DIRTY,
- Groups.GROUP_VISIBLE,
- Groups.NOTES,
- Groups.RES_PACKAGE,
- Groups.SOURCE_ID,
- Groups.SYSTEM_ID,
- Groups.TITLE,
- Groups.VERSION,
- Groups.SYNC1,
- Groups.SYNC2,
- Groups.SYNC3,
- Groups.SYNC4,
- });
-
- assertQueryResults(folder + "/expected_contacts.txt", Contacts.CONTENT_URI, new String[]{
- Contacts._ID,
- Contacts.DISPLAY_NAME_PRIMARY,
- Contacts.SORT_KEY_PRIMARY,
- Contacts.PHOTO_ID,
- Contacts.TIMES_CONTACTED,
- Contacts.LAST_TIME_CONTACTED,
- Contacts.CUSTOM_RINGTONE,
- Contacts.SEND_TO_VOICEMAIL,
- Contacts.STARRED,
- Contacts.IN_VISIBLE_GROUP,
- Contacts.HAS_PHONE_NUMBER,
- Contacts.IS_USER_PROFILE,
- Contacts.LOOKUP_KEY,
- });
-
- assertQueryResults(folder + "/expected_raw_contacts.txt", RawContacts.CONTENT_URI,
- new String[]{
- RawContacts._ID,
- RawContacts.ACCOUNT_NAME,
- RawContacts.ACCOUNT_TYPE,
- RawContacts.DELETED,
- RawContacts.DIRTY,
- RawContacts.SOURCE_ID,
- RawContacts.VERSION,
- RawContacts.SYNC1,
- RawContacts.SYNC2,
- RawContacts.SYNC3,
- RawContacts.SYNC4,
- RawContacts.DISPLAY_NAME_SOURCE,
- RawContacts.DISPLAY_NAME_PRIMARY,
- RawContacts.DISPLAY_NAME_ALTERNATIVE,
- RawContacts.SORT_KEY_PRIMARY,
- RawContacts.SORT_KEY_ALTERNATIVE,
- });
-
- assertQueryResults(folder + "/expected_data.txt", Data.CONTENT_URI, new String[]{
- Data._ID,
- Data.RAW_CONTACT_ID,
- Data.MIMETYPE,
- Data.DATA1,
- Data.DATA2,
- Data.DATA3,
- Data.DATA4,
- Data.DATA5,
- Data.DATA6,
- Data.DATA7,
- Data.DATA8,
- Data.DATA9,
- Data.DATA10,
- Data.DATA11,
- Data.DATA12,
- Data.DATA13,
- Data.DATA14,
- Data.DATA15,
- Data.IS_PRIMARY,
- Data.IS_SUPER_PRIMARY,
- Data.DATA_VERSION,
- Data.SYNC1,
- Data.SYNC2,
- Data.SYNC3,
- Data.SYNC4,
- });
-
- assertQueryResults(folder + "/expected_calls.txt", Calls.CONTENT_URI, new String[]{
- Calls._ID,
- Calls.NUMBER,
- Calls.DATE,
- Calls.DURATION,
- Calls.NEW,
- Calls.TYPE,
- Calls.CACHED_NAME,
- Calls.CACHED_NUMBER_LABEL,
- Calls.CACHED_NUMBER_TYPE,
- });
-
- provider.getDatabaseHelper().close();
- }
-
- private void assertQueryResults(String fileName, Uri uri, String[] projection)
- throws Exception {
- String expected = readAssetAsString(fileName).trim();
- String actual = dumpCursorToString(uri, projection).trim();
- assertEquals("Checking golden file " + fileName, expected, actual);
- }
-
- private String readAssetAsString(String fileName) throws IOException {
- Context context = getTestContext();
- InputStream input = context.getAssets().open(fileName);
- ByteArrayOutputStream contents = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[1024];
- do {
- len = input.read(data);
- if (len > 0) contents.write(data, 0, len);
- } while (len == data.length);
- return contents.toString();
- }
-
- private String dumpCursorToString(Uri uri, String[] projection) {
- Cursor c = mResolver.query(uri, projection, null, null, BaseColumns._ID);
- if (c == null) {
- return "Null cursor";
- }
-
- String cursorDump = DatabaseUtils.dumpCursorToString(c);
- c.close();
- return insertLineNumbers(cursorDump);
- }
-
- private String insertLineNumbers(String multiline) {
- String[] lines = multiline.split("\n");
- StringBuilder sb = new StringBuilder();
-
- // Ignore the first line that is a volatile header and the last line which is "<<<<<"
- for (int i = 1; i < lines.length - 1; i++) {
- sb.append(i).append(" ").append(lines[i]).append('\n');
- }
- return sb.toString();
- }
-
-
- public static class TestCallLogProvider extends CallLogProvider {
- private static ContactsDatabaseHelper mDbHelper;
-
- @Override
- protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
- if (mDbHelper == null) {
- mDbHelper = new ContactsDatabaseHelper(context);
- }
- return mDbHelper;
- }
- }
-}
diff --git a/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
index 285378c..459fec0 100644
--- a/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
+++ b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
@@ -20,6 +20,7 @@
import android.content.res.Resources;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -29,6 +30,7 @@
/**
* Adds support for loading photo files easily from test resources.
*/
+@SmallTest
public class PhotoLoadingTestCase extends AndroidTestCase {
private Map<Integer, PhotoEntry> photoResourceCache = Maps.newHashMap();
diff --git a/tests/src/com/android/providers/contacts/PhotoStoreTest.java b/tests/src/com/android/providers/contacts/PhotoStoreTest.java
index 9b7c50d..601ccf5 100644
--- a/tests/src/com/android/providers/contacts/PhotoStoreTest.java
+++ b/tests/src/com/android/providers/contacts/PhotoStoreTest.java
@@ -23,7 +23,6 @@
import android.database.sqlite.SQLiteDatabase;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhotoFiles;
-import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.LargeTest;
import java.io.File;
diff --git a/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java b/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
index ab20396..ed1c23a 100644
--- a/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
+++ b/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
@@ -438,6 +438,35 @@
"...doe.com\nthe eighteenth episode of Seinfeld, [650]-[253]-0000");
}
+ /**
+ * Test case for bug 5904515
+ */
+ public void testSearchByPhoneNumber_diferSnippetting() {
+ long rawContactId = createRawContact();
+ insertPhoneNumber(rawContactId, "505-123-4567");
+
+ // The bug happened with the old code only when we use \u0001 as the snippet marker.
+ // But note that the expected result has [ and ] instead of \u0001. This is because when
+ // we differ snippetizing, the marker passe to the provider will be ignored; instead
+ // assertStoredValue internally do the client-side snippetizing, which done by
+ // getCursorStringValue(), which is hardcoded to use [ and ].
+ assertStoredValue(buildSearchUri("505", "\u0001,\u0001,\u2026,5", true),
+ SearchSnippetColumns.SNIPPET, "[505]-123-4567");
+ }
+
+ /**
+ * Equivalent to {@link #testSearchByPhoneNumber_diferSnippetting} for email addresses, although
+ * the original bug didn't happen with email addresses... (It *did* happen internally, but
+ * there's no visible breakage.)
+ */
+ public void testSearchByEmail_diferSnippetting() {
+ long rawContactId = createRawContact();
+ insertEmail(rawContactId, "john@doe.com");
+
+ assertStoredValue(buildSearchUri("john", "\u0001,\u0001,\u2026,5", true),
+ SearchSnippetColumns.SNIPPET, "[john@doe.com]");
+ }
+
private Uri buildSearchUri(String filter) {
return buildSearchUri(filter, false);
}
diff --git a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
index 49a930d..4fe3b56 100644
--- a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
+++ b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
@@ -35,6 +35,7 @@
private Account mAccount;
private boolean mNetworkNotified;
private boolean mIsPhone = true;
+ private boolean mIsVoiceCapable = true;
@Override
protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
@@ -82,6 +83,15 @@
return mIsPhone;
}
+ public void setIsVoiceCapable(boolean flag) {
+ mIsVoiceCapable = flag;
+ }
+
+ @Override
+ public boolean isVoiceCapable() {
+ return mIsVoiceCapable;
+ }
+
@Override
public boolean onCreate() {
boolean created = super.onCreate();
@@ -97,6 +107,11 @@
}
@Override
+ protected boolean shouldThrowExceptionForInitializationError() {
+ return true;
+ }
+
+ @Override
protected void scheduleBackgroundTask(int task) {
performBackgroundTask(task, null);
}
@@ -188,11 +203,4 @@
getContactDirectoryManagerForTest().scanAllPackages();
}
-
- @Override
- protected boolean isLegacyContactImportNeeded() {
-
- // We have an explicit test for data conversion - no need to do it every time
- return false;
- }
}
diff --git a/tests/src/com/android/providers/contacts/VoicemailCleanupServiceTest.java b/tests/src/com/android/providers/contacts/VoicemailCleanupServiceTest.java
index a2f4617..f8d76ee 100644
--- a/tests/src/com/android/providers/contacts/VoicemailCleanupServiceTest.java
+++ b/tests/src/com/android/providers/contacts/VoicemailCleanupServiceTest.java
@@ -23,10 +23,12 @@
import android.provider.VoicemailContract;
import android.provider.VoicemailContract.Status;
import android.provider.VoicemailContract.Voicemails;
+import android.test.suitebuilder.annotation.SmallTest;
/**
* Unit tests for {@link VoicemailCleanupService}.
*/
+@SmallTest
public class VoicemailCleanupServiceTest extends BaseVoicemailProviderTest {
private static final String TEST_PACKAGE_1 = "package1";
private static final String TEST_PACKAGE_2 = "package2";
diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
index a1560dd..74195b5 100644
--- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
+++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
@@ -28,6 +28,7 @@
import android.provider.VoicemailContract.Status;
import android.provider.VoicemailContract.Voicemails;
import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -45,6 +46,7 @@
* </code>
*/
// TODO: Test that calltype and voicemail_uri are auto populated by the provider.
+@SmallTest
public class VoicemailProviderTest extends BaseVoicemailProviderTest {
/** Fields specific to call_log provider that should not be exposed by voicemail provider. */
private static final String[] CALLLOG_PROVIDER_SPECIFIC_COLUMNS = {
diff --git a/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java b/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java
index 350e971..43f7c06 100644
--- a/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java
+++ b/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java
@@ -19,14 +19,13 @@
import static com.android.providers.contacts.util.DbQueryUtils.checkForSupportedColumns;
import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
+import com.android.common.content.ProjectionMap;
+import com.android.providers.contacts.EvenMoreAsserts;
+
import android.content.ContentValues;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.providers.contacts.EvenMoreAsserts;
-import com.android.providers.contacts.ProjectionMap;
-import com.android.providers.contacts.util.DbQueryUtils;
-
/**
* Unit tests for the {@link DbQueryUtils} class.
* Run the test like this:
@@ -38,10 +37,11 @@
public class DBQueryUtilsTest extends AndroidTestCase {
public void testGetEqualityClause() {
assertEquals("(foo = 'bar')", DbQueryUtils.getEqualityClause("foo", "bar"));
+ assertEquals("(foo = 2)", DbQueryUtils.getEqualityClause("foo", 2));
}
public void testGetInEqualityClause() {
- assertEquals("(foo != 'bar')", DbQueryUtils.getInequalityClause("foo", "bar"));
+ assertEquals("(foo != 2)", DbQueryUtils.getInequalityClause("foo", 2));
}
public void testConcatenateClauses() {