Merge "Introduce a "loading" view for phove favorite"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a1f8b4a..56c7b9f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -407,7 +407,7 @@
android:launchMode="singleTop"
android:excludeFromRecents="true"
android:noHistory="true"
- android:taskAffinity="android.task.quickcontact"
+ android:taskAffinity=""
android:windowSoftInputMode="stateUnchanged">
<intent-filter>
@@ -474,7 +474,8 @@
<!-- Views the details of a single contact -->
<activity android:name=".activities.ContactDetailActivity"
android:label="@string/viewContactTitle"
- android:theme="@style/DetailActivityTheme">
+ android:theme="@style/DetailActivityTheme"
+ android:parentActivityName=".activities.PeopleActivity">
<intent-filter android:label="@string/viewContactDesription">
<action android:name="android.intent.action.VIEW" />
diff --git a/res/menu-sw580dp-w720dp/people_options.xml b/res/menu-sw580dp-w720dp/people_options.xml
index 5f939cc..6974d80 100644
--- a/res/menu-sw580dp-w720dp/people_options.xml
+++ b/res/menu-sw580dp-w720dp/people_options.xml
@@ -43,6 +43,10 @@
android:title="@string/menu_import_export" />
<item
+ android:id="@+id/menu_clear_frequents"
+ android:title="@string/menu_clear_frequents" />
+
+ <item
android:id="@+id/menu_accounts"
android:orderInCategory="3"
android:title="@string/menu_accounts" />
diff --git a/res/menu-sw580dp/people_options.xml b/res/menu-sw580dp/people_options.xml
index c8c114a..07d9c94 100644
--- a/res/menu-sw580dp/people_options.xml
+++ b/res/menu-sw580dp/people_options.xml
@@ -46,6 +46,10 @@
android:title="@string/menu_import_export" />
<item
+ android:id="@+id/menu_clear_frequents"
+ android:title="@string/menu_clear_frequents" />
+
+ <item
android:id="@+id/menu_accounts"
android:orderInCategory="3"
android:title="@string/menu_accounts" />
diff --git a/res/menu/people_options.xml b/res/menu/people_options.xml
index 0c82f99..4003038 100644
--- a/res/menu/people_options.xml
+++ b/res/menu/people_options.xml
@@ -41,6 +41,10 @@
android:title="@string/menu_import_export" />
<item
+ android:id="@+id/menu_clear_frequents"
+ android:title="@string/menu_clear_frequents" />
+
+ <item
android:id="@+id/menu_accounts"
android:title="@string/menu_accounts" />
diff --git a/res/menu/phone_favorite_options.xml b/res/menu/phone_favorite_options.xml
index ac67d6e..e37759c 100644
--- a/res/menu/phone_favorite_options.xml
+++ b/res/menu/phone_favorite_options.xml
@@ -19,6 +19,10 @@
android:title="@string/menu_import_export" />
<item
+ android:id="@+id/menu_clear_frequents"
+ android:title="@string/menu_clear_frequents" />
+
+ <item
android:id="@+id/menu_accounts"
android:title="@string/menu_accounts" />
</menu>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 31a5ef3..4c02c94 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Verwyder van gunstelinge"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Redigeer"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Vee uit"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopieer"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Plaas op tuisskerm"</string>
<string name="menu_call" msgid="3992595586042260618">"Bel kontak"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Teks - kontak"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 451a8d3..2cedd03 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"ከተወዳጆች አስወግድ"</string>
<string name="menu_editContact" msgid="9042415603857662633">"አርትዕ"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"ሰርዝ"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"ቅዳ"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"መነሻ የማያ ገጽ ላይ አስቀምጥ"</string>
<string name="menu_call" msgid="3992595586042260618">"የጥሪ ዕውቂያ"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"ዕውቂያ ፃፍ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index fe0ee38..bd6c46b 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"إزالة من المفضلة"</string>
<string name="menu_editContact" msgid="9042415603857662633">"تعديل"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"حذف"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"نسخ"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"وضع على الشاشة الرئيسية"</string>
<string name="menu_call" msgid="3992595586042260618">"الاتصال بجهة الاتصال"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"إرسال رسالة لجهة الاتصال"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index bb41964..7f1983e 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Elimina dels preferits"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Edita"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Suprimeix"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Copia"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Posa-ho a la pantalla d\'inici"</string>
<string name="menu_call" msgid="3992595586042260618">"Truca al contacte"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Envia un SMS al contacte"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 38c11da..a901937 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Fjern fra favoritter"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Rediger"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Slet"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopiér"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Placer på startskærmen"</string>
<string name="menu_call" msgid="3992595586042260618">"Ring til kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Send sms til kontakt"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 371a237..36adf42 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Aus Favoriten entfernen"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Bearbeiten"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Löschen"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopieren"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Auf Startbildschirm platzieren"</string>
<string name="menu_call" msgid="3992595586042260618">"Kontakt anrufen"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"SMS an Kontakt"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 136daee..620de1c 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Κατάργηση από τα αγαπημένα"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Επεξεργασία"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Διαγραφή"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Αντιγραφή"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Τοποθέτηση στην αρχική οθόνη"</string>
<string name="menu_call" msgid="3992595586042260618">"Κλήση επαφής"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Αποστολή μηνύματος κειμένου σε επαφή"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index d0cf129..ae1e793 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Remove from favourites"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Edit"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Delete"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Copy"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Place on Home screen"</string>
<string name="menu_call" msgid="3992595586042260618">"Call contact"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Text contact"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 0471ba6..8bdecf7 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Eliminar de favoritos"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Editar"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Eliminar"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Copiar"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Colocar en pantalla principal"</string>
<string name="menu_call" msgid="3992595586042260618">"Llamar al contacto"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Enviar texto al contacto"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 00bb0e6..695f5cb 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Eliminar de \"Favoritos\""</string>
<string name="menu_editContact" msgid="9042415603857662633">"Editar"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Eliminar"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Copiar"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Añadir al escritorio"</string>
<string name="menu_call" msgid="3992595586042260618">"Llamar al contacto"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Enviar SMS al contacto"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 0e56b42..bf0978e 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Eemalda lemmikutest"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Muuda"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Kustuta"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopeeri"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Paiguta avalehele"</string>
<string name="menu_call" msgid="3992595586042260618">"Helista kontaktile"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Saada kontaktile SMS"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index dd246d3..6f3337f 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Supprimer des favoris"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Modifier"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Supprimer"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Copier"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Placer sur l\'écran d\'accueil"</string>
<string name="menu_call" msgid="3992595586042260618">"Appeler le contact"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Envoyer un SMS au contact"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 34274b5..4da7367 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Eltávolítás a kedvencek közül"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Szerkesztés"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Törlés"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Másolás"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Helyezze el a kezdőképernyőn"</string>
<string name="menu_call" msgid="3992595586042260618">"Ismerős hívása"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"SMS küldése ismerősnek"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index fb45614..61375de 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Rimuovi dai preferiti"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Modifica"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Elimina"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Copia"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Aggiungi a schermata Home"</string>
<string name="menu_call" msgid="3992595586042260618">"Chiama"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Invia SMS"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 96f8a27..ada49ed 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"הסר מהמועדפים"</string>
<string name="menu_editContact" msgid="9042415603857662633">"ערוך"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"מחק"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"העתק"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"הצב במסך הבית"</string>
<string name="menu_call" msgid="3992595586042260618">"התקשר לאיש קשר"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"שלח הודעת טקסט לאיש קשר"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 1406c15..bfd4aeb 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Pašalinti iš adresyno"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Redaguoti"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Ištrinti"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopijuoti"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Padėti pagrindiniame ekrane"</string>
<string name="menu_call" msgid="3992595586042260618">"Skambinti adresatui"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Siųsti pranešimą adresatui"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 7482ee8..315a88b 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Noņemt no izlases"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Rediģēt"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Dzēst"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopēt"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Novietot sākuma ekrānā"</string>
<string name="menu_call" msgid="3992595586042260618">"Zvanīt kontaktpersonai"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Sūtīt īsziņu kontaktpersonai"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index be36c51..d22dc0b 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Fjern fra favoritter"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Rediger"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Slett"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopiér"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Plassér på startsiden"</string>
<string name="menu_call" msgid="3992595586042260618">"Ring kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Send SMS til kontakt"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index a7cf2eb..16aa2d9 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Uit favorieten verwijderen"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Bewerken"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Verwijderen"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopiëren"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Op startscherm plaatsen"</string>
<string name="menu_call" msgid="3992595586042260618">"Contact bellen"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Sms\'en naar contact"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 9de25b3..81c8de5 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Удалить из избранных"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Изменить"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Удалить"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Копировать"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Поместить на главный экран"</string>
<string name="menu_call" msgid="3992595586042260618">"Позвонить"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Отправить SMS/MMS"</string>
@@ -159,7 +158,7 @@
<string name="private_num" msgid="6374339738119166953">"Скрытый номер"</string>
<string name="payphone" msgid="4864313342828942922">"Телефон-автомат"</string>
<string name="dialerKeyboardHintText" msgid="5401660096579787344">"Наберите номер с клавиатуры"</string>
- <string name="dialerDialpadHintText" msgid="5824490365898349041">"Наберите номер, чтобы добавить вызов"</string>
+ <string name="dialerDialpadHintText" msgid="5824490365898349041">"Наберите номер"</string>
<string name="simContacts_emptyLoading" msgid="6700035985448642408">"Загрузка с SIM-карты…"</string>
<string name="simContacts_title" msgid="27341688347689769">"Контакты на SIM-карте"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Отсутствуют контакты для отображения. (Если аккаунт был только что добавлен, потребуется несколько минут для синхронизации контактов.)"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 682bdc8..c0d5f8f 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Ta bort från Favoriter"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Redigera"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Ta bort"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopiera"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Placera på startsidan"</string>
<string name="menu_call" msgid="3992595586042260618">"Ring upp kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Skicka SMS till kontakt"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 5f2a18f..db88e7c 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -48,7 +48,7 @@
<string name="menu_deleteContact" msgid="6788644058868189393">"Futa"</string>
<!-- no translation found for menu_copy (6108677035381940698) -->
<skip />
- <string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Mahali kwenye skrini ya Nyumbani"</string>
+ <string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Weka kwenye skrini ya Nyumbani"</string>
<string name="menu_call" msgid="3992595586042260618">"Mpigie"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Tuma ujumbe kwa anwani"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Kando"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 2a18812..469f899 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Видалити з вибраного"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Редагувати"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Видалити"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Копіювати"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Місце на головному екрані"</string>
<string name="menu_call" msgid="3992595586042260618">"Набрати конт."</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Повід. контакт"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 11fc663..0ceb87a 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -46,8 +46,7 @@
<string name="menu_removeStar" msgid="5844227078364227030">"Susa ezintandokazini"</string>
<string name="menu_editContact" msgid="9042415603857662633">"Hlela"</string>
<string name="menu_deleteContact" msgid="6788644058868189393">"Susa"</string>
- <!-- no translation found for menu_copy (6108677035381940698) -->
- <skip />
+ <string name="menu_copy" msgid="6108677035381940698">"Kopisha"</string>
<string name="menu_create_contact_shortcut" msgid="1217971915748509640">"Indawo esikrinini sekhaya"</string>
<string name="menu_call" msgid="3992595586042260618">"Shayela othintana naye"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Bhalela othintana naye"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b030f43..c5d0703 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -433,6 +433,15 @@
<!-- Title of the "Clearing call log" progress-dialog [CHAR LIMIT=35] -->
<string name="clearCallLogProgress_title">Clearing call log\u2026</string>
+ <!-- Title of the confirmation dialog for clearing frequents. [CHAR LIMIT=37] -->
+ <string name="clearFrequentsConfirmation_title">Clear frequently contacted?</string>
+
+ <!-- Confirmation dialog for clearing frequents. [CHAR LIMIT=NONE] -->
+ <string name="clearFrequentsConfirmation">You\'ll clear the frequently contacted list in the People and Phone apps, and force email apps to learn your addressing preferences from scratch.</string>
+
+ <!-- Title of the "Clearing frequently contacted" progress-dialog [CHAR LIMIT=35] -->
+ <string name="clearFrequentsProgress_title">Clearing frequently contacted\u2026</string>
+
<!-- The title of a dialog that displays the IMEI of the phone -->
<string name="imei">IMEI</string>
@@ -1002,6 +1011,9 @@
<!-- The menu item to open the list of accounts -->
<string name="menu_accounts">Accounts</string>
+ <!-- The menu item to clear frequents [CHAR LIMIT=30] -->
+ <string name="menu_clear_frequents">Clear frequents</string>
+
<!-- The menu item to filter the list of contacts displayed -->
<string name="menu_contacts_filter">Contacts to display</string>
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index 17cd1e7..c0399e4 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -19,6 +19,7 @@
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.AccountTypeWithDataSet;
+import com.android.contacts.model.EntityDeltaList;
import com.android.contacts.util.ContactLoaderUtils;
import com.android.contacts.util.DataStatus;
import com.android.contacts.util.StreamItemEntry;
@@ -71,7 +72,7 @@
* Loads a single Contact and all it constituent RawContacts.
*/
public class ContactLoader extends AsyncTaskLoader<ContactLoader.Result> {
- private static final String TAG = "ContactLoader";
+ private static final String TAG = ContactLoader.class.getSimpleName();
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -313,6 +314,13 @@
}
/**
+ * Instantiate a new EntityDeltaList for this contact.
+ */
+ public EntityDeltaList createEntityDeltaList() {
+ return EntityDeltaList.fromIterator(getEntities().iterator());
+ }
+
+ /**
* Returns the contact ID.
*/
@VisibleForTesting
@@ -419,16 +427,31 @@
* writable raw-contact, and false otherwise.
*/
public boolean isWritableContact(final Context context) {
- if (isDirectoryEntry()) return false;
+ return getFirstWritableRawContactId(context) != -1;
+ }
+
+ /**
+ * Return the ID of the first raw-contact in the contact data that belongs to a
+ * contact-writable account, or -1 if no such entity exists.
+ */
+ public long getFirstWritableRawContactId(final Context context) {
+ // Directory entries are non-writable
+ if (isDirectoryEntry()) return -1;
+
+ // Iterate through raw-contacts; if we find a writable on, return its ID.
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
- for (Entity rawContact : getEntities()) {
- final ContentValues rawValues = rawContact.getEntityValues();
- final String accountType = rawValues.getAsString(RawContacts.ACCOUNT_TYPE);
- final String dataSet = rawValues.getAsString(RawContacts.DATA_SET);
- final AccountType type = accountTypes.getAccountType(accountType, dataSet);
- if (type != null && type.areContactsWritable()) return true;
+ for (Entity entity : getEntities()) {
+ ContentValues values = entity.getEntityValues();
+ String type = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ String dataSet = values.getAsString(RawContacts.DATA_SET);
+
+ AccountType accountType = accountTypes.getAccountType(type, dataSet);
+ if (accountType != null && accountType.areContactsWritable()) {
+ return values.getAsLong(RawContacts._ID);
+ }
}
- return false;
+ // No writable raw-contact was found.
+ return -1;
}
public int getDirectoryExportSupport() {
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 3fbca54..fdfd0f7 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -222,7 +222,7 @@
*/
public static Intent createNewRawContactIntent(Context context,
ArrayList<ContentValues> values, AccountWithDataSet account,
- Class<?> callbackActivity, String callbackAction) {
+ Class<? extends Activity> callbackActivity, String callbackAction) {
Intent serviceIntent = new Intent(
context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_NEW_RAW_CONTACT);
@@ -290,8 +290,9 @@
* @param updatedPhotoPath denotes a temporary file containing the contact's new photo.
*/
public static Intent createSaveContactIntent(Context context, EntityDeltaList state,
- String saveModeExtraKey, int saveMode, boolean isProfile, Class<?> callbackActivity,
- String callbackAction, long rawContactId, String updatedPhotoPath) {
+ String saveModeExtraKey, int saveMode, boolean isProfile,
+ Class<? extends Activity> callbackActivity, String callbackAction, long rawContactId,
+ String updatedPhotoPath) {
Bundle bundle = new Bundle();
bundle.putString(String.valueOf(rawContactId), updatedPhotoPath);
return createSaveContactIntent(context, state, saveModeExtraKey, saveMode, isProfile,
@@ -306,8 +307,9 @@
* @param updatedPhotos maps each raw-contact's ID to the file-path of the new photo.
*/
public static Intent createSaveContactIntent(Context context, EntityDeltaList state,
- String saveModeExtraKey, int saveMode, boolean isProfile, Class<?> callbackActivity,
- String callbackAction, Bundle updatedPhotos) {
+ String saveModeExtraKey, int saveMode, boolean isProfile,
+ Class<? extends Activity> callbackActivity, String callbackAction,
+ Bundle updatedPhotos) {
Intent serviceIntent = new Intent(
context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_SAVE_CONTACT);
@@ -317,19 +319,20 @@
serviceIntent.putExtra(EXTRA_UPDATED_PHOTOS, (Parcelable) updatedPhotos);
}
- // Callback intent will be invoked by the service once the contact is
- // saved. The service will put the URI of the new contact as "data" on
- // the callback intent.
- Intent callbackIntent = new Intent(context, callbackActivity);
- callbackIntent.putExtra(saveModeExtraKey, saveMode);
- callbackIntent.setAction(callbackAction);
- serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
+ if (callbackActivity != null) {
+ // Callback intent will be invoked by the service once the contact is
+ // saved. The service will put the URI of the new contact as "data" on
+ // the callback intent.
+ Intent callbackIntent = new Intent(context, callbackActivity);
+ callbackIntent.putExtra(saveModeExtraKey, saveMode);
+ callbackIntent.setAction(callbackAction);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
+ }
return serviceIntent;
}
private void saveContact(Intent intent) {
EntityDeltaList state = intent.getParcelableExtra(EXTRA_CONTACT_STATE);
- Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
boolean isProfile = intent.getBooleanExtra(EXTRA_SAVE_IS_PROFILE, false);
Bundle updatedPhotos = intent.getParcelableExtra(EXTRA_UPDATED_PHOTOS);
@@ -462,15 +465,17 @@
}
}
- if (succeeded) {
- // Mark the intent to indicate that the save was successful (even if the lookup URI
- // is now null). For local contacts or the local profile, it's possible that the
- // save triggered removal of the contact, so no lookup URI would exist..
- callbackIntent.putExtra(EXTRA_SAVE_SUCCEEDED, true);
+ Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+ if (callbackIntent != null) {
+ if (succeeded) {
+ // Mark the intent to indicate that the save was successful (even if the lookup URI
+ // is now null). For local contacts or the local profile, it's possible that the
+ // save triggered removal of the contact, so no lookup URI would exist..
+ callbackIntent.putExtra(EXTRA_SAVE_SUCCEEDED, true);
+ }
+ callbackIntent.setData(lookupUri);
+ deliverCallback(callbackIntent);
}
- callbackIntent.setData(lookupUri);
-
- deliverCallback(callbackIntent);
}
/**
@@ -554,7 +559,7 @@
* @param callbackAction is the intent action for the callback intent
*/
public static Intent createNewGroupIntent(Context context, AccountWithDataSet account,
- String label, long[] rawContactsToAdd, Class<?> callbackActivity,
+ String label, long[] rawContactsToAdd, Class<? extends Activity> callbackActivity,
String callbackAction) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_CREATE_GROUP);
@@ -618,7 +623,7 @@
* Creates an intent that can be sent to this service to rename a group.
*/
public static Intent createGroupRenameIntent(Context context, long groupId, String newLabel,
- Class<?> callbackActivity, String callbackAction) {
+ Class<? extends Activity> callbackActivity, String callbackAction) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_RENAME_GROUP);
serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
@@ -689,7 +694,7 @@
*/
public static Intent createGroupUpdateIntent(Context context, long groupId, String newLabel,
long[] rawContactsToAdd, long[] rawContactsToRemove,
- Class<?> callbackActivity, String callbackAction) {
+ Class<? extends Activity> callbackActivity, String callbackAction) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_UPDATE_GROUP);
serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
@@ -959,7 +964,7 @@
*/
public static Intent createJoinContactsIntent(Context context, long contactId1,
long contactId2, boolean contactWritable,
- Class<?> callbackActivity, String callbackAction) {
+ Class<? extends Activity> callbackActivity, String callbackAction) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_JOIN_CONTACTS);
serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID1, contactId1);
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
index 8d4cb5d..5981e9b 100644
--- a/src/com/android/contacts/activities/AttachPhotoActivity.java
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -18,29 +18,31 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
-import com.android.contacts.model.ExchangeAccountType;
-import com.android.contacts.model.GoogleAccountType;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDeltaList;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.util.ContactPhotoUtils;
+import com.android.contacts.ContactLoader;
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsUtils;
-import android.content.ContentProviderOperation;
import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Intent;
-import android.content.OperationApplicationException;
+import android.content.Loader;
+import android.content.Loader.OnLoadCompleteListener;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.DisplayPhoto;
-import android.provider.ContactsContract.RawContacts;
-import android.widget.Toast;
+import android.provider.MediaStore;
+import android.util.Log;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
+import java.io.File;
/**
* Provides an external interface for other applications to attach images
@@ -49,25 +51,38 @@
* size and give the user a chance to use the face detector.
*/
public class AttachPhotoActivity extends ContactsActivity {
+ private static final String TAG = AttachPhotoActivity.class.getSimpleName();
+
private static final int REQUEST_PICK_CONTACT = 1;
private static final int REQUEST_CROP_PHOTO = 2;
- private static final String RAW_CONTACT_URIS_KEY = "raw_contact_uris";
+ private static final String KEY_CONTACT_URI = "contact_uri";
+ private static final String KEY_TEMP_PHOTO_URI = "temp_photo_uri";
- private Long[] mRawContactIds;
+ private File mTempPhotoFile;
+ private Uri mTempPhotoUri;
private ContentResolver mContentResolver;
- // Height/width (in pixels) to request for the photo - queried from the provider.
+ // Height and width (in pixels) to request for the photo - queried from the provider.
private static int mPhotoDim;
+ private Uri mContactUri;
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (icicle != null) {
- mRawContactIds = toClassArray(icicle.getLongArray(RAW_CONTACT_URIS_KEY));
+ final String uri = icicle.getString(KEY_CONTACT_URI);
+ mContactUri = (uri == null) ? null : Uri.parse(uri);
+
+ mTempPhotoUri = Uri.parse(icicle.getString(KEY_TEMP_PHOTO_URI));
+ mTempPhotoFile = new File(mTempPhotoUri.getPath());
} else {
+ mTempPhotoFile = ContactPhotoUtils.generateTempPhotoFile();
+ mTempPhotoUri = Uri.fromFile(mTempPhotoFile);
+
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(Contacts.CONTENT_ITEM_TYPE);
startActivityForResult(intent, REQUEST_PICK_CONTACT);
@@ -89,32 +104,8 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
-
- if (mRawContactIds != null && mRawContactIds.length != 0) {
- outState.putLongArray(RAW_CONTACT_URIS_KEY, toPrimativeArray(mRawContactIds));
- }
- }
-
- private static long[] toPrimativeArray(Long[] in) {
- if (in == null) {
- return null;
- }
- long[] out = new long[in.length];
- for (int i = 0; i < in.length; i++) {
- out[i] = in[i];
- }
- return out;
- }
-
- private static Long[] toClassArray(long[] in) {
- if (in == null) {
- return null;
- }
- Long[] out = new Long[in.length];
- for (int i = 0; i < in.length; i++) {
- out[i] = in[i];
- }
- return out;
+ if (mContactUri != null) outState.putString(KEY_CONTACT_URI, mContactUri.toString());
+ outState.putString(KEY_TEMP_PHOTO_URI, mTempPhotoUri.toString());
}
@Override
@@ -137,147 +128,94 @@
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", mPhotoDim);
intent.putExtra("outputY", mPhotoDim);
- intent.putExtra("return-data", true);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, mTempPhotoUri);
+
startActivityForResult(intent, REQUEST_CROP_PHOTO);
- // while they're cropping, convert the contact into a raw_contact
- final long contactId = ContentUris.parseId(result.getData());
- final ArrayList<Long> rawContactIdsList = queryForAllRawContactIds(
- mContentResolver, contactId);
- mRawContactIds = new Long[rawContactIdsList.size()];
- mRawContactIds = rawContactIdsList.toArray(mRawContactIds);
+ mContactUri = result.getData();
- if (mRawContactIds == null || rawContactIdsList.isEmpty()) {
- Toast.makeText(this, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- }
} else if (requestCode == REQUEST_CROP_PHOTO) {
- final Bundle extras = result.getExtras();
- if (extras != null && mRawContactIds != null) {
- Bitmap photo = extras.getParcelable("data");
- if (photo != null) {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
-
- final ContentValues imageValues = new ContentValues();
- imageValues.put(Photo.PHOTO, stream.toByteArray());
- imageValues.put(RawContacts.Data.IS_SUPER_PRIMARY, 1);
-
- // attach the photo to every raw contact
- for (Long rawContactId : mRawContactIds) {
-
- // exchange and google only allow one image, so do an update rather than insert
- boolean shouldUpdate = false;
-
- final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
- rawContactId);
- final Uri rawContactDataUri = Uri.withAppendedPath(rawContactUri,
- RawContacts.Data.CONTENT_DIRECTORY);
- insertPhoto(imageValues, rawContactDataUri, true);
- }
+ loadContact(mContactUri, new ContactLoader.Listener() {
+ @Override
+ public void onContactLoaded(ContactLoader.Result contact) {
+ saveContact(contact);
}
- }
- finish();
+ });
}
}
- // TODO: move to background
- public static ArrayList<Long> queryForAllRawContactIds(ContentResolver cr, long contactId) {
- Cursor rawContactIdCursor = null;
- ArrayList<Long> rawContactIds = new ArrayList<Long>();
- try {
- rawContactIdCursor = cr.query(RawContacts.CONTENT_URI,
- new String[] {RawContacts._ID},
- RawContacts.CONTACT_ID + "=" + contactId, null, null);
- if (rawContactIdCursor != null) {
- while (rawContactIdCursor.moveToNext()) {
- rawContactIds.add(rawContactIdCursor.getLong(0));
+ // TODO: consider moving this to ContactLoader, especially if we keep adding similar
+ // code elsewhere (ViewNotificationService is another case). The only concern is that,
+ // although this is convenient, it isn't quite as robust as using LoaderManager... for
+ // instance, the loader doesn't persist across Activity restarts.
+ private void loadContact(Uri contactUri, final ContactLoader.Listener listener) {
+ final ContactLoader loader = new ContactLoader(this, contactUri);
+ loader.registerListener(0, new OnLoadCompleteListener<ContactLoader.Result>() {
+ @Override
+ public void onLoadComplete(
+ Loader<ContactLoader.Result> loader, ContactLoader.Result contact) {
+ try {
+ loader.reset();
}
+ catch (RuntimeException e) {
+ Log.e(TAG, "Error resetting loader", e);
+ }
+ listener.onContactLoaded(contact);
}
- } finally {
- if (rawContactIdCursor != null) {
- rawContactIdCursor.close();
- }
- }
- return rawContactIds;
+ });
+ loader.startLoading();
}
/**
- * Inserts a photo on the raw contact.
- * @param values the photo values
- * @param assertAccount if true, will check to verify that no photos exist for Google,
- * Exchange and unsynced phone account types. These account types only take one picture,
- * so if one exists, the account will be updated with the new photo.
+ * If prerequisites have been met, attach the photo to a raw-contact and save.
+ * The prerequisites are:
+ * - photo has been cropped
+ * - contact has been loaded
*/
- private void insertPhoto(ContentValues values, Uri rawContactDataUri,
- boolean assertAccount) {
+ private void saveContact(ContactLoader.Result contact) {
- ArrayList<ContentProviderOperation> operations =
- new ArrayList<ContentProviderOperation>();
-
- if (assertAccount) {
- // Make sure no pictures exist for Google, Exchange and unsynced phone accounts.
- operations.add(ContentProviderOperation.newAssertQuery(rawContactDataUri)
- .withSelection(Photo.MIMETYPE + "=? AND "
- + RawContacts.DATA_SET + " IS NULL AND ("
- + RawContacts.ACCOUNT_TYPE + " IN (?,?) OR "
- + RawContacts.ACCOUNT_TYPE + " IS NULL)",
- new String[] {Photo.CONTENT_ITEM_TYPE, GoogleAccountType.ACCOUNT_TYPE,
- ExchangeAccountType.ACCOUNT_TYPE})
- .withExpectedCount(0).build());
+ // Obtain the raw-contact that we will save to.
+ EntityDeltaList deltaList = contact.createEntityDeltaList();
+ EntityDelta raw = deltaList.getFirstWritableRawContact(this);
+ if (raw == null) {
+ Log.w(TAG, "no writable raw-contact found");
+ return;
}
- // insert the photo
- values.put(Photo.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
- operations.add(ContentProviderOperation.newInsert(rawContactDataUri)
- .withValues(values).build());
-
- try {
- mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
- } catch (RemoteException e) {
- throw new IllegalStateException("Problem querying raw_contacts/data", e);
- } catch (OperationApplicationException e) {
- // the account doesn't allow multiple photos, so update
- if (assertAccount) {
- updatePhoto(values, rawContactDataUri, false);
- } else {
- throw new IllegalStateException("Problem inserting photo into raw_contacts/data", e);
- }
+ // Create a scaled, compressed bitmap to add to the entity-delta list.
+ final int size = ContactsUtils.getThumbnailSize(this);
+ final Bitmap bitmap = BitmapFactory.decodeFile(mTempPhotoFile.getAbsolutePath());
+ final Bitmap scaled = Bitmap.createScaledBitmap(bitmap, size, size, false);
+ final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);
+ if (compressed == null) {
+ Log.w(TAG, "could not create scaled and compressed Bitmap");
+ return;
}
- }
- /**
- * Tries to update the photo on the raw_contact. If no photo exists, and allowInsert == true,
- * then will try to {@link #updatePhoto(ContentValues, boolean)}
- */
- private void updatePhoto(ContentValues values, Uri rawContactDataUri,
- boolean allowInsert) {
- ArrayList<ContentProviderOperation> operations =
- new ArrayList<ContentProviderOperation>();
-
- values.remove(Photo.MIMETYPE);
-
- // check that a photo exists
- operations.add(ContentProviderOperation.newAssertQuery(rawContactDataUri)
- .withSelection(Photo.MIMETYPE + "=?", new String[] {
- Photo.CONTENT_ITEM_TYPE
- }).withExpectedCount(1).build());
-
- // update that photo
- operations.add(ContentProviderOperation.newUpdate(rawContactDataUri)
- .withSelection(Photo.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE})
- .withValues(values).build());
-
- try {
- mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
- } catch (RemoteException e) {
- throw new IllegalStateException("Problem querying raw_contacts/data", e);
- } catch (OperationApplicationException e) {
- if (allowInsert) {
- // they deleted the photo between insert and update, so insert one
- insertPhoto(values, rawContactDataUri, false);
- } else {
- throw new IllegalStateException("Problem inserting photo raw_contacts/data", e);
- }
+ // Add compressed bitmap to entity-delta... this allows us to save to
+ // a new contact; otherwise the entity-delta-list would be empty, and
+ // the ContactSaveService would not create the new contact, and the
+ // full-res photo would fail to be saved to the non-existent contact.
+ AccountType account = raw.getRawContactAccountType(this);
+ EntityDelta.ValuesDelta values =
+ EntityModifier.ensureKindExists(raw, account, Photo.CONTENT_ITEM_TYPE);
+ if (values == null) {
+ Log.w(TAG, "cannot attach photo to this account type");
+ return;
}
+ values.put(Photo.PHOTO, compressed);
+
+ // Finally, invoke the ContactSaveService.
+ Log.v(TAG, "all prerequisites met, about to save photo to contact");
+ Intent intent = ContactSaveService.createSaveContactIntent(
+ this,
+ deltaList,
+ "", 0,
+ contact.isUserProfile(),
+ null, null,
+ raw.getRawContactId(),
+ mTempPhotoFile.getAbsolutePath());
+ startService(intent);
+ finish();
}
}
diff --git a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
index 98abfbc..aa3be87 100644
--- a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
+++ b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
@@ -560,19 +560,10 @@
}
public void findEditableRawContact() {
- if (mEntityDeltaList == null) {
- return;
- }
- for (EntityDelta state : mEntityDeltaList) {
- final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final String dataSet = state.getValues().getAsString(RawContacts.DATA_SET);
- final AccountType type = mAccountTypeManager.getAccountType(accountType, dataSet);
-
- if (type.areContactsWritable()) {
- mEditableAccountType = type;
- mState = state;
- return;
- }
+ if (mEntityDeltaList == null) return;
+ mState = mEntityDeltaList.getFirstWritableRawContact(this);
+ if (mState != null) {
+ mEditableAccountType = mState.getRawContactAccountType(this);
}
}
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index ea9e116..1ccf32c 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -57,17 +57,8 @@
/** Shows a toogle button for hiding/showing updates. Don't submit with true */
private static final boolean DEBUG_TRANSITIONS = false;
- /**
- * Boolean intent key that specifies whether pressing the "up" affordance in this activity
- * should cause it to finish itself or launch an intent to bring the user back to a specific
- * parent activity - the {@link PeopleActivity}.
- */
- public static final String INTENT_KEY_FINISH_ACTIVITY_ON_UP_SELECTED =
- "finishActivityOnUpSelected";
-
private ContactLoader.Result mContactData;
private Uri mLookupUri;
- private boolean mFinishActivityOnUpSelected;
private ContactDetailLayoutController mContactDetailLayoutController;
private ContactLoaderFragment mLoaderFragment;
@@ -94,9 +85,6 @@
return;
}
- mFinishActivityOnUpSelected = getIntent().getBooleanExtra(
- INTENT_KEY_FINISH_ACTIVITY_ON_UP_SELECTED, false);
-
setContentView(R.layout.contact_detail_activity);
mContactDetailLayoutController = new ContactDetailLayoutController(this, savedState,
@@ -309,24 +297,4 @@
*/
public boolean handleKeyDown(int keyCode);
}
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
- switch (item.getItemId()) {
- case android.R.id.home:
- if (mFinishActivityOnUpSelected) {
- finish();
- return true;
- }
- Intent intent = new Intent(this, PeopleActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- finish();
- return true;
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- }
}
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index ae8fe09..6f13924 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -569,7 +569,7 @@
Log.d(TAG, "onStart(). current position: " + mPageChangeListener.getCurrentPosition()
+ ". Reset all menu visibility state.");
}
- updateFakeMenuButtonsVisibility(currentPosition == TAB_INDEX_DIALER);
+ updateFakeMenuButtonsVisibility(currentPosition == TAB_INDEX_DIALER && !mInSearchUi);
for (int i = 0; i < TAB_INDEX_COUNT; i++) {
sendFragmentVisibilityChange(i, i == currentPosition);
}
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index 6c36b5d..6562278 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -106,7 +106,6 @@
@Override
public void onContactSelected(Uri contactUri) {
Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
- intent.putExtra(ContactDetailActivity.INTENT_KEY_FINISH_ACTIVITY_ON_UP_SELECTED, true);
startActivity(intent);
}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 5915a7a..0283e06 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -27,6 +27,7 @@
import com.android.contacts.detail.ContactDetailUpdatesFragment;
import com.android.contacts.detail.ContactLoaderFragment;
import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
+import com.android.contacts.dialog.ClearFrequentsDialog;
import com.android.contacts.group.GroupBrowseListFragment;
import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
import com.android.contacts.group.GroupDetailFragment;
@@ -1055,12 +1056,6 @@
setupContactDetailFragment(contactLookupUri);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW, contactLookupUri);
- // In search mode, the "up" affordance in the contact detail page should return the
- // user to the search results, so finish the activity when that button is selected.
- if (mActionBarAdapter.isSearchMode()) {
- intent.putExtra(
- ContactDetailActivity.INTENT_KEY_FINISH_ACTIVITY_ON_UP_SELECTED, true);
- }
startActivity(intent);
}
}
@@ -1390,6 +1385,7 @@
return false;
}
+ // Get references to individual menu items in the menu
final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact);
final MenuItem contactsFilterMenu = menu.findItem(R.id.menu_contacts_filter);
@@ -1398,22 +1394,27 @@
addGroupMenu = menu.findItem(R.id.menu_custom_add_group);
}
+ final MenuItem clearFrequentsMenu = menu.findItem(R.id.menu_clear_frequents);
+
final boolean isSearchMode = mActionBarAdapter.isSearchMode();
if (isSearchMode) {
addContactMenu.setVisible(false);
addGroupMenu.setVisible(false);
contactsFilterMenu.setVisible(false);
+ clearFrequentsMenu.setVisible(false);
} else {
switch (mActionBarAdapter.getCurrentTab()) {
case TabState.FAVORITES:
addContactMenu.setVisible(false);
addGroupMenu.setVisible(false);
contactsFilterMenu.setVisible(false);
+ clearFrequentsMenu.setVisible(hasFrequents());
break;
case TabState.ALL:
addContactMenu.setVisible(true);
addGroupMenu.setVisible(false);
contactsFilterMenu.setVisible(true);
+ clearFrequentsMenu.setVisible(false);
break;
case TabState.GROUPS:
// Do not display the "new group" button if no accounts are available
@@ -1424,6 +1425,7 @@
}
addContactMenu.setVisible(false);
contactsFilterMenu.setVisible(false);
+ clearFrequentsMenu.setVisible(false);
break;
}
}
@@ -1437,6 +1439,18 @@
return true;
}
+ /**
+ * Returns whether there are any frequently contacted people being displayed
+ * @return
+ */
+ private boolean hasFrequents() {
+ if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+ return mFrequentFragment.hasFrequents();
+ } else {
+ return mFavoritesFragment.hasFrequents();
+ }
+ }
+
private void makeMenuItemVisible(Menu menu, int itemId, boolean visible) {
MenuItem item =menu.findItem(itemId);
if (item != null) {
@@ -1504,6 +1518,10 @@
ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable());
return true;
}
+ case R.id.menu_clear_frequents: {
+ ClearFrequentsDialog.show(getFragmentManager());
+ return true;
+ }
case R.id.menu_accounts: {
final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
diff --git a/src/com/android/contacts/activities/PhotoSelectionActivity.java b/src/com/android/contacts/activities/PhotoSelectionActivity.java
index d76af25..d443782 100644
--- a/src/com/android/contacts/activities/PhotoSelectionActivity.java
+++ b/src/com/android/contacts/activities/PhotoSelectionActivity.java
@@ -449,45 +449,36 @@
}
private final class PhotoHandler extends PhotoSelectionHandler {
- private PhotoHandler(Context context, View photoView, int photoMode,
- EntityDeltaList state) {
- super(context, photoView, photoMode, mIsDirectoryContact, state);
- setListener(new PhotoListener(context, mIsProfile));
+ private PhotoActionListener mListener;
+
+ private PhotoHandler(
+ Context context, View photoView, int photoMode, EntityDeltaList state) {
+ super(context, photoView, photoMode, PhotoSelectionActivity.this.mIsDirectoryContact,
+ state);
+ mListener = new PhotoListener();
+ }
+
+ @Override
+ public PhotoActionListener getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void startPhotoActivity(Intent intent, int requestCode, File photoFile) {
+ mSubActivityInProgress = true;
+ mCurrentPhotoFile = photoFile;
+ PhotoSelectionActivity.this.startActivityForResult(intent, requestCode);
}
private final class PhotoListener extends PhotoActionListener {
- @SuppressWarnings("hiding")
- private final boolean mIsProfile;
- private final Context mContext;
-
- private PhotoListener(Context context, boolean isProfile) {
- mContext = context;
- mIsProfile = isProfile;
- }
-
- @Override
- public void startTakePhotoActivity(Intent intent, int requestCode, File photoFile) {
- mSubActivityInProgress = true;
- mCurrentPhotoFile = photoFile;
- startActivityForResult(intent, requestCode);
- }
-
- @Override
- public void startPickFromGalleryActivity(Intent intent, int requestCode,
- File photoFile) {
- mSubActivityInProgress = true;
- mCurrentPhotoFile = photoFile;
- startActivityForResult(intent, requestCode);
- }
@Override
public void onPhotoSelected(Bitmap bitmap) {
EntityDeltaList delta = getDeltaForAttachingPhotoToContact();
long rawContactId = getWritableEntityId();
String filePath = mCurrentPhotoFile.getAbsolutePath();
- Intent intent = ContactSaveService.createSaveContactIntent(mContext, delta,
- "", 0, mIsProfile, PhotoSelectionActivity.class,
- ContactEditorActivity.ACTION_SAVE_COMPLETED, rawContactId, filePath);
+ Intent intent = ContactSaveService.createSaveContactIntent(
+ mContext, delta, "", 0, mIsProfile, null, null, rawContactId, filePath);
startService(intent);
finish();
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 2dc7bc4..34250d5 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -2016,8 +2016,7 @@
if (defaultGroupId == -1) return;
// add the group membership to the current state
- final EntityDeltaList contactDeltaList = EntityDeltaList.fromIterator(
- mContactData.getEntities().iterator());
+ final EntityDeltaList contactDeltaList = mContactData.createEntityDeltaList();
final EntityDelta rawContactEntityDelta = contactDeltaList.get(0);
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
diff --git a/src/com/android/contacts/detail/ContactDetailPhotoSetter.java b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
index 13f5970..b674dc0 100644
--- a/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
+++ b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
@@ -63,8 +63,7 @@
@Override
public void onClick(View v) {
// Assemble the intent.
- EntityDeltaList delta = EntityDeltaList.fromIterator(
- mContactData.getEntities().iterator());
+ EntityDeltaList delta = mContactData.createEntityDeltaList();
// Find location and bounds of target view, adjusting based on the
// assumed local density.
diff --git a/src/com/android/contacts/detail/PhotoSelectionHandler.java b/src/com/android/contacts/detail/PhotoSelectionHandler.java
index 1423c65..b336d90 100644
--- a/src/com/android/contacts/detail/PhotoSelectionHandler.java
+++ b/src/com/android/contacts/detail/PhotoSelectionHandler.java
@@ -19,15 +19,14 @@
import com.android.contacts.R;
import com.android.contacts.editor.PhotoActionPopup;
import com.android.contacts.model.AccountType;
-import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityDeltaList;
import com.android.contacts.model.EntityModifier;
+import com.android.contacts.util.ContactPhotoUtils;
import android.app.Activity;
import android.content.ActivityNotFoundException;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@@ -35,10 +34,8 @@
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.Uri;
-import android.os.Environment;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.DisplayPhoto;
-import android.provider.ContactsContract.RawContacts;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
@@ -46,30 +43,20 @@
import android.widget.ListPopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.Toast;
-
-import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
/**
* Handles displaying a photo selection popup for a given photo view and dealing with the results
* that come back.
*/
-public class PhotoSelectionHandler implements OnClickListener {
+public abstract class PhotoSelectionHandler implements OnClickListener {
private static final String TAG = PhotoSelectionHandler.class.getSimpleName();
- private static final File PHOTO_DIR = new File(
- Environment.getExternalStorageDirectory() + "/DCIM/Camera");
-
- private static final String PHOTO_DATE_FORMAT = "'IMG'_yyyyMMdd_HHmmss";
-
private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1001;
private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002;
- private final Context mContext;
+ protected final Context mContext;
private final View mPhotoView;
private final int mPhotoMode;
private final int mPhotoPickSize;
@@ -87,6 +74,8 @@
mIsDirectoryContact = isDirectoryContact;
mState = state;
mPhotoPickSize = getPhotoPickSize();
+
+ // NOTE: subclasses should call setListener()
}
public void destroy() {
@@ -95,13 +84,7 @@
}
}
- public PhotoActionListener getListener() {
- return mListener;
- }
-
- public void setListener(PhotoActionListener listener) {
- mListener = listener;
- }
+ public abstract PhotoActionListener getListener();
@Override
public void onClick(View v) {
@@ -109,10 +92,11 @@
if (getWritableEntityIndex() != -1) {
mPopup = PhotoActionPopup.createPopupMenu(
mContext, mPhotoView, mListener, mPhotoMode);
+ final PhotoActionListener listener = mListener; // a bit more bulletproof
mPopup.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
- mListener.onPhotoSelectionDismissed();
+ listener.onPhotoSelectionDismissed();
}
});
mPopup.show();
@@ -152,25 +136,8 @@
*/
private int getWritableEntityIndex() {
// Directory entries are non-writable.
- if (mIsDirectoryContact) {
- return -1;
- }
-
- // Find the first writable entity.
- int entityIndex = 0;
- for (EntityDelta delta : mState) {
- ContentValues entityValues = delta.getValues().getCompleteValues();
- String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
- String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
- AccountType accountType = AccountTypeManager.getInstance(mContext).getAccountType(
- type, dataSet);
- if (accountType.areContactsWritable()) {
- mWritableAccount = accountType;
- return entityIndex;
- }
- entityIndex++;
- }
- return -1;
+ if (mIsDirectoryContact) return -1;
+ return mState.indexOfFirstWritableRawContact(mContext);
}
/**
@@ -187,7 +154,6 @@
* Utility method to retrieve the entity delta for attaching the given bitmap to the contact.
* This will attach the photo to the first contact-writable account that provided data to the
* contact. It is the caller's responsibility to apply the delta.
- * @param bitmap The photo to use.
* @return An entity delta list that can be applied to associate the bitmap with the contact,
* or null if the photo could not be parsed or none of the accounts associated with the
* contact are writable.
@@ -208,6 +174,9 @@
return null;
}
+ /** Used by subclasses to delegate to their enclosing Activity or Fragment. */
+ protected abstract void startPhotoActivity(Intent intent, int requestCode, File photoFile);
+
/**
* Sends a newly acquired photo to Gallery for cropping
*/
@@ -222,22 +191,33 @@
// Launch gallery to crop the photo
final Intent intent = getCropImageIntent(f);
- mListener.startPickFromGalleryActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, f);
+ startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, f);
} catch (Exception e) {
Log.e(TAG, "Cannot crop image", e);
Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
}
}
- private String getPhotoFileName() {
- Date date = new Date(System.currentTimeMillis());
- SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT);
- return dateFormat.format(date) + ".jpg";
+ /**
+ * Should initiate an activity to take a photo using the camera.
+ * @param photoFile The file path that will be used to store the photo. This is generally
+ * what should be returned by
+ * {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()}.
+ */
+ private void startTakePhotoActivity(File photoFile) {
+ final Intent intent = getTakePhotoIntent(photoFile);
+ startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoFile);
}
- private File getPhotoFile() {
- PHOTO_DIR.mkdirs();
- return new File(PHOTO_DIR, getPhotoFileName());
+ /**
+ * Should initiate an activity pick a photo from the gallery.
+ * @param photoFile The temporary file that the cropped image is written to before being
+ * stored by the content-provider.
+ * {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}.
+ */
+ private void startPickFromGalleryActivity(File photoFile) {
+ final Intent intent = getPhotoPickIntent(photoFile);
+ startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoFile);
}
private int getPhotoPickSize() {
@@ -308,9 +288,7 @@
public void onTakePhotoChosen() {
try {
// Launch camera to take photo for selected contact
- File f = getPhotoFile();
- final Intent intent = getTakePhotoIntent(f);
- startTakePhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, f);
+ startTakePhotoActivity(ContactPhotoUtils.generateTempPhotoFile());
} catch (ActivityNotFoundException e) {
Toast.makeText(mContext, R.string.photoPickerNotFoundText,
Toast.LENGTH_LONG).show();
@@ -321,9 +299,7 @@
public void onPickFromGalleryChosen() {
try {
// Launch picker to choose photo for selected contact
- File f = getPhotoFile();
- final Intent intent = getPhotoPickIntent(f);
- startPickFromGalleryActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, f);
+ startPickFromGalleryActivity(ContactPhotoUtils.generateTempPhotoFile());
} catch (ActivityNotFoundException e) {
Toast.makeText(mContext, R.string.photoPickerNotFoundText,
Toast.LENGTH_LONG).show();
@@ -331,28 +307,6 @@
}
/**
- * Should initiate an activity to take a photo using the camera.
- * @param intent The image capture intent.
- * @param requestCode The request code to use, suitable for handling by
- * {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}.
- * @param photoFile The file path that will be used to store the photo. This is generally
- * what should be returned by
- * {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()}.
- */
- public abstract void startTakePhotoActivity(Intent intent, int requestCode, File photoFile);
-
- /**
- * Should initiate an activity pick a photo from the gallery.
- * @param intent The image capture intent.
- * @param requestCode The request code to use, suitable for handling by
- * @param photoFile The temporary file that the cropped image is written to before being
- * stored by the content-provider.
- * {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}.
- */
- public abstract void startPickFromGalleryActivity(Intent intent, int requestCode,
- File photoFile);
-
- /**
* Called when the user has completed selection of a photo.
* @param bitmap The selected and cropped photo.
*/
diff --git a/src/com/android/contacts/dialog/ClearFrequentsDialog.java b/src/com/android/contacts/dialog/ClearFrequentsDialog.java
new file mode 100644
index 0000000..ecb1a27
--- /dev/null
+++ b/src/com/android/contacts/dialog/ClearFrequentsDialog.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 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.contacts.dialog;
+
+import com.android.contacts.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+
+/**
+ * Dialog that clears the frequently contacted list after confirming with the user.
+ */
+public class ClearFrequentsDialog extends DialogFragment {
+ /** Preferred way to show this dialog */
+ public static void show(FragmentManager fragmentManager) {
+ ClearFrequentsDialog dialog = new ClearFrequentsDialog();
+ dialog.show(fragmentManager, "clearFrequents");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final ContentResolver resolver = getActivity().getContentResolver();
+ final OnClickListener okListener = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ resolver.delete(ContactsContract.DataUsageFeedback.DELETE_USAGE_URI,
+ null, null);
+ return null;
+ }
+ };
+ task.execute();
+ }
+ };
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.clearFrequentsConfirmation_title)
+ .setMessage(R.string.clearFrequentsConfirmation)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, okListener)
+ .setCancelable(true)
+ .create();
+ }
+}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index b9f1afc..d93fb5c 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -462,15 +462,15 @@
mListener.onCustomEditContactActivityRequested(account, uri, null, false);
}
- private void bindEditorsForExistingContact(ContactLoader.Result data) {
+ private void bindEditorsForExistingContact(ContactLoader.Result contact) {
setEnabled(true);
- mState = EntityDeltaList.fromIterator(data.getEntities().iterator());
+ mState = contact.createEntityDeltaList();
setIntentExtras(mIntentExtras);
mIntentExtras = null;
// For user profile, change the contacts query URI
- mIsUserProfile = data.isUserProfile();
+ mIsUserProfile = contact.isUserProfile();
boolean localProfileExists = false;
if (mIsUserProfile) {
@@ -1714,21 +1714,32 @@
private final class PhotoHandler extends PhotoSelectionHandler {
final long mRawContactId;
+ private final BaseRawContactEditorView mEditor;
+ private PhotoActionListener mListener;
public PhotoHandler(Context context, BaseRawContactEditorView editor, int photoMode,
EntityDeltaList state) {
super(context, editor.getPhotoEditor(), photoMode, false, state);
- setListener(new PhotoEditorListener(editor));
+ mEditor = editor;
mRawContactId = editor.getRawContactId();
+ mListener = new PhotoEditorListener();
+ }
+
+ @Override
+ public PhotoActionListener getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void startPhotoActivity(Intent intent, int requestCode, File photoFile) {
+ mRawContactIdRequestingPhoto = mEditor.getRawContactId();
+ mStatus = Status.SUB_ACTIVITY;
+ mCurrentPhotoFile = photoFile;
+ ContactEditorFragment.this.startActivityForResult(intent, requestCode);
}
private final class PhotoEditorListener extends PhotoSelectionHandler.PhotoActionListener
implements EditorListener {
- private final BaseRawContactEditorView mEditor;
-
- private PhotoEditorListener(BaseRawContactEditorView editor) {
- mEditor = editor;
- }
@Override
public void onRequest(int request) {
@@ -1776,23 +1787,6 @@
}
@Override
- public void startTakePhotoActivity(Intent intent, int requestCode, File photoFile) {
- mRawContactIdRequestingPhoto = mEditor.getRawContactId();
- mStatus = Status.SUB_ACTIVITY;
- mCurrentPhotoFile = photoFile;
- startActivityForResult(intent, requestCode);
- }
-
- @Override
- public void startPickFromGalleryActivity(Intent intent, int requestCode,
- File photoFile) {
- mRawContactIdRequestingPhoto = mEditor.getRawContactId();
- mStatus = Status.SUB_ACTIVITY;
- mCurrentPhotoFile = photoFile;
- startActivityForResult(intent, requestCode);
- }
-
- @Override
public void onPhotoSelected(Bitmap bitmap) {
setPhoto(mRawContactIdRequestingPhoto, bitmap, mCurrentPhotoFile);
mRawContactIdRequestingPhoto = -1;
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index 8a0dd0e..0cbe97e 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -20,6 +20,7 @@
import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.util.ContactPhotoUtils;
import com.android.contacts.ContactsUtils;
import android.content.Context;
@@ -27,19 +28,14 @@
import android.graphics.BitmapFactory;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
/**
* Simple editor for {@link Photo}.
*/
public class PhotoEditorView extends FrameLayout implements Editor {
- private static final String TAG = "PhotoEditorView";
private ImageView mPhotoImageView;
private View mFrameView;
@@ -127,23 +123,7 @@
return mHasSetPhoto;
}
- /**
- * Creates a byte[] containing the PNG-compressed bitmap, or null if
- * something goes wrong.
- */
- private static byte[] compressBitmap(Bitmap bitmap) {
- final int size = bitmap.getWidth() * bitmap.getHeight() * 4;
- final ByteArrayOutputStream out = new ByteArrayOutputStream(size);
- try {
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
- out.flush();
- out.close();
- return out.toByteArray();
- } catch (IOException e) {
- Log.w(TAG, "Unable to serialize photo: " + e.toString());
- return null;
- }
- }
+
/**
* Assign the given {@link Bitmap} as the new value, updating UI and
@@ -172,7 +152,8 @@
// there is a change in EITHER the delta-list OR a changed photo...
// this way, there is always a change in the delta-list.
final int size = ContactsUtils.getThumbnailSize(getContext());
- byte[] compressed = compressBitmap(Bitmap.createScaledBitmap(photo, size, size, false));
+ final Bitmap scaled = Bitmap.createScaledBitmap(photo, size, size, false);
+ final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);
if (compressed != null) mEntry.put(Photo.PHOTO, compressed);
}
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 9ea0468..ddf88b0 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -53,6 +53,7 @@
private Resources mResources;
private Cursor mContactCursor = null;
private ContactPhotoManager mPhotoManager;
+ private int mNumFrequents;
/**
* Index of the first NON starred contact in the {@link Cursor}
@@ -119,6 +120,7 @@
mResources = context.getResources();
mColumnCount = (displayType == DisplayType.FREQUENT_ONLY ? 1 : numCols);
mDisplayType = displayType;
+ mNumFrequents = 0;
// Converting padding in dips to padding in pixels
mPaddingInPixels = mContext.getResources()
@@ -184,6 +186,25 @@
public void setContactCursor(Cursor cursor) {
mContactCursor = cursor;
mDividerPosition = getDividerPosition(cursor);
+
+ // count the number of frequents
+ switch (mDisplayType) {
+ case STARRED_ONLY:
+ case GROUP_MEMBERS:
+ mNumFrequents = 0;
+ break;
+ case STREQUENT:
+ case STREQUENT_PHONE_ONLY:
+ mNumFrequents = mContactCursor.getCount() - mDividerPosition;
+ break;
+ case FREQUENT_ONLY:
+ mNumFrequents = mContactCursor.getCount();
+ break;
+ default:
+ throw new IllegalArgumentException("Unrecognized DisplayType " + mDisplayType);
+ }
+
+ // cause a refresh of any views that rely on this data
notifyDataSetChanged();
}
@@ -274,6 +295,13 @@
return contact;
}
+ /**
+ * Returns the number of frequents that will be displayed in the list.
+ */
+ public int getNumFrequents() {
+ return mNumFrequents;
+ }
+
@Override
public int getCount() {
if (mContactCursor == null || mContactCursor.isClosed()) {
@@ -289,11 +317,9 @@
// Takes numbers of rows the Starred Contacts Occupy
int starredRowCount = getRowCount(mDividerPosition);
- // Calculates the number of frequent contacts
- int frequentRowCount = mContactCursor.getCount() - mDividerPosition ;
-
- // If there are any frequent contacts add one for the divider
- frequentRowCount += frequentRowCount == 0 ? 0 : 1;
+ // Compute the frequent row count which is 1 plus the number of frequents
+ // (to account for the divider) or 0 if there are no frequents.
+ int frequentRowCount = mNumFrequents == 0 ? 0 : mNumFrequents + 1;
// Return the number of starred plus frequent rows
return starredRowCount + frequentRowCount;
diff --git a/src/com/android/contacts/list/ContactTileListFragment.java b/src/com/android/contacts/list/ContactTileListFragment.java
index 92ff41d..9f34c54 100644
--- a/src/com/android/contacts/list/ContactTileListFragment.java
+++ b/src/com/android/contacts/list/ContactTileListFragment.java
@@ -61,6 +61,8 @@
private TextView mEmptyView;
private ListView mListView;
+ private boolean mOptionsMenuHasFrequents;
+
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
@@ -98,6 +100,24 @@
getLoaderManager().initLoader(LOADER_CONTACTS, null, mContactTileLoaderListener);
}
+ /**
+ * Returns whether there are any frequents with the side effect of setting the
+ * internal flag mOptionsMenuHasFrequents to the value. This should be called externally
+ * by the activity that is about to prepare the options menu with the clear frequents
+ * menu item.
+ */
+ public boolean hasFrequents() {
+ mOptionsMenuHasFrequents = internalHasFrequents();
+ return mOptionsMenuHasFrequents;
+ }
+
+ /**
+ * Returns whether there are any frequents.
+ */
+ private boolean internalHasFrequents() {
+ return mAdapter.getNumFrequents() > 0;
+ }
+
public void setColumnCount(int columnCount) {
mAdapter.setColumnCount(columnCount);
}
@@ -136,12 +156,25 @@
mAdapter.setContactCursor(data);
mEmptyView.setText(getEmptyStateText());
mListView.setEmptyView(mEmptyView);
+
+ // invalidate the menu options if needed
+ invalidateOptionsMenuIfNeeded();
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {}
};
+ private boolean isOptionsMenuChanged() {
+ return mOptionsMenuHasFrequents != internalHasFrequents();
+ }
+
+ private void invalidateOptionsMenuIfNeeded() {
+ if (isOptionsMenuChanged()) {
+ getActivity().invalidateOptionsMenu();
+ }
+ }
+
private String getEmptyStateText() {
String emptyText;
switch (mDisplayType) {
diff --git a/src/com/android/contacts/list/PhoneFavoriteFragment.java b/src/com/android/contacts/list/PhoneFavoriteFragment.java
index 91f8ea3..a8eafdc 100644
--- a/src/com/android/contacts/list/PhoneFavoriteFragment.java
+++ b/src/com/android/contacts/list/PhoneFavoriteFragment.java
@@ -18,6 +18,7 @@
import com.android.contacts.ContactPhotoManager;
import com.android.contacts.ContactTileLoaderFactory;
import com.android.contacts.R;
+import com.android.contacts.dialog.ClearFrequentsDialog;
import com.android.contacts.interactions.ImportExportDialogFragment;
import com.android.contacts.preference.ContactsPreferences;
import com.android.contacts.util.AccountFilterUtil;
@@ -107,6 +108,9 @@
// Show the filter header with "loading" state.
updateFilterHeaderView();
mAccountFilterHeader.setVisibility(View.VISIBLE);
+
+ // invalidate the options menu if needed
+ invalidateOptionsMenuIfNeeded();
}
@Override
@@ -257,6 +261,8 @@
new ContactsPreferenceChangeListener();
private final ScrollListener mScrollListener = new ScrollListener();
+ private boolean mOptionsMenuHasFrequents;
+
@Override
public void onAttach(Activity activity) {
if (DEBUG) Log.d(TAG, "onAttach()");
@@ -360,6 +366,16 @@
return listLayout;
}
+ private boolean isOptionsMenuChanged() {
+ return mOptionsMenuHasFrequents != hasFrequents();
+ }
+
+ private void invalidateOptionsMenuIfNeeded() {
+ if (isOptionsMenuChanged()) {
+ getActivity().invalidateOptionsMenu();
+ }
+ }
+
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
@@ -367,6 +383,17 @@
}
@Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
+ mOptionsMenuHasFrequents = hasFrequents();
+ clearFrequents.setVisible(mOptionsMenuHasFrequents);
+ }
+
+ private boolean hasFrequents() {
+ return mContactTileAdapter.getNumFrequents() > 0;
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_import_export:
@@ -384,6 +411,9 @@
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
startActivity(intent);
return true;
+ case R.id.menu_clear_frequents:
+ ClearFrequentsDialog.show(getFragmentManager());
+ return true;
}
return false;
}
diff --git a/src/com/android/contacts/list/ShortcutIntentBuilder.java b/src/com/android/contacts/list/ShortcutIntentBuilder.java
index 1be0b74..6c593b0 100644
--- a/src/com/android/contacts/list/ShortcutIntentBuilder.java
+++ b/src/com/android/contacts/list/ShortcutIntentBuilder.java
@@ -248,15 +248,18 @@
Intent shortcutIntent;
// This is a simple shortcut to view a contact.
shortcutIntent = new Intent(ContactsContract.QuickContact.ACTION_QUICK_CONTACT);
- shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ // When starting from the launcher, start in a new, cleared task.
+ // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
+ // clear the whole thing preemptively here since QuickContactActivity will
+ // finish itself when launching other detail activities.
+ shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
shortcutIntent.setData(contactUri);
shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_MODE,
ContactsContract.QuickContact.MODE_LARGE);
shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_EXCLUDE_MIMES,
(String[]) null);
- shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Bitmap icon = generateQuickContactIcon(bitmap);
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index 2cbfa26..2620fb0 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -24,6 +24,7 @@
import android.content.ContentProviderOperation;
import android.content.ContentProviderOperation.Builder;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Entity;
import android.content.Entity.NamedContentValues;
import android.net.Uri;
@@ -210,6 +211,20 @@
}
/**
+ * Return the AccountType that this raw-contact belongs to.
+ */
+ public AccountType getRawContactAccountType(Context context) {
+ ContentValues entityValues = getValues().getCompleteValues();
+ String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
+ String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
+ return AccountTypeManager.getInstance(context).getAccountType(type, dataSet);
+ }
+
+ public Long getRawContactId() {
+ return getValues().getAsLong(RawContacts._ID);
+ }
+
+ /**
* Return the list of child {@link ValuesDelta} from our optimized map,
* creating the list if requested.
*/
diff --git a/src/com/android/contacts/model/EntityDeltaList.java b/src/com/android/contacts/model/EntityDeltaList.java
index 5a9355b..478c879 100644
--- a/src/com/android/contacts/model/EntityDeltaList.java
+++ b/src/com/android/contacts/model/EntityDeltaList.java
@@ -18,6 +18,7 @@
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Entity;
import android.content.EntityIterator;
import android.content.ContentProviderOperation.Builder;
@@ -27,7 +28,6 @@
import android.provider.ContactsContract.AggregationExceptions;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContactsEntity;
import com.google.android.collect.Lists;
@@ -35,7 +35,6 @@
import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
/**
* Container for multiple {@link EntityDelta} objects, usually when editing
@@ -290,6 +289,9 @@
return null;
}
+ /**
+ * Find the raw-contact (an {@link EntityDelta}) with the specified ID.
+ */
public EntityDelta getByRawContactId(Long rawContactId) {
final int index = this.indexOfRawContactId(rawContactId);
return (index == -1) ? null : this.get(index);
@@ -310,6 +312,23 @@
return -1;
}
+ /** Return the index of the first EntityDelta corresponding to a writable raw-contact, or -1. */
+ public int indexOfFirstWritableRawContact(Context context) {
+ // Find the first writable entity.
+ int entityIndex = 0;
+ for (EntityDelta delta : this) {
+ if (delta.getRawContactAccountType(context).areContactsWritable()) return entityIndex;
+ entityIndex++;
+ }
+ return -1;
+ }
+
+ /** Return the first EntityDelta corresponding to a writable raw-contact, or null. */
+ public EntityDelta getFirstWritableRawContact(Context context) {
+ final int index = indexOfFirstWritableRawContact(context);
+ return (index == -1) ? null : get(index);
+ }
+
public ValuesDelta getSuperPrimaryEntry(final String mimeType) {
ValuesDelta primary = null;
ValuesDelta randomEntry = null;
@@ -354,12 +373,14 @@
}
/** {@inheritDoc} */
+ @Override
public int describeContents() {
// Nothing special about this parcel
return 0;
}
/** {@inheritDoc} */
+ @Override
public void writeToParcel(Parcel dest, int flags) {
final int size = this.size();
dest.writeInt(size);
@@ -383,12 +404,14 @@
public static final Parcelable.Creator<EntityDeltaList> CREATOR =
new Parcelable.Creator<EntityDeltaList>() {
+ @Override
public EntityDeltaList createFromParcel(Parcel in) {
final EntityDeltaList state = new EntityDeltaList();
state.readFromParcel(in);
return state;
}
+ @Override
public EntityDeltaList[] newArray(int size) {
return new EntityDeltaList[size];
}
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index 6728913..45565a1 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -224,13 +224,7 @@
mIntent.setDataAndType(mDataUri, mimeType);
}
- // Always launch as new task, since we're like a launcher
- setCommonIntentFlags(mIntent);
- if (mAlternateIntent != null) setCommonIntentFlags(mAlternateIntent);
- }
-
- private static void setCommonIntentFlags(Intent intent) {
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
@Override
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 81aa2e8..7087167 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -31,6 +31,7 @@
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.LoaderManager.LoaderCallbacks;
+import android.app.TaskStackBuilder;
import android.content.ActivityNotFoundException;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -180,8 +181,8 @@
@Override
public void onClick(View v) {
final Intent intent = new Intent(Intent.ACTION_VIEW, mLookupUri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
mContactLoader.cacheResult();
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
startActivity(intent);
hide(false);
}
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index afc88af..38dac60 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -147,9 +147,12 @@
// OnClick launch QuickContact
final Intent intent = new Intent(QuickContact.ACTION_QUICK_CONTACT);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ // When starting from the launcher, start in a new, cleared task.
+ // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
+ // clear the whole thing preemptively here since QuickContactActivity will
+ // finish itself when launching other detail activities.
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setData(contactData.getLookupUri());
intent.putExtra(QuickContact.EXTRA_MODE, QuickContact.MODE_SMALL);
diff --git a/src/com/android/contacts/util/ContactPhotoUtils.java b/src/com/android/contacts/util/ContactPhotoUtils.java
new file mode 100644
index 0000000..f214e9f
--- /dev/null
+++ b/src/com/android/contacts/util/ContactPhotoUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 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.contacts.util;
+
+import android.graphics.Bitmap;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Utilities related to loading/saving contact photos.
+ *
+ */
+public class ContactPhotoUtils {
+ private static final String TAG = "ContactPhotoUtils";
+
+ private static final String PHOTO_DATE_FORMAT = "'IMG'_yyyyMMdd_HHmmss";
+
+ // TODO: /DCIM/Camera isn't the ideal place to stash cropped contact photos.
+ // Where is the right place?
+ private static final File PHOTO_DIR = new File(
+ Environment.getExternalStorageDirectory() + "/DCIM/Camera");
+
+ /**
+ * Generate a new, unique file to be used as an out-of-band communication
+ * channel, since hi-res Bitmaps are too big to serialize into a Bundle.
+ * This file will be passed to other activities (such as the gallery/camera/cropper/etc.),
+ * and read by us once they are finished writing it.
+ */
+ public static File generateTempPhotoFile() {
+ PHOTO_DIR.mkdirs();
+ return new File(PHOTO_DIR, generateTempPhotoFileName());
+ }
+
+ private static String generateTempPhotoFileName() {
+ Date date = new Date(System.currentTimeMillis());
+ SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT);
+ return dateFormat.format(date) + ".jpg";
+ }
+
+ /**
+ * Creates a byte[] containing the PNG-compressed bitmap, or null if
+ * something goes wrong.
+ */
+ public static byte[] compressBitmap(Bitmap bitmap) {
+ final int size = bitmap.getWidth() * bitmap.getHeight() * 4;
+ final ByteArrayOutputStream out = new ByteArrayOutputStream(size);
+ try {
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.flush();
+ out.close();
+ return out.toByteArray();
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to serialize photo: " + e.toString());
+ return null;
+ }
+ }
+}
+
+