summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk7
-rw-r--r--AndroidManifest.xml3
-rw-r--r--AndroidManifest_cm.xml5
-rw-r--r--res/raw/preloaded_contacts.json0
-rw-r--r--res/raw/preloaded_contacts_schema.json59
-rw-r--r--res/values-as-rIN/strings.xml39
-rw-r--r--res/values-ast-rES/strings.xml55
-rw-r--r--res/values-az-rAZ/strings.xml3
-rw-r--r--res/values-be/strings.xml39
-rw-r--r--res/values-br-rFR/strings.xml39
-rw-r--r--res/values-csb-rPL/strings.xml39
-rw-r--r--res/values-cy/strings.xml39
-rw-r--r--res/values-en-rPT/strings.xml39
-rw-r--r--res/values-eo/strings.xml39
-rw-r--r--res/values-es-rMX/strings.xml39
-rw-r--r--res/values-frp-rIT/strings.xml39
-rw-r--r--res/values-fy-rNL/strings.xml39
-rw-r--r--res/values-gd-rGB/strings.xml39
-rw-r--r--res/values-gu-rIN/strings.xml2
-rw-r--r--res/values-ku/strings.xml55
-rw-r--r--res/values-lb/strings.xml55
-rw-r--r--res/values-ml-rIN/strings.xml4
-rw-r--r--res/values-nb/strings.xml2
-rw-r--r--res/values-nl/strings.xml4
-rw-r--r--res/values-oc-rFR/strings.xml39
-rw-r--r--res/values-or-rIN/strings.xml39
-rw-r--r--res/values-pt-rBR/strings.xml35
-rw-r--r--res/values-ro/strings.xml6
-rw-r--r--res/values-sr/strings.xml2
-rw-r--r--res/values-ug/strings.xml49
-rw-r--r--res/values-uz-rUZ/strings.xml2
-rwxr-xr-x[-rw-r--r--]res/values-zh-rCN/strings.xml4
-rw-r--r--res/values/config.xml39
-rw-r--r--res/values/public.xml20
-rwxr-xr-x[-rw-r--r--]res/values/strings.xml3
-rw-r--r--src/com/android/providers/contacts/AccountWithDataSet.java8
-rw-r--r--src/com/android/providers/contacts/CallLogProvider.java59
-rw-r--r--src/com/android/providers/contacts/Constants.java3
-rw-r--r--src/com/android/providers/contacts/ContactsDatabaseHelper.java156
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java656
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java18
-rw-r--r--src/com/android/providers/contacts/HanziToPinyin.java67
-rw-r--r--src/com/android/providers/contacts/LegacyApiSupport.java9
-rw-r--r--src/com/android/providers/contacts/aggregation/ContactAggregator2.java11
-rw-r--r--src/com/android/providers/contacts/util/PreloadedContactsFileParser.java225
-rw-r--r--tests/src/com/android/providers/contacts/CallLogProviderTest.java2
46 files changed, 2026 insertions, 109 deletions
diff --git a/Android.mk b/Android.mk
index 1698686..c6b7b99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -10,6 +10,9 @@ LOCAL_SRC_FILES += \
LOCAL_JAVA_LIBRARIES := ext telephony-common
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest_cm.xml
+
+LOCAL_STATIC_JAVA_AAR_LIBRARIES := ambientsdk
LOCAL_STATIC_JAVA_LIBRARIES += android-common com.android.vcard guava
# The Emma tool analyzes code coverage when running unit tests on the
@@ -26,6 +29,10 @@ LOCAL_EMMA_COVERAGE_FILTER := +com.android.providers.contacts.*
# production builds.
LOCAL_EMMA_COVERAGE_FILTER := +com.android.providers.contacts.*
+LOCAL_AAPT_FLAGS := \
+ --auto-add-overlay \
+ --extra-packages com.cyanogen.ambient
+
LOCAL_PACKAGE_NAME := ContactsProvider
LOCAL_CERTIFICATE := shared
LOCAL_PRIVILEGED_MODULE := true
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4e035f9..dc37607 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,6 +20,9 @@
android:label="Broadcast that a change happened to the call log."
android:protectionLevel="signature|system"/>
+ <uses-sdk android:minSdkVersion="21"
+ android:targetSdkVersion="23" />
+
<application android:process="android.process.acore"
android:label="@string/app_label"
android:icon="@drawable/app_icon"
diff --git a/AndroidManifest_cm.xml b/AndroidManifest_cm.xml
new file mode 100644
index 0000000..e5e5aff
--- /dev/null
+++ b/AndroidManifest_cm.xml
@@ -0,0 +1,5 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.contacts">
+
+ <uses-permission android:name="com.android.contacts.incall.METRICS_PERMISSION" />
+</manifest>
diff --git a/res/raw/preloaded_contacts.json b/res/raw/preloaded_contacts.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res/raw/preloaded_contacts.json
diff --git a/res/raw/preloaded_contacts_schema.json b/res/raw/preloaded_contacts_schema.json
new file mode 100644
index 0000000..384d87f
--- /dev/null
+++ b/res/raw/preloaded_contacts_schema.json
@@ -0,0 +1,59 @@
+/*
+ This file encodes the contact information that must be preloaded into
+ the contacts database. The contact information is encoded according
+ to the schema outlined below.
+
+ The top-level object is an array of 'contacts'. Each of the array elements
+ is a contact with an array called 'data' defined within. Each of the objects
+ within 'data' objects describe an aspect of the contact - name, phone number,
+ address etc. Each aspect becomes a row in the raw_contacts table within
+ the contacts database.
+
+ When describing a property for a contact aspect, the keys and values can
+ reference java fields. These fields will be resolved at runtime. This enables
+ us to leverage the fields defined in android.provider.ContactsContract.* classes
+ to describe the contact information.
+
+ Note that if any java fields are referenced, they must be fully qualified names
+ as seen from a ClassLoader's perspective. There is no validation done to ensure
+ that the fields names can be resolved till runtime.
+
+ Conveniences afforded while declaring contact information :
+ @ = android.provider.ContactsContract$CommonDataKinds
+ @mimetype = android.provider.ContactsContract$Data.MIMETYPE
+
+ Example :
+ {
+ "contacts": [
+ {
+ "data": [
+ {
+ "@mimetype": "{{@$StructuredName.CONTENT_ITEM_TYPE}}",
+ "@$StructuredName.DISPLAY_NAME": "John Doe"
+ },
+
+ {
+ "@mimetype": "{{@$Phone.CONTENT_ITEM_TYPE}}",
+ "@$Phone.NUMBER": "123-456-7890",
+ "@$Phone.TYPE": "{{@$Phone.TYPE_WORK}}"
+ }
+ ]
+ }
+ ]
+ }
+
+ Property values can be static values or expressions that need to be evaluated, like
+ the property keys. Values are interpolated when enclosed within
+ double-curly-braces - '{{' - akin to a templating system like Handlebars.
+
+ Limitations :
+ - currently, for property values, there is no provision to embed an
+ expression-to-be-evaluated, within a larger string
+ ex. "{{com.example.foo.BAR}} additional content" isn't valid
+
+ - lack of compile time validation of the java fields specified or of the syntax
+
+ - lack of compile time validation of the property keys' with-respect-to the
+ columns of the raw_contacts table
+
+*/ \ No newline at end of file
diff --git a/res/values-as-rIN/strings.xml b/res/values-as-rIN/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-as-rIN/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-ast-rES/strings.xml b/res/values-ast-rES/strings.xml
new file mode 100644
index 0000000..2f8bdef
--- /dev/null
+++ b/res/values-ast-rES/strings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <string name="sharedUserLabel">Aplicaciones básiques d\'Android</string>
+ <!-- This is the label for the application that stores contacts data -->
+ <string name="app_label">Información de los contautos</string>
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <string name="provider_label">Contautos</string>
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_ticker">L\'anovamientu de contautos necesita más memoria.</string>
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_title">Anovando almacenamientu pa contautos</string>
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_text">Tocar pa completar l\'anovamientu.</string>
+ <!-- The name of the default contact directory -->
+ <string name="default_directory">Contautos</string>
+ <!-- The name of the invisible local contact directory -->
+ <string name="local_invisible_directory">Otru</string>
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <string name="voicemail_from_column">"Mensaxe de voz de "</string>
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_title">Copiar base de datos de contautos</string>
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_database_message">Tas a piques de 1) facer una copia de la to base de datos, qu\'inclúi la información rellacionada colos contautos y el rexistru de llamaes nel almacenamientu internu y a 2) unviala per corréu-e. Nun escaezas desaniciar la copia en cuantes la tengas copiada n\'otru preséu o tengas recibío\'l corréu-e.</string>
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_delete_button">Desaniciar agora</string>
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_start_button">Aniciar</string>
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_sender_picker">Esbilla un programa pa unviar el ficheru</string>
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_subject">Base datos de contautos axuntada</string>
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_body">La mio base de datos de contautos con tola información de contautos, va axunta a esti mensaxe. Usa esta información con curiáu.</string>
+</resources>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 23c0885..d11f636 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -19,11 +19,14 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"Kontakt Yaddaşı"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontaktlar"</string>
+ <string name="upgrade_msg" msgid="8640807392794309950">"Kontakt data bazası təkmilləşdirilir."</string>
<string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kontakt təkmilləşdirməsi əlavə yaddaş tələb edir."</string>
<string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Kontakt üçün yaddaş təkmilləşdirilir."</string>
<string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Təkmilləşdirməni tamamlamaq üçün toxunun."</string>
<string name="default_directory" msgid="93961630309570294">"Kontaktlar"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Digər"</string>
+ <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Bütün səsli məktublara giriş"</string>
+ <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Tətbiqə bu cihazın giriş əldə edə biləcəyi bütün səsli məktubları saxlamağa və əldə etməyə imkan verir."</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Səsli mesaj göndərən: "</string>
<string name="debug_dump_title" msgid="4916885724165570279">"Kontakt data bazasını kopyalayın"</string>
<string name="debug_dump_database_message" msgid="406438635002392290">"Siz 1) informasiyaya və daxili yaddaş ehtiyatındakı zəng jurnalına bağlı data bazanızın nüsxəsini hazırlamaq 2) və onu e-poçt ilə göndərmək üzrəsiniz. Onu cihazdan kənarda və ya alınmış e-məktubda uğurla kopyalayandan sonra nüsxəsini silməyi unutmayın."</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-be/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-br-rFR/strings.xml b/res/values-br-rFR/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-br-rFR/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-csb-rPL/strings.xml b/res/values-csb-rPL/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-csb-rPL/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-cy/strings.xml b/res/values-cy/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-cy/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-en-rPT/strings.xml b/res/values-en-rPT/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-en-rPT/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-eo/strings.xml b/res/values-eo/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-eo/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-es-rMX/strings.xml b/res/values-es-rMX/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-es-rMX/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-frp-rIT/strings.xml b/res/values-frp-rIT/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-frp-rIT/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-fy-rNL/strings.xml b/res/values-fy-rNL/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-fy-rNL/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-gd-rGB/strings.xml b/res/values-gd-rGB/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-gd-rGB/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 16cd70b..7f58e0c 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sharedUserLabel" msgid="8024311725474286801">"Android Core એપ્લિકેશનો"</string>
+ <string name="sharedUserLabel" msgid="8024311725474286801">"Android Core એપ્લિકેશન્સ"</string>
<string name="app_label" msgid="3389954322874982620">"સંપર્કો સ્ટોરેજ"</string>
<string name="provider_label" msgid="6012150850819899907">"સંપર્કો"</string>
<string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"સંપર્કો અપગ્રેડને વધુ મેમરીની જરૂર છે."</string>
diff --git a/res/values-ku/strings.xml b/res/values-ku/strings.xml
new file mode 100644
index 0000000..d2cb640
--- /dev/null
+++ b/res/values-ku/strings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <string name="sharedUserLabel">نەرمەواڵاکانی ناوەکی ئەندرۆید</string>
+ <!-- This is the label for the application that stores contacts data -->
+ <string name="app_label">بیرگەی ناوەکان</string>
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <string name="provider_label">ناوەکان</string>
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_ticker">نوێکاری ناوەکان پێویستی بە بیرگەی زۆرترە.</string>
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_title">تازەکردنەوەی بیرگە بۆ ناوەکان</string>
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_text">دەست لێدە بۆ تەواوکردنی نوێکارییەکە.</string>
+ <!-- The name of the default contact directory -->
+ <string name="default_directory">ناوەکان</string>
+ <!-- The name of the invisible local contact directory -->
+ <string name="local_invisible_directory">دیکە</string>
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <string name="voicemail_from_column">"دەنگنامە لەلایەن "</string>
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_title">کۆپی کردنی بنکەدراوەی ناوەکان</string>
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_database_message">تۆ بە تەمای 1) کۆپی گرتنەوە لە بنکەدراوەکەتی کە هەموو ناوەکان بە تۆماری پەیوەندی و زانیارییەکانییەوە لەخۆ ئەگرێت، بۆ ناو بیرگەی ناوەکی , هەروەها 2) ئیمێڵ کردنیشی. لەیادت بێ کۆپییەکە بسڕیتەوە هەر کە بە سەرکەوتووانە کۆپیت کردەوە بۆ ئامێرەکەت یان ئیمێڵەکە وەرگیردرا.</string>
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_delete_button">سڕینەوە لە ئێستادا</string>
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_start_button">دەستپێکردن</string>
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_sender_picker">بەرنامەیەک هەڵبژێرە بۆ ناردنی پەڕگەکەت</string>
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_subject">بنکەدراوەی ناوەکان پێوەلکێندرا</string>
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_body">پێوەلکێندراوەکە بنکەدراوەی ناوەکانمە و لەگەڵ هەموو زانیاری ناوەکانم. بە وریایی بیهێڵەرەوە.</string>
+</resources>
diff --git a/res/values-lb/strings.xml b/res/values-lb/strings.xml
new file mode 100644
index 0000000..9017bb4
--- /dev/null
+++ b/res/values-lb/strings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <string name="sharedUserLabel">Kär-Appe vun Android</string>
+ <!-- This is the label for the application that stores contacts data -->
+ <string name="app_label">Späicher fir Kontakter</string>
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <string name="provider_label">Kontakter</string>
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_ticker">D\'Aktualiséierung vun de Kontakter brauch méi Späicher.</string>
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_title">Späicher fir Kontakter gëtt aktualiséiert</string>
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_text">Drécke fir d\'Aktualiséierung ofzeschléissen.</string>
+ <!-- The name of the default contact directory -->
+ <string name="default_directory">Kontakter</string>
+ <!-- The name of the invisible local contact directory -->
+ <string name="local_invisible_directory">Aner</string>
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <string name="voicemail_from_column">"Sproochmessage vum "</string>
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_title">Kontaktdatebank kopéieren</string>
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_database_message">Du bass am Gaang 1) eng Kopie vun denger Datebank an den interne Späicher ze maachen an där all d\'Informatioune vun de Kontakter an den Historique vun allen Telefonsgespréicher stinn, an 2) se ze mailen. Denk drun d\'Kopie ze läsche soubal s du s\'erfollegräich vum Apparat kopéiert hues oder d\'E-Mail ukomm ass.</string>
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_delete_button">Elo läschen</string>
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_start_button">Start</string>
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_sender_picker">Wiel e Programm aus fir däi Fichier ze schécken</string>
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_subject">Kontakt-DB ugehaangen</string>
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_body">Ugehaangen ass meng Kontaktdatebank mat all menge Kontaktinformatiounen. Mat Virsiicht behandelen.</string>
+</resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index a3aa6d1..5493e4b 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -17,10 +17,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="sharedUserLabel" msgid="8024311725474286801">"Android കോർ അപ്ലിക്കേഷനുകൾ"</string>
- <string name="app_label" msgid="3389954322874982620">"കോൺടാക്റ്റുകളുടെ സ്റ്റോറേജ്"</string>
+ <string name="app_label" msgid="3389954322874982620">"കോൺടാക്റ്റുകളുടെ സംഭരണം"</string>
<string name="provider_label" msgid="6012150850819899907">"വിലാസങ്ങൾ"</string>
<string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"കോൺ‌ടാക്റ്റുകൾ അപ്‌ഗ്രേഡുചെയ്യാൻ കൂടുതൽ മെമ്മറി ആവശ്യമാണ്."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"കോൺ‌ടാക്റ്റുകൾക്കായുള്ള സ്റ്റോറേജ് അപ്‌ഗ്രേഡുചെയ്യുന്നു"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"കോൺ‌ടാക്റ്റുകൾക്കായുള്ള സംഭരണം അപ്‌ഗ്രേഡുചെയ്യുന്നു"</string>
<string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"അപ്‌ഗ്രേഡ് പൂർത്തിയാക്കാൻ സ്‌പർശിക്കുക."</string>
<string name="default_directory" msgid="93961630309570294">"വിലാസങ്ങൾ"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"മറ്റുള്ളവ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 1cf97fd..6669a21 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -23,7 +23,7 @@
<string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Oppgraderer lagring for kontakter"</string>
<string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Trykk for å fullføre oppgraderingen."</string>
<string name="default_directory" msgid="93961630309570294">"Kontakter"</string>
- <string name="local_invisible_directory" msgid="705244318477396120">"Annet"</string>
+ <string name="local_invisible_directory" msgid="705244318477396120">"Andre"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Talemelding fra "</string>
<string name="debug_dump_title" msgid="4916885724165570279">"Kopiér kontaktdatabasen"</string>
<string name="debug_dump_database_message" msgid="406438635002392290">"Du er i ferd med å 1) lage en kopi av databasen som omfatter all kontaktrelatert informasjon og alle anropslogger til den interne lagringsplassen, og 2) sende kopien med e-post. Husk å slette kopien så snart du har kopiert den fra enheten eller når e-posten er mottatt."</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 0cfeff5..73ea1a9 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -26,10 +26,10 @@
<string name="local_invisible_directory" msgid="705244318477396120">"Overig"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Voicemail van "</string>
<string name="debug_dump_title" msgid="4916885724165570279">"Contactendatabase kopiëren"</string>
- <string name="debug_dump_database_message" msgid="406438635002392290">"Je staat op het punt 1) een kopie van je database met alle contactgegevens en oproeplogboeken te maken in de interne opslag, en 2) deze te e-mailen. Verwijder de kopie zodra je deze van het apparaat hebt gekopieerd of de e-mail is ontvangen."</string>
+ <string name="debug_dump_database_message" msgid="406438635002392290">"U staat op het punt 1) een kopie van uw database met alle contactgegevens en oproeplogboeken te maken in de interne opslag, en 2) deze te e-mailen. Verwijder de kopie zodra u deze van het apparaat heeft gekopieerd of de e-mail is ontvangen."</string>
<string name="debug_dump_delete_button" msgid="7832879421132026435">"Nu verwijderen"</string>
<string name="debug_dump_start_button" msgid="2837506913757600001">"Starten"</string>
- <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Selecteer een programma om je bestand te verzenden"</string>
+ <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Selecteer een programma om uw bestand te verzenden"</string>
<string name="debug_dump_email_subject" msgid="108188398416385976">"Contactendatabase bijgevoegd"</string>
<string name="debug_dump_email_body" msgid="4577749800871444318">"Bijgevoegd is mijn contactendatabase met al mijn contactgegevens. Ga hier zorgvuldig mee om."</string>
</resources>
diff --git a/res/values-oc-rFR/strings.xml b/res/values-oc-rFR/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-oc-rFR/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-or-rIN/strings.xml b/res/values-or-rIN/strings.xml
new file mode 100644
index 0000000..03e136d
--- /dev/null
+++ b/res/values-or-rIN/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <!-- This is the label for the application that stores contacts data -->
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+</resources>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 6906957..0000000
--- a/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sharedUserLabel" msgid="8024311725474286801">"Principais apps do Android"</string>
- <string name="app_label" msgid="3389954322874982620">"Armazenamento de contatos"</string>
- <string name="provider_label" msgid="6012150850819899907">"Contatos"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"A atualização de contatos precisa de mais memória."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Atualização do armazenamento para contatos"</string>
- <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Toque para concluir a atualização."</string>
- <string name="default_directory" msgid="93961630309570294">"Contatos"</string>
- <string name="local_invisible_directory" msgid="705244318477396120">"Outros"</string>
- <string name="voicemail_from_column" msgid="435732568832121444">"Correio de voz de "</string>
- <string name="debug_dump_title" msgid="4916885724165570279">"Copiar banco de dados de contatos"</string>
- <string name="debug_dump_database_message" msgid="406438635002392290">"Você está prestes a 1) fazer uma cópia de seu banco de dados no armazenamento interno, com todas as informações relacionadas aos contatos e todo o histórico de chamadas e 2) enviar essa cópia por e-mail. Lembre-se de excluir a cópia, logo que você a tiver copiado do dispositivo ou que o e-mail for recebido."</string>
- <string name="debug_dump_delete_button" msgid="7832879421132026435">"Excluir agora"</string>
- <string name="debug_dump_start_button" msgid="2837506913757600001">"Iniciar"</string>
- <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Escolha um programa para enviar o arquivo"</string>
- <string name="debug_dump_email_subject" msgid="108188398416385976">"BD de contatos anexado"</string>
- <string name="debug_dump_email_body" msgid="4577749800871444318">"Meu banco de dados de contatos está anexado, Lá estão todas as informações de meus contatos. Use-o com cuidado."</string>
-</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index f6be18a..e01744e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -20,16 +20,16 @@
<string name="app_label" msgid="3389954322874982620">"Stocarea datelor din Agendă"</string>
<string name="provider_label" msgid="6012150850819899907">"Agendă"</string>
<string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Actualizarea agendei necesită mai multă memorie."</string>
- <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Măriți spaţiul de stocare pentru Agendă"</string>
+ <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Măriţi spaţiul de stocare pentru Agendă"</string>
<string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Atingeţi pentru a finaliza actualizarea."</string>
<string name="default_directory" msgid="93961630309570294">"Agendă"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"Altul"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Mesaj vocal de la "</string>
<string name="debug_dump_title" msgid="4916885724165570279">"Copiaţi baza de date a agendei"</string>
- <string name="debug_dump_database_message" msgid="406438635002392290">"Sunteți pe cale 1) să faceţi o copie, pe stocarea internă, a bazei dvs. de date care include toate informaţiile referitoare la agendă și întregul jurnal de apeluri și 2) să trimiteți această copie prin e-mail. Nu uitați să ştergeţi această copie după ce aţi copiat-o de pe dispozitiv sau după ce a fost primit e-mailul."</string>
+ <string name="debug_dump_database_message" msgid="406438635002392290">"Sunteţi pe cale 1) să faceţi o copie, pe stocarea internă, a bazei dvs. de date care include toate informaţiile referitoare la agendă și întregul jurnal de apeluri și 2) să trimiteţi această copie prin e-mail. Nu uitaţi să ştergeţi această copie după ce aţi copiat-o de pe dispozitiv sau după ce a fost primit e-mailul."</string>
<string name="debug_dump_delete_button" msgid="7832879421132026435">"Ștergeţi acum"</string>
<string name="debug_dump_start_button" msgid="2837506913757600001">"Porniţi"</string>
<string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Alegeţi un program pentru a trimite fişierul"</string>
<string name="debug_dump_email_subject" msgid="108188398416385976">"Atașată baza de date a agendei"</string>
- <string name="debug_dump_email_body" msgid="4577749800871444318">"Vă trimit atașată baza de date cu toate informaţiile din agenda mea. Vă rog să o gestionaţi cu atenţie."</string>
+ <string name="debug_dump_email_body" msgid="4577749800871444318">"Vă trimit ataşată baza de date cu toate informaţiile din agenda mea. Vă rog să o gestionaţi cu atenţie."</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 97b65d5..89d5140 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -23,7 +23,7 @@
<string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Надограђивање меморије за контакте"</string>
<string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Додирните да бисте довршили надоградњу."</string>
<string name="default_directory" msgid="93961630309570294">"Контакти"</string>
- <string name="local_invisible_directory" msgid="705244318477396120">"Другo"</string>
+ <string name="local_invisible_directory" msgid="705244318477396120">"Други"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"Говорна пошта од "</string>
<string name="debug_dump_title" msgid="4916885724165570279">"Копирање базе података са контактима"</string>
<string name="debug_dump_database_message" msgid="406438635002392290">"Управо ћете 1) направити копију базе података која садржи све информације у вези са контактима и целокупну евиденцију позива у интерној меморији и 2) послати је имејлом. Не заборавите да избришете копију чим је будете копирали са уређаја или чим будете примили имејл."</string>
diff --git a/res/values-ug/strings.xml b/res/values-ug/strings.xml
new file mode 100644
index 0000000..168b641
--- /dev/null
+++ b/res/values-ug/strings.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for this app's shared user ID (and thus for all other
+ apps sharing the uid). -->
+ <string name="sharedUserLabel">ئاندىرويىد يادرولۇق ئەپلەر</string>
+ <!-- This is the label for the application that stores contacts data -->
+ <string name="app_label">ئالاقەداش ساقلىغۇچ</string>
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <!-- Ticker for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_ticker">ئالاقەداشلار ساندانىنى يۈكسەلدۈرۈشكە تېخىمۇ كۆپ ئەسلەك كېرەك.</string>
+ <!-- Title for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_title">ئالاقەداشلارنى ساقلىغۇچتىن يۈكسەلدۈرۈش</string>
+ <!-- Text for the notification shown when updating contacts fails because of memory shortage -->
+ <string name="upgrade_out_of_memory_notification_text">چېكىلسە يۈكسەلدۈرۈشنى تاماملايدۇ.</string>
+ <!-- The name of the default contact directory -->
+ <!-- The name of the invisible local contact directory -->
+ <!-- The prefix string before the number used for the display name for the voicemail table.
+ Because of the way this is combined in SQL we can't allow a generic format string.
+ The resulting string will be this string with the number appended to the end.
+ Note that the trailing space is important, and that to achieve it we have to wrap the
+ string in double quotes. -->
+ <string name="voicemail_from_column">"ئۈنخەت ئورنى "</string>
+ <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_title">ئالاقەداشلار ساندانىنى كۆچۈر</string>
+ <!-- Debug tool - message shown to the user on the dialog which sends a copy of the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_database_message">سىز 1)ھەممە ئالاقەداشلىرىڭىزغا مۇناسىۋەتلىك ئۇچۇرلار ۋە چاقىرىش خاتىرىسىنى ئۆز ئىچىگە ئالغان ساندانىڭىزنىڭ كۆچۈرۈلمە نۇسخىسىنى تاييارلاپ، ئىچىدىكى ساقلىغۇچقا ساقلايدۇ، 2)تورخەت يوللىيالايدۇ. ئۈسكۈنىڭىزدىن كۆچۈرۈلمە نۇسخىنى كۆچۈرۈۋالغان ياكى تورخەتنى تاپشۇرۇۋالغاندىن كېيىن كۆچۈرۈلمە نۇسخىسىنى ئامالنىڭ بارىچە تېزراق ئۆچۈرۈۋېتىڭ.</string>
+ <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_subject">ئالاقەداشلار ساندانى قوشۇلدى</string>
+ <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+ <string name="debug_dump_email_body">قوشۇلغىنى مېنىڭ ھەممە ئالاقەداشلار ئۇچۇرۇمنى ئۆز ئىچىگە ئالغان ئالاقەداشلار ساندانىم. ئېھتىيات بىلەن ساقلاڭ.</string>
+</resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 9cdeea0..1bd680a 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="sharedUserLabel" msgid="8024311725474286801">"Android’ga asoslangan ilovalar"</string>
- <string name="app_label" msgid="3389954322874982620">"Kontaktlar ombori"</string>
+ <string name="app_label" msgid="3389954322874982620">"Kontaktlar xotirasi"</string>
<string name="provider_label" msgid="6012150850819899907">"Kontaktlar"</string>
<string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kontaktlarni yangilash uchun ko‘proq xotira kerak."</string>
<string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Kontaktlar uchun xotirani yangilash"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index ad4eda6..c3754e8 100644..100755
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -32,4 +32,8 @@
<string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"选择用于发送文件的程序"</string>
<string name="debug_dump_email_subject" msgid="108188398416385976">"随附通讯录数据库"</string>
<string name="debug_dump_email_body" msgid="4577749800871444318">"附件是我的通讯录数据库,其中包含我所有的联系人信息,因此请谨慎处理。"</string>
+
+ <string name="group_title_co_workers">"同事"</string>
+ <string name="group_title_family">"家人"</string>
+ <string name="group_title_friends">"朋友"</string>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..4c97cb8
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2015, The Linux Foundation. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-->
+
+<resources>
+
+ <!-- If true, it supports fuzzy search for contacts number -->
+ <bool name="phone_number_fuzzy_search">false</bool>
+
+ <!-- request to attempt preload-ing contacts -->
+ <bool name="config_preload_contacts">false</bool>
+
+</resources>
diff --git a/res/values/public.xml b/res/values/public.xml
new file mode 100644
index 0000000..7bed650
--- /dev/null
+++ b/res/values/public.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2016 The CyanogenMod 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.
+-->
+<resources>
+ <public type="string" name="group_title_co_workers" id="0x7f050010" />
+ <public type="string" name="group_title_family" id="0x7f050011" />
+ <public type="string" name="group_title_friends" id="0x7f050012" />
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8be7bca..5c0ef96 100644..100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -69,4 +69,7 @@
<!-- Debug tool - email body [CHAR LIMIT=NONE] -->
<string name="debug_dump_email_body">Attached is my contacts database with all my contacts information. Handle with care.</string>
+ <string name="group_title_co_workers">"Coworkers"</string>
+ <string name="group_title_family">"Family"</string>
+ <string name="group_title_friends">"Friends"</string>
</resources>
diff --git a/src/com/android/providers/contacts/AccountWithDataSet.java b/src/com/android/providers/contacts/AccountWithDataSet.java
index bfae112..a654f50 100644
--- a/src/com/android/providers/contacts/AccountWithDataSet.java
+++ b/src/com/android/providers/contacts/AccountWithDataSet.java
@@ -25,7 +25,11 @@ import com.google.common.base.Objects;
* Account information that includes the data set, if any.
*/
public class AccountWithDataSet {
- public static final AccountWithDataSet LOCAL = new AccountWithDataSet(null, null, null);
+ public static final String PHONE_NAME = "PHONE";
+ public static final String ACCOUNT_TYPE_PHONE = "com.android.localphone";
+ public static final String ACCOUNT_TYPE_SIM = "com.android.sim";
+ public static final AccountWithDataSet LOCAL = new AccountWithDataSet(
+ PHONE_NAME, ACCOUNT_TYPE_PHONE, null);
private final String mAccountName;
private final String mAccountType;
@@ -62,7 +66,7 @@ public class AccountWithDataSet {
}
public boolean isLocalAccount() {
- return (mAccountName == null) && (mAccountType == null);
+ return (PHONE_NAME.equals(mAccountName)) && (ACCOUNT_TYPE_PHONE.equals(mAccountType));
}
@Override
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index f83e4a7..a82eee8 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -49,8 +49,11 @@ import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import com.android.providers.contacts.util.SelectionBuilder;
import com.android.providers.contacts.util.UserUtils;
+
import com.google.common.annotations.VisibleForTesting;
+import com.cyanogen.ambient.incall.CallLogConstants;
+
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -83,7 +86,10 @@ public class CallLogProvider extends ContentProvider {
Calls.DURATION,
Calls.DATA_USAGE,
Calls.PHONE_ACCOUNT_COMPONENT_NAME,
- Calls.PHONE_ACCOUNT_ID
+ Calls.PHONE_ACCOUNT_ID,
+ ContactsDatabaseHelper.CallColumns.ORIGIN,
+ CallLogConstants.PLUGIN_PACKAGE_NAME,
+ CallLogConstants.PLUGIN_USER_HANDLE
};
static final String[] MINIMAL_PROJECTION = new String[] { Calls._ID };
@@ -94,6 +100,13 @@ public class CallLogProvider extends ContentProvider {
private static final int CALLS_FILTER = 3;
+ private static final int CALLS_PLUGIN = 4;
+
+ /** Path Segments */
+ private static final int ALL_CALL_LOGS_PATH = 2;
+ private static final int PLUGIN_PACKAGE_NAME_PATH = 2;
+ private static final int PACKAGE_USER_NAME_PATH = 3;
+
private static final String UNHIDE_BY_PHONE_ACCOUNT_QUERY =
"UPDATE " + Tables.CALLS + " SET " + Calls.PHONE_ACCOUNT_HIDDEN + "=0 WHERE " +
Calls.PHONE_ACCOUNT_COMPONENT_NAME + "=? AND " + Calls.PHONE_ACCOUNT_ID + "=?;";
@@ -106,7 +119,11 @@ public class CallLogProvider extends ContentProvider {
static {
sURIMatcher.addURI(CallLog.AUTHORITY, "calls", CALLS);
sURIMatcher.addURI(CallLog.AUTHORITY, "calls/#", CALLS_ID);
+ sURIMatcher.addURI(CallLog.AUTHORITY, "calls/all", CALLS);
sURIMatcher.addURI(CallLog.AUTHORITY, "calls/filter/*", CALLS_FILTER);
+ sURIMatcher.addURI(CallLog.AUTHORITY, "calls/plugin", CALLS_PLUGIN);
+ sURIMatcher.addURI(CallLog.AUTHORITY, "calls/plugin/*", CALLS_PLUGIN);
+ sURIMatcher.addURI(CallLog.AUTHORITY, "calls/plugin/*/*", CALLS_PLUGIN);
}
private static final HashMap<String, String> sCallsProjectionMap;
@@ -140,6 +157,12 @@ public class CallLogProvider extends ContentProvider {
sCallsProjectionMap.put(Calls.CACHED_PHOTO_ID, Calls.CACHED_PHOTO_ID);
sCallsProjectionMap.put(Calls.CACHED_PHOTO_URI, Calls.CACHED_PHOTO_URI);
sCallsProjectionMap.put(Calls.CACHED_FORMATTED_NUMBER, Calls.CACHED_FORMATTED_NUMBER);
+ sCallsProjectionMap.put(ContactsDatabaseHelper.CallColumns.ORIGIN,
+ ContactsDatabaseHelper.CallColumns.ORIGIN);
+ sCallsProjectionMap.put(CallLogConstants.PLUGIN_PACKAGE_NAME,
+ CallLogConstants.PLUGIN_PACKAGE_NAME);
+ sCallsProjectionMap.put(CallLogConstants.PLUGIN_USER_HANDLE,
+ CallLogConstants.PLUGIN_USER_HANDLE);
}
private HandlerThread mBackgroundThread;
@@ -210,9 +233,15 @@ public class CallLogProvider extends ContentProvider {
selectionBuilder.addClause(EXCLUDE_HIDDEN_SELECTION);
final int match = sURIMatcher.match(uri);
+ List<String> pathSegments = uri.getPathSegments();
+
switch (match) {
- case CALLS:
+ case CALLS: {
+ if (pathSegments.size() < ALL_CALL_LOGS_PATH) {
+ qb.appendWhere(CallLogConstants.PLUGIN_PACKAGE_NAME + " IS NULL ");
+ }
break;
+ }
case CALLS_ID: {
selectionBuilder.addClause(getEqualityClause(Calls._ID,
@@ -221,7 +250,6 @@ public class CallLogProvider extends ContentProvider {
}
case CALLS_FILTER: {
- List<String> pathSegments = uri.getPathSegments();
String phoneNumber = pathSegments.size() >= 2 ? pathSegments.get(2) : null;
if (!TextUtils.isEmpty(phoneNumber)) {
qb.appendWhere("PHONE_NUMBERS_EQUAL(number, ");
@@ -234,6 +262,25 @@ public class CallLogProvider extends ContentProvider {
break;
}
+ case CALLS_PLUGIN: {
+ String pluginName = pathSegments.size() >= PLUGIN_PACKAGE_NAME_PATH ?
+ pathSegments.get(PLUGIN_PACKAGE_NAME_PATH) : null;
+ if (!TextUtils.isEmpty(pluginName)) {
+ qb.appendWhere(CallLogConstants.PLUGIN_PACKAGE_NAME + " == ");
+ qb.appendWhereEscapeString(pluginName);
+ if (pathSegments.size() - 1 >= PACKAGE_USER_NAME_PATH) {
+ String pluginUserName = pathSegments.get(PACKAGE_USER_NAME_PATH);
+ if (!TextUtils.isEmpty(pluginUserName)) {
+ qb.appendWhere(" AND " + CallLogConstants.PLUGIN_USER_HANDLE + " == ");
+ qb.appendWhereEscapeString(pluginUserName);
+ }
+ }
+ } else {
+ qb.appendWhere(CallLogConstants.PLUGIN_PACKAGE_NAME + " IS NOT NULL");
+ }
+ break;
+ }
+
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
@@ -289,6 +336,8 @@ public class CallLogProvider extends ContentProvider {
return Calls.CONTENT_ITEM_TYPE;
case CALLS_FILTER:
return Calls.CONTENT_TYPE;
+ case CALLS_PLUGIN:
+ return Calls.CONTENT_TYPE;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
@@ -364,6 +413,10 @@ public class CallLogProvider extends ContentProvider {
case CALLS:
return getDatabaseModifier(db).delete(Tables.CALLS,
selectionBuilder.build(), selectionArgs);
+ case CALLS_ID:
+ return getDatabaseModifier(db).delete(Tables.CALLS,
+ new SelectionBuilder(Calls._ID + "=?").build(),
+ new String[] { uri.getLastPathSegment() });
default:
throw new UnsupportedOperationException("Cannot delete that URL: " + uri);
}
diff --git a/src/com/android/providers/contacts/Constants.java b/src/com/android/providers/contacts/Constants.java
index 8cf28e6..f828d74 100644
--- a/src/com/android/providers/contacts/Constants.java
+++ b/src/com/android/providers/contacts/Constants.java
@@ -21,4 +21,7 @@ public class Constants {
// Log tag for performance measurement.
// To enable: adb shell setprop log.tag.ContactsPerf VERBOSE
public static final String PERFORMANCE_TAG = "ContactsPerf";
+
+ // log info while preloading `default` contacts
+ public static final String TAG_DEBUG_PRELOAD_CONTACTS = "PreloadContacts";
}
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index aac37ba..143f6b6 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -92,6 +92,9 @@ import com.google.common.annotations.VisibleForTesting;
import libcore.icu.ICU;
+import com.cyanogen.ambient.incall.CallLogConstants;
+
+import java.util.HashMap;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -121,7 +124,7 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
* 1000-1099 M
* </pre>
*/
- static final int DATABASE_VERSION = 1011;
+ static final int DATABASE_VERSION = 1013;
public interface Tables {
public static final String CONTACTS = "contacts";
@@ -285,11 +288,11 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
*/
public static final String GROUP_MEMBER_COUNT =
" LEFT OUTER JOIN (SELECT "
- + "data.data1 AS member_count_group_id, "
- + "COUNT(data.raw_contact_id) AS group_member_count "
- + "FROM data "
+ + "view_data.data1 AS member_count_group_id, "
+ + "COUNT(DISTINCT view_data.contact_id) AS group_member_count "
+ + "FROM view_data "
+ "WHERE "
- + "data.mimetype_id = (SELECT _id FROM mimetypes WHERE "
+ + "view_data.mimetype_id = (SELECT _id FROM mimetypes WHERE "
+ "mimetypes.mimetype = '" + GroupMembership.CONTENT_ITEM_TYPE + "')"
+ "GROUP BY member_count_group_id) AS member_count_table" // End of inner query
+ " ON (groups._id = member_count_table.member_count_group_id)";
@@ -335,9 +338,8 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
"(SELECT " + AccountsColumns._ID +
" FROM " + Tables.ACCOUNTS +
" WHERE " +
- AccountsColumns.ACCOUNT_NAME + " IS NULL AND " +
- AccountsColumns.ACCOUNT_TYPE + " IS NULL AND " +
- AccountsColumns.DATA_SET + " IS NULL)";
+ AccountsColumns.ACCOUNT_TYPE + "='" +
+ AccountWithDataSet.ACCOUNT_TYPE_PHONE + "')";
final String RAW_CONTACT_IS_LOCAL = RawContactsColumns.CONCRETE_ACCOUNT_ID
+ "=" + LOCAL_ACCOUNT_ID;
@@ -351,8 +353,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
"SELECT " +
"MAX((SELECT (CASE WHEN " +
"(CASE" +
- " WHEN " + RAW_CONTACT_IS_LOCAL +
- " THEN 1 " +
" WHEN " + ZERO_GROUP_MEMBERSHIPS +
" THEN " + Settings.UNGROUPED_VISIBLE +
" ELSE MAX(" + Groups.GROUP_VISIBLE + ")" +
@@ -588,6 +588,10 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
String CONTACT_ID = "presence_contact_id";
}
+ public interface CallColumns {
+ String ORIGIN = "origin";
+ }
+
public interface AggregatedPresenceColumns {
String CONTACT_ID = "presence_contact_id";
String CONCRETE_CONTACT_ID = Tables.AGGREGATED_PRESENCE + "." + CONTACT_ID;
@@ -1530,6 +1534,9 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
Calls.CACHED_PHOTO_ID + " INTEGER NOT NULL DEFAULT 0," +
Calls.CACHED_PHOTO_URI + " TEXT," +
Calls.CACHED_FORMATTED_NUMBER + " TEXT," +
+ CallColumns.ORIGIN + " TEXT," +
+ CallLogConstants.PLUGIN_PACKAGE_NAME + " TEXT DEFAULT NULL," +
+ CallLogConstants.PLUGIN_USER_HANDLE + " TEXT DEFAULT NULL," +
Voicemails._DATA + " TEXT," +
Voicemails.HAS_CONTENT + " INTEGER," +
Voicemails.MIME_TYPE + " TEXT," +
@@ -1600,6 +1607,9 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
// Add the legacy API support views, etc.
LegacyApiSupport.createDatabase(db);
+ createPhoneAccount(db);
+ createDefaultGroups4PhoneAccount(db);
+
if (mDatabaseOptimizationEnabled) {
// This will create a sqlite_stat1 table that is used for query optimization
db.execSQL("ANALYZE;");
@@ -2022,13 +2032,18 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
String contactsSelect = "SELECT "
+ ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID + ","
+ contactsColumns + ", "
+ + AccountsColumns.ACCOUNT_NAME + ", "
+ + AccountsColumns.ACCOUNT_TYPE + ", "
+ buildDisplayPhotoUriAlias(ContactsColumns.CONCRETE_ID, Contacts.PHOTO_URI) + ", "
+ buildThumbnailPhotoUriAlias(ContactsColumns.CONCRETE_ID,
Contacts.PHOTO_THUMBNAIL_URI) + ", "
+ dbForProfile() + " AS " + Contacts.IS_USER_PROFILE
+ " FROM " + Tables.CONTACTS
+ " JOIN " + Tables.RAW_CONTACTS + " AS name_raw_contact ON("
- + Contacts.NAME_RAW_CONTACT_ID + "=name_raw_contact." + RawContacts._ID + ")";
+ + Contacts.NAME_RAW_CONTACT_ID + "=name_raw_contact." + RawContacts._ID + ")"
+ + " JOIN " + Tables.ACCOUNTS + " AS name_accounts ON("
+ + "name_raw_contact." + RawContactsColumns.ACCOUNT_ID + "=name_accounts."
+ + AccountsColumns._ID + ")";
db.execSQL("CREATE VIEW " + Views.CONTACTS + " AS " + contactsSelect);
@@ -2910,6 +2925,16 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
oldVersion = 1011;
}
+ if (oldVersion < 1012) {
+ upgradeToVersion1012(db);
+ oldVersion = 1012;
+ }
+
+ if (oldVersion < 1013) {
+ upgradeToVersion1013(db);
+ oldVersion = 1013;
+ }
+
if (upgradeViewsAndTriggers) {
createContactsViews(db);
createGroupsView(db);
@@ -3476,6 +3501,7 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
insertNameLookup(db);
rebuildSortKeys(db);
createContactsIndexes(db, rebuildSqliteStats);
+ rebuildDefaultGroupTitles(db, locales.getPrimaryLocale());
FastScrollingIndexCache.getInstance(mContext).invalidate();
// Update the ICU version used to generate the locale derived data
@@ -3485,6 +3511,35 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
}
/**
+ * change the default groups' title according to the locale
+ */
+ private void rebuildDefaultGroupTitles(SQLiteDatabase db, Locale locale) {
+ String[] PROJECTION = new String[] {Groups._ID, Groups.TITLE_RES};
+ Cursor cursor = db.query(Tables.GROUPS, PROJECTION, Groups.TITLE_RES + " IS NOT NULL ", null
+ ,null, null, Groups._ID);
+
+ if (cursor == null) {
+ return;
+ }
+
+ try {
+ long groupId = -1;
+ int titleRes = 0;
+ ContentValues values = new ContentValues();
+ while (cursor.moveToNext()) {
+ groupId = cursor.getLong(0);
+ titleRes = cursor.getInt(1);
+ values.clear();
+ values.put(Groups.TITLE, mContext.getResources().getString(titleRes));
+ db.update(Tables.GROUPS, values, Groups._ID + " = ?", new String[] {
+ String.valueOf(groupId)});
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
* Regenerates all locale-sensitive data if needed:
* nickname_lookup, name_lookup and sort keys. Invalidates the fast
* scrolling index cache.
@@ -4429,6 +4484,21 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("DROP TABLE IF EXISTS metadata_sync");
}
+ // Add origin column to record where a call came from via the origin extra.
+ private void upgradeToVersion1012(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + Tables.CALLS + " ADD " + CallColumns.ORIGIN
+ + " TEXT DEFAULT NULL;");
+ }
+
+ // Add Plugin name and User handle for incall api plugins.
+ private void upgradeToVersion1013(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + Tables.CALLS + " ADD " + CallLogConstants.PLUGIN_PACKAGE_NAME
+ + " TEXT DEFAULT NULL;");
+
+ db.execSQL("ALTER TABLE " + Tables.CALLS + " ADD " + CallLogConstants.PLUGIN_USER_HANDLE
+ + " TEXT DEFAULT NULL;");
+ }
+
public String extractHandleFromEmailAddress(String email) {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
if (tokens.length == 0) {
@@ -5183,6 +5253,12 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
qb.appendWhere(sb.toString());
}
+ public void buildDataLookupAndContactQuery(SQLiteQueryBuilder qb, String data) {
+ StringBuilder sb = new StringBuilder();
+ buildDataQuery(sb, data);
+ qb.setTables(sb.toString());
+ }
+
/**
* Phone lookup method that uses the custom SQLite function phone_number_compare_loose
* that serves as a fallback in case the regular lookup does not return any results.
@@ -5259,6 +5335,14 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
sb.append("')) AS lookup, " + Tables.DATA);
}
+ private void buildDataQuery(StringBuilder sb, String lookupData) {
+ // Todo: make more performant
+ sb.append(Tables.RAW_CONTACTS +
+ " JOIN " + Views.DATA + " data_view ON (data_view.raw_contact_id = "
+ + Tables.RAW_CONTACTS + "._id) WHERE data1 = ");
+ DatabaseUtils.appendEscapedSQLString(sb, lookupData);
+ }
+
private void appendPhoneLookupSelection(StringBuilder sb, String number, String numberE164) {
sb.append("lookup.data_id=data._id AND data.raw_contact_id=raw_contacts._id");
boolean hasNumberE164 = !TextUtils.isEmpty(numberE164);
@@ -5987,4 +6071,54 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + SearchIndexColumns.CONTACT_ID + "=CAST(? AS int)",
new String[] {String.valueOf(contactId)});
}
+
+ private void createDefaultGroups4PhoneAccount(SQLiteDatabase db) {
+ // 3 default groups
+ String[] title = new String[3];
+ int[] titleRes = new int[3];
+
+ title[0] = mContext.getResources().getString(R.string.group_title_co_workers);
+ titleRes[0] = R.string.group_title_co_workers;
+
+ title[1] = mContext.getResources().getString(R.string.group_title_family);
+ titleRes[1] = R.string.group_title_family;
+
+ title[2] = mContext.getResources().getString(R.string.group_title_friends);
+ titleRes[2] = R.string.group_title_friends;
+
+ for (int i = 0; i < title.length; i++) {
+ db.execSQL("INSERT INTO " + Tables.GROUPS + " (" +
+ GroupsColumns.ACCOUNT_ID + "," +
+ Groups.SOURCE_ID + "," +
+ Groups.VERSION + "," +
+ Groups.DIRTY + "," +
+ Groups.TITLE + "," +
+ Groups.TITLE_RES + "," +
+ Groups.NOTES + "," +
+ Groups.SYSTEM_ID + "," +
+ Groups.DELETED + "," +
+ Groups.GROUP_VISIBLE + "," +
+ Groups.SHOULD_SYNC + "," +
+ Groups.AUTO_ADD + "," +
+ Groups.FAVORITES + "," +
+ Groups.GROUP_IS_READ_ONLY + "," +
+ Groups.SYNC1 + ", " +
+ Groups.SYNC2 + ", " +
+ Groups.SYNC3 + ", " +
+ Groups.SYNC4 + ") " +
+ "VALUES (1,1,1,0,'"
+ + title[i] + "'," + titleRes[i] +
+ ",NULL,NULL,0,1,1,0,0,1,'','','','');"
+ );
+ }
+ }
+
+ public void createPhoneAccount(SQLiteDatabase db) {
+ String sql = "INSERT INTO " + Tables.ACCOUNTS
+ + "(" + AccountsColumns.ACCOUNT_NAME + ","
+ + AccountsColumns.ACCOUNT_TYPE + ") "
+ + "VALUES ('" + AccountWithDataSet.PHONE_NAME + "','"
+ + AccountWithDataSet.ACCOUNT_TYPE_PHONE + "')";
+ db.execSQL(sql);
+ }
}
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 8904dc1..870b912 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -158,10 +158,14 @@ import com.android.providers.contacts.database.MoreDatabaseUtils;
import com.android.providers.contacts.util.Clock;
import com.android.providers.contacts.util.ContactsPermissions;
import com.android.providers.contacts.util.DbQueryUtils;
+import com.android.providers.contacts.util.PreloadedContactsFileParser;
import com.android.providers.contacts.util.NeededForTesting;
import com.android.providers.contacts.util.UserUtils;
import com.android.vcard.VCardComposer;
import com.android.vcard.VCardConfig;
+
+import com.cyanogen.ambient.incall.CallableConstants;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
@@ -175,6 +179,7 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
@@ -193,6 +198,10 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import org.json.JSONException;
+
+import static com.cyanogen.ambient.incall.CallableConstants.ADDITIONAL_CALLABLE_MIMETYPES_PARAM_KEY;
+
/**
* Contacts content provider. The contract between this provider and applications
* is defined in {@link ContactsContract}.
@@ -240,12 +249,15 @@ public class ContactsProvider2 extends AbstractContactsProvider
private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
private static final int BACKGROUND_TASK_CLEAN_DELETE_LOG = 11;
+ private static final int BACKGROUND_TASK_ADD_DEFAULT_CONTACT = 12;
protected static final int STATUS_NORMAL = 0;
protected static final int STATUS_UPGRADING = 1;
protected static final int STATUS_CHANGING_LOCALE = 2;
protected static final int STATUS_NO_ACCOUNTS_NO_CONTACTS = 3;
+ private static final String PREF_PRELOADED_CONTACTS_ADDED = "preloaded_contacts_added";
+
/** Default for the maximum number of returned aggregation suggestions. */
private static final int DEFAULT_MAX_SUGGESTIONS = 5;
@@ -300,6 +312,11 @@ public class ContactsProvider2 extends AbstractContactsProvider
private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
+ Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
+ private static String WITHOUT_SIM_FLAG = "no_sim";
+
+ private boolean isWhereAppended = false;
+
+ public static final String ADD_GROUP_MEMBERS = "add_group_members";
private static final int CONTACTS = 1000;
private static final int CONTACTS_ID = 1001;
@@ -355,9 +372,12 @@ public class ContactsProvider2 extends AbstractContactsProvider
private static final int CALLABLES_FILTER = 3013;
private static final int CONTACTABLES = 3014;
private static final int CONTACTABLES_FILTER = 3015;
+
private static final int PHONES_ENTERPRISE = 3016;
private static final int EMAILS_LOOKUP_ENTERPRISE = 3017;
+ private static final int CALLABLE_CONTACTS = 3018;
+
private static final int PHONE_LOOKUP = 4000;
private static final int PHONE_LOOKUP_ENTERPRISE = 4001;
@@ -532,6 +552,22 @@ public class ContactsProvider2 extends AbstractContactsProvider
+ " FROM " + Tables.GROUPS
+ " WHERE " + Groups.TITLE + "=?)))";
+ private static final String CONTACTS_IN_GROUP_ID_SELECT =
+ Contacts._ID + " IN "
+ + "(SELECT DISTINCT "
+ + Data.CONTACT_ID
+ + " FROM " + Views.DATA
+ + " WHERE " + Data.MIMETYPE + " = '" + GroupMembership.CONTENT_ITEM_TYPE
+ + "' AND " + Data.DATA1 + " = ?)";
+
+ private static final String CONTACTS_NOT_IN_GROUP_ID_SELECT =
+ Contacts._ID + " NOT IN "
+ + "(SELECT DISTINCT "
+ + Data.CONTACT_ID
+ + " FROM " + Views.DATA
+ + " WHERE " + Data.MIMETYPE + " = '" + GroupMembership.CONTENT_ITEM_TYPE
+ + "' AND " + Data.DATA1 + " = ?)";
+
/** Sql for updating DIRTY flag on multiple raw contacts */
private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
"UPDATE " + Tables.RAW_CONTACTS +
@@ -771,6 +807,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
.add(Contacts.IS_USER_PROFILE)
.addAll(sContactsColumns)
.addAll(sContactsPresenceColumns)
+ .add(RawContacts.ACCOUNT_TYPE)
+ .add(RawContacts.ACCOUNT_NAME)
.build();
/** Contains just the contacts columns */
@@ -810,6 +848,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
.add(Phone.LABEL)
.add(Phone.IS_SUPER_PRIMARY)
.add(Phone.CONTACT_ID)
+ .add(Phone.MIMETYPE)
.add(Contacts.IS_USER_PROFILE, "NULL")
.build();
@@ -917,6 +956,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
.addAll(sDataColumns)
.addAll(sDataPresenceColumns)
.addAll(sContactsColumns)
+ .addAll(sRawContactColumns)
.addAll(sContactPresenceColumns)
.addAll(sDataUsageColumns)
.build();
@@ -1203,6 +1243,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
CONTACTS_STREQUENT_FILTER);
+ matcher.addURI(ContactsContract.AUTHORITY, "contacts/group", CONTACTS_GROUP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/delete_usage", CONTACTS_DELETE_USAGE);
@@ -1246,6 +1287,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
matcher.addURI(ContactsContract.AUTHORITY, "data/callables/#", CALLABLES_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter", CALLABLES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter/*", CALLABLES_FILTER);
+ matcher.addURI(ContactsContract.AUTHORITY, "data/callables/contacts", CALLABLE_CONTACTS);
+ matcher.addURI(ContactsContract.AUTHORITY, "data/callables/contacts/*", CALLABLE_CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "data/contactables/", CONTACTABLES);
matcher.addURI(ContactsContract.AUTHORITY, "data/contactables/filter", CONTACTABLES_FILTER);
@@ -1451,6 +1494,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
private Handler mBackgroundHandler;
private long mLastPhotoCleanup = 0;
+ private boolean isPhoneNumberFuzzySearchEnabled;
private FastScrollingIndexCache mFastScrollingIndexCache;
@@ -1524,6 +1568,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
profileInfo.authority = ContactsContract.AUTHORITY;
mProfileProvider.attachInfo(getContext(), profileInfo);
mProfileHelper = mProfileProvider.getDatabaseHelper(getContext());
+ isPhoneNumberFuzzySearchEnabled = getContext().getResources().getBoolean(
+ R.bool.phone_number_fuzzy_search);
// Initialize the pre-authorized URI duration.
mPreAuthorizedUriDuration = DEFAULT_PREAUTHORIZED_URI_EXPIRATION;
@@ -1537,6 +1583,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
scheduleBackgroundTask(BACKGROUND_TASK_CLEAN_DELETE_LOG);
+ scheduleBackgroundTask(BACKGROUND_TASK_ADD_DEFAULT_CONTACT);
return true;
}
@@ -1777,9 +1824,53 @@ public class ContactsProvider2 extends AbstractContactsProvider
DeletedContactsTableUtil.deleteOldLogs(db);
break;
}
+
+ case BACKGROUND_TASK_ADD_DEFAULT_CONTACT: {
+ if (shouldAttemptPreloadingContacts()) {
+ try {
+ InputStream inputStream = getContext().getResources().openRawResource(
+ R.raw.preloaded_contacts);
+ PreloadedContactsFileParser pcfp = new
+ PreloadedContactsFileParser(inputStream);
+ ArrayList<ContentProviderOperation> cpOperations = pcfp.parseForContacts();
+ if (cpOperations == null) break;
+
+ getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY,
+ cpOperations);
+ // persist the completion of the transaction
+ onPreloadingContactsComplete();
+
+ } catch (NotFoundException nfe) {
+ System.out.println();
+ nfe.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (OperationApplicationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ break;
+ }
+
}
}
+ private boolean shouldAttemptPreloadingContacts() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ return getContext().getResources().getBoolean(R.bool.config_preload_contacts) &&
+ !prefs.getBoolean(PREF_PRELOADED_CONTACTS_ADDED, false);
+ }
+
+ private void onPreloadingContactsComplete() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(PREF_PRELOADED_CONTACTS_ADDED, true);
+ editor.commit();
+ }
+
public void onLocaleChanged() {
if (mProviderStatus != STATUS_NORMAL
&& mProviderStatus != STATUS_NO_ACCOUNTS_NO_CONTACTS) {
@@ -5351,13 +5442,43 @@ public class ContactsProvider2 extends AbstractContactsProvider
filterParam = uri.getLastPathSegment();
}
- // If the query consists of a single word, we can do snippetizing after-the-fact for
- // a performance boost. Otherwise, we can't defer.
+ // If the query consists of a single word, we can do snippetizing
+ // after-the-fact for a performance boost. Otherwise, we can't defer.
snippetDeferred = isSingleWordQuery(filterParam)
&& deferredSnipRequested && snippetNeeded(projection);
setTablesAndProjectionMapForContactsWithSnippet(
qb, uri, projection, filterParam, directoryId,
snippetDeferred);
+ long groupId = -1;
+ try {
+ groupId = Long.parseLong(uri.getQueryParameter(Groups._ID));
+ } catch (Exception exception) {
+ groupId = -1;
+ }
+ if (groupId != -1) {
+ StringBuilder groupBuilder = new StringBuilder();
+ if (uri.getBooleanQueryParameter(ADD_GROUP_MEMBERS, false)) {
+ // filter all the contacts that are NOT assigned to the
+ // group whose id is 'groupId'
+ groupBuilder.append(Contacts._ID + " NOT IN (" + " SELECT DISTINCT "
+ + Data.CONTACT_ID
+ + " FROM " + Views.DATA + " WHERE " + Data.MIMETYPE + " = '"
+ + GroupMembership.CONTENT_ITEM_TYPE + "' AND " + Data.DATA1
+ + " = " + groupId + ")");
+ } else {
+ // filter all the contacts that are assigned to the
+ // group whose id is 'groupId'
+ groupBuilder.append(Contacts._ID + " IN (" + " SELECT DISTINCT "
+ + Data.CONTACT_ID
+ + " FROM " + Views.DATA + " WHERE " + Data.MIMETYPE + " = '"
+ + GroupMembership.CONTENT_ITEM_TYPE + "' AND " + Data.DATA1
+ + " = " + groupId + ")");
+ }
+ if (isWhereAppended) {
+ qb.appendWhere(" AND ");
+ }
+ qb.appendWhere(groupBuilder.toString());
+ }
break;
}
@@ -5421,12 +5542,17 @@ public class ContactsProvider2 extends AbstractContactsProvider
mDbHelper.get().getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
final long sipMimeTypeId =
mDbHelper.get().getMimeTypeId(SipAddress.CONTENT_ITEM_TYPE);
+ final String additionalCallableMimeTypes = getCallableMimeTypesFromUri(uri);
+ String mimeTypeIdClauses = phoneMimeTypeId + ", " + sipMimeTypeId;
+ if (!TextUtils.isEmpty(additionalCallableMimeTypes)) {
+ mimeTypeIdClauses += ", " + additionalCallableMimeTypes;
+ }
qb.appendWhere(DbQueryUtils.concatenateClauses(
selection,
"(" + Contacts.STARRED + "=1",
DataColumns.MIMETYPE_ID + " IN (" +
- phoneMimeTypeId + ", " + sipMimeTypeId + ")) AND (" +
+ mimeTypeIdClauses + ")) AND (" +
RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")"));
starredInnerQuery = qb.buildQuery(subProjection, null, null,
null, Data.IS_SUPER_PRIMARY + " DESC," + SORT_BY_DATA_USAGE, null);
@@ -5455,7 +5581,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
selection,
"(" + Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
DataColumns.MIMETYPE_ID + " IN (" +
- phoneMimeTypeId + ", " + sipMimeTypeId + ")) AND (" +
+ mimeTypeIdClauses + ")) AND (" +
RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")"));
frequentInnerQuery = qb.buildQuery(subProjection, null, null, null,
SORT_BY_DATA_USAGE, "25");
@@ -5539,7 +5665,23 @@ public class ContactsProvider2 extends AbstractContactsProvider
case CONTACTS_GROUP: {
setTablesAndProjectionMapForContacts(qb, projection);
- if (uri.getPathSegments().size() > 2) {
+ appendLocalDirectoryAndAccountSelectionIfNeeded(qb, directoryId, uri);
+ long groupId = -1;
+ try {
+ groupId = Long.parseLong(uri.getQueryParameter(Groups._ID));
+ } catch (Exception exception) {
+ groupId = -1;
+ }
+ if (groupId != -1) {
+ qb.appendWhere(" AND ");
+ if (uri.getBooleanQueryParameter(ADD_GROUP_MEMBERS, false)) {
+ qb.appendWhere(CONTACTS_NOT_IN_GROUP_ID_SELECT);
+ } else {
+ qb.appendWhere(CONTACTS_IN_GROUP_ID_SELECT);
+ }
+ selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
+ } else if (uri.getPathSegments().size() > 2) {
+ qb.appendWhere(" AND ");
qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
String groupMimeTypeId = String.valueOf(
mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
@@ -5679,10 +5821,13 @@ public class ContactsProvider2 extends AbstractContactsProvider
DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
final String mimeTypeIsSipExpression =
DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
+
setTablesAndProjectionMapForData(qb, uri, projection, false);
if (match == CALLABLES) {
- qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
- ") OR (" + mimeTypeIsSipExpression + "))");
+ String appendWhere = " AND ((" + mimeTypeIsPhoneExpression + ") OR (" +
+ mimeTypeIsSipExpression + ")" +
+ appendMimeTypeQueryParameters(uri) + ")";
+ qb.appendWhere(appendWhere);
} else {
qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
}
@@ -5704,6 +5849,32 @@ public class ContactsProvider2 extends AbstractContactsProvider
}
break;
}
+ case CALLABLE_CONTACTS: {
+ final String mimeTypeIsPhoneExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
+ final String mimeTypeIsSipExpression =
+ DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
+
+ setTablesAndProjectionMapForData(qb, uri, projection, false);
+
+ String appendWhere = " AND ((" + mimeTypeIsPhoneExpression + ") OR (" +
+ mimeTypeIsSipExpression + ")" +
+ appendMimeTypeQueryParameters(uri) + ")";
+ qb.appendWhere(appendWhere);
+
+ final boolean removeDuplicates = readBooleanQueryParameter(
+ uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
+ if (removeDuplicates) {
+ groupBy = RawContacts.CONTACT_ID;
+
+ // In this case, because we dedupe phone numbers, the address book indexer needs
+ // to take it into account too. (Otherwise headers will appear in wrong
+ // positions.)
+ // So use count(distinct CONTACT_ID) instead of count(*).
+ addressBookIndexerCountExpression = "DISTINCT " + RawContacts.CONTACT_ID;
+ }
+ break;
+ }
case PHONES_ID:
case CALLABLES_ID: {
@@ -5735,8 +5906,10 @@ public class ContactsProvider2 extends AbstractContactsProvider
DataUsageStatColumns.USAGE_TYPE_INT_CALL);
setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
if (match == CALLABLES_FILTER) {
- qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
- ") OR (" + mimeTypeIsSipExpression + "))");
+ String appendWhere = " AND ((" + mimeTypeIsPhoneExpression + ") OR (" +
+ mimeTypeIsSipExpression + ")" +
+ appendMimeTypeQueryParameters(uri) + ")";
+ qb.appendWhere(appendWhere);
} else {
qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
}
@@ -6210,23 +6383,34 @@ public class ContactsProvider2 extends AbstractContactsProvider
String number =
uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
- String numberE164 = PhoneNumberUtils.formatNumberToE164(
- number, mDbHelper.get().getCurrentCountryIso());
- String normalizedNumber = PhoneNumberUtils.normalizeNumber(number);
- mDbHelper.get().buildPhoneLookupAndContactQuery(
- qb, normalizedNumber, numberE164);
- qb.setProjectionMap(sPhoneLookupProjectionMap);
-
- // removeNonStarMatchesFromCursor() requires the cursor to contain
- // PhoneLookup.NUMBER. Therefore, if the projection explicitly omits it, extend
- // the projection.
- String[] projectionWithNumber = projection;
- if (projection != null
- && !ArrayUtils.contains(projection,PhoneLookup.NUMBER)) {
- projectionWithNumber = ArrayUtils.appendElement(
- String.class, projection, PhoneLookup.NUMBER);
+
+ boolean isPhoneNumber = isPhoneNumber(number);
+
+ String[] projectionWithNumber;
+ if (isPhoneNumber) {
+ String numberE164 = PhoneNumberUtils.formatNumberToE164(
+ number, mDbHelper.get().getCurrentCountryIso());
+ String normalizedNumber = PhoneNumberUtils.normalizeNumber(number);
+ mDbHelper.get().buildPhoneLookupAndContactQuery(
+ qb, normalizedNumber, numberE164);
+ qb.setProjectionMap(sPhoneLookupProjectionMap);
+
+ // removeNonStarMatchesFromCursor() requires the cursor to contain
+ // PhoneLookup.NUMBER. Therefore, if the projection explicitly omits it, extend
+ // the projection.
+ projectionWithNumber = projection;
+ if (projection != null
+ && !ArrayUtils.contains(projection,PhoneLookup.NUMBER)) {
+ projectionWithNumber = ArrayUtils.appendElement(
+ String.class, projection, PhoneLookup.NUMBER);
+ }
+ } else {
+ mDbHelper.get().buildDataLookupAndContactQuery(qb, number);
+ projectionWithNumber = new String[0];
+ sortOrder = null;
}
+
// Peek at the results of the first query (which attempts to use fully
// normalized and internationalized numbers for comparison). If no results
// were returned, fall back to using the SQLite function
@@ -6238,8 +6422,12 @@ public class ContactsProvider2 extends AbstractContactsProvider
try {
if (cursor.getCount() > 0) {
foundResult = true;
- return PhoneLookupWithStarPrefix
- .removeNonStarMatchesFromCursor(number, cursor);
+ if (isPhoneNumber) {
+ return PhoneLookupWithStarPrefix
+ .removeNonStarMatchesFromCursor(number, cursor);
+ } else {
+ return cursor;
+ }
}
// Use the fall-back lookup method.
@@ -6492,6 +6680,45 @@ public class ContactsProvider2 extends AbstractContactsProvider
return cursor;
}
+ private String appendMimeTypeQueryParameters(Uri uri) {
+ final String mimeTypesQueryParameter =
+ getQueryParameter(uri, ADDITIONAL_CALLABLE_MIMETYPES_PARAM_KEY);
+ StringBuilder appendWhere = new StringBuilder();
+ if (!TextUtils.isEmpty(mimeTypesQueryParameter)) {
+ List<String> mimeTypesQueryParameterList =
+ Arrays.asList(mimeTypesQueryParameter.split("\\s*,\\s*"));
+ if (mimeTypesQueryParameterList != null && !mimeTypesQueryParameterList.isEmpty()) {
+ // Parse URI
+ for (String mimeType : mimeTypesQueryParameterList) {
+ long mimeTypeId = mDbHelper.get().getMimeTypeId(mimeType);
+ String mimeTypeIsExpression = DataColumns.MIMETYPE_ID + "=" + mimeTypeId;
+ appendWhere.append(" OR (" + mimeTypeIsExpression + ")");
+ }
+ }
+ }
+ return appendWhere.toString();
+ }
+
+ private String getCallableMimeTypesFromUri(Uri uri) {
+ final String mimeTypesQueryParameter =
+ getQueryParameter(uri, ADDITIONAL_CALLABLE_MIMETYPES_PARAM_KEY);
+ StringBuilder mimeTypeIds = new StringBuilder();
+ if (!TextUtils.isEmpty(mimeTypesQueryParameter)) {
+ List<String> mimeTypesQueryParameterList =
+ Arrays.asList(mimeTypesQueryParameter.split("\\s*,\\s*"));
+ if (mimeTypesQueryParameterList != null && !mimeTypesQueryParameterList.isEmpty()) {
+ // Parse URI
+ for (String mimeType : mimeTypesQueryParameterList) {
+ if (!TextUtils.isEmpty(mimeTypeIds.toString())) {
+ mimeTypeIds.append(", ");
+ }
+ mimeTypeIds.append(mDbHelper.get().getMimeTypeId(mimeType));
+ }
+ }
+ }
+ return mimeTypeIds.toString();
+ }
+
// Rewrites query sort orders using SORT_KEY_{PRIMARY, ALTERNATIVE}
// to use PHONEBOOK_BUCKET_{PRIMARY, ALTERNATIVE} as primary key; all
// other sort orders are returned unchanged. Preserves ordering
@@ -7320,9 +7547,63 @@ public class ContactsProvider2 extends AbstractContactsProvider
private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
String[] projection, String filter, long directoryId, boolean deferSnippeting) {
+ isWhereAppended = false;
StringBuilder sb = new StringBuilder();
sb.append(Views.CONTACTS);
+ /* Do not show contacts when SIM card is disabled for CONTACTS_FILTER */
+ StringBuilder sbWhere = new StringBuilder();
+ String withoutSim = getQueryParameter(uri, WITHOUT_SIM_FLAG );
+ if ("true".equals(withoutSim)) {
+ final long[] accountId = getAccountIdWithoutSim(uri);
+ if (accountId == null) {
+ // No such account.
+ sbWhere.setLength(0);
+ sbWhere.append("(1=2)");
+ } else {
+ if (accountId.length > 0) {
+ sbWhere.append(" (" + Contacts._ID + " not IN (" + "SELECT "
+ + RawContacts.CONTACT_ID + " FROM "
+ + Tables.RAW_CONTACTS + " WHERE "
+ + RawContacts.CONTACT_ID + " not NULL AND ( ");
+ for (int i = 0; i < accountId.length; i++) {
+ sbWhere.append(RawContactsColumns.ACCOUNT_ID + "="
+ + accountId[i]);
+ if (i != accountId.length - 1) {
+ sbWhere.append(" OR ");
+ }
+ }
+ sbWhere.append(")))");
+ }
+ }
+ } else {
+ final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
+ // Accounts are valid by only checking one parameter, since we've
+ // already ruled out partial accounts.
+ final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
+ if (validAccount) {
+ final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
+ if (accountId != null) {
+ sbWhere.append(" INNER JOIN (SELECT "
+ + RawContacts.CONTACT_ID
+ + " AS raw_contact_contact_id FROM "
+ + Tables.RAW_CONTACTS + " WHERE "
+ + RawContactsColumns.ACCOUNT_ID + " = "
+ + accountId
+ + ") ON raw_contact_contact_id = " + Contacts._ID);
+ }
+ }
+ }
+
+ if (!TextUtils.isEmpty(sbWhere.toString())) {
+ if ("true".equals(withoutSim)) {
+ qb.appendWhere(sbWhere.toString());
+ isWhereAppended = true;
+ } else {
+ sb.append(sbWhere.toString());
+ }
+ }
+
if (filter != null) {
filter = filter.trim();
}
@@ -7359,13 +7640,230 @@ public class ContactsProvider2 extends AbstractContactsProvider
int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
: DEFAULT_SNIPPET_ARG_MAX_TOKENS;
- appendSearchIndexJoin(
- sb, filter, true, startMatch, endMatch, ellipsis, maxTokens, deferSnippeting);
+ if (isPhoneNumberFuzzySearchEnabled) {
+ appendSearchIndexJoinForFuzzySearch(sb, filter, true,
+ startMatch, endMatch, ellipsis, maxTokens, deferSnippeting);
+ } else {
+ appendSearchIndexJoin(sb, filter, true, startMatch, endMatch,
+ ellipsis, maxTokens, deferSnippeting);
+ }
} else {
- appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
+ if (isPhoneNumberFuzzySearchEnabled) {
+ appendSearchIndexJoinForFuzzySearch(sb, filter, false, null,
+ null, null, 0, false);
+ } else {
+ appendSearchIndexJoin(sb, filter, false, null, null, null, 0,
+ false);
+ }
}
}
+ public void appendSearchIndexJoinForFuzzySearch(StringBuilder sb, String filter,
+ boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
+ int maxTokens, boolean deferSnippeting) {
+ boolean isEmailAddress = false;
+ String emailAddress = null;
+ boolean isPhoneNumber = false;
+ String phoneNumber = null;
+ String numberE164 = null;
+
+
+ if (filter.indexOf('@') != -1) {
+ emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
+ isEmailAddress = !TextUtils.isEmpty(emailAddress);
+ } else {
+ isPhoneNumber = isPhoneNumber(filter);
+ if (isPhoneNumber) {
+ phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
+ numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
+ mDbHelper.get().getCurrentCountryIso());
+ }
+ }
+
+ final String SNIPPET_CONTACT_ID = "snippet_contact_id";
+ sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS " + SNIPPET_CONTACT_ID);
+ if (snippetNeeded) {
+ sb.append(", ");
+ if (isEmailAddress) {
+ sb.append("ifnull(");
+ if (!deferSnippeting) {
+ // Add the snippet marker only when we're really creating snippet.
+ DatabaseUtils.appendEscapedSQLString(sb, startMatch);
+ sb.append("||");
+ }
+ sb.append("(SELECT MIN(" + Email.ADDRESS + ")");
+ sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
+ sb.append(" WHERE " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
+ sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
+ DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
+ sb.append(")");
+ if (!deferSnippeting) {
+ sb.append("||");
+ DatabaseUtils.appendEscapedSQLString(sb, endMatch);
+ }
+ sb.append(",");
+
+ if (deferSnippeting) {
+ sb.append(SearchIndexColumns.CONTENT);
+ } else {
+ appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
+ }
+ sb.append(")");
+ } else if (isPhoneNumber) {
+ sb.append("ifnull(");
+ if (!deferSnippeting) {
+ // Add the snippet marker only when we're really creating snippet.
+ DatabaseUtils.appendEscapedSQLString(sb, startMatch);
+ sb.append("||");
+ }
+ sb.append("(SELECT MIN(" + Phone.NUMBER + ")");
+ sb.append(" FROM " +
+ Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
+ sb.append(" ON " + DataColumns.CONCRETE_ID);
+ sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
+ sb.append(" WHERE " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
+ sb.append("=" + RawContacts.CONTACT_ID);
+ sb.append(" AND (" + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
+ sb.append(phoneNumber);
+ sb.append("%'");
+ sb.append("))");
+ if (! deferSnippeting) {
+ sb.append("||");
+ DatabaseUtils.appendEscapedSQLString(sb, endMatch);
+ }
+ sb.append(",");
+
+ if (deferSnippeting) {
+ sb.append(SearchIndexColumns.CONTENT);
+ } else {
+ appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
+ }
+ sb.append(")");
+ } else {
+ final String normalizedFilter = NameNormalizer.normalize(filter);
+ if (!TextUtils.isEmpty(normalizedFilter)) {
+ if (deferSnippeting) {
+ sb.append(SearchIndexColumns.CONTENT);
+ } else {
+ sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
+ sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
+ sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
+ sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
+ sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
+ sb.append(" GLOB '" + normalizedFilter + "*' AND ");
+ sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
+ sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
+ sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
+ sb.append("=rc." + RawContacts.CONTACT_ID);
+ sb.append(") THEN NULL ELSE ");
+ appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
+ sb.append(" END)");
+ }
+ } else {
+ sb.append("NULL");
+ }
+ }
+ sb.append(" AS " + SearchSnippets.SNIPPET);
+ }
+
+ sb.append(" FROM " + Tables.SEARCH_INDEX);
+ sb.append(" WHERE ");
+ if (isPhoneNumber) {
+ sb.append(Tables.SEARCH_INDEX + " MATCH '");
+ // normalized version of the phone number (phoneNumber can only have
+ // + and digits)
+ final String phoneNumberCriteria = " OR tokens:" + phoneNumber
+ + "*";
+
+ // international version of this number (numberE164 can only have +
+ // and digits)
+ final String numberE164Criteria = (numberE164 != null && !TextUtils
+ .equals(numberE164, phoneNumber)) ? " OR tokens:"
+ + numberE164 + "*" : "";
+
+ // combine all criteria
+ final String commonCriteria = phoneNumberCriteria
+ + numberE164Criteria;
+
+ // search in content
+ sb.append(SearchIndexManager.getFtsMatchQuery(filter,
+ FtsQueryBuilder.getDigitsQueryBuilder(commonCriteria)));
+ sb.append("' AND " + SNIPPET_CONTACT_ID + " IN "
+ + Tables.DEFAULT_DIRECTORY);
+ if (snippetNeeded) {
+ // only support fuzzy search when there is snippet column and
+ // the filter is phone number!
+ sb.append(" UNION SELECT " + SearchIndexColumns.CONTACT_ID + " AS " +
+ SNIPPET_CONTACT_ID);
+ sb.append(", ");
+ if (!deferSnippeting) {
+ // Add the snippet marker only when we're really creating snippet.
+ DatabaseUtils.appendEscapedSQLString(sb, startMatch);
+ sb.append("||");
+ }
+ sb.append("(SELECT MIN(" + Phone.NUMBER + ")");
+ sb.append(" FROM " +
+ Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
+ sb.append(" ON " + DataColumns.CONCRETE_ID);
+ sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
+ sb.append(" WHERE " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
+ sb.append("=" + RawContacts.CONTACT_ID);
+ sb.append(" AND (" + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
+ sb.append(phoneNumber);
+ sb.append("%'");
+ sb.append("))");
+ if (!deferSnippeting) {
+ sb.append("||");
+ DatabaseUtils.appendEscapedSQLString(sb, endMatch);
+ }
+ sb.append(" AS " + SearchSnippets.SNIPPET);
+ sb.append(" FROM " + Tables.SEARCH_INDEX);
+ sb.append(" WHERE " + SearchSnippets.SNIPPET + " IS NOT NULL ");
+ sb.append(" AND " + SNIPPET_CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")");
+ } else {
+ sb.append(")");
+ }
+ } else {
+ sb.append(Tables.SEARCH_INDEX + " MATCH '");
+ if (isEmailAddress) {
+ // we know that the emailAddress contains a @. This phrase search should be
+ // scoped against "content:" only, but unfortunately SQLite doesn't support
+ // phrases and scoped columns at once. This is fine in this case however, because:
+ // We can't erroneously match against name, as it is all-hex (so the @ can't match)
+ // We can't match against tokens, because phone-numbers can't contain @
+ final String sanitizedEmailAddress =
+ emailAddress == null ? "" : sanitizeMatch(emailAddress);
+ sb.append("\"");
+ sb.append(sanitizedEmailAddress);
+ sb.append("*\"");
+ } else if (isPhoneNumber) {
+ // normalized version of the phone number (phoneNumber can only have + and digits)
+ final String phoneNumberCriteria = " OR tokens:" + phoneNumber + "*";
+
+ // international version of this number (numberE164 can only have + and digits)
+ final String numberE164Criteria =
+ (numberE164 != null && !TextUtils.equals(numberE164, phoneNumber))
+ ? " OR tokens:" + numberE164 + "*"
+ : "";
+
+ // combine all criteria
+ final String commonCriteria =
+ phoneNumberCriteria + numberE164Criteria;
+
+ // search in content
+ sb.append(SearchIndexManager.getFtsMatchQuery(filter,
+ FtsQueryBuilder.getDigitsQueryBuilder(commonCriteria)));
+ } else {
+ // general case: not a phone number, not an email-address
+ sb.append(SearchIndexManager.getFtsMatchQuery(filter,
+ FtsQueryBuilder.SCOPED_NAME_NORMALIZING));
+ }
+ // Omit results in "Other Contacts".
+ sb.append("' AND " + SNIPPET_CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")");
+ }
+ sb.append(" ON (" + Contacts._ID + "=" + SNIPPET_CONTACT_ID + ")");
+ }
+
public void appendSearchIndexJoin(StringBuilder sb, String filter,
boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
int maxTokens, boolean deferSnippeting) {
@@ -7747,22 +8245,50 @@ public class ContactsProvider2 extends AbstractContactsProvider
sb.append("(1)");
}
- final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
- // Accounts are valid by only checking one parameter, since we've
- // already ruled out partial accounts.
- final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
- if (validAccount) {
- final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
+ String withoutSim = getQueryParameter(uri, WITHOUT_SIM_FLAG );
+ if ("true".equals(withoutSim)) {
+ final long[] accountId = getAccountIdWithoutSim(uri);
+
if (accountId == null) {
// No such account.
sb.setLength(0);
sb.append("(1=2)");
} else {
- sb.append(
- " AND (" + Contacts._ID + " IN (" +
- "SELECT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS +
- " WHERE " + RawContactsColumns.ACCOUNT_ID + "=" + accountId.toString() +
- "))");
+ if (accountId.length > 0) {
+ sb.append(
+ " AND (" + Contacts._ID + " not IN (" +
+ "SELECT " + RawContacts.CONTACT_ID + " FROM "
+ + Tables.RAW_CONTACTS +
+ " WHERE " + RawContacts.CONTACT_ID + " not NULL AND ( ");
+ for (int i = 0; i < accountId.length; i++) {
+ sb.append(RawContactsColumns.ACCOUNT_ID + "="
+ + accountId[i]);
+ if (i != accountId.length - 1) {
+ sb.append(" or ");
+ }
+ }
+ sb.append(")))");
+ }
+ }
+ }
+ else {
+ final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
+ // Accounts are valid by only checking one parameter, since we've
+ // already ruled out partial accounts.
+ final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
+ if (validAccount) {
+ final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
+ if (accountId == null) {
+ // No such account.
+ sb.setLength(0);
+ sb.append("(1=2)");
+ } else {
+ sb.append(
+ " AND (" + Contacts._ID + " IN (" +
+ "SELECT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + RawContactsColumns.ACCOUNT_ID + "=" + accountId.toString() +
+ "))");
+ }
}
}
qb.appendWhere(sb.toString());
@@ -7812,6 +8338,56 @@ public class ContactsProvider2 extends AbstractContactsProvider
}
}
+ private long[] getAccountIdWithoutSim(Uri uri) {
+ final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
+ final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
+ Cursor c = null;
+ SQLiteDatabase db = mContactsHelper.getWritableDatabase();
+ long[] accountId = null;
+ try {
+ if (null != accountType) {
+ c = db.query(Tables.ACCOUNTS,
+ new String[] { AccountsColumns._ID },
+ AccountsColumns.ACCOUNT_TYPE + "=?",
+ new String[] { String.valueOf(accountType) }, null,
+ null, null);
+ } else if (!TextUtils.isEmpty(accountName)) {
+ String[] names = accountName.split(",");
+ int nameCount = names.length;
+ String where = AccountsColumns.ACCOUNT_NAME + "=?";
+ StringBuilder selection = new StringBuilder();
+ String[] selectionArgs = new String[nameCount];
+ for (int i = 0; i < nameCount; i++) {
+ selection.append(where);
+ if (i != nameCount - 1) {
+ selection.append(" OR ");
+ }
+ selectionArgs[i] = names[i];
+ }
+ c = db.query(Tables.ACCOUNTS,
+ new String[] { AccountsColumns._ID },
+ selection.toString(),
+ selectionArgs, null,
+ null, null);
+ }
+
+ if (c != null) {
+ accountId = new long[c.getCount()];
+
+ for (int i = 0; i < c.getCount(); i++) {
+ if (c.moveToNext()) {
+ accountId[c.getPosition()] = c.getInt(0);
+ }
+ }
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return accountId;
+ }
+
private AccountWithDataSet getAccountWithDataSetFromUri(Uri uri) {
final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
diff --git a/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java b/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java
index 1de0823..42b4884 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java
@@ -17,6 +17,11 @@ package com.android.providers.contacts;
import android.content.Context;
import com.android.providers.contacts.aggregation.AbstractContactAggregator;
+import android.content.ContentValues;
+import android.provider.ContactsContract.Data;
+
+import com.android.providers.contacts.aggregation.ContactAggregator;
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
public class DataRowHandlerForCustomMimetype extends DataRowHandler {
@@ -24,4 +29,17 @@ public class DataRowHandlerForCustomMimetype extends DataRowHandler {
AbstractContactAggregator aggregator, String mimetype) {
super(context, dbHelper, aggregator, mimetype);
}
+ @Override
+ public boolean hasSearchableData() {
+ return true;
+ }
+ @Override
+ public boolean containsSearchableColumns(ContentValues values) {
+ return values.containsKey(Data.DATA1);
+ }
+
+ @Override
+ public void appendSearchableData(IndexBuilder builder) {
+ builder.appendContentFromColumn(Data.DATA1);
+ }
}
diff --git a/src/com/android/providers/contacts/HanziToPinyin.java b/src/com/android/providers/contacts/HanziToPinyin.java
index d140439..a7d90c2 100644
--- a/src/com/android/providers/contacts/HanziToPinyin.java
+++ b/src/com/android/providers/contacts/HanziToPinyin.java
@@ -19,6 +19,7 @@ package com.android.providers.contacts;
import android.text.TextUtils;
import android.util.Log;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Locale;
@@ -37,6 +38,32 @@ public class HanziToPinyin {
private Transliterator mPinyinTransliterator;
private Transliterator mAsciiTransliterator;
+ private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);
+ private static final String[] MULTINAMES = {
+ "\u590f","\u77bf","\u66fe","\u77f3","\u89e3","\u85cf","\u7fdf","\u90fd",
+ "\u516d","\u8584","\u8d3e","\u5c45","\u67e5","\u76db","\u5854","\u548c",
+ "\u84dd","\u6bb7","\u4e7e","\u9646","\u4e5c","\u961a","\u53f6","\u5f3a",
+ "\u6c64","\u4e07","\u6c88","\u4ec7","\u5357","\u5355","\u535c","\u9e1f",
+ "\u601d","\u5bfb","\u65bc","\u4f59","\u6d45","\u6d63","\u65e0","\u4fe1",
+ "\u8a31","\u9f50","\u4fde","\u82e5", };
+
+ private static final byte[][] MULTIPYS = {
+ { 88, 73, 65, 0, 0, 0 }, { 81, 85, 0, 0, 0, 0 }, { 90, 69, 78, 71, 0, 0 },
+ { 83, 72, 73, 0, 0, 0 }, { 88, 73, 69, 0, 0, 0 }, { 90, 65, 78, 71, 0, 0 },
+ { 90, 72, 65, 73, 0, 0 }, { 68, 85, 0, 0, 0, 0 }, { 76, 85, 0, 0, 0, 0 },
+ { 66, 79, 0, 0, 0, 0 }, { 74, 73, 65, 0, 0, 0 }, { 74, 85, 0, 0, 0, 0 },
+ { 90, 72, 65, 0, 0, 0 }, { 83, 72, 69, 78, 71, 0 }, { 84, 65, 0, 0, 0, 0 },
+ { 72, 69, 0, 0, 0, 0 }, { 76, 65, 78, 0, 0, 0 }, { 89, 73, 78, 0, 0, 0 },
+ { 81, 73, 65, 78, 0, 0 }, { 76, 85, 0, 0, 0, 0 }, { 78, 73, 69, 0, 0, 0 },
+ { 75, 65, 78, 0, 0, 0 }, { 89, 69, 0, 0, 0, 0 }, { 81, 73, 65, 78, 71, 0 },
+ { 84, 65, 78, 71, 0, 0 }, { 87, 65, 78, 0, 0, 0 }, { 83, 72, 69, 78, 0, 0 },
+ { 81, 73, 85, 0, 0, 0 }, { 78, 65, 78, 0, 0, 0 }, { 83, 72, 65, 78, 0, 0 },
+ { 66, 85, 0, 0, 0, 0 }, { 78, 73, 65, 79, 0, 0 }, { 83, 73, 0, 0, 0, 0 },
+ { 88, 85, 78, 0, 0, 0 }, { 89, 85, 0, 0, 0, 0 }, { 89, 85, 0, 0, 0, 0 },
+ { 81, 73, 65, 78, 0, 0 }, { 87, 65, 78, 0, 0, 0 }, { 87, 85, 0, 0, 0, 0 },
+ { 88, 73, 78, 0, 0, 0 }, { 88, 85, 0, 0, 0, 0 }, { 81, 73, 0, 0, 0, 0 },
+ { 89, 85, 0, 0, 0, 0 }, { 82, 85, 79, 0, 0, 0 }, };
+
public static class Token {
/**
* Separator between target string for each source char
@@ -94,7 +121,34 @@ public class HanziToPinyin {
}
}
- private void tokenize(char character, Token token) {
+ /**
+ * Check if the first name is multi-pinyin
+ *
+ * @return right pinyin for this first name
+ */
+ private static String getMPinyin(final String firstName) {
+ int offset, cmp;
+ cmp = -1;
+
+ for (offset = 0; offset < MULTINAMES.length; offset++) {
+ cmp = COLLATOR.compare(firstName, MULTINAMES[offset]);
+ if (cmp == 0) {
+ break;
+ }
+ }
+
+ if (cmp != 0) {
+ return null;
+ } else {
+ StringBuilder pinyin = new StringBuilder();
+ for (int j = 0; j < MULTIPYS[offset].length && MULTIPYS[offset][j] != 0; j++) {
+ pinyin.append((char) MULTIPYS[offset][j]);
+ }
+ return pinyin.toString();
+ }
+ }
+
+ private void tokenize(char character, Token token, boolean isFirstName) {
token.source = Character.toString(character);
// ASCII
@@ -113,6 +167,14 @@ public class HanziToPinyin {
}
token.type = Token.PINYIN;
+
+ if (isFirstName) {
+ token.target = getMPinyin(Character.toString(character));
+ if (token.target != null) {
+ return;
+ }
+ }
+
token.target = mPinyinTransliterator.transliterate(token.source);
if (TextUtils.isEmpty(token.target) ||
TextUtils.equals(token.source, token.target)) {
@@ -144,6 +206,7 @@ public class HanziToPinyin {
final StringBuilder sb = new StringBuilder();
int tokenType = Token.LATIN;
Token token = new Token();
+ boolean firstname;
// Go through the input, create a new token when
// a. Token type changed
@@ -156,7 +219,7 @@ public class HanziToPinyin {
addToken(sb, tokens, tokenType);
}
} else {
- tokenize(character, token);
+ tokenize(character, token, i == 0);
if (token.type == Token.PINYIN) {
if (sb.length() > 0) {
addToken(sb, tokens, tokenType);
diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java
index 598a4a0..060f87e 100644
--- a/src/com/android/providers/contacts/LegacyApiSupport.java
+++ b/src/com/android/providers/contacts/LegacyApiSupport.java
@@ -1937,8 +1937,7 @@ public class LegacyApiSupport {
sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
} else {
- sb.append(RawContacts.ACCOUNT_NAME + " IS NULL" +
- " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL");
+ sb.append("1=1");
}
}
@@ -1955,8 +1954,10 @@ public class LegacyApiSupport {
sb.append(" AND " + Groups.ACCOUNT_TYPE + "=");
DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
} else {
- sb.append(Groups.ACCOUNT_NAME + " IS NULL" +
- " AND " + Groups.ACCOUNT_TYPE + " IS NULL");
+ sb.append(Groups.ACCOUNT_NAME + " = '"
+ + AccountWithDataSet.LOCAL.getAccountName() + "' AND "
+ + Groups.ACCOUNT_TYPE + " = '"
+ + AccountWithDataSet.LOCAL.getAccountType() + "'");
}
}
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
index 5372bbd..133a508 100644
--- a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
+++ b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
@@ -19,6 +19,8 @@ package com.android.providers.contacts.aggregation;
import static com.android.providers.contacts.aggregation.util.RawContactMatcher.SCORE_THRESHOLD_PRIMARY;
import static com.android.providers.contacts.aggregation.util.RawContactMatcher.SCORE_THRESHOLD_SECONDARY;
import static com.android.providers.contacts.aggregation.util.RawContactMatcher.SCORE_THRESHOLD_SUGGEST;
+
+import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.provider.ContactsContract.AggregationExceptions;
@@ -71,6 +73,10 @@ public class ContactAggregator2 extends AbstractContactAggregator {
private final RawContactMatcher mMatcher = new RawContactMatcher();
+ private static final String ACTION_CONTACTS_AUTO_MERGE =
+ "com.android.contacts.incall.CONTACTS_AUTO_MERGE";
+ private static final String CONTACT_AUTO_MERGE_KEY_RAW_IDS = "RAW_IDS";
+
/**
* Constructor.
*/
@@ -367,6 +373,11 @@ public class ContactAggregator2 extends AbstractContactAggregator {
if (VERBOSE_LOGGING) {
Log.v(TAG, "Aggregating rids=" + connectedRawContactIds);
}
+ // metrics
+ Intent intent = new Intent(ACTION_CONTACTS_AUTO_MERGE);
+ intent.putExtra(CONTACT_AUTO_MERGE_KEY_RAW_IDS, connectedRids);
+ mContactsProvider.getContext().sendBroadcast(intent);
+
markAggregated(db, connectedRids);
for (Long cid : cidsNeedToBeUpdated) {
diff --git a/src/com/android/providers/contacts/util/PreloadedContactsFileParser.java b/src/com/android/providers/contacts/util/PreloadedContactsFileParser.java
new file mode 100644
index 0000000..ad9d0c0
--- /dev/null
+++ b/src/com/android/providers/contacts/util/PreloadedContactsFileParser.java
@@ -0,0 +1,225 @@
+package com.android.providers.contacts.util;
+
+import android.content.ContentProviderOperation;
+import android.provider.ContactsContract;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.providers.contacts.Constants;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Responsible for parsing the preloaded_contacts.json file and helping generate database commands
+ * to persist that information.
+ *
+ * Details about the json schema and encoding specification for the properties can be found in
+ * the preloaded_contacts_schema.json file under 'res/raw'
+ */
+public class PreloadedContactsFileParser {
+
+ private static final String TAG = "PreloadContacts";
+
+ private static String TOKEN_AT = "@";
+ private static String TOKEN_AT_SUB = "android.provider.ContactsContract$CommonDataKinds";
+ private static String TOKEN_CONTACTS_ROOT = "contacts";
+ private static String TOKEN_CONTACT_DATA = "data";
+ private static String TOKEN_MIMETYPE = "@mimetype";
+ private static String TOKEN_MIMETYPE_SUB = "android.provider.ContactsContract$Data.MIMETYPE";
+
+ private static Character CLASS_NAME_DELIMITER = '.';
+
+ private static Pattern mExpressionPattern = Pattern.compile("\\{\\{(.+?)\\}\\}");
+
+ private boolean mDebug;
+ private JSONObject mJsonRoot;
+ private HashMap<String,String> mResolvedNameCache;
+
+ public PreloadedContactsFileParser(InputStream inputStream) throws JSONException {
+ mDebug = Log.isLoggable(Constants.TAG_DEBUG_PRELOAD_CONTACTS, Log.DEBUG);
+ String jsonString = convertInputStreamToString(inputStream);
+ mJsonRoot = new JSONObject(jsonString);
+ mResolvedNameCache = new HashMap<String, String>();
+ }
+
+ private String convertInputStreamToString(InputStream is) {
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+
+ String line = null;
+ try {
+ while ((line = br.readLine()) != null) {
+ sb.append(line).append('\n');
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Parses the json object and creates the necessary {@link ContentProviderOperation}s to
+ * construct the contacts specified
+ */
+ public ArrayList<ContentProviderOperation> parseForContacts() {
+ try {
+ ArrayList<ContentProviderOperation> cpOps = new ArrayList<ContentProviderOperation>();
+ JSONArray contacts = mJsonRoot.getJSONArray(TOKEN_CONTACTS_ROOT);
+ int numContacts = contacts.length();
+
+ for (int i = 0; i < numContacts; ++i) {
+ JSONArray contactData = contacts.getJSONObject(i).getJSONArray(TOKEN_CONTACT_DATA);
+ int rawEntries = contactData.length();
+
+ // create a new raw contact entry
+ cpOps.add(
+ ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
+ .build() );
+ int cvBackRef = cpOps.size() - 1;
+
+ for (int j = 0; j < rawEntries; ++j) {
+ JSONObject rawEntry = contactData.getJSONObject(j);
+ Iterator<String> keys = rawEntry.keys();
+
+ // build a ContentProviderOperation to add the contact's raw entry
+ ContentProviderOperation.Builder cpoBuilder =
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
+ cpoBuilder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,
+ cvBackRef);
+
+ while (keys.hasNext()) {
+ String key = keys.next();
+ String value = rawEntry.getString(key);
+
+ if (mDebug) {
+ Log.d(TAG, "parsing property : " + key);
+ Log.d(TAG, "parsing property value : " + value);
+ }
+
+ String resolvedKey = null;
+ // keys always need interpolation
+ resolvedKey = resolvePropertyName(key);
+ // determine if the property is an expression that need to be evaluated
+ String resolvedValue = value;
+ Matcher matcher = mExpressionPattern.matcher(value);
+ if (matcher.matches()) {
+ matcher.reset();
+ matcher.find();
+ resolvedValue = resolvePropertyName(matcher.group(1));
+ }
+
+ if (mDebug) {
+ Log.d(TAG, "resolved property name : " + resolvedKey);
+ Log.d(TAG, "resolved property value : " + resolvedValue);
+ }
+
+ if (TextUtils.isEmpty(resolvedKey) || TextUtils.isEmpty(resolvedValue)) {
+ // don't persist this raw_contact value
+ continue;
+ } else {
+ cpoBuilder.withValue(resolvedKey, resolvedValue);
+ }
+
+ }
+
+ cpOps.add(cpoBuilder.build());
+ }
+
+ }
+ return cpOps;
+
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * parses an object's property name to determine its codified value
+ */
+ private String resolvePropertyName(String encodedName) {
+ if (TextUtils.isEmpty(encodedName)) {
+ return null;
+ }
+
+ if (mResolvedNameCache.containsKey(encodedName)) {
+ return mResolvedNameCache.get(encodedName);
+ }
+
+ String unwrappedName = encodedName;
+ // check if any substitution rules apply
+ if (TextUtils.equals(TOKEN_MIMETYPE, encodedName)) {
+ unwrappedName = TOKEN_MIMETYPE_SUB;
+ } else if (encodedName.startsWith(TOKEN_AT)) {
+ unwrappedName = encodedName.replace(TOKEN_AT, TOKEN_AT_SUB);
+ }
+
+ if (mDebug) {
+ Log.d(TAG, "encoded property name : " + encodedName);
+ Log.d(TAG, "resolved property name : " + unwrappedName);
+ }
+
+ String resolvedName = resolveCodifiedName(unwrappedName);
+ mResolvedNameCache.put(encodedName, resolvedName);
+ return resolvedName;
+ }
+
+ /**
+ * returns the string-ified value of the Java field the property name points to
+ */
+ private String resolveCodifiedName(String absoluteName) {
+ int delimiterIndex = TextUtils.lastIndexOf(absoluteName, CLASS_NAME_DELIMITER);
+ // ensure there is a field identifier to read
+ if (delimiterIndex == -1 || delimiterIndex >= absoluteName.length() - 1) {
+ return null;
+ }
+
+ String className = TextUtils.substring(absoluteName, 0, delimiterIndex);
+ String fieldName = absoluteName.substring(delimiterIndex + 1);
+
+ if (mDebug) {
+ Log.d(TAG, "property's class : " + className);
+ Log.d(TAG, "property's field : " + fieldName);
+ }
+
+ try {
+ Class clazz = Class.forName(className);
+ Field field = clazz.getField(fieldName);
+ String fieldValue = field.get(clazz).toString();
+ if (mDebug) {
+ Log.d(TAG, "fully resolved property name : " + fieldValue);
+ }
+ return fieldValue;
+
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+}
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index 189483d..1febe40 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -188,7 +188,7 @@ public class CallLogProviderTest extends BaseContactsProvider2Test {
Uri uri = Calls.addCall(ci, getMockContext(), "1-800-263-7643",
PhoneConstants.PRESENTATION_ALLOWED, Calls.OUTGOING_TYPE, 0, subscription, 2000,
- 40, null);
+ 40, null, null);
ContentValues values = new ContentValues();
values.put(Calls.TYPE, Calls.OUTGOING_TYPE);