() {
+ @Override
+ public CharSequence call() {
+ return view.getText();
+ }
+ });
+ }
+
+ // TODO: Move this class and the appropriate documentation into a test library, having checked
+ // first to see if exactly this code already exists or not.
+ /**
+ * Execute a callable on the ui thread, returning its result synchronously.
+ *
+ * Waits for an idle sync on the main thread (see {@link Instrumentation#waitForIdle(Runnable)})
+ * before executing this callable.
+ */
+ public T runOnUiThreadAndGetTheResult(Callable callable) throws Throwable {
+ FutureTask future = new FutureTask(callable);
+ mInstrumentation.waitForIdle(future);
+ try {
+ return future.get();
+ } catch (ExecutionException e) {
+ // Unwrap the cause of the exception and re-throw it.
+ throw e.getCause();
+ }
+ }
+
+ /**
+ * Wake up the screen, useful in tests that want or need the screen to be on.
+ *
+ * This is usually called from setUp() for tests that require it. After calling this method,
+ * {@link #releaseScreenWakeLock()} must be called, this is usually done from tearDown().
+ */
+ public void acquireScreenWakeLock(Context context) {
+ synchronized (mLock) {
+ Preconditions.checkState(mWakeLock == null, "mWakeLock was already held");
+ mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
+ .newWakeLock(
+ PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE | PowerManager.FULL_WAKE_LOCK, TAG);
+ mWakeLock.acquire();
+ }
+ }
+
+ /** Release the wake lock previously acquired with {@link #acquireScreenWakeLock(Context)}. */
+ public void releaseScreenWakeLock() {
+ synchronized (mLock) {
+ // We don't use Preconditions to force you to have acquired before release.
+ // This is because we don't want unnecessary exceptions in tearDown() since they'll
+ // typically mask the actual exception that happened during the test.
+ // The other reason is that this method is most likely to be called from tearDown(),
+ // which is invoked within a finally block, so it's not infrequently the case that
+ // the setUp() method fails before getting the lock, at which point we don't want
+ // to fail in tearDown().
+ if (mWakeLock != null) {
+ mWakeLock.release();
+ mWakeLock = null;
+ }
+ }
+ }
+
+ /**
+ * Gets all {@link TextView} objects whose {@link TextView#getText()} contains the given text as
+ * a substring.
+ */
+ public List getTextViewsWithString(final Activity activity, final String text)
+ throws Throwable {
+ return getTextViewsWithString(getRootView(activity), text);
+ }
+
+ /**
+ * Gets all {@link TextView} objects whose {@link TextView#getText()} contains the given text as
+ * a substring for the given root view.
+ */
+ public List getTextViewsWithString(final View rootView, final String text)
+ throws Throwable {
+ return runOnUiThreadAndGetTheResult(new Callable>() {
+ @Override
+ public List call() throws Exception {
+ List matchingViews = new ArrayList();
+ for (TextView textView : getAllViews(TextView.class, rootView)) {
+ if (textView.getText().toString().contains(text)) {
+ matchingViews.add(textView);
+ }
+ }
+ return matchingViews;
+ }
+ });
+ }
+
+ /** Find the root view for a given activity. */
+ public static View getRootView(Activity activity) {
+ return activity.findViewById(android.R.id.content).getRootView();
+ }
+
+ /**
+ * Gets a list of all views of a given type, rooted at the given parent.
+ *
+ * This method will recurse down through all {@link ViewGroup} instances looking for
+ * {@link View} instances of the supplied class type. Specifically it will use the
+ * {@link Class#isAssignableFrom(Class)} method as the test for which views to add to the list,
+ * so if you provide {@code View.class} as your type, you will get every view. The parent itself
+ * will be included also, should it be of the right type.
+ *
+ * This call manipulates the ui, and as such should only be called from the application's main
+ * thread.
+ */
+ private static List getAllViews(final Class clazz, final View parent) {
+ List results = new ArrayList();
+ if (parent.getClass().equals(clazz)) {
+ results.add(clazz.cast(parent));
+ }
+ if (parent instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) parent;
+ for (int i = 0; i < viewGroup.getChildCount(); ++i) {
+ results.addAll(getAllViews(clazz, viewGroup.getChildAt(i)));
+ }
+ }
+ return results;
+ }
+}
diff --git a/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/ContactsMockContext.java b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/ContactsMockContext.java
new file mode 100644
index 0000000..ebce6a0
--- /dev/null
+++ b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/ContactsMockContext.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.test.mocks;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.provider.ContactsContract;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+/**
+ * A mock context for contacts unit tests. Forwards everything to
+ * a supplied context, except content resolver operations, which are sent
+ * to mock content providers.
+ */
+public class ContactsMockContext extends ContextWrapper {
+ private ContactsMockPackageManager mPackageManager;
+ private MockContentResolver mContentResolver;
+ private MockContentProvider mContactsProvider;
+ private MockContentProvider mSettingsProvider;
+ private Intent mIntentForStartActivity;
+
+ public ContactsMockContext(Context base) {
+ super(base);
+ mPackageManager = new ContactsMockPackageManager();
+ mContentResolver = new MockContentResolver();
+ mContactsProvider = new MockContentProvider();
+ mContentResolver.addProvider(ContactsContract.AUTHORITY, mContactsProvider);
+ final ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = ContactsContract.AUTHORITY;
+ mContactsProvider.attachInfo(this, providerInfo);
+ mSettingsProvider = new MockContentProvider();
+ mSettingsProvider.attachInfo(this, providerInfo);
+ mContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ public MockContentProvider getContactsProvider() {
+ return mContactsProvider;
+ }
+
+ public MockContentProvider getSettingsProvider() {
+ return mSettingsProvider;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ /**
+ * Instead of actually sending Intent, this method just remembers what Intent was supplied last.
+ * You can check the content via {@link #getIntentForStartActivity()} for verification.
+ */
+ @Override
+ public void startActivity(Intent intent) {
+ mIntentForStartActivity = intent;
+ }
+
+ public Intent getIntentForStartActivity() {
+ return mIntentForStartActivity;
+ }
+
+ public void verify() {
+ mContactsProvider.verify();
+ mSettingsProvider.verify();
+ }
+
+}
diff --git a/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/ContactsMockPackageManager.java b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/ContactsMockPackageManager.java
new file mode 100644
index 0000000..a1557ff
--- /dev/null
+++ b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/ContactsMockPackageManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.test.mocks;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.test.mock.MockPackageManager;
+
+/**
+ */
+public class ContactsMockPackageManager extends MockPackageManager {
+ public ContactsMockPackageManager() {
+ }
+
+ @Override
+ public Drawable getActivityLogo(ComponentName activityName) throws NameNotFoundException {
+ return new ColorDrawable();
+ }
+
+ @Override
+ public Drawable getActivityIcon(ComponentName activityName) {
+ return new ColorDrawable();
+ }
+
+ @Override
+ public Drawable getDrawable(String packageName, int resid, ApplicationInfo appInfo) {
+ // TODO: make programmable
+ return new ColorDrawable();
+ }
+}
diff --git a/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
new file mode 100644
index 0000000..ab2d395
--- /dev/null
+++ b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.test.mocks;
+
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountTypeWithDataSet;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.BaseAccountType;
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A mock {@link AccountTypeManager} class.
+ */
+public class MockAccountTypeManager extends AccountTypeManager {
+
+ public AccountType[] mTypes;
+ public AccountWithDataSet[] mAccounts;
+
+ public MockAccountTypeManager(AccountType[] types, AccountWithDataSet[] accounts) {
+ this.mTypes = types;
+ this.mAccounts = accounts;
+ }
+
+ @Override
+ public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
+ // Add fallback accountType to mimic the behavior of AccountTypeManagerImpl
+ AccountType mFallbackAccountType = new BaseAccountType() {
+ @Override
+ public boolean areContactsWritable() {
+ return false;
+ }
+ };
+ mFallbackAccountType.accountType = "fallback";
+ for (AccountType type : mTypes) {
+ if (Objects.equal(accountTypeWithDataSet.accountType, type.accountType)
+ && Objects.equal(accountTypeWithDataSet.dataSet, type.dataSet)) {
+ return type;
+ }
+ }
+ return mFallbackAccountType;
+ }
+
+ @Override
+ public List getAccounts(boolean writableOnly) {
+ return Arrays.asList(mAccounts);
+ }
+
+ @Override
+ public List getGroupWritableAccounts() {
+ return Arrays.asList(mAccounts);
+ }
+
+ @Override
+ public Map getUsableInvitableAccountTypes() {
+ return Maps.newHashMap(); // Always returns empty
+ }
+
+ @Override
+ public List getAccountTypes(boolean writableOnly) {
+ final List ret = Lists.newArrayList();
+ synchronized (this) {
+ for (AccountType type : mTypes) {
+ if (!writableOnly || type.areContactsWritable()) {
+ ret.add(type);
+ }
+ }
+ }
+ return ret;
+ }
+}
diff --git a/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java
new file mode 100644
index 0000000..db8f06f
--- /dev/null
+++ b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.test.mocks;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.contacts.common.ContactPhotoManager;
+
+/**
+ * A photo preloader that always uses the "no contact" picture and never executes any real
+ * db queries
+ */
+public class MockContactPhotoManager extends ContactPhotoManager {
+ @Override
+ public void loadThumbnail(ImageView view, long photoId, boolean darkTheme, boolean isCircular,
+ DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
+ defaultProvider.applyDefaultImage(view, -1, darkTheme, null);
+ }
+
+ @Override
+ public void loadPhoto(ImageView view, Uri photoUri, int requestedExtent, boolean darkTheme,
+ boolean isCircular, DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider) {
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, null);
+ }
+
+ @Override
+ public void removePhoto(ImageView view) {
+ view.setImageDrawable(null);
+ }
+
+ @Override
+ public void cancelPendingRequests(View fragmentRootView) {
+ }
+
+ @Override
+ public void pause() {
+ }
+
+ @Override
+ public void resume() {
+ }
+
+ @Override
+ public void refreshCache() {
+ }
+
+ @Override
+ public void cacheBitmap(Uri photoUri, Bitmap bitmap, byte[] photoBytes) {
+ }
+
+ @Override
+ public void preloadPhotosInBackground() {
+ }
+}
diff --git a/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockContentProvider.java b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockContentProvider.java
new file mode 100644
index 0000000..686292a
--- /dev/null
+++ b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockContentProvider.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.test.mocks;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A programmable mock content provider.
+ */
+public class MockContentProvider extends ContentProvider {
+ private static final String TAG = "MockContentProvider";
+
+ public static class Query {
+
+ private final Uri mUri;
+ private String[] mProjection;
+ private String[] mDefaultProjection;
+ private String mSelection;
+ private String[] mSelectionArgs;
+ private String mSortOrder;
+ private ArrayList mRows = new ArrayList();
+ private boolean mAnyProjection;
+ private boolean mAnySelection;
+ private boolean mAnySortOrder;
+ private boolean mAnyNumberOfTimes;
+
+ private boolean mExecuted;
+
+ public Query(Uri uri) {
+ mUri = uri;
+ }
+
+ @Override
+ public String toString() {
+ return queryToString(mUri, mProjection, mSelection, mSelectionArgs, mSortOrder);
+ }
+
+ public Query withProjection(String... projection) {
+ mProjection = projection;
+ return this;
+ }
+
+ public Query withDefaultProjection(String... projection) {
+ mDefaultProjection = projection;
+ return this;
+ }
+
+ public Query withAnyProjection() {
+ mAnyProjection = true;
+ return this;
+ }
+
+ public Query withSelection(String selection, String... selectionArgs) {
+ mSelection = selection;
+ mSelectionArgs = selectionArgs;
+ return this;
+ }
+
+ public Query withAnySelection() {
+ mAnySelection = true;
+ return this;
+ }
+
+ public Query withSortOrder(String sortOrder) {
+ mSortOrder = sortOrder;
+ return this;
+ }
+
+ public Query withAnySortOrder() {
+ mAnySortOrder = true;
+ return this;
+ }
+
+ public Query returnRow(ContentValues values) {
+ mRows.add(values);
+ return this;
+ }
+
+ public Query returnRow(Object... row) {
+ mRows.add(row);
+ return this;
+ }
+
+ public Query returnEmptyCursor() {
+ mRows.clear();
+ return this;
+ }
+
+ public Query anyNumberOfTimes() {
+ mAnyNumberOfTimes = true;
+ return this;
+ }
+
+ public boolean equals(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ if (!uri.equals(mUri)) {
+ return false;
+ }
+
+ if (!mAnyProjection && !equals(projection, mProjection)) {
+ return false;
+ }
+
+ if (!mAnySelection && !equals(selection, mSelection)) {
+ return false;
+ }
+
+ if (!mAnySelection && !equals(selectionArgs, mSelectionArgs)) {
+ return false;
+ }
+
+ if (!mAnySortOrder && !equals(sortOrder, mSortOrder)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean equals(String string1, String string2) {
+ if (TextUtils.isEmpty(string1)) {
+ string1 = null;
+ }
+ if (TextUtils.isEmpty(string2)) {
+ string2 = null;
+ }
+ return TextUtils.equals(string1, string2);
+ }
+
+ private static boolean equals(String[] array1, String[] array2) {
+ boolean empty1 = array1 == null || array1.length == 0;
+ boolean empty2 = array2 == null || array2.length == 0;
+ if (empty1 && empty2) {
+ return true;
+ }
+ if (empty1 != empty2 && (empty1 || empty2)) {
+ return false;
+ }
+
+ if (array1.length != array2.length) return false;
+
+ for (int i = 0; i < array1.length; i++) {
+ if (!array1[i].equals(array2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Cursor getResult(String[] projection) {
+ String[] columnNames;
+ if (mAnyProjection) {
+ columnNames = projection;
+ } else {
+ columnNames = mProjection != null ? mProjection : mDefaultProjection;
+ }
+
+ MatrixCursor cursor = new MatrixCursor(columnNames);
+ for (Object row : mRows) {
+ if (row instanceof Object[]) {
+ cursor.addRow((Object[]) row);
+ } else {
+ ContentValues values = (ContentValues) row;
+ Object[] columns = new Object[projection.length];
+ for (int i = 0; i < projection.length; i++) {
+ columns[i] = values.get(projection[i]);
+ }
+ cursor.addRow(columns);
+ }
+ }
+ return cursor;
+ }
+ }
+
+ public static class TypeQuery {
+ private final Uri mUri;
+ private final String mType;
+
+ public TypeQuery(Uri uri, String type) {
+ mUri = uri;
+ mType = type;
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ @Override
+ public String toString() {
+ return mUri + " --> " + mType;
+ }
+
+ public boolean equals(Uri uri) {
+ return getUri().equals(uri);
+ }
+ }
+
+ private ArrayList mExpectedQueries = new ArrayList();
+ private HashMap mExpectedTypeQueries = Maps.newHashMap();
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ public Query expectQuery(Uri contentUri) {
+ Query query = new Query(contentUri);
+ mExpectedQueries.add(query);
+ return query;
+ }
+
+ public void expectTypeQuery(Uri uri, String type) {
+ mExpectedTypeQueries.put(uri, type);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+
+ for (Iterator iterator = mExpectedQueries.iterator(); iterator.hasNext();) {
+ Query query = iterator.next();
+ if (query.equals(uri, projection, selection, selectionArgs, sortOrder)) {
+ query.mExecuted = true;
+ if (!query.mAnyNumberOfTimes) {
+ iterator.remove();
+ }
+ return query.getResult(projection);
+ }
+ }
+
+ if (mExpectedQueries.isEmpty()) {
+ Assert.fail("Unexpected query: "
+ + queryToString(uri, projection, selection, selectionArgs, sortOrder));
+ } else {
+ StringBuilder sb = new StringBuilder();
+ sb.append(mExpectedQueries.get(0));
+ for (int i = 1; i < mExpectedQueries.size(); i++) {
+ sb.append("\n ").append(mExpectedQueries.get(i));
+ }
+ Assert.fail("Incorrect query.\n Expected: " + sb + "\n Actual: " +
+ queryToString(uri, projection, selection, selectionArgs, sortOrder));
+ }
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ if (mExpectedTypeQueries.isEmpty()) {
+ Assert.fail("Unexpected getType query: " + uri);
+ }
+
+ String mimeType = mExpectedTypeQueries.get(uri);
+ if (mimeType != null) {
+ return mimeType;
+ }
+
+ Assert.fail("Unknown mime type for: " + uri);
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static String queryToString(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(uri).append(" ");
+ if (projection != null) {
+ sb.append(Arrays.toString(projection));
+ } else {
+ sb.append("[]");
+ }
+ if (selection != null) {
+ sb.append(" selection: '").append(selection).append("'");
+ if (selectionArgs != null) {
+ sb.append(Arrays.toString(selectionArgs));
+ } else {
+ sb.append("[]");
+ }
+ }
+ if (sortOrder != null) {
+ sb.append(" sort: '").append(sortOrder).append("'");
+ }
+ return sb.toString();
+ }
+
+ public void verify() {
+ ArrayList mMissedQueries = Lists.newArrayList();
+ for (Query query : mExpectedQueries) {
+ if (!query.mExecuted) {
+ mMissedQueries.add(query);
+ }
+ }
+ Assert.assertTrue("Not all expected queries have been called: " +
+ mMissedQueries, mMissedQueries.isEmpty());
+ }
+}
diff --git a/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockSharedPreferences.java b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockSharedPreferences.java
new file mode 100644
index 0000000..13d035e
--- /dev/null
+++ b/ContactsCommon/TestCommon/src/com/android/contacts/common/test/mocks/MockSharedPreferences.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.test.mocks;
+
+import android.content.SharedPreferences;
+
+import com.google.common.collect.Maps;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * A programmable mock content provider.
+ */
+public class MockSharedPreferences implements SharedPreferences, SharedPreferences.Editor {
+
+ private HashMap mValues = Maps.newHashMap();
+ private HashMap mTempValues = Maps.newHashMap();
+
+ public Editor edit() {
+ return this;
+ }
+
+ public boolean contains(String key) {
+ return mValues.containsKey(key);
+ }
+
+ public Map getAll() {
+ return new HashMap(mValues);
+ }
+
+ public boolean getBoolean(String key, boolean defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Boolean)mValues.get(key)).booleanValue();
+ }
+ return defValue;
+ }
+
+ public float getFloat(String key, float defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Float)mValues.get(key)).floatValue();
+ }
+ return defValue;
+ }
+
+ public int getInt(String key, int defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Integer)mValues.get(key)).intValue();
+ }
+ return defValue;
+ }
+
+ public long getLong(String key, long defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Long)mValues.get(key)).longValue();
+ }
+ return defValue;
+ }
+
+ public String getString(String key, String defValue) {
+ if (mValues.containsKey(key))
+ return (String)mValues.get(key);
+ return defValue;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set getStringSet(String key, Set defValues) {
+ if (mValues.containsKey(key)) {
+ return (Set) mValues.get(key);
+ }
+ return defValues;
+ }
+
+ public void registerOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unregisterOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Editor putBoolean(String key, boolean value) {
+ mTempValues.put(key, Boolean.valueOf(value));
+ return this;
+ }
+
+ public Editor putFloat(String key, float value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putInt(String key, int value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putLong(String key, long value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putString(String key, String value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putStringSet(String key, Set values) {
+ mTempValues.put(key, values);
+ return this;
+ }
+
+ public Editor remove(String key) {
+ mTempValues.remove(key);
+ return this;
+ }
+
+ public Editor clear() {
+ mTempValues.clear();
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean commit() {
+ mValues = (HashMap)mTempValues.clone();
+ return true;
+ }
+
+ public void apply() {
+ commit();
+ }
+}
diff --git a/ContactsCommon/build.gradle b/ContactsCommon/build.gradle
new file mode 100644
index 0000000..204eb35
--- /dev/null
+++ b/ContactsCommon/build.gradle
@@ -0,0 +1,33 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ minSdkVersion 11
+ targetSdkVersion 19
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ //HACK around R.java (import com.android.phone.common.R)
+ //java.srcDirs = ['src', 'TestCommon/src', 'build/generated/source/r/release' ]
+ java.srcDirs = ['src', 'TestCommon/src' ]
+ res.srcDirs = ['res']
+ }
+ }
+}
+
+dependencies {
+ compile 'com.android.support:support-v4:23.1.0'
+ compile 'com.googlecode.libphonenumber:libphonenumber-parent:7.2.1'
+ compile 'com.googlecode.libphonenumber:libphonenumber:7.2.1'
+ compile 'com.googlecode.libphonenumber:geocoder:2.31'
+ compile 'com.google.code.findbugs:jsr305:3.0.1'
+ compile 'com.google.guava:guava:18.0'
+ compile project(':vcard')
+ compile project(':ex:common')
+ compile project(':PhoneCommon')
+}
\ No newline at end of file
diff --git a/ContactsCommon/proguard.flags b/ContactsCommon/proguard.flags
new file mode 100644
index 0000000..cd9820a
--- /dev/null
+++ b/ContactsCommon/proguard.flags
@@ -0,0 +1,3 @@
+-keep class *
+
+-verbose
diff --git a/ContactsCommon/res/color/popup_menu_color.xml b/ContactsCommon/res/color/popup_menu_color.xml
new file mode 100644
index 0000000..b0984e6
--- /dev/null
+++ b/ContactsCommon/res/color/popup_menu_color.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/color/tab_text_color.xml b/ContactsCommon/res/color/tab_text_color.xml
new file mode 100644
index 0000000..c242019
--- /dev/null
+++ b/ContactsCommon/res/color/tab_text_color.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable-hdpi/ic_ab_search.png b/ContactsCommon/res/drawable-hdpi/ic_ab_search.png
new file mode 100644
index 0000000..d86b219
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_ab_search.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_arrow_back_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..ddbb2c4
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_arrow_back_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_business_white_120dp.png b/ContactsCommon/res/drawable-hdpi/ic_business_white_120dp.png
new file mode 100644
index 0000000..d5942dc
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_business_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_call_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_call_24dp.png
new file mode 100644
index 0000000..4dc5065
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_call_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_call_note_white_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_call_note_white_24dp.png
new file mode 100644
index 0000000..503e58e
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_call_note_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_call_voicemail_holo_dark.png b/ContactsCommon/res/drawable-hdpi/ic_call_voicemail_holo_dark.png
new file mode 100644
index 0000000..6d64a36
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_call_voicemail_holo_dark.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_close_dk.png b/ContactsCommon/res/drawable-hdpi/ic_close_dk.png
new file mode 100644
index 0000000..9695529
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_close_dk.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_group_white_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_group_white_24dp.png
new file mode 100644
index 0000000..017e4bb
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_group_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_history_white_drawable_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 0000000..703d30b
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_history_white_drawable_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_info_outline_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_info_outline_24dp.png
new file mode 100644
index 0000000..c7b1113
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_info_outline_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_back.png b/ContactsCommon/res/drawable-hdpi/ic_menu_back.png
new file mode 100644
index 0000000..deb3a6d
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_back.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_group_dk.png b/ContactsCommon/res/drawable-hdpi/ic_menu_group_dk.png
new file mode 100644
index 0000000..06bd18f
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_group_dk.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_group_lt.png b/ContactsCommon/res/drawable-hdpi/ic_menu_group_lt.png
new file mode 100644
index 0000000..d829d11
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_group_lt.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_overflow_lt.png b/ContactsCommon/res/drawable-hdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..1ba1295
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_overflow_lt.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_person_dk.png b/ContactsCommon/res/drawable-hdpi/ic_menu_person_dk.png
new file mode 100644
index 0000000..5ff3ac5
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_person_dk.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_person_lt.png b/ContactsCommon/res/drawable-hdpi/ic_menu_person_lt.png
new file mode 100644
index 0000000..b4ebfc7
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_person_lt.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_remove_field_holo_light.png b/ContactsCommon/res/drawable-hdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 0000000..03fd2fb
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_remove_field_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_settings_holo_light.png b/ContactsCommon/res/drawable-hdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..b7bb5c4
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_settings_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_star_dk.png b/ContactsCommon/res/drawable-hdpi/ic_menu_star_dk.png
new file mode 100644
index 0000000..e8cb0f5
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_star_dk.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_star_holo_light.png b/ContactsCommon/res/drawable-hdpi/ic_menu_star_holo_light.png
new file mode 100644
index 0000000..4513796
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_star_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_menu_star_lt.png b/ContactsCommon/res/drawable-hdpi/ic_menu_star_lt.png
new file mode 100644
index 0000000..1c9bb81
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_menu_star_lt.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_message_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_message_24dp.png
new file mode 100644
index 0000000..57177b7
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_message_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_overflow_menu.png b/ContactsCommon/res/drawable-hdpi/ic_overflow_menu.png
new file mode 100644
index 0000000..262e9df
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_overflow_menu.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_person_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_person_24dp.png
new file mode 100644
index 0000000..56708b0
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_person_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_person_add_24dp.png b/ContactsCommon/res/drawable-hdpi/ic_person_add_24dp.png
new file mode 100644
index 0000000..10ae5a7
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_person_add_24dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_person_white_120dp.png b/ContactsCommon/res/drawable-hdpi/ic_person_white_120dp.png
new file mode 100644
index 0000000..69e6f98
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_person_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_rx_videocam.png b/ContactsCommon/res/drawable-hdpi/ic_rx_videocam.png
new file mode 100644
index 0000000..ccdda67
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_rx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_scroll_handle.png b/ContactsCommon/res/drawable-hdpi/ic_scroll_handle.png
new file mode 100644
index 0000000..3aa29b8
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_scroll_handle.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_tx_videocam.png b/ContactsCommon/res/drawable-hdpi/ic_tx_videocam.png
new file mode 100644
index 0000000..603ddc8
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_tx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_videocam.png b/ContactsCommon/res/drawable-hdpi/ic_videocam.png
new file mode 100644
index 0000000..97905c9
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_videocam.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/ic_voicemail_avatar.png b/ContactsCommon/res/drawable-hdpi/ic_voicemail_avatar.png
new file mode 100644
index 0000000..2fb7826
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/ic_voicemail_avatar.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-hdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..4ea7afa
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/list_background_holo.9.png b/ContactsCommon/res/drawable-hdpi/list_background_holo.9.png
new file mode 100644
index 0000000..cddf9be
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/list_background_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/list_focused_holo.9.png b/ContactsCommon/res/drawable-hdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..86578be
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/list_focused_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/list_longpressed_holo_light.9.png b/ContactsCommon/res/drawable-hdpi/list_longpressed_holo_light.9.png
new file mode 100644
index 0000000..e9afcc9
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/list_longpressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/list_pressed_holo_light.9.png b/ContactsCommon/res/drawable-hdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..2054530
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/list_pressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/list_section_divider_holo_custom.9.png b/ContactsCommon/res/drawable-hdpi/list_section_divider_holo_custom.9.png
new file mode 100644
index 0000000..a0f1756
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/list_section_divider_holo_custom.9.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/list_title_holo.9.png b/ContactsCommon/res/drawable-hdpi/list_title_holo.9.png
new file mode 100644
index 0000000..ae93717
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/list_title_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-hdpi/unknown_source.png b/ContactsCommon/res/drawable-hdpi/unknown_source.png
new file mode 100644
index 0000000..0a8f37d
Binary files /dev/null and b/ContactsCommon/res/drawable-hdpi/unknown_source.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-hdpi/ic_menu_settings_holo_light.png b/ContactsCommon/res/drawable-ldrtl-hdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..c3d86ca
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-hdpi/ic_menu_settings_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-hdpi/list_background_holo.9.png b/ContactsCommon/res/drawable-ldrtl-hdpi/list_background_holo.9.png
new file mode 100644
index 0000000..0d80482
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-hdpi/list_background_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-hdpi/list_focused_holo.9.png b/ContactsCommon/res/drawable-ldrtl-hdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..4139942
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-hdpi/list_focused_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png b/ContactsCommon/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png
new file mode 100644
index 0000000..569d28f
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-hdpi/list_title_holo.9.png b/ContactsCommon/res/drawable-ldrtl-hdpi/list_title_holo.9.png
new file mode 100644
index 0000000..5ec4c96
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-hdpi/list_title_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-mdpi/ic_menu_settings_holo_light.png b/ContactsCommon/res/drawable-ldrtl-mdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..ac172b2
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-mdpi/ic_menu_settings_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-mdpi/list_background_holo.9.png b/ContactsCommon/res/drawable-ldrtl-mdpi/list_background_holo.9.png
new file mode 100644
index 0000000..d86d611
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-mdpi/list_background_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-mdpi/list_focused_holo.9.png b/ContactsCommon/res/drawable-ldrtl-mdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..4139942
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-mdpi/list_focused_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png b/ContactsCommon/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png
new file mode 100644
index 0000000..065ff62
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-mdpi/list_title_holo.9.png b/ContactsCommon/res/drawable-ldrtl-mdpi/list_title_holo.9.png
new file mode 100644
index 0000000..013d5e7
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-mdpi/list_title_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..947f03c
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..6d09d72
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..63c7456
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-xhdpi/ic_menu_settings_holo_light.png b/ContactsCommon/res/drawable-ldrtl-xhdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..d3a334a
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-xhdpi/ic_menu_settings_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-xhdpi/list_background_holo.9.png b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_background_holo.9.png
new file mode 100644
index 0000000..f709f2c
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_background_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..4139942
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png
new file mode 100644
index 0000000..af58554
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png differ
diff --git a/ContactsCommon/res/drawable-ldrtl-xhdpi/list_title_holo.9.png b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_title_holo.9.png
new file mode 100644
index 0000000..cb801ac
Binary files /dev/null and b/ContactsCommon/res/drawable-ldrtl-xhdpi/list_title_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_ab_search.png b/ContactsCommon/res/drawable-mdpi/ic_ab_search.png
new file mode 100644
index 0000000..2b23b1e
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_ab_search.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_arrow_back_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..1a21fb4
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_arrow_back_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_business_white_120dp.png b/ContactsCommon/res/drawable-mdpi/ic_business_white_120dp.png
new file mode 100644
index 0000000..3dddca5
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_business_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_call_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_call_24dp.png
new file mode 100644
index 0000000..77f9de5
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_call_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_call_note_white_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_call_note_white_24dp.png
new file mode 100644
index 0000000..9d359db
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_call_note_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_call_voicemail_holo_dark.png b/ContactsCommon/res/drawable-mdpi/ic_call_voicemail_holo_dark.png
new file mode 100644
index 0000000..bf6d006
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_call_voicemail_holo_dark.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_close_dk.png b/ContactsCommon/res/drawable-mdpi/ic_close_dk.png
new file mode 100644
index 0000000..590a728
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_close_dk.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_group_white_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_group_white_24dp.png
new file mode 100644
index 0000000..ad268bf
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_group_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_history_white_drawable_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 0000000..b3000d3
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_history_white_drawable_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_info_outline_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_info_outline_24dp.png
new file mode 100644
index 0000000..353e064
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_info_outline_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_back.png b/ContactsCommon/res/drawable-mdpi/ic_menu_back.png
new file mode 100644
index 0000000..201ad40
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_back.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_group_dk.png b/ContactsCommon/res/drawable-mdpi/ic_menu_group_dk.png
new file mode 100644
index 0000000..9aac6d7
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_group_dk.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_group_lt.png b/ContactsCommon/res/drawable-mdpi/ic_menu_group_lt.png
new file mode 100644
index 0000000..39c16ed
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_group_lt.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_overflow_lt.png b/ContactsCommon/res/drawable-mdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..8415096
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_overflow_lt.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_person_dk.png b/ContactsCommon/res/drawable-mdpi/ic_menu_person_dk.png
new file mode 100644
index 0000000..b8fc39a
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_person_dk.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_person_lt.png b/ContactsCommon/res/drawable-mdpi/ic_menu_person_lt.png
new file mode 100644
index 0000000..736dfd6
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_person_lt.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_remove_field_holo_light.png b/ContactsCommon/res/drawable-mdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 0000000..8c44e70
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_remove_field_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_settings_holo_light.png b/ContactsCommon/res/drawable-mdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..1ebc112
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_settings_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_star_dk.png b/ContactsCommon/res/drawable-mdpi/ic_menu_star_dk.png
new file mode 100644
index 0000000..c16c6c5
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_star_dk.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_star_holo_light.png b/ContactsCommon/res/drawable-mdpi/ic_menu_star_holo_light.png
new file mode 100644
index 0000000..8263b27
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_star_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_menu_star_lt.png b/ContactsCommon/res/drawable-mdpi/ic_menu_star_lt.png
new file mode 100644
index 0000000..c67170e
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_menu_star_lt.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_message_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_message_24dp.png
new file mode 100644
index 0000000..3072b75
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_message_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_overflow_menu.png b/ContactsCommon/res/drawable-mdpi/ic_overflow_menu.png
new file mode 100644
index 0000000..0e720dd
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_overflow_menu.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_person_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_person_24dp.png
new file mode 100644
index 0000000..f0b1c72
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_person_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_person_add_24dp.png b/ContactsCommon/res/drawable-mdpi/ic_person_add_24dp.png
new file mode 100644
index 0000000..38e0a28
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_person_add_24dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_person_white_120dp.png b/ContactsCommon/res/drawable-mdpi/ic_person_white_120dp.png
new file mode 100644
index 0000000..397d933
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_person_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_rx_videocam.png b/ContactsCommon/res/drawable-mdpi/ic_rx_videocam.png
new file mode 100755
index 0000000..1b43a07
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_rx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_scroll_handle.png b/ContactsCommon/res/drawable-mdpi/ic_scroll_handle.png
new file mode 100644
index 0000000..af75db4
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_scroll_handle.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_tx_videocam.png b/ContactsCommon/res/drawable-mdpi/ic_tx_videocam.png
new file mode 100755
index 0000000..64995d1
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_tx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_videocam.png b/ContactsCommon/res/drawable-mdpi/ic_videocam.png
new file mode 100644
index 0000000..dc9655b
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_videocam.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/ic_voicemail_avatar.png b/ContactsCommon/res/drawable-mdpi/ic_voicemail_avatar.png
new file mode 100644
index 0000000..4005f24
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/ic_voicemail_avatar.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-mdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..3bf8e03
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/list_background_holo.9.png b/ContactsCommon/res/drawable-mdpi/list_background_holo.9.png
new file mode 100644
index 0000000..7d5d66d
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/list_background_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/list_focused_holo.9.png b/ContactsCommon/res/drawable-mdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..86578be
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/list_focused_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/list_longpressed_holo_light.9.png b/ContactsCommon/res/drawable-mdpi/list_longpressed_holo_light.9.png
new file mode 100644
index 0000000..3226ab7
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/list_longpressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/list_pressed_holo_light.9.png b/ContactsCommon/res/drawable-mdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..061904c
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/list_pressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/list_section_divider_holo_custom.9.png b/ContactsCommon/res/drawable-mdpi/list_section_divider_holo_custom.9.png
new file mode 100644
index 0000000..1d9371d
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/list_section_divider_holo_custom.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/list_title_holo.9.png b/ContactsCommon/res/drawable-mdpi/list_title_holo.9.png
new file mode 100644
index 0000000..64bd691
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/list_title_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-mdpi/unknown_source.png b/ContactsCommon/res/drawable-mdpi/unknown_source.png
new file mode 100644
index 0000000..356748f
Binary files /dev/null and b/ContactsCommon/res/drawable-mdpi/unknown_source.png differ
diff --git a/ContactsCommon/res/drawable-sw600dp-hdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-sw600dp-hdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..046b24a
Binary files /dev/null and b/ContactsCommon/res/drawable-sw600dp-hdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-sw600dp-mdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-sw600dp-mdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..1ff3373
Binary files /dev/null and b/ContactsCommon/res/drawable-sw600dp-mdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..2eb7c7e
Binary files /dev/null and b/ContactsCommon/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_ab_search.png b/ContactsCommon/res/drawable-xhdpi/ic_ab_search.png
new file mode 100644
index 0000000..71f7827
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_ab_search.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_arrow_back_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..bb73272
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_arrow_back_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_business_white_120dp.png b/ContactsCommon/res/drawable-xhdpi/ic_business_white_120dp.png
new file mode 100644
index 0000000..6256300
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_business_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_call_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_call_24dp.png
new file mode 100644
index 0000000..ef45e93
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_call_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_call_note_white_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_call_note_white_24dp.png
new file mode 100644
index 0000000..40eed1d
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_call_note_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_call_voicemail_holo_dark.png b/ContactsCommon/res/drawable-xhdpi/ic_call_voicemail_holo_dark.png
new file mode 100644
index 0000000..d9684d1
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_call_voicemail_holo_dark.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_close_dk.png b/ContactsCommon/res/drawable-xhdpi/ic_close_dk.png
new file mode 100644
index 0000000..5769f11
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_close_dk.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_group_white_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_group_white_24dp.png
new file mode 100644
index 0000000..09c0e3e
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_group_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_history_white_drawable_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 0000000..e188d4a
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_history_white_drawable_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_info_outline_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_info_outline_24dp.png
new file mode 100644
index 0000000..c571b2e
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_info_outline_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_back.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_back.png
new file mode 100644
index 0000000..d2f7099
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_back.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_group_dk.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_group_dk.png
new file mode 100644
index 0000000..ce5f704
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_group_dk.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_group_lt.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_group_lt.png
new file mode 100644
index 0000000..3d0580f
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_group_lt.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_overflow_lt.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..f91b718
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_overflow_lt.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_person_dk.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_person_dk.png
new file mode 100644
index 0000000..2fbd458
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_person_dk.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_person_lt.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_person_lt.png
new file mode 100644
index 0000000..2cdb2d7
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_person_lt.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_remove_field_holo_light.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 0000000..65a6b7b
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_remove_field_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_settings_holo_light.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..68ba92b
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_settings_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_star_dk.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_star_dk.png
new file mode 100644
index 0000000..48483a0
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_star_dk.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_star_holo_light.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_star_holo_light.png
new file mode 100644
index 0000000..9067911
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_star_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_menu_star_lt.png b/ContactsCommon/res/drawable-xhdpi/ic_menu_star_lt.png
new file mode 100644
index 0000000..e053c75
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_menu_star_lt.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_message_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_message_24dp.png
new file mode 100644
index 0000000..763767b
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_message_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_overflow_menu.png b/ContactsCommon/res/drawable-xhdpi/ic_overflow_menu.png
new file mode 100644
index 0000000..9156076
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_overflow_menu.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_person_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_person_24dp.png
new file mode 100644
index 0000000..aea15f0
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_person_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_person_add_24dp.png b/ContactsCommon/res/drawable-xhdpi/ic_person_add_24dp.png
new file mode 100644
index 0000000..7e7c289
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_person_add_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_person_white_120dp.png b/ContactsCommon/res/drawable-xhdpi/ic_person_white_120dp.png
new file mode 100644
index 0000000..8d80a05
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_person_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_rx_videocam.png b/ContactsCommon/res/drawable-xhdpi/ic_rx_videocam.png
new file mode 100755
index 0000000..43319dc
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_rx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_scroll_handle.png b/ContactsCommon/res/drawable-xhdpi/ic_scroll_handle.png
new file mode 100644
index 0000000..2d43c4d
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_scroll_handle.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_tx_videocam.png b/ContactsCommon/res/drawable-xhdpi/ic_tx_videocam.png
new file mode 100755
index 0000000..d2671ed
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_tx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_videocam.png b/ContactsCommon/res/drawable-xhdpi/ic_videocam.png
new file mode 100644
index 0000000..c1783de
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/ic_voicemail_avatar.png b/ContactsCommon/res/drawable-xhdpi/ic_voicemail_avatar.png
new file mode 100644
index 0000000..f24505d
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/ic_voicemail_avatar.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-xhdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..eda10e6
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/list_background_holo.9.png b/ContactsCommon/res/drawable-xhdpi/list_background_holo.9.png
new file mode 100644
index 0000000..b652725
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/list_background_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/list_focused_holo.9.png b/ContactsCommon/res/drawable-xhdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..86578be
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/list_focused_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/list_longpressed_holo_light.9.png b/ContactsCommon/res/drawable-xhdpi/list_longpressed_holo_light.9.png
new file mode 100644
index 0000000..5532e88
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/list_longpressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/list_pressed_holo_light.9.png b/ContactsCommon/res/drawable-xhdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..f4af926
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/list_pressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/list_section_divider_holo_custom.9.png b/ContactsCommon/res/drawable-xhdpi/list_section_divider_holo_custom.9.png
new file mode 100644
index 0000000..8fb0636
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/list_section_divider_holo_custom.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/list_title_holo.9.png b/ContactsCommon/res/drawable-xhdpi/list_title_holo.9.png
new file mode 100644
index 0000000..f4f00ca
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/list_title_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xhdpi/unknown_source.png b/ContactsCommon/res/drawable-xhdpi/unknown_source.png
new file mode 100644
index 0000000..35e8fb4
Binary files /dev/null and b/ContactsCommon/res/drawable-xhdpi/unknown_source.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_ab_search.png b/ContactsCommon/res/drawable-xxhdpi/ic_ab_search.png
new file mode 100644
index 0000000..142c545
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_ab_search.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_arrow_back_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..72c51b0
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_arrow_back_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_business_white_120dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_business_white_120dp.png
new file mode 100644
index 0000000..8d67e44
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_business_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_call_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_call_24dp.png
new file mode 100644
index 0000000..90ead2e
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_call_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_call_note_white_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_call_note_white_24dp.png
new file mode 100644
index 0000000..2656cad
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_call_note_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_call_voicemail_holo_dark.png b/ContactsCommon/res/drawable-xxhdpi/ic_call_voicemail_holo_dark.png
new file mode 100644
index 0000000..ac5b83b
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_call_voicemail_holo_dark.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_close_dk.png b/ContactsCommon/res/drawable-xxhdpi/ic_close_dk.png
new file mode 100644
index 0000000..670bf79
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_close_dk.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_group_white_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_group_white_24dp.png
new file mode 100644
index 0000000..03cad4c
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_group_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_history_white_drawable_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 0000000..f44df1a
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_history_white_drawable_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_info_outline_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_info_outline_24dp.png
new file mode 100644
index 0000000..c41a5fc
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_info_outline_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_back.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_back.png
new file mode 100644
index 0000000..436a82d
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_back.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_group_dk.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_group_dk.png
new file mode 100644
index 0000000..a70c60c
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_group_dk.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_group_lt.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_group_lt.png
new file mode 100644
index 0000000..c64b9de
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_group_lt.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_overflow_lt.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..ff1759b
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_overflow_lt.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_person_dk.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_person_dk.png
new file mode 100644
index 0000000..878e694
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_person_dk.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_person_lt.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_person_lt.png
new file mode 100644
index 0000000..ed4138f
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_person_lt.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_remove_field_holo_light.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 0000000..0fec2f2
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_remove_field_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_settings_holo_light.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..5b672a3
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_settings_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_dk.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_dk.png
new file mode 100644
index 0000000..fa682b1
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_dk.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_holo_light.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_holo_light.png
new file mode 100644
index 0000000..6c45bc8
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_holo_light.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_lt.png b/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_lt.png
new file mode 100644
index 0000000..955f738
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_menu_star_lt.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_message_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_message_24dp.png
new file mode 100644
index 0000000..0a79824
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_message_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_overflow_menu.png b/ContactsCommon/res/drawable-xxhdpi/ic_overflow_menu.png
new file mode 100644
index 0000000..92526f5
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_overflow_menu.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_person_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_person_24dp.png
new file mode 100644
index 0000000..184f741
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_person_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_person_add_24dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_person_add_24dp.png
new file mode 100644
index 0000000..8f744f0
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_person_add_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_person_white_120dp.png b/ContactsCommon/res/drawable-xxhdpi/ic_person_white_120dp.png
new file mode 100644
index 0000000..b29df2f
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_person_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_rx_videocam.png b/ContactsCommon/res/drawable-xxhdpi/ic_rx_videocam.png
new file mode 100755
index 0000000..89d29b7
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_rx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_scroll_handle.png b/ContactsCommon/res/drawable-xxhdpi/ic_scroll_handle.png
new file mode 100644
index 0000000..55f1d13
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_scroll_handle.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_tx_videocam.png b/ContactsCommon/res/drawable-xxhdpi/ic_tx_videocam.png
new file mode 100755
index 0000000..8d897ba
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_tx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_videocam.png b/ContactsCommon/res/drawable-xxhdpi/ic_videocam.png
new file mode 100644
index 0000000..4ab5ad0
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/ic_voicemail_avatar.png b/ContactsCommon/res/drawable-xxhdpi/ic_voicemail_avatar.png
new file mode 100644
index 0000000..182def8
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/ic_voicemail_avatar.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/list_activated_holo.9.png b/ContactsCommon/res/drawable-xxhdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..52c00dd
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/list_activated_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/list_focused_holo.9.png b/ContactsCommon/res/drawable-xxhdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..3e4ca68
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/list_focused_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/list_longpressed_holo_light.9.png b/ContactsCommon/res/drawable-xxhdpi/list_longpressed_holo_light.9.png
new file mode 100644
index 0000000..230d649
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/list_longpressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/list_pressed_holo_light.9.png b/ContactsCommon/res/drawable-xxhdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..1352a17
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/list_pressed_holo_light.9.png differ
diff --git a/ContactsCommon/res/drawable-xxhdpi/list_title_holo.9.png b/ContactsCommon/res/drawable-xxhdpi/list_title_holo.9.png
new file mode 100644
index 0000000..7ddf14a
Binary files /dev/null and b/ContactsCommon/res/drawable-xxhdpi/list_title_holo.9.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_ab_search.png b/ContactsCommon/res/drawable-xxxhdpi/ic_ab_search.png
new file mode 100644
index 0000000..2ffb2ec
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_ab_search.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_arrow_back_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..ae01a04
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_arrow_back_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_business_white_120dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_business_white_120dp.png
new file mode 100644
index 0000000..1741675
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_business_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_call_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_call_24dp.png
new file mode 100644
index 0000000..b0e0205
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_call_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_call_note_white_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_call_note_white_24dp.png
new file mode 100644
index 0000000..903c162
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_call_note_white_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_close_dk.png b/ContactsCommon/res/drawable-xxxhdpi/ic_close_dk.png
new file mode 100644
index 0000000..3a5540f
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_close_dk.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_history_white_drawable_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 0000000..5b96af5
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_history_white_drawable_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_info_outline_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_info_outline_24dp.png
new file mode 100644
index 0000000..3a82cab
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_info_outline_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_message_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_message_24dp.png
new file mode 100644
index 0000000..fa7c17a
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_message_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_overflow_menu.png b/ContactsCommon/res/drawable-xxxhdpi/ic_overflow_menu.png
new file mode 100644
index 0000000..9028bd4
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_overflow_menu.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_person_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_person_24dp.png
new file mode 100644
index 0000000..33d40d8
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_person_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_person_add_24dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_person_add_24dp.png
new file mode 100644
index 0000000..2fa2cca
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_person_add_24dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_person_white_120dp.png b/ContactsCommon/res/drawable-xxxhdpi/ic_person_white_120dp.png
new file mode 100644
index 0000000..b53cc11
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_person_white_120dp.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_rx_videocam.png b/ContactsCommon/res/drawable-xxxhdpi/ic_rx_videocam.png
new file mode 100755
index 0000000..095e090
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_rx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_scroll_handle.png b/ContactsCommon/res/drawable-xxxhdpi/ic_scroll_handle.png
new file mode 100644
index 0000000..d90782a
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_scroll_handle.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_tx_videocam.png b/ContactsCommon/res/drawable-xxxhdpi/ic_tx_videocam.png
new file mode 100755
index 0000000..79ea67b
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_tx_videocam.png differ
diff --git a/ContactsCommon/res/drawable-xxxhdpi/ic_videocam.png b/ContactsCommon/res/drawable-xxxhdpi/ic_videocam.png
new file mode 100644
index 0000000..0643ea5
Binary files /dev/null and b/ContactsCommon/res/drawable-xxxhdpi/ic_videocam.png differ
diff --git a/ContactsCommon/res/drawable/dialog_background_material.xml b/ContactsCommon/res/drawable/dialog_background_material.xml
new file mode 100644
index 0000000..fb586a0
--- /dev/null
+++ b/ContactsCommon/res/drawable/dialog_background_material.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/fastscroll_thumb.xml b/ContactsCommon/res/drawable/fastscroll_thumb.xml
new file mode 100644
index 0000000..a68f89e
--- /dev/null
+++ b/ContactsCommon/res/drawable/fastscroll_thumb.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_back_arrow.xml b/ContactsCommon/res/drawable/ic_back_arrow.xml
new file mode 100644
index 0000000..42e5e49
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_back_arrow.xml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_call.xml b/ContactsCommon/res/drawable/ic_call.xml
new file mode 100644
index 0000000..e06317b
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_call.xml
@@ -0,0 +1,19 @@
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_message_24dp.xml b/ContactsCommon/res/drawable/ic_message_24dp.xml
new file mode 100644
index 0000000..b1bd743
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_message_24dp.xml
@@ -0,0 +1,19 @@
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_person_add_tinted_24dp.xml b/ContactsCommon/res/drawable/ic_person_add_tinted_24dp.xml
new file mode 100644
index 0000000..3bbf4bd
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_person_add_tinted_24dp.xml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_scroll_handle_default.xml b/ContactsCommon/res/drawable/ic_scroll_handle_default.xml
new file mode 100644
index 0000000..8f490af
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_scroll_handle_default.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_scroll_handle_pressed.xml b/ContactsCommon/res/drawable/ic_scroll_handle_pressed.xml
new file mode 100644
index 0000000..bd62ceb
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_scroll_handle_pressed.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_search_add_contact.xml b/ContactsCommon/res/drawable/ic_search_add_contact.xml
new file mode 100644
index 0000000..9a313cd
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_search_add_contact.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_tab_all.xml b/ContactsCommon/res/drawable/ic_tab_all.xml
new file mode 100644
index 0000000..37d35f4
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_tab_all.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_tab_groups.xml b/ContactsCommon/res/drawable/ic_tab_groups.xml
new file mode 100644
index 0000000..c792bb8
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_tab_groups.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/ic_tab_starred.xml b/ContactsCommon/res/drawable/ic_tab_starred.xml
new file mode 100644
index 0000000..66d468b
--- /dev/null
+++ b/ContactsCommon/res/drawable/ic_tab_starred.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/item_background_material_borderless_dark.xml b/ContactsCommon/res/drawable/item_background_material_borderless_dark.xml
new file mode 100644
index 0000000..2a47e63
--- /dev/null
+++ b/ContactsCommon/res/drawable/item_background_material_borderless_dark.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/item_background_material_dark.xml b/ContactsCommon/res/drawable/item_background_material_dark.xml
new file mode 100644
index 0000000..200fcf7
--- /dev/null
+++ b/ContactsCommon/res/drawable/item_background_material_dark.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ -
+
+
+
diff --git a/ContactsCommon/res/drawable/list_item_activated_background.xml b/ContactsCommon/res/drawable/list_item_activated_background.xml
new file mode 100644
index 0000000..a58577e
--- /dev/null
+++ b/ContactsCommon/res/drawable/list_item_activated_background.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/list_selector_background_transition_holo_light.xml b/ContactsCommon/res/drawable/list_selector_background_transition_holo_light.xml
new file mode 100644
index 0000000..2541a2b
--- /dev/null
+++ b/ContactsCommon/res/drawable/list_selector_background_transition_holo_light.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/searchedittext_custom_cursor.xml b/ContactsCommon/res/drawable/searchedittext_custom_cursor.xml
new file mode 100644
index 0000000..0dc0ce4
--- /dev/null
+++ b/ContactsCommon/res/drawable/searchedittext_custom_cursor.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/drawable/view_pager_tab_background.xml b/ContactsCommon/res/drawable/view_pager_tab_background.xml
new file mode 100644
index 0000000..f9da59d
--- /dev/null
+++ b/ContactsCommon/res/drawable/view_pager_tab_background.xml
@@ -0,0 +1,22 @@
+
+
+
+ -
+
+
+
diff --git a/ContactsCommon/res/layout/account_filter_header.xml b/ContactsCommon/res/layout/account_filter_header.xml
new file mode 100644
index 0000000..2b6e110
--- /dev/null
+++ b/ContactsCommon/res/layout/account_filter_header.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/account_selector_list_item.xml b/ContactsCommon/res/layout/account_selector_list_item.xml
new file mode 100644
index 0000000..076cc88
--- /dev/null
+++ b/ContactsCommon/res/layout/account_selector_list_item.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/call_subject_history.xml b/ContactsCommon/res/layout/call_subject_history.xml
new file mode 100644
index 0000000..be369e0
--- /dev/null
+++ b/ContactsCommon/res/layout/call_subject_history.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/call_subject_history_list_item.xml b/ContactsCommon/res/layout/call_subject_history_list_item.xml
new file mode 100644
index 0000000..b8cce47
--- /dev/null
+++ b/ContactsCommon/res/layout/call_subject_history_list_item.xml
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_detail_list_padding.xml b/ContactsCommon/res/layout/contact_detail_list_padding.xml
new file mode 100644
index 0000000..c5dbd06
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_detail_list_padding.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_list_card.xml b/ContactsCommon/res/layout/contact_list_card.xml
new file mode 100644
index 0000000..93786aa
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_list_card.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_list_content.xml b/ContactsCommon/res/layout/contact_list_content.xml
new file mode 100644
index 0000000..c29bb3d
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_list_content.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_list_filter.xml b/ContactsCommon/res/layout/contact_list_filter.xml
new file mode 100644
index 0000000..d419c7e
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_list_filter.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_list_filter_custom.xml b/ContactsCommon/res/layout/contact_list_filter_custom.xml
new file mode 100644
index 0000000..b6d9229
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_list_filter_custom.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_list_filter_item.xml b/ContactsCommon/res/layout/contact_list_filter_item.xml
new file mode 100644
index 0000000..09bbe55
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_list_filter_item.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_tile_frequent.xml b/ContactsCommon/res/layout/contact_tile_frequent.xml
new file mode 100644
index 0000000..b1e83ce
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_tile_frequent.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_tile_frequent_phone.xml b/ContactsCommon/res/layout/contact_tile_frequent_phone.xml
new file mode 100644
index 0000000..f87dff7
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_tile_frequent_phone.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_tile_starred.xml b/ContactsCommon/res/layout/contact_tile_starred.xml
new file mode 100644
index 0000000..777cc05
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_tile_starred.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/contact_tile_starred_quick_contact.xml b/ContactsCommon/res/layout/contact_tile_starred_quick_contact.xml
new file mode 100644
index 0000000..ecbe583
--- /dev/null
+++ b/ContactsCommon/res/layout/contact_tile_starred_quick_contact.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/custom_contact_list_filter_account.xml b/ContactsCommon/res/layout/custom_contact_list_filter_account.xml
new file mode 100644
index 0000000..b1f3f2b
--- /dev/null
+++ b/ContactsCommon/res/layout/custom_contact_list_filter_account.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/custom_contact_list_filter_group.xml b/ContactsCommon/res/layout/custom_contact_list_filter_group.xml
new file mode 100644
index 0000000..bd8c604
--- /dev/null
+++ b/ContactsCommon/res/layout/custom_contact_list_filter_group.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/default_account_checkbox.xml b/ContactsCommon/res/layout/default_account_checkbox.xml
new file mode 100644
index 0000000..3b45f53
--- /dev/null
+++ b/ContactsCommon/res/layout/default_account_checkbox.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/dialog_call_subject.xml b/ContactsCommon/res/layout/dialog_call_subject.xml
new file mode 100644
index 0000000..419a7f0
--- /dev/null
+++ b/ContactsCommon/res/layout/dialog_call_subject.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/directory_header.xml b/ContactsCommon/res/layout/directory_header.xml
new file mode 100644
index 0000000..b85ceff
--- /dev/null
+++ b/ContactsCommon/res/layout/directory_header.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/list_separator.xml b/ContactsCommon/res/layout/list_separator.xml
new file mode 100644
index 0000000..80abacb
--- /dev/null
+++ b/ContactsCommon/res/layout/list_separator.xml
@@ -0,0 +1,27 @@
+
+
+
diff --git a/ContactsCommon/res/layout/search_bar_expanded.xml b/ContactsCommon/res/layout/search_bar_expanded.xml
new file mode 100644
index 0000000..c530299
--- /dev/null
+++ b/ContactsCommon/res/layout/search_bar_expanded.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/select_account_list_item.xml b/ContactsCommon/res/layout/select_account_list_item.xml
new file mode 100644
index 0000000..0ba4336
--- /dev/null
+++ b/ContactsCommon/res/layout/select_account_list_item.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/layout/select_dialog_item.xml b/ContactsCommon/res/layout/select_dialog_item.xml
new file mode 100644
index 0000000..00ba44b
--- /dev/null
+++ b/ContactsCommon/res/layout/select_dialog_item.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/ContactsCommon/res/mipmap-hdpi/ic_contacts_clr_48cv_44dp.png b/ContactsCommon/res/mipmap-hdpi/ic_contacts_clr_48cv_44dp.png
new file mode 100644
index 0000000..64eff00
Binary files /dev/null and b/ContactsCommon/res/mipmap-hdpi/ic_contacts_clr_48cv_44dp.png differ
diff --git a/ContactsCommon/res/mipmap-mdpi/ic_contacts_clr_48cv_44dp.png b/ContactsCommon/res/mipmap-mdpi/ic_contacts_clr_48cv_44dp.png
new file mode 100644
index 0000000..b4ee821
Binary files /dev/null and b/ContactsCommon/res/mipmap-mdpi/ic_contacts_clr_48cv_44dp.png differ
diff --git a/ContactsCommon/res/mipmap-xhdpi/ic_contacts_clr_48cv_44dp.png b/ContactsCommon/res/mipmap-xhdpi/ic_contacts_clr_48cv_44dp.png
new file mode 100644
index 0000000..6feeadf
Binary files /dev/null and b/ContactsCommon/res/mipmap-xhdpi/ic_contacts_clr_48cv_44dp.png differ
diff --git a/ContactsCommon/res/mipmap-xxhdpi/ic_contacts_clr_48cv_44dp.png b/ContactsCommon/res/mipmap-xxhdpi/ic_contacts_clr_48cv_44dp.png
new file mode 100644
index 0000000..01a3fde
Binary files /dev/null and b/ContactsCommon/res/mipmap-xxhdpi/ic_contacts_clr_48cv_44dp.png differ
diff --git a/ContactsCommon/res/mipmap-xxxhdpi/ic_contacts_clr_48cv_44dp.png b/ContactsCommon/res/mipmap-xxxhdpi/ic_contacts_clr_48cv_44dp.png
new file mode 100644
index 0000000..328e067
Binary files /dev/null and b/ContactsCommon/res/mipmap-xxxhdpi/ic_contacts_clr_48cv_44dp.png differ
diff --git a/ContactsCommon/res/values-af/strings.xml b/ContactsCommon/res/values-af/strings.xml
new file mode 100644
index 0000000..2d0590c
--- /dev/null
+++ b/ContactsCommon/res/values-af/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Teks gekopieer"
+ "Kopieer na knipbord"
+ "Bel %s "
+ "Bel huis"
+ "Bel selfoon"
+ "Bel werk"
+ "Bel werkfaks"
+ "Bel huisfaks"
+ "Bel roeper"
+ "Bel"
+ "Bel terugbel"
+ "Bel motor"
+ "Bel maatskappy, hoofnommer"
+ "Bel ISDN"
+ "Bel hoofnommer"
+ "Bel faks"
+ "Bel radio"
+ "Bel teleks"
+ "Bel TTY/TDD"
+ "Bel werkselfoon"
+ "Bel werkroeper"
+ "Bel %s "
+ "Bel MMS"
+ "SMS %s "
+ "SMS huis"
+ "SMS selfoon"
+ "SMS werk"
+ "SMS werkfaks"
+ "SMS huisfaks"
+ "SMS roeper"
+ "SMS"
+ "SMS terugbel"
+ "SMS motor"
+ "SMS maatskappy, hoofnommer"
+ "SMS ISDN"
+ "SMS hoofnommer"
+ "SMS faks"
+ "SMS radio"
+ "SMS teleks"
+ "SMS TTY/TDD"
+ "SMS werkselfoon"
+ "SMS werkroeper"
+ "SMS %s "
+ "SMS MMS"
+ "Maak video-oproep"
+ "Vee gereeld-gekontaktes uit?"
+ "Jy gaan die gereeld gekontak-lys in die Kontakte- en Foon-program uitvee en e-posprogramme dwing om jou adresvoorkeure van nuuts af te leer."
+ "Vee tans gereeld-gekontaktes uit..."
+ "Beskikbaar"
+ "Weg"
+ "Besig"
+ "Kontakte"
+ "Ander"
+ "Gids"
+ "Alle kontakte"
+ "Ek"
+ "Soek tans…"
+ "Meer as %d gevind."
+ "Geen kontakte nie"
+
+ %d gevind
+ - 1 gevind
+
+ "Vinnige kontak vir %1$s "
+ "(Geen naam nie)"
+ "Gereeld gebel"
+ "Gereeld gekontak"
+ "Bekyk kontak"
+ "Alle kontakte met foonnommers"
+ "Bekyk opdaterings"
+ "Net foon, ongesinkroniseer"
+ "Naam"
+ "Bynaam"
+ "Naam"
+ "Voornaam"
+ "Van"
+ "Naamvoorvoegsel"
+ "Middelnaam"
+ "Naamagtervoegsel"
+ "Fonetiese naam"
+ "Fonetiese voornaam"
+ "Fonetiese middelnaam"
+ "Fonetiese van"
+ "Foon"
+ "E-pos"
+ "Adres"
+ "IM"
+ "Organisasie"
+ "Verhouding"
+ "Spesiale datums"
+ "SMS-boodskap"
+ "Adres"
+ "Maatskappy"
+ "Titel"
+ "Notas"
+ "SIP"
+ "Webwerf"
+ "Groepe"
+ "E-postuis"
+ "E-pos mobiel"
+ "E-pos werk"
+ "E-pos"
+ "E-pos %s "
+ "E-pos"
+ "Straat"
+ "Posbus"
+ "Buurt"
+ "Stad"
+ "Staat"
+ "Poskode"
+ "Land"
+ "Bekyk huisadres"
+ "Bekyk werkadres"
+ "Bekyk adres"
+ "Bekyk %s -adres"
+ "Klets met AIM"
+ "Klets met Windows Live"
+ "Klets met Yahoo"
+ "Klets met Skype"
+ "Klets met QQ"
+ "Klets met Google Talk"
+ "Klets met ICQ"
+ "Klets met Jabber"
+ "Klets"
+ "vee uit"
+ "Vou naamvelde uit of in"
+ "Alle kontakte"
+ "Gester"
+ "Pasmaak"
+ "Kontak"
+ "Alle ander kontakte"
+ "Alle kontakte"
+ "Verwyder sinkroniseergroep"
+ "Voeg sinkroniseergroep by"
+ "Meer groepe"
+ "As jy \"%s \" uit sinkronisering verwyder, sal dit ook enige ongegroepeerde kontakte uit sinkronisering verwyder."
+ "Stoor tans vertoonopsies"
+ "Klaar"
+ "Kanselleer"
+ "Kontakte in %s "
+ "Kontakte in gepasmaakte aansig"
+ "Enkel kontak"
+ "Skep kontak onder rekening"
+ "Voer van SIM-kaart af in"
+ "Voer in vanaf SIM ^1 – ^2 "
+ "Voer in vanaf SIM %1$s "
+ "Voer in vanaf .vcf-lêer"
+ "Kanselleer invoer van %s ?"
+ "Kanselleer uitvoer van %s ?"
+ "Kon nie vCard invoer/uitvoer kanselleer nie"
+ "Onbekende fout."
+ "Kon nie \"%s \" oopmaak nie: %s ."
+ "Kon nie die uitvoerder aktiveer nie: \"%s \"."
+ "Daar is geen uitvoerbare kontak nie."
+ "Jy het \'n vereiste toestemming gedeaktiveer."
+ "\'n Fout het voorgekom tydens uitvoer: \"%s \"."
+ "Vereiste lêernaam is te lank (\"%s \")."
+ "Te veel vCard-lêers is op die SD-kaart."
+ "I/U-fout"
+ "Nie genoeg geheue nie. Die lêer is dalk te groot."
+ "Kon nie vCard ontleed nie weens onverwagte rede."
+ "Die formaat word nie ondersteun nie."
+ "Kon nie meta-inligting van gegewe vCard-lêer(s) versamel nie."
+ "Een of meer lêers kon nie ingevoer word nie (%s)."
+ "Klaar met uitvoer van %s ."
+ "Het kontakte klaar uitgevoer."
+ "Uitvoer van %s is gekanselleer."
+ "Voer kontakdata uit"
+ "Jou kontakdata word uitgevoer na: %s ."
+ "Kon nie databasis-inligting kry nie"
+ "Daar is geen kontakte wat uitgevoer kan word nie. As jy kontakte op jou foon het, kan sommige dataverskaffers dalk nie toelaat dat die foon se kontakte uitgevoer word nie."
+ "Die vCard-opsteller het nie behoorlik begin nie."
+ "Kon nie uitvoer nie"
+ "Die kontakdata is nie uitgevoer nie.\nRede: \"%s \""
+ "Voer tans %s in"
+ "Kon nie vCard-data lees nie"
+ "Lees van vCard-data gekanselleer"
+ "Klaar met invoer van vCard %s "
+ "Invoer van %s gekanselleer"
+ "%s sal binnekort ingevoer word."
+ "Die lêer sal binnekort ingevoer word."
+ "vCard invoerversoek is verwerp. Probeer asseblief later weer."
+ "%s sal binnekort uitgevoer word."
+ "Die lêer sal binnekort uitgevoer word."
+ "vCard uitvoerversoek is verwerp. Probeer asseblief later weer."
+ "kontak"
+ "Kas tans vCard(s) na die plaaslike tydelike berging. Die werklike invoer sal binnekort begin."
+ "Kon nie vCard invoer nie."
+ "Geen vCard-lêer op die SD-kaart gevind nie."
+ "Kontak ontvang via NFC"
+ "Voer kontakte uit?"
+ "Kas tans"
+ "Die SD-kaart kon nie geskandeer word nie. (Rede: \"%s \")"
+ "Voer %s %s in: %s "
+ "Voer uit na .vcf-lêer"
+ "Sorteer volgens"
+ "Voornaam"
+ "Van"
+ "Naamformaat"
+ "Voornaam eerste"
+ "Van eerste"
+ "Deel sigbare kontakte"
+ "Kon nie sigbare kontakte deel nie."
+ "Voer kontakte in/uit"
+ "Voer kontakte in"
+ "Hierdie kontak kan nie gedeel word nie."
+ "Soek"
+ "Kontakte om te wys"
+ "Kontakte om te wys"
+ "Definieer gepasmaakte aansig"
+ "Vind kontakte"
+ "Gunstelinge"
+ "Geen kontakte nie."
+ "Geen sigbare kontakte nie."
+ "Geen gunstelinge nie"
+ "Geen kontakte in %s nie"
+ "Vee dikwels-gebruiktes uit"
+ "Kies SIM-kaart"
+ "Rekeninge"
+ "Voer in/uit"
+ "via %1$s "
+ "%1$s via %2$s "
+ "hou op soek"
+ "Vee soektog uit"
+ "Opsies vir wys van kontakte"
+ "Rekening"
+ "Gebruik dit altyd vir oproepe"
+ "Bel met"
+ "Oproep met \'n nota"
+ "Tik \'n nota om saam met oproep te stuur …"
+ "STUUR EN BEL"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-am/strings.xml b/ContactsCommon/res/values-am/strings.xml
new file mode 100644
index 0000000..877b0bc
--- /dev/null
+++ b/ContactsCommon/res/values-am/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "ፅሁፍ ተገልብጧል"
+ "ወደ ቅንጥብ ሰሌዳ ቅዳ"
+ "ለ%s ይደውሉ"
+ "ለቤት ይደውሉ"
+ "ለተንቀሳቃሽ ስልክ ይደውሉ"
+ "ለሥራ ይደውሉ"
+ "ለስራ ፋክስ ይደውሉ"
+ "ለቤት ፋክስ ይደውሉ"
+ "ለምልክት ማድረጊያ ይደውሉ"
+ "ይደውሉ"
+ "የጥሪ መልስ ይደውሉ"
+ "ለመኪና ይደውሉ"
+ "ለዋናው የኩባኒያ ይደውሉ"
+ "ለISDN ይደውሉ"
+ "ለዋናው ይደውሉ"
+ "ለፋክስ ይደውሉ"
+ "ለሬዲዮ ይደውሉ"
+ "ለቴሌክስ ይደውሉ"
+ "ለTTY/TDD ይደውሉ"
+ "ለየስራ ተንቀሳቃሽ ስልክ ይደውሉ"
+ "ለየሥራ ጥሪ ምልክት ማድረጊያ ይደውሉ"
+ "ለ%s ይደውሉ"
+ "ለኤም ኤም ኤስ ይደውሉ"
+ "ለ%s ጽሑፍ ይላኩ"
+ "ለቤት ጽሑፍ ይላኩ"
+ "ለተንቀሳቃሽ ስልክ ጽሑፍ ይላኩ"
+ "ለስራ ጽሑፍ ይላኩ"
+ "ለስራ ፋክስ ጽሑፍ ይላኩ"
+ "ለቤት ፋክስ ጽሑፍ ይላኩ"
+ "ለምልክት ማድረጊያ ጽሑፍ ይላኩ"
+ "ጽሑፍ ይላኩ"
+ "ለጥሪ መልስ ጽሑፍ ይላኩ"
+ "ለመኪና ጽሑፍ ይላኩ"
+ "ለዋናው ኩባኒያ ጽሑፍ ይላኩ"
+ "ለISDN ጽሑፍ ይላኩ"
+ "ለዋናው ጽሑፍ ይላኩ"
+ "ለፋክስ ጽሑፍ ይላኩ"
+ "ለሬዲዮ ጽሑፍ ይላኩ"
+ "ለቴለክስ ጽሑፍ ይላኩ"
+ "ለTTY/TDD ጽሑፍ ይላኩ"
+ "ለስራ ተንቀሳቃሽ ስልክ ጽሑፍ ይላኩ"
+ "ለሥራ ምልክት ማድረጊያ ጽሑፍ ይላኩ"
+ "ለ%s ጽሑፍ ይላኩ"
+ "ለኤም ኤም ኤስ ጽሑፍ ይላኩ"
+ "የቪዲዮ ጥሪ ያድርጉ"
+ "በተደጋጋሚ የተገኙ ይጽዱ?"
+ "በእውቂያዎች እና በስልክ መተግበሪያዎች ውስጥ በተደጋጋሚ ያገኟቸውን ዝርዝር አጽድተው የኢሜይል መተግበሪያዎች ምርጫዎችዎን ከባዶ ተነስተው እንዲያውቁ ያስገድዱዋቸዋል።"
+ "በተደጋጋሚ የተገኙትን በማጽዳት ላይ…"
+ "የሚገኝ"
+ "ወጣ ብሏል"
+ "ተይዟል"
+ "ዕውቂያዎች"
+ "ሌላ"
+ "ማውጫ"
+ "ሁሉም እውቂያዎች"
+ "እኔ"
+ "በመፈለግ ላይ…"
+ "ከ%d በላይ ተገኝተዋል።"
+ "ምንም እውቂያዎች የሉም"
+
+ %d ተገኝቷል
+ %d ተገኝተዋል
+
+ "ለ%1$s ፈጣን ዕውቂያ"
+ "(ስም የለም)"
+ "በተደጋጋሚ የተደወለለት/ላት"
+ "በተደጋጋሚ የተገኙ"
+ "ዕውቂያ ይመልከቱ"
+ "የስልክ ቁጥር ያላቸው ሁሉም ዕውቂያዎች"
+ "ዝማኔዎችን ይመልከቱ"
+ "ስልክ-ብቻ፣ ያልተመሳሰለ"
+ "ስም"
+ "ቅጽል ስም"
+ "ስም"
+ "የመጀመሪያ ስም"
+ "የመጠሪያ ስም"
+ "ቅድመ-ስም"
+ "የመካከለኛ ስም"
+ "ድህረ-ስም"
+ "የድምፀ ልሳን ስም"
+ "የመጀመሪያ ስም ፎነቲክ"
+ "የድምፀ ልሳን መካከለኛ ስም"
+ "የመጠሪያ ስም ፎነቲክ"
+ "ስልክ"
+ "ኢሜይል"
+ "አድራሻ"
+ "IM"
+ "ድርጅት"
+ "ግንኙነት"
+ "ልዩ ቀኖች"
+ "የፅሁፍ መልዕክት"
+ "አድራሻ"
+ "ኩባንያ"
+ "ርዕስ"
+ "ማስታወሻዎች"
+ "SIP"
+ "ድረ-ገፅ"
+ "ቡድኖች"
+ "ለቤት ኢሜይል ይላኩ"
+ "ለተንቀሳቃሽ ስልክ ኢሜይል ይላኩ"
+ "ለሥራ ኢሜይል ይላኩ"
+ "ኢሜይል"
+ "ለ%s ኢሜይል ይላኩ"
+ "ኢሜይል"
+ "መንገድ"
+ "የፖስታ ሣጥን ቁጥር"
+ "ሰፈር"
+ "ከተማ"
+ "ግዛት"
+ "ዚፕ ኮድ"
+ "አገር"
+ "የቤት አድራሻ ይመልከቱ"
+ "የሥራ አድራሻ ይመልከቱ"
+ "አድራሻ ይመልከቱ"
+ "የ%s አድራሻ ይመልከቱ"
+ "AIMን በመጠቀም ይወያዩ"
+ "Windows Liveን በመጠቀም ይወያዩ"
+ "Yahooን በመጠቀም ይወያዩ"
+ "Skypeን በመጠቀም ይወያዩ"
+ "QQን በመጠቀም ይወያዩ"
+ "Google Talkን በመጠቀም ይወያዩ"
+ "ICQን በመጠቀም ይወያዩ"
+ "Jabberን በመጠቀም 271448"
+ "ውይይት"
+ "ሰርዝ"
+ "የስም መስኮችን ይዘርጉ ወይም ይሰብስቡ"
+ "ሁሉም እውቅያዎች"
+ "ኮከብ የተደረገባቸው"
+ "ያብጁ"
+ "እውቂያ"
+ "ሌሎች ሁሉም ዕውቂያዎች"
+ "ሁሉም እውቅያዎች"
+ "የማመሳሰል ቡድን አስወግድ"
+ "የማመሳሰል ቡድን ያክሉ"
+ "ተጨማሪ ቡድኖች…"
+ "«%s »ን ከማመሳስሉ ማስወገድ ማናቸውም በቡድን ያልተካተቱ ዕውቅያዎችንም ከማመሳሰሉ ያስወግዳቸዋል።"
+ "የማሳያ አማራጮችን በማስቀመጥ ላይ…"
+ "ተከናውኗል"
+ "ይቅር"
+ "በ%s ውስጥ ያሉ ዕውቂያዎች"
+ "እውቂያዎች በብጁ እይታ"
+ "ነጠላ እውቂያ"
+ "በመለያ ስር ዕውቂያ ይፍጠሩ"
+ "ከሲም ካርድ ያስመጡ"
+ "ከSIM አስመጣ ^1 - ^2 "
+ "ከSIM አስመጣ %1$s "
+ "ከ .vcf ፋይል አስመጣ"
+ "የ%s ወደ ውስጥ ማስመጣት ይቅር?"
+ "የ%s ወደ ውጭ መላክ ይቅር?"
+ "vCard ማስመጣት/ወደ ውጪ ይቅር ማለት አልተቻለም"
+ "ያልታወቀ ስህተት።"
+ "«%s »ን መክፈት አልተቻለም፦ %s ።"
+ "ይህንን ላኪ መጀመር አልተቻለም፦ «%s »"
+ "ምንም ወደ ውጭ መላክ የሚችል ዕውቂያ የለም።"
+ "አንድ የሚያስፈልግ ፍቃድ አሰናክለዋል።"
+ "ወደ ውጪ በሚላክበት ጊዜ ስህተት ተከስቷል፦ %s "
+ "የተጠየቀው ፋይል ስም በጣም ረጅም ነው («%s »)።"
+ "በSD ካርዱ ላይ በጣም ብዙ vCard ፋይሎች።"
+ "የግብዓት/ውጽዓት ስህተት"
+ "በቂ ማህደረ ትውስታ የለም። ፋይሉ በጣም ትልቅ ሊሆን ይችላል።"
+ "ባልተጠበቀ ምክንያት vCard መተንተን አልተቻለም።"
+ "ቅርፀቱ አይደገፍም።"
+ "የተሰጠው(ጡት) vCard ፋይል(ሎች) ዲበ ውሂብ መረጃ መሰብሰብ አልተቻለም።"
+ "አንድ ወይም ከዚያ በላይ ፋይሎች ማስመጣት አልተቻለም (%s)።"
+ "%s ን ወደ ውጪ መላክ ተጠናቅቋል።"
+ "እውቂያዎችን ወደ ውጪ መላክ ተጠናቅቋል።"
+ "%s ን ወደ ውጪ መላክ ተሰርዟል።"
+ "የዕውቂያ ውሂብ ወደ ውጪ በመላክ ላይ"
+ "የዕውቅያ ውሂብዎ ወደዚህ በመላክ ላይ ነው፦ %s ።"
+ "ውሂብ ጎታ መረጃን ማግኘት አልተቻለም።"
+ "ወደ ውጭ ሊላኩ የሚችሉ እውቅያዎች የሉም። ስልክዎ ውስጥ እውቅያዎች ካሉዎት አንዳንድ የውሂብ አቅራቢዎች እውቂያዎቹ ከስልኩ ወደ ውጭ እንዲላኩ አይፈቅዱም።"
+ "የvCard አቀናባሪው በትክክል አልጀመረም።"
+ "ወደ ውጭ መላክ አልተቻለም"
+ "የዕውቅያ ውሂቡ ወደ ውጭ አልተላከም።\nምክንያት፦«%s »"
+ "%s ን በማስመጣት ላይ"
+ "የvCard ውሂቡን ማንበብ አልተቻለም"
+ "የvCard ውሂብ ማንበብ ተሰርዟል"
+ "የ%s vCard ማስመጣት ተጠናቅቋል"
+ "የ%s ማስመጣት ተሰርዟል"
+ "%s ከትንሽ ጊዜ በኋላ ይመጣል።"
+ "ፋይሉ ከትንሽ ጊዜ በኋላ ይመጣል።"
+ "የvCard ማስመጣት ጥያቄ ተቀባይነት አላገኘም። ትንሽ ቆይተው ይሞክሩ።"
+ "%s ከትንሽ ጊዜ በኋላ ወደ ውጪ ይላካል።"
+ "ፋይሉ ትንሽ ቆይቶ ወደ ውጪ ይላካል።"
+ "የvCard ወደ ውጪ መላክ ጥያቄ ተቀባይነት አላገኘም። ትንሽ ቆይተው ይሞከሩ።"
+ "እውቂያ"
+ "vCard(s) ወደ ጊዜያዊ ማከማቻ በመሸጎጥ ላይ ነው። ትክክለኛው ማስመጣቱ በቅርቡ ይጀምራል።"
+ "vCardን ማስመጣት አልተቻለም።"
+ "በSD ካርዱ ላይ ምንም vCard ካርድ ፋይል አልተገኘም።"
+ "በNFC የደረሱ ዕውቂያዎች"
+ "እውቅያዎች ይላኩ?"
+ "በመሸጎጥ ላይ"
+ "SD ካርዱ ሊቃኝ አልተቻለም። (ምክንያት፦ «%s »)"
+ "%s /%s ን በማስመጣት ላይ፦ %s "
+ "ወደ የ .vcf ፋይል ላክ"
+ "ደርድር በ"
+ "የመጀመሪያ ስም"
+ "የመጠሪያ ስም"
+ "የስም ቅርጸት"
+ "የመጀመሪያ ስም መጀመሪያ"
+ "የመጠሪያ ስም መጀመሪያ"
+ "የሚታዩ እውቂያዎችን አጋራ"
+ "የሚታዩ እውቂያዎችን ማጋራት አልተሳካም።"
+ "ዕውቂያዎች ያስመጡ/ይላኩ"
+ "እውቅያዎችን ያስመጡ"
+ "ይህ ዕውቂያ ሊጋራ አይችልም።"
+ "ይፈልጉ"
+ "የሚታዩ ዕውቂያዎች"
+ "የሚታዩ ዕውቂያዎች"
+ "ብጁ ዕይታ ይግለጹ"
+ "ዕውቂያዎችን ያግኙ"
+ "ተወዳጆች"
+ "ምንም ዕውቂያዎች የሉም።"
+ "ምንም የሚታዩ ዕውቂያዎች የሉም።"
+ "ምንም ተወዳጆች የለም።"
+ "በ%s ውስጥ ምንም ዕውቂያዎች የሉም"
+ "ተደጋጋሚዎችን አጽዳ"
+ "ሲም ካርድ ይምረጡ"
+ "መለያዎች"
+ "ያስመጡ/ወደ ውጪ ይላኩ"
+ "በ%1$s በኩል"
+ "%1$s በ%2$s በኩል"
+ "መፈለግ አቁም"
+ "ፍለጋን አጽዳ"
+ "የእውቂያ ማሳያ አማራጮች"
+ "መለያ"
+ "ለጥሪዎች ሁልጊዜ ይህንን ተጠቀም"
+ "ይደውሉ ከዚህ ጋር"
+ "ከማስታወሻ ጋር ደውል"
+ "ከጥሪ ጋር ለመላክ የማስታወሻ ጽሑፍ ይተይቡ ..."
+ "ላክ እና ደውል"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ar/strings.xml b/ContactsCommon/res/values-ar/strings.xml
new file mode 100644
index 0000000..7bf18a6
--- /dev/null
+++ b/ContactsCommon/res/values-ar/strings.xml
@@ -0,0 +1,257 @@
+
+
+
+
+ "تم نسخ النص"
+ "نسخ إلى الحافظة"
+ "الاتصال بـ %s "
+ "الاتصال بهاتف منزلي"
+ "الاتصال بالجوال"
+ "الاتصال بهاتف العمل"
+ "الاتصال بفاكس العمل"
+ "الاتصال بفاكس المنزل"
+ "الاتصال بجهاز النداء"
+ "اتصال"
+ "الاتصال برقم معادوة الاتصال"
+ "الاتصال بهاتف السيارة"
+ "الاتصال بهاتف الشركة الرئيسي"
+ "اتصال بـ ISDN"
+ "الاتصال بالهاتف الرئيسي"
+ "الاتصال برقم الفاكس"
+ "اتصال بهاتف لاسلكي"
+ "الاتصال بالتلكس"
+ "اتصال بـ TTY/TDD"
+ "الاتصال بجوال العمل"
+ "الاتصال بجهاز نداء العمل"
+ "الاتصال بـ %s "
+ "اتصال بهاتف رسائل الوسائط المتعددة"
+ "إرسال رسالة نصية إلى %s "
+ "إرسال رسالة نصية إلى هاتف منزلي"
+ "إرسال رسالة نصية إلى جوال"
+ "إرسال رسالة نصية إلى هاتف العمل"
+ "إرسال رسالة نصية إلى فاكس العمل"
+ "إرسال رسالة نصية إلى فاكس المنزل"
+ "إرسال رسالة نصية إلى جهاز النداء"
+ "إرسال رسالة نصية"
+ "إرسال رسالة نصية إلى هاتف معاودة الاتصال"
+ "إرسال رسالة نصية إلى هاتف السيارة"
+ "إرسال رسالة نصية إلى الهاتف الرئيسي للشركة"
+ "إرسال رسالة نصية إلى ISDN"
+ "إرسال رسالة نصية إلى الهاتف الرئيسي"
+ "إرسال رسالة نصية إلى فاكس"
+ "إرسال رسالة نصية إلى هاتف لاسلكي"
+ "إرسال رسالة نصية إلى هاتف تلكس"
+ "إرسال رسالة نصية إلى TTY/TDD"
+ "إرسال رسالة نصية إلى جوال العمل"
+ "إرسال رسالة نصية إلى جهاز نداء العمل"
+ "إرسال رسالة نصية إلى %s "
+ "إرسال رسالة نصية إلى هاتف رسائل الوسائط المتعددة"
+ "إجراء مكالمة فيديو"
+ "محو قائمة من يتم الاتصال بهم كثيرًا؟"
+ "ستمحو قائمة من يتم الاتصال بهم كثيرًا في تطبيقي جهات الاتصال والهاتف، وستفرض على تطبيقات البريد الإلكتروني التعرف على تفضيلات توجيه الرسائل من البداية."
+ "جارٍ محو قائمة المُتصل بهم كثيرًا…"
+ "متاح"
+ "بالخارج"
+ "مشغول"
+ "جهات الاتصال"
+ "غير ذلك"
+ "الدليل"
+ "جميع جهات الاتصال"
+ "أنا"
+ "جارٍ البحث..."
+ "تم العثور على أكثر من %d من جهات الاتصال."
+ "ليست هناك جهات اتصال"
+
+ - لا توجد أية جهة اتصال (
%d )
+ - توجد جهتا اتصال (
%d )
+ - توجد
%d جهات اتصال
+ - توجد
%d جهة اتصال
+ - توجد
%d من جهات الاتصال
+ - توجد جهة اتصال واحدة
+
+ "اتصال سريع لـ %1$s "
+ "(بلا اسم)"
+ "الأكثر اتصالاً"
+ "يتم الاتصال بها بشكل متكرر"
+ "عرض جهة الاتصال"
+ "جميع جهات الاتصال التي لها أرقام هواتف"
+ "عرض التحديثات"
+ "الهاتف فقط، غير متزامنة"
+ "الاسم"
+ "اللقب"
+ "الاسم"
+ "الاسم الأول"
+ "اسم العائلة"
+ "بادئة الاسم"
+ "الاسم الأوسط"
+ "لاحقة الاسم"
+ "الاسم صوتيًا"
+ "الاسم الأول صوتيًا"
+ "الاسم الصوتي الأوسط"
+ "اسم العائلة صوتيًا"
+ "الهاتف"
+ "البريد الإلكتروني"
+ "العنوان"
+ "المراسلة الفورية"
+ "المؤسسة"
+ "العلاقة"
+ "تواريخ خاصة"
+ "رسالة نصية"
+ "العنوان"
+ "الشركة"
+ "العنوان"
+ "ملاحظات"
+ "SIP"
+ "موقع ويب"
+ "المجموعات"
+ "إرسال رسالة إلكترونية إلى عنوان البريد الإلكتروني للمنزل"
+ "إرسال رسالة إلكترونية إلى عنوان بريد إلكتروني على الجوال"
+ "إرسال رسالة إلكترونية إلى عنوان البريد الإلكتروني للعمل"
+ "إرسال رسالة إلكترونية"
+ "البريد الإلكتروني %s "
+ "إرسال رسالة إلكترونية"
+ "الشارع"
+ "صندوق البريد"
+ "منطقة مجاورة"
+ "المدينة"
+ "الولاية"
+ "الرمز البريدي"
+ "البلد"
+ "عرض عنوان المنزل"
+ "عرض عنوان العمل"
+ "عرض العنوان"
+ "عرض عنوان %s "
+ "الدردشة باستخدام AIM"
+ "الدردشة باستخدام Windows Live"
+ "الدردشة باستخدام Yahoo"
+ "الدردشة باستخدام Skype"
+ "الدردشة باستخدام QQ"
+ "الدردشة باستخدام Google Talk"
+ "الدردشة باستخدام ICQ"
+ "الدردشة باستخدام Jabber"
+ "دردشة"
+ "حذف"
+ "توسيع أو تصغير حقول الاسم"
+ "جميع جهات الاتصال"
+ "مميّزة بنجمة"
+ "تخصيص"
+ "جهة الاتصال"
+ "جميع جهات الاتصال الأخرى"
+ "جميع جهات الاتصال"
+ "إزالة مجموعة متزامنة"
+ "إضافة مجموعة متزامنة"
+ "مزيد من المجموعات..."
+ "ستؤدي إزالة \"%s \"من المزامنة أيضًا إلى إزالة أية جهات اتصال غير مجمعة من المزامنة."
+ "جارٍ حفظ خيارات العرض..."
+ "تم"
+ "إلغاء"
+ "جهات الاتصال في %s "
+ "جهات الاتصال في عرض مخصص"
+ "جهة اتصال واحدة"
+ "إنشاء جهة اتصال ضمن حساب"
+ "استيراد من شريحة SIM"
+ "استيراد من شريحة SIM ^1 - ^2 "
+ "استيراد من شريحة SIM %1$s "
+ "الاستيراد من ملف vcf."
+ "هل تريد إلغاء استيراد %s ؟"
+ "هل تريد إلغاء تصدير %s ؟"
+ "تعذر إلغاء استيراد/تصدير vCard"
+ "خطأ غير معروف."
+ "تعذر فتح \"%s \": %s \"."
+ "تعذر بدء المُصدر: \"%s \"."
+ "ليست هناك جهة اتصال قابلة للتصدير."
+ "لقد عطلت إذنًا مطلوبًا."
+ "حدث خطأ أثناء التصدير: \"%s \"."
+ "اسم الملف المطلوب أطول مما يجب (%s )."
+ "هناك ملفات vCard أكثر مما يجب على بطاقة SD."
+ "خطأ I/O"
+ "الذاكرة غير كافية. ربما يكون الملف أكبر مما يجب."
+ "تعذر تحليل vCard لسبب غير متوقع."
+ "التنسيق غير معتمد."
+ "تعذر جمع معلومات وصفية حول ملفات vCard المحددة."
+ "تعذر استيراد ملف أو أكثر (%s)."
+ "تم الانتهاء من تصدير %s ."
+ "تم الانتهاء من تصدير جهات الاتصال."
+ "تم إلغاء تصدير %s ."
+ "تصدير بيانات جهة الاتصال"
+ "يجري تصدير بيانات جهات الاتصال إلى: %s ."
+ "تعذر الحصول على معلومات قاعدة البيانات."
+ "ليست هناك أية جهات اتصال قابلة للتصدير. إذا كانت لديك جهات اتصال على هاتفك، فإن بعض موفري البيانات لا يسمحون بتصدير جهات الاتصال من الهاتف."
+ "لم يبدأ مؤلف vCard بشكل صحيح."
+ "تعذر التصدير"
+ "لم يتم تصدير بيانات جهة الاتصال.\nالسبب: \"%s \""
+ "جارٍ استيراد %s "
+ "تعذرت قراءة بيانات vCard"
+ "تم إلغاء قراءة بيانات vCard"
+ "تم الانتهاء من استيراد ملف vCard %s "
+ "تم إلغاء استيراد %s "
+ "سيتم استيراد %s بعد قليل."
+ "سيتم استيراد الملف بعد قليل."
+ "تم رفض طلب استيراد vCard. أعد المحاولة لاحقًا."
+ "سيتم تصدير %s بعد قليل."
+ "سيتم تصدير الملف بعد قليل."
+ "تم رفض طلب تصدير vCard. أعد المحاولة لاحقًا."
+ "جهة اتصال"
+ "يجري تخزين ملفات vCard مؤقتًا على وحدة تخزين مؤقتة محلية. سيبدأ الاستيراد الفعلي قريبًا."
+ "تعذر استيراد vCard."
+ "لم يتم العثور على ملف vCard على بطاقة SD."
+ "استلام ج اتص.NFC"
+ "تصدير جهات الاتصال؟"
+ "تخزين مؤقت"
+ "تعذر فحص بطاقة SD. (السبب: \"%s \")"
+ "جارٍ استيراد %s /%s : %s "
+ "تصدير إلى ملف vcf."
+ "ترتيب بحسب"
+ "الاسم الأول"
+ "اسم العائلة"
+ "تنسيق الاسم"
+ "الاسم الأول أولاً"
+ "اسم العائلة /الاسم الأول"
+ "مشاركة جهات الاتصال المرئية"
+ "أخفقت مشاركة جهات الاتصال المرئية."
+ "استيراد/تصدير جهات اتصال"
+ "استيراد جهات الاتصال"
+ "لا يمكن مشاركة جهة الاتصال هذه."
+ "البحث"
+ "جهات الاتصال المعروضة"
+ "جهات الاتصال المعروضة"
+ "تحديد عرض مخصص"
+ "البحث عن جهات اتصال"
+ "المفضلة"
+ "ليست هناك جهات اتصال."
+ "ليست هناك جهات اتصال مرئية."
+ "ليست هناك مفضلة."
+ "ليست هناك جهات اتصال في %s "
+ "محو قائمة من يتصل بهم كثيرًا"
+ "تحديد شريحة SIM"
+ "الحسابات"
+ "استيراد/تصدير"
+ "عبر %1$s "
+ "%1$s عبر %2$s "
+ "إيقاف البحث"
+ "محو البحث"
+ "خيارات عرض جهات الاتصال"
+ "الحساب"
+ "استخدام هذا للمكالمات دائمًا"
+ "الاتصال باستخدام"
+ "مكالمة مع ملاحظة"
+ "اكتب ملاحظة لإرسالها مع المكالمة ..."
+ "الإرسال والاتصال"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-az-rAZ/strings.xml b/ContactsCommon/res/values-az-rAZ/strings.xml
new file mode 100644
index 0000000..7dad660
--- /dev/null
+++ b/ContactsCommon/res/values-az-rAZ/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Mətn kopyalandı"
+ "Mübadilə buferinə köçürün"
+ "Zəng %s "
+ "Ev zəngi"
+ "Mobil zəng"
+ "Iş zəngi"
+ "İş faksına zəng edin"
+ "Ev faksına zəng edin"
+ "Peycer zəngi"
+ "Zəng"
+ "Geriyə zəng"
+ "Avtomobil zəngi"
+ "Əsas şirkət zəngi"
+ "ISDN zəng"
+ "Əsas zəng"
+ "Faks zəngi"
+ "Radio zəngi"
+ "Teleks zəngi"
+ "TTY/TDD zəngi"
+ "İş mobil telefonuna zəng"
+ "İş peycerinə zəng"
+ "Zəng %s "
+ "MMS zəngi"
+ "Mətn %s "
+ "Ev mətni"
+ "Mobil mətn"
+ "İş nömrəsinə mesaj"
+ "İş faksı mətni"
+ "Ev faksı mətni"
+ "Peycer nömrəsinə mesaj"
+ "Mətn"
+ "Geri zəng nömrəsinə mesaj"
+ "Avtomobil nömrəsinə mesaj"
+ "Əsas şirkət nömrəsinə mesaj"
+ "ISDN nömrəsinə mesaj"
+ "Əsas mətn"
+ "Faks nömrəsinə mesaj"
+ "Radio nömrəsinə mesaj"
+ "Teleks nömrəsinə mesaj"
+ "TTY/TDD nömrəsinə mesaj"
+ "İş mobil nömrəsinə mesaj"
+ "İş peyceri nömrəsinə mesaj"
+ "Mətn %s "
+ "MMS nömrəsinə mesaj"
+ "Video zəng edin"
+ "Tez-tez ünsiyyət qurulanlar silinsin?"
+ "Əlaqələr və Telefon proqramlarında tez-tez əlaqə saxlanılanların siyahısını siləcəksiniz və fraqmentdən ünvanlama seçimlərinizi öyrənmək üçün e-poçt proqramlarını məcbur edəcəksiniz."
+ "Tez-tez ünsiyyət qurulanlar silinir..."
+ "Əlçatımlı"
+ "Kənar"
+ "Məşğul"
+ "Kontaktlar"
+ "Digər"
+ "Kataloq"
+ "Bütün kontaktlar"
+ "Mən"
+ "Axtarılır..."
+ "%d ədəddən çox tapılıb."
+ "Kontakt yoxdur"
+
+ %d tapıldı
+ - 1 tapıldı
+
+ "%1$s üçün tez kontakt"
+ "(ad yoxdur)"
+ "Tez-tez zəng edilən"
+ "Tez-tez əlaqə saxlanılan"
+ "Kontakta baxın"
+ "Telefon nömrələri olan bütün kontaktlar"
+ "Güncəlləşmələri göstər"
+ "Yalnız telefon, sinxronsuz"
+ "Ad"
+ "Nik"
+ "Ad"
+ "Ad"
+ "Soyad"
+ "Ad prefiksi"
+ "Atasının adı"
+ "Ad suffiksi"
+ "Fonetik adı"
+ "Fonetik ad"
+ "Fonetik ata adı"
+ "Fonetik soyad"
+ "Telefon"
+ "E-poçt"
+ "Ünvan"
+ "IM"
+ "Təşkilat"
+ "Əlaqə"
+ "Xüsusi tarixlər"
+ "Mətn mesajı"
+ "Ünvan"
+ "Şirkət"
+ "Başlıq"
+ "Qeydlər"
+ "SIP"
+ "Veb sayt"
+ "Qruplar"
+ "Evə e-məktub"
+ "Mobil e-poçt"
+ "İşə e-məktub"
+ "E-poçt"
+ "E-poçt %s "
+ "E-poçt"
+ "Küçə"
+ "PO Box"
+ "Qonşuluq"
+ "Şəhər"
+ "Dövlət"
+ "Poçt indeksi"
+ "Ölkə"
+ "Ev ünvanına baxın"
+ "İş ünvanına baxın"
+ "Ünvana baxın"
+ "%s ünvanına baxın"
+ "AIM üzərindən çat"
+ "Windows Live üzərindən çat"
+ "Yahoo üzərindən çat"
+ "Skype üzərindən çat"
+ "QQ üzərindən çat"
+ "Google Söhbət üzərindən çat"
+ "ICQ üzərindən çat"
+ "Jabber üzərindən çat"
+ "Çat"
+ "sil"
+ "Ad sahələrini genişləndirin və yığcamlaşdırın"
+ "Bütün kontaktlar"
+ "Ulduzlu"
+ "Fərdiləşdirin"
+ "Kontakt"
+ "Bütün digər kontaktlar"
+ "Bütün kontaktlar"
+ "Sinxronizasiya qrupunu silin"
+ "Sinx qrup əlavə edin"
+ "Daha çox qrup..."
+ "\"%s \" sinxronizasiyadan silinməsi istənilən qrupsuz kontaktları sinxronizasiyadan siləcək."
+ "Displey seçənəkləri yadda saxlanır..."
+ "Hazırdır"
+ "Ləğv et"
+ "%s adındakı kontaktlar"
+ "Fərdi baxışdan kontakt"
+ "Tək kontakt"
+ "Hesab altında kontakt yaradın"
+ "SIM kartdan import edin"
+ "SIM-dən import edin: ^1 - ^2 "
+ "SIM-dən import edin: %1$s "
+ ".vcf fayldan import edin"
+ "%s importu ləğv olunsun?"
+ "%s eksportu ləğv edilsin?"
+ "Vizit kart importunu/eksportunu ləğv etmək mümkün olmadı"
+ "Naməlum xəta."
+ "\"%s \" açmaq olmadı: %s ."
+ "Eksportçunu başlatmaq olmadı: \"%s \"."
+ "Eksport edilə bilən heç bir kontakt yoxdur"
+ "Tələb olunan icazəni deaktiv etmisiniz."
+ "Eksport zamanı xəta baş verdi: \"%s \"."
+ "Tələb olunan fayl adı çox uzundur (\"%s \")"
+ "SD kartda çox vizit kartı faylları var."
+ "I/O xəta"
+ "Yetərli qədər yaddaş yoxdur. Fayl çox böyük ola bilər.."
+ "Gözlənilməyən səbəbə görə vizit kart təhlil edilə bilmədi."
+ "Format dəstəklənmir."
+ "Verilmiş vizit kartların meta məlumatları toplana bilmədi."
+ "Bir və daha çox fayl İmport edilə bilməz (%s)."
+ "%s eksportu bitdi"
+ "Kontaktların eksportu tamamlandı."
+ "%s eksportu ləğv edildi"
+ "Kontakt datası eksport olunur"
+ "Kontakt datanız bura eksport olunur: %s ."
+ "Verilənlər bazası məlumatları əldə oluna bilmədi."
+ "Eksport edilə bilən kontakt yoxdur. Əgər telefonunuzda kontaktlarınız varsa, bəzi data provayderləri kontaktların telefondan eksport olunmasına icazə verməyə bilər."
+ "Vizit kart tərtibçisi düzgün başlamadı."
+ "Eksport edilə bilmədi"
+ "Kontakt datası eksport edilmədi.\nSəbəb: \"%s \""
+ "%s import edilir"
+ "Vizit kart datası oxuna bilmədi"
+ "Vizit kart datasının oxunması ləğv edildi"
+ "%s vizit kart İmportu qurtardı"
+ "%s importu ləğv edildi"
+ "%s tezliklə import olunacaq."
+ "Bu fayl tezliklə import ediləcək."
+ "Vizit kart import sorğusu rədd edildi. Yenidən cəhd edin."
+ "%s tezliklə eksport ediləcək."
+ "Fayl az sonra eksport ediləcək."
+ "Vizit kart eksport sorğusu rədd edildi. Daha sonra cəhd edin."
+ "kontakt"
+ "Vizit kart yerli müvəqqəti yaddaşa keşlənir. Hazırkı import tezliklə başlayacaq."
+ "Vizit kart import edilə bilmədi."
+ "SD kartda heç bir vizit kart tapılmadı."
+ "Kontakt NFC üzərindən alınıb"
+ "Kontaktlar eksport olunsun?"
+ "Keşləndirilir"
+ "SD kart skan oluna bilmədi. (Səbəb: \"%s \")"
+ "İmport edilir: %s /%s : %s "
+ ".vcf fayldan eksport edin"
+ "Bunlardan biri üzrə sırala"
+ "Ad"
+ "Soyad"
+ "Ad formatı"
+ "Ad ilk yazılsın"
+ "Soyad ilk yazılsın"
+ "Görünən kontaktları paylaşın"
+ "Görünən kontaktları paylaşmaq olmadı"
+ "Kontaktları import/eksport edin"
+ "Kontaktları İmport edin"
+ "Bu kontakt paylaşıla bilməz."
+ "Axtar"
+ "Göstərilməli kontaktlar"
+ "Göstərilməli kontaktlar"
+ "Fərdi görüntünü müəyyənləşdirin"
+ "Kontaktlar tapın"
+ "Favoritlər"
+ "Kontakt yoxdur."
+ "Görünən kontakt yoxdur."
+ "Favoritlər yoxdur."
+ "%s daxilində kontakt yoxdur"
+ "Müntəzəmləri təmizləyin"
+ "SIM kart seçin"
+ "Hesablar"
+ "İmport/eksport"
+ "%1$s vasitəsilə"
+ "%2$s vasitəsilə %1$s "
+ "axtarışı dayandırın"
+ "Axtarışı təmizləyin"
+ "Kontakt göstərilmə seçimləri"
+ "Hesab"
+ "Həmişə bu zənglər üçün istifadə edin"
+ "Çağrı üçün SIM:"
+ "Qeyd ilə zəng edin"
+ "Zəng ilə göndərmək üçün qeyd yazın..."
+ "GÖNDƏRİN & ZƏNG EDİN"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-bg/strings.xml b/ContactsCommon/res/values-bg/strings.xml
new file mode 100644
index 0000000..0531dee
--- /dev/null
+++ b/ContactsCommon/res/values-bg/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Текстът е копиран"
+ "Копиране в буферната памет"
+ "Обаждане на %s "
+ "Обаждане на домашен"
+ "Обаждане на мобилен"
+ "Обаждане на служебен"
+ "Обаждане на служебен факс"
+ "Обаждане на домашен факс"
+ "Обаждане на пейджър"
+ "Обаждане на"
+ "Обаждане на обратно обаждане"
+ "Обаждане на номер в кола"
+ "Обаждане на основен служебен"
+ "Обаждане на ISDN"
+ "Обаждане на основен"
+ "Обаждане на факс"
+ "Обаждане на радио"
+ "Обаждане на телекс"
+ "Обаждане на TTY/TDD"
+ "Обаждане на служебен мобилен"
+ "Обаждане на служебен пейджър"
+ "Обаждане на %s "
+ "Обаждане на MMS"
+ "Изпращане на SMS на %s "
+ "Изпращaне на SMS на домашен"
+ "Изпращане на SMS на мобилен"
+ "Изпращане на SMS на служебен"
+ "Изпращане на SMS на служебен факс"
+ "Изпращане на SMS на домашен факс"
+ "Изпращане на SMS на пейджър"
+ "Изпращане на SMS на"
+ "Изпращане на SMS на отговорилия"
+ "Изпращане на SMS на номер в кола"
+ "Изпращaне на SMS на основен служебен"
+ "Изпращaне на SMS на ISDN"
+ "Изпращане на SMS на основен"
+ "Изпращане на SMS на факс"
+ "Изпращане на SMS на радиотелефон"
+ "Изпращане на SMS на телекс"
+ "Изпращане на SMS на TTY/TDD"
+ "Изпращане на SMS на служебен мобилен"
+ "Изпращане на SMS на служебен пейджър"
+ "Изпращане на SMS на %s "
+ "Изпращане на SMS на MMS"
+ "Провеждане на видеообаждане"
+ "Да се изчистят ли често търсените?"
+ "Ще изчистите списъка с често търсените в приложенията Контакти и Телефон и ще принудите приложенията за имейл да научат предпочитанията ви за адресите, започвайки отначало."
+ "Често търсените се изчистват…"
+ "Налице"
+ "Отсъства"
+ "Зает/а"
+ "Контакти"
+ "Други"
+ "Директория"
+ "Всички контакти"
+ "Аз"
+ "Търси се…"
+ "Намерени са повече от %d ."
+ "Няма контакти"
+
+ - Намерени са
%d
+ - Намерен е 1
+
+ "Бърз контакт за %1$s "
+ "(Няма име)"
+ "Чести обаждания"
+ "Често търсени"
+ "Преглед на контакта"
+ "Всички контакти с телефонни номера"
+ "Преглед на актуализациите"
+ "Само в тел., несинхрон."
+ "Име"
+ "Псевдоним"
+ "Име"
+ "Собствено име"
+ "Фамилно име"
+ "Обръщение"
+ "Презиме"
+ "Титла"
+ "Име (фонетично)"
+ "Собствено име (фонетично)"
+ "Презиме (фонетично)"
+ "Фамилно име (фонетично)"
+ "Телефон"
+ "Имейл"
+ "Адрес"
+ "Незабавни съобщения"
+ "Организация"
+ "Връзки"
+ "Специални дати"
+ "Текстово съобщение"
+ "Адрес"
+ "Фирма"
+ "Наименование"
+ "Бележки"
+ "SIP"
+ "Уебсайт"
+ "Групи"
+ "Изпращане на имейл до домашен"
+ "Изпращане на имейл до мобилен"
+ "Изпращане на имейл до служебен"
+ "Изпращане на имейл"
+ "Изпращане на имейл до %s "
+ "Изпращане на имейл"
+ "Улица"
+ "Пощенска кутия"
+ "Квартал"
+ "Град"
+ "Щат"
+ "Пощенски код"
+ "Държава"
+ "Преглед на домашния адрес"
+ "Преглед на служебния адрес"
+ "Преглед на адреса"
+ "Преглед на адреса: %s "
+ "Чат по AIM"
+ "Чат по Windows Live"
+ "Чат по Yahoo"
+ "Чат по Skype"
+ "Чат по QQ"
+ "Чат по Google Talk"
+ "Чат по ICQ"
+ "Чат по Jabber"
+ "Чат"
+ "изтриване"
+ "Разгъване или свиване на полетата с името"
+ "Всички контакти"
+ "Със звезда"
+ "Персонализиране"
+ "Контакт"
+ "Всички други контакти"
+ "Всички контакти"
+ "Премахване на група за синхронизиране"
+ "Добавяне на група за синхронизиране"
+ "Още групи…"
+ "Ако синхронизирането на „%s “ спре, то ще спре и за негрупираните контакти."
+ "Опциите за показване се запазват…"
+ "Готово"
+ "Отказ"
+ "Контакти във: %s "
+ "Контакти в персонал. изглед"
+ "Един контакт"
+ "Създаване на контакт в профил"
+ "Импортиране от SIM карта"
+ "Импортиране от SIM картата „^1 “ – ^2 "
+ "Импортиране от SIM картата „%1$s “"
+ "Импортиране от .vcf файл"
+ "Да се анулира ли импортирането на „%s “?"
+ "Да се анулира ли експортирането на „%s “?"
+ "Импорт./експорт. не можа да се анулира"
+ "Неизвестна грешка."
+ "„%s “ не можа да се отвори: %s ."
+ "Експортирането не можа да започне: %s ."
+ "Няма контакт, позволяващ експортиране."
+ "Деактивирахте задължително разрешение."
+ "При експортирането възникна грешка: %s ."
+ "Поисканото име на файла е твърде дълго („%s “)."
+ "На SD картата има твърде много vCard файлове."
+ "I/O грешка"
+ "Няма достатъчно памет. Файлът може да е твърде голям."
+ "vCard не можа да се анализира по неочаквана причина."
+ "Форматът не се поддържа."
+ "Метаинформацията от даден/и vCard файл/ове не можа да бъде събрана."
+ "Един или повече файла не можаха да бъдат импортирани (%s)."
+ "Експортирането на „%s “ завърши."
+ "Експортирането на контактите завърши."
+ "Експортирането на „%s “ е анулирано."
+ "Експортиране на данни за контакти"
+ "Данните за контактите ви се експортират във: „%s “."
+ "Информацията за базата от данни не можа да бъде получена."
+ "Няма контакти, позволяващи експортиране. Ако в телефона си имате контакти, е възможно някои доставчици на данни да не позволяват експортирането им извън него."
+ "Създателят на vCard не се стартира правилно."
+ "Не се експортираха"
+ "Данните за контакта не бяха експортирани.\nПричина: %s "
+ "%s се импортира"
+ "Данните от vCard не бяха прочетени"
+ "Четенето на данни от vCard е анулирано"
+ "Импортирането на vCard файла „%s “ завърши"
+ "Импортирането на „%s “ е анулирано"
+ "„%s “ ще се импортира скоро."
+ "Файлът ще се импортира скоро."
+ "Заявката за импортиране на vCard бе отхвърлена. Опитайте отново по-късно."
+ "„%s “ ще се експортира скоро."
+ "Файлът ще се експортира скоро."
+ "Заявката за експортиране на vCard бе отхвърлена. Опитайте отново по-късно."
+ "контакт"
+ "vCard се кешира/т във временно локално хранилище. Самото импортиране ще започне скоро."
+ "vCard не можа да се импортира."
+ "На SD картата не бе намерен vCard файл."
+ "Контакти от КБП"
+ "Да се експортират ли контактите?"
+ "Кешира се"
+ "SD картата не можа да бъде сканирана. (Причина: %s )"
+ "Импортира/т се %s /%s : %s "
+ "Експорт. като .vcf файл"
+ "Сортиране по"
+ "Собствено име"
+ "Фамилно име"
+ "Формат на името"
+ "Първо собственото име"
+ "Първо фамилното име"
+ "Споделяне на видимите контакти"
+ "Споделянето на видимите контакти не бе успешно."
+ "Импортиране/eкспортиране на контакти"
+ "Импортиране на контактите"
+ "Този контакт не може да бъде споделен."
+ "Търсене"
+ "Контакти за показване"
+ "Контакти за показване"
+ "Определяне на изглед"
+ "Намиране на контакти"
+ "Любими"
+ "Няма контакти."
+ "Няма видими контакти."
+ "Няма любими."
+ "Няма контакти във: %s "
+ "Изчистване на често търсените"
+ "Избиране на SIM карта"
+ "Профили"
+ "Импортиране/Експортиране"
+ "чрез %1$s "
+ "%1$s чрез %2$s "
+ "спиране на търсенето"
+ "Изчистване на търсенето"
+ "Опции за показване на контактите"
+ "Профил"
+ "Винаги да се използва за обаждания"
+ "Обаждане чрез"
+ "Обаждане, включващо бележка"
+ "Напишете придружаваща бележка, която ще се изпрати при извършване на обаждането..."
+ "ИЗПРАЩАНЕ И ОБАЖДАНЕ"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-bn-rBD/strings.xml b/ContactsCommon/res/values-bn-rBD/strings.xml
new file mode 100644
index 0000000..f7fb0ea
--- /dev/null
+++ b/ContactsCommon/res/values-bn-rBD/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "পাঠ্য অনুলিপি হয়েছে"
+ "ক্লিপবোর্ডে প্রতিলিপি করুন"
+ "%s এ কল করুন"
+ "বাড়ির নম্বরে কল করুন"
+ "মোবাইল নম্বরে কল করুন"
+ "কর্মক্ষেত্রের নম্বরে কল করুন"
+ "কর্মক্ষেত্রের ফ্যাক্স নম্বরে কল করুন"
+ "বাড়ির ফ্যাক্স নম্বরে কল করুন"
+ "পেজার নম্বরে কল করুন"
+ "কল করুন"
+ "কলব্যাক করার নম্বরে কল করুন"
+ "গাড়ির ফোন নম্বরে কল করুন"
+ "কোম্পানির প্রধান নম্বরে কল করুন"
+ "ISDN নম্বরে কল করুন"
+ "প্রধান নম্বরে কল করুন"
+ "ফ্যাক্স নম্বরে কল করুন"
+ "রেডিওর ফোন নম্বরে কল করুন"
+ "টেলেক্স নম্বরে কল করুন"
+ "TTY/TDD নম্বরে কল করুন"
+ "কর্মক্ষেত্রের মোবাইল নম্বরে কল করুন"
+ "কর্মক্ষেত্রের পেজার নম্বরে কল করুন"
+ "%s এ কল করুন"
+ "MMS নম্বরে কল করুন"
+ "%s নম্বরে পাঠ্য বার্তা পাঠান"
+ "ঘরের ফোন নম্বরে পাঠ্য বার্তা পাঠান"
+ "মোবাইল নম্বরে পাঠ্য বার্তা পাঠান"
+ "কর্মক্ষেত্রের নম্বরে পাঠ্য বার্তা পাঠান"
+ "কার্মক্ষেত্রের ফ্যাক্স নম্বরে পাঠ্য বার্তা পাঠান"
+ "বাড়ির ফ্যাক্স নম্বরে পাঠ্য বার্তা পাঠান"
+ "পেজারের নম্বরে পাঠ্য বার্তা পাঠান"
+ "পাঠ্য বার্তা"
+ "কলব্যাক করার নম্বরে পাঠ্য বার্তা পাঠান"
+ "গাড়ির ফোন নম্বরে পাঠ্য বার্তা পাঠান"
+ "কোম্পানির প্রধান ফোন নম্বরে পাঠ্য বার্তা পাঠান"
+ "ISDN ফোন নম্বরে পাঠ্য বার্তা পাঠান"
+ "প্রধান নম্বরে পাঠ্য বার্তা পাঠান"
+ "ফ্যাক্স নম্বরে পাঠ্য বার্তা পাঠান"
+ "রেডিওর ফোন নম্বরে পাঠ্য বার্তা পাঠান"
+ "টেলেক্স নম্বরে পাঠ্য বার্তা পাঠান"
+ "TTY/TDD ফোন নম্বরে পাঠ্য বার্তা পাঠান"
+ "কর্মক্ষেত্রের মোবাইলে পাঠ্য বার্তা পাঠান"
+ "কর্মক্ষেত্রের পেজারে পাঠ্য বার্তা পাঠান"
+ "%s নম্বরে পাঠ্য বার্তা পাঠান"
+ "MMS ফোন নম্বরে পাঠ্য বার্তা পাঠান"
+ "ভিডিও কল করুন"
+ "ঘন ঘন যোগাযোগ করা হয়েছে এমন পরিচিতিগুলিকে সাফ করবেন?"
+ "পরিচিতি এবং ফোন অ্যাপ্লিকেশানগুলি থেকে আপনি ঘন ঘন যোগাযোগ করা পরিচিতির তালিকা সাফ করবেন, এবং ইমেল অ্যাপ্লিকেশানগুলিকে আবার শুরু থেকে আপনার ঠিকানা অভিরুচি জানতে বাধ্য করবেন৷"
+ "ঘন ঘন যোগাযোগ করা পরিচিতিগুলিকে সাফ করা হচ্ছে…"
+ "উপলব্ধ"
+ "অন্যত্র"
+ "ব্যস্ত"
+ "পরিচিতিগুলি"
+ "অন্যান্য"
+ "ডিরেক্টরি"
+ "সকল পরিচিতি"
+ "আমি"
+ "অনুসন্ধান করছে..."
+ "%d টির থেকে বেশি খুঁজে পাওয়া গিয়েছে৷"
+ "কোনো পরিচিতি নেই"
+
+ %d টি খুঁজে পাওয়া গেছে
+ %d টি খুঁজে পাওয়া গেছে
+
+ "%1$s এর জন্য দ্রুত পরিচিতি"
+ "(কোনও নাম নেই)"
+ "ঘন ঘন কল করা হয়েছে"
+ "ঘন ঘন যোগাযোগ করা হয়েছে"
+ "পরিচিতি দেখুন"
+ "ফোন নম্বর সহ সমস্ত পরিচিতি"
+ "আপডেটগুলি দেখুন"
+ "শুধুমাত্র ফোন, সমন্বয় করা নেই"
+ "নাম"
+ "ডাকনাম"
+ "নাম"
+ "প্রথম নাম"
+ "পারিবারিক নাম"
+ "নামের আগের অংশ"
+ "মাঝের নাম"
+ "নামের পরের অংশ"
+ "উচ্চারণগত নাম"
+ "উচ্চারণগত প্রথম নাম"
+ "উচ্চারণগত মাঝের নাম"
+ "উচ্চারণগত পারিবারিক নাম"
+ "ফোন"
+ "ইমেল"
+ "ঠিকানা"
+ "IM"
+ "সংগঠন"
+ "সম্পর্ক"
+ "বিশেষ তারিখগুলি"
+ "পাঠ্য বার্তা"
+ "ঠিকানা"
+ "কোম্পানি"
+ "নাম"
+ "দ্রষ্টব্য"
+ "SIP"
+ "ওয়েবসাইট"
+ "গোষ্ঠীগুলি"
+ "বাড়ির ইমেল ঠিকানায় ইমেল করুন"
+ "মোবাইলের ইমেল ঠিকানায় ইমেল করুন"
+ "কর্মক্ষেত্রের ইমেল ঠিকানায় ইমেল করুন"
+ "ইমেল করুন"
+ "%s এ ইমেল করুন"
+ "ইমেল"
+ "রাস্তা"
+ "পোস্ট বক্স"
+ "নিকটবর্তী অঞ্চল"
+ "শহর"
+ "রাজ্য"
+ "পিন কোড"
+ "দেশ"
+ "বাড়ির ঠিকানা দেখুন"
+ "কর্মক্ষেত্রের ঠিকানা দেখুন"
+ "ঠিকানা দেখুন"
+ "%s ঠিকানা দেখুন"
+ "AIM ব্যবহার করে চ্যাট করুন"
+ "Windows Live ব্যবহার করে চ্যাট করুন"
+ "Yahoo ব্যবহার করে চ্যাট করুন"
+ "Skype ব্যবহার করে চ্যাট করুন"
+ "QQ ব্যবহার করে চ্যাট করুন"
+ "Google Talk ব্যবহার করে চ্যাট করুন"
+ "ICQ ব্যবহার করে চ্যাট করুন"
+ "Jabber ব্যবহার করে চ্যাট করুন"
+ "চ্যাট করুন"
+ "মুছুন"
+ "নামের ক্ষেত্রটিকে প্রসারিত বা সঙ্কুচিত করুন"
+ "সকল পরিচিতি"
+ "তারকা চিহ্নিত"
+ "নিজের সুবিধামতো করুন"
+ "পরিচিতি"
+ "অন্যান্য সকল পরিচিতি"
+ "সকল পরিচিতি"
+ "সমন্বয় গোষ্ঠী সরান"
+ "সমন্বয় গোষ্ঠী যোগ করুন"
+ "আরো গোষ্ঠী…"
+ "সমন্বয় থেকে \"%s \" সরানো হলে তা সমন্বয় থেকে যেকোনো অগোষ্ঠীবদ্ধ পরিচিতিগুলিকেও সরাবে৷"
+ "প্রদর্শনের বিকল্পগুলি সংরক্ষণ করা হচ্ছে..."
+ "সম্পন্ন হয়েছে"
+ "বাতিল করুন"
+ "%s এ পরিচিতিগুলি"
+ "কাস্টম দৃশ্যে পরিচিতিগুলি"
+ "একক পরিচিতি"
+ "অ্যাকাউন্টের অধীনে পরিচিতি তৈরি করুন"
+ "সিম কার্ড থেকে আমদানি করুন"
+ "^1 SIM থেকে আমদানি করুন - ^2 "
+ "%1$s SIM থেকে আমদানি করুন"
+ ".vcf ফাইল থেকে আমদানি করুন"
+ "%s এর আমদানি বাতিল করবেন?"
+ "%s এর রপ্তানি বাতিল করবেন?"
+ "vCard এর আমদানি/রপ্তানি বাতিল করা যায়নি"
+ "অজানা ত্রুটি৷"
+ "\"%s \" খোলা যায়নি: %s ৷"
+ "রপ্তানিকারক শুরু করা যায়নি: \"%s \"৷"
+ "রপ্তানিযোগ্য কোনো পরিচিতি নেই৷"
+ "আপনি একটি প্রয়োজনীয় অনুমতি অক্ষম করেছেন৷"
+ "রপ্তানির সময় একটি ত্রুটি ঘটেছে: \" %s \"৷"
+ "প্রয়োজনীয় ফাইলের নামটি (\"%s \") অত্যন্ত দীর্ঘ৷"
+ "SD কার্ডে অনেকগুলি vCard ফাইল রয়েছে৷"
+ "I/O ত্রুটি"
+ "যথেষ্ট মেমরি নেই৷ ফাইলটি খুব বড় হতে পারে৷"
+ "একটি অপ্রত্যাশিত কারণে vCard পার্জ করা যায়নি৷"
+ "এই ফর্ম্যাটটি সমর্থিত নয়৷"
+ "প্রদত্ত vCard ফাইলের(গুলির) মেটা তথ্য সংগ্রহ করা যায়নি৷"
+ "একটি বা একাধিক (%s) ফাইল আমদানি করা যাবে না৷"
+ "%s রপ্তানি করা সম্পন্ন হয়েছে৷"
+ "পরিচিতি রপ্তানি করা সম্পন্ন হয়েছে৷"
+ "%s রপ্তানি করা বাতিল হয়েছে৷"
+ "পরিচিতির তথ্য রপ্তানি করা হচ্ছে"
+ "আপনার পরিচিতির ডেটা রপ্তানি করা হচ্ছে: %s ৷"
+ "ডেটাবেসের তথ্য পাওয়া যায়নি৷"
+ "এখানে রপ্তানিযোগ্য কোনো পরিচিতি নেই৷ আপনার ফোনে পরিচিতি থাকলে, কিছু ডেটা সরবরাহকারী আপনার ফোন থেকে সেই পরিচিতিগুলিকে রপ্তানি করা মঞ্জুর নাও করতে পারে৷"
+ "vCard কম্পোজার সঠিকভাবে শুরু করা হয়নি৷"
+ "রপ্তানি করা যায়নি"
+ "পরিচিতির তথ্য রপ্তানি করা যায়নি৷\nকারণ: \"%s \""
+ "%s আমদানি করা হচ্ছে"
+ "vCard ডেটা পড়া যায়নি"
+ "vCard ডেটা পড়া বাতিল করা হয়েছে"
+ "vCard %s আমদানি করা সমাপ্ত হয়েছে"
+ "%s আমদানি করা বাতিল করা হয়েছে"
+ "%s শীঘ্রই আমদানি করা হবে৷"
+ "ফাইলটি শীঘ্রই আমদানি করা হবে৷"
+ "vCard আমদানি করার অনুরোধ প্রত্যাখ্যাত হয়েছে৷ পরে আবার চেষ্টা করুন৷"
+ "%s শীঘ্রই রপ্তানি করা হবে৷"
+ "ফাইলটি শীঘ্রই রপ্তানি করা হবে৷"
+ "vCard রপ্তানি করার অনুরোধ প্রত্যাখ্যাত হয়েছে৷ পরে আবার চেষ্টা করুন৷"
+ "পরিচিতি"
+ "স্থানীয় অস্থায়ী সংগ্রহস্থলে vCard(গুলি)কে ক্যাশ করা হচ্ছে৷ প্রকৃত আমদানি শীঘ্রই শুরু হবে৷"
+ "vCard আমদানি করতে পারা যায় নি৷"
+ "SD কার্ডে কোনো vCard ফাইল পাওয়া যায়নি৷"
+ "NFC এর মাধ্যমে পরিচিতি প্রাপ্ত হয়েছে"
+ "পরিচিতিগুলি রপ্তানি করবেন?"
+ "ক্যাশ করা হচ্ছে"
+ "SD কার্ডটিকে স্ক্যান করতে করতে পারা যায় নি৷ (কারণ: \"%s \")"
+ "আমদানি করা হচ্ছে, %s /%s : %s "
+ ".vcf ফাইলে রপ্তানি করুন"
+ "এই অনুসারে বাছুন"
+ "প্রথম নাম"
+ "পারিবারিক নাম"
+ "নামের বিন্যাস"
+ "প্রথমে আদ্য নাম"
+ "প্রথমে পারিবারিক নাম"
+ "দৃশ্যমান পরিচিতিগুলিকে ভাগ করুন"
+ "দৃশ্যমান পরিচিতিগুলি ভাগ করতে ব্যর্থ হয়েছে৷"
+ "পরিচিতিগুলি আমদানি/রপ্তানি করুন"
+ "পরিচিতিগুলি আমদানি করুন"
+ "এই পরিচিতিটিকে ভাগ করা যাবে না৷"
+ "অনুসন্ধান করুন"
+ "দেখানোর জন্য পরিচিতিগুলি"
+ "দেখানোর জন্য পরিচিতিগুলি"
+ "কাস্টম দর্শন নির্ধারণ করুন"
+ "পরিচিতিগুলি খুঁজুন"
+ "পছন্দগুলি"
+ "কোনো পরিচিতি নেই৷"
+ "কোনো দৃশ্যমান পরিচিতি নেই৷"
+ "কোনো পছন্দসই নেই৷"
+ "%s এ কোনো পরিচিতি নেই"
+ "পুনরাবৃত্তি সাফ করুন"
+ "সিম কার্ড নির্বাচন করুন"
+ "অ্যাকাউন্টগুলি"
+ "আমদানি/রপ্তানি"
+ "%1$s এর মাধ্যমে"
+ "%2$s এর মাধ্যমে %1$s "
+ "অনুসন্ধান বন্ধ করুন"
+ "অনুসন্ধান সাফ করুন"
+ "পরিচিতি প্রদর্শনের বিকল্পগুলি"
+ "অ্যাকাউন্ট"
+ "কলের জন্য সবসময় এটি ব্যবহার করুন"
+ "এর মাধ্যমে কল করুন"
+ "একটি নোট সহ কল করুন"
+ "কলের সাথে পাঠানোর জন্য একটি নোট লিখুন ..."
+ "পাঠান এবং কল করুন"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ca/strings.xml b/ContactsCommon/res/values-ca/strings.xml
new file mode 100644
index 0000000..6d02454
--- /dev/null
+++ b/ContactsCommon/res/values-ca/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Text copiat"
+ "Copia al porta-retalls"
+ "Truca al %s "
+ "Truca a casa"
+ "Truca al mòbil"
+ "Truca a la feina"
+ "Truca al fax de la feina"
+ "Truca al fax de casa"
+ "Truca al cercapersones"
+ "Truca"
+ "Truca a un número de devolució de trucada"
+ "Truca al cotxe"
+ "Truca al telèfon principal de l\'empresa"
+ "Truca a l\'XDSI"
+ "Truca al telèfon principal"
+ "Truca al fax"
+ "Truca a la ràdio"
+ "Truca al tèlex"
+ "Truca a TTY/TDD"
+ "Truca al mòbil de la feina"
+ "Truca al cercapersones de la feina"
+ "Truca al %s "
+ "Truca al número MMS"
+ "Envia SMS al %s "
+ "Envia un SMS a casa"
+ "Envia un SMS al mòbil"
+ "Envia un SMS a la feina"
+ "Envia un SMS al fax de la feina"
+ "Envia un SMS al fax de casa"
+ "Envia un SMS al cercapersones"
+ "Envia un SMS"
+ "Envia un SMS a un número de devolució de trucada"
+ "Envia un SMS al cotxe"
+ "Envia un SMS al telèfon principal de l\'empresa"
+ "Envia un SMS a l\'XDSI"
+ "Envia un SMS al telèfon principal"
+ "Envia un SMS al fax"
+ "Envia un SMS a la ràdio"
+ "Envia un SMS al tèlex"
+ "Envia un SMS al TTY/TDD"
+ "Envia un SMS al telèfon mòbil de la feina"
+ "Envia un SMS al cercapersones de la feina"
+ "Envia un SMS a %s "
+ "Envia un MMS"
+ "Fes una videotrucada"
+ "Vols esborrar els contactes freqüents?"
+ "S\'esborrarà la llista de contactes més freqüents a les aplicacions Contactes i Telèfon i es forçarà les aplicacions de correu electrònic a obtenir informació nova sobre les teves preferències pel que fa a adreces."
+ "S\'estan esborrant contactes freq..."
+ "Disponible"
+ "Absent"
+ "Ocupat"
+ "Contactes"
+ "Altres"
+ "Directori"
+ "Tots els contactes"
+ "Jo"
+ "S\'està cercant..."
+ "Se n\'han trobat més de %d ."
+ "No hi ha cap contacte"
+
+ %d contactes trobats
+ - 1 contacte trobat
+
+ "Contacte ràpid per a %1$s "
+ "(Sense nom)"
+ "Usuaris a qui es truca sovint"
+ "Contactes freqüents"
+ "Mostra el contacte"
+ "Tots els contactes que tenen números de telèfon"
+ "Mostra les actualitzacions"
+ "Només telèfon, sense sincronitzar"
+ "Nom"
+ "Àlies"
+ "Nom"
+ "Nom"
+ "Cognoms"
+ "Prefix del nom"
+ "Segon nom"
+ "Sufix del nom"
+ "Nom fonètic"
+ "Nom fonètic"
+ "Segon nom fonètic"
+ "Cognoms fonètics"
+ "Telèfon"
+ "Adreça electrònica"
+ "Adreça"
+ "Xat"
+ "Organització"
+ "Relació"
+ "Dates especials"
+ "Missatge de text"
+ "Adreça"
+ "Empresa"
+ "Càrrec"
+ "Notes"
+ "SIP"
+ "Lloc web"
+ "Grups"
+ "Envia un correu electrònic a l\'adreça particular"
+ "Envia un correu electrònic al mòbil"
+ "Envia un correu electrònic a la feina"
+ "Correu electrònic"
+ "Envia un correu electrònic a %s "
+ "Correu electrònic"
+ "Carrer"
+ "Apartat postal"
+ "Barri"
+ "Ciutat"
+ "Estat"
+ "Codi postal"
+ "País"
+ "Visualitza l\'adreça particular"
+ "Visualitza l\'adreça de la feina"
+ "Visualitza l\'adreça"
+ "Visualitza l\'adreça %s "
+ "Xateja amb AIM"
+ "Xateja amb Windows Live"
+ "Xateja amb Yahoo"
+ "Xateja amb Skype"
+ "Xateja amb QQ"
+ "Xateja amb Google Talk"
+ "Xateja amb ICQ"
+ "Xateja amb Jabber"
+ "Xat"
+ "suprimeix"
+ "Desplega o replega els camps de nom"
+ "Tots els contactes"
+ "Destacats"
+ "Personalitza"
+ "Contacte"
+ "La resta de contactes"
+ "Tots els contactes"
+ "Elimina el grup de sincronització"
+ "Afegeix un grup de sincronització"
+ "Més grups..."
+ "Si s\'elimina \"%s \" de la sincronització, també se n\'eliminaran els contactes no agrupats."
+ "S\'estan desant les opcions de visualització..."
+ "Fet"
+ "Cancel·la"
+ "Contactes a %s "
+ "Contactes en visualització personalitzada"
+ "Un sol contacte"
+ "Crea el contacte al compte"
+ "Importa de la targeta SIM"
+ "Importa des de la targeta SIM ^1 (^2 )"
+ "Importa des de la targeta SIM %1$s "
+ "Importa d\'un fitxer .vcf"
+ "Vols cancel·lar la importació de %s ?"
+ "Vols cancel·lar l\'exportació de %s ?"
+ "No es pot cancel·lar la imp./exp. vCard"
+ "S\'ha produït un error desconegut."
+ "No s\'ha pogut obrir \"%s \": %s ."
+ "No s\'ha pogut iniciar l\'exportador: \"%s \"."
+ "No hi ha cap contacte que es pugui exportar."
+ "Has desactivat un permís obligatori."
+ "S\'ha produït un error durant l\'exportació: \"%s \"."
+ "El nom de fitxer obligatori és massa llarg (\"%s \")."
+ "Hi ha massa fitxers vCard a la targeta SD."
+ "Error d\'E/S"
+ "No hi ha prou memòria. És possible que el fitxer sigui massa gran."
+ "No s\'ha pogut analitzar la vCard a causa d\'un motiu inesperat."
+ "No s\'admet aquest format."
+ "No s\'ha pogut recopilar metainformació dels fitxers de la vCard."
+ "No s\'ha pogut importar un dels fitxers com a mínim (%s)."
+ "Exportació de %s finalitzada."
+ "S\'han acabat d\'exportar els contactes."
+ "S\'ha cancel·lat l\'exportació de: %s ."
+ "S\'estan exportant les dades de contacte"
+ "S\'estan exportant les teves dades de contacte a: %s ."
+ "No s\'ha pogut obtenir informació de la base de dades."
+ "No hi ha cap contacte que es pugui exportar. Si en tens algun al telèfon, és possible que hi hagi proveïdors de dades que no permetin que els contactes s\'exportin des del telèfon."
+ "No s\'ha iniciat correctament el creador de vCard."
+ "Error en exportar"
+ "No s\'han exportat les dades de contacte.\nMotiu: \"%s \""
+ "S\'està important %s "
+ "No s\'han pogut llegir les dades de vCard"
+ "Lectura de dades de vCard cancel·lada"
+ "Importació de vCard %s finalitzada"
+ "S\'ha cancel·lat la importació de: %s "
+ "%s s\'importarà d\'aquí a poc."
+ "D\'aquí a poc s\'importarà el fitxer."
+ "S\'ha rebutjat la sol·licitud per importar la vCard. Torna-ho a provar més tard."
+ "%s s\'exportarà en breu."
+ "El fitxer s\'exportarà en breu."
+ "S\'ha rebutjat la sol·licitud per exportar la vCard. Torna-ho a provar més tard."
+ "contacte"
+ "S\'estan desant les vCard a l\'emmagatzematge temporal local. La importació real començarà aviat."
+ "No s\'ha pogut importar la vCard."
+ "No s\'ha trobat cap fitxer vCard a la targeta SD."
+ "Contac. reb. NFC"
+ "Vols exportar els contactes?"
+ "Desament a la memòria cau"
+ "No s\'ha pogut explorar la targeta SD. (Motiu: \"%s \")"
+ "S\'està important %s /%s : %s "
+ "Exporta a un fitxer .vcf"
+ "Ordena per"
+ "Nom"
+ "Cognoms"
+ "Format del nom"
+ "Primer el nom"
+ "Primer els cognoms"
+ "Comparteix contactes visibles"
+ "Error en compartir els contactes visibles."
+ "Importa/exporta contactes"
+ "Importa contactes"
+ "No es pot compartir aquest contacte."
+ "Cerca"
+ "Contactes per mostrar"
+ "Contactes per mostrar"
+ "Defineix visualització personalitzada"
+ "Cerca contactes"
+ "Preferits"
+ "No hi ha cap contacte."
+ "No hi ha cap contacte visible."
+ "No hi ha cap preferit."
+ "No hi ha cap contacte a %s "
+ "Esborra contactes freqüents"
+ "Selecciona una targeta SIM"
+ "Comptes"
+ "Importa/exporta"
+ "mitjançant %1$s "
+ "%1$s mitjançant %2$s "
+ "Atura la cerca."
+ "Esborra la cerca."
+ "Opcions de visualització de contactes"
+ "Compte"
+ "Utilitza sempre per a les trucades"
+ "Truca mitjançant"
+ "Trucada amb una nota"
+ "Escriu una nota per enviar-la juntament amb la trucada..."
+ "ENVIA NOTA I TRUCA"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-cs/strings.xml b/ContactsCommon/res/values-cs/strings.xml
new file mode 100644
index 0000000..9d09958
--- /dev/null
+++ b/ContactsCommon/res/values-cs/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Text zkopírován"
+ "Zkopírovat do schránky"
+ "Volat číslo %s "
+ "Volat domů"
+ "Volat mobil"
+ "Volat do práce"
+ "Volat pracovní fax"
+ "Volat domácí fax"
+ "Volat pager"
+ "Volat"
+ "Volat na číslo zpětného volání"
+ "Volat do auta"
+ "Volat firmu (hlavní)"
+ "Volat na číslo ISDN"
+ "Volat na hlavní číslo"
+ "Volat fax"
+ "Volat radiotelefon"
+ "Volat na číslo Telex"
+ "Volat na číslo TTY/TDD"
+ "Volat na pracovní mobil"
+ "Volat na pracovní pager"
+ "Volat kontakt %s "
+ "Volat MMS"
+ "Poslat SMS na číslo %s "
+ "Poslat SMS domů"
+ "Poslat SMS na mobil"
+ "Poslat SMS do práce"
+ "Poslat SMS na pracovní fax"
+ "Poslat SMS na domácí fax"
+ "Poslat SMS na pager"
+ "Poslat SMS"
+ "Poslat SMS na číslo zpětného volání"
+ "Poslat SMS do auta"
+ "Poslat SMS do firmy (hlavní)"
+ "Poslat SMS na číslo ISDN"
+ "Poslat SMS na hlavní číslo"
+ "Poslat SMS na fax"
+ "Poslat SMS na radiotelefon"
+ "Poslat SMS na číslo Telex"
+ "Poslat SMS na číslo TTY/TDD"
+ "Poslat SMS na pracovní mobil"
+ "Poslat SMS na pracovní pager"
+ "Poslat SMS na kontakt %s "
+ "Poslat SMS na číslo MMS"
+ "Uskutečnit videohovor"
+ "Vymazat často kontaktované osoby?"
+ "Vymažete seznam často kontaktovaných osob v aplikacích Kontakty a Telefon a e-mailové aplikace budou muset nastavení adresátů vytvořit znovu."
+ "Mazání často kontaktovaných osob..."
+ "K dispozici"
+ "Pryč"
+ "Nemám čas"
+ "Kontakty"
+ "Jiné"
+ "Adresář"
+ "Všechny kontakty"
+ "Já"
+ "Vyhledávání…"
+ "Nalezeno více kontaktů než %d ."
+ "Žádné kontakty"
+
+ - Nalezeno:
%d
+ - Nalezeno:
%d
+ - Nalezeno:
%d
+ - Nalezeno: 1
+
+ "Rychlý kontakt %1$s "
+ "(Žádné jméno)"
+ "Často volané"
+ "Často používané"
+ "Zobrazit kontakt"
+ "Kontakty s telefonními čísly"
+ "Zobrazit aktualizace"
+ "Pouze v telefonu, nesynchronizováno"
+ "Jméno"
+ "Přezdívka"
+ "Jméno"
+ "Jméno"
+ "Příjmení"
+ "Titul před jménem"
+ "Druhé jméno"
+ "Titul za jménem"
+ "Jméno (foneticky)"
+ "Jméno (foneticky)"
+ "Druhé jméno (foneticky)"
+ "Příjmení (foneticky)"
+ "Telefon"
+ "E-mail"
+ "Adresa"
+ "Chat"
+ "Organizace"
+ "Vztah"
+ "Zvláštní data"
+ "Textová zpráva"
+ "Adresa"
+ "Společnost"
+ "Pozice"
+ "Poznámky"
+ "SIP"
+ "Web"
+ "Skupiny"
+ "E-mail domů"
+ "E-mail na mobil"
+ "E-mail do práce"
+ "E-mail"
+ "E-mail %s "
+ "E-mail"
+ "Ulice"
+ "Číslo poštovní schránky"
+ "Čtvrť"
+ "Město"
+ "Stát"
+ "PSČ"
+ "Země"
+ "Zobrazit adresu domů"
+ "Zobrazit adresu do práce"
+ "Zobrazit adresu"
+ "Zobrazit adresu %s "
+ "Chatovat pomocí AIM"
+ "Chatovat pomocí Windows Live"
+ "Chatovat pomocí Yahoo"
+ "Chatovat pomocí Skype"
+ "Chatovat pomocí QQ"
+ "Chatovat pomocí Google Talk"
+ "Chatovat pomocí ICQ"
+ "Chatovat pomocí Jabberu"
+ "Chatovat"
+ "smazat"
+ "Rozbalit nebo sbalit pole jména"
+ "Všechny kontakty"
+ "Označené hvězdičkou"
+ "Personalizovat"
+ "Kontakt"
+ "Všechny ostatní kontakty"
+ "Všechny kontakty"
+ "Odstranit synchronizovanou skupinu"
+ "Přidat synchronizovanou skupinu"
+ "Další skupiny…"
+ "Odebráním skupiny %s ze synchronizace odeberete ze synchronizace také všechny kontakty mimo skupinu."
+ "Ukládání možností zobrazení…"
+ "Hotovo"
+ "Zrušit"
+ "Kontakty v účtu %s "
+ "Kontakty ve vlastním zobrazení"
+ "Jeden kontakt"
+ "Vytvořit kontakt na základě účtu"
+ "Importovat ze SIM karty"
+ "Importovat ze SIM karty ^1 – ^2 "
+ "Importovat ze SIM karty %1$s "
+ "Importovat ze souboru VCF"
+ "Zrušit import souboru %s ?"
+ "Zrušit export souboru %s ?"
+ "Import/export vizitky vCard nelze zrušit"
+ "Neznámá chyba."
+ "Soubor %s nelze otevřít: %s "
+ "Nelze spustit nástroj pro export: %s ."
+ "Žádný kontakt nelze exportovat."
+ "Zakázali jste požadované oprávnění."
+ "Při exportu došlo k chybě: %s ."
+ "Požadovaný název souboru (%s ) je příliš dlouhý."
+ "Na kartě SD je příliš mnoho souborů vCard."
+ "Chyba I/O"
+ "Není k dispozici dostatek paměti. Soubor může být příliš velký."
+ "Analýza souboru vCard se z neočekávaných důvodů nezdařila."
+ "Formát není podporován."
+ "Informace o metadatech daných souborů vCard se nepodařilo shromáždit."
+ "Jeden nebo více souborů se nepodařilo importovat (%s)."
+ "Export souboru %s byl dokončen."
+ "Exportování kontaktů bylo dokončeno."
+ "Export souboru %s byl zrušen."
+ "Export dat kontaktů"
+ "Data kontaktů jsou exportována do souboru %s ."
+ "Nepodařilo se získat informace o databázi."
+ "Nelze exportovat žádné kontakty. Pokud kontakty v telefonu uložené máte, je možné, že poskytovatel datových služeb zakázal jejich export mimo telefon."
+ "Editor souboru vCard nebyl správně spuštěn."
+ "Export se nezdařil"
+ "Data kontaktů nebyla exportována.\nDůvod: %s "
+ "Probíhá import: %s "
+ "Nepodařilo se přečíst údaje vizitky vCard."
+ "Čtení dat souboru vCard bylo zrušeno"
+ "Import souboru vCard (%s ) byl dokončen"
+ "Import souboru %s byl zrušen."
+ "Soubor %s bude za okamžik importován."
+ "Soubor bude zakrátko importován."
+ "Požadavek na import souborů vCard byl zamítnut. Zkuste to prosím později."
+ "Soubor %s bude za okamžik exportován."
+ "Soubor bude brzy exportován."
+ "Požadavek na export souborů vCard byl zamítnut. Zkuste to prosím později."
+ "kontakt"
+ "Načítání souboru vCard do mezipaměti místního dočasného úložiště. Vlastní import bude zahájen v krátké době."
+ "Soubor vCard se nepodařilo importovat."
+ "Na kartě SD nebyl nalezen žádný soubor vCard."
+ "Kontakt přijatý prostřednictvím komunikace NFC"
+ "Exportovat kontakty?"
+ "Ukládání do mezipaměti"
+ "Prohledání karty SD se nezdařilo. (Důvod: %s )"
+ "Probíhá import: %s /%s : %s "
+ "Exportovat do souboru VCF"
+ "Seřadit podle"
+ "Jméno"
+ "Příjmení"
+ "Formát jména"
+ "Nejprve jméno"
+ "Nejprve příjmení"
+ "Sdílet viditelné kontakty"
+ "Sdílení viditelných kontaktů se nezdařilo."
+ "Importovat nebo exportovat kontakty"
+ "Importovat kontakty"
+ "Tento kontakt nelze sdílet."
+ "Hledat"
+ "Kontakty k zobrazení"
+ "Kontakty k zobrazení"
+ "Definice vl. zobrazení"
+ "Najít kontakty"
+ "Oblíbené"
+ "Žádné kontakty."
+ "Žádné kontakty nejsou viditelné."
+ "Žádné oblíbené kontakty."
+ "Žádné kontakty v položce %s "
+ "Vymazat často kontaktované"
+ "Vybrat SIM kartu"
+ "Účty"
+ "Importovat/Exportovat"
+ "prostřednictvím zdroje %1$s "
+ "%1$s prostřednictvím zdroje %2$s "
+ "zastavit vyhledávání"
+ "Vymazat vyhledávání"
+ "Možnosti zobrazení kontaktů"
+ "Účet"
+ "Vždy používat pro hovory"
+ "Volat pomocí"
+ "Volání s poznámkou"
+ "Zadejte poznámku, která se odešle pomocí volání…"
+ "ODESLAT A ZAVOLAT"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-da/strings.xml b/ContactsCommon/res/values-da/strings.xml
new file mode 100644
index 0000000..25c04cb
--- /dev/null
+++ b/ContactsCommon/res/values-da/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Teksten blev kopieret"
+ "Kopiér til udklipsholder"
+ "Ring til %s "
+ "Ring hjem"
+ "Ring til mobil"
+ "Ring til arbejde"
+ "Ring til arbejdsfax"
+ "Ring til hjemmefax"
+ "Ring til personsøger"
+ "Ring til"
+ "Ring til tilbagekald"
+ "Ring til bil"
+ "Ring til virksomhedens hovednummer"
+ "Ring til ISDN"
+ "Ring til hovednummeret"
+ "Ring til fax"
+ "Ring til radio"
+ "Ring til telex"
+ "Ring til TTY/TDD"
+ "Ring til arbejdsmobiltelefon"
+ "Ring til personsøger på arbejdet"
+ "Ring til %s "
+ "Ring til mms"
+ "Send sms til %s "
+ "Send sms til hjem"
+ "Send sms til mobil"
+ "Send sms til arbejde"
+ "Send sms til arbejdsfax"
+ "Send sms til hjemmefax"
+ "Send sms til personsøger"
+ "Send sms"
+ "Send sms til tilbagekald"
+ "Send sms til bil"
+ "Send sms til virksomhedens hovednummer"
+ "Send sms til ISDN"
+ "Send sms til hovednummeret"
+ "Send sms til fax"
+ "Send sms til radio"
+ "Send sms til telex"
+ "Send sms til TTY/TDD"
+ "Send sms til arbejdsmobiltelefon"
+ "Send sms til personsøger på arbejdet"
+ "Send sms til %s "
+ "Send sms til mms"
+ "Foretag videoopkald"
+ "Vil du rydde de ofte kontaktede?"
+ "Hvis du gør dette, rydder du listen over personer, som du ofte kontakter, i appene Kontaktpersoner og Telefon. Du vil samtidig tvinge e-mailapps til at lære dine adressepræferencer fra bunden."
+ "Ofte kontaktede personer ryddes…"
+ "Tilgængelig"
+ "Ikke til stede"
+ "Optaget"
+ "Kontaktpersoner"
+ "Andet"
+ "Indeks"
+ "Alle kontaktpersoner"
+ "Mig"
+ "Søger..."
+ "Der er fundet mere end %d ."
+ "Ingen kontaktpersoner"
+
+ - Der blev fundet
%d
+ - Der blev fundet
%d
+
+ "Hurtig kontakt til %1$s "
+ "(Intet navn)"
+ "Ofte ringet til"
+ "Ofte kontaktet"
+ "Vis kontaktperson"
+ "Alle kontaktpersoner med telefonnumre"
+ "Se opdateringer"
+ "Kun telefon, ikke synk."
+ "Navn"
+ "Kaldenavn"
+ "Navn"
+ "Fornavn"
+ "Efternavn"
+ "Navnepræfiks"
+ "Mellemnavn"
+ "Navnesuffiks"
+ "Fonetisk navn"
+ "Fonetisk fornavn"
+ "Fonetisk mellemnavn"
+ "Fonetisk efternavn"
+ "Telefon"
+ "E-mail"
+ "Adresse"
+ "Chat"
+ "Organisation"
+ "Forhold"
+ "Særlige datoer"
+ "Sms"
+ "Adresse"
+ "Virksomhed"
+ "Titel"
+ "Noter"
+ "SIP"
+ "Website"
+ "Grupper"
+ "Send e-mail hjem"
+ "Send e-mail til mobil"
+ "Send e-mail til arbejde"
+ "Send e-mail"
+ "Send e-mail til %s "
+ "Send e-mail"
+ "Gade"
+ "Postboks"
+ "Nabolag"
+ "By"
+ "Stat"
+ "Postnummer"
+ "Land"
+ "Vis hjemmeadresse"
+ "Vis arbejdsadresse"
+ "Vis adresse"
+ "Vis %s -adresse"
+ "Chat ved hjælp af AIM"
+ "Chat ved hjælp af Windows Live"
+ "Chat ved hjælp af Yahoo"
+ "Chat ved hjælp af Skype"
+ "Chat ved hjælp af QQ"
+ "Chat ved hjælp af Google Talk"
+ "Chat ved hjælp af ICQ"
+ "Chat ved hjælp af Jabber"
+ "Chat"
+ "slet"
+ "Udvid eller skjul navnefelter"
+ "Alle kontaktpersoner"
+ "Stjernemarkerede"
+ "Tilpas"
+ "Kontaktperson"
+ "Alle andre kontakter"
+ "Alle kontaktpersoner"
+ "Fjern synkroniseringsgruppe"
+ "Tilføj synkroniseringsgruppe"
+ "Flere grupper..."
+ "Hvis du fjerner \"%s \" fra synkroniseringen, fjernes alle ugrupperede kontaktpersoner fra synkroniseringen."
+ "Gemmer valgmuligheder for visning…"
+ "Færdig"
+ "Annuller"
+ "Kontaktpersoner i %s "
+ "Kontakter i tilpasset visning"
+ "Enkelt kontaktperson"
+ "Opret kontaktperson på konto"
+ "Importér fra SIM-kort"
+ "Importér fra SIM ^1 – ^2 "
+ "Importér fra SIM %1$s "
+ "Importér fra .vcf-fil"
+ "Vil du annullere importen af %s ?"
+ "Vil du annullere eksporten af %s ?"
+ "Import/eksport af vCard kunne ikke annulleres"
+ "Ukendt fejl."
+ "\"%s \" kunne ikke åbnes: %s ."
+ "Eksportfunktionen kunne ikke startes: \"%s \""
+ "Der er ingen kontaktpersoner, der kan eksporteres."
+ "Du har deaktiveret en påkrævet tilladelse."
+ "Der opstod en fejl under eksporten: \"%s \"."
+ "Det krævede filnavn er for langt (\"%s \")."
+ "Der er for mange vCard-filer på SD-kortet."
+ "I/O-fejl"
+ "Ikke nok hukommelse. Filen kan være for stor."
+ "vCard kunne ikke parses af uventede årsager."
+ "Formatet understøttes ikke."
+ "Metaoplysninger om de angivne vCard-filer kunne ikke hentes."
+ "En eller flere filer kunne ikke importeres (%s)."
+ "%s er eksporteret."
+ "Kontaktpersoner blev eksporteret."
+ "Eksport af %s er annulleret."
+ "Eksporterer kontaktdata"
+ "Dine kontaktdata bliver eksporteret til: %s ."
+ "Databaseoplysningerne kunne ikke hentes."
+ "Der er ingen kontaktpersoner, der kan eksporteres. Hvis du har kontaktpersoner på din telefon, vil nogle dataudbydere muligvis ikke tillade, at kontaktpersonerne eksporteres fra telefonen."
+ "Oprettelsen af vCard startede ikke korrekt."
+ "Eksport ikke mulig"
+ "Kontaktdataene blev ikke eksporteret.\nÅrsag: \"%s \""
+ "Importerer %s "
+ "Dataene på dette vCard kunne ikke læses"
+ "Læsning af vCard-data blev annulleret"
+ "Import af vCard afsluttet %s "
+ "Import af %s blev annulleret"
+ "%s importeres om et øjeblik."
+ "Filen importeres inden længe."
+ "Anmodningen om import af vCard blev afvist. Prøv igen senere."
+ "%s eksporteres om et øjeblik."
+ "Filen eksporteres om et øjeblik."
+ "Anmodningen om eksport af vCard blev afvist. Prøv igen senere."
+ "kontaktperson"
+ "Cachelagrer vCard-fil(er) til lokalt midlertidigt lager. Den egentlige import starter snart."
+ "vCard kunne ikke importeres."
+ "Der blev ikke fundet nogen vCard-filer på SD-kortet."
+ "Kontakt via NFC"
+ "Eksportér kontaktpersoner?"
+ "Cachelagrer"
+ "SD-kortet kunne ikke scannes. (Årsag: \"%s \")"
+ "Importerer %s /%s : %s "
+ "Eksportér til .vcf-fil"
+ "Sortér efter"
+ "Fornavn"
+ "Efternavn"
+ "Navneformat"
+ "Fornavn først"
+ "Efternavn først"
+ "Del synlige kontaktpersoner"
+ "Det lykkedes ikke at dele synlige kontaktpersoner."
+ "Importér/eksportér kontaktpersoner"
+ "Importér kontaktpersoner"
+ "Denne kontaktperson kan ikke deles."
+ "Søg"
+ "Kontaktpersoner, der skal vises"
+ "Kontaktpersoner, der skal vises"
+ "Angiv tilpasset visning"
+ "Find kontaktpersoner"
+ "Foretrukne"
+ "Ingen kontaktpersoner."
+ "Ingen synlige kontaktpersoner."
+ "Der er ingen foretrukne."
+ "Ingen kontaktpersoner i %s "
+ "Ryd hyppige"
+ "Vælg SIM-kort"
+ "Konti"
+ "Importér/eksportér"
+ "via %1$s "
+ "%1$s via %2$s "
+ "stop søgning"
+ "Ryd søgning"
+ "Indstillinger for visning af kontaktpersoner"
+ "Konto"
+ "Brug altid ved opkald"
+ "Ring med"
+ "Ring med en note"
+ "Indtast en note, som skal sendes ved opkald..."
+ "SEND, OG RING OP"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-de/strings.xml b/ContactsCommon/res/values-de/strings.xml
new file mode 100644
index 0000000..d66170f
--- /dev/null
+++ b/ContactsCommon/res/values-de/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Text kopiert"
+ "In Zwischenablage kopieren"
+ "%s anrufen"
+ "Private Nummer anrufen"
+ "Mobilfunknummer anrufen"
+ "Geschäftliche Nummer anrufen"
+ "Fax (geschäftlich) anrufen"
+ "Fax (privat) anrufen"
+ "Pager anrufen"
+ "Anrufen"
+ "Rückrufnummer anrufen"
+ "Auto anrufen"
+ "Firma (Hauptnr.) anrufen"
+ "ISDN anrufen"
+ "Hauptnummer anrufen"
+ "Fax anrufen"
+ "Funktelefon anrufen"
+ "Telex anrufen"
+ "TTY-/TDD anrufen"
+ "Mobilfunknummer (geschäftlich) anrufen"
+ "Pager (geschäftlich) anrufen"
+ "%s anrufen"
+ "MMS anrufen"
+ "SMS an %s senden"
+ "SMS an private Nummer senden"
+ "SMS an Mobilfunknummer senden"
+ "SMS an geschäftliche Nummer senden"
+ "SMS an Fax (geschäftlich) senden"
+ "SMS an Fax (privat) senden"
+ "SMS an Pager senden"
+ "SMS senden"
+ "SMS an Rückrufnummer senden"
+ "SMS an Autotelefon senden"
+ "SMS an Firma (Hauptnr.) senden"
+ "SMS an ISDN senden"
+ "SMS an Hauptnummer senden"
+ "SMS an Fax senden"
+ "SMS an Funktelefon senden"
+ "SMS an Telex senden"
+ "SMS an TTY-/TDD senden"
+ "SMS an Mobilfunknummer (geschäftlich) senden"
+ "SMS an Pager (geschäftlich) senden"
+ "SMS an %s senden"
+ "SMS an MMS senden"
+ "Videoanruf starten"
+ "Liste \"Häufig kontaktiert\" löschen?"
+ "Sie löschen die Liste \"Häufig kontaktiert\" in den Apps \"Kontakte\" und \"Telefon\" und bewirken so ein Zurücksetzen Ihrer Adresseinstellungen für E-Mail-Apps."
+ "\"Häufig kontaktiert\" wird gelöscht…"
+ "Verfügbar"
+ "Abwesend"
+ "Beschäftigt"
+ "Kontakte"
+ "Sonstige"
+ "Verzeichnis"
+ "Alle Kontakte"
+ "Ich"
+ "Suche läuft…"
+ "Mehr als %d Kontakte gefunden"
+ "Keine Kontakte"
+
+ %d Kontakte gefunden
+ - 1 Kontakt gefunden
+
+ "Schnellkontakt für %1$s "
+ "(Kein Name)"
+ "Häufig angerufen"
+ "Häufig kontaktiert"
+ "Kontakt anzeigen"
+ "Alle Kontakte mit Telefonnummern"
+ "Updates ansehen"
+ "Nur Telefon, nicht synchronisiert"
+ "Name"
+ "Alias"
+ "Name"
+ "Vorname"
+ "Nachname"
+ "Namenspräfix"
+ "Zweiter Vorname"
+ "Namenssuffix"
+ "Phonetischer Name"
+ "Vorname (phonetisch)"
+ "Zweiter Vorname (phonetisch)"
+ "Nachname (phonetisch)"
+ "Telefon"
+ "E-Mail"
+ "Adresse"
+ "Chat"
+ "Unternehmen"
+ "Art der Beziehung"
+ "Besondere Termine"
+ "SMS"
+ "Adresse"
+ "Unternehmen"
+ "Position"
+ "Notizen"
+ "SIP"
+ "Website"
+ "Gruppen"
+ "E-Mail (privat)"
+ "E-Mail (mobil)"
+ "E-Mail (geschäftlich)"
+ "E-Mail"
+ "E-Mail an %s "
+ "E-Mail"
+ "Straße"
+ "Postfach"
+ "Stadtteil"
+ "Stadt"
+ "Bundesland"
+ "Postleitzahl"
+ "Land"
+ "Privatadresse ansehen"
+ "Geschäftsadresse ansehen"
+ "Adresse ansehen"
+ "%s -Adresse ansehen"
+ "Über AIM chatten"
+ "Über Windows Live chatten"
+ "Über Yahoo! chatten"
+ "Über Skype chatten"
+ "Über QQ chatten"
+ "Über Google Talk chatten"
+ "Über ICQ chatten"
+ "Über Jabber chatten"
+ "Chatten"
+ "löschen"
+ "Namensfelder minimieren oder maximieren"
+ "Alle Kontakte"
+ "Markiert"
+ "Personalisieren"
+ "Kontakt"
+ "Alle weiteren Kontakte"
+ "Alle Kontakte"
+ "Synchronisierungsgruppe entfernen"
+ "Synchronisierungsgruppe hinzufügen"
+ "Weitere Gruppen…"
+ "Wenn \"%s \" aus der Synchronisierung entfernt wird, werden auch alle nicht gruppierten Kontakte aus der Synchronisierung entfernt."
+ "Anzeigeoptionen werden gespeichert…"
+ "Fertig"
+ "Abbrechen"
+ "Kontakte in %s "
+ "Kontakte in benutzerdef. Ansicht"
+ "Einzelner Kontakt"
+ "Neuen Kontakt unter Konto erstellen"
+ "Von SIM-Karte importieren"
+ "Von SIM ^1 – ^2 importieren"
+ "Von SIM %1$s importieren"
+ "Aus VCF-Datei importieren"
+ "Import von %s abbrechen?"
+ "Export von %s abbrechen?"
+ "vCard-Import/-Export nicht abgebrochen"
+ "Unbekannter Fehler"
+ "\"%s \" konnte nicht geöffnet werden. Grund: %s ."
+ "Exportprogramm konnte nicht gestartet werden. Grund: %s ."
+ "Es ist kein exportierbarer Kontakt vorhanden."
+ "Sie haben eine erforderliche Berechtigung deaktiviert."
+ "Beim Export ist ein Fehler aufgetreten: \"%s \"."
+ "Erforderlicher Dateiname ist zu lang (%s )."
+ "Zu viele vCard-Dateien auf der SD-Karte"
+ "E/A-Fehler"
+ "Nicht genügend Speicherplatz. Die Datei ist möglicherweise zu groß."
+ "Die vCard konnte aus einem unerwarteten Grund nicht geparst werden."
+ "Das Format wird nicht unterstützt."
+ "Abrufen der Metadaten aus angegebenen vCards nicht möglich"
+ "Eine oder mehrere Dateien können nicht importiert werden (%s)."
+ "Export von %s abgeschlossen"
+ "Kontakte wurden exportiert"
+ "Export von %s abgebrochen"
+ "Kontaktdaten werden exportiert..."
+ "Ihre Kontaktdaten werden in \"%s \" exportiert..."
+ "Datenbankinformationen konnten nicht abgerufen werden."
+ "Es sind keine exportierbaren Kontakte vorhanden. Falls sich Kontakte auf Ihrem Telefon befinden, ist das Exportieren der Kontakte eventuell durch den Datenanbieter gesperrt."
+ "Das Programm zum Erstellen der vCard wurde nicht richtig gestartet."
+ "Export nicht möglich"
+ "Die Kontaktdaten wurden nicht exportiert.\nGrund: %s "
+ "%s wird importiert..."
+ "Lesen der vCard-Daten nicht möglich"
+ "Lesen von vCard-Daten abgebrochen"
+ "Import der vCard %s abgeschlossen"
+ "Import von %s abgebrochen"
+ "%s wird in Kürze importiert."
+ "Die Datei wird in Kürze importiert."
+ "Die vCard-Importanfrage wurde abgelehnt. Bitte versuchen Sie es später erneut."
+ "%s wird in Kürze exportiert."
+ "Die Datei wird in Kürze exportiert."
+ "Die vCard-Exportanfrage wurde abgelehnt. Bitte versuchen Sie es später erneut."
+ "Kontakt"
+ "Caching der vCard(s) in lokalen temporären Speicher wird durchgeführt. Der eigentliche Import beginnt gleich."
+ "Importieren der vCard nicht möglich"
+ "Auf der SD-Karte wurde keine vCard-Datei gefunden."
+ "Kontakt per NFC erhalten"
+ "Kontakte exportieren?"
+ "Caching läuft..."
+ "Die SD-Karte konnte nicht gelesen werden. (Grund: %s )"
+ "%s /%s werden importiert: %s "
+ "In VCF-Datei exportieren"
+ "Sortieren nach"
+ "Vorname"
+ "Nachname"
+ "Namensformat"
+ "Vorname zuerst"
+ "Nachname zuerst"
+ "Sichtbare Kontakte teilen"
+ "Die sichtbaren Kontakte konnten nicht geteilt werden."
+ "Kontakte importieren/exportieren"
+ "Kontakte importieren"
+ "Dieser Kontakt kann nicht geteilt werden."
+ "Suchen"
+ "Angezeigte Kontakte"
+ "Angezeigte Kontakte"
+ "Ansicht festlegen"
+ "Kontakte suchen"
+ "Favoriten"
+ "Keine Kontakte"
+ "Keine sichtbaren Kontakte"
+ "Keine Favoriten"
+ "Keine Kontakte in %s "
+ "\"Häufig kontaktiert\" löschen"
+ "SIM-Karte auswählen"
+ "Konten"
+ "Importieren/Exportieren"
+ "über %1$s "
+ "%1$s über %2$s "
+ "Suche beenden"
+ "Suche zurücksetzen"
+ "Anzeigeoptionen für Kontakte"
+ "Konto"
+ "Diese SIM für alle Anrufe verwenden"
+ "Anrufen mit"
+ "Mit einer Notiz anrufen"
+ "Notiz eingeben, die beim Anrufen gesendet wird..."
+ "Senden & anrufen"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-el/strings.xml b/ContactsCommon/res/values-el/strings.xml
new file mode 100644
index 0000000..d6fe408
--- /dev/null
+++ b/ContactsCommon/res/values-el/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Το κείμενο αντιγράφηκε"
+ "Αντιγραφή στο πρόχειρο"
+ "Κλήση %s "
+ "Κλήση οικίας"
+ "Κλήση κινητής συσκευής"
+ "Κλήση εργασίας"
+ "Κλήση φαξ εργασίας"
+ "Κλήση φαξ οικίας"
+ "Κλήση βομβητή"
+ "Κλήση"
+ "Κλήση αριθμού επανάκλησης"
+ "Κλήση τηλεφώνου αυτοκινήτου"
+ "Κλήση κύριας εταιρικής γραμμής"
+ "Κλήση ISDN"
+ "Κλήση κύριου αριθμού"
+ "Κλήση φαξ"
+ "Κλήση πομπού"
+ "Κλήση σε τηλέτυπο"
+ "Κλήση σε τηλέφωνο TTY/TDD"
+ "Κλήση κινητού τηλεφώνου εργασίας"
+ "Κλήση βομβητή εργασίας"
+ "Κλήση %s "
+ "Κλήση MMS"
+ "Αποστολή μηνύματος κειμένου προς %s "
+ "Αποστολή μηνύματος κειμένου προς οικία"
+ "Αποστολή μηνύματος κειμένου προς κινητή συσκευή"
+ "Αποστολή μηνύματος κειμένου προς εργασία"
+ "Αποστολή μηνύματος κειμένου προς φαξ εργασίας"
+ "Αποστολή μηνύματος κειμένου προς φαξ οικίας"
+ "Αποστολή μηνύματος κειμένου προς βομβητή"
+ "Κείμενο"
+ "Αποστολή μηνύματος κειμένου σε αριθμό επανάκλησης"
+ "Αποστολή μηνύματος κειμένου προς τηλέφωνο αυτοκινήτου"
+ "Αποστολή μηνύματος κειμένου προς κύρια εταιρική γραμμή"
+ "Αποστολή κειμένου σε ISDN"
+ "Αποστολή μηνύματος κειμένου προς κύριο αριθμό"
+ "Αποστολή μηνύματος κειμένου σε φαξ"
+ "Αποστολή μηνύματος κειμένου προς πομπό"
+ "Αποστολή μηνύματος κειμένου σε τηλέτυπο"
+ "Αποστολή μηνύματος κειμένου προς τηλέφωνο TTY/TDD"
+ "Αποστολή μηνύματος κειμένου προς κινητό τηλέφωνο εργασίας"
+ "Αποστολή μηνύματος κειμένου προς βομβητή εργασίας"
+ "Αποστολή μηνύματος προς %s "
+ "Αποστολή μηνύματος κειμένου ως MMS"
+ "Πραγματοποίηση βιντεοκλήσης"
+ "Διαγραφή ατόμων με συχνή επικοινωνία;"
+ "Θα διαγράψετε τη λίστα συχνών επαφών στις εφαρμογές Επαφές και Τηλέφωνο και θα επιβάλλετε στις εφαρμογές ηλεκτρονικού ταχυδρομείου να μάθουν τις προτιμήσεις διευθύνσεών σας από την αρχή,"
+ "Διαγρ. ατόμων με συχνή επικοινωνία…"
+ "Διαθέσιμος"
+ "Μη διαθέσιμος"
+ "Απασχολημένος"
+ "Επαφές"
+ "Άλλο"
+ "Κατάλογος"
+ "Όλες οι επαφές"
+ "Εγώ"
+ "Αναζήτηση…"
+ "Βρέθηκαν περισσότερα από %d ."
+ "Δεν υπάρχουν επαφές"
+
+ - Βρέθηκαν
%d
+ - Βρέθηκε 1
+
+ "Γρήγορη επαφή για %1$s "
+ "(Χωρίς όνομα)"
+ "Συχνές κλήσεις"
+ "Συχνή επικοινωνία"
+ "Προβολή επαφής"
+ "Όλες οι επαφές με αριθμούς τηλεφώνου"
+ "Προβολή ενημερώσεων"
+ "Μόνο στο τηλέφωνο, χωρίς συγχρονισμό"
+ "Όνομα"
+ "Ψευδώνυμο"
+ "Όνομα"
+ "Όνομα"
+ "Επώνυμο"
+ "Πρόθεμα ονόματος"
+ "Πατρώνυμο"
+ "Επίθημα ονόματος"
+ "Φωνητικό όνομα"
+ "Φωνητικό όνομα"
+ "Φωνητικό πατρώνυμου"
+ "Φωνητικό επώνυμο"
+ "Τηλέφωνο"
+ "Ηλεκτρονικό ταχυδρομείο"
+ "Διεύθυνση"
+ "IM"
+ "Οργανισμός"
+ "Σχέση"
+ "Ειδικές ημερομηνίες"
+ "Μήνυμα κειμένου"
+ "Διεύθυνση"
+ "Εταιρεία"
+ "Τίτλος"
+ "Σημειώσεις"
+ "SIP"
+ "Ιστότοπος"
+ "Ομάδες"
+ "Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου προς οικία"
+ "Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου προς κινητό"
+ "Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου προς εργασία"
+ "Μήνυμα ηλεκτρονικού ταχυδρομείου"
+ "Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου προς %s "
+ "Μήνυμα ηλεκτρονικού ταχυδρομείου"
+ "Οδός"
+ "Ταχυδρομική θυρίδα"
+ "Γειτονιά"
+ "Πόλη"
+ "Πολιτεία"
+ "Ταχυδρομικός κώδικας"
+ "Χώρα"
+ "Προβολή διεύθυνσης οικίας"
+ "Προβολή διεύθυνσης εργασίας"
+ "Προβολή διεύθυνσης"
+ "Προβολή διεύθυνσης %s "
+ "Συζήτηση μέσω AIM"
+ "Συζήτηση μέσω Windows Live"
+ "Συζήτηση μέσω Yahoo"
+ "Συζήτηση μέσω Skype"
+ "Συζήτηση μέσω QQ"
+ "Συζήτηση μέσω Google Talk"
+ "Συζήτηση μέσω ICQ"
+ "Συζήτηση μέσω Jabber"
+ "Συζήτηση"
+ "διαγραφή"
+ "Ανάπτυξη ή σύμπτυξη πεδίων ονομάτων"
+ "Όλες οι επαφές"
+ "Με αστέρι"
+ "Προσαρμογή"
+ "Επαφή"
+ "Όλες οι άλλες επαφές"
+ "Όλες οι επαφές"
+ "Κατάργηση ομάδας συγχρονισμού"
+ "Προσθήκη ομάδας συγχρονισμού"
+ "Περισσότερες ομάδες…"
+ "Η κατάργηση της ομάδας \"%s \" από το συγχρονισμό θα καταργήσει επίσης και τις επαφές χωρίς ομαδοποίηση από το συγχρονισμό."
+ "Αποθήκευση επιλογών προβολής…"
+ "Τέλος"
+ "Ακύρωση"
+ "Επαφές στο %s "
+ "Επαφές σε προσ/νη προβολή"
+ "Μία επαφή"
+ "Δημιουργία επαφής στο λογαριασμό"
+ "Εισαγωγή από κάρτα SIM"
+ "Εισαγωγή από κάρτα SIM ^1 - ^2 "
+ "Εισαγωγή από κάρτα SIM %1$s "
+ "Εισαγωγή από αρχείο .vcf"
+ "Ακύρωση της εισαγωγής του αρχείου %s ;"
+ "Ακύρωση της εξαγωγής του αρχείου %s ;"
+ "Αδύνατη ακύρωση εισαγωγής/εξαγωγής vCard"
+ "Άγνωστο σφάλμα."
+ "Δεν ήταν δυνατό το άνοιγμα του αρχείου \"%s \": %s ."
+ "Δεν ήταν δυνατή η έναρξη του προγράμματος εξαγωγής: \"%s \"."
+ "Δεν υπάρχει επαφή με δυνατότητα εξαγωγής."
+ "Απενεργοποιήσατε μια απαιτούμενη άδεια."
+ "Προέκυψε κάποιο σφάλμα κατά την εξαγωγή: \"%s \"."
+ "Το απαιτούμενο όνομα αρχείου είναι πάρα πολύ μεγάλο (\"%s \")."
+ "Υπάρχουν πάρα πολλά αρχεία vCard στην κάρτα SD."
+ "Σφάλμα I/O"
+ "Δεν υπάρχει αρκετή μνήμη. Το αρχείο ενδέχεται να είναι πάρα πολύ μεγάλο."
+ "Δεν ήταν δυνατή η ανάλυση της κάρτας vCard λόγω μη αναμενόμενης αιτίας."
+ "Η μορφή δεν υποστηρίζεται."
+ "Δεν ήταν δυνατή η συλλογή πληροφοριών μεταδεδομένων των καρτών vCard."
+ "Δεν ήταν δυνατή η εισαγωγή ενός ή περισσότερων αρχείων (%s)."
+ "Η εξαγωγή του αρχείου %s ολοκληρώθηκε."
+ "Ολοκλήρωση εξαγωγής επαφών."
+ "Η εξαγωγή του αρχείου %s ακυρώθηκε."
+ "Εξαγωγή δεδομένων επαφών"
+ "Γίνεται εξαγωγή των δεδομένων επαφών σας στο αρχείο: %s ."
+ "Δεν ήταν δυνατή η λήψη πληροφοριών βάσης δεδομένων."
+ "Δεν υπάρχουν επαφές προς εξαγωγή. Αν υπάρχουν επαφές στο τηλέφωνό σας, ορισμένοι πάροχοι δεδομένων ενδέχεται να μην επιτρέπουν την εξαγωγή των επαφών από το τηλέφωνο."
+ "Η έναρξη της vCard δεν ήταν σωστή."
+ "Αδυναμία εξαγωγής"
+ "Δεν έγινε εξαγωγή των δεδομένων επαφής.\nΑιτία: \"%s \""
+ "Εισαγωγή %s "
+ "Αδύνατη η ανάγνωση δεδομένων vCard"
+ "Ακύρωση ανάγνωσης δεδομένων vCard"
+ "Η εισαγωγή του αρχείου %s vCard έχει ολοκληρωθεί"
+ "Η εισαγωγή του αρχείου %s έχει ακυρωθεί"
+ "Η εισαγωγή του αρχείου %s θα γίνει σύντομα."
+ "Η εισαγωγή του αρχείου θα γίνει σύντομα."
+ "Το αίτημα εισαγωγής vCard απορρίφθηκε. Δοκιμάστε ξανά αργότερα."
+ "Η εξαγωγή του αρχείου %s θα γίνει σύντομα."
+ "Η εξαγωγή του αρχείου θα γίνει σύντομα."
+ "Το αίτημα εξαγωγής vCard απορρίφθηκε. Δοκιμάστε ξανά αργότερα."
+ "επαφή"
+ "Αλλαγή vCard σε τοπικό χώρο προσωρινής αποθήκευσης. Η εισαγωγή θα ξεκινήσει σύντομα."
+ "Δεν ήταν δυνατή η εισαγωγή κάρτας vCard."
+ "Δεν βρέθηκε αρχείο vCard στην κάρτα SD."
+ "Λήψη επαφής μέσω ΕΚΠ"
+ "Εξαγωγή επαφών;"
+ "Προσωρινή αποθήκευση"
+ "Δεν ήταν δυνατή η σάρωση της κάρτας SD. (Αιτία: \"%s \")"
+ "Εισαγωγή %s /%s : %s "
+ "Εξαγωγή σε αρχείο .vcf"
+ "Ταξινόμηση κατά"
+ "Όνομα"
+ "Επώνυμο"
+ "Μορφή ονόματος"
+ "Πρώτα το όνομα"
+ "Πρώτα το επώνυμο"
+ "Κοινοποίηση ορατών επαφών"
+ "Η κοινοποίηση των ορατών επαφών απέτυχε."
+ "Εισαγωγή/Εξαγωγή επαφών"
+ "Εισαγωγή επαφών"
+ "Δεν είναι δυνατή η κοινή χρήση αυτής της επαφής."
+ "Αναζήτηση"
+ "Επαφές για προβολή"
+ "Επαφές για προβολή"
+ "Προσ/σμένη προβολή"
+ "Εύρεση επαφών"
+ "Αγαπημένα"
+ "Δεν υπάρχουν επαφές."
+ "Δεν υπάρχουν ορατές επαφές."
+ "Δεν υπάρχουν αγαπημένα."
+ "Δεν υπάρχουν επαφές στο %s "
+ "Διαγραφή ατόμ. με συχνή επικ."
+ "Επιλογή κάρτας SIM"
+ "Λογαριασμοί"
+ "Εισαγωγή/Εξαγωγή"
+ "μέσω %1$s "
+ "%1$s μέσω %2$s "
+ "τέλος αναζήτησης"
+ "Εκκαθάριση αναζητήσεων"
+ "Επιλογές εμφάνισης επαφών"
+ "Λογαριασμός"
+ "Χρήση πάντα για κλήσεις"
+ "Κλήση με"
+ "Κλήση με σημείωση"
+ "Πληκτρολογήστε μια σημείωση για αποστολή με την κλήση…"
+ "ΑΠΟΣΤΟΛΗ ΚΑΙ ΚΛΗΣΗ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-en-rAU/strings.xml b/ContactsCommon/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..a9c9f1b
--- /dev/null
+++ b/ContactsCommon/res/values-en-rAU/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Text copied"
+ "Copy to clipboard"
+ "Call %s "
+ "Call home"
+ "Call mobile"
+ "Call work"
+ "Call work fax"
+ "Call home fax"
+ "Call pager"
+ "Call"
+ "Call callback"
+ "Call car"
+ "Call company main"
+ "Call ISDN"
+ "Call main"
+ "Call fax"
+ "Call radio"
+ "Call telex"
+ "Call TTY/TDD"
+ "Call work mobile"
+ "Call work pager"
+ "Call %s "
+ "Call MMS"
+ "Text %s "
+ "Text home"
+ "Text mobile"
+ "Text work"
+ "Text work fax"
+ "Text home fax"
+ "Text pager"
+ "Text"
+ "Text callback"
+ "Text car"
+ "Text company main"
+ "Text ISDN"
+ "Text main"
+ "Text fax"
+ "Text radio"
+ "Text telex"
+ "Text TTY/TDD"
+ "Text work mobile"
+ "Text work pager"
+ "Text %s "
+ "Text MMS"
+ "Make video call"
+ "Clear frequently contacted?"
+ "You\'ll clear the frequently contacted list in the Contacts and Phone apps, and force email apps to learn your addressing preferences from scratch."
+ "Clearing frequently contacted…"
+ "Available"
+ "Away"
+ "Busy"
+ "Contacts"
+ "Other"
+ "Directory"
+ "All contacts"
+ "Me"
+ "Searching…"
+ "More than %d found."
+ "No contacts"
+
+ %d found
+ - 1 found
+
+ "Quick contact for %1$s "
+ "(No name)"
+ "Frequently called"
+ "Frequently contacted"
+ "View contact"
+ "All contacts with phone numbers"
+ "View updates"
+ "Phone-only, unsynced"
+ "Name"
+ "Nickname"
+ "Name"
+ "First name"
+ "Surname"
+ "Name prefix"
+ "Middle name"
+ "Name suffix"
+ "Phonetic name"
+ "Phonetic first name"
+ "Phonetic middle name"
+ "Phonetic surname"
+ "Phone"
+ "Email"
+ "Address"
+ "IM"
+ "Organisation"
+ "Relationship"
+ "Special dates"
+ "Text message"
+ "Address"
+ "Company"
+ "Title"
+ "Notes"
+ "SIP"
+ "Website"
+ "Groups"
+ "Email home"
+ "Email mobile"
+ "Email work"
+ "Email"
+ "Email %s "
+ "Email"
+ "Street"
+ "PO box"
+ "Neighbourhood"
+ "City"
+ "County"
+ "Postcode"
+ "Country"
+ "View home address"
+ "View work address"
+ "View address"
+ "View %s address"
+ "Chat using AIM"
+ "Chat using Windows Live"
+ "Chat using Yahoo"
+ "Chat using Skype"
+ "Chat using QQ"
+ "Chat using Google Talk"
+ "Chat using ICQ"
+ "Chat using Jabber"
+ "Chat"
+ "delete"
+ "Expand or collapse name fields"
+ "All contacts"
+ "Starred"
+ "Customise"
+ "Contact"
+ "All other contacts"
+ "All contacts"
+ "Remove sync group"
+ "Add sync group"
+ "More groups…"
+ "Removing \"%s \" from sync will also remove any ungrouped contacts from sync."
+ "Saving display options…"
+ "Done"
+ "Cancel"
+ "Contacts in %s "
+ "Contacts in customised view"
+ "Single contact"
+ "Create contact under account"
+ "Import from SIM card"
+ "Import from SIM ^1 - ^2 "
+ "Import from SIM %1$s "
+ "Import from .vcf file"
+ "Cancel import of %s ?"
+ "Cancel export of %s ?"
+ "Couldn\'t cancel vCard import/export"
+ "Unknown error."
+ "Couldn\'t open \"%s \": %s ."
+ "Couldn\'t start the exporter: \"%s \"."
+ "There is no exportable contact."
+ "You have disabled a required permission."
+ "An error occurred during export: \"%s \"."
+ "Required filename is too long (\"%s \")."
+ "Too many vCard files are on the SD card."
+ "I/O error"
+ "Not enough memory. The file may be too large."
+ "Couldn\'t parse vCard for an unexpected reason."
+ "The format isn\'t supported."
+ "Couldn\'t collect meta information of given vCard file(s)."
+ "One or more files couldn\'t be imported (%s)."
+ "Finished exporting %s ."
+ "Finished exporting contacts."
+ "Exporting %s cancelled."
+ "Exporting contact data"
+ "Your contact data is being exported to: %s ."
+ "Couldn\'t get database information."
+ "There are no exportable contacts. If you do have contacts on your phone, some data providers may not allow the contacts to be exported from the phone."
+ "The vCard composer didn\'t start properly."
+ "Couldn\'t export"
+ "The contact data wasn\'t exported.\nReason: \"%s \""
+ "Importing %s "
+ "Couldn\'t read vCard data"
+ "Reading vCard data cancelled"
+ "Finished importing vCard %s "
+ "Importing %s cancelled"
+ "%s will be imported shortly."
+ "The file will be imported shortly."
+ "vCard import request was rejected. Try again later."
+ "%s will be exported shortly."
+ "The file will be exported shortly."
+ "vCard export request was rejected. Try again later."
+ "contact"
+ "Caching vCard(s) to local temporary storage. The actual import will start soon."
+ "Couldn\'t import vCard."
+ "No vCard file found on the SD card."
+ "Contact received over NFC"
+ "Export contacts?"
+ "Caching"
+ "The SD card couldn\'t be scanned. (Reason: \"%s \")"
+ "Importing %s /%s : %s "
+ "Export to .vcf file"
+ "Sort by"
+ "First name"
+ "Surname"
+ "Name format"
+ "First name first"
+ "Surname first"
+ "Share visible contacts"
+ "Failed to share visible contacts."
+ "Import/export contacts"
+ "Import contacts"
+ "This contact cannot be shared."
+ "Search"
+ "Contacts to display"
+ "Contacts to display"
+ "Define customised view"
+ "Find contacts"
+ "Favourites"
+ "No contacts."
+ "No visible contacts."
+ "No favourites."
+ "No contacts in %s "
+ "Clear frequents"
+ "Select SIM card"
+ "Accounts"
+ "Import/Export"
+ "via %1$s "
+ "%1$s via %2$s "
+ "stop searching"
+ "Clear search"
+ "Contact display options"
+ "Account"
+ "Always use this for calls"
+ "Call with"
+ "Call with a note"
+ "Type a note to send with call ..."
+ "SEND & CALL"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-en-rGB/strings.xml b/ContactsCommon/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..a9c9f1b
--- /dev/null
+++ b/ContactsCommon/res/values-en-rGB/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Text copied"
+ "Copy to clipboard"
+ "Call %s "
+ "Call home"
+ "Call mobile"
+ "Call work"
+ "Call work fax"
+ "Call home fax"
+ "Call pager"
+ "Call"
+ "Call callback"
+ "Call car"
+ "Call company main"
+ "Call ISDN"
+ "Call main"
+ "Call fax"
+ "Call radio"
+ "Call telex"
+ "Call TTY/TDD"
+ "Call work mobile"
+ "Call work pager"
+ "Call %s "
+ "Call MMS"
+ "Text %s "
+ "Text home"
+ "Text mobile"
+ "Text work"
+ "Text work fax"
+ "Text home fax"
+ "Text pager"
+ "Text"
+ "Text callback"
+ "Text car"
+ "Text company main"
+ "Text ISDN"
+ "Text main"
+ "Text fax"
+ "Text radio"
+ "Text telex"
+ "Text TTY/TDD"
+ "Text work mobile"
+ "Text work pager"
+ "Text %s "
+ "Text MMS"
+ "Make video call"
+ "Clear frequently contacted?"
+ "You\'ll clear the frequently contacted list in the Contacts and Phone apps, and force email apps to learn your addressing preferences from scratch."
+ "Clearing frequently contacted…"
+ "Available"
+ "Away"
+ "Busy"
+ "Contacts"
+ "Other"
+ "Directory"
+ "All contacts"
+ "Me"
+ "Searching…"
+ "More than %d found."
+ "No contacts"
+
+ %d found
+ - 1 found
+
+ "Quick contact for %1$s "
+ "(No name)"
+ "Frequently called"
+ "Frequently contacted"
+ "View contact"
+ "All contacts with phone numbers"
+ "View updates"
+ "Phone-only, unsynced"
+ "Name"
+ "Nickname"
+ "Name"
+ "First name"
+ "Surname"
+ "Name prefix"
+ "Middle name"
+ "Name suffix"
+ "Phonetic name"
+ "Phonetic first name"
+ "Phonetic middle name"
+ "Phonetic surname"
+ "Phone"
+ "Email"
+ "Address"
+ "IM"
+ "Organisation"
+ "Relationship"
+ "Special dates"
+ "Text message"
+ "Address"
+ "Company"
+ "Title"
+ "Notes"
+ "SIP"
+ "Website"
+ "Groups"
+ "Email home"
+ "Email mobile"
+ "Email work"
+ "Email"
+ "Email %s "
+ "Email"
+ "Street"
+ "PO box"
+ "Neighbourhood"
+ "City"
+ "County"
+ "Postcode"
+ "Country"
+ "View home address"
+ "View work address"
+ "View address"
+ "View %s address"
+ "Chat using AIM"
+ "Chat using Windows Live"
+ "Chat using Yahoo"
+ "Chat using Skype"
+ "Chat using QQ"
+ "Chat using Google Talk"
+ "Chat using ICQ"
+ "Chat using Jabber"
+ "Chat"
+ "delete"
+ "Expand or collapse name fields"
+ "All contacts"
+ "Starred"
+ "Customise"
+ "Contact"
+ "All other contacts"
+ "All contacts"
+ "Remove sync group"
+ "Add sync group"
+ "More groups…"
+ "Removing \"%s \" from sync will also remove any ungrouped contacts from sync."
+ "Saving display options…"
+ "Done"
+ "Cancel"
+ "Contacts in %s "
+ "Contacts in customised view"
+ "Single contact"
+ "Create contact under account"
+ "Import from SIM card"
+ "Import from SIM ^1 - ^2 "
+ "Import from SIM %1$s "
+ "Import from .vcf file"
+ "Cancel import of %s ?"
+ "Cancel export of %s ?"
+ "Couldn\'t cancel vCard import/export"
+ "Unknown error."
+ "Couldn\'t open \"%s \": %s ."
+ "Couldn\'t start the exporter: \"%s \"."
+ "There is no exportable contact."
+ "You have disabled a required permission."
+ "An error occurred during export: \"%s \"."
+ "Required filename is too long (\"%s \")."
+ "Too many vCard files are on the SD card."
+ "I/O error"
+ "Not enough memory. The file may be too large."
+ "Couldn\'t parse vCard for an unexpected reason."
+ "The format isn\'t supported."
+ "Couldn\'t collect meta information of given vCard file(s)."
+ "One or more files couldn\'t be imported (%s)."
+ "Finished exporting %s ."
+ "Finished exporting contacts."
+ "Exporting %s cancelled."
+ "Exporting contact data"
+ "Your contact data is being exported to: %s ."
+ "Couldn\'t get database information."
+ "There are no exportable contacts. If you do have contacts on your phone, some data providers may not allow the contacts to be exported from the phone."
+ "The vCard composer didn\'t start properly."
+ "Couldn\'t export"
+ "The contact data wasn\'t exported.\nReason: \"%s \""
+ "Importing %s "
+ "Couldn\'t read vCard data"
+ "Reading vCard data cancelled"
+ "Finished importing vCard %s "
+ "Importing %s cancelled"
+ "%s will be imported shortly."
+ "The file will be imported shortly."
+ "vCard import request was rejected. Try again later."
+ "%s will be exported shortly."
+ "The file will be exported shortly."
+ "vCard export request was rejected. Try again later."
+ "contact"
+ "Caching vCard(s) to local temporary storage. The actual import will start soon."
+ "Couldn\'t import vCard."
+ "No vCard file found on the SD card."
+ "Contact received over NFC"
+ "Export contacts?"
+ "Caching"
+ "The SD card couldn\'t be scanned. (Reason: \"%s \")"
+ "Importing %s /%s : %s "
+ "Export to .vcf file"
+ "Sort by"
+ "First name"
+ "Surname"
+ "Name format"
+ "First name first"
+ "Surname first"
+ "Share visible contacts"
+ "Failed to share visible contacts."
+ "Import/export contacts"
+ "Import contacts"
+ "This contact cannot be shared."
+ "Search"
+ "Contacts to display"
+ "Contacts to display"
+ "Define customised view"
+ "Find contacts"
+ "Favourites"
+ "No contacts."
+ "No visible contacts."
+ "No favourites."
+ "No contacts in %s "
+ "Clear frequents"
+ "Select SIM card"
+ "Accounts"
+ "Import/Export"
+ "via %1$s "
+ "%1$s via %2$s "
+ "stop searching"
+ "Clear search"
+ "Contact display options"
+ "Account"
+ "Always use this for calls"
+ "Call with"
+ "Call with a note"
+ "Type a note to send with call ..."
+ "SEND & CALL"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-en-rIN/strings.xml b/ContactsCommon/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..a9c9f1b
--- /dev/null
+++ b/ContactsCommon/res/values-en-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Text copied"
+ "Copy to clipboard"
+ "Call %s "
+ "Call home"
+ "Call mobile"
+ "Call work"
+ "Call work fax"
+ "Call home fax"
+ "Call pager"
+ "Call"
+ "Call callback"
+ "Call car"
+ "Call company main"
+ "Call ISDN"
+ "Call main"
+ "Call fax"
+ "Call radio"
+ "Call telex"
+ "Call TTY/TDD"
+ "Call work mobile"
+ "Call work pager"
+ "Call %s "
+ "Call MMS"
+ "Text %s "
+ "Text home"
+ "Text mobile"
+ "Text work"
+ "Text work fax"
+ "Text home fax"
+ "Text pager"
+ "Text"
+ "Text callback"
+ "Text car"
+ "Text company main"
+ "Text ISDN"
+ "Text main"
+ "Text fax"
+ "Text radio"
+ "Text telex"
+ "Text TTY/TDD"
+ "Text work mobile"
+ "Text work pager"
+ "Text %s "
+ "Text MMS"
+ "Make video call"
+ "Clear frequently contacted?"
+ "You\'ll clear the frequently contacted list in the Contacts and Phone apps, and force email apps to learn your addressing preferences from scratch."
+ "Clearing frequently contacted…"
+ "Available"
+ "Away"
+ "Busy"
+ "Contacts"
+ "Other"
+ "Directory"
+ "All contacts"
+ "Me"
+ "Searching…"
+ "More than %d found."
+ "No contacts"
+
+ %d found
+ - 1 found
+
+ "Quick contact for %1$s "
+ "(No name)"
+ "Frequently called"
+ "Frequently contacted"
+ "View contact"
+ "All contacts with phone numbers"
+ "View updates"
+ "Phone-only, unsynced"
+ "Name"
+ "Nickname"
+ "Name"
+ "First name"
+ "Surname"
+ "Name prefix"
+ "Middle name"
+ "Name suffix"
+ "Phonetic name"
+ "Phonetic first name"
+ "Phonetic middle name"
+ "Phonetic surname"
+ "Phone"
+ "Email"
+ "Address"
+ "IM"
+ "Organisation"
+ "Relationship"
+ "Special dates"
+ "Text message"
+ "Address"
+ "Company"
+ "Title"
+ "Notes"
+ "SIP"
+ "Website"
+ "Groups"
+ "Email home"
+ "Email mobile"
+ "Email work"
+ "Email"
+ "Email %s "
+ "Email"
+ "Street"
+ "PO box"
+ "Neighbourhood"
+ "City"
+ "County"
+ "Postcode"
+ "Country"
+ "View home address"
+ "View work address"
+ "View address"
+ "View %s address"
+ "Chat using AIM"
+ "Chat using Windows Live"
+ "Chat using Yahoo"
+ "Chat using Skype"
+ "Chat using QQ"
+ "Chat using Google Talk"
+ "Chat using ICQ"
+ "Chat using Jabber"
+ "Chat"
+ "delete"
+ "Expand or collapse name fields"
+ "All contacts"
+ "Starred"
+ "Customise"
+ "Contact"
+ "All other contacts"
+ "All contacts"
+ "Remove sync group"
+ "Add sync group"
+ "More groups…"
+ "Removing \"%s \" from sync will also remove any ungrouped contacts from sync."
+ "Saving display options…"
+ "Done"
+ "Cancel"
+ "Contacts in %s "
+ "Contacts in customised view"
+ "Single contact"
+ "Create contact under account"
+ "Import from SIM card"
+ "Import from SIM ^1 - ^2 "
+ "Import from SIM %1$s "
+ "Import from .vcf file"
+ "Cancel import of %s ?"
+ "Cancel export of %s ?"
+ "Couldn\'t cancel vCard import/export"
+ "Unknown error."
+ "Couldn\'t open \"%s \": %s ."
+ "Couldn\'t start the exporter: \"%s \"."
+ "There is no exportable contact."
+ "You have disabled a required permission."
+ "An error occurred during export: \"%s \"."
+ "Required filename is too long (\"%s \")."
+ "Too many vCard files are on the SD card."
+ "I/O error"
+ "Not enough memory. The file may be too large."
+ "Couldn\'t parse vCard for an unexpected reason."
+ "The format isn\'t supported."
+ "Couldn\'t collect meta information of given vCard file(s)."
+ "One or more files couldn\'t be imported (%s)."
+ "Finished exporting %s ."
+ "Finished exporting contacts."
+ "Exporting %s cancelled."
+ "Exporting contact data"
+ "Your contact data is being exported to: %s ."
+ "Couldn\'t get database information."
+ "There are no exportable contacts. If you do have contacts on your phone, some data providers may not allow the contacts to be exported from the phone."
+ "The vCard composer didn\'t start properly."
+ "Couldn\'t export"
+ "The contact data wasn\'t exported.\nReason: \"%s \""
+ "Importing %s "
+ "Couldn\'t read vCard data"
+ "Reading vCard data cancelled"
+ "Finished importing vCard %s "
+ "Importing %s cancelled"
+ "%s will be imported shortly."
+ "The file will be imported shortly."
+ "vCard import request was rejected. Try again later."
+ "%s will be exported shortly."
+ "The file will be exported shortly."
+ "vCard export request was rejected. Try again later."
+ "contact"
+ "Caching vCard(s) to local temporary storage. The actual import will start soon."
+ "Couldn\'t import vCard."
+ "No vCard file found on the SD card."
+ "Contact received over NFC"
+ "Export contacts?"
+ "Caching"
+ "The SD card couldn\'t be scanned. (Reason: \"%s \")"
+ "Importing %s /%s : %s "
+ "Export to .vcf file"
+ "Sort by"
+ "First name"
+ "Surname"
+ "Name format"
+ "First name first"
+ "Surname first"
+ "Share visible contacts"
+ "Failed to share visible contacts."
+ "Import/export contacts"
+ "Import contacts"
+ "This contact cannot be shared."
+ "Search"
+ "Contacts to display"
+ "Contacts to display"
+ "Define customised view"
+ "Find contacts"
+ "Favourites"
+ "No contacts."
+ "No visible contacts."
+ "No favourites."
+ "No contacts in %s "
+ "Clear frequents"
+ "Select SIM card"
+ "Accounts"
+ "Import/Export"
+ "via %1$s "
+ "%1$s via %2$s "
+ "stop searching"
+ "Clear search"
+ "Contact display options"
+ "Account"
+ "Always use this for calls"
+ "Call with"
+ "Call with a note"
+ "Type a note to send with call ..."
+ "SEND & CALL"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-es-rUS/strings.xml b/ContactsCommon/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..5708efc
--- /dev/null
+++ b/ContactsCommon/res/values-es-rUS/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Se copió el texto."
+ "Copiar en el portapapeles"
+ "Llamar a %s "
+ "Llamar a casa"
+ "Llamar al móvil"
+ "Llamar al trabajo"
+ "Llamar al fax del trabajo"
+ "Llamar al fax de casa"
+ "Llamar a localizador"
+ "Llamar"
+ "Llamar a un número de devolución de llamada"
+ "Llamar al automóvil"
+ "Llamar al teléfono principal de la empresa"
+ "Llamar a ISDN"
+ "Llamar al teléfono principal"
+ "Llamar a fax"
+ "Llamar a radio"
+ "Llamar a télex"
+ "Llamar a TTY/TDD"
+ "Llamar al móvil del trabajo"
+ "Llamar al localizador del trabajo"
+ "Llamar a %s "
+ "Llamar a MMS"
+ "Enviar SMS a %s "
+ "Enviar SMS a casa"
+ "Enviar SMS al móvil"
+ "Enviar SMS al trabajo"
+ "Enviar SMS al fax del trabajo"
+ "Enviar SMS al fax de casa"
+ "Enviar SMS a localizador"
+ "Enviar SMS"
+ "Enviar SMS a un número de devolución de llamada"
+ "Enviar SMS al automóvil"
+ "Enviar SMS al teléfono principal de la empresa"
+ "Enviar SMS a ISDN"
+ "Enviar SMS al teléfono principal"
+ "Enviar SMS a fax"
+ "Enviar SMS a radio"
+ "Enviar SMS a télex"
+ "Enviar SMS a TTY/TDD"
+ "Enviar SMS al móvil del trabajo"
+ "Enviar SMS al localizador del trabajo"
+ "Enviar SMS a %s "
+ "Enviar SMS a MMS"
+ "Realizar videollamada"
+ "¿Borrar contactos frecuentes?"
+ "Borrarás la lista de personas con las que te pones en contacto frecuentemente de las aplicaciones Contactos y Teléfono. Además, tus aplicaciones de correo deberán establecer tus preferencias nuevamente."
+ "Borrando contactos frecuentes…"
+ "Disponible"
+ "Ausente"
+ "Ocupado"
+ "Contactos"
+ "Otro"
+ "Directorio"
+ "Todos los contactos"
+ "Yo"
+ "Buscando…"
+ "Más de %d encontrados"
+ "No hay contactos"
+
+ - Se encontraron
%d .
+ - Se encontró 1.
+
+ "Contacto rápido para %1$s "
+ "(Sin nombre)"
+ "Llamados con frecuencia"
+ "Contactados con frecuencia"
+ "Ver contacto"
+ "Todos los contactos con número de teléfono"
+ "Ver actualizaciones"
+ "Contacto no sincronizado"
+ "Nombre"
+ "Apodo"
+ "Nombre"
+ "Nombre"
+ "Apellido"
+ "Tratamiento"
+ "Segundo nombre"
+ "Título académico o profesional"
+ "Nombre fonético"
+ "Nombre fonético"
+ "Segundo nombre fonético"
+ "Apellido fonético"
+ "Teléfono"
+ "Dirección de correo"
+ "Dirección"
+ "MI"
+ "Organización"
+ "Relación"
+ "Fechas especiales"
+ "Mensaje de texto"
+ "Dirección"
+ "Empresa"
+ "Título"
+ "Notas"
+ "SIP"
+ "Sitio web"
+ "Grupos"
+ "Correo personal"
+ "Correo móvil"
+ "Correo laboral"
+ "Enviar correo a"
+ "Enviar correo a %s "
+ "Enviar correo"
+ "Calle"
+ "Apartado postal"
+ "Barrio"
+ "Ciudad"
+ "Estado"
+ "Código postal"
+ "País"
+ "Ver dirección personal"
+ "Ver dirección laboral"
+ "Ver dirección"
+ "Ver dirección %s "
+ "Chat mediante AIM"
+ "Chat mediante Windows Live"
+ "Chat mediante Yahoo"
+ "Chat mediante Skype"
+ "Chat mediante QQ"
+ "Chat mediante Google Talk"
+ "Chat mediante ICQ"
+ "Chat mediante Jabber"
+ "Chatear"
+ "eliminar"
+ "Expandir o contraer campos de nombre"
+ "Todos los contactos"
+ "Destacados"
+ "Personalizar"
+ "Contacto"
+ "Todos los otros contactos"
+ "Todos los contactos"
+ "Eliminar grupo de sincronización"
+ "Agregar grupo de sincronización"
+ "Más grupos…"
+ "Si eliminas \"%s \" de la sincronización, también se eliminarán todos los contactos no agrupados."
+ "Guardando opciones de visualización…"
+ "Listo"
+ "Cancelar"
+ "Contactos en %s "
+ "Contactos en vista personalizada"
+ "Contacto único"
+ "Crear contacto en la cuenta"
+ "Importar desde tarjeta SIM"
+ "Importar desde SIM ^1 (^2 )"
+ "Importar desde SIM %1$s "
+ "Importar desde el archivo .vcf"
+ "¿Deseas cancelar la importación de %s ?"
+ "¿Deseas cancelar la exportación de %s ?"
+ "No se canceló impor./expor. de vCard."
+ "Error desconocido"
+ "No se pudo abrir el archivo \"%s \" (%s )."
+ "No se pudo iniciar el exportador (\"%s \")."
+ "No hay contactos para exportar."
+ "Inhabilitaste un permiso necesario."
+ "Se produjo un error durante la exportación (\"%s \")."
+ "El nombre de archivo obligatorio es demasiado largo (\"%s \")."
+ "La tarjeta SD contiene demasiados archivos vCard."
+ "Error de E/S"
+ "Memoria insuficiente (es probable que el archivo sea muy grande)."
+ "No se pudo analizar el archivo vCard debido a un error inesperado."
+ "El formato no se admite."
+ "No se pudieron recopilar los metadatos de los archivos vCard proporcionados."
+ "No se pudieron importar uno o más archivos (%s)."
+ "Exportación de %s finalizada"
+ "Finalizó la exportación de contactos"
+ "Se canceló la exportación de %s "
+ "Exportando datos de contacto"
+ "Los datos de tu contacto se están exportando a %s ."
+ "No se pudo obtener la información de la base de datos."
+ "No hay contactos exportables. Si tienes contactos en el dispositivo, es posible que algunos proveedores de datos no permitan la exportación de contactos desde el dispositivo."
+ "El compositor de la vCard no se inició correctamente."
+ "No se pudo exportar"
+ "No se exportaron los datos del contacto.\nMotivo: \"%s \""
+ "Importando %s "
+ "No se pudieron leer los datos de vCard"
+ "Se canceló la lectura de datos de vCard"
+ "Finalizó la importación de vCard %s "
+ "Se canceló la importación de %s "
+ "%s se importará en breve."
+ "El archivo se importará en breve."
+ "Se rechazó la solicitud de importación de vCard. Vuelve a intentarlo más tarde."
+ "%s se exportará en breve."
+ "El archivo se exportará en breve."
+ "Se rechazó la solicitud de exportación de vCard. Vuelve a intentarlo más tarde."
+ "contactar"
+ "Almacenando vCard(s) en caché local temporal. La importación comenzará pronto."
+ "No se pudo importar el archivo de vCard."
+ "No se encontró ningún archivo vCard en la Tarjeta SD."
+ "Se recibió el contacto por NFC."
+ "¿Exportar contactos?"
+ "Almacenando en caché"
+ "No se pudo examinar la tarjeta SD (motivo: \"%s \")."
+ "Importando %s /%s : %s "
+ "Exportar al archivo .vcf"
+ "Ordenar por"
+ "Nombre"
+ "Apellido"
+ "Formato del nombre"
+ "Nombre primero"
+ "Apellido primero"
+ "Compartir contactos visibles"
+ "Se produjo un error al compartir los contactos visibles."
+ "Importar/exportar contactos"
+ "Importar contactos"
+ "No es posible compartir este contacto."
+ "Buscar"
+ "Contactos para mostrar"
+ "Contactos para mostrar"
+ "Definir vista personalizada"
+ "Buscar contactos"
+ "Favoritos"
+ "No hay contactos."
+ "No hay contactos visibles."
+ "No hay favoritos."
+ "No hay contactos en %s ."
+ "Borrar contactos frecuentes"
+ "Seleccionar tarjeta SIM"
+ "Cuentas"
+ "Importar/exportar"
+ "a través de %1$s "
+ "%1$s a través de %2$s "
+ "detener la búsqueda"
+ "Eliminar búsqueda"
+ "Opciones de visualización de contactos"
+ "Cuenta"
+ "Usar siempre para llamadas"
+ "Llamar con"
+ "Llamada con una nota"
+ "Escribe una nota para enviar con la llamada…"
+ "ENVIAR Y LLAMAR"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-es/strings.xml b/ContactsCommon/res/values-es/strings.xml
new file mode 100644
index 0000000..240f7ca
--- /dev/null
+++ b/ContactsCommon/res/values-es/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texto copiado"
+ "Copiar en el portapapeles"
+ "Llamar a %s "
+ "Llamar a casa"
+ "Llamar al móvil"
+ "Llamar al trabajo"
+ "Llamar al fax del trabajo"
+ "Llamar al fax de casa"
+ "Llamar al busca"
+ "Llamar"
+ "Llamar a un número de devolución de llamada"
+ "Llamar al coche"
+ "Llamar al teléfono principal de la empresa"
+ "Llamar a RDSI"
+ "Llamar al teléfono principal"
+ "Llamar al fax"
+ "Llamar a la radio"
+ "Llamar al télex"
+ "Llamar a TTY/TDD"
+ "Llamar al móvil del trabajo"
+ "Llamar al busca del trabajo"
+ "Llamar al %s "
+ "Llamar a MMS"
+ "Enviar SMS a %s "
+ "Enviar SMS a casa"
+ "Enviar SMS al móvil"
+ "Enviar SMS al trabajo"
+ "Enviar SMS al fax del trabajo"
+ "Enviar SMS al fax de casa"
+ "Enviar SMS al busca"
+ "Enviar SMS"
+ "Enviar SMS a un número de devolución de llamada"
+ "Enviar SMS al coche"
+ "Enviar SMS al teléfono principal de la empresa"
+ "Enviar SMS a RDSI"
+ "Enviar SMS al número de teléfono principal"
+ "Enviar SMS al fax"
+ "Enviar SMS a la radio"
+ "Enviar SMS al télex"
+ "Enviar SMS a TTY/TDD"
+ "Enviar SMS al móvil del trabajo"
+ "Enviar SMS al busca del trabajo"
+ "Enviar SMS al %s "
+ "Enviar SMS a MMS"
+ "Hacer videollamada"
+ "¿Borrar contactos frecuentes?"
+ "Vas a borrar la lista de contactos frecuentes de las aplicaciones Contactos y Teléfono y obligarás a las aplicaciones de correo electrónico a que memoricen tus preferencias de nuevo."
+ "Borrando contactos frecuentes…"
+ "Disponible"
+ "Ausente"
+ "Ocupado"
+ "Contactos"
+ "Otros"
+ "Directorio"
+ "Todos los contactos"
+ "Yo"
+ "Buscando..."
+ "Se han encontrado más de %d contactos."
+ "Ningún contacto"
+
+ %d contactos encontrados
+ - 1 contacto encontrado
+
+ "Contacto rápido de %1$s "
+ "(Sin nombre)"
+ "Más llamados"
+ "Contactos frecuentes"
+ "Ver contacto"
+ "Todos los contactos con número"
+ "Ver actualizaciones"
+ "Solo en teléfono, no sincronizar"
+ "Nombre"
+ "Apodo"
+ "Nombre"
+ "Nombre"
+ "Apellido"
+ "Prefijo del nombre"
+ "Segundo nombre"
+ "Sufijo del nombre"
+ "Nombre fonético"
+ "Nombre fonético"
+ "Segundo nombre fonético"
+ "Apellido fonético"
+ "Teléfono"
+ "Correo electrónico"
+ "Dirección"
+ "Chat"
+ "Organización"
+ "Relación"
+ "Fechas especiales"
+ "Mensaje de texto"
+ "Dirección"
+ "Empresa"
+ "Cargo"
+ "Notas"
+ "SIP"
+ "Sitio web"
+ "Grupos"
+ "Enviar correo a casa"
+ "Enviar correo al móvil"
+ "Enviar correo al trabajo"
+ "Enviar correo"
+ "Enviar correo a %s "
+ "Correo electrónico"
+ "Calle"
+ "Apartado postal"
+ "Vecindario"
+ "Ciudad"
+ "Estado"
+ "Código postal"
+ "País"
+ "Ver dirección de casa"
+ "Ver dirección del trabajo"
+ "Ver dirección"
+ "Ver dirección de %s "
+ "Chatear con AIM"
+ "Chatear con Windows Live"
+ "Chatear con Yahoo!"
+ "Chatear con Skype"
+ "Chatear con QQ"
+ "Chatear con Google Talk"
+ "Chatear con ICQ"
+ "Chatear con Jabber"
+ "Chat"
+ "eliminar"
+ "Ampliar o contraer campos de nombre"
+ "Todos los contactos"
+ "Destacados"
+ "Personalizar"
+ "Contacto"
+ "Todos los demás contactos"
+ "Todos los contactos"
+ "Eliminar grupo de sincronización"
+ "Añadir grupo de sincronización"
+ "Más grupos…"
+ "Si quitas \"%s \" de las sincronización, también se quitarán todos los contactos no agrupados."
+ "Guardando opciones de visualización…"
+ "Listo"
+ "Cancelar"
+ "Contactos en %s "
+ "Contactos en vista personalizada"
+ "Contacto único"
+ "Crear contacto en la cuenta"
+ "Importar contactos de la tarjeta SIM"
+ "Importar de SIM ^1 - ^2 "
+ "Importar de SIM %1$s "
+ "Importar de archivo .vcf"
+ "¿Quieres cancelar la importación de %s ?"
+ "¿Quieres cancelar la exportación de %s ?"
+ "Error al cancelar la importación/exportación de vCard"
+ "Error desconocido"
+ "No se ha podido abrir el archivo \"%s \" (%s )."
+ "No se ha podido iniciar el exportador (\"%s \")."
+ "No hay contactos que exportar."
+ "Has inhabilitado un permiso necesario."
+ "Se ha producido un error durante la exportación (\"%s \")."
+ "El nombre de archivo obligatorio es demasiado largo (\"%s \")."
+ "Hay demasiados archivos vCard en la tarjeta SD."
+ "Error de E/S"
+ "No hay suficiente espacio de memoria (el archivo puede ser demasiado grande)."
+ "No se ha podido analizar el archivo vCard debido a un error inesperado."
+ "Formato no admitido"
+ "No se han podido recuperar los metadatos de los archivos vCard."
+ "No se ha podido importar uno o más archivos (%s)."
+ "Exportación de %s finalizada"
+ "Los contactos se han exportado."
+ "Se ha cancelado la exportación de %s ."
+ "Exportando datos de contacto..."
+ "Se están exportando tus datos de contactos a %s ."
+ "No se ha podido obtener información de la base de datos."
+ "No hay contactos que exportar. Si ya tienes contactos en el teléfono, es posible que el proveedor de datos no permita que se exporten los contactos del teléfono."
+ "El redactor de vCard no se ha iniciado correctamente."
+ "Error al exportar"
+ "No se han exportado los datos de contacto(\nmotivo: \"%s \")."
+ "Importando %s ..."
+ "Error al leer los datos de vCard"
+ "Lectura de datos de vCard cancelada"
+ "Importación de %s de vCard finalizada"
+ "Se ha cancelado la importación de %s ."
+ "%s se importará en breve."
+ "El archivo se importará en breve."
+ "Se ha rechazado la solicitud de importación de vCard. Inténtalo de nuevo más tarde."
+ "%s se exportará en breve."
+ "El archivo se exportará en breve."
+ "Se ha rechazado la solicitud de exportación de vCard. Inténtalo de nuevo más tarde."
+ "contacto"
+ "Se están almacenando los archivos vCard en la caché. La importación empezará pronto."
+ "Error al importar el archivo vCard"
+ "No se han encontrado archivos vCard en la tarjeta SD."
+ "Contacto recibido por NFC"
+ "¿Exportar contactos?"
+ "Almacenando en caché..."
+ "No se ha podido analizar la tarjeta SD (motivo: \"%s \")."
+ "Importando %s /%s : %s "
+ "Exportar a archivo .vcf"
+ "Ordenar por"
+ "Nombre"
+ "Apellido"
+ "Formato del nombre"
+ "Nombre primero"
+ "Apellido primero"
+ "Compartir contactos visibles"
+ "No ha sido posible compartir los contactos visibles."
+ "Importar/exportar contactos"
+ "Importar contactos"
+ "Este contacto no se puede compartir."
+ "Buscar"
+ "Contactos que mostrar"
+ "Contactos que mostrar"
+ "Definir vista personalizada"
+ "Buscar contactos"
+ "Favoritos"
+ "No hay ningún contacto."
+ "No hay contactos visibles."
+ "No hay favoritos."
+ "Ningún contacto en %s "
+ "Borrar frecuentes"
+ "Seleccionar tarjeta SIM"
+ "Cuentas"
+ "Importar/exportar"
+ "a través de %1$s "
+ "%1$s a través de %2$s "
+ "dejar de buscar"
+ "Borrar la búsqueda"
+ "Opciones para mostrar contactos"
+ "Cuenta"
+ "Usar siempre esta para llamadas"
+ "Llamar con"
+ "Llamada con una nota"
+ "Escribe una nota para enviarla con la llamada..."
+ "ENVIAR Y LLAMAR"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-et-rEE/strings.xml b/ContactsCommon/res/values-et-rEE/strings.xml
new file mode 100644
index 0000000..b3406ce
--- /dev/null
+++ b/ContactsCommon/res/values-et-rEE/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Tekst on kopeeritud"
+ "Kopeeri lõikelauale"
+ "Helista numbrile %s "
+ "Helista kodutelefoninumbrile"
+ "Helista mobiilinumbrile"
+ "Helista töönumbrile"
+ "Helista tööfaksinumbrile"
+ "Helista kodufaksinumbrile"
+ "Helista piiparinumbrile"
+ "Helista"
+ "Helista tagasihelistusnumbrile"
+ "Helista autotelefoninumbrile"
+ "Helista ettevõtte põhinumbrile"
+ "Helista ISDN-telefoninumbrile"
+ "Helista põhinumbrile"
+ "Helista faksinumbrile"
+ "Helista raadiotelefoninumbrile"
+ "Helista teleksinumbrile"
+ "Helista TTY-/TDD-numbrile"
+ "Helista töömobiilinumbrile"
+ "Helista tööpiiparinumbrile"
+ "Helista numbrile %s "
+ "Helista MMS-i numbrile"
+ "Saada sõnum numbrile %s "
+ "Saada sõnum kodutelefoninumbrile"
+ "Saada sõnum mobiilinumbrile"
+ "Saada sõnum töönumbrile"
+ "Saada sõnum tööfaksinumbrile"
+ "Saada sõnum kodufaksinumbrile"
+ "Saada sõnum piiparinumbrile"
+ "Saada sõnum"
+ "Saada sõnum tagasihelistusnumbrile"
+ "Saada sõnum autotelefoninumbrile"
+ "Saada sõnum ettevõtte põhitelefoninumbrile"
+ "Saada sõnum ISDN-telefoninumbrile"
+ "Saada sõnum põhinumbrile"
+ "Saada sõnum faksinumbrile"
+ "Saada sõnum raadiotelefoninumbrile"
+ "Saada sõnum teleksinumbrile"
+ "Saada sõnum TTY-/TDD-numbrile"
+ "Saada sõnum töömobiilinumbrile"
+ "Saada sõnum tööpiiparinumbrile"
+ "Saada sõnum numbrile %s "
+ "Saada sõnum MMS-i numbrile"
+ "Videokõne tegemine"
+ "Kas kustutada sagedased kontaktid?"
+ "Kustutate rakendustes Kontaktid ja Telefon sagedaste kontaktide loendi, mistõttu meilirakendused peavad teie adresseerimiseelistused uuesti omandama."
+ "Saged. kontaktide kustutamine ..."
+ "Saadaval"
+ "Eemal"
+ "Hõivatud"
+ "Kontaktid"
+ "Muu"
+ "Kataloog"
+ "Kõik kontaktid"
+ "Mina"
+ "Otsimine ..."
+ "Leitud rohkem kui %d ."
+ "Kontaktid puuduvad"
+
+ - Leiti
%d
+ - Leiti 1
+
+ "Kiirkontakt: %1$s "
+ "(Nimi puudub)"
+ "Sageli valitud üksus"
+ "Sageli valitud kontaktisikud"
+ "Vaadake kontakti"
+ "Kõik telefoninumbritega kontaktid"
+ "Kuva värskendused"
+ "Ainult telefon, sünkroonimata"
+ "Nimi"
+ "Hüüdnimi"
+ "Nimi"
+ "Eesnimi"
+ "Perekonnanimi"
+ "Nime eesliide"
+ "Teine nimi"
+ "Nime järelliide"
+ "Foneetiline nimi"
+ "Foneetiline eesnimi"
+ "Foneetiline keskmine nimi"
+ "Foneetiline perekonnanimi"
+ "Telefon"
+ "Meil"
+ "Aadress"
+ "IM"
+ "Organisatsioon"
+ "Suhe"
+ "Kindlad kuupäevad"
+ "Tekstisõnum"
+ "Aadress"
+ "Ettevõte"
+ "Nimetus"
+ "Märkmed"
+ "SIP"
+ "Veebisait"
+ "Rühmad"
+ "Saada meilisõnum kodusele aadressile"
+ "Saada meilisõnum mobiilile"
+ "Saada meiliaadress tööaadressile"
+ "Meilimine"
+ "Saada meilisõnum aadressile %s "
+ "Meilimine"
+ "Tänav"
+ "Postkast"
+ "Naabruskond"
+ "Linn"
+ "Osariik"
+ "Sihtnumber"
+ "Riik"
+ "Kuva kodune aadress"
+ "Kuva tööaadress"
+ "Kuva aadress"
+ "Kuva aadress (%s )"
+ "Vestlus AIM-i kasutades"
+ "Vestlus Windows Live\'i kasutades"
+ "Vestlus Yahood kasutades"
+ "Vestlus Skype\'i kasutades"
+ "Vestlus QQ kasutades"
+ "Vestlus Google Talki kasutades"
+ "Vestlus ICQ-d kasutades"
+ "Vestlus Jabberit kasutades"
+ "Vestlus"
+ "kustutamine"
+ "Nimeväljade laiendamine või ahendamine"
+ "Kõik kontaktid"
+ "Tärniga tähistatud"
+ "Kohanda"
+ "Kontakt"
+ "Kõik teised kontaktid"
+ "Kõik kontaktid"
+ "Eemalda sünkroonimisrühm"
+ "Lisa sünkroonimisrühm"
+ "Rohkem rühmi ..."
+ "Rühma „%s ” eemaldamisel sünkroonimisest eemaldatakse sünkroonimisest ka kõik rühmitamata kontaktid."
+ "Kuvavalikute salvestamine ..."
+ "Valmis"
+ "Tühista"
+ "Konto %s kontaktid"
+ "Kontaktid kohandatud vaates"
+ "Üks kontakt"
+ "Loo konto all kontakt"
+ "Impordi SIM-kaardilt"
+ "Importimine SIM-kaardilt ^1 – ^2 "
+ "Importimine SIM-kaardilt %1$s "
+ "Importimine VCF-failist"
+ "Kas tühistada faili %s importimine?"
+ "Kas tühistada faili %s eksportimine?"
+ "vCardi impordi/ekspordi tühist. ebaõnn."
+ "Tundmatu viga."
+ "Faili „%s ” ei saa avada: %s ."
+ "Eksportijat ei saa käivitada: „%s ”."
+ "Eksporditavad kontaktid puuduvad."
+ "Olete nõutud loa keelanud."
+ "Viga eksportimisel: „%s ”."
+ "Kohustuslik failinimi on liiga pikk („%s ”)."
+ "SD-kaardil on liiga palju vCardi faile."
+ "I/O viga"
+ "Pole piisavalt mälu. Fail võib olla liiga suur."
+ "Ootamatul põhjusel vCardi sõelumine ebaõnnestus."
+ "Vormingut ei toetata."
+ "vCardi faili(de) metaandmete kogumine ebaõnnestus."
+ "Vähemalt ühe faili importimine ebaõnnestus (%s)."
+ "Faili %s eksportimine on lõpetatud."
+ "Kontaktide eksportimine on lõpetatud."
+ "Faili %s eksportimine tühistati."
+ "Kontaktandmete eksportimine"
+ "Teie kontaktandmed eksporditakse faili: %s ."
+ "Andmebaasiteabe hankimine ebaõnnestus."
+ "Eksporditavaid kontakte pole. Kui teil siiski on telefonis kontakte, ei pruugi mõned andmesidepakkujad kontaktide eksportimist telefonist lubada."
+ "vCardi helilooja ei käivitunud korralikult."
+ "Eksport ebaõnnestus"
+ "Kontaktandmeid ei eksporditud.\nPõhjus: „%s ”"
+ "Importimine: %s "
+ "vCardi andmete lugemine ebaõnnestus"
+ "vCardi andmete lugemine tühistati"
+ "vCardi faili %s importimine lõpetatud"
+ "Faili %s importimine tühistati"
+ "Fail %s imporditakse peagi."
+ "Fail imporditakse peagi."
+ "vCardi importimistaotlus lükati tagasi. Proovige hiljem uuesti."
+ "Fail %s eksporditakse peagi."
+ "Fail eksporditakse peagi."
+ "vCardi eksportimistaotlus lükati tagasi. Proovige hiljem uuesti."
+ "kontakt"
+ "vCardi(de) vahemälustamine kohalikku ajutisse mäluruumi. Tegelik importimine algab peagi."
+ "vCardi importimine ebaõnnestus."
+ "SD-kaardilt ei leitud vCardi faili."
+ "Kontakt saadud NFC kaudu"
+ "Eksportida kontakt?"
+ "Vahemällu salvestamine"
+ "SD-kaarti ei saa skannida. (Põhjus: „%s ”)"
+ "Importimine: %s /%s – %s "
+ "Eksportimine VCF-faili"
+ "Sortimisalus:"
+ "Eesnimi"
+ "Perekonnanimi"
+ "Nimevorming"
+ "Eesnimi enne"
+ "Perekonnanimi enne"
+ "Jaga nähtavaid kontakte"
+ "Nähtavate kontaktide jagamine ebaõnnestus."
+ "Kontaktide import/eksport"
+ "Impordi kontaktid"
+ "Seda kontakti ei saa jagada."
+ "Otsing"
+ "Kuvatavad kontaktisikud"
+ "Kuvatavad kontaktisikud"
+ "Kohandatud vaate määramine"
+ "Otsige kontakte"
+ "Lemmikud"
+ "Kontaktid puuduvad."
+ "Nähtavaid kontakte pole."
+ "Lemmikuid pole."
+ "Grupis %s pole kontakte"
+ "Sagedaste kustutamine"
+ "SIM-kaardi valimine"
+ "Kontod"
+ "Impordi/ekspordi"
+ "allika %1$s kaudu"
+ "%1$s allika %2$s kaudu"
+ "otsimise peatamine"
+ "Otsingu kustutamine"
+ "Kontaktide kuvavalikud"
+ "Konto"
+ "Kasuta helistamiseks alati seda"
+ "Helistamine kontoga"
+ "Kõne koos märkusega"
+ "Sisestage märkus, mis koos kõnega saata ..."
+ "SAADA JA HELISTA"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-eu-rES/strings.xml b/ContactsCommon/res/values-eu-rES/strings.xml
new file mode 100644
index 0000000..d71b2f5
--- /dev/null
+++ b/ContactsCommon/res/values-eu-rES/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Testua kopiatu da"
+ "Kopiatu arbelean"
+ "Deitu %s zenbakira"
+ "Deitu etxera"
+ "Deitu mugikorrera"
+ "Deitu lanera"
+ "Deitu laneko faxera"
+ "Deitu etxeko faxera"
+ "Deitu bilagailura"
+ "Deitu"
+ "Dei bidezko erantzuna"
+ "Deitu autoko telefonora"
+ "Deitu enpresaren zenbaki nagusira"
+ "Deitu ISDN zenbakira"
+ "Deitu zenbaki nagusira"
+ "Deitu faxera"
+ "Deitu irratira"
+ "Deitu telexera"
+ "Deitu TTY/TDD zenbakira"
+ "Deitu laneko mugikorrera"
+ "Deitu laneko bilagailura"
+ "Deitu %s zenbakira"
+ "Deitu MMS zenbakira"
+ "Bidali testu-mezua honi: %s "
+ "Bidali testu-mezua etxera"
+ "Bidali testu-mezua mugikorrera"
+ "Bidali testu-mezua lanera"
+ "Bidali testu-mezua laneko faxera"
+ "Bidali testu-mezua etxeko faxera"
+ "Bidali testu-mezua bilagailura"
+ "Bidali testu-mezua"
+ "Testu bidezko erantzuna"
+ "Bidali testu-mezua autora"
+ "Bidali testu-mezua enpresako zenbaki nagusira"
+ "Bidali testu-mezua ISDN zenbakira"
+ "Bidali testu-mezua zenbaki nagusira"
+ "Bidali testu-mezua faxera"
+ "Bidali testu-mezua irratira"
+ "Bidali testu-mezua telexera"
+ "Bidali testu-mezua TTY/TDD zenbakira"
+ "Bidali testu-mezua laneko mugikorrera"
+ "Bidali testu-mezua laneko bilagailura"
+ "Bidali testu-mezua honi: %s "
+ "Bidali testu-mezua MMS zenbakira"
+ "Egin bideo-deia"
+ "Sarri erabilitako kontaktuak garbitu?"
+ "Kontaktuak eta Telefonoa aplikazioetako sarri erabilitako kontaktuen zerrenda garbituko duzu; beraz, posta elektronikoaren aplikazioek helbideen hobespenak hutsetik ikasi beharko dituzte."
+ "Sarri erabilitako kontaktuak garbitzen…"
+ "Libre"
+ "Kanpoan"
+ "Okupatuta"
+ "Kontaktuak"
+ "Beste bat"
+ "Direktorioa"
+ "Kontaktu guztiak"
+ "Ni"
+ "Bilatzen…"
+ "%d baino gehiago aurkitu dira."
+ "Ez dago kontakturik."
+
+ %d aurkitu dira
+ - Bat aurkitu da
+
+ "Honen kontaktu bizkorra: %1$s "
+ "(Izenik ez)"
+ "Sarri deitutakoak"
+ "Sarri kontaktatutakoak"
+ "Ikusi kontaktua"
+ "Telefono-zenbakiak dituzten kontaktu guztiak"
+ "Ikusi berritasunak"
+ "Telefonoan soilik, sinkronizatu gabe"
+ "Izena"
+ "Goitizena"
+ "Izena"
+ "Izena"
+ "Abizena"
+ "Izenaren aurrizkia"
+ "Bigarren izena"
+ "Izenaren atzizkia"
+ "Izen fonetikoa"
+ "Izen fonetikoa"
+ "Bigarren izen fonetikoa"
+ "Abizen fonetikoa"
+ "Telefonoa"
+ "Helbide elektronikoa"
+ "Helbidea"
+ "IM"
+ "Erakundea"
+ "Harremana"
+ "Data bereziak"
+ "Testu-mezua"
+ "Helbidea"
+ "Enpresa"
+ "Izena"
+ "Oharrak"
+ "SIP"
+ "Webgunea"
+ "Taldeak"
+ "Bidali mezu elektronikoa etxera"
+ "Bidali mezu elektronikoa mugikorrera"
+ "Bidali mezu elektronikoa lanera"
+ "Bidali mezu elektronikoa"
+ "Bidali mezu elektronikoa %s helbidera"
+ "Bidali mezu elektronikoa"
+ "Kalea"
+ "Posta-kutxa"
+ "Auzoa"
+ "Hiria"
+ "Estatua"
+ "Posta-kodea"
+ "Herrialdea"
+ "Ikusi etxeko helbidea"
+ "Ikusi laneko helbidea"
+ "Ikusi helbidea"
+ "Ikusi %s helbidea"
+ "Txateatu AIM bidez"
+ "Txateatu Windows Live bidez"
+ "Txateatu Yahoo bidez"
+ "Txateatu Skype bidez"
+ "Txateatu QQ bidez"
+ "Txateatu Google Talk bidez"
+ "Txateatu ICQ bidez"
+ "Txateatu Jabber bidez"
+ "Txateatu"
+ "ezabatu"
+ "Zabaldu edo tolestu izenen eremuak"
+ "Kontaktu guztiak"
+ "Izardunak"
+ "Pertsonalizatu"
+ "Kontaktua"
+ "Beste kontaktu guztiak"
+ "Kontaktu guztiak"
+ "Kendu sinkronizazio-taldea"
+ "Gehitu sinkronizazio-taldea"
+ "Talde gehiago…"
+ "\"%s \" sinkronizaziotik kentzen baduzu, talderik gabeko kontaktu guztiak ere ezabatuko dira."
+ "Bistaratze-aukerak gordetzen…"
+ "Eginda"
+ "Utzi"
+ "Kontu honetako kontaktuak: %s "
+ "Ikuspegi pertsonalizatuko kontaktuak"
+ "Kontaktu bakarra"
+ "Sortu kontaktua kontu baten barruan"
+ "Inportatu SIM txarteletik"
+ "Inportatu SIM honetatik: ^1 - ^2 "
+ "Inportatu SIM honetatik: %1$s "
+ "Inportatu .vcf fitxategitik"
+ "%s fitxategiaren inportazioa utzi?"
+ "%s fitxategiaren esportazioa utzi?"
+ "Ezin da vCard inportazioa/esportazioa utzi"
+ "Errore ezezaguna."
+ "Ezin izan da \"%s \" ireki. Arrazoia: %s ."
+ "Ezin izan da esportatzailea hasi. Arrazoia: \"%s \"."
+ "Ez dago esporta daitekeen kontakturik."
+ "Desgaitu egin duzu beharrezko baimena."
+ "Errore bat gertatu da esportatu bitartean. Arrazoia: \"%s \"."
+ "Beharrezko fitxategi-izena luzeegia (\"%s \")."
+ "VCard fitxategi gehiegi daude SD txartelean."
+ "Sarrera- edo irteera-errorea"
+ "Ez dago nahikoa memoria. Baliteke fitxategia handiegia izatea."
+ "Ezin izan da vCard analizatu ustekabeko arrazoiren batengatik."
+ "Formatua ez da bateragarria."
+ "Ezin izan da emandako vCard txartelaren fitxategien meta-informazioa bildu."
+ "Ezin izan da fitxategi bat edo gehiago inportatu (%s)."
+ "Amaitu %s esportatzen."
+ "Esportatu dira kontaktuak."
+ "%s fitxategia esportatzeari utzi zaio."
+ "Kontaktatzeko datuak esportatzen"
+ "Zurekin kontaktatzeko datuak fitxategi honetara esportatzen ari dira: %s ."
+ "Ezin izan da datu-baseko informazioa lortu."
+ "Ez dago esporta daitekeen kontakturik. Telefonoan kontaktuak badituzu, baliteke datu-hornitzaileek kontaktuak telefonotik esportatzen ez uztea."
+ "vCard txartelen idazlea ez da behar bezala hasi."
+ "Ezin da esportatu"
+ "Ez dira kontaktuaren datuak esportatu.\nArrazoia: \"%s \""
+ "%s inportatzen"
+ "Ezin izan dira vCard datuak irakurri"
+ "vCard datuen irakurketa utzi da"
+ "%s vCard fitxategia inportatzen bukatu da"
+ "%s fitxategiaren inportazioa utzi da"
+ "%s fitxategia laster inportatuko da."
+ "Fitxategia laster inportatuko da."
+ "vCard fitxategia inportatzeko eskaera ukatu da. Saiatu geroago."
+ "%s fitxategia laster esportatuko da."
+ "Laster esportatuko da fitxategia."
+ "vCard fitxategia esportatzeko eskaera ukatu da. Saiatu berriro geroago."
+ "kontaktua"
+ "vCard-ak aldi baterako biltegi lokalaren cachean gordetzen ari dira. Inportazioa bera laster hasiko da."
+ "Ezin izan da vCard fitxategia inportatu."
+ "Ez da vCard fitxategirik aurkitu SD memorian."
+ "NFC bidez jasotako kontaktua"
+ "Kontaktuak esportatu?"
+ "Cachean gordetzen"
+ "Ezin izan da SD txartelean bilatu. (Arrazoia: \"%s \")"
+ "%s /%s inportatzen: %s "
+ "Esportatu .vcf fitxategira"
+ "Ordenatzeko irizpidea"
+ "Izena"
+ "Abizena"
+ "Izenaren formatua"
+ "Izena lehenengo"
+ "Abizena lehenengo"
+ "Partekatu ikusgai dauden kontaktuak"
+ "Ezin izan dira partekatu ikusgai dauden kontaktuak."
+ "Inportatu edo esportatu kontaktuak"
+ "Inportatu kontaktuak"
+ "Ezin da kontaktua partekatu."
+ "Bilatu"
+ "Bistaratzeko kontaktuak"
+ "Bistaratzeko kontaktuak"
+ "Definitu ikuspegi pertsonalizatua"
+ "Aurkitu kontaktuak"
+ "Gogokoak"
+ "Ez dago kontakturik."
+ "Ez dago ikusgai dagoen kontakturik."
+ "Ez dago gogokorik."
+ "Ez dago kontakturik hemen: %s "
+ "Garbitu sarri erabilitakoak"
+ "Hautatu SIM txartela"
+ "Kontuak"
+ "Inportatu/esportatu"
+ "%1$s bidez"
+ "%1$s , %2$s bidez"
+ "gelditu bilaketa"
+ "Garbitu bilaketa"
+ "Kontaktuak bistaratzeko aukerak"
+ "Kontua"
+ "Erabili beti hau deietarako"
+ "Deitu kontu honekin:"
+ "Egin deia oharrarekin"
+ "Idatzi deiarekin batera bidali beharreko oharra…"
+ "BIDALI ETA DEITU"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-fa/strings.xml b/ContactsCommon/res/values-fa/strings.xml
new file mode 100644
index 0000000..3181699
--- /dev/null
+++ b/ContactsCommon/res/values-fa/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "نوشتار کپی شد"
+ "کپی در بریدهدان"
+ "تماس با %s "
+ "تماس با منزل"
+ "تماس با تلفن همراه"
+ "تماس با محل کار"
+ "تماس با نمابر محل کار"
+ "تماس با نمابر منزل"
+ "تماس با پیجر"
+ "تماس"
+ "تماس با شماره بازگرداندن تماس"
+ "تماس با تلفن خودرو"
+ "تماس با خط اصلی شرکت"
+ "تماس با ISDN"
+ "تماس با خط اصلی"
+ "تماس با نمابر"
+ "تماس با تلفن رادیویی"
+ "تماس با تلکس"
+ "تماس با TTY/TDD"
+ "تماس با تلفن همراه محل کار"
+ "تماس با پیجر محل کار"
+ "تماس با %s "
+ "تماس با MMS"
+ "ارسال پیامک به %s "
+ "ارسال پیامک به تلفن منزل"
+ "ارسال پیامک به تلفن همراه"
+ "ارسال پیامک به محل کار"
+ "ارسال پیامک به نمابر محل کار"
+ "ارسال پیامک به نمابر منزل"
+ "ارسال پیامک به پیجر"
+ "ارسال پیامک"
+ "ارسال پیامک به شماره برگرداندن تماس"
+ "ارسال پیامک به تلفن خودرو"
+ "ارسال پیامک به خط اصلی شرکت"
+ "ارسال پیامک به ISDN"
+ "ارسال پیامک به شماره اصلی"
+ "ارسال پیامک به نمابر"
+ "ارسال پیامک به تلفن رادیویی"
+ "ارسال پیامک به تلکس"
+ "ارسال پیامک به TTY/TDD"
+ "ارسال پیامک به تلفن همراه محل کار"
+ "ارسال پیامک به پیجر محل کار"
+ "ارسال پیامک به %s "
+ "ارسال پیامک به MMS"
+ "برقراری تماس ویدیویی"
+ "لیست تماس مکرر پاک شود؟"
+ "بااینکار، فهرست افرادی را که بیشتر با آنها تماس گرفتهاید، در برنامههای «مخاطبین» و «تلفن» پاک میکنید و برنامههای ایمیل را وادار میکنید تنظیمات برگزیده مخاطبین شما را از اول یاد بگیرند."
+ "در حال پاک کردن لیست تماس مکرر…"
+ "در دسترس"
+ "غایب"
+ "مشغول"
+ "مخاطبین"
+ "موارد دیگر"
+ "فهرست"
+ "همه مخاطبین"
+ "من"
+ "درحال جستجو..."
+ "بیش از %d مورد یافت شد."
+ "مخاطبی موجود نیست"
+
+ %d مورد پیدا شد
+ %d مورد پیدا شد
+
+ "تماس سریع برای %1$s "
+ "(بدون نام)"
+ "بیشتر تماس گرفته شده"
+ "بیشتر ارتباط برقرار شده"
+ "مشاهده مخاطب"
+ "همه مخاطبین دارای شماره تلفن"
+ "مشاهده بهروزرسانیها"
+ "فقط تلفن، همگامسازی نشده"
+ "نام"
+ "نام مستعار"
+ "نام"
+ "نام"
+ "نام خانوادگی"
+ "پیشوند نام"
+ "نام میانی"
+ "پسوند نام"
+ "تلفظ نام"
+ "تلفظ نام"
+ "تلفظ نام میانی"
+ "تلفظ نام خانوادگی"
+ "تلفن"
+ "ایمیل"
+ "آدرس"
+ "پیام فوری"
+ "سازمان"
+ "نسبت"
+ "تاریخهای ویژه"
+ "پیامک"
+ "آدرس"
+ "شرکت"
+ "عنوان"
+ "یادداشتها"
+ "SIP"
+ "وب سایت"
+ "گروهها"
+ "ایمیل به منزل"
+ "ایمیل به تلفن همراه"
+ "ایمیل به محل کار"
+ "ایمیل"
+ "ایمیل به %s "
+ "ایمیل"
+ "خیابان"
+ "صندوق پستی"
+ "محله"
+ "شهر"
+ "ایالت"
+ "کد پستی"
+ "کشور"
+ "مشاهده آدرس منزل"
+ "مشاهده آدرس محل کار"
+ "مشاهده آدرس"
+ "مشاهده آدرس %s "
+ "گپ با استفاده از AIM"
+ "گپ با استفاده از Windows Live"
+ "گپ با استفاده از Yahoo"
+ "گپ با استفاده از Skype"
+ "گپ با استفاده از QQ"
+ "گپ با استفاده از Google Talk"
+ "گپ با استفاده از ICQ"
+ "گپ با استفاده از Jabber"
+ "گپ"
+ "حذف"
+ "بزرگ یا کوچک کردن قسمتهای نام"
+ "همه مخاطبین"
+ "ستاره دار"
+ "سفارشی کردن"
+ "مخاطب"
+ "همهٔ مخاطبین دیگر"
+ "همه مخاطبین"
+ "حذف گروه همگامسازی"
+ "افزودن گروه همگامسازی"
+ "گروههای بیشتر..."
+ "حذف «%s » از همگامسازی نیز هر گونه مخاطب گروهبندی نشدهای را از همگامسازی حذف میکند."
+ "در حال ذخیره گزینههای نمایش..."
+ "تمام"
+ "لغو"
+ "مخاطبین در %s "
+ "مخاطبین در نمای سفارشی"
+ "مخاطب تکی"
+ "ایجاد مخاطبین تحت حساب"
+ "وارد کردن از سیم کارت"
+ "وارد کردن از سیمکارت ^1 - ^2 "
+ "وارد کردن از سیم کارت %1$s "
+ "وارد کردن از فایل .vcf"
+ "وارد کردن %s لغو شود؟"
+ "صادر کردن به %s لغو شود؟"
+ "وارد/صادرکردن کارت ویزیت لغو نمیشود"
+ "خطای ناشناخته."
+ "\"%s \" باز نشد: %s ."
+ "صادر کننده راهاندازی نشد: \"%s \"."
+ "هیچ مخاطب قابل صدوری موجود نیست."
+ "یک مجوز الزامی را غیرفعال کردهاید."
+ "خطایی در هنگام صادر کردن روی داد: \"%s \""
+ "نام فایل خیلی طولانی است (\"%s \")."
+ "فایلهای کارت ویزیت بسیار زیادی در کارت SD وجود دارد."
+ "خطای ورودی/خروجی"
+ "حافظه کافی نیست. ممکن است فایل بسیار بزرگ باشد."
+ "تفسیر کارت ویزیت به دلیل پیشبینی نشدهای ممکن نیست."
+ "قالب پشتیبانی نمیشود."
+ "نمیتوان اطلاعات متای فایل(های) کارت ویزیت داده شده را جمعآوری کرد."
+ "نمیتوان یک یا چند فایل را وارد کرد (%s)."
+ "صادر کردن %s پایان یافت."
+ "صادرکردن مخاطبین تمام شد."
+ "صادر کردن %s لغو شد."
+ "صدور اطلاعات مخاطب"
+ "دادههای تماس شما در حال صادر شدن به این فایل هستند: %s ."
+ "نمیتوان اطلاعات پایگاه داده را دریافت کرد."
+ "هیچ مخاطب قابل صدوری وجود ندارد. اگر در گوشی خود مخاطبینی دارید، بعضی از ارائهدهندگان داده ممکن است اجازه ندهند تا مخاطبین از گوشی صادر شوند."
+ "سازنده فایل کارت ویزیت به درستی اجرا نشد."
+ "صادر نمیشود"
+ "دادههای مخاطب صادر نشد.\nعلت: «%s »"
+ "وارد کردن %s "
+ "خواندن دادههای کارت ویزیت ممکن نیست"
+ "خواندن داده کارت ویزیت لغو شد"
+ "وارد کردن کارت ویزیت %s پایان یافت"
+ "واردکردن %s لغو شد"
+ "%s به زودی وارد میشود."
+ "فایل پس از مدت کوتاهی وارد میشود."
+ "درخواست وارد کردن کارت ویزیت رد شد. لطفاً بعداً امتحان کنید."
+ "%s به زودی صادر میشود."
+ "فایل بهزودی منقضی میشود."
+ "درخواست صدور کارت ویزیت رد شد. لطفاً بعداً امتحان کنید."
+ "مخاطب"
+ "در حال ذخیره کارت(های) ویزیت در حافظه موقت محلی است. وارد کردن واقعی به زودی آغاز خواهد شد."
+ "وارد کردن کارت ویزیت انجام نشد."
+ "هیچ فایل کارت ویزیتی در کارت SD یافت نشد."
+ "دریافت مخاطب باNFC"
+ "مخاطبین صادر شوند؟"
+ "در حال ذخیره در حافظهٔ پنهان"
+ "کارت SD نمیتواند بررسی شود. (علت: \"%s \")"
+ "وارد کردن %s %s : %s "
+ "صادر کردن به فایل .vcf"
+ "مرتبسازی براساس"
+ "نام"
+ "نام خانوادگی"
+ "قالب نام"
+ "ابتدا نام"
+ "ابتدا نام خانوادگی"
+ "اشتراکگذاری مخاطبین قابل مشاهده"
+ "مخاطبین قابل مشاهده به اشتراک گذاشته نشدند."
+ "وارد کردن/صادر کردن مخاطبین"
+ "وارد کردن مخاطبین"
+ "این مخاطب قابل اشتراکگذاری نیست."
+ "جستجو"
+ "مخاطبین جهت نمایش"
+ "مخاطبین جهت نمایش"
+ "تعیین نمای سفارشی"
+ "پیدا کردن مخاطبین"
+ "موارد دلخواه"
+ "مخاطبی موجود نیست."
+ "مخاطب قابل مشاهدهای موجود نیست."
+ "هیچ مورد دلخواهی وجود ندارد."
+ "هیچ مخاطبی در %s موجود نیست"
+ "پاک کردن موارد مکرر"
+ "انتخاب سیمکارت"
+ "حسابها"
+ "وارد کردن/صادر کردن"
+ "از طریق %1$s "
+ "%1$s از طریق %2$s "
+ "توقف جستجو"
+ "پاک کردن جستجو"
+ "گزینههای نمایش تماس"
+ "حساب"
+ "همیشه این سیم برای تماس استفاده شود"
+ "تماس با"
+ "تماس به همراه یادداشت"
+ "یادداشتی بنویسید که همراه تماس ارسال شود…"
+ "ارسال و تماس"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-fi/strings.xml b/ContactsCommon/res/values-fi/strings.xml
new file mode 100644
index 0000000..4c9c8a8
--- /dev/null
+++ b/ContactsCommon/res/values-fi/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Teksti kopioitu"
+ "Kopioi leikepöydälle"
+ "Soita: %s "
+ "Soita kotinumeroon"
+ "Soita matkapuhelimeen"
+ "Soita työnumeroon"
+ "Soita faksinumeroon (työ)"
+ "Soita faksinumeroon (koti)"
+ "Soita hakulaitteeseen"
+ "Soita"
+ "Soita takaisinsoittonumeroon"
+ "Soita autopuhelimeen"
+ "Soita yrityksen ensisijaiseen numeroon"
+ "Soita ISDN-numeroon"
+ "Soita ensisijaiseen numeroon"
+ "Soita faksinumeroon"
+ "Soita radiopuhelimeen"
+ "Soita teleksinumeroon"
+ "Soita tekstipuhelimeen"
+ "Soita matkapuhelimeen (työ)"
+ "Soita hakulaitteeseen (työ)"
+ "Soita: %s "
+ "Soita MMS-numeroon"
+ "Lähetä tekstiviesti: %s "
+ "Lähetä tekstiviesti kotinumeroon"
+ "Lähetä tekstiviesti matkapuhelimeen"
+ "Lähetä tekstiviesti työnumeroon"
+ "Lähetä tekstiviesti faksinumeroon (työ)"
+ "Lähetä tekstiviesti faksinumeroon (koti)"
+ "Lähetä tekstiviesti hakulaitteeseen"
+ "Teksti"
+ "Lähetä tekstiviesti takaisinsoittonumeroon"
+ "Lähetä tekstiviesti autopuhelimeen"
+ "Lähetä tekstiviesti yrityksen ensisijaiseen numeroon"
+ "Lähetä tekstiviesti ISDN-numeroon"
+ "Lähetä tekstiviesti ensisijaiseen numeroon"
+ "Lähetä tekstiviesti faksinumeroon"
+ "Lähetä tekstiviesti radiopuhelimeen"
+ "Lähetä tekstiviesti teleksinumeroon"
+ "Lähetä tekstiviesti tekstipuhelimeen"
+ "Lähetä tekstiviesti matkapuhelimeen (työ)"
+ "Lähetä tekstiviesti hakulaitteeseen (työ)"
+ "Lähetä tekstiviesti: %s "
+ "Lähetä tekstiviesti MMS-numeroon"
+ "Soita videopuhelu"
+ "Tyhjennetäänkö usein käytetyt?"
+ "Toiminto tyhjentää Yhteystiedot- ja Puhelin-sovellusten usein käytettyjen kontaktien luettelon. Lisäksi sähköpostisovellukset pakotetaan opettelemaan osoiteasetuksesi uudestaan."
+ "Tyhjennetään usein käytetyt..."
+ "Saatavilla"
+ "Poissa"
+ "Varattu"
+ "Yhteystiedot"
+ "Muu"
+ "Osoitekirja"
+ "Kaikki yhteystiedot"
+ "Minä"
+ "Haetaan..."
+ "Löytyi yli %d yhteystietoa."
+ "Ei yhteystietoja"
+
+ %d löytyi
+ - 1 löytyi
+
+ "Pikayhteys henkilöön %1$s "
+ "(Ei nimeä)"
+ "Soitettu usein"
+ "Usein käytetyt"
+ "Näytä kontakti"
+ "Kaikki kontaktit, joilla on puhelinnumero"
+ "Näytä päivitykset"
+ "Vain puhelin, ei synkronoitu"
+ "Nimi"
+ "Lempinimi"
+ "Nimi"
+ "Etunimi"
+ "Sukunimi"
+ "Nimen etuliite"
+ "Toinen nimi"
+ "Nimen jälkiliite"
+ "Nimen ääntämistapa"
+ "Etunimen ääntämistapa"
+ "Toisen nimen ääntämistapa"
+ "Sukunimen ääntämistapa"
+ "Puhelin"
+ "Sähköposti"
+ "Osoite"
+ "IM"
+ "Organisaatio"
+ "Suhde"
+ "Erikoispäivät"
+ "Tekstiviesti"
+ "Osoite"
+ "Yritys"
+ "Nimi"
+ "Muistiinpanot"
+ "SIP"
+ "Sivusto"
+ "Ryhmät"
+ "Lähetä sähköpostia kotiosoitteeseen"
+ "Lähetä sähköpostia mobiiliosoitteeseen"
+ "Lähetä sähköpostia työosoitteeseen"
+ "Lähetä sähköpostia"
+ "Lähetä sähköpostia osoitteeseen %s "
+ "Lähetä sähköpostia"
+ "Katu"
+ "Postilokero"
+ "Kaupunginosa"
+ "Kaupunki"
+ "Osavaltio/alue"
+ "Postinumero"
+ "Maa"
+ "Näytä kotiosoite"
+ "Näytä työosoite"
+ "Näytä osoite"
+ "Näytä osoite %s "
+ "Keskustele AIM:n avulla"
+ "Keskustele Windows Liven avulla"
+ "Keskustele Yahoon avulla"
+ "Keskustele Skypen avulla"
+ "Keskustele QQ:n avulla"
+ "Keskustele Google Talkin avulla"
+ "Keskustele ICQ:n avulla"
+ "Keskustele Jabberin avulla"
+ "Keskustelu"
+ "poista"
+ "Laajenna tai tiivistä nimikentät"
+ "Kaikki yhteystiedot"
+ "Tähdelliset"
+ "Muokkaa"
+ "Yhteystieto"
+ "Kaikki muut yhteystiedot"
+ "Kaikki yhteystiedot"
+ "Poista synkronointiryhmä"
+ "Lisää synkronointiryhmä"
+ "Lisää ryhmiä…"
+ "Ryhmän %s poistaminen synkronoinnista lopettaa myös ryhmittelemättömien yhteystietojen synkronoinnin."
+ "Tallennetaan näyttövalintoja…"
+ "Valmis"
+ "Peruuta"
+ "Yhteystiedot tilissä %s "
+ "Muokatun näkymän yhteystiedot"
+ "Yksi yhteystieto"
+ "Luo yhteystieto tilissä"
+ "Tuo SIM-kortilta"
+ "Tuo SIM-kortilta ^1 - ^2 "
+ "Tuo SIM-kortilta %1$s "
+ "Tuo .vcf-tiedostosta"
+ "Peruutetaanko kohteen %s tuonti?"
+ "Peruutetaanko kohteen %s vienti?"
+ "vCardin tuonnin/viennin peruutus epäonn."
+ "Tuntematon virhe."
+ "Tiedostoa %s ei voi avata: %s "
+ "Vientiohjelman käynnistys epäonnistui: %s "
+ "Ei vietäviä yhteystietoja."
+ "Olet poistanut käytöstä tarvittavan käyttöoikeuden."
+ "Virhe viennin aikana: %s ."
+ "Tarvittava tiedostonimi on liian pitkä (%s )"
+ "SD-kortilla on liian monta vCard-tiedostoa."
+ "I/O-virhe"
+ "Muisti ei riitä. Tiedosto voi olla liian suuri."
+ "vCardia ei voi jäsentää odottamattomasta syystä."
+ "Muotoa ei tueta."
+ "Annettujen vCard-tiedostojen sisällönkuvaustietojen noutaminen epäonnistui."
+ "Ainakin yhden tiedoston tuominen epäonnistui (%s)."
+ "Kohde %s on viety."
+ "Yhteystiedot on viety."
+ "Kohteen %s vienti peruutettiin."
+ "Viedään yhteystietoja"
+ "Yhteystietosi viedään kohteeseen %s ."
+ "Tietokannan tietojen hakeminen epäonnistui."
+ "Vietäviä yhteystietoja ei ole. Jos puhelimessasi on yhteystietoja, tietojen tarjoaja on saattanut estää niiden viemisen puhelimen ulkopuolelle."
+ "vCard-luonti ei käynnistynyt oikein."
+ "Vieminen epäonnistui"
+ "Yhteystietoja ei viety.\nSyy: %s "
+ "Tuodaan kohdetta %s "
+ "vCard-tietojen lukeminen epäonnistui"
+ "vCard-tietojen lukeminen peruutettiin"
+ "vCard %s on tuotu"
+ "Kohteen %s tuonti peruutettiin"
+ "%s tuodaan pian."
+ "Tiedosto tuodaan pian."
+ "vCard-tuontipyyntö hylättiin. Yritä myöhemmin uudelleen."
+ "%s viedään pian."
+ "Tiedosto viedään pian."
+ "vCard-vientipyyntö hylättiin. Yritä myöhemmin uudelleen."
+ "yhteystieto"
+ "Lisätään vCard-tietojen välimuistiversiot paikalliseen väliaikaistallennustilaan. Tuonti alkaa pian."
+ "vCard-tietojen tuominen epäonnistui."
+ "SD-kortilta ei löydy vCard-tiedostoa."
+ "Yht. saatu (NFC)"
+ "Viedäänkö yhteystietoja?"
+ "Vie välimuistiin"
+ "SD-korttia ei voi lukea. (Syy: %s )"
+ "Tuodaan %s /%s : %s "
+ "Vie .vcf-tiedostoon"
+ "Lajitteluperuste"
+ "Etunimi"
+ "Sukunimi"
+ "Nimen muoto"
+ "Etunimi ensin"
+ "Sukunimi ensin"
+ "Jaa näkyvät yhteystiedot"
+ "Näkyvien yhteystietojen jakaminen epäonnistui."
+ "Tuo/vie yhteystietoja"
+ "Tuo yhteystietoja"
+ "Yhteystieto ei jaettavissa"
+ "Haku"
+ "Näytettävät yhteystiedot"
+ "Näytettävät yhteystiedot"
+ "Määritä oma näkymä"
+ "Etsi yhteystietoja"
+ "Suosikit"
+ "Ei yhteystietoja."
+ "Ei näkyviä yhteystietoja."
+ "Ei suosikkeja."
+ "Ei yhteystietoja ryhmässä %s "
+ "Tyhjennä usein käytetyt"
+ "Valitse SIM-kortti"
+ "Tilit"
+ "Tuo/vie"
+ "lähteestä %1$s "
+ "%1$s lähteestä %2$s "
+ "lopeta hakeminen"
+ "Tyhjennä haku"
+ "Yhteystietojen näyttöasetukset"
+ "Tili"
+ "Käytä kaikille puheluille"
+ "Valitse puhelinoperaattori:"
+ "Soita ja lähetä muistiinpano"
+ "Kirjoita muistiinpano lähetettäväksi puhelun kanssa…"
+ "LÄHETÄ ja SOITA"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-fr-rCA/strings.xml b/ContactsCommon/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..2e48d56
--- /dev/null
+++ b/ContactsCommon/res/values-fr-rCA/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texte copié."
+ "Copier dans le presse-papiers"
+ "Appeler le %s "
+ "Appeler le numéro de téléphone du domicile"
+ "Appeler le numéro de téléphone mobile"
+ "Appeler le numéro de téléphone professionnel"
+ "Appeler le numéro de télécopie professionnel"
+ "Appeler le numéro de télécopie du domicile"
+ "Appeler le téléavertisseur"
+ "Appeler"
+ "Appeler le numéro de rappel"
+ "Appeler le numéro de télphone de la voiture"
+ "Appeler le numéro de téléphone principal de l\'entreprise"
+ "Appeler le numéro de téléphone RNIS"
+ "Appeler le numéro de téléphone principal"
+ "Appeler le numéro de télécopie"
+ "Appeler par signal radio"
+ "Appeler par télex"
+ "Appeler le numéro TTY/TDD (malentendants)"
+ "Appeler le numéro de téléphone mobile professionnel"
+ "Appeler le téléavertisseur professionnel"
+ "Appeler le %s "
+ "Appeler le numéro de téléphone MMS"
+ "Envoyer un SMS au %s "
+ "Envoyer un SMS au numéro de téléphone du domicile"
+ "Envoyer un SMS au numéro de téléphone mobile"
+ "Envoyer un SMS au numéro de téléphone professionnel"
+ "Envoyer un SMS au numéro de télécopie professionnel"
+ "Envoyer un SMS au numéro de télécopie du domicile"
+ "Envoyer un SMS au téléavertisseur"
+ "Envoyer un SMS"
+ "Envoyer un SMS au numéro de rappel"
+ "Envoyer un SMS au numéro de téléphone de la voiture"
+ "Envoyer un SMS au numéro de téléphone principal de l\'entreprise"
+ "Envoyer un SMS au numéro de téléphone RNIS"
+ "Envoyer un SMS au numéro de téléphone principal"
+ "Envoyer un SMS au numéro de télécopie"
+ "Envoyer un SMS par signal radio"
+ "Envoyer un SMS par télex"
+ "Envoyer un SMS au numéro TTY/TDD (malentendants)"
+ "Envoyer un SMS au numéro de téléphone mobile professionnel"
+ "Envoyer un SMS au téléavertisseur professionnel"
+ "Envoyer un SMS au %s "
+ "Envoyer un SMS au numéro de téléph MMS"
+ "Faire un appel vidéo"
+ "Effacer les contacts fréquents?"
+ "Cette opération efface la liste des personnes avec qui vous communiquez le plus souvent dans les applications Contacts et Téléphone, et forcera les applications de courriel à mémoriser de nouveau les adresses que vous utilisez le plus."
+ "Suppression des contacts fréquents…"
+ "Disponible"
+ "Absent"
+ "Occupé(e)"
+ "Contacts"
+ "Autre"
+ "Répertoire"
+ "Tous les contacts"
+ "Moi"
+ "Recherche en cours..."
+ "Plus de %d contacts ont été trouvés."
+ "Aucun contact"
+
+ %d résultat
+ %d résultats
+
+ "Lien rapide vers %1$s "
+ "(Sans nom)"
+ "Appels fréquents"
+ "Contacts fréquents"
+ "Afficher le contact"
+ "Tous les contacts disposant d\'un numéro de téléphone"
+ "Afficher les mises à jour"
+ "Téléphone uniquement, sans synchronisation"
+ "Nom"
+ "Pseudo"
+ "Nom"
+ "Prénom"
+ "Nom de famille"
+ "Préfixe du nom"
+ "Deuxième prénom"
+ "Suffixe du nom"
+ "Nom phonétique"
+ "Prononciation du prénom"
+ "Deuxième prénom phonétique"
+ "Prononciation du nom de famille"
+ "Numéro de téléphone"
+ "Adresse de courriel :"
+ "Adresse"
+ "MI"
+ "Entreprise"
+ "Relation"
+ "Dates spéciales"
+ "Message texte"
+ "Adresse"
+ "Entreprise"
+ "Titre"
+ "Notes"
+ "SIP"
+ "Site Web"
+ "Groupes"
+ "Envoyer un courriel au domicile"
+ "Envoyer un courriel sur le mobile"
+ "Envoyer un courriel au bureau"
+ "Adresse de courriel :"
+ "Envoyer un courriel à %s "
+ "Adresse de courriel :"
+ "Rue"
+ "Case postale"
+ "Quartier"
+ "Ville"
+ "État/province"
+ "Code postal"
+ "Pays"
+ "Afficher l\'adresse personnelle"
+ "Afficher l\'adresse professionnelle"
+ "Afficher l\'adresse"
+ "Afficher l\'adresse %s "
+ "Clavarder via AIM"
+ "Clavarder via Windows Live"
+ "Clavarder via Yahoo!"
+ "Clavarder via Skype"
+ "Clavarder via QQ"
+ "Clavarder via Google Talk"
+ "Clavarder via ICQ"
+ "Clavarder via Jabber"
+ "Clavarder"
+ "supprimer"
+ "Développer ou réduire les champs de nom"
+ "Tous les contacts"
+ "Favoris"
+ "Personnaliser"
+ "Contact"
+ "Tous les autres contacts"
+ "Tous les contacts"
+ "Supprimer le groupe de synchronisation"
+ "Ajouter groupe de synchronisation"
+ "Autres groupes…"
+ "Le retrait du groupe \"%s \" de la synchronisation entraîne également le retrait des contacts n\'appartenant à aucun groupe."
+ "Enregistrement des options d\'affichage en cours…"
+ "OK"
+ "Annuler"
+ "Contacts du compte \"%s \""
+ "Contacts en affichage personnalisé"
+ "Contact"
+ "Créer un contact sous le compte"
+ "Importer depuis la carte SIM"
+ "Importer de la carte SIM ^1 - ^2 "
+ "Importer de la carte SIM %1$s "
+ "Importer d\'un fichier .vcf"
+ "Annuler l\'importation du fichier %s ?"
+ "Annuler l\'exportation du fichier %s ?"
+ "Impossible annuler import./export. vCard"
+ "Erreur inconnue."
+ "Impossible d\'ouvrir le fichier %s pour la raison suivante : %s ."
+ "Impossible de démarrer le programme d\'exportation pour la raison suivante : %s ."
+ "Aucun contact ne peut être exporté."
+ "Vous avez désactivé une autorisation obligatoire."
+ "Une erreur s\'est produite lors de l\'exportation : %s ."
+ "Le nom de fichier requis est trop long (\"%s \")."
+ "La carte SD contient trop de fichiers vCard."
+ "Erreur d\'E/S."
+ "Mémoire insuffisante. Le fichier est peut-être trop volumineux."
+ "Impossible d\'analyser le fichier vCard pour une raison inattendue."
+ "Le format n\'est pas compatible."
+ "Impossible de collecter des métadonnées contenues dans le ou les fichiers vCard."
+ "Impossible d\'importer un ou plusieurs fichiers (%s)."
+ "Exportation du fichier %s terminée"
+ "Les contacts ont été exportés"
+ "Exportation du fichier %s annulée"
+ "Exportation des données des contacts en cours"
+ "Vos données de contact sont en cours d\'exportation vers le fichier %s ."
+ "Impossible d\'obtenir les informations concernant la base de données."
+ "Aucun contact ne peut être exporté. Si des contacts sont enregistrés sur votre téléphone, il est possible qu\'un fournisseur de données n\'autorise pas l\'exportation de contacts vers un autre appareil."
+ "Le système de composition vCard n\'a pas démarré correctement."
+ "Échec exportation"
+ "Les données du contact n\'ont pas été exportées.\nMotif : %s ."
+ "Importation (%s )"
+ "Impossible de lire les données vCard"
+ "Lecture des données vCard annulée"
+ "Le fichier vCard %s a bien été importé"
+ "Importation du fichier %s annulée"
+ "Le fichier %s va bientôt être importé."
+ "Le fichier va bientôt être importé."
+ "La demande d\'importation du fichier vCard a été rejetée. Veuillez réessayer plus tard."
+ "Le fichier %s va bientôt être exporté."
+ "Le fichier sera bientôt exporté."
+ "La demande d\'exportation du fichier vCard a été rejetée. Veuillez réessayer plus tard."
+ "contact"
+ "Mise en cache des fichiers vCard dans l\'espace de stockage temporaire local. L\'importation va bientôt démarrer."
+ "Impossible d\'importer le fichier vCard."
+ "Aucun fichier vCard trouvé sur la carte SD."
+ "Contact reçu via NFC"
+ "Exporter les contacts?"
+ "Mise en cache…"
+ "Impossible d\'analyser la carte SD pour la raison suivante : %s ."
+ "Importation %s sur %s (%s )"
+ "Exporter en format .vcf"
+ "Trier par"
+ "Prénom"
+ "Nom de famille"
+ "Format de nom"
+ "Prénom en premier"
+ "Nom de famille en premier"
+ "Partager les contacts visibles"
+ "Échec du partage des contacts visibles."
+ "Importer/Exporter des contacts"
+ "Téléverser des contacts"
+ "Impossible de partager ce contact."
+ "Rechercher"
+ "Contacts à afficher"
+ "Contacts à afficher"
+ "Définir un affichage personnalisé"
+ "Rechercher des contacts"
+ "Favoris"
+ "Aucun contact"
+ "Aucun contact visible"
+ "Aucun favori"
+ "Aucun contact dans %s "
+ "Effacer les contacts fréquents"
+ "Sélectionner une carte SIM"
+ "Comptes"
+ "Importer/Exporter"
+ "par %1$s "
+ "%1$s par %2$s "
+ "arrêter la recherche"
+ "Effacer les termes de recherche"
+ "Options d\'affichage des contacts"
+ "Compte"
+ "Toujours l\'utiliser pour les appels"
+ "Appeler avec"
+ "Appeler avec une note"
+ "Tapez une note à envoyer avec l\'appel..."
+ "ENVOYER ET APPELER"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-fr/strings.xml b/ContactsCommon/res/values-fr/strings.xml
new file mode 100644
index 0000000..f3aaedd
--- /dev/null
+++ b/ContactsCommon/res/values-fr/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texte copié."
+ "Copier dans le Presse-papiers"
+ "Appeler le %s "
+ "Appeler le numéro de téléphone du domicile"
+ "Appeler le numéro de téléphone mobile"
+ "Appeler le numéro de téléphone professionnel"
+ "Appeler le numéro de télécopie professionnel"
+ "Appeler le numéro de télécopie du domicile"
+ "Appeler le téléavertisseur"
+ "Appeler"
+ "Appeler le numéro de rappel"
+ "Appeler le numéro de téléphone de la voiture"
+ "Appeler le numéro de téléphone principal de l\'entreprise"
+ "Appeler le numéro de téléphone RNIS"
+ "Appeler le numéro de téléphone principal"
+ "Appeler le numéro de télécopie"
+ "Appeler par signal radio"
+ "Appeler par télex"
+ "Appeler le numéro TTY/TDD (malentendants)"
+ "Appeler le numéro de téléphone mobile professionnel"
+ "Appeler le téléavertisseur professionnel"
+ "Appeler le %s "
+ "Appeler le numéro de téléphone MMS"
+ "Envoyer un SMS au %s "
+ "Envoyer un SMS au numéro de téléphone du domicile"
+ "Envoyer un SMS au numéro de téléphone mobile"
+ "Envoyer un SMS au numéro de téléphone professionnel"
+ "Envoyer un SMS au numéro de télécopie professionnel"
+ "Envoyer un SMS au numéro de télécopie du domicile"
+ "Envoyer un SMS au téléavertisseur"
+ "Envoyer un SMS"
+ "Envoyer un SMS au numéro de rappel"
+ "Envoyer un SMS au numéro de téléphone de la voiture"
+ "Envoyer un SMS au numéro de téléphone principal de l\'entreprise"
+ "Envoyer un SMS au numéro de téléphone RNIS"
+ "Envoyer un SMS au numéro de téléphone principal"
+ "Envoyer un SMS au numéro de télécopie"
+ "Envoyer un SMS par signal radio"
+ "Envoyer un SMS par télex"
+ "Envoyer un SMS au numéro TTY/TDD (malentendants)"
+ "Envoyer un SMS au numéro de téléphone mobile professionnel"
+ "Envoyer un SMS au téléavertisseur professionnel"
+ "Envoyer un SMS au %s "
+ "Envoyer un SMS au numéro de téléphone MMS"
+ "Passer un appel vidéo"
+ "Effacer les contacts fréquents ?"
+ "Cette opération efface la liste des personnes que vous contactez le plus souvent dans les applications Contacts et Téléphone, et entraîne une réinitialisation des adresses mémorisées comme celles que vous utilisez le plus fréquemment dans vos applications de messagerie électronique."
+ "Suppression des contacts fréquents…"
+ "Disponible"
+ "Absent"
+ "Occupé"
+ "Contacts"
+ "Autre"
+ "Répertoire"
+ "Tous les contacts"
+ "Moi"
+ "Recherche en cours…"
+ "Plus de %d contacts ont été trouvés."
+ "Aucun contact"
+
+ %d contact trouvé.
+ %d contacts trouvés.
+
+ "Lien rapide vers %1$s "
+ "(Sans nom)"
+ "Appels fréquents"
+ "Contacts fréquents"
+ "Afficher le contact"
+ "Tous les contacts disposant d\'un numéro de téléphone"
+ "Afficher les mises à jour"
+ "Téléphone uniquement, sans synchronisation"
+ "Nom"
+ "Pseudo"
+ "Nom"
+ "Prénom"
+ "Nom"
+ "Préfixe du nom"
+ "Deuxième prénom"
+ "Suffixe du nom"
+ "Nom phonétique"
+ "Prénom phonétique"
+ "2e prénom phonétique"
+ "Nom phonétique"
+ "Numéro de téléphone"
+ "Adresse e-mail"
+ "Adresse"
+ "Chat"
+ "Entreprise"
+ "Type de relation"
+ "Dates importantes"
+ "SMS"
+ "Adresse"
+ "Entreprise"
+ "Titre"
+ "Notes"
+ "Adresse SIP"
+ "Site Web"
+ "Groupes"
+ "Envoyer un e-mail au domicile"
+ "Envoyer un e-mail sur le mobile"
+ "Envoyer un e-mail au bureau"
+ "Envoyer un e-mail"
+ "Envoyer un e-mail à %s "
+ "Envoyer un e-mail"
+ "Rue"
+ "Boîte postale"
+ "Quartier"
+ "Ville"
+ "État"
+ "Code postal"
+ "Pays"
+ "Afficher l\'adresse personnelle"
+ "Afficher l\'adresse professionnelle"
+ "Afficher l\'adresse"
+ "Afficher l\'adresse %s "
+ "Chatter via AIM"
+ "Chatter via Windows Live"
+ "Chatter via Yahoo!"
+ "Chatter via Skype"
+ "Chatter via QQ"
+ "Chatter via Google Talk"
+ "Chatter via ICQ"
+ "Chatter via Jabber"
+ "Chatter"
+ "supprimer"
+ "Développer ou réduire les champs de nom"
+ "Tous les contacts"
+ "Favoris"
+ "Personnaliser"
+ "Contact"
+ "Tous les autres contacts"
+ "Tous les contacts"
+ "Supprimer le groupe de synchronisation"
+ "Ajouter un groupe de synchronisation"
+ "Autres groupes…"
+ "Le retrait du groupe \"%s \" de la synchronisation entraîne également le retrait des contacts n\'appartenant à aucun groupe."
+ "Enregistrement des options d\'affichage en cours…"
+ "OK"
+ "Annuler"
+ "Contacts du compte \"%s \""
+ "Contacts avec affichage perso."
+ "Contact"
+ "Créer un contact sous le compte"
+ "Importer depuis la carte SIM"
+ "Importer depuis la carte SIM ^1 (^2 )"
+ "Importer depuis la carte SIM %1$s "
+ "Importer à partir de fichier VCF"
+ "Annuler l\'importation du fichier %s ?"
+ "Annuler l\'exportation du fichier %s ?"
+ "Impossible annuler import./export. vCard"
+ "Erreur inconnue."
+ "Impossible d\'ouvrir le fichier %s pour la raison suivante : %s ."
+ "Impossible de démarrer le programme d\'exportation pour la raison suivante : %s ."
+ "Aucun contact ne peut être exporté."
+ "Vous avez désactivé une autorisation nécessaire."
+ "Une erreur s\'est produite lors de l\'exportation : %s ."
+ "Le nom de fichier requis est trop long (\"%s \")."
+ "La carte SD contient trop de fichiers vCard."
+ "Erreur d\'E/S."
+ "Mémoire insuffisante. Le fichier est peut-être trop volumineux."
+ "Impossible d\'analyser le fichier vCard pour une raison inattendue."
+ "Le format n\'est pas compatible."
+ "Impossible de collecter des métadonnées contenues dans le ou les fichiers vCard."
+ "Impossible d\'importer un ou plusieurs fichiers (%s)."
+ "Exportation du fichier %s terminée"
+ "Les contacts ont bien été exportés"
+ "Exportation du fichier %s annulée"
+ "Exportation des données des contacts"
+ "Vos données de contact sont en cours d\'exportation vers le fichier %s ."
+ "Impossible d\'obtenir les informations concernant la base de données."
+ "Aucun contact ne peut être exporté. Si des contacts sont enregistrés sur votre téléphone, il est possible qu\'un fournisseur de données n\'autorise pas l\'exportation de contacts vers un autre appareil."
+ "Le système de composition vCard n\'a pas démarré correctement."
+ "Échec exportation"
+ "Les données du contact n\'ont pas été exportées.\nMotif : %s ."
+ "Importation (%s )"
+ "Impossible de lire les données vCard"
+ "Lecture des données vCard annulée"
+ "Le fichier vCard %s a bien été importé"
+ "Importation du fichier %s annulée"
+ "Le fichier %s va bientôt être importé."
+ "Le fichier va bientôt être importé."
+ "La demande d\'importation du fichier vCard a été rejetée. Veuillez réessayer ultérieurement."
+ "Le fichier %s va bientôt être exporté."
+ "Le fichier va bientôt être exporté."
+ "La demande d\'exportation du fichier vCard a été rejetée. Veuillez réessayer ultérieurement."
+ "contact"
+ "Mise en cache des fichiers vCard dans l\'espace de stockage temporaire local. L\'importation va bientôt démarrer."
+ "Impossible d\'importer le fichier vCard."
+ "Aucun fichier vCard trouvé sur la carte SD."
+ "Contact reçu via NFC"
+ "Exporter les contacts ?"
+ "Mise en cache"
+ "Impossible d\'explorer la carte SD pour la raison suivante : %s ."
+ "Importation %s sur %s (%s )"
+ "Exporter dans fichier VCF"
+ "Trier par"
+ "Prénom"
+ "Nom"
+ "Format du nom"
+ "Prénom en premier"
+ "Nom en premier"
+ "Partager les contacts visibles"
+ "Échec du partage des contacts visibles."
+ "Importer/Exporter des contacts"
+ "Importer des contacts"
+ "Impossible de partager ce contact."
+ "Rechercher"
+ "Contacts à afficher"
+ "Contacts à afficher"
+ "Définir affichage perso"
+ "Rechercher des contacts"
+ "Favoris"
+ "Aucun contact"
+ "Aucun contact visible"
+ "Aucun favori"
+ "Aucun contact dans %s "
+ "Effacer les contacts fréquents"
+ "Sélectionner une carte SIM"
+ "Comptes"
+ "Importer/Exporter"
+ "via %1$s "
+ "%1$s via %2$s "
+ "arrêter la recherche"
+ "Effacer la recherche"
+ "Options d\'affichage des contacts"
+ "Compte"
+ "Toujours l\'utiliser pour les appels"
+ "Appeler avec"
+ "Appeler avec une note"
+ "Saisissez une note pour accompagner l\'appel..."
+ "ENVOYER L\'OBJET ET APPELER"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-gl-rES/strings.xml b/ContactsCommon/res/values-gl-rES/strings.xml
new file mode 100644
index 0000000..b99073b
--- /dev/null
+++ b/ContactsCommon/res/values-gl-rES/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texto copiado"
+ "Copiar no portapapeis"
+ "Chamar ao %s "
+ "Chama á casa"
+ "Chamar ao móbil"
+ "Chamar ao traballo"
+ "Chamar ao número de fax do traballo"
+ "Chamar ao número de fax da casa"
+ "Chamar ao busca"
+ "Chamar"
+ "Chamar ao número de devolución de chamada"
+ "Chamar ao coche"
+ "Chamar ao número principal da empresa"
+ "Chamar ao número de RDSI"
+ "Chamar ao número principal"
+ "Chamar ao fax"
+ "Chamar á radio"
+ "Chamar ao télex"
+ "Chamar ao número de TTY/TDD"
+ "Chamar ao móbil do traballo"
+ "Chamar ao busca do traballo"
+ "Chamar a %s "
+ "Chamar ao número de teléfono de MMS"
+ "Enviar unha mensaxe a %s "
+ "Enviar unha mensaxe á casa"
+ "Enviar unha mensaxe ao teléfono móbil"
+ "Enviar unha mensaxe ao traballo"
+ "Enviar unha mensaxe ao fax do traballo"
+ "Enviar unha mensaxe ao fax de casa"
+ "Enviar unha mensaxe ao busca"
+ "Enviar unha mensaxe"
+ "Enviar unha mensaxe ao número de devolución da chamada"
+ "Enviar unha mensaxe ao coche"
+ "Enviar unha mensaxe ao teléfono principal da empresa"
+ "Enviar unha mensaxe ao teléfono de RDSI"
+ "Enviar unha mensaxe ao teléfono principal"
+ "Enviar unha mensaxe ao fax"
+ "Enviar unha mensaxe á radio"
+ "Enviar unha mensaxe ao télex"
+ "Enviar unha mensaxe ao TTY/TDD"
+ "Enviar unha mensaxe ao móbil do traballo"
+ "Enviar unha mensaxe ao busca do traballo"
+ "Enviar unha mensaxe a %s "
+ "Enviar unha mensaxe ao teléfono da MMS"
+ "Realiza unha videochamada"
+ "Borrar contactados con frecuencia?"
+ "Borra a lista de persoas coas que contactaches frecuentemente das aplicacións Contactos e Teléfono, e obriga ás aplicacións de correo electrónico a que memoricen as túas preferencias de enderezos desde cero."
+ "Borrando contactados frecuencia..."
+ "Dispoñible"
+ "Ausente"
+ "Ocupado"
+ "Contactos"
+ "Outro"
+ "Directorio"
+ "Todos os contactos"
+ "Eu"
+ "Buscando..."
+ "Encontráronse máis de %d contactos."
+ "Ningún contacto"
+
+ %d contactos encontrados
+ - Un contacto encontrado
+
+ "Contacto rápido para %1$s "
+ "(Sen nome)"
+ "Chamados frecuentemente"
+ "Contactados frecuentemente"
+ "Ver contacto"
+ "Todos os contactos con números de teléfono"
+ "Ver actualizacións"
+ "Só no teléfono, non sincronizado"
+ "Nome"
+ "Alcume"
+ "Nome"
+ "Nome"
+ "Apelidos"
+ "Tratamento do nome"
+ "Segundo nome"
+ "Información profesional do nome"
+ "Nome fonético"
+ "Nome fonético"
+ "Segundo nome fonético"
+ "Apelido fonético"
+ "Teléfono"
+ "Correo electrónico"
+ "Enderezo"
+ "MI"
+ "Organización"
+ "Relación"
+ "Datas especiais"
+ "Mensaxe de texto"
+ "Enderezo"
+ "Empresa"
+ "Título"
+ "Notas"
+ "SIP"
+ "Sitio web"
+ "Grupos"
+ "Enviar correo electrónico á casa"
+ "Enviar correo electrónico ao móbil"
+ "Enviar correo electrónico ao traballo"
+ "Enviar correo electrónico"
+ "Enviar correo electrónico a %s "
+ "Correo electrónico"
+ "Rúa"
+ "Apartado de correos"
+ "Barrio"
+ "Cidade"
+ "Estado"
+ "Código postal"
+ "País"
+ "Ver enderezo da casa"
+ "Ver enderezo do traballo"
+ "Ver enderezo"
+ "Ver enderezo %s "
+ "Chatear con AIM"
+ "Chatear con Windows Live"
+ "Chatear con Yahoo"
+ "Chatear con Skype"
+ "Chatear con QQ"
+ "Chatear con Google Talk"
+ "Chatear con ICQ"
+ "Chatear con Jabber"
+ "Chatear"
+ "eliminar"
+ "Amplía ou contrae os campos do nome"
+ "Todos os contactos"
+ "Marcados con asterisco"
+ "Personalizar"
+ "Contacto"
+ "Todos os demais contactos"
+ "Todos os contactos"
+ "Eliminar grupo de sincronización"
+ "Engadir grupo de sincronización"
+ "Máis grupos..."
+ "Se eliminas \"%s \" da sincronización, tamén eliminarás os contactos non agrupados da sincronización."
+ "Gardando opcións de visualización..."
+ "Feito"
+ "Cancelar"
+ "Contactos en %s "
+ "Contactos na vista personalizada"
+ "Un só contacto"
+ "Crear contacto na conta"
+ "Importar da tarxeta SIM"
+ "Importar da SIM ^1 - ^2 "
+ "Importar da SIM %1$s "
+ "Importar de ficheiro .vcf"
+ "Cancelar importación de %s ?"
+ "Cancelar exportación de %s ?"
+ "Imposible cancelar import./export. vCard"
+ "Erro descoñecido"
+ "Non se puido abrir \"%s \": %s ."
+ "Non se puido iniciar o exportador: \"%s \"."
+ "Non hai ningún contacto exportable."
+ "Desactivaches un permiso necesario."
+ "Produciuse un erro durante a exportación: \"%s \"."
+ "O nome do ficheiro necesario é demasiado longo (\"%s \")."
+ "Hai demasiados ficheiros vCard na tarxeta SD."
+ "Erro de E/S"
+ "Non hai memoria suficiente. É posible que o ficheiro sexa demasiado grande."
+ "Non se puido analizar o vCard debido a un motivo inesperado."
+ "O formato non é compatible."
+ "Non se puido recoller a información meta de determinados ficheiros vCard."
+ "Non se puideron importar un ou máis ficheiros (%s)."
+ "Finalizouse a exportación de %s ."
+ "Finalizou a exportación dos contactos."
+ "Cancelouse a exportación de %s ."
+ "Exportando datos de contacto"
+ "Estanse exportando os teus datos de contacto a: %s ."
+ "Non se puido obter información da base de datos."
+ "Non hai ningún contacto exportable. Se tes contactos no teu teléfono, é posible que algúns provedores de datos non permitan a exportación dos contactos desde o teléfono."
+ "O redactor de vCard non se iniciou correctamente."
+ "Imposible exportar"
+ "Non se exportaron os datos dos contactos.\n Motivo: \"%s \""
+ "Importando %s "
+ "Non se puideron ler os datos de vCard"
+ "Lectura dos datos de vCard cancelada"
+ "Acabouse de importar o vCard %s "
+ "Importación de %s cancelada"
+ "%s importarase en breve."
+ "O ficheiro importarase en breve."
+ "Rexeitouse a solicitude de importación de vCard. Téntao de novo máis tarde."
+ "%s exportarase en breve."
+ "O ficheiro exportarase en breve."
+ "Rexeitouse a solicitude de exportación do vCard. Téntao de novo máis tarde."
+ "contacto"
+ "Almacenando vCard na memoria caché do almacenamento temporal local. A importación real iniciarase en breve."
+ "Non se puido importar o vCard."
+ "Non se encontrou ningún ficheiro vCard na tarxeta SD."
+ "Contacto por NFC"
+ "Exportar contactos?"
+ "Almacenando na caché"
+ "Non se puido buscar a tarxeta SD. (Motivo: \"%s \")"
+ "Importando %s /%s : %s "
+ "Exportar a ficheiro .vcf"
+ "Ordenar por"
+ "Nome"
+ "Apelidos"
+ "Formato do nome"
+ "Primeiro o nome"
+ "Primeiro os apelidos"
+ "Compartir contactos visibles"
+ "Produciuse un erro ao compartir os contactos visibles."
+ "Importar/exportar contactos"
+ "Importar contactos"
+ "Non se pode compartir este contacto."
+ "Buscar"
+ "Contactos para mostrar"
+ "Contactos para mostrar"
+ "Definir vista personalizada"
+ "Buscar contactos"
+ "Favoritos"
+ "Ningún contacto"
+ "Ningún contacto visible"
+ "Ningún favorito"
+ "Ningún contacto en %s "
+ "Borrar frecuentes"
+ "Seleccionar tarxeta SIM"
+ "Contas"
+ "Importar/exportar"
+ "a través de %1$s "
+ "%1$s a través de %2$s "
+ "detén a busca"
+ "Borrar busca"
+ "Opcións de visualización de contactos"
+ "Conta"
+ "Usar sempre para as chamadas"
+ "Chamar con"
+ "Chamar cunha nota"
+ "Escribe unha nota para enviala coa chamada…"
+ "ENVIAR e CHAMAR"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-gu-rIN/strings.xml b/ContactsCommon/res/values-gu-rIN/strings.xml
new file mode 100644
index 0000000..b7bfba1
--- /dev/null
+++ b/ContactsCommon/res/values-gu-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "ટેક્સ્ટ કૉપિ કર્યો"
+ "ક્લિપબોર્ડ પર કૉપિ કરો"
+ "%s ને કૉલ કરો"
+ "ઘરે કૉલ કરો"
+ "મોબાઇલ પર કૉલ કરો"
+ "કાર્યાલય પર કૉલ કરો"
+ "કાર્યાલયના ફૅક્સ પર કૉલ કરો"
+ "ઘરના ફૅક્સ પર કૉલ કરો"
+ "પેજર પર કૉલ કરો"
+ "કૉલ કરો"
+ "કૉલબેક પર કૉલ કરો"
+ "કાર પર કૉલ કરો"
+ "કંપની મુખ્ય પર કૉલ કરો"
+ "ISDN પર કૉલ કરો"
+ "મુખ્ય પર કૉલ કરો"
+ "ફૅક્સ પર કૉલ કરો"
+ "રેડિઓ પર કૉલ કરો"
+ "ટેલેક્સ પર કૉલ કરો"
+ "TTY/TDD પર કૉલ કરો"
+ "કાર્યાલયના મોબાઇલ પર કૉલ કરો"
+ "કાર્યાલયના પેજર પર કૉલ કરો"
+ "%s ને કૉલ કરો"
+ "MMS પર કૉલ કરો"
+ "%s ને ટેક્સ્ટ કરો"
+ "ઘરે ટેક્સ્ટ કરો"
+ "મોબાઇલ પર ટેક્સ્ટ કરો"
+ "કાર્યાલય પર ટેક્સ્ટ કરો"
+ "કાર્યાલયના ફૅક્સ પર ટેક્સ્ટ કરો"
+ "ઘરના ફૅક્સ પર ટેક્સ્ટ કરો"
+ "પેજર પર ટેક્સ્ટ કરો"
+ "ટેક્સ્ટ કરો"
+ "કૉલબેક પર ટેક્સ્ટ કરો"
+ "કાર પર ટેક્સ્ટ કરો"
+ "કંપની મુખ્ય પર ટેક્સ્ટ કરો"
+ "ISDN પર ટેક્સ્ટ કરો"
+ "મુખ્ય પર ટેક્સ્ટ કરો"
+ "ફૅક્સ પર ટેક્સ્ટ કરો"
+ "રેડિઓ પર ટેક્સ્ટ કરો"
+ "ટેલેક્સ પર ટેક્સ્ટ કરો"
+ "TTY/TDD પર ટેક્સ્ટ કરો"
+ "કાર્યાલયના મોબાઇલ પર ટેક્સ્ટ કરો"
+ "કાર્યાલયના પેજર પર ટેક્સ્ટ કરો"
+ "%s ને ટેક્સ્ટ કરો"
+ "MMS પર ટેક્સ્ટ કરો"
+ "વિડિઓ કૉલ કરો"
+ "વારંવાર સંપર્ક કરેલા સાફ કરીએ?"
+ "તમે સંપર્કો અને ફોન એપ્લિકેશન્સમાં વારંવાર સંપર્ક કરેલ સૂચિને સાફ કરશો અને ઇમેઇલ એપ્લિકેશન્સને તમારી સંબોધન પસંદગીઓને શરૂઆતથી જાણવા માટે ફરજ પાડશો."
+ "વારંવાર સંપર્ક કરેલા સાફ કરે છે…"
+ "ઉપલબ્ધ"
+ "દૂર"
+ "વ્યસ્ત"
+ "સંપર્કો"
+ "અન્ય"
+ "નિર્દેશિકા"
+ "તમામ સંપર્કો"
+ "મારા"
+ "શોધી રહ્યું છે..."
+ "%d કરતાં વધુ મળ્યાં."
+ "સંપર્કો નથી"
+
+ %d મળ્યાં
+ %d મળ્યાં
+
+ "%1$s માટે ઝડપી સંપર્ક"
+ "(નામ નથી)"
+ "વારંવાર કૉલ કરેલા"
+ "વારંવાર સંપર્ક કરેલા"
+ "સંપર્ક જુઓ"
+ "ફોન નંબર્સ સાથેના તમામ સંપર્કો"
+ "અપડેટ્સ જુઓ"
+ "ફક્ત ફોન, અસમન્વયિત"
+ "નામ"
+ "ઉપનામ"
+ "નામ"
+ "પ્રથમ નામ"
+ "છેલ્લું નામ"
+ "નામ ઉપસર્ગ"
+ "મધ્ય નામ"
+ "નામ પ્રત્યય"
+ "ધ્વન્યાત્મક નામ"
+ "ધ્વન્યાત્મક પ્રથમ નામ"
+ "ધ્વન્યાત્મક મધ્ય નામ"
+ "ધ્વન્યાત્મક છેલ્લું નામ"
+ "ફોન"
+ "ઇમેઇલ"
+ "સરનામું"
+ "IM"
+ "સંગઠન"
+ "સંબંધ"
+ "વિશિષ્ટ તારીખો"
+ "ટેક્સ્ટ સંદેશ"
+ "સરનામું"
+ "કંપની"
+ "શીર્ષક"
+ "નોંધો"
+ "SIP"
+ "વેબસાઇટ"
+ "જૂથો"
+ "ઘરે ઇમેઇલ કરો"
+ "મોબાઇલ પર ઇમેઇલ કરો"
+ "કાર્યાલય પર ઇમેઇલ કરો"
+ "ઇમેઇલ"
+ "%s ને ઇમેઇલ કરો"
+ "ઇમેઇલ"
+ "શેરી"
+ "પોસ્ટ બોક્સ"
+ "પડોશ"
+ "શહેર"
+ "રાજ્ય"
+ "પિન કોડ"
+ "દેશ"
+ "ઘરનું સરનામું જુઓ"
+ "કાર્યાલયનું સરનામું જુઓ"
+ "સરનામું જુઓ"
+ "%s સરનામું જુઓ"
+ "AIM નો ઉપયોગ કરીને ચેટ કરો"
+ "Windows Live નો ઉપયોગ કરીને ચેટ કરો"
+ "Yahoo નો ઉપયોગ કરીને ચેટ કરો"
+ "Skype નો ઉપયોગ કરીને ચેટ કરો"
+ "QQ નો ઉપયોગ કરીને ચેટ કરો"
+ "Google Talk નો ઉપયોગ કરીને ચેટ કરો"
+ "ICQ નો ઉપયોગ કરીને ચેટ કરો"
+ "Jabber નો ઉપયોગ કરીને ચેટ કરો"
+ "ચેટ"
+ "કાઢી નાખો"
+ "નામ ફીલ્ડ્સ વિસ્તૃત કરો અથવા સંકુચિત કરો"
+ "તમામ સંપર્કો"
+ "તારાંકિત"
+ "કસ્ટમાઇઝ કરો"
+ "સંપર્ક"
+ "તમામ અન્ય સંપર્કો"
+ "તમામ સંપર્કો"
+ "સમન્વયન જૂથ દૂર કરો"
+ "સમન્વયન જૂથ ઉમેરો"
+ "વધુ જૂથો…"
+ "સમન્વયનમાંથી \"%s \" ને દૂર કરવું, સમન્વયનમાંથી કોઈપણ વણજૂથબદ્ધ સંપર્કોને પણ દૂર કરશે."
+ "પ્રદર્શન વિકલ્પો સાચવી રહ્યું છે…"
+ "થઈ ગયું"
+ "રદ કરો"
+ "%s માં સંપર્કો"
+ "કસ્ટમ દૃશ્યમાં સંપર્કો"
+ "એકલ સંપર્ક"
+ "એકાઉન્ટ અંતર્ગત સંપર્ક બનાવો"
+ "SIM કાર્ડમાંથી આયાત કરો"
+ "SIM ^1 - ^2 માંથી આયાત કરો"
+ "SIM %1$s માંથી આયાત કરો"
+ ".vcf ફાઇલમાંથી આયાત કરો"
+ "%s ના આયાતને રદ કરીએ?"
+ "%s ના નિકાસને રદ કરીએ?"
+ "vCard આયાત/નિકાસને રદ કરી શક્યાં નહીં"
+ "અજાણી ભૂલ."
+ "\"%s \" ખોલી શકાઈ નથી: %s ."
+ "નિકાસકર્તા શરૂ કરી શક્યાં નહીં: \"%s \"."
+ "કોઈ નિકાસયોગ્ય સંપર્ક નથી."
+ "તમે આવશ્યક પરવાનગી અક્ષમ કરી છે."
+ "નિકાસ દરમિયાન ભૂલ આવી: \"%s \"."
+ "જરૂરી ફાઇલનું નામ ખૂબ લાંબું છે (\"%s \")."
+ "SD કાર્ડ પર ઘણી બધી vCard ફાઇલો છે."
+ "I/O ભૂલ"
+ "પર્યાપ્ત મેમરી નથી. આ ફાઇલ ખૂબ મોટી હોઈ શકે છે."
+ "અનપેક્ષિત કારણસર vCard નું વિશ્લેષણ કરી શકાયું નથી."
+ "ફોર્મેટ સમર્થિત નથી."
+ "આપેલ vCard ફાઇલ(લો)ની મેટા માહિતી ભેગી કરી શકાઈ નથી."
+ "એક અથવા વધુ ફાઇલો આયાત કરી શકાઈ નથી (%s)."
+ "%s ને નિકાસ કરવું સમાપ્ત થયું."
+ "સંપર્કોને નિકાસ કરવાનું સમાપ્ત થયું."
+ "%s ને નિકાસ કરવું રદ કર્યું."
+ "સંપર્ક ડેટા નિકાસ કરી રહ્યાં છે"
+ "તમારો સંપર્ક ડેટા આ પર નિકાસ કરવામાં આવશે: %s ."
+ "ડેટાબેસ જાણકારી મેળવી શકાઈ નથી."
+ "કોઈ નિકાસયોગ્ય સંપર્કો નથી. જો તમારી પાસે તમારા ફોન પર કોઈ સંપર્કો નથી, તો કેટલાક ડેટા પ્રદાતા ફોન પરથી સંપર્કોને નિકાસ કરવાની મંજૂરી આપી શકશે નહીં."
+ "vCard કમ્પોઝર ઠીકથી પ્રારંભ થયું નથી."
+ "નિકાસ કરી શક્યાં નથી"
+ "સંપર્ક ડેટા નિકાસ કર્યો નહોતો.\nકારણ: \"%s \""
+ "%s ને આયાત કરી રહ્યાં છે"
+ "vCard ડેટા વાંચી શકાયો નથી"
+ "vCard ડેટા વાંચવું રદ કર્યું"
+ "vCard %s ને આયાત કરવું સમાપ્ત થયું"
+ "%s ને આયાત કરવું રદ કર્યું"
+ "%s ને ટૂંક સમયમાં આયાત કરવામાં આવશે."
+ "ફાઇલ ટૂંક સમયમાં આયાત કરવામાં આવશે."
+ "vCard આયાતની વિનંતી નકારી હતી. પછીથી ફરી પ્રયાસ કરો."
+ "%s ને ટૂંક સમયમાં નિકાસ કરવામાં આવશે."
+ "ફાઇલને ટૂંક સમયમાં નિકાસ કરવામાં આવશે."
+ "vCard નિકાસની વિનંતી નકારી હતી. પછીથી ફરી પ્રયાસ કરો."
+ "સંપર્ક"
+ "સ્થાનિક અસ્થાયી સ્ટોરેજ પર vCard કેશ કરી રહ્યાં છે. વાસ્તવિક આયાત જલ્દી જ શરૂ થશે."
+ "vCard આયાત કરી શકાયો નથી."
+ "SD કાર્ડમાં કોઈ vCard ફાઇલ મળી નથી."
+ "NFC પર સંપર્ક પ્રાપ્ત"
+ "સંપર્કો નિકાસ કરીએ?"
+ "કેશ કરી રહ્યાં છે"
+ "SD કાર્ડ સ્કેન કરી શકાયું નથી. (કારણ: \"%s \")"
+ "%s /%s આયાત કરે છે: %s "
+ ".vcf ફાઇલ પર નિકાસ કરો"
+ "આ પ્રમાણે સૉર્ટ કરો"
+ "પ્રથમ નામ"
+ "છેલ્લું નામ"
+ "નામ ફોર્મેટ"
+ "પ્રથમ નામ પહેલા"
+ "છેલ્લું નામ પહેલા"
+ "દૃશ્યક્ષમ સંપર્કોને શેર કરો"
+ "દૃશ્યક્ષમ સંપર્કો શેર કરવામાં નિષ્ફળ થયાં."
+ "સંપર્કો આયાત/નિકાસ કરો"
+ "સંપર્કો આયાત કરો"
+ "આ સંપર્ક શેર કરી શકાતો નથી."
+ "શોધો"
+ "પ્રદર્શિત કરવાના સંપર્કો"
+ "પ્રદર્શિત કરવાના સંપર્કો"
+ "કસ્ટમ દૃશ્ય વ્યાખ્યાયિત કરો"
+ "સંપર્કો શોધો"
+ "મનપસંદ"
+ "કોઈ સંપર્કો નથી."
+ "કોઈ દૃશ્યક્ષમ સંપર્કો નથી."
+ "કોઈ મનપસંદ નથી."
+ "%s માં કોઈ સંપર્કો નથી"
+ "વારંવારના સાફ કરો"
+ "SIM કાર્ડ પસંદ કરો"
+ "એકાઉન્ટ્સ"
+ "આયાત/નિકાસ કરો"
+ "%1$s મારફતે"
+ "%2$s મારફતે %1$s ના રોજ"
+ "શોધવાનું રોકો"
+ "શોધ સાફ કરો"
+ "સંપર્ક પ્રદર્શન વિકલ્પો"
+ "એકાઉન્ટ"
+ "કૉલ્સ માટે આનો ઉપયોગ હંમેશાં કરો"
+ "આની સાથે કૉલ કરો"
+ "નોંધ સાથે કૉલ કરો"
+ "કૉલ સાથે મોકલવા માટે એક નોંધ લખો ..."
+ "મોકલો અને કૉલ કરો"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-hi/strings.xml b/ContactsCommon/res/values-hi/strings.xml
new file mode 100644
index 0000000..20ca0b9
--- /dev/null
+++ b/ContactsCommon/res/values-hi/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "लेख की प्रतिलिपि बनाई गई"
+ "क्लिपबोर्ड पर कॉपी बनाएं"
+ "%s पर कॉल करें"
+ "घर के फ़ोन नंबर पर कॉल करें"
+ "मोबाइल पर कॉल करें"
+ "कार्यालय के फ़ोन नंबर पर कॉल करें"
+ "कार्यालय के फ़ैक्स पर कॉल करें"
+ "घर के फ़ैक्स पर कॉल करें"
+ "पेजर पर कॉल करें"
+ "कॉल करें"
+ "कॉलबैक नंबर पर कॉल करें"
+ "कार फ़ोन पर कॉल करें"
+ "कंपनी के मुख्य नंबर पर कॉल करें"
+ "ISDN पर कॉल करें"
+ "मुख्य फ़ोन नंबर पर कॉल करें"
+ "फ़ैक्स पर कॉल करें"
+ "रेडियो फ़ोन नंबर पर कॉल करें"
+ "टेलेक्स पर कॉल करें"
+ "TTY/TDD पर कॉल करें"
+ "कार्यालय के मोबाइल पर कॉल करें"
+ "कार्यालय के पेजर पर कॉल करें"
+ "%s पर कॉल करें"
+ "MMS पर कॉल करें"
+ "%s पर लेख संदेश भेजें"
+ "घर के फ़ोन नंबर पर लेख संदेश भेजें"
+ "मोबाइल पर लेख संदेश भेजें"
+ "कार्यालय के फ़ोन नंबर पर लेख संदेश भेजें"
+ "कार्यालय के फ़ैक्स पर लेख संदेश भेजें"
+ "घर के फ़ैक्स पर लेख संदेश भेजें"
+ "पेजर पर लेख संदेश भेजें"
+ "लेख संदेश भेजें"
+ "कॉलबैक फ़ोन नंबर पर लेख संदेश भेजें"
+ "कार फ़ोन नंबर पर लेख संदेश भेजें"
+ "कंपनी के मुख्य फ़ोन नंबर पर लेख संदेश भेजें"
+ "ISDN पर लेख संदेश भेजें"
+ "मुख्य फ़ोन नंबर पर लेख संदेश भेजें"
+ "फ़ैक्स पर लेख संदेश भेजें"
+ "रेडियो फ़ोन नंबर पर लेख संदेश भेजें"
+ "टेलेक्स पर लेख संदेश भेजें"
+ "TTY/TDD पर लेख संदेश भेजें"
+ "कार्यालय के मोबाइल पर लेख संदेश भेजें"
+ "कार्यालय के पेजर पर लेख संदेश भेजें"
+ "%s पर लेख संदेश भेजें"
+ "MMS पर लेख संदेश भेजें"
+ "वीडियो कॉल करें"
+ "अक्सर किए जाने वाले संपर्क साफ करें?"
+ "आपको संपर्क और फ़ोन ऐप्स से अक्सर संपर्क किए जाने वाली सूची साफ़ करनी होगी, और अपने ईमेल ऐप्स को आपकी पता प्राथमिकताओं को प्रारंभ से जानने के लिए बाध्य करना होगा."
+ "अक्सर किए जाने वाले संपर्क साफ कर रहा है…"
+ "उपलब्ध"
+ "दूर"
+ "व्यस्त"
+ "संपर्क"
+ "अन्य"
+ "निर्देशिका"
+ "सभी संपर्क"
+ "मुझे"
+ "खोज रहा है…"
+ "%d से अधिक मिले."
+ "कोई संपर्क नहीं"
+
+ %d मिले
+ %d मिले
+
+ "%1$s के लिए त्वरित संपर्क"
+ "(कोई नाम नहीं)"
+ "बार-बार कॉल किए गए"
+ "बार-बार संपर्क किए गए"
+ "संपर्क देखें"
+ "फ़ोन नंबरों वाले सभी संपर्क"
+ "नई जानकारी देखें"
+ "केवल-फ़ोन पर, असमन्वयित"
+ "नाम"
+ "प्रचलित नाम"
+ "नाम"
+ "नाम"
+ "उपनाम"
+ "नाम का प्रारंभिक भाग"
+ "मध्य नाम"
+ "नाम का अंतिम भाग"
+ "फ़ोनेटिक नाम"
+ "फ़ोनेटिक नाम"
+ "फ़ोनेटिक मध्य नाम"
+ "फ़ोनेटिक उपनाम"
+ "फ़ोन"
+ "ईमेल"
+ "पता"
+ "IM"
+ "संगठन"
+ "संबंध"
+ "विशेष दिनांक"
+ "लेख संदेश"
+ "पता"
+ "कंपनी"
+ "शीर्षक"
+ "नोट"
+ "SIP"
+ "वेबसाइट"
+ "समूह"
+ "घर के ईमेल पते पर ईमेल करें"
+ "मोबाइल ईमेल पते पर ईमेल करें"
+ "कार्यालय के ईमेल पते पर ईमेल करें"
+ "ईमेल करें"
+ "%s पर ईमेल करें"
+ "ईमेल करें"
+ "मार्ग का नाम"
+ "पीओ बॉक्स"
+ "पड़ोस"
+ "शहर"
+ "राज्य"
+ "पिन कोड"
+ "देश"
+ "घर का पता देखें"
+ "कार्यालय का पता देखें"
+ "पता देखें"
+ "%s पता देखें"
+ "AIM का उपयोग करके बातचीत करें"
+ "Windows Live का उपयोग करके बातचीत करें"
+ "Yahoo का उपयोग करके बातचीत करें"
+ "Skype का उपयोग करके बातचीत करें"
+ "QQ का उपयोग करके बातचीत करें"
+ "Google टॉक का उपयोग करके बातचीत करें"
+ "ICQ का उपयोग करके बातचीत करें"
+ "Jabber का उपयोग करके बातचीत करें"
+ "बातचीत करें"
+ "हटाएं"
+ "नाम फ़ील्ड विस्तृत या संक्षिप्त करें"
+ "सभी संपर्क"
+ "तारांकित"
+ "कस्टमाइज़ करें"
+ "संपर्क"
+ "अन्य सभी संपर्क"
+ "सभी संपर्क"
+ "समन्वयन समूह निकालें"
+ "समन्वयन समूह जोड़ें"
+ "अधिक समूह…"
+ "\"%s \" को समन्वयन से निकालने पर समन्वयन से सभी असमूहीकृत संपर्क भी निकल जाएंगे."
+ "प्रदर्शन विकल्प सहेज रहा है…"
+ "पूर्ण"
+ "अभी नहीं"
+ "%s के संपर्क"
+ "कस्टम दृश्य में संपर्क"
+ "एकल संपर्क"
+ "खाते के अंतर्गत संपर्क बनाएं"
+ "सिम कार्ड से आयात करें"
+ "^1 - ^2 सिम से आयात करें"
+ "%1$s सिम से आयात करें"
+ ".vcf फ़ाइल से आयात करें"
+ "%s का आयात रहने दें?"
+ "%s का निर्यात रहने दें?"
+ "vCard आयात/निर्यात रद्द नहीं हो सका"
+ "अज्ञात त्रुटि."
+ "\"%s \" नहीं खोली जा सकी: %s ."
+ "निर्यातकर्ता प्रारंभ नहीं किया जा सका: \"%s \"."
+ "कोई भी निर्यात-योग्य संपर्क नहीं है."
+ "आपने एक आवश्यक अनुमति को अक्षम कर दिया है."
+ "निर्यात करते समय कोई त्रुटि हुई: \"%s \"."
+ "आवश्यक फ़ाइल नाम बहुत बड़ा है (\"%s \")."
+ "SD कार्ड पर बहुत अधिक VCard फ़ाइलें हैं."
+ "I/O त्रुटि"
+ "स्मृति पर्याप्त नहीं है. हो सकता है फ़ाइल बहुत बड़ी हो."
+ "किसी अप्रत्याशित कारण से vCard पार्स नहीं किया जा सका."
+ "प्रारूप समर्थित नहीं है."
+ "दी गई vCard फ़ाइल (फ़ाइलों) की मेटा जानकारी एकत्र नहीं की जा सकी."
+ "एक या अधिक फ़ाइलें आयात नहीं की जा सकीं (%s)."
+ "%s का निर्यात पूरा हो गया."
+ "संपर्कों का निर्यात किया जाना समाप्त हो गया."
+ "%s को निर्यात करना रद्द कर दिया गया."
+ "संपर्क डेटा निर्यात हो रहा है"
+ "आपका संपर्क डेटा इस पर निर्यात किया जा रहा है: %s ."
+ "डेटाबेस जानकारी नहीं मिल सकी."
+ "कोई भी निर्यात-योग्य संपर्क नहीं है. यदि आपके पास अपने फ़ोन पर संपर्क हों, तो हो सकता है कि कुछ डेटा प्रदाता संपर्कों को फ़ोन से निर्यात न करने दें."
+ "vCard कंपोज़र ठीक से प्रारंभ नहीं हुआ."
+ "निर्यात नहीं कर सका"
+ "संपर्क डेटा निर्यात नहीं किया गया था.\nकारण: \"%s \""
+ "%s आयात कर रहा है"
+ "vCard डेटा नहीं पढ़ा जा सका"
+ "vCard डेटा को पढ़ना रद्द कर दिया गया"
+ "vCard %s आयात करना पूर्ण"
+ "%s को आयात करना रद्द कर दिया गया"
+ "%s को जल्दी ही आयात किया जाएगा."
+ "फ़ाइल शीघ्र ही आयात की जाएगी."
+ "vCard आयात अनुरोध अस्वीकार हो गया था. बाद में पुन: प्रयास करें."
+ "%s को जल्दी ही निर्यात किया जाएगा."
+ "फ़ाइल शीघ्र ही निर्यात की जाएगी."
+ "vCard निर्यात अनुरोध अस्वीकार हो गया था. बाद में पुन: प्रयास करें."
+ "संपर्क"
+ "vCard को स्थानीय अस्थायी मेमोरी में संचित कर रहा है. वास्तविक आयात जल्दी ही प्रारंभ होगा."
+ "vCard आयात नहीं कर सका."
+ "SD कार्ड पर कोई vCard फ़ाइल नहीं मिली."
+ "NFC पर प्राप्त संपर्क"
+ "संपर्कों को निर्यात करें?"
+ "संचय कर रहा है"
+ "SD कार्ड स्कैन नहीं किया जा सका. (कारण: \"%s \")"
+ "%s /%s आयात कर रहा है: %s "
+ ".vcf फाइल में निर्यात करें"
+ "इससे क्रमित करें"
+ "नाम"
+ "उपनाम"
+ "नाम प्रारूप"
+ "नाम पहले"
+ "उपनाम पहले"
+ "दिखाई देने वाले संपर्क साझा करें"
+ "दृश्यमान संपर्क साझा करने में विफल रहा."
+ "संपर्कों को आयात/निर्यात करें"
+ "संपर्कों को आयात करें"
+ "यह संपर्क साझा नहीं किया जा सकता."
+ "खोजें"
+ "दिखाने के लिए संपर्क"
+ "दिखाने के लिए संपर्क"
+ "कस्टम दृश्य निर्धारित करें"
+ "संपर्क ढूंढें"
+ "पसंदीदा"
+ "कोई संपर्क नहीं."
+ "कोई दृश्यमान संपर्क नहीं."
+ "कोई पसंदीदा नहीं."
+ "%s में कोई संपर्क नहीं"
+ "अक्सर किए जाने वाले साफ़ करें"
+ "सिम कार्ड चुनें"
+ "खाते"
+ "आयात करें/निर्यात करें"
+ "%1$s द्वारा"
+ "%2$s द्वारा %1$s को"
+ "खोजना बंद करें"
+ "खोज साफ़ करें"
+ "संपर्क प्रदर्शन विकल्प"
+ "खाता"
+ "कॉल के लिए हमेशा इसका उपयोग करें"
+ "इस सिम से कॉल करें"
+ "नोट के साथ कॉल करें"
+ "कॉल के साथ भेजने के लिए नोट लिखें ..."
+ "भेजें और कॉल करें"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-hr/strings.xml b/ContactsCommon/res/values-hr/strings.xml
new file mode 100644
index 0000000..8fca497
--- /dev/null
+++ b/ContactsCommon/res/values-hr/strings.xml
@@ -0,0 +1,254 @@
+
+
+
+
+ "Tekst kopiran"
+ "Kopiraj u međuspremnik"
+ "Nazovi %s "
+ "Nazovi kućni broj"
+ "Nazovi mobitel"
+ "Nazovi ured"
+ "Nazovi uredski faks"
+ "Nazovi kućni faks"
+ "Nazovi dojavljivač"
+ "Nazovi"
+ "Uzvrati poziv"
+ "Nazovi automobilski"
+ "Nazovi glavni broj tvrtke"
+ "Nazovi ISDN"
+ "Nazovi glavni broj"
+ "Nazovi faks"
+ "Nazovi radio"
+ "Nazovi teleks"
+ "Nazovi TTY/TDD"
+ "Nazovi poslovni mobitel"
+ "Nazovi poslovni dojavljivač"
+ "Nazovi %s "
+ "Nazovi MMS"
+ "Pošalji SMS na %s "
+ "Pošalji SMS na kućni"
+ "Pošalji SMS na mobitel"
+ "Pošalji SMS na uredski"
+ "Pošalji SMS na uredski faks"
+ "Pošalji SMS na kućni faks"
+ "Pošalji SMS na dojavljivač"
+ "Pošalji SMS"
+ "Pošalji SMS na povratni"
+ "Pošalji SMS na automobilski"
+ "Pošalji SMS na glavni u tvrtki"
+ "Pošalji SMS na ISDN"
+ "Pošalji SMS na glavni"
+ "Pošalji SMS na faks"
+ "Pošalji SMS na radio"
+ "Pošalji SMS na teleks"
+ "Pošalji SMS na TTY/TDD"
+ "Pošalji SMS na poslovni mobitel"
+ "Pošalji SMS na poslovni dojavljivač"
+ "Pošalji SMS na %s "
+ "Pošalji SMS na MMS"
+ "Uputite videopoziv"
+ "Brisati podatke o čestim kontaktima?"
+ "Izbrisat ćete popis osoba s kojima često kontaktirate u aplikacijama Kontakti i Osobe, pa će aplikacije e-pošte morati ispočetka učiti vaše postavke adresiranja."
+ "Brisanje često kontaktiranih..."
+ "Dostupan"
+ "Odsutan"
+ "Zauzet"
+ "Kontakti"
+ "Ostalo"
+ "Direktorij"
+ "Svi kontakti"
+ "Ja"
+ "Pretraživanje…"
+ "Pronađeno je više od %d ."
+ "Nema kontakata"
+
+ %d pronađeni
+ %d pronađena
+ %d pronađenih
+
+ "Brzi kontakt za korisnika %1$s "
+ "(Bez imena)"
+ "Često nazivani"
+ "Često kontaktirani"
+ "Prikaži kontakt"
+ "Svi kontakti s telefonskim brojevima"
+ "Prikaži ažuriranja"
+ "Samo telefon, bez sinkronizacije"
+ "Ime"
+ "Nadimak"
+ "Ime"
+ "Ime"
+ "Prezime"
+ "Prefiks imena"
+ "Srednje ime"
+ "Sufiks imena"
+ "Ime fonetski"
+ "Fonetski zapis imena"
+ "Fonetski zapis srednjeg imena"
+ "Fonetski zapis prezimena"
+ "Telefon"
+ "E-pošta"
+ "Adresa"
+ "IM"
+ "Organizacija"
+ "Odnos"
+ "Posebni datumi"
+ "Tekstna poruka"
+ "Adresa"
+ "Tvrtka"
+ "Naslov"
+ "Bilješke"
+ "SIP"
+ "Web-lokacija"
+ "Grupe"
+ "Pošalji e-poruku na kućnu e-adresu"
+ "Pošalji e-poruku na mobilnu e-adresu"
+ "Pošalji e-poruku na poslovnu e-adresu"
+ "Pošalji e-poruku"
+ "Pošalji e-poruku kontaktu %s "
+ "Pošalji e-poruku"
+ "Ulica"
+ "Poštanski pretinac"
+ "Četvrt"
+ "Grad"
+ "Država"
+ "Poštanski broj"
+ "Zemlja"
+ "Prikaz kućne adrese"
+ "Prikaz adrese na poslu"
+ "Prikaz adrese"
+ "Prikaz %s adrese"
+ "Chatajte pomoću AIM-a"
+ "Chatajte uz Windows Live"
+ "Chatajte uz Yahoo"
+ "Chatajte uz Skype"
+ "Chatajte uz QQ"
+ "Chatajte uslugom Google Talk"
+ "Chatajte pomoću ICQ-a"
+ "Chatajte uz Jabber"
+ "Chat"
+ "izbriši"
+ "Proširi ili sažmi nazive polja"
+ "Svi kontakti"
+ "Sa zvjezdicom"
+ "Prilagodi"
+ "Kontakt"
+ "Svi ostali kontakti"
+ "Svi kontakti"
+ "Ukloni grupu sinkronizacije"
+ "Dodaj grupu sinkroniziranja"
+ "Više grupa..."
+ "Uklanjanje grupe \"%s \" iz sinkronizacije također će ukloniti sve negrupirane kontakte iz sinkronizacije."
+ "Spremanje opcija prikaza..."
+ "Gotovo"
+ "Odustani"
+ "Kontakti na računu %s "
+ "Kontakti u prilagođenom prikazu"
+ "Jedan kontakt"
+ "Izrada kontakta pod računom"
+ "Uvoz sa SIM kartice"
+ "Uvoz sa SIM-a ^1 – ^2 "
+ "Uvoz sa SIM-a %1$s "
+ "Uvezi iz .vcf datoteke"
+ "Otkazati uvoz datoteke %s ?"
+ "Otkazati izvoz datoteke %s ?"
+ "Uvoz/izvoz kartice vCard nije otkazan"
+ "Nepoznata pogreška."
+ "Nije bilo moguće otvoriti datoteku \"%s \": %s ."
+ "Alat za izvoz ne može se pokrenuti: \"%s \"."
+ "Nema kontakata koji se mogu izvoziti."
+ "Onemogućili ste obavezno dopuštenje."
+ "Tijekom izvoza došlo je do pogreške: \"%s \"."
+ "Obavezan naziv datoteke predug je (\"%s \")."
+ "Na SD kartici ima previše vCard datoteka."
+ "I/O pogreška"
+ "Nema dovoljno memorije. Datoteka je možda prevelika."
+ "Iz neočekivanog razloga nije moguće analizirati vCard datoteku."
+ "Format nije podržan."
+ "Neuspješno prikupljanje metainformacija danih datoteka kartice vCard."
+ "Uvoz jedne ili više datoteka nije uspio (%s)."
+ "Završetak izvoza datoteke %s ."
+ "Dovršen je izvoz kontakata."
+ "Izvoz datoteke %s otkazan je."
+ "Izvoz podataka o kontaktu"
+ "Vaši podaci o kontaktu izvoze se u datoteku %s ."
+ "Dohvaćanje podataka iz baze podataka nije uspjelo."
+ "Nema kontakata koji se mogu izvoziti. Ako na svojem telefonu imate kontakte, neki davatelji podataka možda ne dopuštaju izvoz kontakata s tog telefona."
+ "Sastavljač za vCard nije se ispravno pokrenuo."
+ "Izvoz nije uspio"
+ "Podaci o kontaktu nisu izvezeni.\nRazlog: \"%s \""
+ "Uvozi se %s "
+ "Čitanje podataka vCarda nije uspjelo"
+ "Čitanje podataka kartice vCard otkazano"
+ "Završetak uvoza datoteke %s kartice vCard"
+ "Uvoz datoteke %s otkazan je"
+ "Datoteka %s uskoro će biti uvezena."
+ "Datoteka će uskoro biti uvezena."
+ "Zahtjev za uvoz formata vCard odbijen je. Pokušajte ponovo kasnije."
+ "Datoteka %s uskoro će biti izvezena."
+ "Datoteka će se uskoro izvesti."
+ "Zahtjev za izvoz formata vCard odbijen je. Pokušajte ponovo kasnije."
+ "kontakt"
+ "Spremanje vCard datoteka u lokalnu privremenu pohranu. Stvarni uvoz počet će uskoro."
+ "Uvoz vCard datoteke nije uspio."
+ "Na SD kartici nije pronađena nijedna vCard datoteka."
+ "Kontakt NFC-om"
+ "Izvesti kontakte?"
+ "Spremanje u predmemoriju"
+ "SD kartica ne može se skenirati. (Razlog: \"%s \")"
+ "Uvoz %s /%s : %s "
+ "Izvezi u .vcf datoteku"
+ "Poredaj po"
+ "Ime"
+ "Prezime"
+ "Oblik imena"
+ "Najprije ime"
+ "Najprije prezime"
+ "Dijeli vidljive kontakte"
+ "Nije uspjelo dijeljenje vidljivih kontakata"
+ "Uvoz/izvoz kontakata"
+ "Uvoz kontakata"
+ "Ovaj kontakt nije moguće dijeliti."
+ "Pretraži"
+ "Kontakti za prikaz"
+ "Kontakti za prikaz"
+ "Prilagodba prikaza"
+ "Pronađi kontakte"
+ "Favoriti"
+ "Nema kontakata."
+ "Nema vidljivih kontakata."
+ "Nema favorita."
+ "Nema kontakata pod: %s "
+ "Briši često kontaktirane"
+ "Odaberi SIM karticu"
+ "Računi"
+ "Uvoz/izvoz"
+ "putem izvora %1$s "
+ "%1$s putem izvora %2$s "
+ "zaustavi pretraživanje"
+ "Brisanje pretraživanja"
+ "Opcije prikaza kontakata"
+ "Račun"
+ "Uvijek upotrebljavaj za pozive"
+ "Poziv putem usluge"
+ "Poziv uz bilješku"
+ "Napišite bilješku koju ćete poslati uz poziv..."
+ "POŠALJI I NAZOVI"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-hu/strings.xml b/ContactsCommon/res/values-hu/strings.xml
new file mode 100644
index 0000000..5aff9b6
--- /dev/null
+++ b/ContactsCommon/res/values-hu/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Másolt szöveg"
+ "Másolás vágólapra"
+ "%s hívása"
+ "Otthoni szám hívása"
+ "Mobil hívása"
+ "Munkahelyi telefon hívása"
+ "Munkahelyi fax hívása"
+ "Otthoni fax hívása"
+ "Csipogó hívása"
+ "Hívás"
+ "Visszahívási szám hívása"
+ "Gépkocsi hívása"
+ "Céges fővonal hívása"
+ "ISDN-telefon hívása"
+ "Fő telefonszám hívása"
+ "Fax hívása"
+ "Rádiótelefon hívása"
+ "Telex hívása"
+ "TTY/TDD-szám hívása"
+ "Munkahelyi mobil hívása"
+ "Munkahelyi csipogó hívása"
+ "%s hívása"
+ "MMS-ben szereplő telefonszám hívása"
+ "SMS: %s "
+ "SMS küldése haza"
+ "SMS küldése mobiltelefonra"
+ "SMS küldése munkahelyi telefonra"
+ "SMS küldése munkahelyi faxszámra"
+ "SMS küldése otthoni faxszámra"
+ "SMS küldése csipogóra"
+ "SMS küldése"
+ "SMS küldése visszahívandó számra"
+ "SMS küldése gépkocsinak"
+ "SMS küldése a céges fővonalra"
+ "SMS küldése ISDN-telefonra"
+ "SMS küldése fő telefonszámra"
+ "SMS küldése faxszámra"
+ "SMS küldése rádiótelefonra"
+ "SMS küldése telexre"
+ "SMS küldése szöveges telefonra (TTY/TDD)"
+ "SMS küldése munkahelyi mobilra"
+ "SMS küldése munkahelyi csipogóra"
+ "SMS küldése ide: %s "
+ "SMS küldése MMS-ben szereplő számra"
+ "Videohívás kezdeményezése"
+ "Törli a gyakran keresetteket?"
+ "Törölni fogja a gyakran keresett személyek listáját a Névjegyek és a Telefon alkalmazásban, és arra kényszeríti az e-mail alkalmazásokat, hogy elölről kezdjék az Ön címzési szokásainak megtanulását."
+ "Gyakran keresettek törlése..."
+ "Elérhető"
+ "Nincs a gépnél"
+ "Elfoglalt"
+ "Névjegyek"
+ "Egyéb"
+ "Címtár"
+ "Összes névjegy"
+ "Én"
+ "Keresés…"
+ "Több mint %d találat."
+ "Nincsenek névjegyek"
+
+ %d találat
+ - 1 találat
+
+ "%1$s gyors elérése"
+ "(Nincs név)"
+ "Gyakran hívott"
+ "Gyakran keresettek"
+ "Névjegy megtekintése"
+ "Minden névjegy telefonszámokkal"
+ "Frissítések megtekintése"
+ "Csak a telefonon, nem szinkronizált"
+ "Név"
+ "Becenév"
+ "Név"
+ "Utónév"
+ "Vezetéknév"
+ "Név előtagja"
+ "Második utónév"
+ "Név utótagja"
+ "Név fonetikusan"
+ "Utónév fonetikusan"
+ "Második utónév fonetikusan"
+ "Vezetéknév fonetikusan"
+ "Telefonszám"
+ "E-mail"
+ "Cím"
+ "IM"
+ "Szervezet"
+ "Kapcsolat"
+ "Fontos dátumok"
+ "Szöveges üzenet"
+ "Cím"
+ "Cég"
+ "Beosztás"
+ "Megjegyzések"
+ "SIP"
+ "Webhely"
+ "Csoportok"
+ "E-mail küldése haza"
+ "E-mail küldése mobiltelefonra"
+ "E-mail küldése munkahelyi címre"
+ "E-mail küldése"
+ "E-mail küldése a(z) %s címre"
+ "E-mail"
+ "Utca, házszám"
+ "Postafiók"
+ "Környék"
+ "Település"
+ "Állam"
+ "Irányítószám"
+ "Ország"
+ "Otthoni cím megtekintése"
+ "Munkahelyi cím megtekintése"
+ "Cím megtekintése"
+ "%s cím megtekintése"
+ "Csevegés AIM-on"
+ "Csevegés a Windows Live-on"
+ "Csevegés a Yahoon"
+ "Csevegés Skype használatával"
+ "Csevegés a QQ-n"
+ "Csevegés a Google Csevegő használatával"
+ "Csevegés az ICQ-n"
+ "Csevegés Jabberen"
+ "Csevegés"
+ "törlés"
+ "Névmezők részletes vagy listanézete"
+ "Összes névjegy"
+ "Csillaggal megjelölt"
+ "Személyre szabás"
+ "Névjegy"
+ "Az összes többi névjegy"
+ "Összes névjegy"
+ "Szinkronizálási csoport eltávolítása"
+ "Szinkronizálási csoport hozzáadása"
+ "További csoportok..."
+ "Ha leállítja a(z) „%s ” csoport szinkronizálását, ugyanez történik a nem csoportosított névjegyekkel is."
+ "Megjelenítési beállítások mentése..."
+ "Kész"
+ "Mégse"
+ "Ismerősök itt: %s "
+ "Egyéni nézet névjegyei"
+ "Egyetlen névjegy"
+ "Névjegy létrehozása a következő fiókban:"
+ "Importálás SIM kártyáról"
+ "Importálás a következő SIM kártyáról: ^1 – ^2 "
+ "Importálás a következő SIM kártyáról: %1$s "
+ "Importálás .vcf fájlból"
+ "Megszakítja %s importálását?"
+ "Megszakítja %s exportálását?"
+ "vCard imp./exp. megszakítása sikertelen"
+ "Ismeretlen hiba."
+ "A(z) „%s ” fájl nem nyitható meg: %s ."
+ "Nem sikerült elindítani az exportálót: „%s ”."
+ "Nincs exportálható névjegy."
+ "Letiltott egy szükséges engedélyt."
+ "Hiba történt az exportálás során: „%s ”."
+ "A fájlnév túl hosszú („%s ”)."
+ "Túl sok vCard fájl van az SD-kártyán."
+ "I/O hiba"
+ "Nincs elég memória. Lehet, hogy túl nagy a fájl."
+ "Váratlan ok miatt nem sikerült a vCard szintaktikai elemzése."
+ "A formátum nem támogatott."
+ "Nem sikerült begyűjteni a vCard-fájl(ok) metaadatait."
+ "Egy vagy több fájl nem importálható (%s)."
+ "A(z) %s exportálása befejeződött."
+ "A névjegyek exportálása befejeződött."
+ "A(z) %s exportálása megszakítva."
+ "Névjegyadatok exportálása"
+ "Névjegyadatok exportálása ide: %s ."
+ "Nem sikerült lekérni az adatbázis-információkat."
+ "Nincsenek exportálható névjegyek. Ha vannak névjegyek a telefonján, akkor előfordulhat, hogy az adatszolgáltató nem teszi lehetővé a névjegyek exportálását a telefonról."
+ "A vCard-készítő nem megfelelően indult el."
+ "Sikertelen export"
+ "Nem sikerült a névjegyadatok exportálása.\nOk: „%s ”"
+ "Importálás – %s "
+ "Nem sikerült beolvasni a vCard adatait."
+ "A vCard-adatok beolvasása megszakítva"
+ "A(z) %s vCard importálása befejeződött"
+ "A(z) %s importálása megszakítva"
+ "A(z) %s hamarosan importálva lesz."
+ "A fájl importálása nemsokára megtörténik."
+ "A vCard-importálási kérelem elutasítva. Próbálja újra később."
+ "A(z) %s hamarosan exportálva lesz."
+ "A fájl exportálása hamarosan megtörténik."
+ "A vCard-exportálási kérelem elutasítva. Próbálja újra később."
+ "névjegy"
+ "vCard(ok) mentése az ideiglenes helyi tárolóba. A tényleges importálás hamarosan megkezdődik."
+ "Nem sikerült a vCard importálása."
+ "Nem található vCard fájl az SD-kártyán"
+ "NFC-n kapott név"
+ "Exportálja a névjegyeket?"
+ "Gyorsítótárazás"
+ "Az SD-kártyát nem lehet beolvasni. (Ok: „%s ”)"
+ "Importálás – %s /%s : %s "
+ "Exportálás .vcf fájlba"
+ "Rendezés"
+ "Utónév"
+ "Vezetéknév"
+ "Névformátum"
+ "Utónév elöl"
+ "Vezetéknév elöl"
+ "Látható névjegyek megosztása"
+ "Nem sikerült megosztani a látható névjegyeket."
+ "Névjegyek importálása/exportálása"
+ "Névjegyek importálása"
+ "Ezt a névjegyet nem lehet megosztani."
+ "Keresés"
+ "Megjelenítendő névjegyek"
+ "Megjelenítendő névjegyek"
+ "Egyéni nézet megadása"
+ "Névjegy keresése"
+ "Kedvencek"
+ "Nincsenek névjegyek."
+ "Nincsenek látható névjegyek."
+ "Nincsenek kedvencek."
+ "Nincsenek névjegyek itt: %s "
+ "Gyakran keresettek törlése"
+ "SIM kártya kiválasztása"
+ "Fiókok"
+ "Importálás/exportálás"
+ "itt: %1$s "
+ "%1$s itt: %2$s "
+ "keresés leállítása"
+ "Keresés törlése"
+ "Névjegy megjelenítési lehetőségei"
+ "Fiók"
+ "Mindig ezt használja hívásokhoz"
+ "Hívás a következővel:"
+ "Hívás üzenettel"
+ "Írjon üzenetet, amelyet elküldhetünk a hívással együtt…"
+ "KÜLDÉS és HÍVÁS"
+ "%2$s /%1$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-hy-rAM/strings.xml b/ContactsCommon/res/values-hy-rAM/strings.xml
new file mode 100644
index 0000000..220ed15
--- /dev/null
+++ b/ContactsCommon/res/values-hy-rAM/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Տեսքտը պատճենվեց"
+ "Պատճենել սեղմատախտակին"
+ "Զանգել %s համարին"
+ "Զանգել տուն"
+ "Զանգել շարժականին"
+ "Զանգել աշխաատավայր"
+ "Զանգել աշխատավայրի ֆաքսին"
+ "Զանգել տնային ֆաքսին"
+ "Զանգել փեյջերին"
+ "Զանգել"
+ "Հետզանգել"
+ "Զանգել մեքենայի հեռախոսահամարին"
+ "Զանգել ընկերության հիմնական համարին"
+ "Զանգել ISDN համարին"
+ "Զանգել հիմնական համարին"
+ "Զանգել ֆաքսին"
+ "Զանգել ռադիո համարին"
+ "Զանգել տելեքս համարին"
+ "Զանգել TTY/TDD համարին"
+ "Զանգել աշխատավայրի բջջայինին"
+ "Զանգել աշխատավայրի փեյջերին"
+ "Զանգել %s -ին"
+ "Զանգել MMS համարին"
+ "Հաղորդագրել %s -ին"
+ "Հաղորդագրել տուն"
+ "Հաղորդագրել բջջայինին"
+ "Հաղորդագրել աշխատանքայինին"
+ "Հաղորդագրել աշխատանքային ֆաքսի համարին"
+ "Հաղորդագրել տնային ֆաքսի համարին"
+ "Հաղորդագրել փեյջերին"
+ "Հաղորդագրել"
+ "Հաղորդագրել հետզանգման համարին"
+ "Հաղորդագրել մեքենայի հեռախոսահամարին"
+ "Հաղորդագրել ընկերության հիմնականին"
+ "Հաղորդագրել ISDN համարին"
+ "Հաղորդագրել հիմնական համարին"
+ "Հաղորդագրել ֆաքսի համարին"
+ "Հաղորդագրել ռադիո համարին"
+ "Հաղորդագրել տելեքս համարին"
+ "Հաղորդագրել TTY/TDD համարին"
+ "Հաղորդագրել աշխատանքային բջջայինին"
+ "Հաղորդագրել աշխատանքային փեյջերին"
+ "Հաղորդագրել %s -ին"
+ "Հաղորդագրել MMS համարին"
+ "Կատարել տեսազանգ"
+ "Մաքրե՞լ հաճախակի հաղորդակցվածները"
+ "Դուք կմաքրեք հաճախակի հաղորդակցվողների ցանկը Կոնտակտներ և Հեռախոս հավելվածներում, և ձեր էլփոստի ծրագիրը զրոյից կսովորի ձեր հասցեագրումների նախընտրությունները:"
+ "Հաճախակի հաղորդակցումների մաքրում..."
+ "Հասանելի"
+ "Տեղում չէ"
+ "Զբաղված"
+ "Կոնտակտներ"
+ "Այլ"
+ "Գրացուցակ"
+ "Բոլոր կոնտակտները"
+ "Ես"
+ "Որոնում..."
+ "Գտնվել են %d -ից ավելի:"
+ "Կոնտակտներ չկան"
+
+ - Գտնվել է
%d կոնտակտ
+ - Գտնվել է
%d կոնտակտ
+
+ "Արագ կապ %1$s -ի հետ"
+ "(Անանուն)"
+ "Հաճախակի կանչվող"
+ "Հաճախակի հաղորդակցվող"
+ "Դիտել կոնտակտը"
+ "Բոլոր հեռախոսահամարներով կոնտատկները"
+ "Դիտել թարմացումները"
+ "Միայն հեռախոսային, չհամաժամեցված"
+ "Անունը"
+ "Մականունը"
+ "Անունը"
+ "Անուն"
+ "Ազգանուն"
+ "Անվան նախածանցը"
+ "Հայրանուն"
+ "Անվան վերջնածանցը"
+ "Հնչյունաբանական անունը"
+ "Անունը՝ տառադարձությամբ"
+ "Հնչյունաբանական հայրանունը"
+ "Ազգանունը՝ տառադարձությամբ"
+ "Հեռախոսը"
+ "Էլփոստ"
+ "Հասցեն"
+ "IM"
+ "Կազմակերպությունը"
+ "Հարաբերություն"
+ "Հատուկ ամսաթվեր"
+ "Տեքստային հաղորդագրություն"
+ "Հասցեն"
+ "Ընկերությունը"
+ "Վերնագիրը"
+ "Գրառումներ"
+ "SIP"
+ "Վեբ-կայք"
+ "Խմբերը"
+ "Նամակագրել տուն"
+ "Նամակագրել բջջայինին"
+ "Նամակագրել աշխատավայր"
+ "Էլփոստ"
+ "Նամակագրել %s -ին"
+ "Էլփոստ"
+ "Փողոցը"
+ "Բաժանորդային արկղը"
+ "Շրջակայքը"
+ "Քաղաքը"
+ "Նահանգը"
+ "Փոստային կոդը"
+ "Երկիրը"
+ "Դիտել տան հասցեն"
+ "Դիտել աշխատավայրի հասցեն"
+ "Դիտել հասցեն"
+ "Դիտել %s հասցեն"
+ "Զրույց AIM-ով"
+ "Զրույց Windows Live-ով"
+ "Զրուցել Yahoo-ով"
+ "Զրույց Skype-ով"
+ "Զրույց QQ-ով"
+ "Զրույց Google Talk-ով"
+ "Զրուցել ICQ-ով"
+ "Զրուցել Jabber-ով"
+ "Զրույց"
+ "ջնջել"
+ "Ընդարձակել կամ սեղմել անունների դաշտերը"
+ "Բոլոր կոնտակտները"
+ "Աստղանշված"
+ "Հարմարեցնել"
+ "Կոնտակտ"
+ "Բոլոր այլ կոնտակտները"
+ "Բոլոր կոնտակտները"
+ "Հեռացնել համաժամեցված խումբը"
+ "Ավելացնել համաժամեցված խումբ"
+ "Այլ խմբեր..."
+ "«%s »-ի հեռացումը համաժամեցումից կհեռացնի այնտեղից նաև չխմբավորված կոնտակտները:"
+ "Ցուցադրվող ընտրանքները պահվում են..."
+ "Կատարված է"
+ "Չեղարկել"
+ "%s -ի կոնտակտները"
+ "Հատուկ տեսքով կոնտակտներ"
+ "Մեկ կոնտակտ"
+ "Ստեղծել կոնտակտ հաշվի հետ"
+ "Ներմուծել SIM քարտից"
+ "Ներմուծել ^1 - ^2 SIM-ից"
+ "Ներմուծել %1$s SIM-ից"
+ "Ներմուծել .vcf ֆայլից"
+ "Չեղարկե՞լ %s -ի ներմուծումը:"
+ "Չեղարկե՞լ %s -ի արտահանումը"
+ "Հնարավոր չէ չեղարկել vCard-ի ներմուծումը/արտահանումը"
+ "Անհայտ սխալ:"
+ "Հնարավոր չէ բացել «%s »-ը. պատճառը` %s :"
+ "Հնարավոր չէ մեկնարկել արտահանողին. պատճառը` «%s »:"
+ "Արտահանելի կոնտակտներ չկան:"
+ "Դուք անջատել եք անհրաժեշտ թույլտվությունը:"
+ "Արտահանման ընթացքում սխալ է տեղի ունեցել. պատճառը` «%s »:"
+ "Պահանջվող ֆայլի անունը («%s ») շատ երկար է:"
+ "SD քարտում չափից շատ vCard ֆայլեր կան:"
+ "I/O սխալ"
+ "Հիշողությունը բավարար չէ: Հնարավոր է` ֆայլը չափազանց մեծ է:"
+ "Չհաջողվեց վերլուծել vCard-ը անսպասելի պատճառով:"
+ "Ձևաչափը չի աջակցվում:"
+ "Հնարավոր չէ հավաքել vCard ֆայլ(եր)ի մետա տեղեկատվությունը:"
+ "Հնարավոր չէ ներմուծել մեկ կամ ավելի ֆայլեր (%s):"
+ "%s -ի արտահանումը ավարտվեց:"
+ "Կոնտակտների արտահանումը ավարտվեց:"
+ "%s -ի արտահանումը չեղարկվեց:"
+ "Կոնտակտային տվյալների արտահանում"
+ "Ձեր կոնտակտային տվյալները արտահանվում են %s :"
+ "Հնարավոր չէ ստանալ տվյալների շտեմարանի տեղեկատվությունը:"
+ "Արտահանելի կոնտակտներ չկան: Եթե դուք ձեր հեռախոսի մեջ ունեք կոնտակտներ, հնարավոր է՝ որոշ տվյալների մատակարարներ չեն թույլատրում հեռախոսից կոնտակտների արտահանումը:"
+ "vCard-ի կազմիչը ճիշտ չի մեկնարկել:"
+ "Հնարավոր չէ արտահանել"
+ "Կոնտակտային տվյալները չեն արտահանվել:\nՊատճառը՝ «%s »"
+ "%s -ի ներմուծում"
+ "Չհաջողվեց ընթերցել vCard-ի տվյալները"
+ "vCard տվյալների ընթերցումը չեղարկվեց"
+ "%s -ի ներմուծումը ավարտվեց vCard-ից"
+ "%s -ի ներմուծումը չեղարկվեց"
+ "%s -ը կներմուծվի շուտով:"
+ "Ֆայլը շուտով կներմուծվի:"
+ "vCard-ի ներմուծման հայցը մերժվել է: Կրկին փորձեք ավելի ուշ:"
+ "%s -ը կարտահանվի շուտով:"
+ "Ֆայլը շուտով կարտահանվի:"
+ "vCard-ի արտահանման հայցը մերժվեց: Փորձեք կրկին ավելի ուշ:"
+ "կոնտակտ"
+ "vCard(եր)-ի քեշավորում ժամանակավոր պաոց: Փաստացի ներմուծումը կսկսվի շուտով:"
+ "Չհաջողվեց ներմուծել vCard-ը:"
+ "SD քարտում vCard ֆայլ չի հայտնաբերվել:"
+ "NFC-ով ստացված կոնտակտ"
+ "Արտահանե՞լ կոնտակտները"
+ "Քեշավորում"
+ "Չհաջողվեց սկանավորել SD քարտը: (Պատճառը՝ «%s »)"
+ "%s /%s : %s -ի ներմուծում"
+ "Արտահանել .vcf ֆայլ"
+ "Դասավորել ըստ"
+ "Անուն"
+ "Ազգանուն"
+ "Անվան ձևաչափը"
+ "Սկզբում՝ անունը"
+ "Սկզբում՝ ազգանունը"
+ "Փոխանցել տեսանելի կոնտակտները"
+ "Չհաջողվեց համօգտագործել տեսանելի կոնտակտները:"
+ "Կոնտակտների ներմուծում/արտահանում"
+ "Ներմուծել կոնտակտներ"
+ "Հնարավոր չէ տարածել կոնտակտը:"
+ "Որոնել"
+ "Ցուցադրվող կոնտկատներ"
+ "Ցուցադրվող կոնտակտներ"
+ "Սահմանել հատուկ տեսքը"
+ "Գտնել կոնտակտներ"
+ "Ընտրյալներ"
+ "Կոնտակտներ չկան:"
+ "Տեսանելի կոնտակտներ չկան:"
+ "Ընտրյալներ չկան:"
+ "%s -ում կոնտակտներ չկան"
+ "Մաքրել հաճախակիները"
+ "Ընտրեք SIM քարտը"
+ "Հաշիվներ"
+ "Ներմուծել/արտահանել"
+ "%1$s -ի միջոցով"
+ "%1$s ` %2$s -ի միջոցով"
+ "դադարեցնել որոնումը"
+ "Մաքրել որոնման դաշտը"
+ "Կոնտակտի ցուցադրման ընտրանքները"
+ "Հաշիվ"
+ "Միշտ օգտագործել սա՝ զանգերի համար"
+ "Զանգահարել հետևյալով"
+ "Գրառումով զանգ"
+ "Մուտքագրեք գրառում՝ զանգի հետ ուղարկելու համար ..."
+ "ՈՒՂԱՐԿԵԼ ԵՎ ԶԱՆԳԵԼ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-in/strings.xml b/ContactsCommon/res/values-in/strings.xml
new file mode 100644
index 0000000..aa7bd5a
--- /dev/null
+++ b/ContactsCommon/res/values-in/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Teks disalin"
+ "Salin ke papan klip"
+ "Hubungi %s "
+ "Telepon rumah"
+ "Hubungi nomor seluler"
+ "Hubungi kantor"
+ "Hubungi faks kantor"
+ "Hubungi faks rumah"
+ "Hubungi pager"
+ "Hubungi"
+ "Hubungi panggilan balik"
+ "Hubungi nomor telepon mobil"
+ "Hubungi nomor telepon utama perusahaan"
+ "Hubungi ISDN"
+ "Hubungi nomor utama"
+ "Hubungi faks"
+ "Hubungi radio"
+ "Hubungi teleks"
+ "Hubungi TTY/TDD"
+ "Hubungi nomor seluler kantor"
+ "Hubungi pager kantor"
+ "Hubungi %s "
+ "Hubungi MMS"
+ "SMS %s "
+ "SMS rumah"
+ "SMS nomor seluler"
+ "SMS kantor"
+ "SMS faks kantor"
+ "SMS faks rumah"
+ "SMS pager"
+ "Teks"
+ "SMS panggilan balik"
+ "SMS mobil"
+ "SMS nomor utama perusahaan"
+ "SMS ISDN"
+ "SMS nomor utama"
+ "SMS faks"
+ "SMS radio"
+ "SMS teleks"
+ "SMS TTY/TDD"
+ "SMS nomor seluler kantor"
+ "SMS pager kantor"
+ "SMS %s "
+ "SMS MMS"
+ "Lakukan video call"
+ "Hapus yang sering dihubungi?"
+ "Anda akan menghapus daftar yang sering dihubungi pada aplikasi Kontak dan Ponsel, serta memaksa aplikasi email untuk mempelajari preferensi penanganan dari awal."
+ "Menghapus yang sering dihubungi..."
+ "Ada"
+ "Keluar"
+ "Sibuk"
+ "Kontak"
+ "Lainnya"
+ "Direktori"
+ "Semua kontak"
+ "Saya"
+ "Menelusuri…"
+ "Ditemukan lebih dari %d ."
+ "Tidak ada kontak"
+
+ %d ditemukan
+ - 1 ditemukan
+
+ "Kontak cepat untuk %1$s "
+ "(Tanpa nama)"
+ "Paling sering dipanggil"
+ "Paling sering dihubungi"
+ "Lihat kontak"
+ "Semua kontak dengan nomor telepon"
+ "Lihat pembaruan"
+ "Ponsel saja, tidak disinkronkan"
+ "Nama"
+ "Nama julukan"
+ "Nama"
+ "Nama depan"
+ "Nama belakang"
+ "Awalan nama"
+ "Nama tengah"
+ "Akhiran nama"
+ "Nama fonetik"
+ "Nama depan fonetik"
+ "Nama tengah fonetik"
+ "Nama belakang fonetik"
+ "Telepon"
+ "Email"
+ "Alamat"
+ "IM"
+ "Organisasi"
+ "Hubungan"
+ "Tanggal khusus"
+ "Pesan teks"
+ "Alamat"
+ "Perusahaan"
+ "Jabatan"
+ "Catatan"
+ "SIP"
+ "Situs web"
+ "Grup"
+ "Email ke rumah"
+ "Email ke seluler"
+ "Email ke kantor"
+ "Email"
+ "Email %s "
+ "Email"
+ "Jalan"
+ "Kotak pos"
+ "Lingkungan"
+ "Kota"
+ "Negara Bagian"
+ "Kode pos"
+ "Negara"
+ "Tampilkan alamat rumah"
+ "Tampilkan alamat kantor"
+ "Lihat alamat"
+ "Tampilkan alamat %s "
+ "Ngobrol menggunakan AIM"
+ "Ngobrol menggunakan Windows Live"
+ "Ngobrol menggunakan Yahoo"
+ "Ngobrol menggunakan Skype"
+ "Ngobrol menggunakan QQ"
+ "Ngobrol menggunakan Google Talk"
+ "Ngobrol menggunakan ICQ"
+ "Ngobrol menggunakan Jabber"
+ "Ngobrol"
+ "hapus"
+ "Luaskan atau ciutkan bidang nama"
+ "Semua kontak"
+ "Yang berbintang"
+ "Sesuaikan"
+ "Kontak"
+ "Semua kontak lainnya"
+ "Semua kontak"
+ "Hapus grup sinkronisasi"
+ "Tambahkan grup sinkronisasi"
+ "Grup lainnya..."
+ "Menghapus \"%s \" dari sinkronisasi juga akan menghapus setiap kontak di luar grup dari sinkronisasi."
+ "Menyimpan opsi tampilan..."
+ "Selesai"
+ "Batal"
+ "Kontak di %s "
+ "Kontak dalam tampilan khusus"
+ "Kontak tunggal"
+ "Buat kontak di bawah akun"
+ "Impor dari kartu SIM"
+ "Impor dari SIM ^1 - ^2 "
+ "Impor dari SIM %1$s "
+ "Impor dari file .vcf"
+ "Batalkan impor %s ?"
+ "Batalkan ekspor %s ?"
+ "Tidak dapat membatalkan impor/ekspor vCard"
+ "Kesalahan tidak dikenal."
+ "Tidak dapat membuka \"%s \": %s ."
+ "Tidak dapat memulai pengekspor: \"%s \"."
+ "Tidak ada kontak yang dapat diekspor."
+ "Anda telah menonaktifkan izin yang diperlukan."
+ "Terjadi kesalahan saat ekspor: \"%s \"."
+ "Nama file yang diperlukan terlalu panjang (\"%s \")."
+ "Terlalu banyak file vCard di kartu SD."
+ "Kesalahan I/O"
+ "Memori tidak cukup. File mungkin terlalu besar."
+ "Tidak dapat mengurai vCard karena alasan yang tak terduga."
+ "Format tidak didukung."
+ "Tidak dapat mengumpulkan informasi meta dari file vCard yang diberikan."
+ "Satu file atau lebih tidak dapat diimpor (%s)."
+ "Selesai mengekspor %s ."
+ "Selesai mengekspor kontak."
+ "Batal mengekspor %s ."
+ "Mengekspor data kontak"
+ "Data kontak Anda sedang diekspor ke: %s ."
+ "Tidak dapat memperoleh informasi basis data."
+ "Tidak ada kontak yang dapat diekspor. Jika Anda menyimpan kontak pada ponsel, beberapa penyedia data mungkin tidak mengizinkan kontak diekspor dari ponsel."
+ "Penyusun vCard tidak memulai dengan semestinya."
+ "Tak dapat mengekspor"
+ "Data kontak tidak diekspor.\nAlasan: \"%s \""
+ "Mengimpor %s "
+ "Tidak dapat membaca data vCard"
+ "Batal membaca data vCard"
+ "Selesai mengimpor vCard %s "
+ "Batal mengimpor %s "
+ "%s akan segera diimpor."
+ "File akan segera diimpor."
+ "Permintaan impor vCard ditolak. Coba lagi nanti."
+ "%s akan segera diekspor."
+ "File akan segera diekspor."
+ "Permintaan ekspor vCard ditolak. Coba lagi nanti."
+ "kontak"
+ "Menyimpan vCard ke dalam cache penyimpanan lokal sementara. Impor yang sebenarnya akan segera dimulai."
+ "Tidak dapat mengimpor vCard."
+ "Tidak ditemukan file vCard pada kartu SD."
+ "Kontak diterima lewat NFC"
+ "Ekspor kontak?"
+ "Menyimpan ke cache"
+ "Kartu SD tidak dapat dipindai. (Alasan: \"%s \")"
+ "Mengimpor %s /%s : %s "
+ "Ekspor ke file .vcf"
+ "Urutkan menurut"
+ "Nama depan"
+ "Nama belakang"
+ "Format nama"
+ "Nama depan pertama"
+ "Nama belakang pertama"
+ "Bagikan kontak yang terlihat"
+ "Gagal membagikan kontak yang terlihat."
+ "Impor/ekspor kontak"
+ "Impor kontak"
+ "Kontak ini tidak dapat dibagi."
+ "Telusuri"
+ "Kontak untuk ditampilkan"
+ "Kontak untuk ditampilkan"
+ "Tentukan tampilan khusus"
+ "Temukan kontak"
+ "Favorit"
+ "Tidak ada kontak."
+ "Tidak ada kontak yang terlihat."
+ "Tidak ada favorit."
+ "Tidak ada kontak di %s "
+ "Hapus yang sering"
+ "Pilih kartu SIM"
+ "Akun"
+ "Impor/ekspor"
+ "melalui %1$s "
+ "%1$s melalui %2$s "
+ "menghentikan penelusuran"
+ "Hapus penelusuran"
+ "Opsi tampilan kontak"
+ "Akun"
+ "Selalu gunakan ini untuk telepon"
+ "Telepon dengan"
+ "Telepon dengan catatan"
+ "Ketik catatan untuk dikirim dengan panggilan telepon ..."
+ "KIRIM & PANGGILAN"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-is-rIS/strings.xml b/ContactsCommon/res/values-is-rIS/strings.xml
new file mode 100644
index 0000000..61a623d
--- /dev/null
+++ b/ContactsCommon/res/values-is-rIS/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texti afritaður"
+ "Afrita á klippiborð"
+ "Hringja í %s "
+ "Hringja heim"
+ "Hringa í farsíma"
+ "Hringja í vinnusíma"
+ "Hringja í faxnúmer vinnu"
+ "Hringja í faxnúmer heima"
+ "Hringja í símboða"
+ "Hringja"
+ "Hringja svarhringingu"
+ "Hringja í bílasíma"
+ "Hringja í aðalsíma fyrirtækis"
+ "Hringja í ISDN-númer"
+ "Hringja í aðalnúmer"
+ "Hringja í faxnúmer"
+ "Hringja í talstöð"
+ "Hringja í telex"
+ "Hringja í fjarrita"
+ "Hringja í vinnufarsíma"
+ "Hringja í vinnusímboða"
+ "Hringja í %s "
+ "Hringja í MMS-númer"
+ "Senda textaskilaboð í %s "
+ "Senda textaskilaboð heim"
+ "Senda textaskilaboð í farsíma"
+ "Senda textaskilaboð í vinnusíma"
+ "Senda textaskilaboð í faxnúmer vinnu"
+ "Senda textaskilaboð í faxnúmer heima"
+ "Senda textaskilaboð í símboða"
+ "Senda textaskilaboð"
+ "Senda textaskilaboð í svarhringingarnúmer"
+ "Senda textaskilaboð í bílasíma"
+ "Senda textaskilaboð í aðalnúmer fyrirtækis"
+ "Senda textaskilaboð í ISDN-númer"
+ "Senda textaskilaboð í aðalnúmer"
+ "Senda textaskilaboð í faxnúmer"
+ "Senda textaskilaboð í talstöð"
+ "Senda textaskilaboð í telex"
+ "Senda textaskilaboð til fjarrita"
+ "Senda textaskilaboð í vinnufarsíma"
+ "Senda textaskilaboð í vinnusímboða"
+ "Senda textaskilaboð til %s "
+ "Senda textaskilaboð í MMS-númer"
+ "Hringja myndsímtal"
+ "Hreinsa mest notaða tengiliði?"
+ "Þetta hreinsar tengiliðina sem þú hefur mest samskipti við úr forritunum Tengiliðir og Sími og þvingar tölvupóstforrit til að læra upp á nýtt hvernig þú notar netföng."
+ "Hreinsar mest notaða tengiliði…"
+ "Laus"
+ "Í burtu"
+ "Upptekin(n)"
+ "Tengiliðir"
+ "Aðrir"
+ "Skrá"
+ "Allir tengiliðir"
+ "Ég"
+ "Leitar…"
+ "Yfir %d fundust."
+ "Engir tengiliðir"
+
+ %d fannst
+ %d fundust
+
+ "Flýtitengiliður fyrir %1$s "
+ "(Nafn vantar)"
+ "Oft hringt í"
+ "Oft haft samband við"
+ "Skoða tengilið"
+ "Allir tengiliðir með símanúmer"
+ "Skoða uppfærslur"
+ "Aðeins í símanum, ekki samstilltur"
+ "Nafn"
+ "Gælunafn"
+ "Nafn"
+ "Fornafn"
+ "Eftirnafn"
+ "Forskeyti nafns"
+ "Millinafn"
+ "Viðskeyti nafns"
+ "Hljóðrétt nafn"
+ "Hljóðritað fornafn"
+ "Hljóðrétt millinafn"
+ "Hljóðritað eftirnafn"
+ "Sími"
+ "Tölvupóstur"
+ "Heimilisfang"
+ "Spjall"
+ "Fyrirtæki"
+ "Tengsl"
+ "Merkisdagar"
+ "Senda textaskilaboð"
+ "Heimilisfang"
+ "Fyrirtæki"
+ "Titill"
+ "Athugasemdir"
+ "SIP"
+ "Vefsvæði"
+ "Hópar"
+ "Senda tölvupóst heim"
+ "Senda tölvupóst í fartæki"
+ "Senda tölvupóst í vinnu"
+ "Senda tölvupóst"
+ "Senda tölvupóst til %s "
+ "Senda tölvupóst"
+ "Gata"
+ "Pósthólf"
+ "Hverfi"
+ "Borg/bær"
+ "Ríki"
+ "Póstnúmer"
+ "Land"
+ "Skoða heimilisfang"
+ "Skoða póstfang vinnu"
+ "Skoða póstfang"
+ "Skoða heimilisfang %s "
+ "Spjalla með AIM"
+ "Spjalla með Windows Live"
+ "Spjalla með Yahoo"
+ "Spjalla með Skype"
+ "Spjalla með QQ"
+ "Spjalla með Google spjalli"
+ "Spjalla með ICQ"
+ "Spjalla með Jabber"
+ "Spjalla"
+ "eyða"
+ "Birta fleiri eða færri nafnareiti"
+ "Allir tengiliðir"
+ "Stjörnumerktir"
+ "Sérsníða"
+ "Tengiliður"
+ "Allir aðrir tengiliðir"
+ "Allir tengiliðir"
+ "Fjarlægja samstillingarhóp"
+ "Bæta við samstillingarhóp"
+ "Fleiri hópar…"
+ "Ef hópurinn „%s “ er fjarlægður úr samstillingu verður samstillingu einnig hætt á öllum tengiliðum sem ekki eru í hóp."
+ "Vistar birtingarvalkosti…"
+ "Lokið"
+ "Hætta við"
+ "Tengiliðir á %s "
+ "Tengiliðir á sérsniðnu yfirliti"
+ "Einn tengiliður"
+ "Búa til tengilið á reikningnum"
+ "Flytja inn af SIM-korti"
+ "Flytja inn af SIM-korti ^1 – ^2 "
+ "Flytja inn af SIM-korti %1$s "
+ "Flytja inn úr .vcf-skrá"
+ "Hætta við innflutning á %s ?"
+ "Hætta við útflutning á %s ?"
+ "Ekki tókst að hætta við flutning vCard"
+ "Óþekkt villa."
+ "Ekki tókst að opna „%s “: %s ."
+ "Ekki tókst að ræsa útflutningsverkfærið: „%s “."
+ "Engir tengiliðir sem flytja má út."
+ "Þú hefur gert nauðsynlegt leyfi óvirkt."
+ "Villa kom upp við útflutninginn: „%s “."
+ "Skráarheitið er of langt („%s “)."
+ "Of margar vCard-skrár eru á SD-kortinu."
+ "Inntaks-/úttaksvilla"
+ "Ekki nægt minni. Skráin kann að vera of stór."
+ "Ekki tókst að þátta vCard-skrána af óþekktri ástæðu."
+ "Ekki er stuðningur við sniðið."
+ "Ekki tókst að safna lýsigögnum fyrir uppgefna(r) vCard-skrá(r)."
+ "Ekki tókst að flytja inn eina eða fleiri skrár (%s)."
+ "Útflutningi á %s lokið."
+ "Útflutningi tengiliða lokið."
+ "Hætt við útflutning á %s ."
+ "Flytur út tengiliðagögn"
+ "Verið er að flytja tengiliðagögnin þín út í skrána: %s ."
+ "Ekki tókst að sækja upplýsingar úr gagnagrunni."
+ "Engir tengiliðir til útflutnings. Ef þú ert með tengiliði í símanum getur verið að vissar gagnaveitur leyfi ekki útflutning tengiliða úr símanum."
+ "Ræsing vCard-skrifviðmótsins tókst ekki."
+ "Flutningur mistókst"
+ "Tengiliðagögn voru ekki flutt út.\nÁstæða: „%s \""
+ "Flytur inn %s "
+ "Ekki tókst að lesa gögn úr vCard-skrá"
+ "Hætt við lestur vCard-gagna"
+ "Innflutningi lokið á vCard-skránni %s "
+ "Hætt við innflutning á %s "
+ "%s verður flutt inn innan skamms."
+ "Skráin verður flutt inn innan skamms."
+ "Beiðni um innflutning vCard-skrár hafnað. Reyndu aftur síðar."
+ "%s verður flutt út innan skamms."
+ "Skráin verður flutt út innan skamms."
+ "Beiðni um útflutning vCard-skrár hafnað. Reyndu aftur síðar."
+ "tengiliður"
+ "Setur vCard í skyndiminni í staðbundinni geymslu. Raunverulegur innflutningur hefst innan skamms."
+ "Ekki tókst að flytja vCard-skrána inn."
+ "Engin vCard-skrá fannst á SD-kortinu."
+ "Tengiliður mótt. um NFC"
+ "Flytja út tengiliði?"
+ "Setur í skyndiminni"
+ "Ekki tókst að leita á SD-kortinu. (Ástæða: „%s “)"
+ "Flytur inn %s /%s : %s "
+ "Flytja út í .vcf-skrá"
+ "Raða eftir"
+ "Fornafn"
+ "Eftirnafn"
+ "Nafnasnið"
+ "Fornafn fyrst"
+ "Eftirnafn fyrst"
+ "Deila sýnilegum tengiliðum"
+ "Mistókst að deila sýnilegum tengiliðum."
+ "Tengiliðir fluttir inn/út"
+ "Flytja tengiliði inn"
+ "Ekki er hægt að deila þessum tengilið."
+ "Leita"
+ "Tengiliðir til að birta"
+ "Tengiliðir til að birta"
+ "Veldu sjálfgefið yfirlit"
+ "Finna tengiliði"
+ "Uppáhald"
+ "Engir tengiliðir."
+ "Engir sýnilegir tengiliðir."
+ "Enginn í uppáhaldi."
+ "Engir tengiliðir í %s "
+ "Hreinsa algenga tengiliði"
+ "Veldu SIM-kort"
+ "Reikningar"
+ "Innflutningur og útflutningur"
+ "í gegnum %1$s "
+ "%1$s í gegnum %2$s "
+ "hætta leit"
+ "Hreinsa leit"
+ "Birtingarkostir fyrir tengiliði"
+ "Reikningur"
+ "Nota þetta alltaf fyrir símtöl"
+ "Hringja með"
+ "Símtal með texta"
+ "Sláðu inn texta til að senda með símtalinu..."
+ "SENDA OG HRINGJA"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-it/strings.xml b/ContactsCommon/res/values-it/strings.xml
new file mode 100644
index 0000000..93c49d8
--- /dev/null
+++ b/ContactsCommon/res/values-it/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Testo copiato"
+ "Copia negli appunti"
+ "Chiama %s "
+ "Chiama casa"
+ "Chiama cellulare"
+ "Chiama n. lavoro"
+ "Chiama fax lavoro"
+ "Chiama fax casa"
+ "Chiama cercapersone"
+ "Chiama"
+ "Chiama n. di richiamata"
+ "Chiama auto"
+ "Chiama azienda (principale)"
+ "Chiama ISDN"
+ "Chiama principale"
+ "Chiama fax"
+ "Chiama radiotelefono"
+ "Chiama telex"
+ "Chiama TTY/TDD"
+ "Chiama n. cellulare lavoro"
+ "Chiama cercapersone lavoro"
+ "Chiama %s "
+ "Chiama MMS"
+ "SMS a %s "
+ "SMS a casa"
+ "SMS a cellulare"
+ "SMS a lavoro"
+ "SMS a fax lavoro"
+ "SMS a fax casa"
+ "SMS a cercapersone"
+ "Invia SMS"
+ "SMS a n. di richiamata"
+ "SMS a auto"
+ "SMS a n. azienda principale"
+ "SMS a ISDN"
+ "SMS a principale"
+ "SMS a fax"
+ "SMS a n. radiotelefono"
+ "SMS a telex"
+ "SMS a TTY/TDD"
+ "SMS a cellulare lavoro"
+ "SMS a cercapersone lavoro"
+ "SMS a %s "
+ "SMS a MMS"
+ "Fai una videochiamata"
+ "Cancellare contattati di frequente?"
+ "Verrà cancellato l\'elenco dei contatti frequenti nelle app Contatti e Telefono e le app email dovranno apprendere da zero le tue preferenze di comunicazione."
+ "Cancellazione contattati di frequente…"
+ "Disponibile"
+ "Assente"
+ "Occupato"
+ "Contatti"
+ "Altro"
+ "Directory"
+ "Tutti i contatti"
+ "Io"
+ "Ricerca…"
+ "Più di %d trovati."
+ "Nessun contatto"
+
+ %d trovati
+ - 1 trovato
+
+ "Accesso rapido ai contatti per %1$s "
+ "(Nessun nome)"
+ "Numeri più chiamati"
+ "Contattati spesso"
+ "Visualizza contatto"
+ "Tutti i contatti con numeri di telefono"
+ "Visualizza aggiornamenti"
+ "Solo su telefono, non sincronizzato"
+ "Nome"
+ "Nickname"
+ "Nome"
+ "Nome"
+ "Cognome"
+ "Prefisso nome"
+ "Secondo nome"
+ "Suffisso nome"
+ "Nome fonetico"
+ "Nome fonetico"
+ "Secondo nome fonetico"
+ "Cognome fonetico"
+ "Telefono"
+ "Email"
+ "Indirizzo"
+ "Chat"
+ "Organizzazione"
+ "Relazione"
+ "Date speciali"
+ "SMS"
+ "Indirizzo"
+ "Società"
+ "Ruolo"
+ "Note"
+ "SIP"
+ "Sito web"
+ "Gruppi"
+ "Invia email a indirizzo casa"
+ "Invia email a indirizzo cellulare"
+ "Invia email a indirizzo lavoro"
+ "Invia email"
+ "Invia email a %s "
+ "Email"
+ "Via"
+ "Casella postale"
+ "Quartiere"
+ "Città"
+ "Provincia"
+ "Codice postale"
+ "Paese"
+ "Visualizza indirizzo casa"
+ "Visualizza indirizzo lavoro"
+ "Visualizza indirizzo"
+ "Visualizza indirizzo %s "
+ "Chatta su AIM"
+ "Chatta su Windows Live"
+ "Chatta su Yahoo"
+ "Chatta su Skype"
+ "Chatta su QQ"
+ "Chatta su Google Talk"
+ "Chatta su ICQ"
+ "Chatta su Jabber"
+ "Chatta"
+ "elimina"
+ "Espandi o comprimi i campi dei nomi"
+ "Tutti i contatti"
+ "Speciali"
+ "Personalizza"
+ "Contatto"
+ "Tutti gli altri contatti"
+ "Tutti i contatti"
+ "Rimuovi gruppo sincronizzazione"
+ "Aggiungi gruppo sincronizzazione"
+ "Altri gruppi..."
+ "Se rimuovi \"%s \" dalla sincronizzazione, verranno rimossi anche gli eventuali contatti separati."
+ "Salvataggio opzioni di visualizzazione..."
+ "Fine"
+ "Annulla"
+ "Contatti in %s "
+ "Contatti in visualizzazione personalizzata"
+ "Contatto singolo"
+ "Crea contatto nell\'account"
+ "Importa da scheda SIM"
+ "Importa da SIM ^1 - ^2 "
+ "Importa da SIM %1$s "
+ "Importa da file .vcf"
+ "Annullare l\'importazione di %s ?"
+ "Annullare l\'esportazione di %s ?"
+ "Annull. import./esport. vCard non riuscito"
+ "Errore sconosciuto."
+ "Apertura di \"%s \" non riuscita: %s ."
+ "Avvio dell\'utilità di esportazione non riuscito: \"%s \"."
+ "Nessun contatto esportabile."
+ "Hai disattivato un\'autorizzazione obbligatoria."
+ "Si è verificato un errore durante l\'esportazione: \"%s \"."
+ "Il nome file richiesto è troppo lungo (\"%s \")."
+ "Troppi file vCard sulla scheda SD."
+ "Errore I/O"
+ "Memoria insufficiente. Il file potrebbe essere troppo grande."
+ "Analisi vCard non riuscita per motivo imprevisto."
+ "Il formato non è supportato."
+ "Raccolta dei metadati dei file vCard specificati non riuscita."
+ "Importazione di uno o più file non riuscita (%s)."
+ "Esportazione di %s terminata."
+ "Esportazione dei contatti terminata."
+ "Esportazione di %s annullata."
+ "Esportazione dati contatti"
+ "I dati dei contatti sono in fase di esportazione in: %s ."
+ "Recupero informazioni database non riuscito."
+ "Non sono presenti contatti esportabili. Se hai contatti sul telefono, è possibile che alcuni fornitori di dati non consentano l\'esportazione dei contatti dal telefono."
+ "Il compositore di vCard non si è avviato correttamente."
+ "Impossibile esportare"
+ "I dati dei contatti non sono stati esportati.\nMotivo: \"%s \""
+ "Importazione di %s "
+ "Lettura dati vCard non riuscita"
+ "Lettura dati vCard annullata"
+ "Importazione vCard %s terminata"
+ "Importazione di %s annullata"
+ "La vCard %s verrà importata a breve."
+ "Il file sarà importato a breve."
+ "Richiesta importazione vCard rifiutata. Riprova più tardi."
+ "La vCard %s verrà esportata a breve."
+ "Il file verrà esportato a breve."
+ "Richiesta esportazione vCard rifiutata. Riprova più tardi."
+ "contatto"
+ "Memorizzazione delle vCard nella cache di archiviazione temporanea locale. L\'importazione reale inizierà a breve."
+ "Importazione vCard non riuscita."
+ "Nessun file vCard trovato sulla scheda SD."
+ "Contatto via NFC"
+ "Esportare contatti?"
+ "Memorizzazione nella cache"
+ "Scansione della scheda SD non riuscita. Motivo: \"%s \""
+ "Importazione di %s /%s : %s "
+ "Esporta in file .vcf"
+ "Ordina per"
+ "Nome"
+ "Cognome"
+ "Formato nome"
+ "Prima il nome"
+ "Prima il cognome"
+ "Condividi contatti visibili"
+ "Impossibile condividere i contatti visibili."
+ "Importa/esporta contatti"
+ "Importa contatti"
+ "Questo contatto non può essere condiviso."
+ "Cerca"
+ "Contatti da visualizzare"
+ "Contatti da visualizzare"
+ "Visualizzazione personalizzata"
+ "Trova contatti"
+ "Preferiti"
+ "Nessun contatto."
+ "Nessun contatto visibile."
+ "Nessun preferito."
+ "Nessun contatto in %s "
+ "Cancella frequenti"
+ "Seleziona scheda SIM"
+ "Account"
+ "Importa/esporta"
+ "tramite %1$s "
+ "%1$s tramite %2$s "
+ "interrompi ricerca"
+ "Cancella ricerca"
+ "Opzioni di visualizzazione dei contatti"
+ "Account"
+ "Usa sempre questa per chiamare"
+ "Chiama con"
+ "Chiama e invia una nota"
+ "Digita una nota da inviare con la chiamata..."
+ "INVIA E CHIAMA"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-iw/strings.xml b/ContactsCommon/res/values-iw/strings.xml
new file mode 100644
index 0000000..245a05e
--- /dev/null
+++ b/ContactsCommon/res/values-iw/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "טקסט הועתק"
+ "העתק ללוח"
+ "התקשר אל %s "
+ "התקשר לבית"
+ "התקשר לנייד"
+ "התקשר לעבודה"
+ "התקשר לפקס בעבודה"
+ "התקשר לפקס בבית"
+ "התקשר לזימונית"
+ "התקשר"
+ "התקשר למספר להתקשרות חזרה"
+ "התקשר למספר במכונית"
+ "התקשר למספר הראשי של החברה"
+ "התקשר ל-ISDN"
+ "התקשר למספר הראשי"
+ "התקשר לפקס"
+ "התקשר לרדיו"
+ "התקשר לטלקס"
+ "התקשר ל-TTY/TDD"
+ "התקשר לנייד של העבודה"
+ "התקשר לזימונית של העבודה"
+ "התקשר אל %s "
+ "התקשר ל-MMS"
+ "שלח הודעת טקסט אל %s "
+ "שלח הודעת טקסט לבית"
+ "שלח הודעת טקסט לנייד"
+ "שלח הודעת טקסט לעבודה"
+ "שלח הודעת טקסט לפקס בעבודה"
+ "שלח הודעת טקסט לפקס בבית"
+ "שלח הודעת טקסט לזימונית"
+ "שלח הודעת טקסט"
+ "שלח הודעת טקסט למספר להתקשרות חזרה"
+ "שלח הודעת טקסט למספר במכונית"
+ "שלח הודעת טקסט למספר הראשי של החברה"
+ "שלח הודעת טקסט ל-ISDN"
+ "שלח הודעת טקסט למספר הראשי"
+ "שלח הודעת טקסט לפקס"
+ "שלח הודעת טקסט לרדיו"
+ "שלח הודעת טקסט לטלקס"
+ "שלח הודעת טקסט ל-TTY/TDD"
+ "שלח הודעת טקסט לנייד של עבודה"
+ "שלח הודעת טקסט לזימונית של עבודה"
+ "שלח הודעת טקסט אל %s "
+ "שלח הודעת טקסט ל-MMS"
+ "בצע שיחת וידאו"
+ "האם למחוק אנשי קשר קבועים?"
+ "הפעולה הזו תמחק מהאפליקציות \'אנשים\' ו\'טלפון\' את רשימת אנשי הקשר שאיתם אתה יוצר קשר בתדירות גבוהה, ויהיה עליך ללמד מחדש את אפליקציות האימייל את הכתובות המועדפות עליך."
+ "מוחק אנשי קשר קבועים…"
+ "זמין"
+ "לא נמצא"
+ "לא פנוי"
+ "אנשי קשר"
+ "אחר"
+ "ספריה"
+ "כל אנשי הקשר"
+ "אני"
+ "מחפש…"
+ "נמצאו יותר מ-%d ."
+ "אין אנשי קשר"
+
+ - נמצאו
%d
+ - נמצאו
%d
+ - נמצאו
%d
+ - נמצא אחד
+
+ "קשר מהיר עבור %1$s "
+ "(ללא שם)"
+ "התקשרות לעתים קרובות"
+ "בקשר לעתים קרובות"
+ "הצג איש קשר"
+ "כל אנשי הקשר עם מספרי טלפון"
+ "הצג עדכונים"
+ "טלפון בלבד, לא מסונכרן"
+ "שם"
+ "כינוי"
+ "שם"
+ "שם פרטי"
+ "שם משפחה"
+ "תחילית שם"
+ "שם אמצעי"
+ "סיומת שם"
+ "שם פונטי"
+ "שם פרטי פונטי"
+ "שם אמצעי פונטי"
+ "שם משפחה פונטי"
+ "טלפון"
+ "אימייל"
+ "כתובת"
+ "הודעות מיידיות"
+ "ארגון"
+ "קשר"
+ "תאריכים מיוחדים"
+ "הודעת טקסט"
+ "כתובת"
+ "חברה"
+ "תפקיד"
+ "הערות"
+ "SIP"
+ "אתר"
+ "קבוצות"
+ "שלח אימייל לבית"
+ "שלח אימייל לנייד"
+ "שלח אימייל לעבודה"
+ "שלח אימייל"
+ "שלח אימייל אל %s "
+ "שלח אימייל"
+ "רחוב"
+ "תא דואר"
+ "שכונה"
+ "עיר"
+ "מדינה"
+ "מיקוד"
+ "ארץ"
+ "הצג כתובת בית"
+ "הצג כתובת עבודה"
+ "הצג כתובת"
+ "הצג כתובת %s "
+ "שוחח בצ\'אט באמצעות AIM"
+ "שוחח בצ\'אט באמצעות Windows Live"
+ "שוחח בצ\'אט באמצעות Yahoo"
+ "שוחח בצ\'אט באמצעות Skype"
+ "שוחח בצ\'אט באמצעות QQ"
+ "שוחח בצ\'אט באמצעות Google Talk"
+ "שוחח בצ\'אט באמצעות ICQ"
+ "שוחח בצ\'אט באמצעות Jabber"
+ "צ\'אט"
+ "מחק"
+ "הרחב או כווץ שמות של שדות"
+ "כל אנשי הקשר"
+ "מסומן בכוכב"
+ "התאם אישית"
+ "איש קשר"
+ "כל אנשי הקשר האחרים"
+ "כל אנשי הקשר"
+ "הסר קבוצת סנכרון"
+ "הוסף קבוצת סנכרון"
+ "קבוצות נוספות…"
+ "הסרת \"%s \" מהסנכרון תסיר מהסנכרון גם אנשי קשר שאינם מקובצים."
+ "שומר אפשרויות תצוגה…"
+ "בוצע"
+ "בטל"
+ "אנשי קשר ב-%s "
+ "אנשי קשר בתצוגה מותאמת אישית"
+ "איש קשר יחיד"
+ "צור איש קשר בחשבון"
+ "יבא מכרטיס SIM"
+ "יבא מ-SIM ^1 - ^2 "
+ "יבא מ-SIM %1$s "
+ "יבא מקובץ .vcf"
+ "האם לבטל את הייבוא של %s ?"
+ "האם לבטל את הייצוא של %s ?"
+ "לא ניתן היה לבטל ייבוא/ייצוא של vCard"
+ "שגיאה לא ידועה."
+ "לא ניתן היה לפתוח את \"%s \": %s ."
+ "לא ניתן להפעיל את המייצא: \"%s \"."
+ "אין אנשי קשר הניתנים לייצוא."
+ "השבתת הרשאה נדרשת."
+ "אירעה שגיאה במהלך הייצוא: \"%s \"."
+ "שם הקובץ הדרוש ארוך מדי (\"%s \")"
+ "קיימים בכרטיס ה-SD קובצי vCard רבים מדי."
+ "שגיאת קלט/פלט"
+ "אין מספיק זיכרון. ייתכן שהקובץ גדול מדי."
+ "לא ניתן היה לנתח את ה-vCard מסיבה בלתי צפויה."
+ "הפורמט אינו נתמך."
+ "לא ניתן היה לאסוף מטא-מידע של קובצי vCard נתונים."
+ "לא ניתן היה לייבא קובץ אחד או יותר (%s)."
+ "הייצוא של %s הסתיים."
+ "ייצוא אנשי הקשר הושלם."
+ "הייצוא של %s בוטל."
+ "מייצא נתונים של אנשי קשר"
+ "נתוני אנשי הקשר שלך מיוצאים אל: %s ."
+ "לא ניתן היה לקבל מידע ממסד הנתונים."
+ "אין אנשי קשר הניתנים לייצוא. אם מוגדרים אנשי קשר בטלפון שלך, ייתכן שספקי נתונים מסוימים אינם מאפשרים ייצוא של אנשי קשר מהטלפון."
+ "יישום יצירת ה-vCard לא הופעל כהלכה."
+ "לא ניתן היה לייצא"
+ "נתוני אנשי הקשר לא יוצאו.\nסיבה: \"%s \""
+ "מייבא את %s "
+ "לא ניתן היה לקרוא נתוני vCard"
+ "קריאת נתוני ה-VCard בוטלה"
+ "הייבוא של קובץ vCard %s הסתיים"
+ "הייבוא של %s בוטל"
+ "הייבוא של %s יתבצע תוך זמן קצר."
+ "ייבוא הקובץ יתבצע תוך זמן קצר."
+ "הבקשה לייבוא ה-vCard נדחתה. נסה שוב מאוחר יותר."
+ "הייצוא של %s יתבצע תוך זמן קצר."
+ "ייצוא הקובץ יתבצע בעוד זמן קצר."
+ "הבקשה לייצוא ה-vCard נדחתה. נסה שוב מאוחר יותר."
+ "איש קשר"
+ "קובצי ה-vCard נשמרים כקבצים באחסון מקומי זמני. הייבוא בפועל יחל בקרוב."
+ "לא ניתן היה לייבא את ה-vCard."
+ "לא נמצאו קובצי vCard בכרטיס ה-SD."
+ "איש הקשר התקבל באמצעות NFC"
+ "לייצא את אנשי הקשר?"
+ "שומר כקובץ שמור"
+ "לא ניתן היה לסרוק את כרטיס ה-SD. (סיבה: \"%s \")"
+ "מייבא %s /%s : %s "
+ "יצא לקובץ .vcf"
+ "מיין לפי"
+ "שם פרטי"
+ "שם משפחה"
+ "פורמט השם"
+ "שם פרטי יופיע ראשון"
+ "שם משפחה יופיע ראשון"
+ "שתף אנשי קשר שמוצגים כעת"
+ "שיתוף של התוכן הגלוי נכשל."
+ "יבא/יצא אנשי קשר"
+ "יבא אנשי קשר"
+ "לא ניתן לשתף איש קשר זה."
+ "חפש"
+ "אנשי קשר להצגה"
+ "אנשי קשר להצגה"
+ "תצוגה מותאמת אישית"
+ "חפש אנשי קשר"
+ "מועדפים"
+ "אין אנשי קשר."
+ "אין אנשי קשר גלויים."
+ "אין מועדפים."
+ "אין אנשי קשר ב-%s "
+ "מחק אנשי קשר קבועים"
+ "בחר כרטיס SIM"
+ "חשבונות"
+ "ייבוא/ייצוא"
+ "באמצעות %1$s "
+ "%1$s באמצעות %2$s "
+ "הפסק לחפש"
+ "נקה את החיפוש"
+ "אפשרויות להצגת אנשי קשר"
+ "חשבון"
+ "השתמש תמיד עבור שיחות"
+ "התקשר באמצעות"
+ "התקשר עם הערה"
+ "הקלד הערה לשליחה עם השיחה..."
+ "שלח והתקשר"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ja/strings.xml b/ContactsCommon/res/values-ja/strings.xml
new file mode 100644
index 0000000..5de2f2a
--- /dev/null
+++ b/ContactsCommon/res/values-ja/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "テキストをコピーしました"
+ "クリップボードにコピー"
+ "%s に発信"
+ "自宅に発信"
+ "携帯端末に発信"
+ "勤務先に発信"
+ "勤務先FAXに発信"
+ "自宅FAXに発信"
+ "ポケベルに発信"
+ "発信"
+ "コールバック先に発信"
+ "クルマに発信"
+ "会社代表番号に発信"
+ "ISDNに発信"
+ "電話番号1に発信"
+ "FAXに発信"
+ "無線に発信"
+ "テレックスに発信"
+ "TTY/TDDに発信"
+ "携帯端末(勤務先)に発信"
+ "ポケベル(勤務先)に発信"
+ "%s に発信"
+ "MMSに発信"
+ "%s にSMS"
+ "自宅にSMS"
+ "携帯端末にSMS"
+ "勤務先にSMS"
+ "勤務先FAXにSMS"
+ "自宅FAXにSMS"
+ "ポケベルにSMS"
+ "電話にSMS"
+ "コールバック先にSMS"
+ "クルマにSMS"
+ "会社代表番号にSMS"
+ "ISDNにSMS"
+ "電話番号1にSMS"
+ "FAXにSMS"
+ "無線にSMS"
+ "テレックスにSMS"
+ "TTY/TDDにSMS"
+ "携帯端末(勤務先)にSMS"
+ "ポケベル(勤務先)にSMS"
+ "%s にSMS"
+ "MMSにSMS"
+ "ビデオハングアウト"
+ "よく使う連絡先を消去しますか?"
+ "連絡帳アプリや電話アプリのよく使う連絡先リストを消去し、メールアプリがアドレス設定を初めから保存していくようにします。"
+ "よく使う連絡先を消去しています…"
+ "オンライン"
+ "不在"
+ "取り込み中"
+ "連絡先"
+ "その他"
+ "ディレクトリ"
+ "すべての連絡先"
+ "自分"
+ "検索しています…"
+ "%d 件以上見つかりました。"
+ "連絡先はありません"
+
+ %d 件見つかりました
+ - 1件見つかりました
+
+ "%1$s さんのクイックコンタクト"
+ "(名前なし)"
+ "よく使う連絡先"
+ "よく使う連絡先"
+ "連絡先を表示"
+ "電話番号のあるすべての連絡先"
+ "更新情報を表示"
+ "電話のみ(非同期)"
+ "名前"
+ "ニックネーム"
+ "名前"
+ "名"
+ "姓"
+ "敬称(名前の前)"
+ "ミドルネーム"
+ "敬称(名前の後)"
+ "フリガナ"
+ "フリガナ(名)"
+ "フリガナ(ミドルネーム)"
+ "フリガナ(姓)"
+ "電話"
+ "メール"
+ "住所"
+ "IM"
+ "組織"
+ "関係"
+ "特別な日"
+ "テキストメッセージ"
+ "住所"
+ "会社"
+ "役職"
+ "メモ"
+ "SIP"
+ "ウェブサイト"
+ "グループ"
+ "自宅にメール"
+ "携帯端末にメール"
+ "勤務先にメール"
+ "メール"
+ "%s にメール"
+ "メール"
+ "番地"
+ "私書箱"
+ "街区(中国等で使用)"
+ "市区町村"
+ "都道府県"
+ "郵便番号"
+ "国"
+ "自宅の住所を表示"
+ "勤務先の住所を表示"
+ "住所を表示"
+ "%s の住所を表示"
+ "AIMでチャット"
+ "Windows Liveでチャット"
+ "Yahooでチャット"
+ "Skypeでチャット"
+ "QQでチャット"
+ "Googleトークでチャット"
+ "ICQでチャット"
+ "Jabberでチャット"
+ "チャット"
+ "削除"
+ "名前フィールドの展開/折りたたみ"
+ "すべての連絡先"
+ "スター付き"
+ "カスタマイズ"
+ "連絡先"
+ "その他すべての連絡先"
+ "すべての連絡先"
+ "同期グループを削除"
+ "同期グループを追加"
+ "他のグループ…"
+ "「%s 」を同期から除外すると、グループに含まれない連絡先もすべて同期から除外されます。"
+ "表示オプションを保存しています…"
+ "完了"
+ "キャンセル"
+ "%s の連絡先"
+ "連絡先のカスタム表示"
+ "単独の連絡先"
+ "アカウントに連絡先を作成"
+ "SIMカードからインポート"
+ "SIM(^1 - ^2 )からインポート"
+ "SIM(%1$s )からインポート"
+ ".vcfからインポート"
+ "%s のインポートをキャンセルしますか?"
+ "%s のエクスポートをキャンセルしますか?"
+ "vCardインポート/エクスポート取り消し不可"
+ "不明なエラーです。"
+ "「%s 」を開けませんでした: %s 。"
+ "エクスポータを起動できませんでした: 「%s 」。"
+ "エクスポートできる連絡先がありません。"
+ "必要な権限が無効にされています。"
+ "エクスポート中にエラーが発生しました: 「%s 」。"
+ "ファイル名(必須)が長すぎます(「%s 」)。"
+ "SDカードのvCardファイルが多すぎます。"
+ "送受信エラー"
+ "メモリが不足しています。ファイルが大きすぎる可能性があります。"
+ "予期しない理由によりvCardを解析できませんでした。"
+ "このフォーマットには対応していません。"
+ "指定されたvCardファイルのメタ情報を取得できませんでした。"
+ "1つ以上のファイルをインポートできませんでした(%s)。"
+ "%s のエクスポートの完了"
+ "連絡先のエクスポートの完了"
+ "%s のエクスポートのキャンセル"
+ "連絡先データのエクスポート"
+ "連絡先データを%s にエクスポートします。"
+ "データベース情報を取得できませんでした。"
+ "エクスポートできる連絡先がありません。携帯端末に連絡先がある場合でも、データプロバイダによっては携帯端末から連絡先をエクスポートできないことがあります。"
+ "vCardコンポーザーが正しく起動しませんでした。"
+ "エクスポート失敗"
+ "連絡先データはエクスポートされませんでした。\n理由: 「%s 」"
+ "%s をインポート中"
+ "vCardデータの読み取りの失敗"
+ "vCardデータの読み取りのキャンセル"
+ "vCardの%s のインポートの終了"
+ "%s のインポートのキャンセル"
+ "%s はまもなくインポートされます。"
+ "ファイルはまもなくインポートされます。"
+ "vCardのインポートリクエストは拒否されました。しばらくしてからもう一度お試しください。"
+ "%s はまもなくエクスポートされます。"
+ "ファイルはまもなくエクスポートされます。"
+ "vCardのエクスポートリクエストは拒否されました。しばらくしてからもう一度お試しください。"
+ "連絡先"
+ "vCardをローカル一時ストレージにキャッシュしています。まもなくインポート処理を開始します。"
+ "vCardをインポートできませんでした。"
+ "SDカードにvCardファイルが見つかりません。"
+ "NFC受信の連絡先"
+ "連絡先をエクスポートしますか?"
+ "キャッシュ中"
+ "SDカードをスキャンできませんでした(理由: 「%s 」)。"
+ "%s /%s 件をインポート中: %s "
+ ".vcfにエクスポート"
+ "並べ替え"
+ "名"
+ "姓"
+ "名前の形式"
+ "名が先"
+ "姓が先"
+ "表示可能な連絡先を共有"
+ "表示可能な連絡先を共有できませんでした。"
+ "連絡先のインポート/エクスポート"
+ "連絡先のインポート"
+ "この連絡先は共有できません。"
+ "検索"
+ "表示する連絡先"
+ "表示する連絡先"
+ "カスタム表示の設定"
+ "連絡先を検索"
+ "お気に入り"
+ "連絡先がありません。"
+ "表示できる連絡先はありません。"
+ "お気に入りはありません。"
+ "%s の連絡先はありません"
+ "よく使う連絡先のクリア"
+ "SIMカードを選択"
+ "アカウント"
+ "インポート/エクスポート"
+ "更新元: %1$s "
+ "%1$s 、更新元: %2$s "
+ "検索を停止"
+ "検索をクリア"
+ "連絡先表示オプション"
+ "アカウント"
+ "このSIMを常に通話に使用する"
+ "発信用の端末アカウントを選択してください"
+ "メモを添付して発信"
+ "発信時に送信するメモを入力..."
+ "送信と発信"
+ "%1$s /%2$s "
+ "%1$s 、%2$s "
+
diff --git a/ContactsCommon/res/values-ka-rGE/strings.xml b/ContactsCommon/res/values-ka-rGE/strings.xml
new file mode 100644
index 0000000..a553b34
--- /dev/null
+++ b/ContactsCommon/res/values-ka-rGE/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "კოპირებული ტექსტი"
+ "გაცვლის ბუფერში კოპირება"
+ "დარეკვა %s ნომერზე"
+ "დარეკვა სახლში"
+ "დარეკვა მობილურზე"
+ "დარეკვა სამსახურში"
+ "დარეკვა სამსახურის ფაქსზე"
+ "დარეკვა სახლის ფაქსზე"
+ "დარეკვა პეიჯერზე"
+ "დარეკვა"
+ "დარეკვა უკუგამოძახების ნომერზე"
+ "დარეკვა მანქანის ტელეფონზე"
+ "დარეკვა კომპანიის მთავარ ნომერზე"
+ "დარეკვა ISDN-ის ნომერზე"
+ "დარეკვა მთავარ ნომერზე"
+ "დარეკვა ფაქსზე"
+ "დარეკვა გადამცემზე"
+ "დარეკვა ტელექსზე"
+ "დარეკვა TTY/TDD-ზე"
+ "დარეკვა სამსახურის მობილურზე"
+ "დარეკვა სამსახურის პეიჯერზე"
+ "დარეკვა %s -თან"
+ "დარეკვა MMS-ის ნომერზე"
+ "ტექსტური შეტყობინების გაგზავნა %s -ზე"
+ "ტექსტური შეტყობინების გაგზავნა სახლში"
+ "ტექსტური შეტყობინების გაგზავნა მობილურზე"
+ "ტექსტური შეტყობინების გაგზავნა სამსახურში"
+ "ტექსტური შეტყობინების გაგზავნა სამუშაო ფაქსზე"
+ "ტექსტური შეტყობინების გაგზავნა სახლის ფაქსზე"
+ "ტექსტური შეტყობინების გაგზავნა პეიჯერზე"
+ "ტექსტური შეტყობინება"
+ "ტექსტური შეტყობინების გაგზავნა უკუგამოძახების ნომერზე"
+ "ტექსტური შეტყობინების გაგზავნა მანქანის ნომერზე"
+ "ტექსტური შეტყობინების გაგზავნა კომპანიის მთავარ ნომერზე"
+ "ტექსტური შეტყობინების გაგზავნა ISDN ნომერზე"
+ "ტექსტური შეტყობინების გაგზავნა მთავარ ნომერზე"
+ "ტექსტური შეტყობინების გაგზავნა ფაქსზე"
+ "ტექსტური შეტყობინების გაგზავნა გადამცემზე"
+ "ტექსტური შეტყობინების გაგზავნა ტელექსზე"
+ "ტექსტური შეტყობინების გაგზავნა TTY/TDD-ზე"
+ "ტექსტური შეტყობინების გაგზავნა სამსახურის მობილურზე"
+ "ტექსტური შეტყობინების გაგზავნა სამსახურის პეიჯერზე"
+ "ტექსტური შეტყობინების გაგზავნა %s -ისთვის"
+ "ტექსტური შეტყობინების გაგზავნა MMS ნომერზე"
+ "ვიდეოზარის განხორციელება"
+ "გსურთ ხშირი კონტაქტების წაშლა?"
+ "თქვენ წაშლით ხშირად დაკავშირებულთა სიას კონტაქტებიდან და ტელეფონის აპლიკაციიდან და აიძულებთ ელ-ფოსტის აპლიკაციებს შეისწავლონ თქვენი ადრესატების პრიორიტეტები ნულიდან."
+ "ხშირი კონტაქტები იშლება…"
+ "ხელმისაწვდომი"
+ "გასული"
+ "დაკავებული"
+ "კონტაქტები"
+ "სხვა"
+ "კატალოგი"
+ "ყველა კონტაქტი"
+ "მე"
+ "ძიება..."
+ "ნაპოვნია %d -ზე მეტი."
+ "კონტაქტები არ არის"
+
+ %d მოიძებნა
+ - 1 მოიძებნა
+
+ "სწრაფი დაკავშირება %1$s -თან"
+ "(სახელის გარეშე)"
+ "ხშირად დარეკილი"
+ "ხშირი კონტაქტები"
+ "კონტაქტის ნახვა"
+ "ყველა ტელეფონის ნომრიანი კონტაქტი"
+ "განახლებების ნახვა"
+ "მხოლოდ ტელეფონზე, არასინქრონიზებული"
+ "სახელი"
+ "მეტსახელი"
+ "სახელი"
+ "სახელი"
+ "გვარი"
+ "სახელის პრეფიქსი"
+ "მეორე სახელი"
+ "სახელის სუფიქსი"
+ "სახელის ტრანსკრიფცია"
+ "ფონეტიკური სახელი"
+ "მეორე სახელის ტრანსკრიფცია"
+ "ფონეტიკური გვარი"
+ "ტელეფონი"
+ "ელფოსტა"
+ "მისამართი"
+ "IM"
+ "ორგანიზაცია"
+ "ურთიერთობა"
+ "საგანგებო შეთავაზებები"
+ "ტექსტური შეტყობინება (SMS)"
+ "მისამართი"
+ "კომპანია"
+ "სათაური"
+ "შენიშვნები"
+ "SIP"
+ "ვებ-საიტი"
+ "ჯგუფები"
+ "ელფოსტის გაგზავნა სახლში"
+ "ელფოსტის გაგზავნა მობილურზე"
+ "ელფოსტის გაგზავნა სამსახურში"
+ "ელფოსტის გაგზავნა"
+ "ელფოსტის გაგზავნა %s "
+ "ელფოსტის გაგზავნა"
+ "ქუჩა"
+ "საფოსტო ყუთი"
+ "უბანი"
+ "ქალაქი"
+ "შტატი"
+ "ZIP-კოდი"
+ "ქვეყანა"
+ "სახლის მისამართის ნახვა"
+ "სამსახურის მისამართის ნახვა"
+ "მისამართის ნახვა"
+ "%s მისამართის ნახვა"
+ "ჩეთი AIM-ით"
+ "ჩეთი Windows Live-ით"
+ "ჩეთი Yahoo-ს საშუალებით"
+ "ჩეთი Skype-ით"
+ "ჩეთი QQ-ით"
+ "ჩეთი Google Talk-ით"
+ "ჩეთი ICQ-ით"
+ "ჩეთი Jabber-ით"
+ "ჩეთი"
+ "წაშლა"
+ "სახელთა ველების გაშლა ან აკეცვა"
+ "ყველა კონტატი"
+ "ვარსკვლავიანი"
+ "მორგება"
+ "კონტაქტი"
+ "ყველა სხვა კონტაქტი"
+ "ყველა კონტატი"
+ "სინქრონიზაციის ჯგუფის წაშლა"
+ "სინქრონიზაციის ჯგუფის დამატება"
+ "სხვა ჯგუფები…"
+ "სინქრონიზაციიდან „%s “-ის ამოშლა წაშლის ყველა დაუჯგუფებელ კონტაქტს."
+ "ეკრანის პარამეტრების შენახვა…"
+ "დასრულდა"
+ "გაუქმება"
+ "კონტაქტები %s -ში"
+ "კონტაქტები მორგებულ ხედში"
+ "ერთი კონტაქტი"
+ "კონტაქტის შექმნა ანგარიშში"
+ "SIM ბარათიდან იმპორტი"
+ "იმპორტი SIM-იდან ^1 - ^2 "
+ "იმპორტი SIM-იდან %1$s "
+ "იმპორტი .vcf ფაილიდან"
+ "გსურთ %s -ის იმპორტის გაუქმება?"
+ "გსურთ %s -ის ექსპორტის გაუქმება?"
+ "vCard-ის იმპორტი/ექსპორტი ვერ გაუქმდა"
+ "უცნობი შეცდომა."
+ "„%s “-ის გახსნა ვერ მოხერხდა: %s ."
+ "ექსპორტერის გაშვება ვერ მოხერხდა: „%s “."
+ "ექსპორტირებადი კონტაქტი არ არსებობს."
+ "თქვენ გათიშული გაქვთ საჭირო ნებართვა."
+ "შეცდომის გამო ექსპორტი ვერ მოხერხდა: „%s “."
+ "საჭირო ფაილის სახელი ძალიან გრძელია („%s “)"
+ "SD ბარათზე ძალიან ბევრი vCard ფაილია."
+ "I/O შეცდომა"
+ "მეხსიერება არასაკმარისია. შესაძლოა ფაილი ძალიან დიდია."
+ "vCard ფაილის გარჩევა ვერ მოხერხდა გაუთვალისწინებული მიზეზით."
+ "ფორმატი მხარდაუჭერელია."
+ "მოცემული vCard ფაილ(ებ)ის მეტა ინფორმაციის შეგროვება ვერ მოხერხდა."
+ "ერთი ან მეტი ფაილის იმპორტი ვერ მოხერხდა (%s)."
+ "%s -ის ექსპორტი დასრულდა."
+ "კონტაქტების ექსპორტი დასრულდა."
+ "%s -ის ექსპორტი გაუქმდა."
+ "მიმდინარეობს კონტაქტების მონაცემების ექსპორტი"
+ "მიმდინარეობს თქვენი კონტაქტების მონაცემების ექსპორტი %s -ში."
+ "მონაცემთა ბაზის შესახებ ინფორმაციის მიღება ვერ მოხერხდა."
+ "ექსპორტირებადი კონტაქტები არ მოიპოვება. თუ კონტაქტები ნამდვილად არის თქვენს ტელეფონში, შესაძლოა ზოგიერთი მონაცემთა პროვაიდერი არ იძლევა ტელეფონიდან მათი ექსპორტის უფლებას."
+ "vCard ფაილის კომპოზიტორი გაშვებულია არასწორად."
+ "ექსპორტი ჩაიშალა."
+ "კონტაქტების მონაცემები არ არის ექსპორტირებული.\nმიზეზი: „%s “"
+ "იმპორტირდება %s "
+ "vCard მონაცემთა წაკითხვა ვერ მოხერხდა"
+ "vCard მონაცემთა წაკითხვა გაუქმდა"
+ "vCard ფაილის %s იმპორტი დასრულდა"
+ "%s -ის იმპორტი გაუქმდა"
+ "%s მალე იმპორტირდება."
+ "ეს ფაილი მალე იმპორტირდება."
+ "vCard-ის იმპორტის მოთხოვნა უარყოფილია. სცადეთ მოგვიანებით."
+ "%s მალე ექსპორტირდება."
+ "ამ ფაილის ექსპორტი მალე შესრულდება."
+ "vCard-ის ექსპორტის მოთხოვნა უარყოფილია. სცადეთ მოგვიანებით."
+ "კონტაქტი"
+ "მიმდინარეობს vCard ფაილ(ებ)ის ქეშირება ადგილობრივ დროებით მეხსიერებაში. ფაქტიური იმპორტი დაიწყება მალე."
+ "vCard-ის იმპორტი ვერ მოხერხდა."
+ "SD ბარათზე vCard ფაილი ვერ მოიძებნა."
+ "კონტაქტი NFC-ით"
+ "გსურთ კონტაქტების ექსპორტი?"
+ "ქეშირება"
+ "SD ბარათის სკანირება ვერ მოხერხდა. (მიზეზი: „%s “)"
+ "იმპორტირდება %s /%s : %s "
+ "ექსპორტი .vcf ფაილში"
+ "სორტირება:"
+ "სახელი"
+ "გვარი"
+ "სახელის ფორმატი"
+ "ჯერ სახელით"
+ "ჯერ გვარით"
+ "ხილული კონტაქტების გაზიარება"
+ "ხილული კონტაქტების გაზიარება ვერ მოხერხდა."
+ "კონტაქტების იმპორტი/ექსპორტი"
+ "კონტაქტების იმპორტი"
+ "ამ კონტაქტის გაზიარება შეუძლებელია."
+ "ძიება"
+ "კონტაქტები საჩვენებლად"
+ "კონტაქტები საჩვენებლად"
+ "მორგებული ხედის განსაზღვრა"
+ "კონტაქტების პოვნა"
+ "რჩეულები"
+ "კონტაქტები არ არის."
+ "ხილული კონტაქტები არ არის."
+ "რჩეულები არ არის."
+ "%s -ში კონტაქტები არ არის"
+ "ხშირი კონტაქტების წაშლა"
+ "აირჩიეთ SIM ბარათი"
+ "ანგარიშები"
+ "იმპორტი/ექსპორტი"
+ "%1$s -ის მეშვეობით"
+ "%1$s , %2$s -ის მეშვეობით"
+ "ძიების შეჩერება"
+ "ძიების გასუფთავება"
+ "კონტაქტის ჩვენების ვარიანტები"
+ "ანგარიში"
+ "ზარებისთვის მუდამ ამის გამოყენება"
+ "დარეკვა ანგარიშით:"
+ "ზარი შენიშვნასთან ერთად"
+ "აკრიფეთ შენიშვნა ზართან ერთად გასაგზავნად ..."
+ "გაგზავნა & დარეკვა"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-kk-rKZ/strings.xml b/ContactsCommon/res/values-kk-rKZ/strings.xml
new file mode 100644
index 0000000..181569c
--- /dev/null
+++ b/ContactsCommon/res/values-kk-rKZ/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Мәтін көшірілді"
+ "Аралық сақтағышқа көшіру"
+ "%s нөміріне қоңырау шалу"
+ "Үйге қоңырау шалу"
+ "Ұялы телефонға қоңырау шалу"
+ "Жұмысқа қоңырау шалу"
+ "Жұмыс факсіне қоңырау шалу"
+ "Үй факсіне қоңырау шалу"
+ "Пэйджерге қоңырау шалу"
+ "Қоңырау шалу"
+ "Кері қоңырау шалу нөміріне қоңырау шалу"
+ "Автокөлікке қоңырау шалу"
+ "Компанияның негізгі нөміріне қоңырау шалу"
+ "ISDN нөміріне қоңырау шалу"
+ "Негізгі нөмірге қоңырау шалу"
+ "Факске қоңырау шалу"
+ "Радиоға қоңырау шалу"
+ "Телекске қоңырау шалу"
+ "Есту қабілеті төмен адамдарға арналған телетайпқа қоңырау шалу"
+ "Жұмыс ұялы телефонына қоңырау шалу"
+ "Жұмыс пэйджеріне қоңырау шалу"
+ "%s нөміріне қоңырау соғу"
+ "MMS нөміріне қоңырау шалу"
+ "%s мәтін-хабар жіберу"
+ "Үй телефонына мәтін-хабар жіберу"
+ "Ұялы телефонған мәтін-хабар жіберу"
+ "Жұмыс нөміріне мәтін-хабар жіберу"
+ "Жұмыс факсіне мәтін-хабар жіберу"
+ "Үй факсіне мәтін-хабар жіберу"
+ "Пейджерге мәтін-хабар жіберу"
+ "Мәтін-хабар"
+ "Кері қоңырау шалу нөміріне мәтін-хабар жіберу"
+ "Автокөлікке мәтін-хабар жіберу"
+ "Негізгі компанияға мәтін-хабар жіберу"
+ "ISDN нөміріне мәтін-хабар жіберу"
+ "Негізгі нөмірге мәтін-хабар жіберу"
+ "Факске мәтін-хабар жіберу"
+ "Радиоға мәтін-хабр жіберу"
+ "Tелетайпқа мәтін-хабар жіберу"
+ "Есту қабілеті төмен адамдарға арналған телетайпқа мәтін-хабар жіберу"
+ "Жұмыс ұялы телефонына мәтін-хабар жіберу"
+ "Жұмыс пейджеріне мәтін-хабар жіберу"
+ "%s нөміріне мәтін-хабар жіберу"
+ "MMS нөміріне мәтін-хабар жіберу"
+ "Бейне қоңырау шалу"
+ "Жиі қолданылғандар тазартылсын ба?"
+ "Контактілер және Телефондар қолданбаларындағы жиі хабарласатындар тізімі тазаланады және электрондық пошта қолданбалары мекенжай параметрлерін басынан үйренуге мәжбүрленеді."
+ "Жиі қолданылғандар тазартылуда…"
+ "Қол жетімді"
+ "Желіден тыс"
+ "Бос емес"
+ "Контактілер"
+ "Басқа"
+ "Анықтама"
+ "Барлық контактілер"
+ "Мен"
+ "Іздеуде..."
+ "%d көбірек табылды."
+ "Ешқандай контактілер жоқ"
+
+ %d табылды
+ - 1 табылды
+
+ "%1$s үшін жылдам байланыс"
+ "(Атаусыз)"
+ "Қоңырау жиі шалынғандар"
+ "Жиі қолданылғандар"
+ "Контактіні көру"
+ "Телефон нөмірі бар барлық контактілер"
+ "Жаңартуларды көру"
+ "Телефон ғана, синхрондалмайды"
+ "Аты-жөні"
+ "Қысқа аты"
+ "Аты-жөні"
+ "Аты"
+ "Тегі"
+ "Аттың префиксі"
+ "Әкесінің аты"
+ "Аттың суффиксі"
+ "Фонетикалық есімі"
+ "Фонетикалық аты"
+ "Әкесінің фонетикалық аты"
+ "Фонетикалық тегі"
+ "Телефон"
+ "Э-пошта"
+ "Мекенжай"
+ "IM"
+ "Ұйым"
+ "Қарым-қатынас"
+ "Ерекше күндер"
+ "Мәтін-хабар"
+ "Мекенжай"
+ "Компания"
+ "Атауы"
+ "Ескертулер"
+ "SIP"
+ "Веб-сайт"
+ "Топтар"
+ "Үй э-поштасына хат жіберу"
+ "Ұялы телефонға хат жіберу"
+ "Жұмысқа хат жіберу"
+ "Хат жіберу"
+ "%s мекенжайына хат жіберу"
+ "Э-пошта"
+ "Көше"
+ "Пошта жәшігі"
+ "Аудан"
+ "Қала"
+ "Штат"
+ "Индекс"
+ "Ел"
+ "Үй мекенжайын көру"
+ "Жұмыс мекенжайын көру"
+ "Мекенжайын көру"
+ "%s мекенжайын көру"
+ "AIM қолданып чаттасу"
+ "Windows Live қолданып чаттасу"
+ "Yahoo қолданып чаттасу"
+ "Skype қолданып чаттасу"
+ "QQ қолданып чаттасу"
+ "Google Talk функциясы арқылы чаттасу"
+ "ICQ қолданып чаттасу"
+ "Jabber қолданып чаттасу"
+ "Чаттасу"
+ "жою"
+ "Ат аймақтарын кеңейту немесе қирату"
+ "Барлық контактілер"
+ "Жұлдызшалы"
+ "Қалау бойынша реттеу"
+ "Контакт"
+ "Барлық басқа топтар"
+ "Барлық контактілер"
+ "Синх тобын алып тастау"
+ "Синх тобын қосу"
+ "Басқа топтар..."
+ "\"%s \" синрондаудан алу топталмаған контактілердің синхрондаудан алынуына себеп болады."
+ "Дисплей опцияларын сақтау…"
+ "Дайын"
+ "Өшіру"
+ "%s ішіндегі контактілер"
+ "Контактілердің жеке көрінісі"
+ "Жалғыз контакт"
+ "Есептік жазба арқылы контакт жасақтау"
+ "SIM картадан импорттау"
+ "^1 – ^2 SIM картасынан импорттау"
+ "%1$s SIM картасынан импорттау"
+ ".vcf файлынан импорттау"
+ "%s импорттау тоқтатылсын ба?"
+ "%s экспорттау тоқтатылсын ба?"
+ "vКартасын импорттау/экспорттауды тоқтату мүмкін болмады"
+ "Белгісіз қателік."
+ "\"%s \" файлын аша алмады: %s ."
+ "Экспорттаушыны бастау мүмкін болмады: \"%s \"."
+ "Экспортталатын контакт жоқ."
+ "Сіз міндетті рұқсатты өшірдіңіз."
+ "Экспорттау кезінде қателік орын алды: \"%s \"."
+ "Қажетті файл атауы тым ұзақ (\"%s \")."
+ "SD картада тым көп vКарта файлдары."
+ "I/O қателігі"
+ "Жад жеткіліксіз. Файл тым үлкен болуы мүмкін."
+ "vКартасын талдау күтпеген себеппен мүмкін болмады."
+ "Форматтың қолдауы жоқ."
+ "Осы vКарта файлдары туралы мета ақпарат жинай алмады."
+ "Бір немесе бірнеше карталарды импорттау мүмкін болмады (%s)."
+ "%s экспорттау аяқталды."
+ "Контактілер экспортталып болды."
+ "%s экспорттау тоқтатылды."
+ "Контакт дерекқор экспортталуда"
+ "Контакт дерекқорыңыз келесі файлға экспортталуда: %s ."
+ "Дерекқор ақпаратын ала алмады."
+ "Экспорттауға болатын контактілер жоқ. Телефоныңызда контактілер болса, кейбір дерекқор жадбықтаушылар контактілердің телефоннан экспортталуына рұқсат бермейді."
+ "vКарта жасақтаушы дұрыс басталмады."
+ "Экспорттай алмады"
+ "Контакт деректері экспортталмады. \nСебебі: \"%s \""
+ "%s импортталуда"
+ "vКарта дерекқорын оқи алмады"
+ "vКарта дерекқорын оқу тоқтатылды"
+ "%s vКарта файлын импорттау аяқталды"
+ "%s импорттау тоқтатылды"
+ "%s жуық арада импортталады."
+ "Файл жуық арада импортталады."
+ "vКартасын импорттау өтініші қабылданбады. Кейінірек қайта әрекеттеніп көріңіз."
+ "%s жуық арада экспортталады."
+ "Файл қысқа уақыттан кейін экспортталады."
+ "vКартасын экспорттау өтініші қабылданбады. Кейінірек қайта әрекеттеніп көріңіз."
+ "контакт"
+ "vКарталарын жергілікті уақытша жадқа кэштеу. Импорттау жуық арада басталады."
+ "vКартасын экспорттау мүмкін болмады."
+ "SD картада ешқандай vCard файлы жоқ."
+ "ЖӨБ контактісі"
+ "Контактілер экспортталсын ба?"
+ "Кэштеу"
+ "SD картасын скандау мүмкін болмады. (Себеп: \"%s \")"
+ "%s /%s импорттауда: %s "
+ ".vcf файлына экспорттау"
+ "Сұрыптау шарты"
+ "Аты"
+ "Тегі"
+ "Ат пішімі"
+ "Алдымен аты"
+ "Алдымен тегі"
+ "Көрінетін контактілерді бөлісу"
+ "Көрінетін контактілерді бөлісу сәтсіз аяқталды."
+ "Контактілерді импорттау/экспорттау"
+ "Контактілерді импорттау"
+ "Бұл контактіні бөлісу мүмкін болмады"
+ "Іздеу"
+ "Көрсетілетін контактілер"
+ "Көрсетілетін контактілер"
+ "Қалаулы көріністі анықтау"
+ "Контактілерді табу"
+ "Сүйіктілер"
+ "Ешқандай контактілер жоқ."
+ "Ешқандай көрінетін контактілер жоқ."
+ "Таңдаулылар жоқ."
+ "%s контактілер жоқ"
+ "Жиі қолданылғандары өшіру"
+ "SIM картасын таңдау"
+ "Есептік жазбалар"
+ "Импорттау/экспорттау"
+ "%1$s арқылы"
+ "%1$s дерегі %2$s арқылы"
+ "іздеуді тоқтату"
+ "Іздеуді тазалау"
+ "Контактілерді көрсету опциялары"
+ "Есептік жазба"
+ "Осыны қоңыраулар үшін әрқашан пайд."
+ "Келесімен қоңырау шалу"
+ "Ескертпе бар қоңырау"
+ "Қоңыраумен жіберу үшін ескертпе теріңіз ..."
+ "ЖІБЕРУ ЖӘНЕ ҚОҢЫРАУ СОҒУ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-km-rKH/strings.xml b/ContactsCommon/res/values-km-rKH/strings.xml
new file mode 100644
index 0000000..0fac10a
--- /dev/null
+++ b/ContactsCommon/res/values-km-rKH/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "បានចម្លងអត្ថបទ"
+ "ចម្លងទៅក្ដារតម្បៀតខ្ទាស់?"
+ "ហៅ %s "
+ "ហៅទៅផ្ទះ"
+ "ហៅទៅទូរស័ព្ទចល័ត"
+ "ហៅទៅកន្លែងធ្វើការ"
+ "ហៅទៅទូរសារកន្លែងធ្វើការ"
+ "ហៅទៅទូរសារផ្ទះ"
+ "ហៅទៅលេខភេយ័រ"
+ "ហៅ"
+ "ហៅទៅលេខហៅទៅវិញ"
+ "ហៅទៅទូរស័ព្ទរថយន្ត"
+ "ហៅទៅលេខសំខាន់របស់ក្រុមហ៊ុន"
+ "ហៅទៅ ISDN"
+ "ហៅទៅលេខចម្បង"
+ "ហៅទៅទូរសារ"
+ "ហៅទៅលេខវិទ្យុ"
+ "ហៅទៅ telex"
+ "ហៅទៅ TTY/TDD"
+ "ហៅទៅទូរស័ព្ទចល័តកន្លែងធ្វើការ"
+ "ហៅទៅភេយ័រកន្លែងធ្វើការ"
+ "ហៅទៅ %s "
+ "ហៅ MMS"
+ "អត្ថបទ %s "
+ "សារទៅផ្ទះ"
+ "អត្ថបទទូរស័ព្ទចល័ត"
+ "អត្ថបទការងារ"
+ "សារទូរសារកន្លែងធ្វើការ"
+ "អត្ថបទទូរសារផ្ទះ"
+ "អត្ថបទភេយ័រ"
+ "អត្ថបទ"
+ "អត្ថបទហៅទៅវិញ"
+ "អត្ថបទទៅរថយន្ត"
+ "អត្ថបទចម្បងរបស់ក្រុមហ៊ុន"
+ "អត្ថបទ ISDN"
+ "សារចម្បង"
+ "អត្ថបទទូរសារ"
+ "អត្ថបទវិទ្យុ"
+ "អត្ថបទ telex"
+ "អត្ថបទ TTY/TDD"
+ "អត្ថបទទូរស័ព្ទចល័តកន្លែងធ្វើការ"
+ "អត្ថបទភេយ័រកន្លែងធ្វើការ"
+ "អត្ថបទ %s "
+ "អត្ថបទ MMS"
+ "ហៅជាវីដេអូ"
+ "សម្អាតទំនាក់ទំនងញឹកញាប់?"
+ "អ្នកនឹងជម្រះបញ្ជីដែលអ្នកទាក់ទងជាញឹកញាប់នៅក្នុងកម្មវិធីទូរស័ព្ទ និងទំនាក់ទំនង ហើយបង្ខំឲ្យកម្មវិធីអ៊ីមែលស្វែងយល់ពីចំណូលចិត្តទំនាក់ទំនងរបស់អ្នកតាំងពីដំបូង។"
+ "សម្អាតទំនាក់ទំនងញឹកញាប់..."
+ "ទំនេរ"
+ "ចាកឆ្ងាយ"
+ "រវល់"
+ "ទំនាក់ទំនង"
+ "ផ្សេងៗ"
+ "ថត"
+ "ទំនាក់ទំនងទាំងអស់"
+ "ខ្ញុំ"
+ "កំពុងរក..."
+ "បានរកឃើញច្រើនជាង %d ។"
+ "មិនមានទំនាក់ទំនង។"
+
+ - បានរកឃើញ
%d
+ - បានរកឃើញ 1
+
+ "ទំនាក់ទំនងរហ័សសម្រាប់ %1$s "
+ "(គ្មានឈ្មោះ)"
+ "បានហៅញឹកញាប់"
+ "បានទាក់ទងញឹកញាប់"
+ "មើលទំនាក់ទំនង"
+ "ទំនាក់ទំនងទាំងអស់ដែលមានលេខទូរស័ព្ទ"
+ "មើលបច្ចុប្បន្នភាព"
+ "តែទូរស័ព្ទ, មិនបានធ្វើសមកាលកម្ម"
+ "ឈ្មោះ"
+ "ឈ្មោះហៅក្រៅ"
+ "ឈ្មោះ"
+ "នាមខ្លួន"
+ "នាមត្រកូល"
+ "បុព្វបទឈ្មោះ"
+ "ឈ្មោះកណ្ដាល"
+ "បច្ច័យឈ្មោះ"
+ "សូរស័ព្ទឈ្មោះ"
+ "នាមខ្លួនតាមសូរស័ព្ទ"
+ "សូរស័ព្ទឈ្មោះកណ្ដាល"
+ "នាមត្រកូលតាមសូរស័ព្ទ"
+ "ទូរស័ព្ទ"
+ "អ៊ីមែល"
+ "អាសយដ្ឋាន"
+ "IM"
+ "ស្ថាប័ន"
+ "ចំណងទាក់ទង"
+ "កាលបរិច្ឆេទពិសេស"
+ "សារអត្ថបទ"
+ "អាសយដ្ឋាន"
+ "ក្រុមហ៊ុន"
+ "មុខងារ"
+ "ចំណាំ"
+ "SIP"
+ "តំបន់បណ្ដាញ"
+ "ក្រុម"
+ "អ៊ីមែលទៅផ្ទះ"
+ "អ៊ីមែលទៅទូរស័ព្ទចល័ត"
+ "អ៊ីមែលទៅកន្លែងធ្វើការ"
+ "អ៊ីមែល"
+ "អ៊ីមែល %s "
+ "អ៊ីមែល"
+ "ផ្លូវ"
+ "ប្រអប់សំបុត្រ"
+ "អ្នកជិតខាង"
+ "ទីក្រុង"
+ "រដ្ឋ"
+ "លេខកូដតំបន់"
+ "ប្រទេស"
+ "មើលអាសយដ្ឋានផ្ទះ"
+ "មើលអាសយដ្ឋានការងារ"
+ "មើលអាសយដ្ឋាន"
+ "មើលអាសយដ្ឋាន %s "
+ "ជជែកដោយប្រើ AIM"
+ "ជជែកដោយប្រើ Windows Live"
+ "ជជែកដោយប្រើ Yahoo"
+ "ជជែកដោយប្រើ Skype"
+ "ជជែកដោយប្រើ QQ"
+ "ជជែកដោយប្រើ Google Talk"
+ "ជជែកដោយប្រើ ICQ"
+ "ជជែកដោយប្រើ Jabber"
+ "ជជែក"
+ "លុប"
+ "ពង្រីក ឬបង្រួមវាលឈ្មោះ"
+ "ទំនាក់ទំនងទាំងអស់"
+ "បានដាក់ផ្កាយ"
+ "ប្ដូរតាមតម្រូវការ"
+ "ទំនាក់ទំនង"
+ "ទំនាក់ទំនងទាំងអស់ផ្សេងទៀត"
+ "ទំនាក់ទំនងទាំងអស់"
+ "លុបក្រុមសមកាលកម្ម"
+ "បន្ថែមក្រុមសមកាលកម្ម"
+ "ក្រុមច្រើនទៀត..."
+ "ការលុប \"%s \" ចេញពីសមកាលកម្មវាក៏នឹងលុបទំនាក់ទំនងដែលមិននៅក្នុងក្រុមចេញពីសមកាលកម្មផងដែរ។"
+ "កំពុងរក្សាទុកជម្រើសបង្ហាញ..."
+ "រួចរាល់"
+ "បោះបង់"
+ "ទំនាក់ទំនងនៅក្នុង %s "
+ "ទំនាក់ទំនងក្នុងទិដ្ឋភាពផ្ទាល់ខ្លួន"
+ "ទំនាក់ទំនងទោល"
+ "បង្កើតទំនាក់ទំនងនៅក្នុងគណនី"
+ "នាំចូលពីស៊ីមកាត"
+ "នាំចូលពីស៊ីម ^1 - ^2 "
+ "នាំចូលពីស៊ីម %1$s "
+ "នាំចូូលពីឯកសារ .vcf"
+ "បោះបង់ការនាំចូល %s ?"
+ "បោះបង់ការនាំចេញ %s ?"
+ "មិនអាចបោះបង់ការនាំចេញ/នាំចូល vCard"
+ "មិនស្គាល់កំហុស។"
+ "មិនអាចបើក \"%s \"៖ %s ."
+ "មិនអាចចាប់ផ្ដើមកម្មវិធីនាំចេញ៖ \"%s \" ។"
+ "មិនមានទំនាក់ទំនងដើម្បីនាំចេញទេ។"
+ "អ្នកបានបិទសិទ្ធិអនុញ្ញាតដែលតម្រូវឲ្យមាន។"
+ "មានកំហុសកើតឡើងពេលនាំចេញ៖ \"%s \" ។"
+ "ឈ្មោះឯកសារដែលបានទាមទារគឺវែងពេក (\"%s \") ។"
+ "មានឯកសារ vCard ច្រើនពេកនៅក្នុងកាតអេសឌី។"
+ "កំហុស I/O"
+ "អង្គចងចាំមិនគ្រប់គ្រាន់ (ប្រហែលជាឯកសារធំពេក)"
+ "មិនអាចញែក vCard បានព្រោះហេតុផលមិនរំពឹងទុក។"
+ "មិនគាំទ្រទ្រង់ទ្រាយ។"
+ "មិនអាចប្រមូលព័ត៌មានមេតារបស់ឯកសារ vCard ដែលបានផ្ដល់។"
+ "មានឯកសារមួយ ឬច្រើនដែលមិនអាចនាំចូល (%s) បាន។"
+ "បានបញ្ចប់ការនាំចេញ %s ។"
+ "បានបញ្ចប់ការនាំចេញទំនាក់ទំនង។"
+ "បានបោះបង់ការនាំចេញ %s ។"
+ "កំពុងនាំចេញព័ត៌មានទំនាក់ទំនង"
+ "កំពុងនាំចេញព័ត៌មានទំនាក់ទំនងរបស់អ្នកទៅកាន់៖ %s ។"
+ "មិនអាចយកព័ត៌មានមូលដ្ឋានទិន្នន័យ។"
+ "មិនមានទំនាក់ទំនងដើម្បីនាំចេញទេ។ ប្រសិនបើអ្នកពិតជាមានទំនាក់ទំនងនៅក្នុងទូរស័ព្ទរបស់អ្នកមែន នោះទំនាក់ទំនងទាំងអស់អាចបានហាមមិនឲ្យនាំចេញដោយក្រុមហ៊ុនផ្ដល់ទិន្នន័យមួយចំនួន។"
+ "កម្មវិធីតែងរបស់ vCard មិនបានចាប់ផ្ដើមត្រឹមត្រូវទេ។"
+ "មិនអាចនាំចេញ"
+ "មិនបាននាំចេញព័ត៌មានទំនាក់ទំនង។\nមូលហេតុ៖ \"%s \""
+ "ការនាំចូល %s "
+ "មិនអាចអានទិន្នន័យ vCard"
+ "បានបោះបង់ការអានទិន្នន័យ vCard"
+ "បានបញ្ចប់ការនាំចូល vCard %s "
+ "បានបោះបង់ការនាំចូល %s "
+ "នឹងនាំចូល %s ក្នុងពេលឆាប់ៗ។"
+ "នឹងនាំចូលឯកសារក្នុងពេលឆាប់ៗ។"
+ "បានបដិសេធសំណើនាំចូល vCard ។ សូមព្យាយាមម្ដងទៀតនៅពេលក្រោយ។"
+ "នឹងនាំចេញ %s ក្នុងពេលឆាប់ៗ។"
+ "នឺងនាំចេញឯកសារក្នុងពេលឆាប់ៗ។"
+ "បានបដិសេធសំណើនាំចេញ vCard ។ សូមព្យាយាមម្ដងទៀតនៅពេលក្រោយ។"
+ "ទំនាក់ទំនង"
+ "ការផ្ទុក vCard(s) ទៅកាន់ឧបករណ៍ផ្ទុកបណ្ដោះអាសន្នមូលដ្ឋាន។ ការនាំចូលពិតប្រាកដនឹងចាប់ផ្ដើមក្នុងពេលឆាប់ៗ។"
+ "មិនអាចនាំចូល vCard ។"
+ "រកមិនឃើញឯកសារ vCard នៅលើកាតអេសឌី។"
+ "ទំនាក់ទំនងដែលបានទទួលតាម NFC"
+ "នាំចេញទំនាក់ទំនង?"
+ "ការផ្ទុកក្នុងឃ្លាំងសម្ងាត់"
+ "មិនអាចវិភាគរកកាតអេសឌី (មូលហេតុ៖ \"%s \")"
+ "កំពុងនាំចូល %s /%s ៖ %s "
+ "នាំចេញទៅឯកសារ .vcf"
+ "តម្រៀបតាម"
+ "នាមខ្លួន"
+ "នាមត្រកូល"
+ "ទ្រង់ទ្រាយឈ្មោះ"
+ "នាមខ្លួនមុន"
+ "នាមត្រកូលមុន"
+ "ចែករំលែកទំនាក់ទំនងដែលអាចមើលឃើញ"
+ "បានបរាជ័យក្នុងការចែករំលែកទំនាក់ទំនងដែលអាចមើលឃើញ។"
+ "នាំចេញ/នាំចូលទំនាក់ទំនង"
+ "នាំចូលទំនាក់ទំនង"
+ "ទំនាក់ទំនងនេះមិនអាចចែករំលែកបានទេ។"
+ "ស្វែងរក"
+ "ទំនាក់ទំនងដែលត្រូវបង្ហាញ"
+ "ទំនាក់ទំនងដែលត្រូវបង្ហាញ"
+ "កំណត់ទិដ្ឋភាពផ្ទាល់ខ្លួន"
+ "រកទំនាក់ទំនង"
+ "សំណព្វ"
+ "មិនមានទំនាក់ទំនង។"
+ "មើលមិនឃើញទំនាក់ទំនង។"
+ "គ្មានសំណព្វ។"
+ "មិនមានទំនាក់ទំនងនៅក្នុង %s "
+ "សម្អាតញឹកញាប់"
+ "ជ្រើសស៊ីមកាត"
+ "គណនី"
+ "នាំចេញ/នាំចូល"
+ "តាមរយៈ %1$s "
+ "%1$s តាមរយៈ %2$s "
+ "បញ្ឈប់ការស្វែងរក"
+ "សម្អាតការស្វែងរក"
+ "ជម្រើសបង្ហាញទំនាក់ទំនង"
+ "គណនី"
+ "ប្រើវាសម្រាប់ការហៅជានិច្ច"
+ "ហៅជាមួយ"
+ "ការហៅព្រមជាមួយចំណាំ"
+ "វាយបញ្ចូលចំណាំដែលត្រូវផ្ញើជាមួយការហៅទូរស័ព្ទ ..."
+ "ផ្ញើ & ហៅទូរស័ព្ទ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-kn-rIN/strings.xml b/ContactsCommon/res/values-kn-rIN/strings.xml
new file mode 100644
index 0000000..4c79839
--- /dev/null
+++ b/ContactsCommon/res/values-kn-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "ಪಠ್ಯವನ್ನು ನಕಲಿಸಲಾಗಿದೆ"
+ "ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಿ"
+ "%s ಕರೆ ಮಾಡಿ"
+ "ಮನೆಗೆ ಕರೆ ಮಾಡಿ"
+ "ಮೊಬೈಲ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ಕೆಲಸದ ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿ"
+ "ಕೆಲಸದ ಫ್ಯಾಕ್ಸ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ಮನೆಯ ಫ್ಯಾಕ್ಸ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ಪೇಜರ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ಕರೆ ಮಾಡು"
+ "ಕಾಲ್ಬ್ಯಾಕ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ಕಾರ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ಕಂಪನಿಯ ಪ್ರಮುಖ ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿ"
+ "ISDN ಕರೆ ಮಾಡಿ"
+ "ಮುಖ್ಯ ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿ"
+ "ಫ್ಯಾಕ್ಸ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ರೇಡಿಯೋಗೆ ಕರೆ ಮಾಡಿ"
+ "ಟೆಲೆಕ್ಸ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "TTY/TDD ಕರೆ ಮಾಡಿ"
+ "ಕೆಲಸದ ಮೊಬೈಲ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "ಕೆಲಸದ ಪೇಜರ್ಗೆ ಕರೆ ಮಾಡಿ"
+ "%s ಕರೆ ಮಾಡಿ"
+ "MMS ಕರೆ ಮಾಡಿ"
+ "%s ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ನಿವಾಸಕ್ಕೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಮೊಬೈಲ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಕೆಲಸದ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಕೆಲಸದ ಫ್ಯಾಕ್ಸ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಮನೆಯ ಫ್ಯಾಕ್ಸ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಪೇಜರ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಪಠ್ಯ"
+ "ಕಾಲ್ಬ್ಯಾಕ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಕಾರ್ನ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಕಂಪನಿಯ ಮುಖ್ಯ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ISDN ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಮುಖ್ಯ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಫ್ಯಾಕ್ಸ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ರೇಡಿಯೋ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಟೆಲೆಕ್ಸ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "TTY/TDD ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಕೆಲಸದದ ಮೊಬೈಲ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "ಕೆಲಸದ ಪೇಜರ್ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "%s ಗೆ ಸಂದೇಶ ಮಾಡಿ"
+ "MMS ಸಂದೇಶ ಮಾಡಿ"
+ "ವೀಡಿಯೊ ಕರೆ ಮಾಡಿ"
+ "ಪದೇ ಪದೇ ಸಂಪರ್ಕಿಸಿರುವುದನ್ನು ತೆರುವುಗೊಳಿಸುವುದೇ?"
+ "ಸಂಪರ್ಕಗಳು ಮತ್ತು ಫೋನ್ ಅಪ್ಲಿಕೇಶನ್ಗಳಲ್ಲಿ ಪದೇ ಪದೇ ಸಂಪರ್ಕಪಡಿಸಿರುವ ಪಟ್ಟಿಯನ್ನು ನೀವು ತೆರುವುಗೊಳಿಸುತ್ತೀರಿ ಮತ್ತು ಮೊದಲಿನಿಂದ ನಿಮ್ಮ ವಿಳಾಸ ಪ್ರಾಶಸ್ತ್ಯಗಳನ್ನು ತಿಳಿಯಲು ಇಮೇಲ್ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಒತ್ತಾಯಿಸಿ."
+ "ಪದೇ ಪದೇ ಸಂಪರ್ಕಿಸಿರುವುದನ್ನು ತೆರುವುಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"
+ "ಲಭ್ಯವಿದೆ"
+ "ದೂರ"
+ "ಕಾರ್ಯನಿರತ"
+ "ಸಂಪರ್ಕಗಳು"
+ "ಇತರೆ"
+ "ಡೈರೆಕ್ಟರಿ"
+ "ಎಲ್ಲಾ ಸಂಪರ್ಕಗಳು"
+ "ನಾನು"
+ "ಹುಡುಕಲಾಗುತ್ತಿದೆ…"
+ "%d ಗಿಂತ ಹೆಚ್ಚಾಗಿ ಕಂಡುಬಂದಿದೆ."
+ "ಯಾವುದೇ ಸಂಪರ್ಕಗಳಿಲ್ಲ"
+
+ %d ಕಂಡುಬಂದಿದೆ
+ %d ಕಂಡುಬಂದಿದೆ
+
+ "%1$s ಇವರಿಗಾಗಿ ತಕ್ಷಣದ ಸಂಪರ್ಕ"
+ "(ಯಾವುದೇ ಹೆಸರಿಲ್ಲ)"
+ "ಪದೇ ಪದೇ ಕರೆ ಮಾಡಲಾಗಿದೆ"
+ "ಪದೇ ಪದೇ ಸಂಪರ್ಕಿಸಿರುವುದು"
+ "ಸಂಪರ್ಕವನ್ನು ವೀಕ್ಷಿಸಿ"
+ "ಫೋನ್ ಸಂಖ್ಯೆಗಳೊಂದಿಗೆ ಎಲ್ಲ ಸಂಪರ್ಕಗಳು"
+ "ನವೀಕರಣಗಳನ್ನು ವೀಕ್ಷಿಸಿ"
+ "ಫೋನ್ ಮಾತ್ರ, ಅನ್ಸಿಂಕ್ ಮಾಡಲಾಗಿದೆ"
+ "ಹೆಸರು"
+ "ಅಡ್ಡಹೆಸರು"
+ "ಹೆಸರು"
+ "ಮೊದಲ ಹೆಸರು"
+ "ಕೊನೆಯ ಹೆಸರು"
+ "ಹೆಸರಿನ ಪೂರ್ವಪ್ರತ್ಯಯ"
+ "ಮಧ್ಯದ ಹೆಸರು"
+ "ಹೆಸರಿನ ಪ್ರತ್ಯಯ"
+ "ಫೋನೆಟಿಕ್ ಹೆಸರು"
+ "ಫೋನೆಟಿಕ್ ಮೊದಲ ಹೆಸರು"
+ "ಫೋನೆಟಿಕ್ ಮಧ್ಯ ಹೆಸರು"
+ "ಫೋನೆಟಿಕ್ ಕೊನೆಯ ಹೆಸರು"
+ "ಫೋನ್"
+ "ಇಮೇಲ್"
+ "ವಿಳಾಸ"
+ "IM"
+ "ಸಂಘಟನೆ"
+ "ಸಂಬಂಧ"
+ "ವಿಶೇಷ ದಿನಾಂಕಗಳು"
+ "ಪಠ್ಯ ಸಂದೇಶ"
+ "ವಿಳಾಸ"
+ "ಕಂಪನಿ"
+ "ಶೀರ್ಷಿಕೆ"
+ "ಟಿಪ್ಪಣಿಗಳು"
+ "SIP"
+ "ವೆಬ್ಸೈಟ್"
+ "ಗುಂಪುಗಳು"
+ "ಮನೆಗೆ ಇಮೇಲ್ ಮಾಡಿ"
+ "ಮೊಬೈಲ್ಗೆ ಇಮೇಲ್ ಮಾಡಿ"
+ "ಕೆಲಸದ ವಿಳಾಸಕ್ಕೆ ಇಮೇಲ್ ಮಾಡಿ"
+ "ಇಮೇಲ್"
+ "%s ಇಮೇಲ್ ಮಾಡಿ"
+ "ಇಮೇಲ್"
+ "ಗಲ್ಲಿ"
+ "PO ಬಾಕ್ಸ್"
+ "ನೆರೆಹೊರೆ"
+ "ನಗರ"
+ "ರಾಜ್ಯ"
+ "ಪಿನ್ ಕೋಡ್"
+ "ರಾಷ್ಟ್ರ"
+ "ಮನೆಯ ವಿಳಾಸವನ್ನು ವೀಕ್ಷಿಸಿ"
+ "ಕೆಲಸದ ವಿಳಾಸವನ್ನು ವೀಕ್ಷಿಸಿ"
+ "ವಿಳಾಸವನ್ನು ವೀಕ್ಷಿಸಿ"
+ "%s ವಿಳಾಸವನ್ನು ವೀಕ್ಷಿಸಿ"
+ "AIM ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "Windows Live ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "Yahoo ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "Skype ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "QQ ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "Google Talk ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "ICQ ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "Jabber ಬಳಸಿಕೊಂಡು ಚಾಟ್ ಮಾಡಿ"
+ "ಚಾಟ್"
+ "ಅಳಿಸಿ"
+ "ಹೆಸರಿನ ಕ್ಷೇತ್ರಗಳನ್ನು ವಿಸ್ತರಿಸಿ ಅಥವಾ ಸಂಕುಚಿಸಿ"
+ "ಎಲ್ಲಾ ಸಂಪರ್ಕಗಳು"
+ "ನಕ್ಷತ್ರ ಹಾಕಿರುವುದು"
+ "ಕಸ್ಟಮೈಜ್"
+ "ಸಂಪರ್ಕ"
+ "ಇತರ ಎಲ್ಲ ಸಂಪರ್ಕಗಳು"
+ "ಎಲ್ಲಾ ಸಂಪರ್ಕಗಳು"
+ "ಸಿಂಕ್ ಗುಂಪನ್ನು ತೆಗೆದುಹಾಕಿ"
+ "ಸಿಂಕ್ ಗುಂಪನ್ನು ಸೇರಿಸಿ"
+ "ಇನ್ನಷ್ಟು ಗುಂಪುಗಳು…"
+ "ಸಿಂಕ್ನಿಂದ \"%s \" ಅನ್ನು ತೆಗೆದುಹಾಕುವುದರಿಂದ ಸಿಂಕ್ನಿಂದ ಯಾವುದೇ ಗುಂಪು ಮಾಡದಿರುವ ಸಂಪರ್ಕಗಳನ್ನು ಸಹ ತೆಗೆದುಹಾಕುತ್ತದೆ."
+ "ಪ್ರದರ್ಶನ ಆಯ್ಕೆಗಳನ್ನು ಉಳಿಸಲಾಗುತ್ತಿದೆ..."
+ "ಮುಗಿದಿದೆ"
+ "ರದ್ದುಮಾಡು"
+ "%s ನಲ್ಲಿ ಸಂಪರ್ಕಗಳು"
+ "ಕಸ್ಟಮ್ ವೀಕ್ಷಣೆಯಲ್ಲಿನ ಸಂಪರ್ಕಗಳು"
+ "ಏಕೈಕ ಸಂಪರ್ಕ"
+ "ಖಾತೆಯ ಅಡಿಯಲ್ಲಿ ಸಂಪರ್ಕವನ್ನು ರಚಿಸಿ"
+ "ಸಿಮ್ ಕಾರ್ಡ್ನಿಂದ ಆಮದು ಮಾಡಿ"
+ "^1 - ^2 SIM ನಿಂದ ಆಮದು ಮಾಡಿ"
+ "%1$s SIM ನಿಂದ ಆಮದು ಮಾಡಿ"
+ ".vcf ಫೈಲ್ನಿಂದ ಆಮದು ಮಾಡಿ"
+ "%s ಆಮದು ಮಾಡುವುದನ್ನು ರದ್ದುಗೊಳಿಸುವುದೇ?"
+ "%s ಅನ್ನು ರಫ್ತು ಮಾಡುವುದನ್ನು ರದ್ದುಗೊಳಿಸುವುದೇ?"
+ "vCard ಆಮದು/ರಫ್ತು ಮಾಡುವುದನ್ನು ರದ್ದುಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"
+ "ಅಜ್ಞಾತ ದೋಷ."
+ "\"%s \" ಅನ್ನು ತೆರೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ: %s ."
+ "ರಫ್ತುದಾರರನ್ನು ಪ್ರಾರಂಭಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ: \"%s \"."
+ "ಯಾವುದೇ ರಫ್ತುಮಾಡಬಹುದಾದ ಸಂಪರ್ಕವಿಲ್ಲ."
+ "ನೀವು ಅಗತ್ಯವಿರುವ ಅನುಮತಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿರುವಿರಿ."
+ "ರಫ್ತು ಮಾಡುವ ಸಂದರ್ಭದಲ್ಲಿ ದೋಷವೊಂದು ಕಂಡುಬಂದಿದೆ: \"%s \"."
+ "ಅಗತ್ಯವಿರುವ ಫೈಲ್ ಹೆಸರು ತುಂಬಾ ದೊಡ್ಡದಾಗಿದೆ (\"%s \")."
+ "SD ಕಾರ್ಡ್ನಲ್ಲಿ ಹಲವಾರು vCard ಫೈಲ್ಗಳಿವೆ."
+ "I/O ದೋಷ"
+ "ಸಾಕಷ್ಟು ಮೆಮೊರಿ ಇಲ್ಲ. ಫೈಲ್ ತುಂಬಾ ದೊಡ್ಡದಾಗಿರಬಹುದು."
+ "ಅನಿರೀಕ್ಷಿತ ಕಾರಣದಿಂದಾಗಿ vCard ಪಾರ್ಸ್ ಮಾಡಲಾಗಲಿಲ್ಲ."
+ "ಸ್ವರೂಪಕ್ಕೆ ಬೆಂಬಲವಿಲ್ಲ."
+ "ನೀಡಿದ vCard ಫೈಲ್(ಗಳ) ಮೆಟಾ ಮಾಹಿತಿಯನ್ನು ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."
+ "ಒಂದು ಅಥವಾ ಹೆಚ್ಚಿನ ಫೈಲ್ಗಳನ್ನು ಆಮದು ಮಾಡಲಾಗುವುದಿಲ್ಲ (%s)."
+ "%s ರಫ್ತು ಮುಗಿದಿದೆ."
+ "ಸಂಪರ್ಕಗಳ ರಫ್ತು ಮಾಡುವಿಕೆ ಮುಗಿದಿದೆ."
+ "%s ರಫ್ತು ಮಾಡುವುದನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ."
+ "ಸಂಪರ್ಕ ಡೇಟಾವನ್ನು ರಫ್ತುಮಾಡಲಾಗುತ್ತಿದೆ"
+ "ನಿಮ್ಮ ಸಂಪರ್ಕ ಡೇಟಾವನ್ನು ಇದಕ್ಕೆ ರಫ್ತು ಮಾಡಲಾಗುತ್ತಿದೆ: %s ."
+ "ಡೇಟಾಬೇಸ್ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲಾಗಲಿಲ್ಲ."
+ "ಯಾವುದೇ ರಫ್ತುಮಾಡಿದ ಸಂಪರ್ಕಗಳಿಲ್ಲ. ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ನೀವು ಸಂಪರ್ಕಗಳನ್ನು ಹೊಂದಿದ್ದರೆ, ಫೋನ್ನಿಂದ ರಫ್ತು ಮಾಡಲಿರುವ ಸಂಪರ್ಕಗಳಿಗೆ ಕೆಲವು ಡೇಟಾ ಪೂರೈಕೆದಾರರು ಅನುಮತಿ ನೀಡದಿರಬಹುದು."
+ "vCard ಸಂಯೋಜಕ ಸರಿಯಾಗಿ ಪ್ರಾರಂಭವಾಗಿಲ್ಲ."
+ "ರಫ್ತು ಮಾಡಲಾಗುವುದಿಲ್ಲ"
+ "ಸಂಪರ್ಕ ಡೇಟಾವನ್ನು ರಫ್ತು ಮಾಡಲಿಲ್ಲ.\nಕಾರಣ: \"%s \""
+ "%s ಆಮದು ಮಾಡಲಾಗುತ್ತಿದೆ"
+ "vCard ಡೇಟಾವನ್ನು ಓದಲಾಗಲಿಲ್ಲ"
+ "vCard ಡೇಟಾ ಓದುವಿಕೆ ರದ್ದಗೊಳಿಸಲಾಗಿದೆ"
+ "%s vCard ಆಮದು ಮುಕ್ತಾಯಗೊಂಡಿದೆ"
+ "%s ಆಮದು ರದ್ದುಪಡಿಸಲಾಗಿದೆ"
+ "%s ಅನ್ನು ಸ್ವಲ್ಪ ಸಮಯದಲ್ಲಿ ಆಮದು ಮಾಡಲಾಗುತ್ತದೆ."
+ "ಫೈಲ್ ಅನ್ನು ಸ್ವಲ್ಪ ಸಮಯದಲ್ಲಿ ಆಮದು ಮಾಡಲಾಗುತ್ತದೆ."
+ "vCard ಆಮದು ವಿನಂತಿಯನ್ನು ತಿರಸ್ಕರಿಸಲಾಗಿದೆ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."
+ "%s ಸ್ವಲ್ಪ ಸಮಯದಲ್ಲಿ ರಫ್ತು ಮಾಡಲಾಗುತ್ತದೆ."
+ "ಫೈಲ್ ಅನ್ನು ಸ್ವಲ್ಪ ಸಮಯದಲ್ಲಿ ರಪ್ತು ಮಾಡಲಾಗುತ್ತದೆ."
+ "vCard ರಫ್ತು ವಿನಂತಿಯನ್ನು ತಿರಸ್ಕರಿಸಲಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."
+ "ಸಂಪರ್ಕ"
+ "vCard(ಗಳು) ಅನ್ನು ಸ್ಥಳೀಯ ತಾತ್ಕಾಲಿಕ ಸಂಗ್ರಹಣೆಗೆ ಸಂಗ್ರಹ ಮಾಡಲಾಗುತ್ತಿದೆ. ನಿಜವಾದ ಆಮದು ಶೀಘ್ರದಲ್ಲೇ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ."
+ "vCard ಆಮದು ಮಾಡಲಾಗಿಲ್ಲ."
+ "SD ಕಾರ್ಡ್ನಲ್ಲಿ ಯಾವುದೇ vCard ಫೈಲ್ ಕಂಡುಬಂದಿಲ್ಲ."
+ "NFC ಮೂಲಕ ಸ್ವೀಕರಿಸಲಾದ ಸಂಪರ್ಕ"
+ "ಸಂಪರ್ಕಗಳನ್ನು ರವಾನಿಸುವುದೇ?"
+ "ಸಂಗ್ರಹಿಸಲಾಗುತ್ತಿದೆ"
+ "SD ಕಾಡ್ ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗಲಿಲ್ಲ. (ಕಾರಣ: \"%s \")"
+ "%s /%s ಆಮದು ಮಾಡಲಾಗುತ್ತಿದೆ: %s "
+ ".vcf ಫೈಲ್ಗೆ ರಫ್ತು ಮಾಡಿ"
+ "ಈ ಪ್ರಕಾರ ವಿಂಗಡಿಸು"
+ "ಮೊದಲ ಹೆಸರು"
+ "ಕೊನೆಯ ಹೆಸರು"
+ "ಹೆಸರಿನ ಫಾರ್ಮ್ಯಾಟ್"
+ "ಮೊದಲ ಹೆಸರು ಮೊದಲು"
+ "ಕೊನೆಯ ಹೆಸರು ಮೊದಲು"
+ "ಗೋಚರಿಸುವ ಸಂಪರ್ಕಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"
+ "ಗೋಚರಿಸುವ ಸಂಪರ್ಕಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ವಿಫಲವಾಗಿದೆ."
+ "ಸಂಪರ್ಕಗಳನ್ನು ಆಮದು/ರವಾನೆ ಮಾಡಿ"
+ "ಸಂಪರ್ಕಗಳನ್ನು ಆಮದು ಮಾಡಿ"
+ "ಈ ಸಂಪರ್ಕವನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಸಾಧ್ಯವಿಲ್ಲ."
+ "ಹುಡುಕು"
+ "ಪ್ರದರ್ಶಿಸಲು ಸಂಪರ್ಕಗಳು"
+ "ಪ್ರದರ್ಶಿಸಲು ಸಂಪರ್ಕಗಳು"
+ "ಕಸ್ಟಮ್ ವೀಕ್ಷಣೆ ವಿವರಿಸಿ"
+ "ಸಂಪರ್ಕಗಳನ್ನು ಹುಡುಕಿ"
+ "ಮೆಚ್ಚಿನವುಗಳು"
+ "ಯಾವುದೇ ಸಂಪರ್ಕಗಳಿಲ್ಲ."
+ "ಯಾವುದೇ ಗೋಚರಿಸುವ ಸಂಪರ್ಕಗಳಿಲ್ಲ."
+ "ಯಾವುದೇ ಮೆಚ್ಚಿನವುಗಳು ಇಲ್ಲ."
+ "%s ರಲ್ಲಿ ಯಾವುದೇ ಸಂಪರ್ಕಗಳಿಲ್ಲ"
+ "ಪುನರಾವರ್ತನೆಗಳನ್ನು ತೆರುವುಗೊಳಿಸಿ"
+ "ಸಿಮ್ ಕಾರ್ಡ್ ಆಯ್ಕೆಮಾಡಿ"
+ "ಖಾತೆಗಳು"
+ "ಆಮದು/ರವಾನೆ ಮಾಡು"
+ "%1$s ಮೂಲಕ"
+ "%1$s %2$s ಮೂಲಕ"
+ "ಹುಡುಕಾಟವನ್ನು ನಿಲ್ಲಿಸಿ"
+ "ಹುಡುಕಾಟವನ್ನು ತೆರವುಗೊಳಿಸಿ"
+ "ಸಂಪರ್ಕ ಪ್ರದರ್ಶನ ಆಯ್ಕೆಗಳು"
+ "ಖಾತೆ"
+ "ಕರೆಗಳನ್ನು ಮಾಡಲು ಯಾವಾಗಲೂ ಇದನ್ನು ಬಳಸಿ"
+ "ಇದರೊಂದಿಗೆ ಕರೆ ಮಾಡಿ"
+ "ಟಿಪ್ಪಣಿಯೊಂದಿಗೆ ಕರೆ"
+ "ಕರೆ ಕಳುಹಿಸಲು ಟಿಪ್ಪಣಿಯನ್ನು ಟೈಪ್ ಮಾಡಿ ..."
+ "ಕಳುಹಿಸು ಮತ್ತು ಕರೆಮಾಡು"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ko/strings.xml b/ContactsCommon/res/values-ko/strings.xml
new file mode 100644
index 0000000..68f74f2
--- /dev/null
+++ b/ContactsCommon/res/values-ko/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "텍스트 복사됨"
+ "클립보드에 복사"
+ "%s (으)로 전화걸기"
+ "집으로 전화걸기"
+ "휴대전화로 전화걸기"
+ "직장으로 전화걸기"
+ "직장 팩스로 전화걸기"
+ "집 팩스로 전화걸기"
+ "호출기로 전화걸기"
+ "전화걸기"
+ "콜백 번호로 전화걸기"
+ "카폰으로 전화걸기"
+ "직장 기본전화로 전화걸기"
+ "ISDN으로 전화걸기"
+ "기본전화로 전화걸기"
+ "팩스로 전화걸기"
+ "무선통신으로 전화걸기"
+ "텔렉스로 전화걸기"
+ "TTY/TDD로 전화걸기"
+ "직장 휴대전화로 전화걸기"
+ "직장 호출기로 전화 걸기"
+ "%s (으)로 전화걸기"
+ "MMS로 전화걸기"
+ "%s 에 문자 보내기"
+ "집으로 문자 보내기"
+ "휴대전화로 문자 보내기"
+ "직장으로 문자 보내기"
+ "직장 팩스로 문자 보내기"
+ "집 팩스로 문자 보내기"
+ "호출기로 문자 보내기"
+ "문자 보내기"
+ "콜백 번호로 문자 보내기"
+ "카폰으로 문자 보내기"
+ "직장 기본전화로 문자 보내기"
+ "ISDN에 문자 보내기"
+ "기본전화로 문자 보내기"
+ "팩스로 문자 보내기"
+ "무선통신으로 문자 보내기"
+ "텔렉스로 문자 보내기"
+ "TTY/TDD에 문자 보내기"
+ "직장 휴대전화로 문자 보내기"
+ "직장 호출기로 문자 보내기"
+ "%s (으)로 문자 보내기"
+ "MMS로 문자 보내기"
+ "화상 통화하기"
+ "자주 연락하는 사람들 목록을 삭제하시겠습니까?"
+ "주소록 및 휴대전화 앱에서 자주 연락하는 사람의 목록을 삭제하고 이메일 앱이 주소록 환경설정을 처음부터 다시 반영하도록 합니다."
+ "자주 연락하는 사람들 목록을 삭제하는 중…"
+ "온라인"
+ "자리 비움"
+ "다른 용무 중"
+ "주소록"
+ "기타"
+ "디렉토리"
+ "모든 주소록"
+ "나"
+ "검색 중…"
+ "%d 개 이상 찾았습니다."
+ "주소록 없음"
+
+ %d 개 찾음
+ - 1개 찾음
+
+ "%1$s 님의 빠른 주소록"
+ "(이름 없음)"
+ "자주 통화한 목록"
+ "자주 연락하는 사람들"
+ "연락처 보기"
+ "전화번호가 포함된 모든 연락처"
+ "업데이트 보기"
+ "휴대전화 전용(동기화되지 않음)"
+ "이름"
+ "닉네임"
+ "이름"
+ "이름"
+ "성"
+ "경칭"
+ "중간 이름"
+ "이름 접미어"
+ "이름(소리나는 대로)"
+ "이름(소리나는 대로)"
+ "중간 이름(소리나는 대로)"
+ "성(소리나는 대로)"
+ "전화"
+ "이메일"
+ "주소"
+ "메신저"
+ "조직"
+ "관계"
+ "특별한 날"
+ "문자 메시지"
+ "주소"
+ "회사"
+ "직책"
+ "메모"
+ "SIP"
+ "웹사이트"
+ "그룹"
+ "개인 이메일 주소로 이메일 보내기"
+ "모바일로 이메일 보내기"
+ "직장으로 이메일 보내기"
+ "이메일 보내기"
+ "%s 에 이메일 보내기"
+ "이메일"
+ "도로명"
+ "사서함"
+ "인근 지역"
+ "시"
+ "도"
+ "우편번호"
+ "국가"
+ "집 주소 보기"
+ "직장 주소 보기"
+ "주소 보기"
+ "%s 주소 보기"
+ "AIM으로 채팅"
+ "Windows Live로 채팅"
+ "Yahoo로 채팅"
+ "Skype로 채팅"
+ "QQ로 채팅"
+ "Google 토크로 채팅"
+ "ICQ로 채팅"
+ "Jabber로 채팅"
+ "채팅"
+ "삭제"
+ "이름 입력란 펼치기/접기"
+ "모든 주소록"
+ "별표"
+ "맞춤설정"
+ "연락처"
+ "다른 모든 주소록"
+ "모든 주소록"
+ "동기화 그룹 삭제"
+ "동기화 그룹 추가"
+ "그룹 더보기..."
+ "\'%s \'을(를) 동기화에서 제거하면 그룹화되지 않은 연락처도 동기화에서 제거됩니다."
+ "표시 옵션 저장 중..."
+ "완료"
+ "취소"
+ "%s 의 주소록"
+ "주소록 맞춤 보기"
+ "단일 연락처"
+ "연락처 추가할 계정 선택하기"
+ "SIM 카드에서 가져오기"
+ "SIM ^1 - ^2 에서 가져오기"
+ "SIM %1$s 에서 가져오기"
+ ".VCF 파일에서 가져오기"
+ "%s 가져오기를 취소하시겠습니까?"
+ "%s 내보내기를 취소하시겠습니까?"
+ "vCard 가져오기/내보내기를 취소하지 못했습니다."
+ "알 수 없는 오류입니다."
+ "\'%s \'을(를) 열지 못했습니다. 이유: %s "
+ "내보내기를 시작하지 못했습니다. 이유: \'%s \'"
+ "내보낼 수 있는 연락처가 없습니다."
+ "필수 권한을 사용 중지했습니다."
+ "내보내는 중에 오류가 발생했습니다. 이유: \'%s \'"
+ "필수 파일 이름이 너무 깁니다(\'%s \')."
+ "SD 카드에 vCard 파일이 너무 많습니다."
+ "I/O 오류"
+ "메모리가 부족합니다. 파일이 너무 크기 때문일 수 있습니다."
+ "예기치 못한 이유로 인해 vCard를 파싱하지 못했습니다."
+ "지원되지 않는 형식입니다."
+ "지정한 vCard 파일에 대한 메타 정보를 수집하지 못했습니다."
+ "하나 이상의 파일을 가져오지 못했습니다(%s)."
+ "%s 내보내기 완료됨"
+ "연락처 내보내기 완료"
+ "%s 내보내기 취소됨"
+ "연락처 데이터 내보내기"
+ "연락처 데이터를 %s (으)로 내보내고 있습니다."
+ "데이터베이스 정보를 가져오지 못했습니다."
+ "내보낼 수 있는 연락처가 없습니다. 휴대전화에 연락처가 있다면 일부 데이터 제공업체에서 연락처를 휴대전화에서 내보내지 못하도록 했기 때문일 수 있습니다."
+ "vCard 작성기가 제대로 시작되지 않았습니다."
+ "내보내기 실패"
+ "주소록 데이터를 내보내지 못했습니다.\n(이유: \'%s \')"
+ "%s 가져오는 중"
+ "vCard 데이터를 읽지 못함"
+ "vCard 데이터 읽기 취소"
+ "%s vCard 가져오기 완료됨"
+ "%s 가져오기 취소됨"
+ "%s 을(를) 곧 가져옵니다."
+ "파일을 곧 가져옵니다."
+ "vCard 가져오기 요청이 거부되었습니다. 나중에 다시 시도해 주세요."
+ "%s 을(를) 곧 내보냅니다."
+ "파일을 곧 내보냅니다."
+ "vCard 내보내기 요청이 거부되었습니다. 나중에 다시 시도해 주세요."
+ "연락처"
+ "vCard를 로컬 임시 저장공간에 캐시하는 중입니다. 곧 가져오기가 시작됩니다."
+ "vCard를 가져오지 못했습니다."
+ "SD 카드에 vCard 파일이 없습니다."
+ "NFC를 통해 받은 연락처"
+ "주소록을 내보내시겠습니까?"
+ "캐시"
+ "SD 카드를 검색하지 못했습니다. 이유: \'%s \'"
+ "%s /%s 가져오는 중: %s "
+ ".VCF 파일로 내보내기"
+ "정렬 기준:"
+ "이름"
+ "성"
+ "이름 형식"
+ "이름 먼저"
+ "성 먼저"
+ "표시되는 연락처 모두 공유"
+ "표시되는 연락처를 공유하지 못했습니다."
+ "주소록 가져오기/내보내기"
+ "주소록 가져오기"
+ "연락처를 공유할 수 없습니다."
+ "검색"
+ "표시할 연락처"
+ "표시할 연락처"
+ "맞춤 보기 정의"
+ "연락처 찾기"
+ "즐겨찾기"
+ "연락처가 없습니다."
+ "표시할 수 있는 연락처가 없습니다."
+ "즐겨찾기가 없습니다."
+ "%s 에 연락처가 없습니다."
+ "자주 연락하는 사람 목록 삭제"
+ "SIM 카드 선택"
+ "계정"
+ "가져오기/내보내기"
+ "출처: %1$s "
+ "%1$s (출처: %2$s )"
+ "검색 중지"
+ "검색창 지우기"
+ "연락처 표시 옵션"
+ "계정"
+ "통화에 항상 사용"
+ "통화에 사용할 SIM"
+ "메모가 포함된 통화"
+ "통화에 함께 전송할 메모를 입력하세요..."
+ "보내기 및 통화"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ky-rKG/strings.xml b/ContactsCommon/res/values-ky-rKG/strings.xml
new file mode 100644
index 0000000..5fe7d0e
--- /dev/null
+++ b/ContactsCommon/res/values-ky-rKG/strings.xml
@@ -0,0 +1,258 @@
+
+
+
+
+ "Текст көчүрүлдү"
+ "Буферге көчүрүү"
+ "Чалуу %s "
+ "Үйгө чалуу"
+ "Мобилге чалуу"
+ "Жумушка чалуу"
+ "Жумуш факсына чалуу"
+ "Үй факсына чалуу"
+ "Пейжерге чалуу"
+ "Чалуу"
+ "Кайра чалуу номуруна чалуу"
+ "Автомобилге чалуу"
+ "Компаниянын негизгисине чалуу"
+ "ISDN телефонго чалуу"
+ "Негизгисине чалуу"
+ "Факска чалуу"
+ "Радио телефонго чалуу"
+ "Телекске чалуу"
+ "TTY/TDD чалуу"
+ "Жумушчу мобилге чалуу"
+ "Жумушчу пейжерге чалуу"
+ "Чалуу %s "
+ "MMS номурна чалуу"
+ "SMS %s "
+ "Үйгө SMS жөнөтүү"
+ "Мобилге SMS жөнөтүү"
+ "Жумушка SMS жөнөтүү"
+ "Жумуш факсына SMS жөнөтүү"
+ "Үй факсына SMS жөнөтүү"
+ "Пейжерге SMS жөнөтүү"
+ "SMS жөнөтүү"
+ "Кайра чалууну номуруна SMS"
+ "Автомобилге текст жөнөтүү"
+ "Компаниянын негизгисине SMS жөнөтүү"
+ "ISDN телефонуна SMS жөнөтүү"
+ "Негизгиге SMS жөнөтүү"
+ "Факска SMS жөнөтүү"
+ "Радио телефонго SMS жөнөтүү"
+ "Телекске SMS жөнөтүү"
+ "TTY/TDD телефонго SMS жөнөтүү"
+ "Жумушчу мобилге SMS жөнөтүү"
+ "Жумушчу пейжерге SMS жөнөтүү"
+ "Текст жөнөтүү %s "
+ "MMS телефонго SMS жөнөтүү"
+ "Видео түрүндө чалуу"
+ "Көп чалынгандар тизмеси тазалансынбы?"
+ "Байланыштар жана Телефон колдонмолорунан көп байланышкан адамдар тизмесин тазалап, даректүү жеке жөндөөлөр тууралуу билүү үчүн электрондук почта колдонмолорун иштетиңиз."
+ "Көп чалынгандар тизмеси тазаланууда…"
+ "Жеткиликтүү"
+ "Чыгып кетти"
+ "Бош эмес"
+ "Байланыштар"
+ "Башка"
+ "Директорий"
+ "Бардык байланыштар"
+ "Мен"
+ "Изделүүдө…"
+ "%d ашык табылды."
+ "Эч бир байланыш жок"
+
+ %d табылды
+ - 1 табылды
+
+ "%1$s үчүн тез байланышуу"
+ "(Аты жок)"
+
+
+
+
+
+
+
+
+
+ "Жаңыртууларды көрүү"
+ "Телефон үчүн гана, сихрондоштурулбаган"
+ "Аты"
+ "Каймана аты"
+ "Аты"
+ "Ысымы"
+ "Фамилиясы"
+ "Атынын префикси"
+ "Атасынын аты"
+ "Атынын суффикси"
+ "Фонетикалык аты"
+ "Фонетикалык ысымы"
+ "Атасынын фонетикалык аты"
+ "Фонетикалык фамилиясы"
+ "Телефону"
+ "Эмейл"
+ "Дареги"
+ "IM"
+ "Иштеген жери"
+ "Мамиле"
+ "Өзгөчө күндөр"
+ "Текст билдирүүлөрү"
+ "Толук дареги"
+ "Компания"
+ "Кызматы"
+ "Эскертмелер"
+ "SIP"
+ "Вебсайт"
+ "Топтор"
+ "Үй дарегине эмейлдөө"
+ "Мобилге эмейлдөө"
+ "Жумушка эмейлдөө"
+ "Эмейл"
+ "%s эмейл жөнөтүү"
+ "Эмейл"
+ "Көчөсү"
+ "Абонент кутусу"
+ "Району"
+ "Шаары"
+ "Облусу"
+ "Индекси"
+ "Өлкөсү"
+ "Үй дарегин көрүү"
+ "Жумуш дарегин көрүү"
+ "Дарегин көрүү"
+ "%s дарегин көрүү"
+ "AIM аркылуу чатташуу"
+ "Windows Live аркылуу чатташуу"
+ "Yahoo аркылуу чатташуу"
+ "Skype аркылуу чатташуу"
+ "QQ аркылуу чатташуу"
+ "Google Talk аркылуу чатташуу"
+ "ICQ аркылуу чатташуу"
+ "Jabber аркылуу чатташуу"
+ "Чат"
+ "жок кылуу"
+ "Аттар талааларын жаюу же түрүү"
+ "Бардык байланыштар"
+ "Жылдызчаланган"
+ "Ыңгайлаштыруу"
+ "Байланыш"
+ "Бардык башка байланыштар"
+ "Бардык байланыштар"
+ "Синхрондоо тобун жоюу"
+ "Синхрондоштуруу тобуна кошуу"
+ "Дагы топтор…"
+ "\"%s \" тобун синхрондон алып салуу, башка топторго кирбеген байланыштарды дагы синхрондон чыгарып салат."
+ "Көрсөтүү тууралоолору сакталууда…"
+ "Даяр"
+ "Жокко чыгаруу"
+ "%s байланыштары"
+ "Байланыштардын ыңгайлаштырылган көрүнүшү"
+ "Жалгыз байланыш"
+ "Кийинки эсеп менен байланыш түзүү:"
+ "SIM-картадан импорттоо"
+ "SIM ^1 – ^2 ичинен импорттоо"
+ "SIM %1$s ичинен импорттоо"
+ ".vcf файлынан импорттоо"
+ "%s импортто токтотулсунбу?"
+ "%s экспорттоо токтотулсунбу?"
+ "vCard импортоо/экспортоо токтотулбады."
+ "Белгисиз ката."
+ "\"%s \" ача албай жатат: %s ."
+ "Экспортчу башталбай жатат: \"%s \"."
+ "Экспортко жарактуу байланыш жок."
+ "Керектүү уруксатты өчүрүп койгонсуз."
+ "Экспорттоо учурунда ката кетти: \"%s \"."
+ "Талап кылынган файл аты өтө узун (\"%s \")."
+ "SD-картада өтө көп vCard файлдар бар."
+ "I/O катасы"
+ "Эс тутум жетишсиз. Файл өтө чоң окшойт."
+ "Аныкталбаган себептерден улам vCard\'ды талданбай албай жатат."
+ "Бул формат колдоого алынбайт."
+ "Берилген vCard файл(дар)ынын мета маалыматтарын чогултуу мүмкүн болбой жатат."
+ "Бир же эки файл импорттолбой жатат (%s)."
+ "%s экспорттоо аяктады."
+ "Байланыштар өткөрүлүп бүттү."
+ "%s экспортто токтотулду."
+ "Байланыш берилиштери экспорттолууда"
+ "Сиздин байланыш берилиштериңиз %s файлына экспорттолууда."
+ "Берилиштер корунун маалыматтарын алуу мүмкүн эмес."
+ "Экспортко жарактуу байланыштар жок. Эгер байланыштар телефонуңузда болсо, айрым берилиштер камсыздоочулары байланыштарды телефондон экспорттоого жол бербеши мүмкүн."
+ "vCard түзүүчү туура эмес иштеп баштады."
+ "Экспорттоо мүмкүн эмес"
+ "Байланыш маалыматтары экспорттолгон жок.\nСебеби: \"%s \""
+ "%s импорттолууда"
+ "vCard берилиштерин окуу мүмкүн эмес"
+ "vCard берилиштерин окуу токтотулду"
+ "%s vCard импорттолуп бүттү"
+ "%s файлын импорттоо токтотулду"
+ "%s жакынкы убакытта импорттолот."
+ "Файл жакынкы убакытта импорттолот."
+ "vCard импорттоо талабы четке кагылды. Кийинчерээк кайра аракеттениңиз."
+ "%s жакынкы убакытта экспорттолот."
+ "Файл бир аздан кийин өткөрүлүп берилет."
+ "vCard экспорттоо талабы четке кагылды. Кийинчерээк кайра аракеттениңиз."
+ "байланыш"
+ "vCard(дар) жергиликтүү убактылуу жайга топтолууда. Чыныгы импорт жакында башталат."
+ "vCard импорт кылынган жок."
+ "SD-картада эч бир vCard файл табылган жок."
+ "Байланыш NFC аркылуу алынды"
+ "Байланыштар экспорттолсунбу?"
+ "Топтоо"
+ "SD-картаны скандоо мүмкүн эмес. (Себеби: \"%s \")"
+ "%s /%s : %s импорттолууда"
+ ".vcf файлга экспорттоо"
+ "Төмөнкү боюнча иреттештирүү"
+ "Ысымы"
+ "Фамилиясы"
+ "Ысым форматы"
+ "Биринчи ысымы"
+ "Биринчи фамилиясы"
+ "Көрүнүктүү байланыштарды бөлүшүү"
+ "Көрүнүктүү байланыштар бөлүшүлбөй койду."
+ "Байланыштарды импорттоо/эскорттоо"
+ "Байланыштарды импорттоо"
+ "Бул байланышты бөлүшүү мүмкүн эмес."
+ "Издөө"
+ "Көрсөтүлүүчү байланыштар"
+ "Көрсөтүлүүчү байланыштар"
+ "Ыңгайлаштырылган көрүнүштү аныктоо"
+ "Байланыштарды табуу"
+ "Тандамалдар"
+ "Эч бир байланыш жок."
+ "Көрүнүктүү байланыштар жок."
+ "Тандамалдар бош."
+ "%s ичинде байланыштар жок"
+ "Көп чалуулар тизмесин тазалоо"
+ "SIM карта тандаңыз"
+ "Эсептер"
+ "Импортоо/экспортоо"
+ "%1$s аркылуу"
+ "%1$s %2$s аркылуу"
+ "издөөнү токтотуу"
+ "Издөөнү тазалоо"
+ "Байланышты көрсөтүү параметрлери"
+ "Каттоо эсеби"
+ "Бул ар дайым чалуулр үчн колдонулсн"
+ "Төмөнкү менен чалуу"
+ "Кыска жазуу менен чалуу"
+ "Чалуу менен жөнөтүлө турган кыска жазууну териңиз …"
+ "ЖӨНӨТҮҮ ЖАНА ЧАЛУУ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-land/integers.xml b/ContactsCommon/res/values-land/integers.xml
new file mode 100644
index 0000000..8ddef80
--- /dev/null
+++ b/ContactsCommon/res/values-land/integers.xml
@@ -0,0 +1,22 @@
+
+
+
+ 3
+
+
+ 60
+
diff --git a/ContactsCommon/res/values-lo-rLA/strings.xml b/ContactsCommon/res/values-lo-rLA/strings.xml
new file mode 100644
index 0000000..40b2fa3
--- /dev/null
+++ b/ContactsCommon/res/values-lo-rLA/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "ສຳເນົາຂໍ້ຄວາມແລ້ວ"
+ "ສຳເນົາໄວ້ໃນຄລິບບອດ"
+ "ໂທຫາ %s "
+ "ໂທຫາເບີບ້ານ"
+ "ໂທຫາເບີມືຖື"
+ "ໂທຫາເບີບ່ອນເຮັດວຽກ"
+ "ໂທຫາເບີແຟັກບ່ອນເຮັດວຽກ"
+ "ໂທຫາເບີແຟັກບ້ານ"
+ "ໂທຫາເບີ pager"
+ "ໂທ"
+ "ໂທຫາເບີໂທກັບ"
+ "ໂທຫາເບີລົດ"
+ "ໂທຫາເບີຫຼັກຂອງບໍລິສັດ"
+ "ໂທຫາເບີ ISDN"
+ "ໂທຫາເບີຫຼັກ"
+ "ໂທຫາເບີແຟັກ"
+ "ໂທຫາເບີວິທະຍຸ"
+ "ໂທຫາເບີ telex"
+ "ໂທຫາເບີ TTY/TDD"
+ "ໂທຫາເບີມືຖືບ່ອນເຮັດວຽກ"
+ "ໂທຫາ pager ບ່ອນເຮັດວຽກ"
+ "ໂທຫາ %s "
+ "ໂທຫາເບີ MMS"
+ "ສົ່ງຂໍ້ຄວາມຫາ %s "
+ "ສົ່ງຂໍ້ຄວາມຫາເບີບ້ານ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີມືຖື"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີບ່ອນເຮັດວຽກ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີແຟັກບ່ອນເຮັດວຽກ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີແຟັກບ້ານ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີ pager"
+ "ສົ່ງຂໍ້ຄວາມ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີໂທກັບ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີລົດ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີຫຼັກຂອງບໍລິສັດ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີ ISDN"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີຫຼັກ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີແຟັກ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີວິທະຍຸ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີ telex"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີ TTY/TDD"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີມືຖືບ່ອນເຮັດວຽກ"
+ "ສົ່ງຂໍ້ຄວາມຫາເບີ pager ບ່ອນເຮັດວຽກ"
+ "ສົ່ງຂໍ້ຄວາມຫາ %s "
+ "ສົ່ງຂໍ້ຄວາມຫາເບີ MMS"
+ "ໂທອອກດ້ວຍວິດີໂອ"
+ "ລຶບລາຍຊື່ທີ່ຕິດຕໍ່ເລື້ອຍໆອອກ?"
+ "ທ່ານຈະລຶບຂໍ້ມູນລາຍຊື່ທີ່ຕິດຕໍ່ຫາເລື້ອຍໆຢູ່ໃນແອັບຯລາຍຊື່ ແລະໂທລະສັບ ພ້ອມທັງບັງຄັບໃຫ້ແອັບຯອີເມວເລີ່ມຮຽນຮູ້ຄ່າກຳນົດທີ່ຢູ່ຂອງທ່ານໃໝ່ຕັ້ງແຕ່ຕົ້ນ."
+ "ກຳລັງລຶບລ້າງລາຍຊື່ທີ່ຕິດຕໍ່ຫາເລື້ອຍໆ..."
+ "ສາມາດໃຊ້ໄດ້"
+ "ບໍ່ຢູ່"
+ "ບໍ່ຫວ່າງ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ອື່ນໆ"
+ "ໄດເຣັກທໍຣີ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ທັງໝົດ"
+ "ຂ້ອຍ"
+ "ກຳລັງຊອກຫາ..."
+ "ພົບຫຼາຍກວ່າ %d ລາຍການ."
+ "ບໍ່ມີລາຍຊື່ຜູ່ຕິດຕໍ່"
+
+ %d ພົບແລ້ວ
+ - 1 ພົບແລ້ວ
+
+ "ຂໍ້ມູນລາຍຊື່ຜູ່ຕິດຕໍ່ດ່ວນຂອງ %1$s "
+ "(ບໍ່ມີຊື່)"
+ "ເບີທີ່ໂທເລື້ອຍໆ"
+ "ຕິດຕໍ່ຫາເລື້ອຍໆ"
+ "ເບິ່ງລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ທັງໝົດທີ່ມີເບີໂທລະສັບ"
+ "ເບິ່ງອັບເດດ"
+ "ໂທລະສັບເທົ່ານັ້ນ, ບໍ່ຊິ້ງຂໍ້ມູນ"
+ "ຊື່"
+ "ຊື່ຫຼິ້ນ"
+ "ຊື່"
+ "ຊື່"
+ "ນາມສະກຸນ"
+ "ຄຳນຳໜ້າຊື່"
+ "ຊື່ກາງ"
+ "ຄຳຕໍ່ທ້າຍຊື່"
+ "ການອອກສຽງຊື່"
+ "ການອອກສຽງຊື່"
+ "ການອອກສຽງຊື່ກາງ"
+ "ການອອກສຽງນາມສະກຸນ"
+ "ໂທລະສັບ"
+ "ອີເມວ"
+ "ທີ່ຢູ່"
+ "IM"
+ "ອົງກອນ"
+ "ຄວາມສຳພັນ"
+ "ວັນພິເສດ"
+ "ຂໍ້ຄວາມ"
+ "ທີ່ຢູ່"
+ "ບໍລິສັດ"
+ "ຊື່"
+ "ໝາຍເຫດ"
+ "SIP"
+ "ເວັບໄຊ"
+ "ກຸ່ມ"
+ "ສົ່ງອີເມວຫາອີເມວເຮືອນ"
+ "ສົ່ງອີເມວຫາອີເມວມືຖື"
+ "ສົ່ງອີເມວຫາບ່ອນເຮັດວຽກ"
+ "ສົ່ງອີເມວ"
+ "ສົ່ງອີເມວຫາ %s "
+ "ອີເມວ"
+ "ຖະໜົນ"
+ "ຕູ້ໄປສະນີ"
+ "ບໍລິເວນໃກ້ຄຽງ"
+ "ເມືອງ"
+ "ລັດ"
+ "ລະຫັດ ZIP"
+ "ປະເທດ"
+ "ເບິ່ງເຮືອນທີ່ຢູ່ເບິ່ງທີ່ຢູ່ເຮືອນ"
+ "ເບິ່ງທີ່ຢູ່ບ່ອນເຮັດວຽກ"
+ "ເບິ່ງທີ່ຢູ່"
+ "ເບິ່ງທີ່ຢູ່ %s "
+ "ສົນທະນາໂດຍໃຊ້ AIM"
+ "ສົນທະນາໂດຍໃຊ້ Windows Live"
+ "ສົນທະນາໂດຍໃຊ້ Yahoo"
+ "ສົນທະນາໂດຍໃຊ້ Skype"
+ "ສົນທະນາໂດຍໃຊ້ QQ"
+ "ສົນທະນາໂດຍໃຊ້ Google Talk"
+ "ສົນທະນາໂດຍໃຊ້ ICQ"
+ "ສົນທະນາໂດຍໃຊ້ Jabber"
+ "ສົນທະນາ"
+ "ລຶບ"
+ "ຂະຫຍາຍ ຫຼືຫຍໍ້ຊ່ອງຂໍ້ມູນຊື່"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ທັງໝົດ"
+ "ໝາຍດາວໄວ້"
+ "ປັບແຕ່ງ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ອື່ນໆທັງໝົດ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ທັງໝົດ"
+ "ລຶບກຸ່ມການຊິ້ງຂໍ້ມູນ"
+ "ເພີ່ມກຸ່ມຊິ້ງຂໍ້ມູນ"
+ "ກຸ່ມເພີ່ມເຕີມ..."
+ "ການລຶບ \"%s \" ອອກຈາກການຊິ້ງຂໍ້ມູນ ຈະເປັນການລຶບລາຍຊື່ຜູ່ຕິດຕໍ່ທີ່ບໍ່ໄດ້ຢູ່ໃນກຸ່ມ ອອກຈາກການຊິ້ງຂໍ້ມູນນຳ."
+ "ກຳລັງບັນທຶກໂຕເລືອກການສະແດງຜົນ..."
+ "ແລ້ວໆ"
+ "ຍົກເລີກ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ໃນ %s "
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ໃນມຸມມອງກຳນົດເອງ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ດ່ຽວ"
+ "ສ້າງລາຍຊື່ຜູ່ຕິດຕໍ່ພາຍໃຕ້ບັນຊີ"
+ "ນຳເຂົ້າຈາກ SD card"
+ "ນຳເຂົ້າຈາກ SIM ^1 - ^2 "
+ "ນຳເຂົ້າຈາກ SIM %1$s "
+ "ນຳເຂົ້າຈາກໄຟລ໌ .vcf"
+ "ຍົກເລີກການນຳເຂົ້າ %s ?"
+ "ຍົກເລີກການສົ່ງອອກ %s ?"
+ "ບໍ່ສາມາດຍົກເລີກ ການນຳເຂົ້າ/ສົ່ງອອກ vCard ໄດ້"
+ "ຄວາມຜິດພາດບໍ່ຮູ້ສາຍເຫດ."
+ "ບໍ່ສາມາດເປີດ \"%s \" ໄດ້: %s ."
+ "ບໍ່ສາມາດເລີ່ມໂປຣແກຣມສົ່ງອອກໄດ້: \"%s \"."
+ "ບໍ່ມີລາຍຊື່ຜູ່ຕິດຕໍ່ທີ່ສາມາດສົ່ງອອກໄດ້."
+ "ທ່ານປິດໃຊ້ງານການອະນຸຍາດທີ່ຕ້ອງການແລ້ວ."
+ "ເກີດຄວາມຜິດພາດໃນລະຫວ່າງການສົ່ງອອກ: \"%s \"."
+ "ຊື່ໄຟລ໌ທີ່ຕ້ອງການຍາວເກີນໄປ (\"%s \")."
+ "ມີໄຟລ໌ vCard ໃນ SD card ຫຼາຍເກີນໄປ."
+ "I/O ຜິດພາດ"
+ "ໜ່ວຍຄວາມຈຳບໍ່ພໍ. ເປັນໄປໄດ້ວ່າໄຟລ໌ໃຫຍ່ເກີນໄປ."
+ "ບໍ່ສາມາດວິເຄາະຂໍ້ມູນ vCard ຂອງທ່ານໄດ້ເນື່ອງຈາກສາຍເຫດທີ່ບໍ່ຄາດຄິດ."
+ "ບໍ່ຮອງຮັບຮູບແບບນີ້."
+ "ບໍ່ສາມາດເກັບກຳຂໍ້ມູນ meta ຂອງໄຟລ໌ vCard ທີ່ລະບຸໄດ້."
+ "ມີໄຟລ໌ນຶ່ງ ຫຼືຫຼາຍກວ່ານັ້ນບໍ່ສາມາດນຳເຂົ້າໄດ້ (%s)."
+ "ສິ້ນສຸດການສົ່ງອອກແລ້ວ %s ."
+ "ສຳເລັດການສົ່ງອອກລາຍຊື່ອແລ້ວ."
+ "ການສົ່ງອອກ %s ຖືກຍົກເລີກແລ້ວ."
+ "ກຳລັງສົ່ງອອກຂໍ້ມູນລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ຂໍ້ມູນລາຍຊື່ລາຍຊື່ຜູ່ຕິດຕໍ່ຂອງທ່ານ ກຳລັງຖືກສົ່ງອອກເປັນ: %s ."
+ "ບໍ່ສາມາດດຶງຂໍ້ມູນຖານຂໍ້ມູນໄດ້."
+ "ບໍ່ມີລາຍຊື່ຜູ່ຕິດຕໍ່ທີ່ສາມາດສົ່ງອອກໄດ້. ຫາກທ່ານມີລາຍຊື່ຜູ່ຕິດຕໍ່ໃນໂທລະສັບຂອງທ່ານ ແຕ່ບໍ່ສາມາດສົ່ງອອກໄດ້ ກໍອາດເປັນເພາະບາງຜູ່ໃຫ້ບໍລິການຂໍ້ມູນບໍ່ອະນຸຍາດ ໃຫ້ສົ່ງລາຍຊື່ຜູ່ຕິດຕໍ່ອອກຈາກໂທລະສັບໄດ້."
+ "ໂປຣແກຣມຂຽນ vCard ຖືກເລີ່ມຢ່າງບໍ່ຖືກຕ້ອງ."
+ "ບໍ່ສາມາດສົ່ງອອກໄດ້"
+ "ຂໍ້ມູນລາຍຊື່ຜູ່ຕິດຕໍ່ຍັງບໍ່ໄດ້ຖືກສົ່ງອອກເທື່ອ.\nເຫດຜົນ: \"%s \""
+ "ກຳລັງນຳເຂົ້າ %s "
+ "ບໍ່ສາມາດອ່ານຂໍ້ມູນ vCard ໄດ້"
+ "ການອ່ານຂໍ້ມູນ vCard ຖືກຍົກເລີກແລ້ວ"
+ "ການນຳເຂົ້າໄຟລ໌ vCard %s ສິ້ນສຸດແລ້ວ"
+ "ການນຳເຂົ້າ %s ຖືກຍົກເລີກແລ້ວ"
+ "%s ຈະຖືກນຳເຂົ້າໃນໄວໆນີ້."
+ "ໄຟລ໌ຈະຖືກນຳເຂົ້າໃນໄວໆນີ້."
+ "ການຮ້ອງຂໍການນຳເຂົ້າ vCard ຖືກປະຕິເສດ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ."
+ "%s ຈະຖືກສົ່ງອອກໃນໄວໆນີ້."
+ "ໄຟລ໌ຈະຖືກສົ່ງອອກໄວໆນີ້."
+ "ຄຳຂໍການສົ່ງອອກ vCard ຖືກປະຕິເສດ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ."
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ກຳລັງເກັບຂໍ້ມູນ vCard ໃສ່ບ່ອນຈັດເກັບຂໍ້ມູນຊົ່ວຄາວໃນອຸປະກອນ. ການນຳເຂົ້າຈະເລີ່ມຂຶ້ນໃນໄວໆນີ້."
+ "ບໍ່ສາມາດນຳເຂົ້າ vCard ໄດ້."
+ "ບໍ່ພົບໄຟລ໌ vCard ໃນ SD card."
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ທີ່ໄດ້ຮັບຜ່ານ NFC"
+ "ສົ່ງອອກລາຍຊື່ຜູ່ຕິດຕໍ່?"
+ "ກຳລັງຈັດເກັບຂໍ້ມູນ"
+ "ບໍ່ສາມາດສະແກນ SD card ໄດ້. (ເຫດຜົນ: \"%s \")"
+ "ກຳລັງນຳເຂົ້າ %s /%s : %s "
+ "ສົ່ງອອກຫາໄຟລ໌ .vcf"
+ "ຮຽງຕາມ"
+ "ຊື່"
+ "ນາມສະກຸນ"
+ "ຮູບແບບຊື່"
+ "ຊື່ກ່ອນ"
+ "ນາມສະກຸນກ່ອນ"
+ "ແບ່ງປັນລາຍຊື່ຜູ່ຕິດຕໍ່ທີ່ເບິ່ງເຫັນໄດ້"
+ "ແບ່ງປັນລາຍຊື່ຕິດຕໍ່ສາມາດເຫັນໄດ້ບໍ່ສຳເລັດ."
+ "ນຳເຂົ້າ/ສົ່ງອອກ ລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ນຳເຂົ້າລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ບໍ່ສາມາດແບ່ງປັນລາຍຊື່ລາຍຊື່ຜູ່ຕິດຕໍ່ນີ້ໄດ້."
+ "ຊອກຫາ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ເພື່ອສະແດງ"
+ "ລາຍຊື່ຜູ່ຕິດຕໍ່ເພື່ອສະແດງ"
+ "ກຳນົດມຸມມອງເອງ"
+ "ຊອກຫາລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ລາຍການທີ່ມັກ"
+ "ບໍ່ມີລາຍຊື່ຜູ່ຕິດຕໍ່."
+ "ບໍ່ມີລາຍຊື່ຜູ່ຕິດຕໍ່ທີ່ເບິ່ງເຫັນໄດ້."
+ "ບໍ່ມີລາຍການທີ່ມັກ"
+ "ບໍ່ມີລາຍຊື່ຜູ່ຕິດຕໍ່ໃນ %s "
+ "ລຶບລາຍຊື່ຄົນທີ່ຕິດຕໍ່ຫາເລື້ອຍໆ"
+ "ເລືອກ SIM ກາດ"
+ "ບັນຊີ"
+ "ນຳເຂົ້າ/ສົ່ງອອກ"
+ "ຜ່ານ %1$s "
+ "%1$s ຜ່ານ %2$s "
+ "ຢຸດການຊອກຫາ"
+ "ລຶບການຊອກຫາ"
+ "ໂຕເລືອກການສະແດງລາຍຊື່ຜູ່ຕິດຕໍ່"
+ "ບັນຊີ"
+ "ໃຊ້ຊິມນີ້ເພື່ອການໂທທຸກເທື່ອ"
+ "ໂທດ້ວຍ"
+ "ໂທດ້ວຍບັນທຶກ"
+ "ພິມບັນທຶກ ເພື່ອສົ່ງກັບການໂທ ..."
+ "ສົ່ງ ແລະ ໂທ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-lt/strings.xml b/ContactsCommon/res/values-lt/strings.xml
new file mode 100644
index 0000000..cdcfe4a
--- /dev/null
+++ b/ContactsCommon/res/values-lt/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Tekstas nukopijuotas"
+ "Kopijuoti į iškarpinę"
+ "Skambinti %s "
+ "Skambinti namų telefono numeriu"
+ "Skambinti mobiliojo telefono numeriu"
+ "Skambinti darbo telefono numeriu"
+ "Skambinti darbo fakso numeriu"
+ "Skambinti namų fakso numeriu"
+ "Skambinti pranešimų gaviklio numeriu"
+ "Skambinti"
+ "Skambinti atgalinio skambinimo numeriu"
+ "Skambinti automobilio telefono numeriu"
+ "Skambinti pagrindinio įmonės telefono numeriu"
+ "Skambinti ISDN telefono numeriu"
+ "Skambinti pagrindinio telefono numeriu"
+ "Skambinti fakso numeriu"
+ "Skambinti radijo telefono numeriu"
+ "Skambinti telekso numeriu"
+ "Skambinti TTY / TDD numeriu"
+ "Skambinti darbo mobiliojo telefono numeriu"
+ "Skambinti darbo pranešimų gaviklio numeriu"
+ "Skambinti %s "
+ "Skambinti MMS telefono numeriu"
+ "Siųsti teksto pranešimą %s "
+ "Siųsti teksto pranešimą namų telefono numeriu"
+ "Siųsti teksto pranešimą mobiliojo telefono numeriu"
+ "Siųsti teksto pranešimą darbo telefono numeriu"
+ "Siųsti teksto pranešimą darbo fakso numeriu"
+ "Siųsti teksto pranešimą namų fakso numeriu"
+ "Siųsti teksto pranešimą pranešimų gaviklio numeriu"
+ "Siųsti teksto pranešimą"
+ "Siųsti teksto pranešimą atgalinio skambinimo numeriu"
+ "Siųsti teksto pranešimą automobilio telefono numeriu"
+ "Siųsti teksto pranešimą pagrindinio įmonės telefono numeriu"
+ "Siųsti teksto pranešimą ISDN telefono numeriu"
+ "Siųsti teksto pranešimą pagrindinio telefono numeriu"
+ "Siųsti teksto pranešimą fakso numeriu"
+ "Siųsti teksto pranešimą radijo telefono numeriu"
+ "Siųsti teksto pranešimą telekso numeriu"
+ "Siųsti teksto pranešimą TTY / TDD numeriu"
+ "Siųsti teksto pranešimą darbo mobiliojo telefono numeriu"
+ "Siųsti teksto pranešimą darbo pranešimų gaviklio numeriu"
+ "Siųsti teksto pranešimą %s "
+ "Siųsti MMS telefono numeriu"
+ "Atlikti vaizdo skambutį"
+ "Išvalyti dažniausius kontaktus?"
+ "Išvalysite dažniausių kontaktų sąrašą Kontaktų ir Telefono programose, o el. pašto programoms reikės iš naujo gauti adresavimo nuostatas."
+ "Valomi dažniausi kontaktai…"
+ "Galima"
+ "Pasišalinęs"
+ "Užsiėmęs"
+ "Kontaktai"
+ "Kitas"
+ "Katalogas"
+ "Visi kontaktai"
+ "Aš"
+ "Ieškoma…"
+ "Rasta daugiau nei %d ."
+ "Kontaktų nėra"
+
+ - Rastas
%d kontaktas
+ - Rasti
%d kontaktai
+ - Rasta
%d kontakto
+ - Rasta
%d kontaktų
+
+ "Spartusis %1$s kontaktas"
+ "(Nėra pavadinimo)"
+ "Dažniausiai skambinta"
+ "Dažniausiai susisiekta"
+ "Peržiūrėti kontaktą"
+ "Visi kontaktai su telefonų numeriais"
+ "Peržiūrėti naujinius"
+ "Tik telefonas, nesinchronizuojama"
+ "Pavadinimas"
+ "Slapyvardis"
+ "Pavadinimas"
+ "Vardas"
+ "Pavardė"
+ "Priešvardis"
+ "Antrasis vardas"
+ "Povardis"
+ "Fonetinis vardas"
+ "Vardo fonetinė forma"
+ "Fonetinis antrasis vardas"
+ "Pavardės fonetinė forma"
+ "Telefonas"
+ "El. paštas"
+ "Adresas"
+ "TP"
+ "Organizacija"
+ "Ryšys"
+ "Specialios datos"
+ "Teksto pranešimas"
+ "Adresas"
+ "Įmonė"
+ "Pavadinimas"
+ "Pastabos"
+ "SIP"
+ "Svetainė"
+ "Grupės"
+ "Siųsti el. laišką namų el. pašto adresu"
+ "Siųsti el. laišką el. pašto adresu mobiliesiems"
+ "Siųsti el. laišką darbo el. pašto adresu"
+ "El. paštas"
+ "El. paštas %s "
+ "El. paštas"
+ "Gatvė"
+ "Pašto dėžutė"
+ "Kaimynystė"
+ "Miestas"
+ "Valstija"
+ "Pašto kodas"
+ "Šalis"
+ "Peržiūrėti namų adresą"
+ "Peržiūrėti darbo adresą"
+ "Peržiūrėti adresą"
+ "Peržiūrėti %s adresą"
+ "Kalbėti naudojant AIM"
+ "Kalbėti naudojant „Windows Live“"
+ "Kalbėti naudojant „Yahoo“"
+ "Kalbėti naudojant „Skype“"
+ "Kalbėti naudojant QQ"
+ "Kalbėti naudojant „Google“ pokalbius"
+ "Kalbėti naudojant ICQ"
+ "Kalbėti naudojant „Jabber“"
+ "Pokalbis"
+ "ištrinti"
+ "Išskleisti arba sutraukti pavadinimų laukus"
+ "Visi kontaktai"
+ "Pažymėta žvaigždute"
+ "Tinkinti"
+ "Kontaktas"
+ "Visi kiti kontaktai"
+ "Visi kontaktai"
+ "Pašalinti sinchronizuojamą grupę"
+ "Pridėti sinchronizuotą grupę"
+ "Daugiau grupių…"
+ "Iš sinchronizavimo pašalinus „%s “, bus pašalinti ir nesugrupuoti kontaktai."
+ "Išsaugomos pateikties parinktys…"
+ "Atlikta"
+ "Atšaukti"
+ "%s kontaktai"
+ "Kontaktai tinkintame rodinyje"
+ "Vienas kontaktas"
+ "Kurti kontaktą paskyroje"
+ "Importuoti iš SIM kortelės"
+ "Importuoti iš SIM kortelės „^1 “ – ^2 "
+ "Importuoti iš SIM kortelės „%1$s “"
+ "Importuoti iš VCF failo"
+ "Atšaukti „%s “ importavimą?"
+ "Atšaukti „%s “ eksportavimą?"
+ "Nepav. atš. el. viz. kort. imp. / eksp."
+ "Nežinoma klaida."
+ "Nepavyko atidaryti „%s “: %s ."
+ "Nepavyko paleisti eksportavimo priemonės: „%s “."
+ "Nėra eksportuojamo kontakto."
+ "Išjungėte būtiną leidimą."
+ "Eksportuojant įvyko klaida: „%s “."
+ "Reikalingo failo pavadinimas per ilgas („%s “)."
+ "Per daug el. vizitinių kortelių failų SD kortelėje."
+ "Įvesties / išvesties klaida"
+ "Nepakanka atminties. Gali būti, kad failas per didelis."
+ "Dėl netikėtos priežasties nepavyko išanalizuoti el. vizitinės kortelės."
+ "Formatas nepalaikomas."
+ "Nepavyko surinkti pateikto (-ų) el. vizitinės kortelės failo (-ų) metainformacijos."
+ "Nepavyko importuoti vieno ar daugiau failų (%s)."
+ "Baigta eksportuoti „%s “."
+ "Baigta eksportuoti kontaktus."
+ "„%s “ eksportavimas atšauktas."
+ "Eksportuojami kontaktų duomenys"
+ "Kontaktų duomenys eksportuojami į: „%s “."
+ "Nepavyko gauti duomenų informacijos."
+ "Nėra eksportuojamų kontaktų. Jei telefone yra kontaktų, kai kurie duomenų paslaugų teikėjai gali neleisti eksportuoti kontaktų iš telefono."
+ "El. vizitinių kortelių rengyklė nebuvo tinkamai paleista."
+ "Nepavyko eksportuoti"
+ "Kontakto duomenys nebuvo eksportuoti.\nPriežastis: „%s “"
+ "Importuojama %s "
+ "Nepavyko nusk. el. vizit. kort. duomenų"
+ "El. vizit. kort. duomenų skaitymas atš."
+ "Baigtas „%s “ el. vizit. kort. importavimas"
+ "Atšauktas „%s “ importavimas"
+ "„%s “ bus netrukus importuotas."
+ "Failas bus netrukus importuotas."
+ "El. vizitinės kortelės importavimo užklausa atmesta. Vėliau bandykite dar kartą."
+ "„%s “ bus netrukus eksportuotas."
+ "Failas bus eksportuotas netrukus."
+ "El. vizitinės kortelės eksportavimo užklausa buvo atmesta. Vėliau bandykite dar kartą."
+ "kontaktas"
+ "El. vizitinė (-ės) kortelė (-ės) padedama (-os) į vietinę laikinąją saugyklą. Netrukus bus pradėtas tikrasis importavimas."
+ "Nepavyko importuoti el. vizit. kortelės."
+ "SD kortelėje nerasta jokių el. vizitinių kortelių failų."
+ "Kont. g. per ALR"
+ "Eksportuoti kontaktus?"
+ "Dedama į talpyklą"
+ "SD kortelės negalima nuskaityti. (Priežastis: „%s “)"
+ "Importuojama %s / %s : %s "
+ "Eksportuoti į VCF failą"
+ "Rūšiuoti pagal"
+ "Vardas"
+ "Pavardė"
+ "Vardo formatas"
+ "Pirmiausia vardas"
+ "Pirmiausia pavardė"
+ "Bendrinti matomus kontaktus"
+ "Nepavyko bendrinti matomų kontaktų."
+ "Importuoti / eksportuoti kontaktus"
+ "Importuoti kontaktus"
+ "Šio kontakto negalima bendrinti."
+ "Ieškoti"
+ "Pateiktini kontaktai"
+ "Pateiktini kontaktai"
+ "Apibrėžti tinkintą rodinį"
+ "Ieškoti kontaktų"
+ "Mėgstamiausi"
+ "Kontaktų nėra."
+ "Nėra matomų kontaktų."
+ "Į adresyną nieko neįtraukta."
+ "„%s “ kontaktų nėra"
+ "Valyti dažniausiai naudojamus"
+ "Pasirinkti SIM kortelę"
+ "Paskyros"
+ "Importuoti / eksportuoti"
+ "naudojant „%1$s “"
+ "%1$s naudojant „%2$s “"
+ "sustabdyti paiešką"
+ "Išvalyti paiešką"
+ "Kontaktų rodymo parinktys"
+ "Paskyra"
+ "Visada naudoti tai skambučiams"
+ "Skambinkite naudodami"
+ "Skambutis su užrašu"
+ "Įveskite užrašą, kurį galima išsiųsti skambinant..."
+ "SIŲSTI IR SKAMBINTI"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-lv/strings.xml b/ContactsCommon/res/values-lv/strings.xml
new file mode 100644
index 0000000..fd63186
--- /dev/null
+++ b/ContactsCommon/res/values-lv/strings.xml
@@ -0,0 +1,254 @@
+
+
+
+
+ "Teksts ir nokopēts."
+ "Kopēt starpliktuvē"
+ "Zvanīt: %s "
+ "Zvanīt uz mājas tālruni"
+ "Zvanīt uz mobilo tālruni"
+ "Zvanīt uz darba tālruni"
+ "Zvanīt uz darba faksa numuru"
+ "Zvanīt uz mājas faksa numuru"
+ "Zvanīt uz peidžeri"
+ "Zvanīt"
+ "Zvanīt uz atzvana numuru"
+ "Zvanīt uz automobiļa tālruņa numuru"
+ "Zvanīt uz uzņēmuma galveno tālruņa numuru"
+ "Zvanīt uz ISDN"
+ "Zvanīt uz galveno tālruņa numuru"
+ "Zvanīt uz faksu"
+ "Zvanīt uz radioierīci"
+ "Zvanīt uz teleksu"
+ "Zvanīt uz teksta tālruni/surdotālruni"
+ "Zvanīt uz darba mobilo tālruni"
+ "Zvanīt uz darba peidžeri"
+ "Zvanīt: %s "
+ "Zvanīt uz multiziņas numuru"
+ "Sūtīt īsziņu: %s "
+ "Sūtīt īsziņu uz mājas tālruņa numuru"
+ "Sūtīt īsziņu uz mobilo tālruni"
+ "Sūtīt īsziņu uz darba tālruni"
+ "Sūtīt īsziņu uz darba faksa numuru"
+ "Sūtīt īsziņu uz mājas faksa numuru"
+ "Sūtīt īsziņu uz peidžeri"
+ "Sūtīt īsziņu"
+ "Sūtīt īsziņu uz atzvana numuru"
+ "Sūtīt īsziņu uz automobiļa tālruņa numuru"
+ "Sūtīt īsziņu uz uzņēmuma galveno tālruņa numuru"
+ "Sūtīt īsziņu uz ISDN"
+ "Sūtīt īsziņu uz galveno tālruņa numuru"
+ "Sūtīt īsziņu uz faksu"
+ "Sūtīt īsziņu uz radioierīci"
+ "Sūtīt īsziņu uz teleksu"
+ "Sūtīt īsziņu uz teksta tālruni/surdotālruni"
+ "Sūtīt īsziņu uz darba mobilo tālruni"
+ "Sūtīt īsziņu uz darba peidžeri"
+ "Sūtīt īsziņu: %s "
+ "Sūtīt multiziņu"
+ "Veikt videozvanu"
+ "Vai dzēst bieži lietotos kontaktus?"
+ "Tiks dzēsts bieži lietoto kontaktpersonu saraksts lietotnēs Kontaktpersonas un Tālrunis, un e-pasta lietotnēs no jauna tiks sākta adrešu preferenču saglabāšana."
+ "Bieži lietoto kontaktu dzēšana..."
+ "Pieejams"
+ "Prombūtnē"
+ "Aizņemts"
+ "Kontaktpersonas"
+ "Cits"
+ "Katalogs"
+ "Visas kontaktpersonas"
+ "Es"
+ "Notiek meklēšana…"
+ "Atrastas vairāk nekā %d kontaktpersonas."
+ "Nav kontaktpersonu"
+
+ - Atrastas
%d kontaktpersonas
+ - Atrasta
%d kontaktpersona
+ - Atrastas
%d kontaktpersonas
+
+ "Ātrā saziņa ar kontaktpersonu %1$s "
+ "(Nav vārda)"
+ "Kontaktpersonas, kurām bieži zvanāt"
+ "Personas, ar kurām bieži sazināties"
+ "Skatīt kontaktpersonu"
+ "Visas kontaktpersonas ar tālruņa numuriem"
+ "Skatīt atjauninājumus"
+ "Tikai tālrunī, bez sinhronizācijas"
+ "Vārds un uzvārds"
+ "Segvārds"
+ "Vārds un uzvārds"
+ "Vārds"
+ "Uzvārds"
+ "Uzruna"
+ "Otrais vārds"
+ "Uzruna"
+ "Vārda un uzvārda izruna"
+ "Vārda izruna"
+ "Otrā vārda izruna"
+ "Uzvārda izruna"
+ "Tālrunis"
+ "E-pasta adrese"
+ "Adrese"
+ "Tūlītēja ziņapmaiņa"
+ "Organizācija"
+ "Saistība"
+ "Īpašie datumi"
+ "Īsziņa"
+ "Adrese"
+ "Uzņēmums"
+ "Nosaukums"
+ "Piezīmes"
+ "SIP"
+ "Vietne"
+ "Grupas"
+ "Sūtīt e-pasta ziņojumu uz privāto adresi"
+ "Sūtīt e-pasta ziņojumu uz mobilo tālruni"
+ "Sūtīt e-pasta ziņojumu uz darba adresi"
+ "Sūtīt e-pasta ziņojumu"
+ "Sūtīt e-pasta ziņojumu uz: %s "
+ "Sūtīt e-pasta ziņojumu"
+ "Iela"
+ "Abonenta kastīte"
+ "Teritoriālā vienība"
+ "Pilsēta"
+ "Štats"
+ "Pasta indekss"
+ "Valsts"
+ "Skatīt mājas adresi"
+ "Skatīt darba adresi"
+ "Skatīt adresi"
+ "Skatīt lietotāja %s adresi"
+ "Tērzēt, izmantojot AIM"
+ "Tērzēt, izmantojot Windows Live"
+ "Tērzēt, izmantojot Yahoo"
+ "Tērzēt, izmantojot Skype"
+ "Tērzēt, izmantojot QQ"
+ "Tērzēt, izmantojot Google Talk"
+ "Tērzēt, izmantojot ICQ"
+ "Tērzēt, izmantojot Jabber"
+ "Tērzēt"
+ "dzēst"
+ "Izvērst vai sakļaut nosaukumu laukus"
+ "Visas kontaktpersonas"
+ "Atzīmēts ar zvaigznīti"
+ "Pielāgot"
+ "Kontaktpersonas"
+ "Citas kontaktpersonas"
+ "Visas kontaktpersonas"
+ "Noņemt sinhronizējamo grupu"
+ "Pievienot sinhronizējamu grupu"
+ "Vairāk grupu..."
+ "Pārtraucot grupas %s sinhronizāciju, tiks pārtraukta arī visu negrupēto kontaktpersonu sinhronizācija."
+ "Notiek attēlojuma opciju saglabāšana..."
+ "Gatavs"
+ "Atcelt"
+ "Kontaktpersonas sarakstā %s "
+ "Lietotāju filtrs"
+ "Viena kontaktpersona"
+ "Izveidot jaunu kontaktpersonu kontā"
+ "Importēt no SIM kartes"
+ "Importēt no SIM kartes ^1 — ^2 "
+ "Importēt no SIM kartes %1$s "
+ "Importēt no .vcf faila"
+ "Vai atcelt faila %s importēšanu?"
+ "Vai atcelt faila %s eksportēšanu?"
+ "Nevarēja atcelt vCard f. imp./eksp."
+ "Nezināma kļūda."
+ "Nevarēja atvērt failu %s : %s ."
+ "Nevarēja startēt eksportētāju: %s ."
+ "Nav eksportējamu kontaktpersonu datu."
+ "Jūs esat atspējojis obligātu atļauju."
+ "Eksportēšanas laikā radās kļūda: %s ."
+ "Faila nosaukums ir pārāk garš (%s )."
+ "SD kartē ir pārāk daudz vCard failu."
+ "Ievades/izvades kļūda"
+ "Atmiņā nepietiek vietas. Iespējams, fails ir pārāk liels."
+ "Neparedzēta iemesla dēļ nevarēja parsēt vCard failu."
+ "Šāds formāts netiek atbalstīts."
+ "Nevarēja iegūt metainformāciju par vienu vai vairākiem konkrētiem vCard failiem."
+ "Nevarēja importēt vienu vai vairākus failus (%s)."
+ "Faila %s eksportēšana pabeigta"
+ "Kontaktpersonu eksportēšana ir pabeigta."
+ "Faila %s eksportēšana atcelta"
+ "Kontaktpersonu datu eksportēšana"
+ "Kontaktpersonas dati tiek eksportēti uz failu %s ."
+ "Nevarēja iegūt informāciju no datu bāzes."
+ "Nav nevienas eksportējamas kontaktpersonas. Ja jūsu tālrunī ir saglabātas kontaktpersonas, iespējams, jūsu datu pakalpojumu sniedzējs neļauj eksportēt kontaktpersonas no tālruņa."
+ "vCard veidotājs netika pareizi startēts."
+ "Nevarēja eksportēt"
+ "Kontaktpersonas dati netika eksportēti.\nIemesls: %s "
+ "Notiek importēšana: %s "
+ "Nevarēja nolasīt vCard datus"
+ "Atcelta vCard datu lasīšana"
+ "vCard faila %s importēšana pabeigta"
+ "Faila %s importēšana atcelta"
+ "Fails %s drīzumā tiks importēts."
+ "Fails drīzumā tiks importēts."
+ "Tika noraidīts vCard faila importēšanas pieprasījums. Vēlāk mēģiniet vēlreiz."
+ "Fails %s drīzumā tiks eksportēts."
+ "Fails drīzumā tiks eksportēts."
+ "Tika noraidīts vCard faila eksportēšanas pieprasījums. Vēlāk mēģiniet vēlreiz."
+ "kontaktpersona"
+ "Notiek vCard failu saglabāšana vietējā pagaidu kešatmiņā. Importēšana tiks sākta pēc neilga brīža."
+ "Nevarēja importēt vCard datus."
+ "SD kartē netika atrasts neviens vCard fails."
+ "Kont. saņ., izm. NFC."
+ "Vai eksportēt kontaktp. datus?"
+ "Notiek saglabāšana kešatmiņā"
+ "SD karti nevarēja skenēt. (Iemesls: %s )"
+ "Importē %s . no %s : %s "
+ "Eksportēt .vcf failā"
+ "Kārtot pēc"
+ "Vārds"
+ "Uzvārds"
+ "Vārda formāts"
+ "Vispirms rādīt vārdu"
+ "Vispirms rādīt uzvārdu"
+ "Kopīgot redzamo kontaktpersonu datus"
+ "Neizdevās kopīgot redzamās kontaktpersonas."
+ "Kontaktpersonu importēšana/eksportēšana"
+ "Kontaktpersonu datu importēšana"
+ "Šīs kontaktpersonas datus nevar kopīgot."
+ "Meklēt"
+ "Rādāmās kontaktpersonas"
+ "Rādāmās kontaktpersonas"
+ "Kontaktu grupas izvēle"
+ "Meklēt kontaktpersonas"
+ "Izlase"
+ "Nav kontaktpersonu."
+ "Nav redzamu kontaktpersonu."
+ "Izlasē nav nevienas kontaktpersonas."
+ "Nav kontaktpersonu šādā grupā: %s "
+ "Dzēst bieži lietotos kontaktus"
+ "Atlasiet SIM karti"
+ "Konti"
+ "Importēt/eksportēt"
+ "izmantojot %1$s "
+ "%1$s , izmantojot %2$s "
+ "pārtraukt meklēšanu"
+ "Notīrīt meklēšanas lauku"
+ "Kontaktpersonu rādīšanas opcijas"
+ "Konts"
+ "Vienmēr izmantot zvaniem"
+ "Zvanīt, izmantojot"
+ "Zvanīt ar piezīmi"
+ "Ierakstiet piezīmi, ko nosūtīt ar zvanu..."
+ "SŪTĪT un ZVANĪT"
+ "%1$s no %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-mk-rMK/strings.xml b/ContactsCommon/res/values-mk-rMK/strings.xml
new file mode 100644
index 0000000..6b7e53d
--- /dev/null
+++ b/ContactsCommon/res/values-mk-rMK/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Текстот е копиран"
+ "Копирај во клип-таблата"
+ "Повикај %s "
+ "Јави се дома"
+ "Повикај мобилен"
+ "Јави се на работа"
+ "Повикај факс на работа"
+ "Повикај факс дома"
+ "Повикај пејџер"
+ "Повикај"
+ "Направи повратен повик"
+ "Повикај автомобил"
+ "Повикај главен број во компанија"
+ "Повикај ISDN"
+ "Повикај главен број"
+ "Повикај факс"
+ "Повикај радио"
+ "Повикај телекс"
+ "Повикај TTY/TDD"
+ "Повикај мобилен на работа"
+ "Повикај пејџер на работа"
+ "Повикај %s "
+ "Повикај MMS"
+ "Испрати текстуална порака на %s "
+ "Испрати текстуална порака дома"
+ "Испрати текстуална порака на мобилен"
+ "Испрати текстуална порака на работа"
+ "Испрати текстуална порака на факс на работа"
+ "Испрати текстуална порака на факс дома"
+ "Испрати текстуална порака на пејџер"
+ "Испрати текстуална порака"
+ "Испрати текстуална порака за повратен повик"
+ "Испрати текстуална порака за автомобил"
+ "Испрати текстуална порака на главен број во компанија"
+ "Испрати текстуална порака на ISDN"
+ "Испрати текстуална порака на главен број"
+ "Испрати текстуална порака на факс"
+ "Испрати текстуална порака на радио"
+ "Испрати текстуална порака на телекс"
+ "Испрати текстуална порака на TTY/TDD"
+ "Испрати текстуална порака на мобилен на работа"
+ "Испрати текстуална порака на пејџер на работа"
+ "Испрати текстуална порака на %s "
+ "Испрати текстуална порака на MMS"
+ "Остварете видеоповик"
+ "Исчисти често контактирани?"
+ "Ќе го исчистите списокот на често контактирани лица во апликациите „Контакти“ и „Телефон“ и ќе ги принудите апликациите на е-пошта одново да ги дознаат вашите параметри на обраќање."
+ "Чистење често контактирани..."
+ "Достапен"
+ "Отсутен"
+ "Зафатен"
+ "Контакти"
+ "Друг"
+ "Адресар"
+ "Сите контакти"
+ "Јас"
+ "Се пребарува..."
+ "Повеќе од %d се пронајдени."
+ "Нема контакти"
+
+ %d пронајден
+ %d пронајдени
+
+ "Брз контакт за %1$s "
+ "(Без име)"
+ "Често повикувани"
+ "Често контактирани"
+ "Прикажи контакт"
+ "Сите контакти со телефонски броеви"
+ "Прикажи ажурирања"
+ "Само телефон, несинхрнизиран"
+ "Име"
+ "Прекар"
+ "Име"
+ "Име"
+ "Презиме"
+ "Претставка на име"
+ "Татково име"
+ "Наставка на име"
+ "Фонетско име"
+ "Фонетско име"
+ "Фонетско татково име"
+ "Фонетско презиме"
+ "Телефон"
+ "E-пошта"
+ "Адреса"
+ "IM"
+ "Организација"
+ "Врска"
+ "Посебни датуми"
+ "Текстуална порака"
+ "Адреса"
+ "Компанија"
+ "Име"
+ "Белешки"
+ "СИП"
+ "Веб-сајт"
+ "Групи"
+ "Домашна е-пошта"
+ "Мобилна е-пошта"
+ "Работна е-пошта"
+ "E-пошта"
+ "%s е-пошта"
+ "E-пошта"
+ "Улица"
+ "Поштенски фах"
+ "Соседство"
+ "Град"
+ "Држава"
+ "Поштенски број"
+ "Земја"
+ "Прикажи домашна адреса"
+ "Прикажи адреса на работа"
+ "Прикажи адреса"
+ "Прикажи %s адреса"
+ "Разговор на AIM"
+ "Разговор на Windows Live"
+ "Разговор на Yahoo"
+ "Разговор на Skype"
+ "Разговор на QQ"
+ "Разговор на Google Talk"
+ "Разговор на ICQ"
+ "Разговор на Jabber"
+ "Разговор"
+ "избриши"
+ "Отвори или затвори полиња со име"
+ "Сите контакти"
+ "Со ѕвезда"
+ "Прилагоди"
+ "Контакт"
+ "Сите други контакти"
+ "Сите контакти"
+ "Отстрани синхронизирана група"
+ "Додај синхронизирана група"
+ "Повеќе групи..."
+ "Отстранувањето на „%s “ од синхронизацијата ќе ги отстрани и сите негрупирани контакти од синхронизација."
+ "Се зачувуваат опциите на екранот..."
+ "Готово"
+ "Откажи"
+ "Контакти во %s "
+ "Контакти во прилагоден приказ"
+ "Еден контакт"
+ "Создај контакт под сметка"
+ "Увези од СИМ картичка"
+ "Увези од СИМ ^1 – ^2 "
+ "Увези од СИМ %1$s "
+ "Увези од датотеката .vcf"
+ "Откажи увоз на %s ?"
+ "Откажи извоз на %s ?"
+ "Не можеше да се откаже увоз/извоз на визит картичка"
+ "Непозната грешка."
+ "Не можеше да се отвори „%s “: %s ."
+ "Не можеше да се вклучи извозникот: „%s “."
+ "Нема контакт што може да се извезе."
+ "Оневозможивте потребна дозвола."
+ "Настанаи грешка при извоз: „%s “."
+ "Бараното име на датотеката е предолго („%s “)."
+ "На СД картичката има премногу датотеки за визит картичка."
+ "I/O грешка"
+ "Нема доволно меморија. Датотеката е можеби премногу голема."
+ "Визит картичката не можеше да се разложи од неочекувана причина."
+ "Форматот не е поддржан."
+ "Не можеше да собере мета информации за дадени датотека(и) на визит картичка(и)."
+ "Една или повеќе датотеки не можеа да се увезат (%s)."
+ "Заврши извезувањето на %s ."
+ "Извозот на контакти заврши."
+ "Извезувањето на %s е откажано."
+ "Извезување податоци за контакт"
+ "Вашите податоци за контакт се извезуваат во: %s ."
+ "Не можеше да добие информации за базата на податоци."
+ "Нема контакти што може да се извезат. Ако имате контакти на вашиот телефон, некои даватели на податоци може да не дозволат контактите да се извезуваат од телефонот."
+ "Композиторот на визит картичката не започна правилно."
+ "Не можеа да се извезат"
+ "Податоците за контакт не се извезоа.\nПричина: „%s “"
+ "Увезување %s "
+ "Не можеше да прочита податоци од визит картичка"
+ "Читањето податоци од визит картичка е откажано"
+ "Заврши увезувањето на визит картичката %s "
+ "Увезувањето на %s е откажано"
+ "%s ќе се увезе наскоро."
+ "Датотеката ќе се увезе наскоро."
+ "Барањето за увезување визит картичка беше одбиено. Обидете се повторно подоцна."
+ "%s ќе се извезе наскоро."
+ "Датотеката ќе се извезе наскоро."
+ "Барањето за извезување визит картичка беше одбиено. Обидете се повторно подоцна."
+ "контакт"
+ "Кеширање визит картичка(и) во локална привремена меморија. Реалниот увоз ќе започне наскоро."
+ "Не можеше да се увезе визит картичка."
+ "Не е пронајдена датотека со визит картичка на СД картичката."
+ "Контакт добиен преку NFC"
+ "Извези контакти?"
+ "Кеширање"
+ "СД картичката не можеше да се скенира. (Причина: „%s “)"
+ "Увезување %s /%s : %s "
+ "Извези во датотеката .vcf"
+ "Подреди по"
+ "Име"
+ "Презиме"
+ "Формат на име"
+ "Прво името"
+ "Прво презимето"
+ "Сподели видливи контакти"
+ "Не успеаја да се споделат видливите контакти."
+ "Увези/извези контакти"
+ "Увези контакти"
+ "Овој контакт не може да се сподели."
+ "Пребарај"
+ "Контакти за прикажување"
+ "Контакти за прикажување"
+ "Дефинирај прилагоден приказ"
+ "Пронајди контакти"
+ "Омилени"
+ "Нема контакти."
+ "Нема видливи контакти."
+ "Нема омилени."
+ "Нема контакти во %s "
+ "Исчисти чести"
+ "Изберете СИМ-картичка"
+ "Сметки"
+ "Увези/извези"
+ "од %1$s "
+ "на %1$s од %2$s "
+ "запри пребарување"
+ "Исчисти го полето за пребарување"
+ "Опции за прикажување контакт"
+ "Сметка"
+ "Секогаш користи го ова за повици"
+ "Повикајте со"
+ "Повик со белешка"
+ "Напишете белешка да се испрати со повикот..."
+ "ИСПРАТИ И ПОВИКАЈ"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ml-rIN/strings.xml b/ContactsCommon/res/values-ml-rIN/strings.xml
new file mode 100644
index 0000000..306b928
--- /dev/null
+++ b/ContactsCommon/res/values-ml-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "വാചകം പകർത്തി"
+ "ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തുക"
+ "വിളിക്കുക %s "
+ "വീട്ടിലെ ഫോണിലേക്ക് വിളിക്കുക"
+ "മൊബൈലിലേക്ക് വിളിക്കുക"
+ "ഔദ്യോഗിക ഫോണിലേക്ക് വിളിക്കുക"
+ "ഔദ്യോഗിക ഫാക്സിലേക്ക് വിളിക്കുക"
+ "വീട്ടിലെ ഫാക്സിലേക്ക് വിളിക്കുക"
+ "പേജറിലേക്ക് വിളിക്കുക"
+ "വിളിക്കുക"
+ "കോൾബാക്ക് നമ്പറിലേക്ക് വിളിക്കുക"
+ "കാർ നമ്പറിലേക്ക് വിളിക്കുക"
+ "കമ്പനിയിലെ പ്രധാന ഫോണിലേക്ക് വിളിക്കുക"
+ "ISDN-ലേക്ക് വിളിക്കുക"
+ "പ്രധാന ഫോണിലേക്ക് വിളിക്കുക"
+ "ഫാക്സിലേക്ക് വിളിക്കുക"
+ "റേഡിയോയിലേക്ക് വിളിക്കുക"
+ "ടെലക്സിലേക്ക് വിളിക്കുക"
+ "TTY/TDD-യിലേക്ക് വിളിക്കുക"
+ "ഔദ്യോഗിക മൊബൈലിലേക്ക് വിളിക്കുക"
+ "ഔദ്യോഗിക പേജറിലേക്ക് വിളിക്കുക"
+ "വിളിക്കുക %s "
+ "MMS ഫോണിലേക്ക് വിളിക്കുക"
+ "%s എന്നതിലേക്ക് വാചക സന്ദേശമയയ്ക്കുക"
+ "വീട്ടിലെ ഫോണിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "മൊബൈലിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "ഔദ്യോഗിക ഫോണിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "ഔദ്യോഗിക ഫാക്സിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "വീട്ടിലെ ഫാക്സിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "പേജറിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "വാചകസന്ദേശമയയ്ക്കുക"
+ "കോൾബാക്ക് ഫോണിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "കാർ ഫോണിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "കമ്പനി പ്രധാന ഫോണിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "ISDN-ലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "പ്രധാന ഫോണിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "ഫാക്സിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "റേഡിയോയിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "ടെലക്സിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "TTY/TDD എന്നതിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "ഔദ്യോഗിക മൊബൈലിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "ഔദ്യോഗിക പേജറിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "%s എന്നതിലേക്ക് വാചക സന്ദേശമയയ്ക്കുക"
+ "MMS നമ്പറിലേക്ക് വാചകസന്ദേശമയയ്ക്കുക"
+ "വീഡിയോ കോൾ ചെയ്യുക"
+ "പതിവായി കോൺടാക്റ്റുചെയ്യുന്നവരെ മായ്ക്കണോ?"
+ "നിങ്ങൾ കോൺടാക്റ്റുകളുടെയും ഫോണിന്റെയും അപ്ലിക്കേഷനുകളിലെ പതിവായി കോൺടാക്റ്റുചെയ്യുന്നവരുടെ ലിസ്റ്റ് മായ്ക്കുകയും സ്ക്രാച്ചിൽ നിന്നും ബന്ധപ്പെടൽ മുൻഗണനകൾ അറിയാൻ ഇമെയിൽ അപ്ലിക്കേഷനുകളെ പ്രേരിപ്പിക്കുകയും ചെയ്യും."
+ "പതിവായി കോൺടാക്റ്റുചെയ്യുന്നവരെ മായ്ക്കുന്നു…"
+ "ലഭ്യം"
+ "ലഭ്യമല്ല"
+ "തിരക്കിലാണ്"
+ "വിലാസങ്ങൾ"
+ "മറ്റുള്ളവ"
+ "ഡയറക്ടറി"
+ "എല്ലാ കോൺടാക്റ്റുകളും"
+ "ഞാന്"
+ "തിരയുന്നു…"
+ "%d -ൽ കൂടുതൽ കണ്ടെത്തി."
+ "കോൺടാക്റ്റുകളൊന്നുമില്ല"
+
+ %d എണ്ണം കണ്ടെത്തി
+ - ഒരെണ്ണം കണ്ടെത്തി
+
+ "%1$s എന്നയാളുടെ ദ്രുത കോൺടാക്റ്റ്"
+ "(പേരില്ല)"
+ "പതിവായി വിളിച്ചവർ"
+ "പതിവായി കോൺടാക്റ്റുചെയ്യുന്നവർ"
+ "കോൺടാക്റ്റ് കാണുക"
+ "ഫോൺ നമ്പറുകളുള്ള എല്ലാ കോൺടാക്റ്റുകളും"
+ "അപ്ഡേറ്റുകള് കാണുക"
+ "ഫോണിൽ മാത്രം, സമന്വയിപ്പിക്കില്ല"
+ "പേര്"
+ "വിളിപ്പേര്"
+ "പേര്"
+ "പേരിന്റെ ആദ്യഭാഗം"
+ "പേരിന്റെ അവസാനഭാഗം"
+ "പേര് പ്രിഫിക്സ്"
+ "പേരിന്റെ മധ്യഭാഗം"
+ "പേര് സഫിക്സ്"
+ "ഉച്ചാരണപ്രകാരമുള്ള പേര്"
+ "പേരിന്റെ ആദ്യ ഭാഗം"
+ "പേരിന്റെ മധ്യഭാഗം"
+ "പേരിന്റെ അവസാന ഭാഗം"
+ "ഫോണ്"
+ "ഇമെയിൽ"
+ "വിലാസം"
+ "IM"
+ "ഓര്ഗനൈസേഷന്"
+ "ബന്ധം"
+ "പ്രത്യേക തീയതികൾ"
+ "വാചക സന്ദേശം"
+ "വിലാസം"
+ "കമ്പനി"
+ "ശീര്ഷകം"
+ "കുറിപ്പുകള്"
+ "SIP"
+ "വെബ്സൈറ്റ്"
+ "ഗ്രൂപ്പുകള്"
+ "വീട്ടിലെ ഇമെയിൽ"
+ "മൊബൈൽ ഇമെയിൽ"
+ "ഔദ്യോഗിക ഇമെയിൽ"
+ "ഇമെയിൽ"
+ "%s എന്നതിലേക്ക് ഇമെയിൽ ചെയ്യുക"
+ "ഇമെയിൽ"
+ "സ്ട്രീറ്റ്"
+ "PO ബോക്സ്"
+ "സമീപസ്ഥലം"
+ "നഗരം"
+ "സംസ്ഥാനം"
+ "തപാൽ കോഡ്"
+ "രാജ്യം"
+ "വീട്ടുവിലാസം കാണുക"
+ "ഔദ്യോഗിക വിലാസം കാണുക"
+ "വിലാസം കാണുക"
+ "%s വിലാസം കാണുക"
+ "AIM ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "Windows Live ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "Yahoo ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "Skype ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "QQ ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "Google Talk ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "ICQ ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "Jabber ഉപയോഗിച്ച് ചാറ്റുചെയ്യുക"
+ "ചാറ്റുചെയ്യുക"
+ "ഇല്ലാതാക്കുക"
+ "പേരിന്റെ ഫീൽഡുകൾ വിപുലീകരിക്കുക അല്ലെങ്കിൽ ചുരുക്കുക"
+ "എല്ലാ കോൺടാക്റ്റുകളും"
+ "നക്ഷത്രമിട്ടവ"
+ "ഇഷ്ടാനുസൃതമാക്കുക"
+ "കോൺടാക്റ്റ്"
+ "മറ്റെല്ലാ കോൺടാക്റ്റുകളും"
+ "എല്ലാ കോൺടാക്റ്റുകളും"
+ "സമന്വയ ഗ്രൂപ്പ് നീക്കംചെയ്യുക"
+ "സമന്വയ ഗ്രൂപ്പ് ചേർക്കുക"
+ "കൂടുതൽ ഗ്രൂപ്പുകൾ…"
+ "സമന്വയത്തിൽ നിന്നും \"%s \" നീക്കംചെയ്യുന്നത്, സമന്വയത്തിൽ നിന്നും ഗ്രൂപ്പുചെയ്യാത്ത എല്ലാ കോൺടാക്റ്റുകളേയും നീക്കംചെയ്യുന്നതിനിടയാക്കും."
+ "ഡിസ്പ്ലേ ഓപ്ഷനുകൾ സംരക്ഷിക്കുന്നു…"
+ "പൂർത്തിയായി"
+ "റദ്ദാക്കുക"
+ "%s എന്നതിലെ കോൺടാക്റ്റുകൾ"
+ "ഇഷ്ടാനുസൃതകാഴ്ചയിലെ കോൺടാക്റ്റ്"
+ "സിംഗിൾ കോൺടാക്റ്റ്"
+ "അക്കൗണ്ടിന് കീഴിൽ കോൺടാക്റ്റ് സൃഷ്ടിക്കുക"
+ "സിം കാർഡിൽ നിന്നും ഇമ്പോർട്ടുചെയ്യുക"
+ "SIM-ൽ നിന്ന് ഇമ്പോർട്ടുചെയ്യുക ^1 - ^2 "
+ "SIM-ൽ നിന്ന് ഇമ്പോർട്ടുചെയ്യുക %1$s "
+ ".vcf ഫയലിൽ നിന്ന് ഇമ്പോർട്ടുചെയ്യൂ"
+ "%s എന്നത് ഇമ്പോർട്ടുചെയ്യുന്നത് റദ്ദാക്കണോ?"
+ "%s എന്നത് എക്സ്പോർട്ടുചെയ്യുന്നത് റദ്ദാക്കണോ?"
+ "vCard ഡൗൺലോഡ്/അപ്ലോഡ് റദ്ദാക്കാനായില്ല"
+ "അജ്ഞാത പിശക്."
+ "\"%s \" തുറക്കാനായില്ല: %s ."
+ "എക്സ്പോർട്ടർ ആരംഭിക്കാനായില്ല: \"%s \"."
+ "എക്സ്പോർട്ടുചെയ്യാനാകുന്ന കോൺടാക്റ്റ് ഒന്നുമില്ല."
+ "ആവശ്യമായ ഒരു അനുമതി നിങ്ങൾ പ്രവർത്തനരഹിതമാക്കി."
+ "എക്സ്പോർട്ടുചെയ്യുമ്പോൾ ഒരു പിശക് സംഭവിച്ചു: \"%s \"."
+ "ആവശ്യമായ ഫയലിന്റെ പേര് ദൈർഘ്യമേറിയതാണ് (\"%s \")."
+ "SD കാർഡിൽ വളരെയധികം vCard ഫയലുകളുണ്ട്."
+ "I/O പിശക്"
+ "ആവശ്യമായ മെമ്മറിയില്ല. ഫയൽ വളരെ വലുതായിരിക്കാം."
+ "ഒരു അപ്രതീക്ഷിത കാരണത്താൽ vCard പാഴ്സുചെയ്യാനായില്ല."
+ "ഫോർമാറ്റിനെ പിന്തുണയ്ക്കില്ല."
+ "നൽകിയിരിക്കുന്ന vCard ഫയലിന്റെ (ഫയലുകളുടെ) മീറ്റ വിവരം ശേഖരിക്കാനായില്ല."
+ "ഒന്നോ അതിലധികമോ ഫയലുകൾ ഇമ്പോർട്ടുചെയ്യാനായില്ല (%s)."
+ "%s എക്സ്പോർട്ടുചെയ്യൽ പൂർത്തിയായി."
+ "കോൺടാക്റ്റുകൾ എക്സ്പോർട്ടുചെയ്യൽ പൂർത്തിയായി."
+ "%s എക്സ്പോർട്ടുചെയ്യൽ റദ്ദാക്കി."
+ "കോൺടാക്റ്റ് ഡാറ്റ എക്സ്പോർട്ടുചെയ്യുന്നു"
+ "നിങ്ങളുടെ കോൺടാക്റ്റ് ഡാറ്റ ഇതിലേക്ക് എക്സ്പോർട്ടുചെയ്യുന്നു: %s ."
+ "ഡാറ്റാബേസ് വിവരം നേടാനായില്ല."
+ "എക്സ്പോർട്ടുചെയ്യാനാകുന്ന കോൺടാക്റ്റുകളൊന്നുമില്ല. നിങ്ങളുടെ ഫോണിൽ കോൺടാക്റ്റുകളുണ്ടെങ്കിൽ, ഫോണിൽ നിന്നും കോൺടാക്റ്റുകൾ എക്സ്പോർട്ടുചെയ്യാൻ ചില സേവന ദാതാക്കൾ അനുവദിക്കാനിടയില്ല."
+ "vCard കമ്പോസർ ശരിയായി ആരംഭിച്ചില്ല."
+ "എക്സ്പോർട്ടുചെയ്യാനായില്ല"
+ "കോൺടാക്റ്റ് ഡാറ്റ എക്സ്പോർട്ടുചെയ്തില്ല.\nകാരണം: \"%s \""
+ "%s എന്നയാളെ ഇമ്പോർട്ടുചെയ്യുന്നു."
+ "vCard ഡാറ്റ വായിക്കാനായില്ല"
+ "vCard ഡാറ്റ വായിക്കുന്നത് റദ്ദാക്കി"
+ "vCard %s ഇമ്പോർട്ടുചെയ്യൽ പൂർത്തിയായി"
+ "%s ഇമ്പോർട്ടുചെയ്യൽ റദ്ദാക്കി"
+ "%s എന്നത് ഉടൻ ഇമ്പോർട്ടുചെയ്യും."
+ "ഈ ഫയൽ ഉടൻ ഇമ്പോർട്ടുചെയ്യും."
+ "vCard ഇമ്പോർട്ടുചെയ്യൽ അഭ്യർത്ഥന നിരസിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."
+ "%s എന്നത് ഉടൻ എക്സ്പോർട്ടുചെയ്യും."
+ "ഫയൽ ഉടൻ എക്സ്പോർട്ടുചെയ്യും."
+ "vCard എക്സ്പോർട്ടുചെയ്യൽ അഭ്യർത്ഥന നിരസിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."
+ "കോൺടാക്റ്റ്"
+ "പ്രാദേശിക താൽക്കാലിക സംഭരണത്തിലേക്ക് vCard (vCard-കൾ) കാഷെ ചെയ്യുന്നു. യഥാർത്ഥ ഇമ്പോർട്ടുചെയ്യൽ ഉടൻ ആരംഭിക്കും."
+ "vCard ഇമ്പോർട്ടുചെയ്യാനായില്ല."
+ "SD കാർഡിൽ vCard ഫയലുകളൊന്നും കണ്ടെത്തിയില്ല."
+ "NFC മുഖേന ലഭിച്ച കോൺടാക്റ്റ്"
+ "കോൺടാക്റ്റുകൾ എക്സ്പോർട്ടുചെയ്യണോ?"
+ "കാഷെ ചെയ്യൽ"
+ "SD കാർഡ് സ്കാൻ ചെയ്യാനായില്ല. (കാരണം: \"%s \")"
+ "%s /%s ഇമ്പോർട്ടുചെയ്യുന്നു: %s "
+ ".vcf ഫയലിലേക്ക് എക്സ്പോർട്ട് ചെയ്യൂ"
+ "ഇതുപ്രകാരം അടുക്കുക"
+ "പേരിന്റെ ആദ്യഭാഗം"
+ "പേരിന്റെ അവസാനഭാഗം"
+ "പേരിന്റെ ഫോർമാറ്റ്"
+ "പേരിന്റെ ആദ്യഭാഗം ആദ്യം"
+ "പേരിന്റെ അവസാന ഭാഗം ആദ്യം"
+ "ദൃശ്യമായ കോൺടാക്റ്റുകൾ പങ്കിടുക"
+ "ദൃശ്യമായ കോൺടാക്റ്റുകൾ പങ്കിടുന്നത് പരാജയപ്പെട്ടു."
+ "കോൺടാക്റ്റ് ഡൗൺലോഡ്/അപ്ലോഡ്"
+ "കോൺടാക്റ്റുകൾ ഇമ്പോർട്ടുചെയ്യുക"
+ "ഈ കോൺടാക്റ്റ് പങ്കിടാനാകില്ല."
+ "തിരയുക"
+ "ദൃശ്യമാക്കേണ്ടവ"
+ "ദൃശ്യമാക്കേണ്ടവ"
+ "കസ്റ്റം കാഴ്ച നിശ്ചയിക്കൂ"
+ "കോൺടാക്റ്റ് കണ്ടെത്തൂ"
+ "പ്രിയപ്പെട്ടവർ"
+ "കോൺടാക്റ്റുകൾ ഒന്നുമില്ല."
+ "ദൃശ്യമായ കോൺടാക്റ്റുകളൊന്നുമില്ല."
+ "പ്രിയപ്പെട്ടവർ ആരുമില്ല."
+ "%s എന്നതിൽ കോൺടാക്റ്റുകളൊന്നുമില്ല"
+ "പതിവായി കോൺടാക്റ്റുചെയ്യുന്നവരെ മായ്ക്കുക"
+ "സിം കാർഡ് തിരഞ്ഞെടുക്കുക"
+ "അക്കൗണ്ടുകൾ"
+ "ഡൗൺലോഡ്/അപ്ലോഡ്"
+ "%1$s വഴി"
+ "%2$s വഴി %1$s "
+ "തിരയൽ നിർത്തുക"
+ "തിരയുന്നത് മായ്ക്കുക"
+ "കോൺടാക്റ്റ് ഡിസ്പ്ലേ ഓപ്ഷനുകൾ"
+ "അക്കൗണ്ട്"
+ "ഇത് എല്ലായ്പ്പോഴും കോളുകൾക്കായി ഉപയോഗിക്കുക"
+ "ഇത് ഉപയോഗിച്ച് വിളിക്കുക"
+ "കുറിപ്പിനൊപ്പം വിളിക്കുക"
+ "കോളിനൊപ്പം അയയ്ക്കുന്നതിന് ഒരു കുറിപ്പ് ടൈപ്പുചെയ്യുക ..."
+ "അയയ്ക്കുക, വിളിക്കുക"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-mn-rMN/strings.xml b/ContactsCommon/res/values-mn-rMN/strings.xml
new file mode 100644
index 0000000..17730a2
--- /dev/null
+++ b/ContactsCommon/res/values-mn-rMN/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Текст хуулагдав"
+ "Түр санах ойд хуулах"
+ "%s руу залгах"
+ "Гэрийн утас руу залгах"
+ "Гар утас руу залгах"
+ "Ажил руу залгах"
+ "Ажлын факс руу залгах"
+ "Гэрийн факс руу залгах"
+ "Пейжер рүү залгах"
+ "Залгах"
+ "Дуудлага хүлээж авахаар залгах"
+ "Машин руу залгах"
+ "Компанийн үндсэн дугаар руу залгах"
+ "ISDN руу залгах"
+ "Үндсэн дугаар руу залгах"
+ "Факс руу залгах"
+ "Радио руу залгах"
+ "Телекс рүү залгах"
+ "TTY/TDD рүү залгах"
+ "Ажлын гар утас руу залгах"
+ "Ажлын пейжер рүү залгах"
+ "%s руу залгах"
+ "MMS рүү залгах"
+ "%s руу зурвас илгээх"
+ "Гэрийн утас руу зурвас илгээх"
+ "Гар утас руу зурвас илгээх"
+ "Ажил руу зурвас илгээх"
+ "Ажлын факс руу зурвас илгээх"
+ "Гэрийн факс руу зурвас илгээх"
+ "Пейжер рүү зурвас илгээх"
+ "Зурвас илгээх"
+ "Дуудлага авах зурвас илгээх"
+ "Машин руу зурвас илгээх"
+ "Компанийн үндсэн дугаар руу зурвас илгээх"
+ "ISDN рүү зурвас илгээх"
+ "Үндсэн дугаар руу зурвас илгээх"
+ "Факс руу зурвас илгээх"
+ "Радио руу зурвас илгээх"
+ "Телекс рүү зурвас илгээх"
+ "TTY/TDD рүү зурвас илгээх"
+ "Ажлын гар утас руу зурвас илгээх"
+ "Ажлын пейжэр рүү зурвас илгээх"
+ "%s руу зурвас бичих"
+ "MMS руу зурвас илгээх"
+ "Видео дуудлага хийх"
+ "Байнга холбоо барьдаг харилцагчдын жагсаалтыг устгах уу?"
+ "Та холбоо барих хэсэг болон утасны програмд байгаа тогтмол холбоо баридаг хаягуудыг устгах ба имэйл програмуудыг таны холбоо барих хаягуудыг эрэмбэлэх үйлдлийг идэвхжүүлэх болно."
+ "Байнга холбоо барьдаг харилцагчдын жагсаалтыг устгаж байна…"
+ "Холбогдсон"
+ "Холдсон"
+ "Завгүй"
+ "Харилцагчид"
+ "Бусад"
+ "Директор"
+ "Бүх харилцагчид"
+ "Би"
+ "Хайж байна..."
+ "%d -с олон олдлоо."
+ "Харилцагч байхгүй"
+
+ %d олдсон байна
+ - 1 олдсон байна
+
+ "%1$s -тай шууд холбогдох"
+ "(нэр байхгүй)"
+ "Байнга залгасан"
+ "Байнга холбоо барьдаг"
+ "Харилцагчийг харах"
+ "Утасны дугаартай бүх харилцагчид"
+ "Шинэчлэлтүүдийг харах"
+ "Синк хийгдээгүй, зөвхөн утас"
+ "Нэр"
+ "Хоч"
+ "Нэр"
+ "Өөрийн нэр"
+ "Овог"
+ "Нэрний урьдитгал"
+ "Дундах нэр"
+ "Нэрний дагавар"
+ "Авианы нэр"
+ "Өөрийн нэрний дуудлага"
+ "Дундах авианы нэр"
+ "Овгийн дуудлага"
+ "Утас"
+ "Имэйл"
+ "Хаяг"
+ "IM"
+ "Байгууллага"
+ "Хамаарал"
+ "Онцгой огноонууд"
+ "Зурвас"
+ "Хаяг"
+ "Компани"
+ "Гарчиг"
+ "Тэмдэглэл"
+ "SIP"
+ "Вебсайт"
+ "Бүлгэмүүд"
+ "Гэр рүү имэйлдэх"
+ "Гар утас руу имэйлдэх"
+ "Ажил руу имэйлдэх"
+ "Имэйлдэх"
+ "%s рүү имэйлдэх"
+ "Имэйл"
+ "Гудамж"
+ "ШХ"
+ "Хөрш"
+ "Хот"
+ "Муж улс"
+ "Зип код"
+ "Улс"
+ "Гэрийн хаяг харах"
+ "Ажлын хаяг харах"
+ "Хаяг харах"
+ "%s хаяг харах"
+ "AIM ашиглан чатлах"
+ "Windows Live ашиглан чатлах"
+ "Yahoo ашиглан чатлах"
+ "Skype ашиглан чатлах"
+ "QQ ашиглан чатлах"
+ "Google Talk ашиглан чатлах"
+ "ICQ ашиглан чатлах"
+ "Jabber ашиглан чатлах"
+ "Чат"
+ "устгах"
+ "Нэрийн талбаруудыг дэлгэх буюу хумих"
+ "Бүх харилцагчид"
+ "Одтой"
+ "Тохируулах"
+ "Харилцагч"
+ "Бусад бүх харилцагчид"
+ "Бүх харилцагчид"
+ "Синк бүлгэмийг арилгах"
+ "Синк бүлгэм нэмэх"
+ "Өөр бүлгэмүүд…"
+ "\"%s \"-г синкээс хассанаар бүлгэмээс хасагдсан бүх харилцагчдыг мөн синкээс хасах болно."
+ "Харуулах тохиргоог хадгалж байна…"
+ "Дууссан"
+ "Цуцлах"
+ "%s доторх харилцагчид"
+ "Хувийн тохиргоотой харагдац дахь харилцагчид"
+ "Ганц харилцагч"
+ "Акаунт дотор харилцагч үүсгэх"
+ "SIM картаас импорт хийх"
+ "SIM ^1 - ^2 -с импортлох"
+ "SIM %1$s -с импортлох"
+ ".vcf файлаас импортлох"
+ "%s -г импорт хийхийг цуцлах уу?"
+ "%s -г экспорт хийхийг цуцлах уу?"
+ "vCard импорт/экспорт хийхийг цуцлаж чадсангүй"
+ "Тодорхойгүй алдаа."
+ "\"%s \"-г нээж чадсангүй: %s ."
+ "Экспорт хийгчийг эхлүүлж чадсангүй: \"%s \"."
+ "Экспорт хийж болох харилцагч байхгүй."
+ "Та шаардлагатай зөвшөөрлийг идэвхгүй болгосон байна."
+ "Экспорт хийх явцад алдаа гарсан: \"%s \"."
+ "Шаардагдах файлын нэр хэт урт (\"%s \")"
+ "SD картад хэт олон vCard файл байна."
+ "I/O алдаа"
+ "Санах ой хүрэхгүй байна. Файл хэт том байж магадгүй."
+ "Тодорхойгүй шалтгаанаар vCard-г задлаж чадсангүй."
+ "Формат нь дэмжигдэхгүй байна."
+ "Өгөгдсөн vCard файлын мета мэдээллийг цуглуулж чадсангүй."
+
+
+
+ "%s -г экспорт хийж дууссан."
+ "Харилцагчийн жагсаалтыг экспортолж дууслаа."
+ "%s -г экспорт хийхийг цуцлав."
+ "Харилцагчийн өгөгдлийг экспорт хийж байна"
+ "Таны харилцагчийн өгөгдлийг дараах руу экспорт хийж байна: %s ."
+ "Өгөгдлийн сангийн мэдээллийг авч чадсангүй"
+ "Экспорт хийж болох харилцагчид алга байна. Хэрэв та утсандаа харилцагчидтай байгаа бол зарим өгөгдөл нийлүүлэгчээс харилцагчдын мэдээллийг утаснаас экспорт хийхийг зөвшөөрөхгүй байж магадгүй."
+ "vCard бичигч зохих ёсоор эхэлсэнгүй."
+ "Экспорт хийж чадсангүй"
+ "Харилцагчийн өгөгдлийг экспорт хийсэнгүй.\nШалтгаан: \"%s \""
+ "%s -г импорт хийж байна"
+ "vCard өгөгдлийг уншиж чадсангүй"
+ "vCard өгөгдөл уншихыг цуцлав"
+ "vCard %s -г импорт хийж дууссан"
+ "%s -г импорт хийхийг цуцлав"
+ "%s -г удахгүй импорт хийх болно."
+ "Файлыг удахгүй импорт хийх болно."
+ "vCard импорт хийх хүсэлтийг зөвшөөрсөнгүй. Дараа дахин оролдоно уу."
+ "%s -г удахгүй экспорт хийх болно."
+ "Энэ файлыг удахгүй экспортлох болно."
+ "vCard экспорт хийх хүсэлтийг зөвшөөрсөнгүй. Дараа дахин оролдоно уу."
+ "харилцагч"
+ "vCard-г дотоод түр санд кеш хийж байна. Удахгүй бодитоор импорт хийж эхлэх болно."
+ "vCard-г импорт хийж чадсангүй."
+ "SD карт дотор vCard файл олдсонгүй."
+ "NFC-р хүлээн авсан харилцагч"
+ "Харилцагчдыг экспорт хийх үү?"
+ "Кеш хийж байна"
+ "SD карт скан хийгдэхгүй байна. (Шалтгаан: \"%s \")"
+ "Импорт хийж байна %s /%s : %s "
+ ".vcf файл болгож экспортлох"
+ "Эрэмбэлэх"
+ "Өөрийн нэр"
+ "Овог"
+ "Нэрний формат"
+ "Өөрийн нэрийг эхэнд нь"
+ "Овгийг эхэнд нь"
+ "Харагдах харилцагчдыг хуваалцах"
+ "Харагдаж байгаа харилцагчийн хаягийг хуваалцаж чадсангүй."
+ "Харилцагчид импорт/экспорт хийх"
+ "Харилцагчид импорт хийх"
+ "Энэ харилцагчийг хуваалцах боломжгүй."
+ "Хайх"
+ "Харуулах харилцагчид"
+ "Харуулах харилцагчид"
+ "Хувийн тохиргоот харагдацыг тодорхойлох"
+ "Харилцагч хайх"
+ "Таалагддаг"
+ "Харилцагч байхгүй."
+ "Харагдах харилцагчид байхгүй."
+ "Дуртай байхгүй."
+ "%s дотор харилцагчид байхгүй"
+ "Байнга харилцсаныг арилгах"
+ "SIM карт сонгоно уу"
+ "Акаунт"
+ "Импорт/экспорт"
+ "%1$s -р"
+ "%2$s -н %1$s "
+ "хайлтыг зогсоох"
+ "Хайлтыг цэвэрлэх"
+ "Харилцагчийн харагдах сонголт"
+ "Акаунт"
+ "Дуудлагад байнга үүнийг ашиглах"
+ "Залгах"
+ "Тэмдэглэл бүхий дуудлага хийх"
+ "Дуудлаганд илгээх тэмдэглэл бичнэ үү..."
+ "ИЛГЭЭХ & ЗАЛГАХ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-mr-rIN/strings.xml b/ContactsCommon/res/values-mr-rIN/strings.xml
new file mode 100644
index 0000000..6a34e2a
--- /dev/null
+++ b/ContactsCommon/res/values-mr-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "मजकूर कॉपी केला"
+ "क्लिपबोर्डवर कॉपी करा"
+ "%s ला कॉल करा"
+ "निवासस्थानी कॉल करा"
+ "मोबाईलवर कॉल करा"
+ "कार्यस्थानी कॉल करा"
+ "कार्यस्थानी फॅक्स वर कॉल करा"
+ "निवास फॅक्स वर कॉल करा"
+ "पेजर वर कॉल करा"
+ "कॉल करा"
+ "कॉलबॅकवर कॉल करा"
+ "कारला कॉल करा"
+ "कंपनी मुख्य ला कॉल करा"
+ "ISDN कॉल करा"
+ "मुख्य वर कॉल करा"
+ "फॅक्स वर कॉल करा"
+ "रेडिओ वर कॉल करा"
+ "टेलेक्स वर कॉल करा"
+ "TTY/TDD वर कॉल करा"
+ "कार्यस्थानी मोबाईलवर कॉल करा"
+ "कार्यस्थानी पेजरवर कॉल करा"
+ "%s ला कॉल करा"
+ "MMS वर कॉल करा"
+ "%s मजकूर पाठवा"
+ "निवासस्थानी मजकूर पाठवा"
+ "मोबाईलवर मजकूर पाठवा"
+ "कार्यस्थानी मजकूर पाठवा"
+ "कार्य फॅक्सवर मजकूर पाठवा"
+ "निवासस्थान फॅक्सवर मजकूर पाठवा"
+ "पेजरवर मजकूर पाठवा"
+ "मजकूर"
+ "कॉलबॅक वर मजकूर पाठवा"
+ "कार वर मजकूर पाठवा"
+ "कंपनी मुख्य वर मजकूर पाठवा"
+ "ISDN वर मजकूर पाठवा"
+ "मुख्य वर मजकूर पाठवा"
+ "फॅक्सवर मजकूर पाठवा"
+ "रेडिओवर मजकूर पाठवा"
+ "टेलेक्सवर मजकूर पाठवा"
+ "TTY/TDD वर मजकूर पाठवा"
+ "कार्य मोबाईलवर मजकूर पाठवा"
+ "कार्य पेजरवर मजकूर पाठवा"
+ "%s मजकूर पाठवा"
+ "MMS वर मजकूर पाठवा"
+ "व्हिडिओ कॉल करा"
+ "वारंवार सपर्क साधलेले साफ करायचे?"
+ "आपण संपर्क आणि फोन अॅप्स मधील वारंवार संपर्क साधलेली सूची साफ कराल आणि ईमेल अॅप्सना सुरवातीपासून आपली पत्ता प्राधान्ये जाणून घेण्याची सक्ती कराल."
+ "वारंवार सपर्क साधलेले साफ करीत आहे..."
+ "उपलब्ध"
+ "दूर आहे"
+ "व्यस्त"
+ "संपर्क"
+ "इतर"
+ "निर्देशिका"
+ "सर्व संपर्क"
+ "मी"
+ "शोधत आहे..."
+ "%d पेक्षा जास्त आढळले."
+ "कोणतेही संपर्क नाहीत"
+
+ %d आढळला
+ %d आढळले
+
+ "%1$s साठी दृत संपर्क"
+ "(नाव नाही)"
+ "वारंवार कॉल केलेले"
+ "वारंवार संपर्क केलेले"
+ "संपर्क पहा"
+ "फोन नंबरसह सर्व संपर्क"
+ "अद्यतने पहा"
+ "केवळ-फोन, संकालित न केलेले"
+ "नाव"
+ "टोपणनाव"
+ "नाव"
+ "नाव"
+ "आडनाव"
+ "नाव प्रत्यय"
+ "मधले नाव"
+ "नाव प्रत्यय"
+ "ध्वन्यात्मक नाव"
+ "ध्वन्यात्मक नाव"
+ "ध्वन्यात्मक मधले नाव"
+ "ध्वन्यात्मक आडनाव"
+ "फोन"
+ "ईमेल"
+ "पत्ता"
+ "IM"
+ "संस्था"
+ "नातेसंबंध"
+ "विशेष तारखा"
+ "मजकूर संदेश"
+ "पत्ता"
+ "कंपनी"
+ "शीर्षक"
+ "टिपा"
+ "SIP"
+ "वेबसाइट"
+ "गट"
+ "निवासस्थानी ईमेल करा"
+ "मोबाईलवर ईमेल करा"
+ "कार्यस्थानावर ईमेल करा"
+ "ईमेल"
+ "%s वर ईमेल करा"
+ "ईमेल"
+ "मार्ग"
+ "PO बॉक्स"
+ "अतिपरिचित क्षेत्र"
+ "शहर"
+ "राज्य"
+ "पिनकोड"
+ "देश"
+ "निवास पत्ता पहा"
+ "कार्य पत्ता पहा"
+ "पत्ता पहा"
+ "%s पत्ता पहा"
+ "AIM चा वापर करून चॅट करा"
+ "Windows Live चा वापर करून चॅट करा"
+ "Yahoo चा वापर करून चॅट करा"
+ "Skype चा वापर करून चॅट करा"
+ "QQ चा वापर करून चॅट करा"
+ "Google Talk चा वापर करून चॅट करा"
+ "ICQ चा वापर करून चॅट करा"
+ "Jabber चा वापर करून चॅट करा"
+ "चॅट करा"
+ "हटवा"
+ "नाव फील्ड विस्तृत करा किंवा संकुचित करा"
+ "सर्व संपर्क"
+ "तारांकित"
+ "सानुकूलित करा"
+ "संपर्क"
+ "इतर सर्व संपर्क"
+ "सर्व संपर्क"
+ "संकालन समूह काढून टाका"
+ "संकालन गट जोडा"
+ "अधिक गट..."
+ "संकालन करा मधून \"%s \" हटविल्याने संकालन करा मधून कोणत्याही गटबद्ध न केलेल्या संपर्कांना देखील हटवेल."
+ "प्रदर्शन पर्याय जतन करत आहे…"
+ "पूर्ण झाले"
+ "रद्द करा"
+ "%s मधील संपर्क"
+ "सानुकूल दृश्यामधील संपर्क"
+ "एकल संपर्क"
+ "खात्याअंतर्गत संपर्क तयार करा"
+ "सिमकार्डवरुन आयात करा"
+ "^1 - ^2 SIM वरून आयात करा"
+ "%1$s SIM वरून आयात करा"
+ ".vcf फाईल वरून आयात करा"
+ "%s चे आयात रद्द करायचे?"
+ "%s चे निर्यात रद्द करायचे?"
+ "vCard आयात/निर्यात रद्द करू शकलो नाही"
+ "अज्ञात त्रुटी."
+ "\"%s \" उघडू शकलो नाही: %s ."
+ "निर्यातकर्ता प्रारंभ करू शकला नाही: \"%s \"."
+ "कोणताही निर्यात करण्यायोग्य संपर्क नाही."
+ "आपण आवश्यक असलेली एक परवानगी अक्षम केली आहे."
+ "निर्यात दरम्यान त्रुटी आली: \"%s \"."
+ "आवश्यक फाईल नाव (\"%s \") खूप मोठे आहे."
+ "SD कार्डवर खूप vCard फायली आहेत."
+ "I/O त्रुटी"
+ "पुरेशी मेमरी नाही. फाईल कदाचित खूप मोठी असू शकते."
+ "अनपेक्षित कारणासाठी vCard विश्लेषण करू शकलो नाही."
+ "स्वरूपन समर्थित नाही."
+ "दिलेल्या vCard फाईल(यली) ची मेटा माहिती संकलित करू शकलो नाही."
+ "एक किंवा अधिक फायली आयात केल्या जाऊ शकत नाहीत(%s)."
+ "%s निर्यात करणे समाप्त झाले."
+ "संपर्क आयात करणे समाप्त झाले"
+ "%s निर्यात करणे रद्द केले."
+ "संपर्क डेटा निर्यात करीत आहे"
+ "आपला संपर्क डेटा यावर निर्यात केला जात आहे: %s ."
+ "डेटाबेस माहिती मिळवू शकलो नाही."
+ "निर्यात करण्यायोग्य संपर्क नाहीत. आपल्या फोनवर संपर्क असल्यास, काही डेटा प्रदाते फोनवरून संपर्क निर्यात करण्यास कदाचित अनुमती देणार नाहीत."
+ "vCard रचनाकाराने योग्यरित्या प्रारंभ केला नाही."
+ "निर्यात करू शकलो नाही"
+ "संपर्क डेटा निर्यात केला नाही.\nकारण: \"%s \""
+ "%s आयात करीत आहे"
+ "vCard डेटा वाचू शकलो नाही"
+ "vCard डेटा वाचणे रद्द केले"
+ "vCard %s आयात करणे समाप्त झाले"
+ "%s आयात करणे रद्द झाले"
+ "%s लवकरच आयात केली जाईल."
+ "फाईल लवकरच आयात केली जाईल."
+ "vCard आयात विनंती नाकारली. नंतर पुन्हा प्रयत्न करा."
+ "%s लवकरच निर्यात केली जाईल."
+ "फाईल लवकरच निर्यात केली जाईल."
+ "vCard निर्यात विनंती नाकारली. नंतर पुन्हा प्रयत्न करा."
+ "संपर्क"
+ "स्थानिक तात्पुरत्या संचयनावर vCard(s) कॅशे करीत आहे. वास्तविक आयात लवकरच प्रारंभ होईल."
+ "vCard आयात करू शकलो नाही."
+ "SD कार्डवर कोणतीही vCard फाईल आढळली नाही."
+ "NFC वरील संपर्काचे पुनरावलोकन केले"
+ "संपर्क निर्यात करायचे?"
+ "कॅशे करीत आहे"
+ "SD कार्ड स्कॅन होऊ शकले नाही. (कारण: \"%s \")"
+ "आयात करत आहे %s /%s : %s "
+ ".vcf फाईलवर निर्यात करा"
+ "नुसार क्रमवारी लावा"
+ "नाव"
+ "आडनाव"
+ "नाव स्वरूपन"
+ "नाव प्रथम"
+ "आडनाव प्रथम"
+ "दृश्यमान संपर्क सामायिक करा"
+ "दृश्यमान संपर्क सामायिक करण्यात अयशस्वी झाले."
+ "संपर्क आयात/निर्यात करा"
+ "संपर्क आयात करा"
+ "हा संपर्क सामायिक केला जाऊ शकत नाही."
+ "शोधा"
+ "प्रदर्शित करण्यासाठी संपर्क"
+ "प्रदर्शित करण्यासाठी संपर्क"
+ "सानुकूल दृश्य परिभाषित करा"
+ "संपर्क शोधा"
+ "आवडते"
+ "कोणतेही संपर्क नाहीत."
+ "कोणतेही दृश्यमान संपर्क नाहीत."
+ "कोणत्याही आवडी नाहीत."
+ "%s मध्ये कोणतेही सपंर्क नाहीत"
+ "वारंवारता साफ करा"
+ "सिम कार्ड निवडा"
+ "खाती"
+ "आयात/निर्यात"
+ "%1$s द्वारे"
+ "%2$s द्वारे %1$s "
+ "शोध थांबवा"
+ "शोध साफ करा"
+ "संपर्क प्रदर्शन पर्याय"
+ "खाते"
+ "कॉलसाठी हे नेहमी वापरा"
+ "यासह कॉल करा"
+ "टीपसह कॉल करा"
+ "कॉलसह पाठविण्यासाठी एक टीप टाइप करा..."
+ "पाठवा आणि कॉल करा"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ms-rMY/strings.xml b/ContactsCommon/res/values-ms-rMY/strings.xml
new file mode 100644
index 0000000..0bc8304
--- /dev/null
+++ b/ContactsCommon/res/values-ms-rMY/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Teks yang disalin"
+ "Salin ke papan keratan"
+ "Panggil %s "
+ "Panggil nombor rumah"
+ "Panggil nombor mudah alih"
+ "Panggil tempat kerja"
+ "Panggil faks tempat kerja"
+ "Panggil faks rumah"
+ "Panggil alat kelui"
+ "Panggil"
+ "Panggil nombor panggil balik"
+ "Panggil kereta"
+ "Panggil nombor utama syarikat"
+ "Panggil ISDN"
+ "Panggil nombor utama"
+ "Panggil faks"
+ "Panggil radio"
+ "Panggil teleks"
+ "Panggil TTY/TDD"
+ "Panggil telefon mudah alih tempat kerja"
+ "Panggil alat kelui tempat kerja"
+ "Panggil %s "
+ "Panggil MMS"
+ "SMS %s "
+ "SMS rumah"
+ "SMS telefon mudah alih"
+ "SMS nombor tempat kerja"
+ "SMS faks tempat kerja"
+ "SMS faks rumah"
+ "SMS alat kelui"
+ "SMS"
+ "SMS nombor panggil balik"
+ "SMS kereta"
+ "SMS nombor utama syarikat"
+ "SMS ISDN"
+ "SMS nombor utama"
+ "SMS nombor faks"
+ "SMS radio"
+ "SMS nombor teleks"
+ "SMS TTY/TDD"
+ "SMS telefon mudah alih tempat kerja"
+ "SMS alat kelui tempat kerja"
+ "SMS %s "
+ "SMS nombor MMS"
+ "Buat panggilan video"
+ "Padam bersih senarai kerap dihubungi?"
+ "Anda akan mengosongkan senarai yang kerap dihubungi dalam apl Kenalan dan Telefon serta memaksa apl e-mel untuk mempelajari pilihan alamat anda dari awal."
+ "Memadam bersih senarai kerap dihubungi..."
+ "Ada"
+ "Tiada"
+ "Sibuk"
+ "Kenalan"
+ "Lain-lain"
+ "Direktori"
+ "Semua kenalan"
+ "Saya"
+ "Mencari..."
+ "lebih daripada %d ditemui"
+ "Tiada kenalan"
+
+ %d ditemui
+ - 1 ditemui
+
+ "Kenalan pantas untuk %1$s "
+ "(Tiada nama)"
+ "Kerap dipanggil"
+ "Kerap dihubungi"
+ "Lihat kenalan"
+ "Semua kenalan dengan nombor telefon"
+ "Lihat kemas kini"
+ "Telefon sahaja, tidak disegerakkan"
+ "Nama"
+ "Nama panggilan"
+ "Nama"
+ "Nama pertama"
+ "Nama keluarga"
+ "Awalan nama"
+ "Nama tengah"
+ "Akhiran nama"
+ "Nama fonetik"
+ "Nama pertama fonetik"
+ "Nama tengah fonetik"
+ "Nama keluarga fonetik"
+ "Telefon"
+ "E-mel"
+ "Alamat"
+ "IM"
+ "Organisasi"
+ "Hubungan"
+ "Tarikh istimewa"
+ "Mesej teks"
+ "Alamat"
+ "Syarikat"
+ "Jawatan"
+ "Nota"
+ "SIP"
+ "Tapak web"
+ "Kumpulan"
+ "E-mel rumah"
+ "E-mel telefon mudah alih"
+ "E-mel tempat kerja"
+ "E-mel"
+ "E-mel %s "
+ "E-mel"
+ "Jalan"
+ "Peti surat"
+ "Kawasan kejiranan"
+ "Bandar"
+ "Negeri"
+ "Poskod"
+ "Negara"
+ "Lihat alamat rumah"
+ "Lihat alamat tempat kerja"
+ "Lihat alamat"
+ "Lihat alamat %s "
+ "Sembang menggunakan AIM"
+ "Sembang menggunakan Windows Live"
+ "Sembang menggunakan Yahoo"
+ "Sembang menggunakan Skype"
+ "Sembang menggunakan QQ"
+ "Sembang menggunakan Bual Google"
+ "Sembang menggunakan ICQ"
+ "Sembang menggunakan Jabber"
+ "Sembang"
+ "padam"
+ "Kembangkan atau runtuhkan medan nama"
+ "Semua kenalan"
+ "Dibintangkan"
+ "Peribadikan"
+ "Kenalan"
+ "Semua kenalan lain"
+ "Semua kenalan"
+ "Alih keluar kumpulan penyegerakan"
+ "Tambah kumpulan segerak"
+ "Lagi kumpulan..."
+ "Mengalih keluar \"%s \" daripada penyegerakan juga akan turut mengalih keluar sebarang kenalan tanpa kumpulan dari penyegerakan."
+ "Menyimpan pilihan paparan..."
+ "Selesai"
+ "Batal"
+ "Kenalan dalam %s "
+ "Kenalan dalam paparan tersuai"
+ "Kenalan tunggal"
+ "Wujudkan kenalan di bawah akaun"
+ "Import daripada kad SIM"
+ "Import dari SIM ^1 - ^2 "
+ "Import dari SIM %1$s "
+ "Import daripada fail .vcf"
+ "Batalkan import %s ?"
+ "Batalkan eksport %s ?"
+ "Tidak dapat membatalkan import/eksport vCard"
+ "Ralat tidak diketahui."
+ "Tidak dapat membuka \"%s \": %s ."
+ "Tidak dapat memulakan pengeksport: \"%s \"."
+ "Tiada kenalan yang dapat dieksport."
+ "Anda telah melumpuhkan kebenaran yang diperlukan."
+ "Ralat berlaku semasa eksport: \"%s \"."
+ "Nama fail yang diperlukan terlalu panjang (\"%s \")."
+ "Terlalu banyak fail vCard pada kad SD."
+ "Ralat I/O"
+ "Tidak cukup memori. Fail itu mungkin terlalu besar."
+ "Tidak dapat menghurai vCard atas sebab-sebab yang tidak dijangka."
+ "Format tidak disokong."
+ "Tidak dapat mengumpul maklumat meta fail Vcard yang dinyatakan."
+
+
+
+ "Selesai mengeksport %s ."
+ "Selesai mengeksport kenalan."
+ "Mengeksport %s dibatalkan."
+ "Mengeksport data kenalan"
+ "Data kenalan anda sedang dieksport ke: %s ."
+ "Tidak boleh mendapatkan maklumat pangkalan data."
+ "Tiada kenalan boleh dieksport. Jika anda mempunyai kenalan pada telefon anda, sesetengah pembekal data tidak boleh membenarkan kenalan dieksport dari telefon."
+ "Komposer vCard tidak bermula dengan betul."
+ "Tidak dapat mengeksport"
+ "Data kenalan tidak dieksport.\nAlasan: \"%s \""
+ "Mengimport %s "
+ "Tidak dapat membaca data vCard"
+ "Membaca data vCard dibatalkan"
+ "Selesai mengimport vCard %s "
+ "Pengimportan %s dibatalkan"
+ "%s akan diimport sebentar lagi."
+ "Fail akan diimport sebentar lagi."
+ "Permintaan import vCard telah ditolak. Cuba lagi nanti."
+ "%s akan dieksport sebentar lagi."
+ "Fail akan dieksport sebentar lagi."
+ "Permintaan eksport vCard telah ditolak. Cuba lagi nanti."
+ "kenalan"
+ "Membuat cache vCard ke storan sementara setempat. Pengimportan sebenar akan bermula tidak lama lagi."
+ "Tidak dapat mengimport vCard."
+ "Tiada fail vCard ditemui pada kad SD."
+ "Diterima dr NFC"
+ "Eksport kenalan?"
+ "Mengcache"
+ "Kad SD tidak dapat diimbas. (Alasan: \"%s \")"
+ "Mengimport %s /%s : %s "
+ "Eksport ke fail .vcf"
+ "Isih mengikut"
+ "Nama pertama"
+ "Nama keluarga"
+ "Format nama"
+ "Nama pertama dahulu"
+ "Nama keluarga dahulu"
+ "Kongsi kenalan yang kelihatan"
+ "Gagal berkongsi kenalan yang kelihatan"
+ "Import/eksport kenalan"
+ "Import kenalan"
+ "Kenalan ini tidak boleh dikongsi."
+ "Cari"
+ "Kenalan untuk dipaparkan"
+ "Kenalan untuk dipaparkan"
+ "Tentukan paparan tersuai"
+ "Cari kenalan"
+ "Kegemaran"
+ "Tiada kenalan."
+ "Tiada kenalan yang dapat dilihat."
+ "Tiada kegemaran."
+ "Tiada kenalan dalam %s "
+ "Padam bersih kerap dihubungi"
+ "Pilih kad SIM"
+ "Akaun"
+ "Import/eksport"
+ "melalui %1$s "
+ "%1$s melalui %2$s "
+ "berhenti mencari"
+ "Kosongkan carian"
+ "Pilihan paparan kenalan"
+ "Akaun"
+ "Sentiasa gunakan ini untuk panggilan"
+ "Panggil dengan"
+ "Panggilan dengan nota"
+ "Taip nota untuk dihantar dengan panggilan…"
+ "HANTAR & PANGGIL"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-my-rMM/strings.xml b/ContactsCommon/res/values-my-rMM/strings.xml
new file mode 100644
index 0000000..2f3b823
--- /dev/null
+++ b/ContactsCommon/res/values-my-rMM/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "စာသားကူးယူပြီး"
+ "ကလစ်ဘုတ်သို့ကူးယူရန်"
+ "%s ကိုခေါ်ပါ"
+ "အိမ်ကိုခေါ်ပါ"
+ "မိုဘိုင်းကိုခေါ်ပါ"
+ "အလုပ်ကိုခေါ်ပါ"
+ "အလုပ်ဖက်စ်ကိုခေါ်ပါ"
+ "အိမ်ဖက်စ်ကိုခေါ်ပါ"
+ "ပေဂျာကိုခေါ်ပါ"
+ "ခေါ်ပါ"
+ "ပြန်ခေါ်ခြင်းဝန်ဆောင်မှုကို ခေါ်ပါ"
+ "ကားကိုခေါ်ပါ"
+ "ကုမ္ပဏီအဓိက နံပါတ်ကို ခေါ်ပါ"
+ "ISDN ကိုခေါ်ပါ"
+ "အဓိကနံပါတ်ကိုခေါ်ပါ"
+ "ဖက်စ်ကိုခေါ်ပါ"
+ "ရေဒီယိုကိုခေါ်ပါ"
+ "တဲလက်ဇ်နံပါတ်ကိုခေါ်ပါ"
+ "TTY/TDD ကိုခေါ်ပါ"
+ "အလုပ်မိုဘိုင်းကိုခေါ်ပါ"
+ "အလုပ်ပေဂျာကိုခေါ်ပါ"
+ "%s ကိုခေါ်ပါ"
+ "MMS ကိုခေါ်ပါ"
+ "%s သို့ စာပို့ပါ"
+ "အိမ်သို့ စာပို့ပါ"
+ "မိုဘိုင်းသို့ စာပို့ပါ"
+ "အလုပ်နံပါတ်သို့ စာပို့ပါ"
+ "အလုပ်ဖက်စ်သို့ စာပို့ပါ"
+ "အိမ်ဖက်စ်သို့ စာပို့ပါ"
+ "ပေဂျာနံပါတ်သို့ စာပို့ပါ"
+ "စာတို"
+ "ပြန်ခေါ်မှုနံပါတ်ထံ စာပို့ပါ"
+ "ကားဆီ စာပို့ပါ"
+ "ကုမ္ပဏီ ပင်မနံပါတ်သို့ စာပို့ပါ"
+ "ISDN စာပို့ပါ"
+ "အဓိကနံပါတ်သို့ စာပို့ပါ"
+ "ဖက်စ်နံပါတ်သို့ စာပို့ပါ"
+ "ရေဒီယိုနံပါတ်သို့ စာပို့ပါ"
+ "တဲလက်စ်နံပါတ်သို့ စာပို့ပါ"
+ "TTY/TDD နံပါတ်သို့ စာပို့ပါ"
+ "အလုပ်မိုဘိုင်းသို့ စာပို့ပါ"
+ "အလုပ်ပေ့ဂျာသို့ စာပို့ပါ"
+ "%s ထံ စာပို့ပါ"
+ "MMS နံပါတ်သို့ စာပို့ပါ"
+ "ဗီဒီယို ခေါ်ဆိုမှု ပြုလုပ်ရန်"
+ "အသုံးများသောလိပ်စာများရှင်းပစ်မလား?"
+ "အသုံးများသော အဆက်အသွယ်စာရင်းအား Contacts နှင့် Phone app များမှ သင် ရှင်းလင်းပစ်မှာဖြစ်ပြီး၊ အီးမေး app များအား သင့်နှစ်သက်ရာ ပြောဆိုဆက်ဆံမှုပုံစံကို အစမှပြန်လည် လေ့လာခိုင်းမည်။"
+ "အသုံးများသောလိပ်စာများ ရှင်းလင်းနေစဉ်"
+ "ဆက်သွယ်နိုင်ပါသည်"
+ "အဝေးရောက်နေပါသည်"
+ "အလုပ်များနေသည်"
+ "အဆက်အသွယ်များ"
+ "တစ်ခြား"
+ "လမ်းညွှန်"
+ "လိပ်စာများအားလုံး"
+ "ကျွန်ုပ်"
+ "ရှာဖွေနေသည်…"
+ "%d ထက်ပိုတွေ့ရှိသည်"
+ "အဆက်အသွယ်များမရှိပါ"
+
+ %d တွေ့ခဲ့၏
+ - ၁ ခု တွေခဲ့၏
+
+ "%1$s အတွက် အမြန်ဆက်သွယ်လိပ်စာ"
+ "(အမည်မရှိ)"
+ "မကြာခဏခေါ်ရန်သူများ"
+ "မကြာခဏဆက်သွယ်ရန်သူများ"
+ "အဆက်အသွယ်အား ကြည့်ရန်"
+ "ဖုန်းနံပါတ်ပါသော လိပ်စာများအားလုံး"
+ "အဆင့်မြှင့်ခြင်းများပြရန်"
+ "ဖုန်းတွင်သာ ညီတူမညှိထားပါ"
+ "အမည်"
+ "နာမည်ဝှက်"
+ "အမည်"
+ "ပထမ အမည်"
+ "နောက်ဆုံး အမည်"
+ "နာမည်ရှေ့ဆောင်"
+ "အလယ်နာမည်"
+ "နာမည်နောက်စွဲ"
+ "အသံထွက်နာမည်"
+ "အသံထွက် ပထမ အမည်"
+ "အသံထွက် အလယ်နာမည်"
+ "အသံထွက် နောက်ဆုံး အမည်"
+ "ဖုန်း"
+ "အီးမေးလ်"
+ "လိပ်စာ"
+ "IM"
+ "အဖွဲ့အစည်း"
+ "ပတ်သတ်မှု"
+ "အထူးနေ့စွဲများ"
+ "စာတို စာပို့ခြင်း"
+ "လိပ်စာ"
+ "ကုမ္ပဏီ"
+ "ခေါင်းစဉ်"
+ "မှတ်စုများ"
+ "SIP"
+ "ဝဘ်ဆိုက်"
+ "အုပ်စုများ"
+ "အိမ်ကို အီးမေးလ် ပို့ပါ"
+ "မိုဘိုင်း ကို အီးမေးလ် ပို့ပါ"
+ "အလုပ်ကို အီးမေးလ် ပို့ပါ"
+ "အီးမေးလ်"
+ "အီးမေးလ် %s "
+ "အီးမေးလ်"
+ "လမ်း"
+ "စာတိုက်သေတ္တာ"
+ "ပတ်ဝန်းကျင်"
+ "မြို့"
+ "ပြည်နယ်"
+ "စာပို့သင်္ကေတ"
+ "နိုင်ငံ"
+ "အိမ်လိပ်စာအားကြည့်ရန်"
+ "အလုပ်လိပ်စာအားကြည့်ရန်"
+ "လိပ်စာအားကြည့်ရန်"
+ "%s လိပ်စာအားကြည့်ရန်"
+ "AIM သုံး၍ ချက်တင်လုပ်ခြင်း"
+ "Windows Liveသုံး၍ ချက်တင်ပြုလုပ်ခြင်း"
+ "Yahoo သုံး၍ ချက်တင်ပြုလုပ်ခြင်း"
+ "Skype သုံး၍ ချက်တင်ပြုလုပ်ခြင်း"
+ "QQ သုံး၍ ချက်တင်ပြုလုပ်ခြင်း"
+ "ဂူးဂဲလ်တော့သုံး၍ ချက်တင်ပြုလုပ်ခြင်း"
+ "ICQ သုံး၍ ချက်တင်ပြုလုပ်ခြင်း"
+ "Jabberသုံး၍ ချက်တင်ပြုလုပ်ခြင်း"
+ "ချက်တင်းပြောသည်"
+ "ဖျက်သည်"
+ "နာမည်အကွက်များအား ဖြန့်ချ သို့မဟုတ် လိပ်တင်ပါ"
+ "လိပ်စာများအားလုံး"
+ "စတားပေးထားသော အရာ"
+ "မိမိစိတ်ကြိုက်ပြုလုပ်ခြင်း"
+ "အဆက်အသွယ်"
+ "တခြားအဆက်အသွယ်များအားလုံး"
+ "လိပ်စာများအားလုံး"
+ "ထပ်တူပြုလုပ်ခြင်း အုပ်စုအား ဖယ်ရှားရန်"
+ "ထပ်တူပြုလုပ်မှု အုပ်စုများ ထပ်ထည့်ပါ"
+ "ပိုမိုသော အုပ်စုများ…"
+ "\"%s \" အား ထပ်တူပြုလုပ်ခြင်းမှ ဖယ်ထုတ်ခြင်းသည် တခြား အုပ်စုမလုပ်ထားသော အဆက်အသွယ်များအားလည်း ဖယ်ထုတ်ပါလိမ့်မည်။"
+ "ပြသမှု ရွေးချယ်ခြင်းများ သိမ်းဆည်နေစဉ်…"
+ "ပြီးပါပြီ"
+ "ထားတော့"
+ "%s ထဲမှ အဆက်အသွယ်များ"
+ "မိမိစိတ်ကြိုက် မြင်ကွင်းမှ"
+ "အဆက်အသွယ်တစ်ခုတည်း"
+ "အကောင့်အောက်မှာ အဆက်အသွယ်များပြုလုပ်ပါ"
+ "ဆင်းမ်ကဒ်ထဲမှ အထဲသို့သွင်းရန်"
+ "ဆင်းမ်ကဒ်မှ သွင်းယူရန် ^1 - ^2 "
+ "ဆင်းမ်ကဒ်မှ သွင်းယူရန် %1$s "
+ ".vcf ဖိုင်မှသွင်းမည်"
+ "%s ကို အထဲသို့သွင်းခြင်းအား ရပ်တန့်မလား?"
+ "%s ကိုအပြင်သို့ထုတ်ခြင်းအား ရပ်တန့်မလား?"
+ "လိပ်စာကဒ် အသွင်း၊အထုတ်ကို ရပ်၍မရပါ"
+ "အမည်မသိသော မှားယွင်းမှု"
+ "\"%s \" ကို ဖွင့်မရပါ: %s ."
+ "အပြင်ထုတ်သောစနစ်အား စတင်လို့မရပါ: \"%s \"."
+ "အပြင်သို့ထုတ်ယူရနိုင်သော အဆက်အသွယ်မရှိပါ"
+ "လိုအပ်သည့် ခွင့်ပြုချက်ကို သင်ပိတ်လိုက်သည်။"
+ "အပြင်ထုတ်နေစဉ် အမှားပေါ်ပါသည်: \"%s \"."
+ "လိုအပ်သောဖိုင်နာမည် အလွန်ရှည်နေပါသည် (\"%s \")."
+ "SD ကဒ်ပေါ်တွင် လိပ်စာကဒ်ဖိုင်များ အရမ်းများနေပါသည်"
+ "I/O အမှား"
+ "လုံလောက်သော မှတ်ဉာဏ်မရှိပါ။ ဖိုင်အရမ်းကြီးနေတာ ဖြစ်နိုင်ပါသည်"
+ "လိပ်စာကဒ်အား အမျိုးအမည်မသိအမှားကြောင့် ဖတ်လို့မရပါ"
+ "ပံ့ပိုးမှုပေးနိုင်သော ပုံစံမဟုတ်ပါ"
+ "ဖော်ပြပါ လိပ်စာကဒ်ဖိုင်(များ)၏ အချက်အလက်များအား စုဆောင်းလို့မရပါ"
+ "ဖိုင်တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော ဖိုင်များအား အထဲသို့သွင်းလို့မရပါ(%s)."
+ "%s ကို အပြင်ထုတ်ခြင်းပြီးစီးပါပြီး"
+ "အဆက်အသွယ်များ တင်ပို့ခြင်း ပြီးပါပြီ။"
+ "%s ကို အပြင်ထုတ်ခြင်းအား ပယ်ဖျက်ပြီး"
+ "အဆက်အသွယ်ဒေတာများအား အပြင်သို့ထုတ်နေစဉ်"
+ "သင့်အဆက်အသွယ်အချက်အလက်များကို ဖော်ပြပါထံထုတ်လိုက်ပါပြီ: %s ."
+ "ဒေတာဘေ့စ်အချက်အလက်အား မရရှိနိုင်ပါ"
+ "အပြင်သို့ထုတ်ယူရနိုင်သော လိပ်စာမရှိပါ။ သင့်ဖုန်းထဲမှာ လိပ်စာများရှိပါက တချို့ ဒေတာဝန်ဆောင်မှုက ထိုလိပ်စာများအား ဖုန်းထဲမှအပြင်သို့ ထုတ်ခွင့်ပေးခွင့် မပေးတာမျိုးဖြစ်နိုင်ပါသည်"
+ "လိပ်စာကဒ်ပြုလုပ်သောစန် ကောင်းမွန်စာ မစတင်ပါ"
+ "အပြင်ထုတ်လို့မရပါ"
+ "အဆက်အသွယ် အချက်အလက်ကို \nအကြောင်းပြချက်ကြောင့် မထုတ်နိုင်ပါ။ \"%s \""
+ "%s အား အထဲသွင်းစဉ်"
+ "လိပ်စာကဒ်ဒေတာအား ဖတ်မရပါ"
+ "လိပ်စာကဒ်ဒေတာဖတ်ခြင်းအား ရပ်တန့်ပြီး"
+ "%s လိပ်စာကဒ် အထဲသွင်းခြင်း ပြီးပါပြီ"
+ "%s အားအထဲသွင်းခြင်း ရပ်ဆိုင်းပြီး"
+ "%s ကို မကြာမှီ အထဲသို့ သွင်းပါမည်"
+ "ဖိုင်အား မကြာမှီ အထဲသို့ သွင်းပါမည်"
+ "လိပ်စာကဒ်အား အထဲသွင်းရန် တောင်းဆိုမှု ငြင်းဆန်ခံရပါသည်။ နောင်မှ ပြန်လည်ကြိုးစားပါ"
+ "%s ကို မကြာမီ အပြင်သို့ ထုတ်ပါမည်"
+ "ဖိုင်အား မကြာမီ တင်ပို့ပါလိမ့်မည်။"
+ "လိပ်စာကဒ်အား အပြင်ထုတ်ရန် တောင်းဆိုမှု ငြင်းဆန်ခံရပါသည်။ နောင်မှ ပြန်လည်ကြိုးစားပါ"
+ "အဆက်အသွယ်"
+ "လိပ်စာကဒ်(များ)ကို စက်တွင်း ခဏသိမ်းဆည်းရာနေရာသို့ ပို့နေပါသည်။ အမှန်တကယ် တင်သွင်းခြင်း မကြာခင် စပါမည်။"
+ "လိပ်စာကဒ်အား အထဲသွင်းလို့မရပါ"
+ "SD ကဒ်ထဲတွင် လိပ်စာကဒ်ဖိုင်မှမရှိပါ"
+ "NFCမှရသောလိပ်စာ"
+ "လိပ်စာများအပြင်သို့ထုတ်မလား?"
+ "ယာယီသိမ်းထားခြင်း"
+ "SD ကဒ်ကို စကင်လုပ်မရပါ။ (အကြောင်းပြချက်: \"%s \")"
+ "%s /%s ကို သွင်းနေစဉ်: %s "
+ ".vcf ဖိုင်သို့ထုတ်မည်"
+ "ဖြင့် စီပေးရန်"
+ "ပထမ အမည်"
+ "နောက်ဆုံး အမည်"
+ "အမည် ချထားပုံစံ"
+ "ပထမ အမည် ဦးစွာ"
+ "နောက်ဆုံး အမည် ဦးစွာ"
+ "မြင်နိုင်သော အဆက်အသွယ်များအား မျှဝေပါ"
+ "မြင်သာသော အဆက်အသွယ်တွေကိုဝေမျှခြင်းမပြုနိုင်ခဲ့ပါ။"
+ "အဆက်အသွယ်များ သွင်းယူ၊ ထုတ်ယူရန်"
+ "အဆက်အသွယ်များ တင်သွင်းပါ"
+ "ဒီလိပ်စာအား မျှဝေလို့ မရပါ"
+ "ရှာဖွေခြင်း"
+ "ပြရန် အဆက်အသွယ်များ"
+ "အဆက်အသွယ်များပြရန်"
+ "မိမိစိတ်ကြိုက်မြင်ကွင်း သတ်မှတ်ပါ"
+ "အဆက်အသွယ်များရှာပါ"
+ "အနှစ်သက်ဆုံးများ"
+ "အဆက်အသွယ်များမရှိပါ"
+ "မြင်နိုင်သော အဆက်အသွယ်မရှိပါ"
+ "အနှစ်သက်ဆုံး မရှိပါ"
+ "%s ထဲတွင် အဆက်အသွယ်မရှိပါ"
+ "မကြာခဏအရာများအား ဖယ်ရှားရန်"
+ "SIM ကဒ်ကို ရွေးရန်"
+ "အကောင့်များ"
+ "သွင်းယူ၊ ထုတ်ယူခြင်း"
+ "%1$s မှတဆင့်"
+ "%2$s မှတဆင့် %1$s "
+ "ရှာဖွေမှုကို ရပ်ရန်"
+ "ရှာဖွေမှုကို ရှင်းပါ"
+ "အဆက်အသွယ် ပြသမှု ရွေးစရာများ"
+ "အကောင့်"
+ "ခေါ်ဆိုမှုများ အတွက် အမြဲတမ်း ဒါကို သုံးရန်"
+ "ဖြင့် ခေါ်ဆိုရန်"
+ "မှတ်စုတစ်ခုဖြင့် ခေါ်ဆိုမည်"
+ "ခေါ်ဆိုမှုဖြင့် ပေးပို့ရန် မှတ်စုတစ်ခု ရိုက်ပါ ..."
+ "ပေးပို့မည် & ခေါ်ဆိုမည်"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-nb/strings.xml b/ContactsCommon/res/values-nb/strings.xml
new file mode 100644
index 0000000..e2a2b3c
--- /dev/null
+++ b/ContactsCommon/res/values-nb/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Teksten er kopiert"
+ "Kopiér til utklippstavlen"
+ "Ring %s "
+ "Ring hjem"
+ "Ring mobilnummer"
+ "Ring arbeidsnummer"
+ "Ring faksnummer (arbeid)"
+ "Ring faksnummer (hjemme)"
+ "Ring personsøkernummer"
+ "Ring"
+ "Ring tilbakeringingsnummer"
+ "Ring til bilnummer"
+ "Ring til firma (hovednummer)"
+ "Ring ISDN-nummer"
+ "Ring hovednummer"
+ "Ring faksnummer"
+ "Ring radionummer"
+ "Ring teleksnummer"
+ "Ring TTY/TDD"
+ "Ring mobilnummer (arbeid)"
+ "Ring personsøkernummer (arbeid)"
+ "Ring %s "
+ "Ring multimediemeldingnummer"
+ "Send tekstmelding til %s "
+ "Send tekstmelding til hjemmenummer"
+ "Send tekstmelding til mobilnummer"
+ "Send tekstmelding til arbeidsnummer"
+ "Send tekstmelding til faksnummer (arbeid)"
+ "Send tekstmelding til faksnummer (hjemme)"
+ "Send tekstmelding til personsøkernummer"
+ "Send tekstmelding"
+ "Send tekstmelding til tilbakeringingsnummer"
+ "Send tekstmelding til bilnummer"
+ "Send tekstmelding til firma (hovednummer)"
+ "Send tekstmelding til ISDN-nummer"
+ "Send tekstmelding til hovednummer"
+ "Send tekstmelding til faksnummer"
+ "Send tekstmelding til radionummer"
+ "Send tekstmelding til teleksnummer"
+ "Send tekstmelding til TTY/TDD"
+ "Send tekstmelding til mobilnummer (arbeid)"
+ "Send tekstmelding til personsøkernummer (arbeid)"
+ "Send tekstmelding til %s "
+ "Send tekstmelding til multimediemeldingnummer"
+ "Utfør videoanrop"
+ "Vil du fjerne ofte kontaktede personer?"
+ "Du fjerner listen over ofte kontaktede personer i Kontakter- og Telefon-appene, og tvinger e-postappene til å lære seg adresseinnstillingene dine på nytt."
+ "Fjerner ofte kontaktede personer ..."
+ "Tilgjengelig"
+ "Borte"
+ "Opptatt"
+ "Kontakter"
+ "Annet"
+ "Katalog"
+ "Alle kontakter"
+ "Jeg"
+ "Søker ..."
+ "Fant mer enn %d ."
+ "Ingen kontakter"
+
+ %d er funnet
+ - 1 er funnet
+
+ "Hurtigkontakt for %1$s "
+ "(Uten navn)"
+ "Ofte oppringt"
+ "Ofte kontaktet"
+ "Se kontakt"
+ "Alle kontakter med telefonnumre"
+ "Se oppdateringer"
+ "Bare telefon (usynkronisert)"
+ "Navn"
+ "Kallenavn"
+ "Navn"
+ "Fornavn"
+ "Etternavn"
+ "Navneprefiks"
+ "Mellomnavn"
+ "Navnesuffiks"
+ "Fonetisk navn"
+ "Fonetisk fornavn"
+ "Fonetisk mellomnavn"
+ "Fonetisk etternavn"
+ "Telefon"
+ "E-post"
+ "Adresse"
+ "Nettprat"
+ "Organisasjon"
+ "Tilknytning"
+ "Spesielle datoer"
+ "Tekstmelding"
+ "Adresse"
+ "Firma"
+ "Stilling"
+ "Notater"
+ "SIP"
+ "Nettsted"
+ "Grupper"
+ "Send e-post (privat)"
+ "Send e-post (mobil)"
+ "Send e-post (jobb)"
+ "Send e-post"
+ "Send e-post (%s )"
+ "E-post"
+ "Gate"
+ "Postboks"
+ "Nabolag"
+ "By"
+ "Fylke/delstat"
+ "Postnummer"
+ "Land"
+ "Se hjemmeadressen"
+ "Se jobbadressen"
+ "Se adressen"
+ "Se %s -adressen"
+ "Nettprat med AIM"
+ "Nettprat med Windows Live"
+ "Nettprat med Yahoo"
+ "Nettprat med Skype"
+ "Nettprat med QQ"
+ "Nettprat med Google Talk"
+ "Nettprat med ICQ"
+ "Nettprat med Jabber"
+ "Nettprat"
+ "slett"
+ "Vis eller skjul navnefelt"
+ "Alle kontakter"
+ "Med stjerne"
+ "Tilpass"
+ "Kontakt"
+ "Alle andre kontakter"
+ "Alle kontakter"
+ "Fjern synkronisert gruppe"
+ "Legg til synkronisert gruppe"
+ "Flere grupper …"
+ "Hvis du fjerner «%s » fra synkroniseringen, fjernes også alle ugrupperte kontakter fra synkroniseringen."
+ "Lagrer visningsalternativer …"
+ "Ferdig"
+ "Avbryt"
+ "Kontakter i %s "
+ "Kontakter i tilpasset visning"
+ "Enkeltkontakt"
+ "Opprett kontakt under konto"
+ "Importér fra SIM-kort"
+ "Importér fra SIM-kortet ^1 – ^2 "
+ "Importér fra SIM-kortet %1$s "
+ "Importér fra .vcf-fil"
+ "Vil du avbryte importeringen av %s ?"
+ "Vil du avbryte eksporteringen av %s ?"
+ "Kunne ikke avbryte imp./eksp. av vCard"
+ "Ukjent feil."
+ "Kan ikke åpne «%s »: %s "
+ "Kunne ikke starte eksporteringen: %s ."
+ "Du har ingen kontakter som kan eksporteres."
+ "Du har slått av en nødvendig tillatelse."
+ "Det oppsto en feil under eksporteringen: %s ."
+ "Det obligatoriske filnavnet er for langt (%s )."
+ "SD-kortet inneholder for mange vCard-filer."
+ "Inn-/ut-feil"
+ "Ikke nok minne. Det er mulig at filen er for stor."
+ "Kan ikke analysere vCard pga. uventet årsak."
+ "Formatet støttes ikke."
+ "Kunne ikke hente metainformasjon for aktuell(e) vCard-fil(er)."
+ "En eller flere filer kan ikke importeres (%s)."
+ "Eksporteringen av %s er fullført."
+ "Eksportering av kontaktene er fullført."
+ "Eksporteringen av %s ble avbrutt."
+ "Eksporterer kontaktdata"
+ "Kontaktdataene dine eksporteres til: %s ."
+ "Kunne ikke hente databaseinformasjon."
+ "Du har ingen kontakter som kan eksporteres. Hvis du har kontakter på telefonen, kan det hende at dataleverandøren din ikke tillater at kontaktene eksporteres fra telefonen."
+ "vCard-oppretteren startet ikke som den skulle."
+ "Eksporten mislyktes"
+ "Kontaktdataene ble ikke eksportert.\nÅrsak: %s "
+ "Importerer %s "
+ "Kunne ikke lese vCard-dataene"
+ "Lesingen av vCard-dataene ble avbrutt"
+ "Importen av vCard-filen %s er fullført"
+ "Importeringen av %s ble avbrutt"
+ "%s blir snart importert."
+ "Filen importeres snart."
+ "Forespørselen om vCard-importering ble avvist. Prøv på nytt senere."
+ "%s blir snart eksportert."
+ "Filen eksporteres snart."
+ "Forespørselen om eksport av vCard ble avvist. Prøv på nytt senere."
+ "kontakt"
+ "Bufrer vCard for import til lokal, midlertidig lagring. Selve importeringen starter snart."
+ "Kunne ikke importere vCard."
+ "Fant ingen vCard-filer på SD-kortet."
+ "Kontakt mottatt via NFC"
+ "Vil du eksportere kontaktene?"
+ "Bufrer"
+ "SD-kortet kan ikke skannes. (Årsak: %s )"
+ "Importerer %s /%s : %s "
+ "Eksportér til .vcf-fil"
+ "Sortér etter"
+ "Fornavn"
+ "Etternavn"
+ "Navneformat"
+ "Fornavnet først"
+ "Etternavnet først"
+ "Del synlige kontakter"
+ "Delingen av synlige kontakter mislyktes."
+ "Importer/eksporter kontakter"
+ "Importer kontakter"
+ "Denne kontakten kan ikke deles."
+ "Søk"
+ "Kontakter som skal vises"
+ "Kontakter som skal vises"
+ "Definer tilpasset visning"
+ "Finn kontakter"
+ "Favoritter"
+ "Ingen kontakter."
+ "Ingen synlige kontakter."
+ "Ingen favoritter."
+ "Ingen kontakter i %s "
+ "Fjern ofte kontaktede personer"
+ "Velg SIM-kort"
+ "Kontoer"
+ "Importer/eksporter"
+ "via %1$s "
+ "%1$s via %2$s "
+ "avslutt søket"
+ "Slett søket"
+ "Alternativer for visning av kontakter"
+ "Konto"
+ "Bruk alltid dette for samtaler"
+ "Ring med"
+ "Ring med et notat"
+ "Skriv et notat du vil sende med anropet …"
+ "SEND OG RING"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ne-rNP/strings.xml b/ContactsCommon/res/values-ne-rNP/strings.xml
new file mode 100644
index 0000000..a515193
--- /dev/null
+++ b/ContactsCommon/res/values-ne-rNP/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "पाठको प्रतिलिपि गरियो"
+ "क्लिपबोर्डमा प्रतिलिपि गर्नुहोस्"
+ "%s लाई कल गर्नुहोस्"
+ "घरमा कल गर्नुहोस्"
+ "मोबाइलमा कल गर्नुहोस्"
+ "काममा कल गर्नुहोस्"
+ "कामको फ्याक्समा कल गर्नुहोस्"
+ "घरको फ्याक्समा कल गर्नुहोस्"
+ "पेजरलाई कल गर्नुहोस्"
+ "कल गर्नुहोस्"
+ "कलब्याक कल गर्नुहोस्"
+ "कारलाई कल गर्नुहोस्"
+ "कम्पनी मूललाई कल गर्नुहोस्"
+ "ISDN कल गर्नुहोस्"
+ "मुख्यलाई कल गर्नुहोस्"
+ "फ्याक्सलाई कल गर्नुहोस्"
+ "रेडियोमा कल गर्नुहोस्"
+ "टेलेक्सलाई कल गर्नुहोस्"
+ "TTY/TDD लाई कल गर्नुहोस्"
+ "काम मोबाइलमा कल गर्नुहोस्"
+ "काम पेजरमा कल गर्नुहोस्"
+ "%s लाई कल गर्नुहोस्"
+ "MMS लाई कल गर्नुहोस्"
+ "पाठ %s "
+ "पाठ घर"
+ "पाठ मोबाइल"
+ "पाठ काम"
+ "पाठ काम फ्याक्स"
+ "पाठ घर फ्याक्स"
+ "पाठ पेजर"
+ "पाठ"
+ "पाठ कलब्याक"
+ "पाठ कार"
+ "पाठ कम्पनी मूल"
+ "पाठ ISDN"
+ "पाठ मुख्य"
+ "पाठ फ्याक्स"
+ "पाठ रेडियो"
+ "पाठ टेलेक्स"
+ "पाठ TTY/TDD"
+ "पाठ काम मोबाइल"
+ "पाठ काम पेजर"
+ "पाठ %s "
+ "पाठ MMS"
+ "भिडियो कल बनाउनुहोस्"
+ "प्रायः सम्पर्क भएकालाई हटाउने?"
+ "तपाईंले सम्पर्कहरू र फोन अनुप्रयोगहरूमा बारम्बार सम्पर्क गरेको सूची खाली गर्नुहुनेछ र स्क्रयाचबाट तपाईंको ठेगाना प्राथमिकताहरू सिक्नको लागि इमेल अनुप्रयोगहरूलाई दबाब दिनुहुनेछ।"
+ "बारम्बार सम्पर्क गरिएकाहरूलाई मेटाउँदै ..."
+ "उपलब्ध"
+ "टाढा"
+ "व्यस्त"
+ "सम्पर्कहरू"
+ "अन्य"
+ "निर्देशिका"
+ "सबै सम्पर्कहरू"
+ "मलाई"
+ "खोजी गरिँदै..."
+ "%d भन्दा बढी पाइयो।"
+ "कुनै सम्पर्कहरू छैनन्।"
+
+ %d फेला पर्यो
+ - 1 फेला पर्यो
+
+ "%1$s लाई तत्काल सम्पर्क गर्नुहोस्"
+ "(नाम छैन)"
+ "बारम्बार कल गरिएको"
+ "लगातार सम्पर्क गरिएको"
+ "सम्पर्क हेर्नुहोस्"
+ "फोन नम्बर भएका सबै सम्पर्कहरू"
+ "अद्यावधिकहरू अवलोकन गर्नुहोस्"
+ "फोन मात्र, सिङ्क नभएको"
+ "नाम"
+ "उपनाम"
+ "नाम"
+ "नाम"
+ "थर"
+ "नाम उपसर्ग"
+ "बिचको नाम"
+ "नाम प्रत्यय"
+ "फोनेटिक नाम"
+ "ध्वनितात्त्विक नाम"
+ "फोनेटिक मध्य नाम"
+ "ध्वनितात्त्विक थर"
+ "फोन"
+ "इमेल गर्नुहोस्"
+ "ठेगाना"
+ "IM"
+ "संगठन"
+ "सम्बन्ध"
+ "विशेष मितिहरु"
+ "पाठ सन्देश"
+ "ठेगाना"
+ "कम्पनी"
+ "शीर्षक"
+ "टिप्पणीहरू"
+ "SIP"
+ "वेबसाइट"
+ "समूहहरू"
+ "घरमा इमेल गर्नुहोस्"
+ "इमेल मोबाइल"
+ "काममा इमेल गर्नुहोस्"
+ "इमेल गर्नुहोस्"
+ "%s लाई इमेल गर्नुहोस्"
+ "इमेल गर्नुहोस्"
+ "सडक"
+ "PO Box:"
+ "छिमेक"
+ "शहर"
+ "स्थिति"
+ "ZIP कोड"
+ "देश"
+ "घरको ठेगाना हेर्नुहोस्"
+ "कामको ठेगाना हेर्नुहोस्"
+ "ठेगाना हेर्नुहोस्"
+ "%s ठेगाना हेर्नुहोस्"
+ "AIM को प्रयोग गरेर च्याट गर्नुहोस्"
+ "विन्डोज लाइभ चलाई कुराकानी गर्नुहोस्"
+ "याहुको प्रयोग गरेर च्याट गर्नुहोस्"
+ "स्काइप चलाएर कुराकानी गर्नुहोस्"
+ "QQ चलाएर कुराकानी गर्नुहोस्"
+ "Google टक चलाएर कुराकानी गर्नुहोस्"
+ "ICQको प्रयोग गरेर च्याट गर्नुहोस्"
+ "जाब्बरको प्रयोग गरेर च्याट गर्नुहोस्"
+ "कुराकानी"
+ "मेटाउनुहोस्"
+ "नाम फिल्डहरू विस्तार गर्नुहोस् वा खुम्चाउनुहोस्"
+ "सबै सम्पर्कहरू"
+ "ताराङ्कित"
+ "व्यक्तिगत बनाउनुहोस्"
+ "ठेगाना"
+ "सबै अन्य सम्पर्कहरू"
+ "सबै सम्पर्कहरू"
+ "सिङ्क समूह हटाउनुहोस्"
+ "सिङ्क समूह थप्नुहोस्"
+ "अधिक समूहहरू..."
+ "\"%s \" सिङ्कबाट हटाइदै, सिङ्कबाट समूहमा विभाजन नभएका सम्पर्कहरूलाई हटाउने छ।"
+ "प्रदर्शन विकल्पहरू बचत गर्दै ..."
+ "भयो"
+ "रद्द गर्नुहोस्"
+ "%s मा सम्पर्कहरू"
+ "कस्टम दृश्यमा रहेका सम्पर्कहरू"
+ "एकल सम्पर्क"
+ "खाताअन्तर्गत सम्पर्क सिर्जना गर्नुहोस्"
+ "SIM कार्डबाट आयात गर्नुहोस्"
+ "SIM ^1 - ^2 बाट आयात"
+ "%1$s सिमबाट आयात"
+ ".vcf फाइलबाट आयात गर्नुहोस्"
+ "%s को आयात रद्द गर्ने हो?"
+ "%s को निर्यात रद्द गर्नहोस्?"
+ "VCard आयात/निर्यात रद्द गर्न सकेन"
+ "अज्ञात त्रुटि।"
+ "खोल्न सकेन \" %s : \" %s ."
+ "निर्यातकले सुरु गर्न सकेन \" %s \"।"
+ "निर्यात गर्न मिल्ने कुनै सम्पर्क छैन।"
+ "तपाईँले आवश्यक अनुमति असक्षम गर्नुभएको छ।"
+ "निर्यात गर्दा एउटा त्रुटि देखा पर्यो \" %s \"।"
+ "आवश्यक फाइल नाम धेरै लामो छ (\" %s \")।"
+ "अधिक vCard फाइलहरू SD कार्डमा छन्।"
+ "I/O त्रुटि"
+ "अप्रयाप्त मेमोरी। फाइल धेरै ठूलो हुन सक्छ।"
+ "एक अप्रत्यासित कारणले गर्दा vCard पार्स गर्न सकेन।"
+ "स्वरूप समर्थित छैन।"
+ "दिइएको vCard फाइल(हरू)को अधिजानकारी जम्मा गर्न सकेन।"
+ "एक वा बढी फाइलहरू आयात गर्न सकिएन (%s)।"
+ "%s निर्यात सकियो।"
+ "सम्पर्क ठेगानाहरू निर्यात गर्ने सकियो।"
+ "%s निर्यात रद्द गरियो।"
+ "सम्पर्क डेटा निर्यात हुँदै"
+ "तपाईँको सम्पर्क डेटा %s मा निर्यात हुँदै छ।"
+ "डेटाबेस जानकारी पाउन सकेन।"
+ "निर्यात योग्य सम्पर्कहरू छैनन्। यदि तपाईँसँग फोनमा सम्पर्कहरू छन् भने केही डेटा प्रदायकहरूले फोनबाट सम्पर्कहरू निर्यात गर्ने अनुमति दिँदैनन्।"
+ "vCard रचनाकार राम्ररी सुरु भएन।"
+ "निर्यात गर्न सकेन"
+ "सम्पर्क डेटा निर्यात गरिएको थिएन \n कारण: \" %s \""
+ "%s आयत गर्दै"
+ "VCard डेटा पढ्न सकेन"
+ "vCard डेटा पढ्ने रद्द भयो"
+ "vCard आयात गर्ने समाप्त भयो %s "
+ "%s आयत गर्ने रद्द भयो"
+ "%s केहीबेर मै आयात गरिने छ।"
+ "फाइल तुरुन्तै आयात गरिने छ।"
+ "vCard आयात अनुरोध अस्वीकृत गरियो। पछि फेरि प्रयास गर्नुहोस्।"
+ "%s तुरुन्तै निर्यात गरिने छ।"
+ "फाइल तुरुन्तै निर्यात गरिने छ।"
+ "vCard निर्यात अनुरोध अस्वीकार गरियो। कृपया फेरि प्रयास गर्नुहोस्।"
+ "सम्पर्क"
+ "स्थानीय अस्थायी भण्डारणका लागि vCard (हरू) क्यास गर्दै । वास्तविक आयात छिट्टै सुरु हुने छ।"
+ "VCard आयात गर्न सकेन।"
+ "SD कार्डमा कुनै पनि vCard फाइल भेटिएन।"
+ "NFCमा प्राप्त सम्पर्क"
+ "सम्पर्कहरू निर्यात गर्ने?"
+ "क्यासिङ हुँदै"
+ "SD कार्ड स्क्यान गर्न सकिएन । (कारण: \" %s \")"
+ "आयात गर्दै %s / %s : %s "
+ ".vcf फाइलमा निर्यात गर्नुहोस्"
+ "क्रमवद्घ गर्नुहोस्"
+ "नाम"
+ "थर"
+ "नाम ढाँचा"
+ "नाम पहिले"
+ "थर पहिला"
+ "देखिने सम्पर्कहरू साझेदारी गर्नुहोस्"
+ "देख्न सकिने सम्पर्कहरू साझेदारी गर्न असफल भयो।"
+ "आयात/निर्यात सम्पर्कहरू"
+ "सम्पर्क आयात गर्नुहोस्"
+ "यस सम्पर्कलाई साझेदारी गर्न सकिँदैन।"
+ "खोज्नुहोस्"
+ "प्रदर्शन गर्नका लागि सम्पर्कहरू"
+ "प्रदर्शन गर्न सम्पर्कहरू"
+ "कस्टम दृश्य परिभाषित गर्नुहोस्"
+ "सम्पर्क पत्ता लगाउनुहोस्"
+ "मनपर्नेहरू"
+ "कुनै सम्पर्कहरू छैनन्।"
+ "देखिने कुनै सम्पर्कहरू छैनन्।"
+ "मनपर्ने कुनै छैन।"
+ "%s मा कुनै सम्पर्कहरू छैनन्"
+ "बारम्बारताहरू हटाउनुहोस्"
+ "सिम कार्ड चयन गर्नुहोस्"
+ "खाताहरू"
+ "आयात/निर्यात"
+ "%1$s को मार्फत"
+ "%1$s मार्फत %2$s "
+ "खोजी गर्न रोक्नुहोस्"
+ "खोजी खाली गर्नुहोस्"
+ "सम्पर्क प्रदर्शन विकल्पहरू"
+ "खाता"
+ "कल गर्नका लागि यसको प्रयोग सधैं गर्नुहोस्"
+ "संग कल"
+ "टिप्पणीसँगै कल गर्नुहोस्"
+ "यस कलसँग पठाउन एक टिप्पणी टाइप गर्नुहोस्"
+ "पठाउनुहोस् र कल गर्नुहोस्"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-nl/strings.xml b/ContactsCommon/res/values-nl/strings.xml
new file mode 100644
index 0000000..0107ae0
--- /dev/null
+++ b/ContactsCommon/res/values-nl/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Tekst gekopieerd"
+ "Kopiëren naar klembord"
+ "%s bellen"
+ "Privé bellen"
+ "Mobiel bellen"
+ "Werk bellen"
+ "Fax werk bellen"
+ "Fax privé bellen"
+ "Pager bellen"
+ "Bellen"
+ "Terugbelnummer bellen"
+ "Auto bellen"
+ "Hoofdnummer bedrijf bellen"
+ "ISDN bellen"
+ "Hoofdnummer bellen"
+ "Fax bellen"
+ "Radio bellen"
+ "Telex bellen"
+ "TTY/TDD bellen"
+ "Mobiel nummer werk bellen"
+ "Pager werk bellen"
+ "%s bellen"
+ "MMS bellen"
+ "Sms\'en naar %s "
+ "Sms\'en naar huis"
+ "Sms\'en naar mobiel"
+ "Sms\'en naar werk"
+ "Sms\'en naar fax werk"
+ "Sms\'en naar fax privé"
+ "Sms\'en naar pager"
+ "Sms\'en"
+ "Sms\'en naar terugbelnummer"
+ "Sms\'en naar auto"
+ "Sms\'en naar hoofdnummer bedrijf"
+ "Sms\'en naar ISDN"
+ "Sms\'en naar hoofdnummer"
+ "Sms\'en naar fax"
+ "Sms\'en naar radio"
+ "Sms\'en naar telex"
+ "Sms\'en naar TTY/TDD"
+ "Sms\'en naar mobiel nummer werk"
+ "Sms\'en naar pager werk"
+ "Sms\'en naar %s "
+ "Sms\'en naar MMS"
+ "Videogesprek starten"
+ "Lijst regelmatige contacten wissen?"
+ "Je wist de lijst met contacten waarmee je regelmatig contact opneemt in de apps Contacten en Telefoon, en e-mailapps moeten je voorkeursadressen weer opnieuw leren."
+ "Regelmatige contacten wissen..."
+ "Beschikbaar"
+ "Niet beschikbaar"
+ "Bezet"
+ "Contacten"
+ "Overig"
+ "Directory"
+ "Alle contacten"
+ "Ik"
+ "Zoeken..."
+ "Meer dan %d gevonden."
+ "Geen contacten"
+
+ %d gevonden
+ - 1 gevonden
+
+ "Snelcontact voor %1$s "
+ "(Geen naam)"
+ "Vaak gebeld"
+ "Regelmatig contact mee opgenomen"
+ "Contact weergeven"
+ "Alle contacten met telefoonnummers"
+ "Updates bekijken"
+ "Telefoon-contact, synchr. uit"
+ "Naam"
+ "Bijnaam"
+ "Naam"
+ "Voornaam"
+ "Achternaam"
+ "Voorvoegsel van naam"
+ "Tweede voornaam"
+ "Achtervoegsel van naam"
+ "Fonetische naam"
+ "Fonetische voornaam"
+ "Fonetische tweede voornaam"
+ "Fonetische achternaam"
+ "Telefoon"
+ "E-mail"
+ "Adres"
+ "IM"
+ "Organisatie"
+ "Relatie"
+ "Speciale datums"
+ "Sms"
+ "Adres"
+ "Bedrijf"
+ "Titel"
+ "Opmerkingen"
+ "SIP"
+ "Website"
+ "Groepen"
+ "E-mailen naar huis"
+ "E-mailen naar mobiel"
+ "E-mailen naar werk"
+ "E-mailen"
+ "E-mailen naar %s "
+ "E-mailen"
+ "Straat"
+ "Postbus"
+ "Buurt"
+ "Stad"
+ "Staat"
+ "Postcode"
+ "Land"
+ "Thuisadres weergeven"
+ "Werkadres weergeven"
+ "Adres weergeven"
+ "%s adres weergeven"
+ "Chatten via AIM"
+ "Chatten via Windows Live"
+ "Chatten via Yahoo"
+ "Chatten via Skype"
+ "Chatten via QQ"
+ "Chatten via Google Talk"
+ "Chatten via ICQ"
+ "Chatten via Jabber"
+ "Chatten"
+ "verwijderen"
+ "Naamvelden uitvouwen of samenvouwen"
+ "Alle contacten"
+ "Met ster"
+ "Aanpassen"
+ "Contact"
+ "Alle andere contacten"
+ "Alle contacten"
+ "Synchronisatiegroep verwijderen"
+ "Synchronisatiegroep toevoegen"
+ "Meer groepen…"
+ "Als je \'%s \' verwijdert uit de synchronisatie, worden ook contacten die niet bij een groep horen uit de synchronisatie verwijderd."
+ "Weergaveopties opslaan..."
+ "Gereed"
+ "Annuleren"
+ "Contacten in %s "
+ "Contacten in aangepaste weergave"
+ "Eén contact"
+ "Contact in account maken"
+ "Importeren van simkaart"
+ "Importeren van simkaart ^1 - ^2 "
+ "Importeren van simkaart %1$s "
+ "Importeren uit VCF-bestand"
+ "Import van %s annuleren?"
+ "Export van %s annuleren?"
+ "Kan vCard-import/export niet annuleren"
+ "Onbekende fout."
+ "Kan \'%s \' niet openen: %s ."
+ "Kan het exportprogramma niet starten: \'%s \'."
+ "Er is geen contact dat kan worden geëxporteerd."
+ "Je hebt een vereist recht uitgeschakeld."
+ "Er is een fout opgetreden tijdens het exporteren: \'%s \'."
+ "Vereiste bestandsnaam is te lang (\'%s \')."
+ "Er staan te veel vCard-bestanden op de SD-kaart."
+ "I/O-fout"
+ "Onvoldoende geheugen. Het bestand is mogelijk te groot."
+ "Kan vCard om onverwachte reden niet parseren."
+ "De indeling wordt niet ondersteund."
+ "Kan metagegevens niet verzamelen uit vCard-bestand(en)."
+ "Kan een of meer bestanden niet importeren (%s)."
+ "Exporteren van %s voltooid."
+ "Contacten geëxporteerd."
+ "Exporteren van %s geannuleerd."
+ "Contactgegevens exporteren"
+ "Je contactgegevens worden geëxporteerd naar: %s ."
+ "Kan databasegegevens niet ophalen."
+ "Er zijn geen exporteerbare contacten. Als je wel contacten op je telefoon hebt opgeslagen, staat je gegevensprovider het exporteren van contacten van de telefoon mogelijk niet toe."
+ "De vCard-editor is niet correct gestart."
+ "Kan niet exporteren"
+ "De contactgegevens zijn niet geëxporteerd.\nReden: \'%s \'"
+ "%s importeren"
+ "Kan vCard-gegevens niet lezen"
+ "Lezen van vCard-gegevens geannuleerd"
+ "Importeren van vCard %s voltooid"
+ "Importeren van %s geannuleerd"
+ "%s wordt binnenkort geïmporteerd."
+ "Bestand wordt binnenkort geïmporteerd."
+ "Verzoek voor vCard-import is geweigerd. Probeer het later opnieuw."
+ "%s wordt binnenkort geëxporteerd."
+ "Dit bestand wordt binnenkort geëxporteerd."
+ "Verzoek voor vCard-export is geweigerd. Probeer het later opnieuw."
+ "contact"
+ "Bezig met opslaan van vCard(s) in de lokale tijdelijke opslag. Het daadwerkelijke importeren begint binnenkort."
+ "Kan vCard niet importeren."
+ "Geen vCard-bestand gevonden op SD-kaart."
+ "Contact via NFC"
+ "Contacten exporteren?"
+ "In cachegeheugen opslaan"
+ "De SD-kaart kan niet worden gescand. (Reden: \'%s \')"
+ "Importeren %s /%s : %s "
+ "Exporteren naar VCF-bestand"
+ "Sorteren op"
+ "Voornaam"
+ "Achternaam"
+ "Naamindeling"
+ "Voornaam eerst"
+ "Achternaam eerst"
+ "Zichtbare contacten delen"
+ "Kan zichtbare contacten niet delen."
+ "Contacten importeren/exporteren"
+ "Contacten importeren"
+ "Dit contact kan niet worden gedeeld."
+ "Zoeken"
+ "Zichtbare contacten"
+ "Zichtbare contacten"
+ "Aangepaste weergave definiëren"
+ "Contacten zoeken"
+ "Favorieten"
+ "Geen contacten."
+ "Geen zichtbare contacten."
+ "Geen favorieten."
+ "Geen contacten in %s "
+ "Regelmatige contacten wissen"
+ "Simkaart selecteren"
+ "Accounts"
+ "Importeren/exporteren"
+ "via %1$s "
+ "%1$s via %2$s "
+ "stoppen met zoeken"
+ "Zoekopdracht wissen"
+ "Opties voor contactweergave"
+ "Account"
+ "Altijd gebruiken voor oproepen"
+ "Bellen met"
+ "Oproep met een notitie"
+ "Typ een notitie om te verzenden met de oproep..."
+ "VERZENDEN EN BELLEN"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-pa-rIN/strings.xml b/ContactsCommon/res/values-pa-rIN/strings.xml
new file mode 100644
index 0000000..5a69286
--- /dev/null
+++ b/ContactsCommon/res/values-pa-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "ਟੈਕਸਟ ਕਾਪੀ ਕੀਤਾ"
+ "ਕਲਿਪਬੋਰਡ ਤੇ ਕਾਪੀ ਕਰੋ"
+ "%s ਨੂੰ ਕਾਲ ਕਰੋ"
+ "ਘਰ ਕਾਲ ਕਰੋ"
+ "ਮੋਬਾਈਲ ਤੇ ਕਾਲ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਕਾਲ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਦੀ ਫੈਕਸ ਤੇ ਕਾਲ ਕਰੋ"
+ "ਘਰ ਦੀ ਫੈਕਸ ਤੇ ਕਾਲ ਕਰੋ"
+ "ਪੇਜ਼ਰ ਤੇ ਕਾਲ ਕਰੋ"
+ "ਕਾਲ ਕਰੋ"
+ "ਕਾਲਬੈਕ ਕਾਲ ਕਰੋ"
+ "ਕਾਰ ਕਾਲ ਕਰੋ"
+ "ਕੰਪਨੀ ਦੇ ਮੁੱਖ ਨੰਬਰ ਤੇ ਕਾਲ ਕਰੋ"
+ "ISDN ਤੇ ਕਾਲ ਕਰੋ"
+ "ਮੁੱਖ ਨੰਬਰ ਤੇ ਕਾਲ ਕਰੋ"
+ "ਫੈਕਸ ਤੇ ਕਾਲ ਕਰੋ"
+ "ਰੇਡੀਓ ਕਾਲ ਕਰੋ"
+ "ਟੈਲੈਕਸ ਤੇ ਕਾਲ ਕਰੋ"
+ "TTY/TDD ਤੇ ਕਾਲ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਦੇ ਮੋਬਾਈਲ ਤੇ ਕਾਲ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਦੇ ਪੇਜ਼ਰ ਤੇ ਕਾਲ ਕਰੋ"
+ "%s ਨੂੰ ਕਾਲ ਕਰੋ"
+ "MMS ਕਾਲ ਕਰੋ"
+ "%s ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਘਰ ਦੇ ਨੰਬਰ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਮੋਬਾਈਲ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਦੇ ਨੰਬਰ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਦੀ ਫੈਕਸ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਘਰ ਦੀ ਫੈਕਸ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਪੇਜ਼ਰ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਟੈਕਸਟ ਕਰੋ"
+ "ਕਾਲਬੈਕ ਟੈਕਸਟ ਕਰੋ"
+ "ਕਾਰ ਟੈਕਸਟ ਕਰੋ"
+ "ਕੰਪਨੀ ਦੇ ਮੁੱਖ ਨੰਬਰ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ISDN ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਮੁੱਖ ਨੰਬਰ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਫੈਕਸ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਰੇਡੀਓ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਟੈਲੈਕਸ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "TTY/TDD ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਦੇ ਮੋਬਾਈਲ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਦੇ ਪੇਜ਼ਰ ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "%s ਨੂੰ ਟੈਕਸਟ ਕਰੋ"
+ "MMS ਤੇ ਟੈਕਸਟ ਕਰੋ"
+ "ਵੀਡੀਓ ਕਾਲ ਕਰੋ"
+ "ਕੀ ਅਕਸਰ ਸੰਪਰਕ ਕੀਤੇ ਜਾਣ ਵਾਲੇ ਹਟਾਉਣੇ ਹਨ?"
+ "ਤੁਸੀਂ ਵਾਰੀ-ਵਾਰੀ ਸੰਪਰਕ ਅਤੇ ਫੋਨ ਐਪਸ ਵਿੱਚ ਸੰਪਰਕ ਕੀਤੀ ਸੂਚੀ ਹਟਾਓਗੇ ਅਤੇ ਈਮੇਲ ਐਪਸ ਤੇ ਸਕ੍ਰੈਚ ਨਾਲ ਤੁਹਾਡੀਆਂ ਪਤਾ ਲਗਾਉਣ ਦੀਆਂ ਤਰਜੀਹਾਂ ਜਾਣਨ ਲਈ ਜ਼ੋਰ ਪਾਓਗੇ।"
+ "ਅਕਸਰ ਸੰਪਰਕ ਕੀਤੇ ਜਾਣ ਵਾਲੇ ਹਟਾ ਰਿਹਾ ਹੈ…"
+ "ਉਪਲਬਧ"
+ "ਦੂਰ"
+ "ਰੁੱਝਾ ਹੋਇਆ ਹੈ"
+ "ਸੰਪਰਕ"
+ "ਹੋਰ"
+ "ਡਾਇਰੈਕਟਰੀ"
+ "ਸਾਰੇ ਸੰਪਰਕ"
+ "ਮੈਂ"
+ "ਖੋਜ ਰਿਹਾ ਹੈ..."
+ "%d ਤੋਂ ਵੱਧ ਮਿਲੇ।"
+ "ਕੋਈ ਸੰਪਰਕ ਨਹੀਂ"
+
+ %d ਮਿਲਿਆ
+ %d ਮਿਲਿਆ
+
+ "%1$s ਲਈ ਤਤਕਾਲ ਸੰਪਰਕ"
+ "(ਕੋਈ ਨਾਮ ਨਹੀਂ)"
+ "ਅਕਸਰ ਕਾਲ ਕਰਨ ਵਾਲੇ"
+ "ਅਕਸਰ ਸੰਪਰਕ ਕੀਤੇ ਜਾਣ ਵਾਲੇ"
+ "ਸੰਪਰਕ ਦੇਖੋ"
+ "ਫੋਨ ਨੰਬਰਾਂ ਵਾਲੇ ਸਾਰੇ ਸੰਪਰਕ"
+ "ਅਪਡੇਟਾਂ ਦੇਖੋ"
+ "ਫੋਨ-ਓਨਲੀ, ਅਣਸਿੰਕਡ"
+ "ਨਾਮ"
+ "ਉਪਨਾਮ"
+ "ਨਾਮ"
+ "ਪਹਿਲਾ ਨਾਮ"
+ "ਆਖਰੀ ਨਾਮ"
+ "ਨਾਮ ਅਗੇਤਰ"
+ "ਵਿਚਕਾਰਲਾ ਨਾਮ"
+ "ਨਾਮ ਪਿਛੇਤਰ"
+ "ਧੁਨੀਆਤਮਿਕ ਨਾਮ"
+ "ਧੁਨੀਆਤਮਿਕ ਪਹਿਲਾ ਨਾਮ"
+ "ਧੁਨੀਆਤਮਿਕ ਵਿਚਕਾਰਲਾ ਨਾਮ"
+ "ਧੁਨੀਆਤਮਿਕ ਆਖਰੀ ਨਾਮ"
+ "ਫੋਨ"
+ "ਈਮੇਲ"
+ "ਪਤਾ"
+ "IM"
+ "ਕੰਪਨੀ"
+ "ਰਿਸ਼ਤਾ"
+ "ਖ਼ਾਸ ਤਾਰੀਖਾਂ"
+ "ਟੈਕਸਟ ਸੁਨੇਹਾ"
+ "ਪਤਾ"
+ "ਕੰਪਨੀ"
+ "ਸਿਰਲੇਖ"
+ "ਸੂਚਨਾਵਾਂ"
+ "SIP"
+ "ਵੈਬਸਾਈਟ"
+ "ਸਮੂਹ"
+ "ਘਰ ਈਮੇਲ ਕਰੋ"
+ "ਮੋਬਾਈਲ ਤੇ ਈਮੇਲ ਕਰੋ"
+ "ਦਫ਼ਤਰ ਈਮੇਲ ਕਰੋ"
+ "ਈਮੇਲ"
+ "%s ਨੂੰ ਈਮੇਲ ਕਰੋ"
+ "ਈਮੇਲ"
+ "ਗਲੀ"
+ "PO ਬੌਕਸ"
+ "ਗਵਾਂਢ"
+ "ਸ਼ਹਿਰ"
+ "ਰਾਜ"
+ "ਜ਼ਿੱਪ ਕੋਡ"
+ "ਦੇਸ਼"
+ "ਘਰ ਦਾ ਪਤਾ ਦੇਖੋ"
+ "ਦਫ਼ਤਰ ਦਾ ਪਤਾ ਦੇਖੋ"
+ "ਪਤਾ ਦੇਖੋ"
+ "%s ਪਤਾ ਦੇਖੋ"
+ "AIM ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "Windows Live ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "Yahoo ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "Skype ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "QQ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "Google Talk ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "ICQ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "Jabber ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
+ "ਚੈਟ"
+ "ਮਿਟਾਓ"
+ "ਨਾਮ ਖੇਤਰਾਂ ਦਾ ਵਿਸਤਾਰ ਕਰੋ ਜਾਂ ਨਸ਼ਟ ਕਰੋ"
+ "ਸਾਰੇ ਸੰਪਰਕ"
+ "ਸਟਾਰ ਵਾਲੇ"
+ "ਅਨੁਕੂਲਿਤ ਕਰੋ"
+ "ਸੰਪਰਕ"
+ "ਹੋਰ ਸਾਰੇ ਸੰਪਰਕ"
+ "ਸਾਰੇ ਸੰਪਰਕ"
+ "ਸਿੰਕ ਸਮੂਹ ਹਟਾਓ"
+ "ਸਿੰਕ ਸਮੂਹ ਜੋੜੋ"
+ "ਹੋਰ ਸਮੂਹ…"
+ "ਸਿੰਕ ਵਿੱਚੋਂ \"%s \" ਨੂੰ ਹਟਾਉਣ ਨਾਲ ਸਿੰਕ ਵਿੱਚੋਂ ਕੋਈ ਵੀ ਅਨਗਰੁੱਪ ਕੀਤੇ ਸੰਪਰਕ ਵੀ ਹਟ ਜਾਣਗੇ।"
+ "ਡਿਸਪਲੇ ਚੋਣਾਂ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"
+ "ਹੋ ਗਿਆ"
+ "ਰੱਦ ਕਰੋ"
+ "%s ਵਿੱਚ ਸੰਪਰਕ"
+ "ਕਸਟਮ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਸੰਪਰਕ"
+ "ਸਿੰਗਲ ਸੰਪਰਕ"
+ "ਖਾਤੇ ਦੇ ਅਧੀਨ ਸੰਪਰਕ ਬਣਾਓ"
+ "SIM ਕਾਰਡ ਵਿੱਚੋਂ ਆਯਾਤ ਕਰੋ"
+ "SIM ^1 - ^2 ਵਿੱਚੋਂ ਆਯਾਤ ਕਰੋ"
+ "SIM %1$s ਵਿੱਚੋਂ ਆਯਾਤ ਕਰੋ"
+ ".vcf ਫ਼ਾਈਲ ਤੋਂ ਆਯਾਤ ਕਰੋ"
+ "ਕੀ %s ਦਾ ਆਯਾਤ ਰੱਦ ਕਰਨਾ ਹੈ?"
+ "ਕੀ %s ਦਾ ਨਿਰਯਾਤ ਰੱਦ ਕਰਨਾ ਹੈ?"
+ "vCard ਆਯਾਤ/ਨਿਰਯਾਤ ਰੱਦ ਨਹੀਂ ਕਰ ਸਕਿਆ"
+ "ਅਗਿਆਤ ਅਸ਼ੁੱਧੀ।"
+ "\"%s \" ਨੂੰ ਖੋਲ੍ਹ ਨਹੀਂ ਸਕਿਆ: %s ।"
+ "ਐਕਸਪੋਰਟ ਚਾਲੂ ਨਹੀੰ ਕਰ ਸਕਿਆ: \"%s \"।"
+ "ਕੋਈ ਵੀ ਨਿਰਯਾਤ ਕਰਨ ਯੋਗ ਸੰਪਰਕ ਨਹੀਂ ਹਨ।"
+ "ਤੁਸੀਂ ਇੱਕ ਲੁੜੀਂਦੀ ਅਨੁਮਤੀ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਇਆ ਹੈ।"
+ "ਨਿਰਯਾਤ ਕਰਨ ਦੌਰਾਨ ਇੱਕ ਅਸ਼ੁੱਧੀ ਵਾਪਰੀ: \"%s \"।"
+ "ਲੁੜੀਂਦਾ ਫਾਈਲ ਨਾਮ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵੱਡਾ ਹੈ (\"%s \")।"
+ "SD ਕਾਰਡ ਤੇ ਬਹੁਤ ਜ਼ਿਆਦਾ vCard ਫਾਈਲਾਂ ਹਨ।"
+ "I/O ਅਸ਼ੁੱਧੀ"
+ "ਮੈਮਰੀ ਕਾਫ਼ੀ ਨਹੀਂ। ਫਾਈਲ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵੱਡੀ ਹੋ ਸਕਦੀ ਹੈ।"
+ "ਇੱਕ ਅਕਲਪਿਤ ਕਾਰਨ ਕਰਕੇ vCard ਪਾਰਸ ਨਹੀਂ ਕਰ ਸਕਿਆ।"
+ "ਫੌਰਮੈਟ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।"
+ "ਦਿੱਤੀ ਗਈ vCard ਫਾਈਲ(ਫਾਈਲਾਂ) ਦੀ meta ਜਾਣਕਾਰੀ ਇਕੱਤਰ ਨਹੀਂ ਕਰ ਸਕਿਆ।"
+ "ਇੱਕ ਜਾਂ ਵੱਧ ਫਾਈਲਾਂ ਆਯਾਤ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕੀਆਂ (%s)।"
+ "%s ਨੂੰ ਨਿਰਯਾਤ ਕਰਨਾ ਪੂਰਾ ਹੋਇਆ।"
+ "ਸੰਪਰਕਾਂ ਨੂੰ ਨਿਰਯਾਤ ਕਰਨਾ ਪੂਰਾ ਹੋਇਆ।"
+ "%s ਨੂੰ ਨਿਰਯਾਤ ਕਰਨਾ ਰੱਦ ਕੀਤਾ।"
+ "ਸੰਪਰਕ ਡਾਟਾ ਨਿਰਯਾਤ ਕਰ ਰਿਹਾ ਹੈ"
+ "ਤੁਹਾਡਾ ਸੰਪਰਕ ਡਾਟਾ ਇਸ ਵਿੱਚ ਨਿਰਯਾਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ: %s ।"
+ "ਡਾਟਾਬੇਸ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰ ਸਕਿਆ।"
+ "ਨਿਰਯਾਤ ਕਰਨ ਯੋਗ ਕੋਈ ਸੰਪਰਕ ਨਹੀਂ ਹਨ। ਜੇਕਰ ਤੁਹਾਡੇ ਫੋਨ ਤੇ ਕੋਈ ਸੰਪਰਕ ਨਹੀਂ ਹਨ, ਤਾਂ ਕੁਝ ਡਾਟਾ ਪ੍ਰਦਾਤਾ ਸੰਪਰਕਾਂ ਨੂੰ ਫੋਨ ਤੋਂ ਨਿਰਯਾਤ ਕੀਤੇ ਜਾਣ ਦੀ ਆਗਿਆ ਨਹੀਂ ਵੀ ਦੇ ਸਕਦੇ।"
+ "vCard ਕੰਪੋਜ਼ਰ ਸਹੀ ਢੰਗ ਨਾਲ ਚਾਲੂ ਨਹੀਂ ਹੋਇਆ।"
+ "ਨਿਰਯਾਤ ਨਹੀਂ ਕਰ ਸਕਿਆ"
+ "ਸੰਪਰਕ ਡਾਟਾ ਨਿਰਯਾਤ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਸੀ।\nਕਾਰਨ: \"%s \""
+ "%s ਨੂੰ ਆਯਾਤ ਕਰ ਰਿਹਾ ਹੈ"
+ "vCard ਡਾਟਾ ਨਹੀਂ ਪੜ੍ਹ ਸਕਿਆ"
+ "vCard ਡਾਟਾ ਪੜ੍ਹਨਾ ਰੱਦ ਕੀਤਾ"
+ "vCard %s ਨੂੰ ਆਯਾਤ ਕਰਨਾ ਪੂਰਾ ਹੋਇਆ"
+ "%s ਨੂੰ ਆਯਾਤ ਕਰਨਾ ਰੱਦ ਕੀਤਾ।"
+ "%s ਨੂੰ ਥੋੜ੍ਹੀ ਦੇਰ ਵਿੱਚ ਆਯਾਤ ਕੀਤਾ ਜਾਏਗਾ।"
+ "ਫਾਈਲ ਥੋੜ੍ਹੀ ਦੇਰ ਵਿੱਚ ਆਯਾਤ ਕੀਤੀ ਜਾਏਗੀ।"
+ "vCard ਆਯਾਤ ਬੇਨਤੀ ਅਸਵੀਕਾਰ ਕੀਤੀ ਗਈ ਸੀ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
+ "%s ਨੂੰ ਥੋੜ੍ਹੀ ਦੇਰ ਵਿੱਚ ਨਿਰਯਾਤ ਕੀਤਾ ਜਾਏਗਾ।"
+ "ਫਾਈਲ ਥੋੜ੍ਹੀ ਦੇਰ ਵਿੱਚ ਨਿਰਯਾਤ ਕੀਤੀ ਜਾਏਗੀ।"
+ "vCard ਨਿਰਯਾਤ ਬੇਨਤੀ ਅਸਵੀਕਾਰ ਕੀਤੀ ਗਈ ਸੀ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
+ "ਸੰਪਰਕ"
+ "ਸਥਾਨਕ ਅਸਥਾਈ ਸਟੋਰੇਜ ਲਈ vCard ਕੈਚ ਕਰ ਰਿਹਾ ਹੈ। ਅਸਲੀ ਆਯਾਤ ਜਲਦੀ ਹੀ ਚਾਲੂ ਹੋਵੇਗਾ।"
+ "vCard ਆਯਾਤ ਨਹੀਂ ਕਰ ਸਕਿਆ।"
+ "SD ਕਾਰਡ ਤੇ ਕੋਈ vCard ਫਾਈਲ ਨਹੀਂ ਮਿਲੀ।"
+ "NFC ਤੇ ਪ੍ਰਾਪਤ ਕੀਤਾ ਸੰਪਰਕ"
+ "ਕੀ ਸੰਪਰਕ ਨਿਰਯਾਤ ਕਰਨੇ ਹਨ?"
+ "ਕੈਚ ਕਰ ਰਿਹਾ ਹੈ"
+ "SD ਕਾਰਡ ਸਕੈਨ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। (ਕਾਰਨ: \"%s \")"
+ "%s /%s ਨੂੰ ਆਯਾਤ ਕਰ ਰਿਹਾ ਹੈ: %s "
+ ".vcf ਫ਼ਾਈਲ ਵਿੱਚ ਨਿਰਯਾਤ ਕਰੋ"
+ "ਇਸ ਅਨੁਸਾਰ ਛਾਂਟੋ"
+ "ਪਹਿਲਾ ਨਾਮ"
+ "ਆਖਰੀ ਨਾਮ"
+ "ਨਾਮ ਫੌਰਮੈਟ"
+ "ਪਹਿਲਾਂ ਪਹਿਲਾ ਨਾਮ"
+ "ਪਹਿਲਾਂ ਆਖ਼ਰੀ ਨਾਮ"
+ "ਦਿੱਸਦੇ ਸੰਪਰਕ ਸ਼ੇਅਰ ਕਰੋ"
+ "ਦ੍ਰਿਸ਼ਮਾਨ ਸੰਪਰਕ ਸ਼ੇਅਰ ਕਰਨ ਵਿੱਚ ਅਸਫਲ।"
+ "ਸੰਪਰਕ ਆਯਾਤ/ਨਿਰਯਾਤ ਕਰੋ"
+ "ਸੰਪਰਕ ਆਯਾਤ ਕਰੋ"
+ "ਇਸ ਸੰਪਰਕ ਸ਼ੇਅਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"
+ "ਖੋਜੋ"
+ "ਡਿਸਪਲੇ ਕਰਨ ਲਈ ਸੰਪਰਕ"
+ "ਡਿਸਪਲੇ ਕਰਨ ਲਈ ਸੰਪਰਕ"
+ "ਕਸਟਮ ਦ੍ਰਿਸ਼ ਨਿਰਧਾਰਿਤ ਕਰੋ"
+ "ਸੰਪਰਕ ਲੱਭੋ"
+ "ਮਨਪਸੰਦ"
+ "ਕੋਈ ਸੰਪਰਕ ਨਹੀਂ।"
+ "ਕੋਈ ਦਿੱਸਦੇ ਸੰਪਰਕ ਨਹੀਂ।"
+ "ਕੋਈ ਮਨਪਸੰਦ ਨਹੀਂ।"
+ "%s ਵਿੱਚ ਕੋਈ ਸੰਪਰਕ ਨਹੀਂ"
+ "ਫ੍ਰੀਕਵੈਂਟਸ ਹਟਾਓ"
+ "SIM ਕਾਰਡ ਚੁਣੋ"
+ "ਖਾਤੇ"
+ "ਆਯਾਤ/ਨਿਰਯਾਤ ਕਰੋ"
+ "%1$s ਰਾਹੀਂ"
+ "%2$s ਰਾਹੀਂ %1$s "
+ "ਖੋਜ ਕਰਨਾ ਬੰਦ ਕਰੋ"
+ "ਖੋਜ ਹਟਾਓ"
+ "ਸੰਪਰਕ ਡਿਸਪਲੇ ਚੋਣਾਂ"
+ "ਖਾਤਾ"
+ "ਕਾਲਾਂ ਲਈ ਹਮੇਸ਼ਾਂ ਇਹ ਵਰਤੋ"
+ "ਇਸਦੇ ਨਾਲ ਕਾਲ ਕਰੋ"
+ "ਕੋਈ ਨੋਟ ਦੇ ਨਾਲ ਕਾਲ ਕਰੋ"
+ "ਕਾਲ ਦੇ ਨਾਲ ਭੇਜਣ ਲਈ ਕੋਈ ਨੋਟ ਟਾਈਪ ਕਰੋ ..."
+ "ਭੇਜੋ ਅਤੇ ਕਾਲ ਕਰੋ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-pl/strings.xml b/ContactsCommon/res/values-pl/strings.xml
new file mode 100644
index 0000000..f4dd8fb
--- /dev/null
+++ b/ContactsCommon/res/values-pl/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Tekst skopiowany"
+ "Skopiuj do schowka"
+ "Połącz – %s "
+ "Połącz – domowy"
+ "Połącz – komórka"
+ "Połącz – służbowy"
+ "Połącz – faks służbowy"
+ "Połącz – faks domowy"
+ "Połącz – pager"
+ "Zadzwoń"
+ "Połącz – numer zwrotny"
+ "Połącz – samochód"
+ "Połącz – firmowy główny"
+ "Połącz – ISDN"
+ "Połącz – numer główny"
+ "Połącz – faks"
+ "Połącz – radio"
+ "Połącz – teleks"
+ "Połącz – TTY/TDD"
+ "Połącz – służbowa komórka"
+ "Połącz – służbowy pager"
+ "Połącz – %s "
+ "Połącz – MMS"
+ "SMS – %s "
+ "SMS – domowy"
+ "SMS – komórka"
+ "SMS – służbowy"
+ "SMS – faks służbowy"
+ "SMS – faks domowy"
+ "SMS – pager"
+ "SMS"
+ "SMS – numer zwrotny"
+ "SMS – samochód"
+ "SMS – firmowy główny"
+ "SMS – ISDN"
+ "SMS – główny"
+ "SMS – faks"
+ "SMS – radio"
+ "SMS – teleks"
+ "SMS – TTY/TDD"
+ "SMS – służbowa komórka"
+ "SMS – służbowy pager"
+ "SMS – %s "
+ "SMS – MMS"
+ "Rozmowa wideo"
+ "Wyczyścić częste kontakty?"
+ "Wyczyścisz listę częstych kontaktów w aplikacjach Kontakty i Telefon. Aplikacje pocztowe będą musiały od nowa poznać Twoje preferencje adresowe."
+ "Czyszczę częste kontakty…"
+ "Dostępny"
+ "Nieobecny"
+ "Zajęty"
+ "Kontakty"
+ "Inne"
+ "Katalog"
+ "Wszystkie kontakty"
+ "Ja"
+ "Szukam…"
+ "Znaleziono więcej niż %d ."
+ "Brak kontaktów"
+
+ - Znaleziono
%d
+ - Znaleziono
%d
+ - Znaleziono
%d
+ - Znaleziono 1
+
+ "Szybki kontakt dla: %1$s "
+ "(Bez nazwy)"
+ "Częste połączenia"
+ "Częste kontakty"
+ "Pokaż kontakt"
+ "Wszystkie kontakty z numerami telefonów"
+ "Pokaż aktualizacje"
+ "Tylko telefon, brak synchronizacji"
+ "Imię i nazwisko"
+ "Pseudonim"
+ "Imię i nazwisko"
+ "Imię"
+ "Nazwisko"
+ "Tytuł przed nazwiskiem"
+ "Drugie imię"
+ "Tytuł po nazwisku"
+ "Nazwisko fonetycznie"
+ "Imię fonetycznie"
+ "Drugie imię fonetycznie"
+ "Nazwisko fonetycznie"
+ "Telefon"
+ "E-mail"
+ "Adres"
+ "Komunikator"
+ "Organizacja"
+ "Relacja"
+ "Specjalne daty"
+ "SMS"
+ "Adres"
+ "Firma"
+ "Tytuł"
+ "Uwagi"
+ "Adres SIP"
+ "Witryna"
+ "Grupy"
+ "E-mail – dom"
+ "E-mail – komórka"
+ "E-mail – praca"
+ "E-mail"
+ "E-mail – %s "
+ "E-mail"
+ "Ulica"
+ "Skrytka pocztowa"
+ "Dzielnica"
+ "Miasto"
+ "Stan"
+ "Kod pocztowy"
+ "Kraj"
+ "Pokaż adres domowy"
+ "Pokaż adres służbowy"
+ "Pokaż adres"
+ "Pokaż adres: %s "
+ "Czat w AIM"
+ "Czat w Windows Live"
+ "Czat w Yahoo"
+ "Czat w Skype"
+ "Czat w QQ"
+ "Czat w Google Talk"
+ "Czat w ICQ"
+ "Czat w Jabberze"
+ "Czat"
+ "usuń"
+ "Rozwiń lub zwiń pola imion i nazwisk"
+ "Wszystkie kontakty"
+ "Oznaczone gwiazdką"
+ "Zdefiniuj filtr"
+ "Kontakt"
+ "Wszystkie inne kontakty"
+ "Wszystkie kontakty"
+ "Usuń grupę synchronizacji"
+ "Dodaj grupę synchronizacji"
+ "Więcej grup…"
+ "Usunięcie grupy „%s ” z ustawień synchronizacji spowoduje też usunięcie wszelkich rozgrupowanych kontaktów z tych ustawień."
+ "Zapisuję opcje wyświetlania…"
+ "Gotowe"
+ "Anuluj"
+ "Kontakty na koncie %s "
+ "Kontakty spełniające kryteria"
+ "Jeden kontakt"
+ "Utwórz kontakt na koncie:"
+ "Importuj z karty SIM"
+ "Importuj z karty SIM ^1 – ^2 "
+ "Importuj z karty SIM %1$s "
+ "Importuj z pliku .vcf"
+ "Anulować importowanie pliku %s ?"
+ "Anulować eksportowanie pliku %s ?"
+ "Nie można anulować importu/eksportu vCard"
+ "Nieznany błąd."
+ "Nie udało się otworzyć pliku „%s ”: %s ."
+ "Nie udało się uruchomić programu eksportującego: „%s ”."
+ "Brak kontaktów, które można wyeksportować."
+ "Wyłączyłeś wymagane uprawnienia"
+ "Podczas eksportu wystąpił błąd: „%s ”."
+ "Wymagana nazwa pliku jest zbyt długa („%s ”)."
+ "Na karcie SD znajduje się zbyt wiele plików vCard."
+ "Błąd wejścia/wyjścia"
+ "Za mało pamięci. Plik może być zbyt duży."
+ "Nie można przeanalizować pliku vCard z nieoczekiwanego powodu."
+ "Format nie jest obsługiwany."
+ "Nie można zebrać metainformacji z podanych plików vCard."
+ "Nie można zaimportować co najmniej jednego pliku (%s)."
+ "Zakończono eksportowanie pliku %s ."
+ "Eksportowanie kontaktów zostało zakończone."
+ "Anulowano eksportowanie pliku %s ."
+ "Eksportowanie danych kontaktowych"
+ "Dane kontaktów są eksportowane do pliku %s ."
+ "Nie udało się pobrać informacji z bazy danych."
+ "Brak kontaktów do eksportu. Jeśli masz w telefonie kontakty, być może dostawcy danych nie zezwalają na ich eksport z telefonu."
+ "Obiekt tworzenia danych vCard nie uruchomił się poprawnie."
+ "Eksport nieudany"
+ "Dane kontaktów nie zostały wyeksportowane.\nPrzyczyna: „%s ”"
+ "Importowanie: %s "
+ "Nie udało się odczytać danych vCard"
+ "Anulowano odczyt danych kart vCard"
+ "Zakończono importowanie pliku vCard %s "
+ "Anulowano importowanie pliku %s "
+ "Plik %s zostanie za chwilę zaimportowany."
+ "Plik zostanie za chwilę zaimportowany."
+ "Żądanie importu danych vCard zostało odrzucone. Spróbuj ponownie później."
+ "Plik %s zostanie za chwilę wyeksportowany."
+ "Plik zostanie za chwilę wyeksportowany."
+ "Żądanie eksportu danych vCard zostało odrzucone. Spróbuj ponownie później."
+ "kontakt"
+ "Trwa buforowanie plików vCard w lokalnym obszarze tymczasowym. Właściwy import rozpocznie się za chwilę."
+ "Nie udało się zaimportować pliku vCard."
+ "Na karcie SD nie znaleziono żadnego pliku vCard."
+ "Kontakt odebrany przez NFC"
+ "Wyeksportować kontakty?"
+ "Buforowanie"
+ "Nie udało się przeszukać karty SD. (Przyczyna: „%s ”)"
+ "Importuję %s /%s : %s "
+ "Eksportuj do pliku .vcf"
+ "Sortuj według"
+ "Imię"
+ "Nazwisko"
+ "Format imienia i nazwiska"
+ "Najpierw imię"
+ "Najpierw nazwisko"
+ "Udostępnij widoczne kontakty"
+ "Nie udało się udostępnić widocznych kontaktów."
+ "Importuj/eksportuj kontakty"
+ "Importuj kontakty"
+ "Tego kontaktu nie można udostępnić."
+ "Wyszukiwarka"
+ "Kontakty do wyświetlenia"
+ "Kontakty do wyświetlenia"
+ "Zdefiniuj filtr"
+ "Znajdź kontakty"
+ "Ulubione"
+ "Brak kontaktów"
+ "Brak widocznych kontaktów"
+ "Brak ulubionych"
+ "Brak kontaktów w grupie %s "
+ "Wyczyść częste kontakty"
+ "Wybierz kartę SIM"
+ "Konta"
+ "Importuj/eksportuj"
+ "przez: %1$s "
+ "%1$s przez: %2$s "
+ "zatrzymaj wyszukiwanie"
+ "Wyczyść wyszukiwanie"
+ "Opcje wyświetlania kontaktów"
+ "Konto"
+ "Zawsze używaj do połączeń"
+ "Zadzwoń, używając"
+ "Połącz i wyślij notatkę"
+ "Wpisz notatkę, którą chcesz wysłać razem z połączeniem..."
+ "WYŚLIJ I POŁĄCZ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-pt-rBR/strings.xml b/ContactsCommon/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..4fa7d9e
--- /dev/null
+++ b/ContactsCommon/res/values-pt-rBR/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texto copiado"
+ "Copiar para a área de transferência"
+ "Ligar para %s "
+ "Ligar para residência"
+ "Ligar para celular"
+ "Ligar para trabalho"
+ "Ligar para fax comercial"
+ "Ligar para fax residencial"
+ "Ligar para pager"
+ "Ligar"
+ "Retornar chamada"
+ "Ligar para carro"
+ "Ligar para empresa (principal)"
+ "Ligar para ISDN"
+ "Ligar para principal"
+ "Ligar para fax"
+ "Ligar para rádio"
+ "Ligar para telex"
+ "Ligar para TTY/TDD"
+ "Ligar para celular comercial"
+ "Ligar para pager comercial"
+ "Ligar para %s "
+ "Ligar para MMS"
+ "Enviar SMS para %s "
+ "Enviar SMS para residência"
+ "Enviar SMS para celular"
+ "Enviar SMS para trabalho"
+ "Enviar SMS para fax comercial"
+ "Enviar SMS para fax residencial"
+ "Enviar SMS para pager"
+ "Enviar SMS"
+ "Enviar SMS para retorno de chamada"
+ "Enviar SMS para carro"
+ "Enviar SMS para empresa (principal)"
+ "Enviar SMS para ISDN"
+ "Enviar SMS para principal"
+ "Enviar SMS para fax"
+ "Enviar SMS para rádio"
+ "Enviar SMS para telex"
+ "Enviar SMS para TTY/TDD"
+ "Enviar SMS para celular comercial"
+ "Enviar SMS para pager comercial"
+ "Enviar SMS para %s "
+ "Enviar SMS para MMS"
+ "Fazer vídeo chamada"
+ "Apagar contatos frequentes?"
+ "Você apagará a lista de contatos frequentes nos apps Contatos e Telefone, fazendo com que os apps de e-mail tenham que redefinir suas preferências de endereçamento."
+ "Apagando contatos frequentes…"
+ "Disponível"
+ "Ausente"
+ "Ocupado"
+ "Contatos"
+ "Outro"
+ "Diretório"
+ "Todos os contatos"
+ "Eu"
+ "Pesquisando…"
+ "Mais de %d encontrados."
+ "Nenhum contato"
+
+ %d encontrados
+ %d encontrados
+
+ "Contato rápido de %1$s "
+ "(Sem nome)"
+ "Mais chamados"
+ "Contatos frequentes"
+ "Visualizar contato"
+ "Todos os contatos com números de telefone"
+ "Ver atualizações"
+ "Somente telefone, não sincronizado"
+ "Nome"
+ "Apelido"
+ "Nome"
+ "Nome"
+ "Sobrenome"
+ "Prefixo do nome"
+ "Nome do meio"
+ "Sufixo do nome"
+ "Nome fonético"
+ "Nome fonético"
+ "Nome do meio fonético"
+ "Sobrenome fonético"
+ "Telefone"
+ "E-mail"
+ "Endereço"
+ "IM"
+ "Organização"
+ "Relacionamento"
+ "Datas especiais"
+ "Enviar SMS"
+ "Endereço"
+ "Empresa"
+ "Título"
+ "Notas"
+ "SIP"
+ "Website"
+ "Grupos"
+ "Enviar e-mail para residencial"
+ "Enviar e-mail para celular"
+ "Enviar e-mail para comercial"
+ "Enviar e-mail"
+ "Enviar e-mail para %s "
+ "Enviar e-mail"
+ "Rua"
+ "Caixa postal"
+ "Bairro"
+ "Cidade"
+ "Estado"
+ "CEP"
+ "País"
+ "Ver endereço residencial"
+ "Ver endereço comercial"
+ "Ver endereço"
+ "Ver endereço de %s "
+ "Bater papo usando o AIM"
+ "Bater papo usando o Windows Live"
+ "Bater papo usando o Yahoo"
+ "Bater papo usando o Skype"
+ "Bater papo usando o QQ"
+ "Bater papo usando o Google Talk"
+ "Bater papo usando o ICQ"
+ "Bater papo usando o Jabber"
+ "Bater papo"
+ "excluir"
+ "Expandir ou recolher campos de nome"
+ "Todos os contatos"
+ "Com estrela"
+ "Personalizar"
+ "Contato"
+ "Todos os outros contatos"
+ "Todos os contatos"
+ "Remover sincronização do grupo"
+ "Adicionar grupo de sincronização"
+ "Mais grupos…"
+ "A remoção de \"%s \" da sincronização também removerá os contatos não agrupados da sincronização."
+ "Salvando opções de exibição…"
+ "Concluído"
+ "Cancelar"
+ "Contatos em %s "
+ "Cont. na vis. pers."
+ "Contato único"
+ "Criar contato na conta"
+ "Importar do cartão SIM"
+ "Importar do SIM ^1 - ^2 "
+ "Importar do SIM %1$s "
+ "Importar de arquivo .vcf"
+ "Cancelar a importação de %s ?"
+ "Cancelar a exportação de %s ?"
+ "Impossível cancelar imp./export. vCard"
+ "Erro desconhecido."
+ "Não foi possível abrir \"%s \": %s ."
+ "Não foi possível iniciar o exportador: \"%s \"."
+ "Não há contato exportável."
+ "Você desativou uma permissão obrigatória."
+ "Ocorreu um erro ao exportar: \"%s \"."
+ "O nome de arquivo exigido é muito longo (\"%s \")."
+ "Excesso de arquivos vCard no cartão SD."
+ "Erro E/S"
+ "Não há memória suficiente. O arquivo pode ser muito grande."
+ "Não foi possível analisar o vCard por um motivo inesperado."
+ "O formato não é suportado."
+ "Não foi possível coletar informações meta de determinados arquivos vCard."
+ "Um ou mais arquivos não puderam ser importados (%s)."
+ "Exportação de %s concluída."
+ "A exportação de contatos foi concluída."
+ "Exportação de %s cancelada."
+ "Exportando dados do contato"
+ "Seus dados de contato estão sendo exportados para: %s ."
+ "Não foi possível obter as informações do banco de dados."
+ "Não há contatos exportáveis. Se você tiver contatos em seu telefone, alguns provedores de dados podem não permitir que os contatos sejam exportados a partir do telefone."
+ "O criador do vCard não iniciou corretamente."
+ "Impossível exportar"
+ "Os dados de contato não foram exportados.\nMotivo: \"%s \""
+ "Importando %s "
+ "Não foi possível ler os dados do vCard"
+ "Leitura dos dados do vCard cancelada"
+ "Importação do vCard %s concluída"
+ "Importação do vCard %s cancelada"
+ "%s será importado em breve."
+ "O arquivo será importado em breve."
+ "O pedido de importação do vCard foi rejeitado. Tente novamente mais tarde."
+ "%s será exportado em breve."
+ "O arquivo será exportado em breve."
+ "O pedido de exportação do vCard foi rejeitado. Tente novamente mais tarde."
+ "contato"
+ "Armazenando VCards em cache no armazenamento temporário local. A importação real começará em breve."
+ "Não foi possível importar o vCard."
+ "Nenhum arquivo vCard encontrado no cartão SD."
+ "Contato via NFC"
+ "Exportar contatos?"
+ "Armazenando em cache"
+ "Não foi possível ler o cartão SD. Motivo: \"%s \"."
+ "Importando %s /%s : %s "
+ "Exportar p/ arquivo .vcf"
+ "Classificar por"
+ "Nome"
+ "Sobrenome"
+ "Formato de nome"
+ "Primeiro o nome"
+ "Primeiro o sobrenome"
+ "Compartilhar contatos visíveis"
+ "Falha ao compartilhar contatos visíveis."
+ "Importar/exportar contatos"
+ "Importar contatos"
+ "Este contato não pode ser compartilhado."
+ "Pesquisar"
+ "Contatos para exibição"
+ "Contatos para exibição"
+ "Def. vis. pers."
+ "Localizar contatos"
+ "Favoritos"
+ "Nenhum contato."
+ "Não há contatos visíveis."
+ "Nenhum favorito."
+ "Nenhum contato em %s "
+ "Apagar frequentes"
+ "Selecionar cartão SIM"
+ "Contas"
+ "Importar/exportar"
+ "via %1$s "
+ "%1$s via %2$s "
+ "parar de pesquisar"
+ "Limpar pesquisa"
+ "Opções de exibição de contato"
+ "Conta"
+ "Sempre usar esta opção para chamadas"
+ "Ligar com"
+ "Chamada com uma nota"
+ "Escreva uma nota para enviar com a chamada..."
+ "ENVIAR E LIGAR"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-pt-rPT/strings.xml b/ContactsCommon/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..dbb2e8c
--- /dev/null
+++ b/ContactsCommon/res/values-pt-rPT/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texto copiado"
+ "Copiar para a área de transferência"
+ "Telefonar para %s "
+ "Telefonar para casa"
+ "Telefonar para telemóvel"
+ "Telefonar para o trabalho"
+ "Telefonar para o faxe do trabalho"
+ "Telefonar para o faxe de casa"
+ "Telefonar para o pager"
+ "Telefonar"
+ "Telefonar para número de retorno de chamada"
+ "Telefonar para o telefone do carro"
+ "Telefonar para o número geral da empresa"
+ "Telefonar para um telefone ISDN"
+ "Telefonar para o número geral"
+ "Telefonar para o faxe"
+ "Telefonar para um radiotelefone"
+ "Telefonar para um telex"
+ "Telefonar para um teletipo (TTY/TDD)"
+ "Telefonar para o telemóvel do trabalho"
+ "Telefonar para o pager do trabalho"
+ "Telefonar para %s "
+ "Telefonar para um número MMS"
+ "Enviar mensagem de texto para %s "
+ "Enviar mensagem de texto para o telefone de casa"
+ "Enviar mensagem de texto para telemóvel"
+ "Enviar mensagem de texto para trabalho"
+ "Enviar mensagem de texto para o faxe do trabalho"
+ "Enviar mensagem de texto para o faxe de casa"
+ "Enviar mensagem de texto para o pager"
+ "Enviar mensagem de texto"
+ "Enviar mensagem de texto para um número de retorno de chamada"
+ "Enviar mensagem de texto para o telefone do carro"
+ "Enviar mensagem de texto para o número geral da empresa"
+ "Enviar mensagem de texto para um telefone ISDN"
+ "Enviar mensagem de texto para o número de telefone geral"
+ "Enviar mensagem de texto para o faxe"
+ "Enviar mensagem de texto para um radiotelefone"
+ "Enviar mensagem de texto para o telex"
+ "Enviar mensagem de texto para um teletipo (TTY/TDD)"
+ "Enviar mensagem de texto para o telemóvel do trabalho"
+ "Enviar mensagem de texto para o pager do trabalho"
+ "Enviar mensagem de texto para %s "
+ "Enviar mensagem de texto para um número MMS"
+ "Fazer videochamada"
+ "Limpar contactos frequentes?"
+ "Limpa a lista de contactos frequentes nas aplicações Contactos e Telemóvel e força as aplicações de email a aprenderem as suas preferências de endereço de raiz."
+ "A limpar contactos frequentes..."
+ "Disponível"
+ "Ausente"
+ "Ocupado(a)"
+ "Contactos"
+ "Outro"
+ "Diretório"
+ "Todos os contactos"
+ "Eu"
+ "A pesquisar…"
+ "Foram encontrados mais de %d ."
+ "Sem contactos"
+
+ %d encontrados
+ - 1 encontrado
+
+ "Contacto rápido para %1$s "
+ "(Sem nome)"
+ "Números de marcação frequente"
+ "Contactados frequentemente"
+ "Ver contacto"
+ "Todos os contactos com números de telefone"
+ "Ver atualizações"
+ "Apenas telemóvel, não sincronizado"
+ "Nome"
+ "Pseudónimo"
+ "Nome"
+ "Nome próprio"
+ "Apelido"
+ "Prefixo do nome"
+ "Primeiro apelido"
+ "Sufixo do nome"
+ "Nome (fonética)"
+ "Nome próprio fonético"
+ "Nome do meio fonético"
+ "Apelido fonético"
+ "Telefone"
+ "Email"
+ "Endereço"
+ "Chat"
+ "Entidade"
+ "Tipo de relação"
+ "Datas especiais"
+ "SMS"
+ "Endereço"
+ "Empresa"
+ "Título"
+ "Notas"
+ "SIP"
+ "Website"
+ "Grupos"
+ "Enviar email para residência"
+ "Enviar email para dispositivo móvel"
+ "Enviar email para emprego"
+ "Enviar email"
+ "Enviar email a %s "
+ "Enviar email"
+ "Rua"
+ "Apartado"
+ "Bairro"
+ "Cidade"
+ "Estado"
+ "Código postal"
+ "País"
+ "Ver endereço da residência"
+ "Ver endereço do emprego"
+ "Ver endereço"
+ "Ver endereço de %s "
+ "Chat através do AIM"
+ "Chat através do Windows Live"
+ "Chat através do Yahoo"
+ "Chat através do Skype"
+ "Chat através do QQ"
+ "Chat através do Google Talk"
+ "Chat através do ICQ"
+ "Chat através do Jabber"
+ "Chat"
+ "eliminar"
+ "Expandir ou reduzir campos dos nomes"
+ "Todos os contactos"
+ "Com estrela"
+ "Personalizar"
+ "Contacto"
+ "Todos os outros contactos"
+ "Todos os contactos"
+ "Remover grupo de sincronização"
+ "Adicionar grupo de sincronização"
+ "Mais grupos..."
+ "Ao remover \"%s \" da sincronização, removerá também quaisquer contactos não agrupados."
+ "A guardar opções de visualização..."
+ "Concluído"
+ "Cancelar"
+ "Contactos em %s "
+ "Contactos na vista personalizada"
+ "Contacto único"
+ "Criar contacto na conta"
+ "Importar do cartão SIM"
+ "Importar do SIM ^1 – ^2 "
+ "Importar do SIM %1$s "
+ "Importar de ficheiro .vcf"
+ "Cancelar a importação de %s ?"
+ "Cancelar a exportação de %s ?"
+ "Imposs. cancel. import./export. do vCard"
+ "Erro desconhecido."
+ "Não foi possível abrir \"%s \": %s ."
+ "Não foi possível iniciar o exportador: \"%s \"."
+ "Não existe um contacto exportável."
+ "Desativou uma autorização obrigatória."
+ "Ocorreu um erro durante a exportação: \"%s \"."
+ "Nome de ficheiro demasiado longo (\"%s \")."
+ "Existem demasiados ficheiros vCard no cartão SD."
+ "Erro de E/S"
+ "Memória insuficiente. O ficheiro pode ser demasiado grande."
+ "Não foi possível analisar o vCard por um motivo inesperado."
+ "O formato não é suportado."
+ "Não foi possível recolher meta informações de determinado(s) ficheiro(s) vCard."
+ "Não foi possível importar um ou mais ficheiros (%s)."
+ "A exportação de %s terminou."
+ "Exportação de contactos concluída."
+ "A exportação de %s foi cancelada."
+ "A exportar dados do contacto"
+ "Os dados de contactos estão a ser exportados para: %s ."
+ "Não foi possível obter informações da base de dados"
+ "Não existem contactos exportáveis. Se tiver contactos no seu telemóvel, alguns fornecedores de dados podem não permitir a exportação dos contactos a partir do telemóvel."
+ "O compositor vCard não iniciou corretamente."
+ "Impossível exportar"
+ "Os dados de contactos não foram exportados.\nMotivo: \"%s \""
+ "A importar %s "
+ "Não foi possível ler dados do vCard"
+ "A leitura de dados vCard foi cancelada"
+ "A importação do vCard terminou %s "
+ "A importação de %s foi cancelada"
+ "%s será importado em breve."
+ "O ficheiro será importado em breve."
+ "O pedido de importação do vCard foi rejeitado. Tente novamente mais tarde."
+ "O %s será exportado em breve."
+ "O ficheiro é exportado em breve."
+ "O pedido de exportação do vCard foi rejeitado. Tente novamente mais tarde."
+ "contacto"
+ "A colocar vCard(s) em cache no armazenamento temporário local. A importação efetiva começará brevemente."
+ "Não foi possível importar o vCard."
+ "Não foi encontrado nenhum ficheiro vCard no cartão SD."
+ "Contacto recebido através de NFC"
+ "Exportar contactos?"
+ "A colocar em cache"
+ "Não foi possível analisar o cartão SD. (Motivo: \"%s \")"
+ "A importar %s /%s : %s "
+ "Exportar p/ ficheiro .vcf"
+ "Ordenar por"
+ "Nome próprio"
+ "Apelido"
+ "Formato do nome"
+ "Nome próprio em primeiro lugar"
+ "Apelido em primeiro lugar"
+ "Partilhar contactos visíveis"
+ "Falha ao partilhar os contactos visíveis."
+ "Importar/exportar contactos"
+ "Importar contactos"
+ "Não é possível partilhar este contacto."
+ "Pesquisar"
+ "Contactos a apresentar"
+ "Contactos a apresentar"
+ "Definir vista personalizada"
+ "Localizar contactos"
+ "Favoritos"
+ "Sem contactos."
+ "Sem contactos visíveis"
+ "Sem favoritos."
+ "Sem contactos em %s "
+ "Limpar frequentes"
+ "Selecionar cartão SIM"
+ "Contas"
+ "Importar/exportar"
+ "através do %1$s "
+ "%1$s através do %2$s "
+ "parar de pesquisar"
+ "Limpar pesquisa"
+ "Opções de visualização de contactos"
+ "Conta"
+ "Utilizar sempre este para chamadas"
+ "Ao telefone com"
+ "Ligar com uma nota"
+ "Escrever uma nota para enviar com a chamada..."
+ "ENVIAR E LIGAR"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-pt/strings.xml b/ContactsCommon/res/values-pt/strings.xml
new file mode 100644
index 0000000..4fa7d9e
--- /dev/null
+++ b/ContactsCommon/res/values-pt/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texto copiado"
+ "Copiar para a área de transferência"
+ "Ligar para %s "
+ "Ligar para residência"
+ "Ligar para celular"
+ "Ligar para trabalho"
+ "Ligar para fax comercial"
+ "Ligar para fax residencial"
+ "Ligar para pager"
+ "Ligar"
+ "Retornar chamada"
+ "Ligar para carro"
+ "Ligar para empresa (principal)"
+ "Ligar para ISDN"
+ "Ligar para principal"
+ "Ligar para fax"
+ "Ligar para rádio"
+ "Ligar para telex"
+ "Ligar para TTY/TDD"
+ "Ligar para celular comercial"
+ "Ligar para pager comercial"
+ "Ligar para %s "
+ "Ligar para MMS"
+ "Enviar SMS para %s "
+ "Enviar SMS para residência"
+ "Enviar SMS para celular"
+ "Enviar SMS para trabalho"
+ "Enviar SMS para fax comercial"
+ "Enviar SMS para fax residencial"
+ "Enviar SMS para pager"
+ "Enviar SMS"
+ "Enviar SMS para retorno de chamada"
+ "Enviar SMS para carro"
+ "Enviar SMS para empresa (principal)"
+ "Enviar SMS para ISDN"
+ "Enviar SMS para principal"
+ "Enviar SMS para fax"
+ "Enviar SMS para rádio"
+ "Enviar SMS para telex"
+ "Enviar SMS para TTY/TDD"
+ "Enviar SMS para celular comercial"
+ "Enviar SMS para pager comercial"
+ "Enviar SMS para %s "
+ "Enviar SMS para MMS"
+ "Fazer vídeo chamada"
+ "Apagar contatos frequentes?"
+ "Você apagará a lista de contatos frequentes nos apps Contatos e Telefone, fazendo com que os apps de e-mail tenham que redefinir suas preferências de endereçamento."
+ "Apagando contatos frequentes…"
+ "Disponível"
+ "Ausente"
+ "Ocupado"
+ "Contatos"
+ "Outro"
+ "Diretório"
+ "Todos os contatos"
+ "Eu"
+ "Pesquisando…"
+ "Mais de %d encontrados."
+ "Nenhum contato"
+
+ %d encontrados
+ %d encontrados
+
+ "Contato rápido de %1$s "
+ "(Sem nome)"
+ "Mais chamados"
+ "Contatos frequentes"
+ "Visualizar contato"
+ "Todos os contatos com números de telefone"
+ "Ver atualizações"
+ "Somente telefone, não sincronizado"
+ "Nome"
+ "Apelido"
+ "Nome"
+ "Nome"
+ "Sobrenome"
+ "Prefixo do nome"
+ "Nome do meio"
+ "Sufixo do nome"
+ "Nome fonético"
+ "Nome fonético"
+ "Nome do meio fonético"
+ "Sobrenome fonético"
+ "Telefone"
+ "E-mail"
+ "Endereço"
+ "IM"
+ "Organização"
+ "Relacionamento"
+ "Datas especiais"
+ "Enviar SMS"
+ "Endereço"
+ "Empresa"
+ "Título"
+ "Notas"
+ "SIP"
+ "Website"
+ "Grupos"
+ "Enviar e-mail para residencial"
+ "Enviar e-mail para celular"
+ "Enviar e-mail para comercial"
+ "Enviar e-mail"
+ "Enviar e-mail para %s "
+ "Enviar e-mail"
+ "Rua"
+ "Caixa postal"
+ "Bairro"
+ "Cidade"
+ "Estado"
+ "CEP"
+ "País"
+ "Ver endereço residencial"
+ "Ver endereço comercial"
+ "Ver endereço"
+ "Ver endereço de %s "
+ "Bater papo usando o AIM"
+ "Bater papo usando o Windows Live"
+ "Bater papo usando o Yahoo"
+ "Bater papo usando o Skype"
+ "Bater papo usando o QQ"
+ "Bater papo usando o Google Talk"
+ "Bater papo usando o ICQ"
+ "Bater papo usando o Jabber"
+ "Bater papo"
+ "excluir"
+ "Expandir ou recolher campos de nome"
+ "Todos os contatos"
+ "Com estrela"
+ "Personalizar"
+ "Contato"
+ "Todos os outros contatos"
+ "Todos os contatos"
+ "Remover sincronização do grupo"
+ "Adicionar grupo de sincronização"
+ "Mais grupos…"
+ "A remoção de \"%s \" da sincronização também removerá os contatos não agrupados da sincronização."
+ "Salvando opções de exibição…"
+ "Concluído"
+ "Cancelar"
+ "Contatos em %s "
+ "Cont. na vis. pers."
+ "Contato único"
+ "Criar contato na conta"
+ "Importar do cartão SIM"
+ "Importar do SIM ^1 - ^2 "
+ "Importar do SIM %1$s "
+ "Importar de arquivo .vcf"
+ "Cancelar a importação de %s ?"
+ "Cancelar a exportação de %s ?"
+ "Impossível cancelar imp./export. vCard"
+ "Erro desconhecido."
+ "Não foi possível abrir \"%s \": %s ."
+ "Não foi possível iniciar o exportador: \"%s \"."
+ "Não há contato exportável."
+ "Você desativou uma permissão obrigatória."
+ "Ocorreu um erro ao exportar: \"%s \"."
+ "O nome de arquivo exigido é muito longo (\"%s \")."
+ "Excesso de arquivos vCard no cartão SD."
+ "Erro E/S"
+ "Não há memória suficiente. O arquivo pode ser muito grande."
+ "Não foi possível analisar o vCard por um motivo inesperado."
+ "O formato não é suportado."
+ "Não foi possível coletar informações meta de determinados arquivos vCard."
+ "Um ou mais arquivos não puderam ser importados (%s)."
+ "Exportação de %s concluída."
+ "A exportação de contatos foi concluída."
+ "Exportação de %s cancelada."
+ "Exportando dados do contato"
+ "Seus dados de contato estão sendo exportados para: %s ."
+ "Não foi possível obter as informações do banco de dados."
+ "Não há contatos exportáveis. Se você tiver contatos em seu telefone, alguns provedores de dados podem não permitir que os contatos sejam exportados a partir do telefone."
+ "O criador do vCard não iniciou corretamente."
+ "Impossível exportar"
+ "Os dados de contato não foram exportados.\nMotivo: \"%s \""
+ "Importando %s "
+ "Não foi possível ler os dados do vCard"
+ "Leitura dos dados do vCard cancelada"
+ "Importação do vCard %s concluída"
+ "Importação do vCard %s cancelada"
+ "%s será importado em breve."
+ "O arquivo será importado em breve."
+ "O pedido de importação do vCard foi rejeitado. Tente novamente mais tarde."
+ "%s será exportado em breve."
+ "O arquivo será exportado em breve."
+ "O pedido de exportação do vCard foi rejeitado. Tente novamente mais tarde."
+ "contato"
+ "Armazenando VCards em cache no armazenamento temporário local. A importação real começará em breve."
+ "Não foi possível importar o vCard."
+ "Nenhum arquivo vCard encontrado no cartão SD."
+ "Contato via NFC"
+ "Exportar contatos?"
+ "Armazenando em cache"
+ "Não foi possível ler o cartão SD. Motivo: \"%s \"."
+ "Importando %s /%s : %s "
+ "Exportar p/ arquivo .vcf"
+ "Classificar por"
+ "Nome"
+ "Sobrenome"
+ "Formato de nome"
+ "Primeiro o nome"
+ "Primeiro o sobrenome"
+ "Compartilhar contatos visíveis"
+ "Falha ao compartilhar contatos visíveis."
+ "Importar/exportar contatos"
+ "Importar contatos"
+ "Este contato não pode ser compartilhado."
+ "Pesquisar"
+ "Contatos para exibição"
+ "Contatos para exibição"
+ "Def. vis. pers."
+ "Localizar contatos"
+ "Favoritos"
+ "Nenhum contato."
+ "Não há contatos visíveis."
+ "Nenhum favorito."
+ "Nenhum contato em %s "
+ "Apagar frequentes"
+ "Selecionar cartão SIM"
+ "Contas"
+ "Importar/exportar"
+ "via %1$s "
+ "%1$s via %2$s "
+ "parar de pesquisar"
+ "Limpar pesquisa"
+ "Opções de exibição de contato"
+ "Conta"
+ "Sempre usar esta opção para chamadas"
+ "Ligar com"
+ "Chamada com uma nota"
+ "Escreva uma nota para enviar com a chamada..."
+ "ENVIAR E LIGAR"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ro/strings.xml b/ContactsCommon/res/values-ro/strings.xml
new file mode 100644
index 0000000..3fb98e2
--- /dev/null
+++ b/ContactsCommon/res/values-ro/strings.xml
@@ -0,0 +1,254 @@
+
+
+
+
+ "Text copiat"
+ "Copiați în clipboard"
+ "Apelaţi %s "
+ "Apelaţi numărul de domiciliu"
+ "Apelaţi numărul de mobil"
+ "Apelaţi numărul de serviciu"
+ "Apelaţi numărul de fax de serviciu"
+ "Apelaţi numărul de fax de domiciliu"
+ "Apelaţi numărul de pager"
+ "Apelaţi"
+ "Apelaţi un număr cu apelare inversă"
+ "Apelaţi numărul de maşină"
+ "Apelaţi numărul principal al companiei"
+ "Apelaţi ISDN"
+ "Apelaţi numărul principal"
+ "Apelaţi numărul de fax"
+ "Apelaţi un număr radio"
+ "Apelaţi un număr de telex"
+ "Apelaţi TTY/TDD"
+ "Apelaţi numărul de mobil de la serviciu"
+ "Apelaţi numărul de pager de serviciu"
+ "Apelaţi %s "
+ "Apelaţi MMS"
+ "Trimiteți un mesaj text către %s "
+ "Trimiteți un mesaj text către telefonul de domiciliu"
+ "Trimiteți un mesaj text către numărul de mobil"
+ "Trimiteți un mesaj text către numărul de serviciu"
+ "Trimiteți un mesaj text către un număr de fax de serviciu"
+ "Trimiteți un mesaj text către un număr de fax de domiciliu"
+ "Trimiteți un mesaj text către un număr de pager"
+ "Trimiteți un mesaj text"
+ "Trimiteți un mesaj text către un număr cu apelare inversă"
+ "Trimiteți un mesaj text către un număr de telefon de maşină"
+ "Trimiteți un mesaj text către numărul principal al companiei"
+ "Trimiteți un mesaj text către un număr ISDN"
+ "Trimiteți un mesaj text către numărul principal"
+ "Trimiteți un mesaj text către un număr de fax"
+ "Trimiteți un mesaj text către un număr radio"
+ "Trimiteți un mesaj text către un număr de telex"
+ "Trimiteți un mesaj text către TTY/TDD"
+ "Trimiteți un mesaj text către numărul de mobil de serviciu"
+ "Trimiteți un mesaj text către un număr de pager de serviciu"
+ "Trimiteți un mesaj text către %s "
+ "Trimiteți un mesaj text către un număr MMS"
+ "Inițiați un apel video"
+ "Ștergeţi pers. frecvent contactate?"
+ "Veți șterge lista persoanelor contactate frecvent din aplicațiile Agendă și Telefon și veți forța aplicațiile de e-mail să vă învețe preferințele pentru adrese de la zero."
+ "Se şterg pers. frecvent contactate…"
+ "Disponibil(ă)"
+ "Plecat(ă)"
+ "Ocupat(ă)"
+ "Agendă"
+ "Altele"
+ "Director"
+ "Toată Agenda"
+ "Eu"
+ "Se caută…"
+ "S-au găsit peste %d (de) persoane de contact."
+ "Nu există persoane de contact"
+
+ %d găsite
+ %d găsite
+ - Una găsită
+
+ "Contact rapid pentru %1$s "
+ "(Fără nume)"
+ "Apelate frecvent"
+ "Contactate frecvent"
+ "Afişaţi persoana de contact"
+ "Toate persoanele de contact cu numere de telefon"
+ "Afişaţi actualizări"
+ "Numai pe telefon, nesincronizată"
+ "Nume"
+ "Pseudonim"
+ "Nume"
+ "Prenume"
+ "Nume"
+ "Prefixul numelui"
+ "Al doilea prenume"
+ "Sufixul numelui"
+ "Nume fonetic"
+ "Prenume fonetic"
+ "Al doilea prenume fonetic"
+ "Nume fonetic"
+ "Telefon"
+ "Adrese de e-mail"
+ "Adrese poștale"
+ "IM"
+ "Organizație"
+ "Relaţie"
+ "Date speciale"
+ "Mesaj text"
+ "Adresă poştală"
+ "Companie"
+ "Titlu"
+ "Note"
+ "SIP"
+ "Site web"
+ "Grupuri"
+ "Trimiteți un e-mail la o adresă de e-mail de domiciliu"
+ "Trimiteți un e-mail către un telefon mobil"
+ "Trimiteți un e-mail la o adresă de e-mail de serviciu"
+ "Trimiteți un e-mail"
+ "Trimiteți un e-mail la %s "
+ "Trimiteți un e-mail"
+ "Stradă"
+ "Căsuţă poştală"
+ "Cartier"
+ "Oraş"
+ "Stat"
+ "Cod poştal"
+ "Țară"
+ "Vizualizaţi adresa de domiciliu"
+ "Vizualizaţi adresa de serviciu"
+ "Vizualizaţi adresa poştală"
+ "Vizualizaţi adresa %s "
+ "Conversaţi prin AIM"
+ "Conversaţi prin Windows Live"
+ "Conversaţi prin Yahoo"
+ "Conversaţi prin Skype"
+ "Conversaţi prin QQ"
+ "Conversaţi prin Google Talk"
+ "Conversaţi prin ICQ"
+ "Conversaţi prin Jabber"
+ "Conversaţi prin chat"
+ "ştergeţi"
+ "Extindeţi sau restrângeți câmpurile pentru nume"
+ "Toată agenda"
+ "Cu stea"
+ "Personalizați"
+ "Persoana de contact"
+ "Toate celelalte contacte"
+ "Toată agenda"
+ "Eliminați grup de sincronizare"
+ "Adăugați grup de sincronizare"
+ "Mai multe grupuri…"
+ "Eliminarea grupului „%s ” din sincronizare va elimina, de asemenea, din sincronizare orice persoană de contact care nu face parte dintr-un grup."
+ "Se salvează opțiunile de afișare…"
+ "Terminat"
+ "Anulați"
+ "Agenda din %s "
+ "Agenda în afișarea personalizată"
+ "O singură persoană de contact"
+ "Creați o persoană de contact în contul"
+ "Importați de pe cardul SIM"
+ "Importați de pe cardul SIM ^1 - ^2 "
+ "Importați de pe cardul SIM %1$s "
+ "Importați din fișier .vcf"
+ "Anulați importul fișierului %s ?"
+ "Anulați exportul fișierului %s ?"
+ "Anulare import/export vCard nereușită"
+ "Eroare necunoscută."
+ "Nu s-a putut deschide fișierul „%s ”: %s ."
+ "Nu s-a putut inițializa instrumentul de export: „%s ”"
+ "Nu există persoane de contact care să poată fi exportate."
+ "Ați dezactivat o permisiune necesară."
+ "A apărut o eroare în timpul exportului: „%s ”."
+ "Numele de fișier solicitat este prea lung („%s ”)."
+ "Există prea multe fișiere vCard pe cardul SD."
+ "Eroare I/O"
+ "Memoria este insuficientă (probabil fișierul este prea mare)."
+ "Nu s-au putut analiza datele de pe vCard dintr-un motiv neașteptat."
+ "Formatul nu este acceptat."
+ "Nu s-au putut colecta metainformațiile pentru fișierele vCard indicate."
+ "Unul sau mai multe fișiere nu s-au putut importa (%s)."
+ "S-a finalizat exportul fișierului %s ."
+ "Persoanele de contact au fost exportate."
+ "Exportul fișierului %s a fost anulat."
+ "Se exportă datele persoanelor de contact"
+ "Datele persoanelor de contact se exportă în fișierul: %s ."
+ "Nu s-au putut obține informații din baza de date."
+ "Nu există persoane de contact care să poată fi exportate. Dacă aveți persoane de contact pe telefon, este posibil ca exportul acestora de pe telefon să fie interzis de unii furnizori de date."
+ "Editorul de vCard nu a pornit în mod corespunzător."
+ "Nu s-a putut exporta"
+ "Datele persoanelor de contact nu au fost exportate.\nMotivul: „%s ”"
+ "Se importă %s "
+ "Nu s-au putut citi datele de pe vCard"
+ "Citirea datelor vCard a fost anulată"
+ "S-a finalizat importul fișierului vCard %s "
+ "Importul %s a fost anulat"
+ "%s va fi importat în curând."
+ "Fișierul va fi importat în curând."
+ "Solicitarea de import a fișierului vCard a fost respinsă. Încercați din nou mai târziu."
+ "%s va fi exportat în curând."
+ "Fișierul va fi exportat în curând."
+ "Solicitarea de export a fișierului vCard a fost respinsă. Încercați din nou mai târziu."
+ "persoană de contact"
+ "Fișierele vCard se stochează în memoria cache într-un spațiu de stocare local temporar. Importul propriu-zis va începe în curând."
+ "Nu s-a putut importa fișierul vCard."
+ "Nu s-a găsit niciun fișier vCard pe cardul SD."
+ "Persoană primită prin NFC"
+ "Exportați agenda?"
+ "Se stochează în cache"
+ "Cardul SD nu s-a putut scana. (Motivul: „%s ”)"
+ "Se importă %s /%s : %s "
+ "Exportați ca fișier .vcf"
+ "Sortați după"
+ "Prenume"
+ "Nume"
+ "Format pentru nume"
+ "Întâi prenumele"
+ "Întâi numele"
+ "Distribuiți persoanele de contact vizibile"
+ "Persoanele de contact vizibile nu au putut fi trimise."
+ "Importați/exportați agenda"
+ "Importați agenda"
+ "Nu se poate permite accesul la această intrare."
+ "Căutați"
+ "Agendă de afișat"
+ "Agendă de afișat"
+ "Definiți afișarea personalizată"
+ "Găsiți persoane de contact"
+ "Preferate"
+ "Nicio persoană de contact."
+ "Nicio persoană de contact nu este vizibilă."
+ "Nu există preferate."
+ "Nicio persoană de contact în %s "
+ "Ștergeți contacte frecvente"
+ "Selectați cardul SIM"
+ "Conturi"
+ "Importați/exportați"
+ "prin %1$s "
+ "%1$s prin %2$s "
+ "nu mai căutați"
+ "Ștergeți căutarea"
+ "Opțiuni de afișare pentru persoanele de contact"
+ "Cont"
+ "Utilizați mereu pentru apeluri"
+ "Apelați cu"
+ "Apelați împreună cu o notă"
+ "Introduceți o notă ca să o trimiteți împreună cu apelul..."
+ "TRIMITEȚI ȘI APELAȚI"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ru/strings.xml b/ContactsCommon/res/values-ru/strings.xml
new file mode 100644
index 0000000..8487d97
--- /dev/null
+++ b/ContactsCommon/res/values-ru/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Текст скопирован"
+ "Копировать в буфер обмена"
+ "Вызов:%s "
+ "Домашний"
+ "Мобильный"
+ "Рабочий"
+ "Рабочий факс"
+ "Домашний факс"
+ "Пейджер"
+ "Набор"
+ "Обратный вызов"
+ "Телефон в машине"
+ "Телефон офиса"
+ "Номер ISDN"
+ "Основной телефон"
+ "Факс"
+ "Радиотелефон"
+ "Телекс"
+ "Телетайп"
+ "Рабочий мобильный"
+ "Рабочий пейджер"
+ "Вызов: %s "
+ "Номер MMS"
+ "SMS: %s "
+ "SMS: домашний"
+ "SMS: мобильный"
+ "SMS: рабочий"
+ "SMS: рабочий факс"
+ "SMS: домашний факс"
+ "SMS: пейджер"
+ "SMS"
+ "SMS: номер обратного вызова"
+ "SMS: телефон в машине"
+ "SMS: телефон офиса"
+ "SMS: номер ISDN"
+ "SMS: основной номер"
+ "SMS: факс"
+ "SMS: радиотелефон"
+ "SMS: телекс"
+ "SMS: телетайп"
+ "SMS: рабочий мобильный"
+ "SMS: рабочий пейджер"
+ "SMS: %s "
+ "SMS: номер MMS"
+ "Начать видеовстречу"
+ "Очистить список популярных контактов?"
+ "Список популярных контактов в приложениях \"Контакты\" и \"Телефон\" будет очищен, а приложения электронной почты начнут запоминать адреса заново."
+ "Подождите…"
+ "Доступен"
+ "Отсутствует"
+ "Не беспокоить"
+ "Контакты"
+ "Другое"
+ "Каталог"
+ "Все контакты"
+ "Я"
+ "Поиск…"
+ "Найдено контактов: более %d "
+ "Ничего не найдено."
+
+ - Найден
%d контакт
+ - Найдено
%d контакта
+ - Найдено
%d контактов
+ - Найдено
%d контакта
+
+ "Быстрый вызов, контакт: %1$s "
+ "Имя не указано"
+ "Часто вызываемые"
+ "Часто набираемые"
+ "Показать сведения о контакте"
+ "Контакты с номерами телефонов"
+ "Просмотреть обновления"
+ "Только телефон, без синхронизации"
+ "Имя"
+ "Псевдоним"
+ "Полное имя"
+ "Имя"
+ "Фамилия"
+ "Префикс имени"
+ "Отчество"
+ "Суффикс имени"
+ "Транскрипция имени"
+ "Транскрипция имени"
+ "Транскрипция отчества"
+ "Транскрипция фамилии"
+ "Телефон"
+ "Эл. почта"
+ "Адрес"
+ "Чат"
+ "Организация"
+ "Связи"
+ "Значимые даты"
+ "SMS"
+ "Адрес"
+ "Организация"
+ "Должность"
+ "Примечания"
+ "SIP"
+ "Веб-сайт"
+ "Группы"
+ "Написать на личный адрес"
+ "Написать на мобильную эл. почту"
+ "Написать на рабочий адрес"
+ "Написать письмо"
+ "Написать письмо (%s )"
+ "Написать письмо"
+ "Улица"
+ "Абонентский ящик"
+ "Район"
+ "Город"
+ "Регион"
+ "Индекс"
+ "Страна"
+ "Посмотреть домашний адрес"
+ "Посмотреть рабочий адрес"
+ "Посмотреть адрес"
+ "Посмотреть адрес (%s )"
+ "Чат через AIM"
+ "Чат через Windows Live"
+ "Чат через Yahoo"
+ "Чат через Skype"
+ "Чат через QQ"
+ "Чат через Google Talk"
+ "Чат через ICQ"
+ "Чат через Jabber"
+ "Чат"
+ "удалить"
+ "Показать/скрыть дополнительные поля"
+ "Все контакты"
+ "Помеченные"
+ "Настроить"
+ "Выбранный контакт"
+ "Остальные контакты"
+ "Все контакты"
+ "Удалить группу синхронизации"
+ "Добавить группу синхронизации"
+ "Другие группы"
+ "Исключение из синхронизации группы \"%s \" приведет к исключению из синхронизации всех контактов, не относящихся к какой-либо группе."
+ "Сохранение параметров…"
+ "Готово"
+ "Отмена"
+ "Контакты аккаунта \"%s \""
+ "Пользовательский фильтр"
+ "Один контакт"
+ "Создать контакт в аккаунте"
+ "Импорт с SIM-карты"
+ "Импорт с SIM-карты ^1 (^2 )"
+ "Импорт с SIM-карты %1$s "
+ "Импортировать из файла VCF"
+ "Отменить импорт файла \"%s \"?"
+ "Отменить экспорт файла \"%s \"?"
+ "Не удалось отменить импорт/экспорт vCard"
+ "Неизвестная ошибка."
+ "Не удалось открыть файл \"%s \". %s "
+ "Не удалось запустить инструмент экспорта. %s "
+ "Нет контактов для экспорта."
+ "Отсутствует необходимое разрешение."
+ "Произошла ошибка экспорта. %s "
+ "Слишком длинное название файла (\"%s \")."
+ "Слишком много файлов vCard на SD-карте."
+ "Ошибка ввода-вывода"
+ "Недостаточно памяти. Возможно, файл слишком большой."
+ "Не удалось выполнить синтаксический анализ файла vCard."
+ "Формат не поддерживается."
+ "Не удалось собрать метаданные файлов vCard."
+ "Не удалось импортировать один или несколько файлов (%s)."
+ "Экспорт файла \"%s \" завершен"
+ "Экспорт контактов завершен."
+ "Экспорт файла \"%s \" отменен"
+ "Экспорт данных контакта"
+ "Данные экспортируются в файл \"%s \"."
+ "База данных недоступна."
+ "Не найдены контакты для экспорта. Возможно, экспорт контактов с телефона не поддерживается поставщиком услуг передачи данных."
+ "Сбой при запуске редактора vCard."
+ "Ошибка экспорта"
+ "Не удалось экспортировать данные.\nПричина: %s "
+ "Импорт файла \"%s \"…"
+ "Данные файла vCard не прочитаны"
+ "Чтение данных vCard отменено"
+ "Файл \"%s \" импортирован"
+ "Импорт файла \"%s \" отменен"
+ "Импорт файла \"%s \" скоро начнется."
+ "Импорт файла скоро начнется."
+ "Запрос на импорт данных vCard отклонен. Повторите попытку позже."
+ "Экспорт файла \"%s \" скоро начнется."
+ "Файл будет экспортирован в ближайшее время."
+ "Запрос на экспорт данных vCard отклонен. Повторите попытку позже."
+ "контакт"
+ "Выполняется кеширование файлов vCard в локальное временное хранилище, после чего начнется импорт."
+ "Не удалось импортировать данные vCard."
+ "На SD-карте нет файлов vCard."
+ "Получено по NFC"
+ "Экспортировать контакты?"
+ "Кеширование…"
+ "Не удалось сканировать SD-карту. %s "
+ "Импорт %s из %s : %s ..."
+ "Экспортировать в файл VCF"
+ "Сортировка"
+ "Имя"
+ "Фамилия"
+ "Формат имени"
+ "Сначала имя"
+ "Сначала фамилия"
+ "Передать видимые контакты"
+ "Не удалось поделиться видимыми контактами."
+ "Импорт/экспорт контактов"
+ "Импорт контактов"
+ "Не удалось передать данные"
+ "Поиск"
+ "Фильтр контактов"
+ "Фильтр контактов"
+ "Выберите группы"
+ "Поиск контактов"
+ "Избранное"
+ "Нет контактов"
+ "Нет видимых контактов"
+ "Нет избранных контактов"
+ "Нет контактов в группе \"%s \""
+ "Очистить популярные"
+ "Выбрать SIM-карту"
+ "Аккаунты"
+ "Импорт/экспорт"
+ "в %1$s "
+ "%1$s в %2$s "
+ "прекратить поиск"
+ "Очистить условия поиска"
+ "Отображение контактов"
+ "Аккаунт"
+ "Всегда использовать для звонков"
+ "Аккаунт для звонка"
+ "Написать сообщение абоненту"
+ "Введите текст…"
+ "ОТПРАВИТЬ СООБЩЕНИЕ И ПОЗВОНИТЬ"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-si-rLK/strings.xml b/ContactsCommon/res/values-si-rLK/strings.xml
new file mode 100644
index 0000000..d6f1edf
--- /dev/null
+++ b/ContactsCommon/res/values-si-rLK/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "පෙළ පිටපත් කරන ලදී"
+ "පසුරු පුවරුවට පිටපත් කරන්න"
+ "%s අමතන්න"
+ "ගෙදර අමතන්න"
+ "ජංගම දුරකථනය අමතන්න"
+ "කාර්යාලය අමතන්න"
+ "කාර්යාල ෆැක්ස් අමතන්න"
+ "නිවෙස් ෆැක්ස් අමතන්න"
+ "පේජර් අමතන්න"
+ "ඇමතුම"
+ "නැවත ඇමතුම අමතන්න"
+ "කාරය අමතන්න"
+ "ආයතන මූලිකය අමතන්න"
+ "ISDN අමතන්න"
+ "මූලිකය අමතන්න"
+ "ෆැක්ස් අමතන්න"
+ "රේඩියෝව අමතන්න"
+ "ටෙලෙක්ස් අමතන්න"
+ "TTY/TDD අමතන්න"
+ "කාර්යාල ජංගම දුරකථනය අමතන්න"
+ "කාර්යාල පේජරය අමතන්න"
+ "%s අමතන්න"
+ "MMS අමතන්න"
+ "%s ට කෙටි පණිවිඩයක් යවන්න"
+ "නිවසට කෙටි පණිවිඩයක් යවන්න"
+ "ජංගම දුරකථනයට කෙටි පණිවිඩයක් යවන්න"
+ "කාර්යාලයට කෙටි පණිවිඩයක් යවන්න"
+ "කාර්යාල ෆැක්ස් වෙත කෙටි පණිවිඩයක් යවන්න"
+ "නිවෙස් ෆැක්ස් වෙත කෙටි පණිවිඩයක් යවන්න"
+ "පේජරයට කෙටි පණිවිඩයක් යවන්න"
+ "කෙටි පණිවිඩයක් යවන්න"
+ "නැවත ඇමතුමට කෙටි පණිවිඩයක් යවන්න"
+ "කාරයට කෙටි පණිවිඩයක් යවන්න"
+ "ආයතනයේ මූලික එකට කෙටි පණිවිඩයක් යවන්න"
+ "ISDN වෙත කෙටි පණිවිඩයක් යවන්න"
+ "මූලික අංකයට කෙටි පණිවිඩයක් යවන්න"
+ "ෆැක්ස් වෙත කෙටි පණිවිඩයක් යවන්න"
+ "රේඩියෝවට කෙටි පණිවිඩයක් යවන්න"
+ "ටෙලෙක්ස් වෙත කෙටි පණිවිඩයක් යවන්න"
+ "TTY/TDD වෙත කෙටි පණිවිඩයක් යවන්න"
+ "කාර්යාල ජංගම දුරකථනයට කෙටි පණිවිඩයක් යවන්න"
+ "කාර්යාල පේජරයට කෙටි පණිවිඩයක් යවන්න"
+ "%s ට කෙටි පණිවිඩයක් යවන්න"
+ "MMS වෙත කෙටි පණිවිඩයක් යවන්න"
+ "වීඩියෝ ඇමතුමක් ලබාගන්න"
+ "නිතරම සම්බන්ධ වන අය හිස් කරන්නද?"
+ "ඔබ සම්බන්ධතා සහ දුරකථන යෙදුම්වලින් නිතරම සම්බන්ධ වුණු අයගේ ලැයිස්තුව හිස් කර, මුල සිටම ඔබගේ ලිපින අභිරුචි ඉගෙනීමට ඊ-තැපැල් යෙදුම්වලට බල කරයි."
+ "නිතරම සම්බන්ධ වන අය හිස් කරමින්…"
+ "සිටියි"
+ "ළඟ නැත"
+ "කාර්යබහුල"
+ "සම්බන්ධතා"
+ "වෙනත්"
+ "නාමාවලිය"
+ "සියලුම සම්බන්ධතා"
+ "මම"
+ "සොයමින්..."
+ "%d ට වඩා සොයාගන්නා ලදී."
+ "සම්බන්ධතා නැත"
+
+ %d ක් සොයා ගන්නා ලදි
+ %d ක් සොයා ගන්නා ලදි
+
+ "%1$s සඳහා ඉක්මන් සම්බන්ධතාව"
+ "(නමක් නොමැත)"
+ "නිතරම අමතන ලද"
+ "නිතරම සම්බන්ධ වන"
+ "සම්බන්ධතාව පෙන්වන්න"
+ "දුරකථන අංක සහිත සම්බන්ධතා"
+ "යාවත්කාලීන වීම් පෙන්වන්න"
+ "දුරකථනය-පමණි, සමමුහුර්ත නොකරනලද"
+ "නම"
+ "අපනාමය"
+ "නම"
+ "පළමු නම"
+ "අවසාන නම"
+ "නම් උපසර්ගය"
+ "මැද නම"
+ "නම් ප්රත්යය"
+ "ස්වර නම"
+ "ශබ්ද විද්යාත්මක මුල් නම"
+ "ස්වර මැද නම"
+ "ශබ්ද විද්යාත්මක අවසාන නම"
+ "දුරකථනය"
+ "ඊ-තැපෑල"
+ "ලිපිනය"
+ "IM"
+ "සංවිධානය"
+ "ඥාතිත්වය"
+ "විශේෂ දින"
+ "පෙළ පණිවුඩය"
+ "ලිපිනය"
+ "සමාගම"
+ "මාතෘකාව"
+ "සටහන්"
+ "SIP"
+ "වෙබ් අඩවිය"
+ "කණ්ඩායම්"
+ "නිවසට ඊ-තැපැල් කරන්න"
+ "ජංගමයට ඊ-තැපැල් කරන්න"
+ "කාර්යාලයට ඊ-තැපැල් කරන්න"
+ "ඊ-තැපෑල"
+ "%s ඊ-තැපැල් කරන්න"
+ "ඊ-තැපෑල"
+ "වීථිය"
+ "තැපැල් පෙට්ටිය"
+ "අසල්වැසි ප්රදේශය"
+ "නගරය"
+ "ජනපදය"
+ "ZIP කේතය"
+ "රට"
+ "නිවෙස් ලිපිනය පෙන්වන්න"
+ "කාර්යාල ලිපිනය පෙන්වන්න"
+ "ලිපිනය පෙන්වන්න"
+ "%s ලිපිනය පෙන්වන්න"
+ "AIM භාවිතයෙන් කතාබස් කරන්න"
+ "Windows Live භාවිතයෙන් කතාබස් කරන්න"
+ "Yahoo භාවිතයෙන් කතාබස් කරන්න"
+ "Skype භාවිතයෙන් කතාබස් කරන්න"
+ "QQ භාවිතයෙන් කතාබස් කරන්න"
+ "Google Talk භාවිතයෙන් කතාබස් කරන්න"
+ "ICQ භාවිතයෙන් කතාබස් කරන්න"
+ "Jabber භාවිතයෙන් කතාබස් කරන්න"
+ "කතාබස්"
+ "මකන්න"
+ "නම් ක්ෂේත්ර විහිදන්න හෝ හකුළන්න"
+ "සියලුම සම්බන්ධතා"
+ "තරුව සලකුණු කළ"
+ "අභිරුචිකරණය"
+ "සම්බන්ධතාවය"
+ "සියලු වෙනත් සම්බන්ධතා"
+ "සියලුම සම්බන්ධතා"
+ "සමමුහුර්ත කණ්ඩායම ඉවත් කරන්න"
+ "සමමුහුර්ත කණ්ඩායම එක් කරන්න"
+ "තවත් කණ්ඩායම්…"
+ "\"%s \" සමමුහුර්ත කිරීමෙන් ඉවත් කිරීමෙන් ඕනෑම කණ්ඩායම් නොකළ සම්බන්ධතාවයක් සමමුහුර්තයෙන් ඉවත් කෙරෙනු ඇත."
+ "පෙන්වීම් විකල්ප සුරකිමින්…"
+ "හරි"
+ "අවලංගු කරන්න"
+ "%s හි සම්බන්ධතා"
+ "අභිරුචි පෙනුමේ සම්බන්ධතා"
+ "එක් සම්බන්ධතාවය"
+ "ගිණුම යටතේ සම්බන්ධතාවය නිර්මාණය කරන්න"
+ "SIM පතෙන් ආයාත කරන්න"
+ "SIM ^1 - ^2 වෙතින් ආයාත කරන්න"
+ "SIM %1$s වෙතින් ආයාත කරන්න"
+ ".vcf ගොනු වෙතින් ආයාත කිරීම"
+ "%s ආයාත කිරීම අවලංගු කරන්නද?"
+ "%s නිර්යාත කිරීම අවලංගු කරන්නද?"
+ "vCard ආයාත/නිර්යාත කිරීම අවලංගු කළ නොහැක"
+ "නොදන්නා දෝෂය."
+ "\"%s \" විවෘත කිරීමට නොහැක: %s "
+ "නිර්යාතකරු පටන් ගැනීමට නොහැක: \"%s \""
+ "නිර්යාත කළ හැකි සම්බන්ධතාවයක් නොමැත."
+ "ඔබ අවශ්ය අවසරයක් අබල කර ඇත."
+ "නිර්යාතය අතරතුර දෝෂයක් සිදු විය: \"%s \"."
+ "අවශ්ය කරන ගොනු නම දිග වැඩිය (\"%s \")."
+ "SD පතෙහි vCard ගොනු ඉතා විශාල ප්රමාණයක් ඇත."
+ "I/O දෝෂය"
+ "මතකය මදිය. ගොනුව විශාල වැඩි විය හැක."
+ "බලාපොරොත්තු නොවූ හේතුවක් නිසා vCard ය විග්රහ කළ නොහැක."
+ "මෙම ආකෘතිය වෙත සහාය නොදක්වයි."
+ "ලබාදුන් vCard ගොනු(ව) වල පාර දත්ත එකතු කළ නොහැකි විය."
+ "ගොනු එකක් හෝ කිහිපයක් ආයාත කිරීමට නොහැකි විය (%s)."
+ "%s නිර්යාත කිරීම සම්පූර්ණ කෙරුණි."
+ "සම්බන්ධතා නිර්යාත කිරීම සම්පූර්ණ කෙරුණි."
+ "%s නිර්යාත කිරීම අවලංගු කෙරුණි."
+ "සම්බන්ධතා දත්ත නිර්යාත කිරීම"
+ "%s වෙත ඔබගේ සම්බන්ධතා දත්ත නිර්යාත කරමින් පවතී."
+ "දත්ත සමුදායේ තොරතුරු ලබාගත නොහැකි විය."
+ "නිර්යාත කළ හැකි සම්බන්ධතා නැත. ඔබගේ දුරකථනයේ සම්බන්ධතා තිබේ නම්, සමහර දත්ත සපයන්නන් දුරකථනයෙන් සම්බන්ධතා නිර්යාත කිරීමට අවසර ලබා නොදිය හැක."
+ "vCard සකසනය නිවැරදිව පටන් ගත්තේ නැත."
+ "නිර්යාත කළ නොහැකි වීය"
+ "සම්බන්ධතා දත්ත නිර්යාත නොකරන ලදි.\nහේතුව: \"%s \""
+ "%s ආයාත කරමින්"
+ "vCard දත්ත කියවිය නොහැක විය"
+ "vCard දත්ත කියවීම අවලංගු කෙරුණි"
+ "vCard %s ආයාත කිරීම සම්පූර්ණ විය"
+ "%s ආයාත කිරීම අවලංගු කෙරුණි"
+ "%s කෙටි වේලාවකින් ආයාත වනු ඇත."
+ "ගොනුව කෙටි වේලාවකින් ආයාත කරනු ඇත."
+ "vCard ආයාත ඉල්ලීම ප්රතික්ෂේප කරන ලදි. පසුව නැවත උත්සාහ කරන්න."
+ "%s කෙටි වේලාවකින් නිර්යාත කරනු ඇත."
+ "ගොනුව කෙටි වේලාවකින් නිර්යාත කරනු ඇත."
+ "vCard නිර්යාත අයැදුම ප්රතික්ෂේප කරන ලදි. පසුව නැවත උත්සාහ කරන්න."
+ "සම්බන්ධතාවය"
+ "පෙදෙසි තාවකාලික ආචයනයට vCard(s) හැඹිලිගත කරමින් පවතී. සැබෑ ආයාත කිරීම ඉක්මනින් පටන් ගනු ඇත."
+ "vCard ආයාත කිරීමට නොහැකි විය."
+ "SD පතෙහි vCard ගොනුවක් සොයා නොගන්නා ලදී."
+ "NFC හරහා සම්බන්ධතාව ලැබුණි"
+ "සම්බන්ධතා නිර්යාත කරන්නද?"
+ "හැඹිලි ගත කරමින්"
+ "SD පත පරිලෝකනය කළ නොහැකි විය. (හේතුව: \"%s \")"
+ "%s /%s : %s ආයාත කරමින්"
+ ".vcf ගොනු වෙත නිර්යාත කිරීම"
+ "අනුපිළිවෙලට සකසා ඇත්තේ"
+ "පළමු නම"
+ "අවසාන නම"
+ "නමේ ආකෘතිය"
+ "මුල් නම මුලින්ම"
+ "අවසාන නම මුලින්ම"
+ "පෙනෙන සම්බන්ධතා බෙදාගන්න"
+ "දෘශ්යමාන සම්බන්ධතා බෙදා ගැනීම අසාර්ථක විය."
+ "සම්බන්ධතා ආයාත/නිර්යාත කිරීම"
+ "සම්බන්ධතා ආයාත කරන්න"
+ "මෙම සම්බන්ධතාව බෙදා ගත නොහැක."
+ "සෙවීම"
+ "පෙන්වීමට සම්බන්ධතා"
+ "පෙන්වීමට සම්බන්ධතා"
+ "අභිරුචි පෙනුම අර්ථ දක්වන්න"
+ "සම්බන්ධතා සොයන්න"
+ "ප්රියතම"
+ "සම්බන්ධතා නැත."
+ "දෘශ්ය සම්බන්ධතා නැත."
+ "ප්රියතමයන් නැත."
+ "%s හි සම්බන්ධතා නැත"
+ "නිතරමයන් හිස් කරන්න"
+ "SIM කාඩ්පත තෝරන්න"
+ "ගිණුම්"
+ "ආයාත/නිර්යාත"
+ "%1$s හරහා"
+ "%2$s මඟින් %1$s "
+ "සෙවීම අවසන් කරන්න"
+ "සෙවීම හිස් කරන්න"
+ "දර්ශනය කිරීමේ විකල්පය සම්බන්ධ කරගන්න"
+ "ගිණුම"
+ "ඇමතුම් සඳහා මෙම එක සැමවිටම භාවිතා කරන්න"
+ "සමඟ අමතන්න"
+ "සටහනක් සමගින් අමතන්න"
+ "ඇමතුම සමග යැවීමට සටහනක් ටයිප් කරන්න ..."
+ "යවන්න සහ අමතන්න"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-sk/strings.xml b/ContactsCommon/res/values-sk/strings.xml
new file mode 100644
index 0000000..a9c377c
--- /dev/null
+++ b/ContactsCommon/res/values-sk/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Text bol skopírovaný"
+ "Kopírovať do schránky"
+ "Volať kontakt %s "
+ "Volať na domáci telefón"
+ "Volať na mobil"
+ "Volať na pracovný telefón"
+ "Volať na pracovný fax"
+ "Volať na domáci fax"
+ "Volať na pager"
+ "Volať"
+ "Volať na číslo spätného volania"
+ "Volať na telefón v aute"
+ "Volať firme (hlavné číslo)"
+ "Volať na číslo ISDN"
+ "Volať na hlavné číslo"
+ "Volať na fax"
+ "Volať na rádiostanicu"
+ "Volať na číslo Telex"
+ "Volať na číslo TTY/TDD"
+ "Volať na pracovný mobil"
+ "Volať na pracovný pager"
+ "Volať kontakt %s "
+ "Volať na číslo MMS"
+ "Poslať správu kontaktu %s "
+ "Poslať správu na domáci telefón"
+ "Poslať správu na mobil"
+ "Poslať správu na pracovný telefón"
+ "Poslať správu na pracovný fax"
+ "Poslať správu na domáci fax"
+ "Poslať správu na pager"
+ "Poslať správu"
+ "Poslať správu na číslo spätného volania"
+ "Poslať správu na telefón v aute"
+ "Poslať správu do firmy (hlavné číslo)"
+ "Poslať správu na číslo ISDN"
+ "Poslať správu na hlavné číslo"
+ "Poslať správu na fax"
+ "Poslať správu na rádiostanicu"
+ "Poslať správu na číslo Telex"
+ "Poslať správu na číslo TTY/TDD"
+ "Poslať správu na pracovný mobil"
+ "Poslať správu na pracovný pager"
+ "Poslať správu kontaktu %s "
+ "Poslať správu na číslo MMS"
+ "Uskutočniť videohovor"
+ "Vymazať často kontaktované osoby?"
+ "Vymažete zoznam často kontaktovaných osôb v aplikáciách Kontakty a Telefón a e-mailové aplikácie budú musieť odznova vytvoriť predvoľby adresátov."
+ "Mazanie často kontaktov. osôb..."
+ "Som tu"
+ "Som preč"
+ "Nemám čas"
+ "Kontakty"
+ "Iné"
+ "Adresár"
+ "Všetky kontakty"
+ "Ja"
+ "Hľadá sa…"
+ "Našlo sa viac ako %d ."
+ "Žiadne kontakty"
+
+ - Našli sa
%d kontakty
+ - Našlo sa
%d kontaktu
+ - Našlo sa
%d kontaktov
+ - Našiel sa 1 kontakt
+
+ "Rýchly kontakt pre osobu %1$s "
+ "(Bez mena)"
+ "Najčastejšie volané kontakty"
+ "Najčastejšie používané kontakty"
+ "Zobraziť kontakt"
+ "Všetky kontakty s telefónnymi číslami"
+ "Zobraziť aktualizácie"
+ "Iba v telefóne, nesynchronizované"
+ "Meno"
+ "Prezývka"
+ "Meno"
+ "Meno"
+ "Priezvisko"
+ "Titul pred menom"
+ "Stredné meno"
+ "Titul za menom"
+ "Meno (foneticky)"
+ "Krstné meno (foneticky)"
+ "Stredné meno (foneticky)"
+ "Priezvisko (foneticky)"
+ "Telefón"
+ "E-mail"
+ "Adresa"
+ "Čet"
+ "Organizácia"
+ "Vzťah"
+ "Mimoriadne dátumy"
+ "Textová správa"
+ "Adresa"
+ "Spoločnosť"
+ "Názov"
+ "Poznámky"
+ "SIP"
+ "Web"
+ "Skupiny"
+ "E-mail domov"
+ "E-mail na mobil"
+ "E-mail do práce"
+ "E-mail"
+ "Poslať e-mail na adresu %s "
+ "E-mail"
+ "Ulica"
+ "PO box"
+ "Štvrť"
+ "Mesto"
+ "Štát"
+ "PSČ"
+ "Krajina"
+ "Zobraziť adresu domov"
+ "Zobraziť pracovnú adresu"
+ "Zobraziť adresu"
+ "Zobraziť adresu %s "
+ "Zhovárať sa pomocou služby AIM"
+ "Zhovárať sa pomocou služby Windows Live"
+ "Zhovárať sa pomocou služby Yahoo"
+ "Zhovárať sa pomocou služby Skype"
+ "Zhovárať sa pomocou služby QQ"
+ "Zhovárať sa pomocou aplikácie Google Talk"
+ "Zhovárať sa pomocou služby ICQ"
+ "Zhovárať sa pomocou služby Jabber"
+ "Četovať"
+ "odstrániť"
+ "Rozbaliť alebo zbaliť pole mena"
+ "Všetky kontakty"
+ "Označené hviezdičkou"
+ "Prispôsobiť"
+ "Kontakt"
+ "Všetky ostatné kontakty"
+ "Všetky kontakty"
+ "Odstrániť synchronizovanú skupinu"
+ "Pridať synchronizovanú skupinu"
+ "Ďalšie skupiny..."
+ "Ak zo synchronizácie odstránite skupinu „%s “, odstránite zo synchronizácie aj všetky kontakty, ktoré nie sú zaradené do žiadnej skupiny."
+ "Prebieha ukladanie možností zobrazenia..."
+ "Hotovo"
+ "Zrušiť"
+ "Kontakty v účte %s "
+ "Kontakty vo vlastnom zobrazení"
+ "Jednotlivý kontakt"
+ "Vytvoriť kontakt na základe účtu"
+ "Importovať zo SIM karty"
+ "Importovať zo SIM karty ^1 – ^2 "
+ "Importovať zo SIM karty %1$s "
+ "Importovať zo súboru .vcf"
+ "Zrušiť importovanie súboru %s ?"
+ "Zrušiť exportovanie súboru %s ?"
+ "Import alebo export vizitky nie je možné zrušiť"
+ "Neznáma chyba."
+ "Súbor „%s “ sa nepodarilo otvoriť: %s "
+ "Nástroj na exportovanie sa nepodarilo spustiť: „%s “."
+ "Nedá sa exportovať žiadny kontakt."
+ "Zakázali ste požadované povolenie."
+ "Počas exportovania sa vyskytla chyba: „%s “."
+ "Požadovaný názov súboru (%s ) je príliš dlhý."
+ "Na SD karte sa nachádza príliš veľa súborov vCard."
+ "Chyba I/O"
+ "Nedostatok pamäte. Súbor je možno príliš veľký."
+ "Analýza karty vCard zlyhala z neznámeho dôvodu."
+ "Formát nie je podporovaný."
+ "Metaúdaje daných súborov vizitiek vCard sa nepodarilo zhromaždiť."
+ "Nepodaril sa import jedného alebo viacerých súborov (%s)."
+ "Exportovanie súboru %s bolo dokončené."
+ "Exportovanie kontaktov bolo dokončené"
+ "Exportovanie súboru %s bolo zrušené."
+ "Export údajov kontaktov"
+ "Vaše údaje o kontaktoch sa exportujú do súboru: %s ."
+ "Nepodarilo sa získať informácie z databázy."
+ "Nenašli sa žiadne kontakty, ktoré by bolo možné exportovať. Ak v telefóne skutočne máte kontakty, problém môže byť spôsobený tým, že niektorí poskytovatelia údajov neumožňujú export kontaktov z telefónu."
+ "Nástroj na tvorbu vizitiek vCard sa nespustil správne."
+ "Exportovanie zlyhalo"
+ "Údaje o kontaktoch sa neexportovali.\nDôvod: „%s “"
+ "Importuje sa %s "
+ "Nepodarilo sa prečítať údaje vizitky vCard"
+ "Čítanie údajov vizitky vCard bolo zrušené"
+ "Import vizitky vCard %s bol dokončený"
+ "Importovanie súboru %s bolo zrušené"
+ "Vizitka %s bude čoskoro importovaná."
+ "Súbor bude čoskoro importovaný."
+ "Žiadosť o import vizitky vCard bola odmietnutá. Skúste to znova neskôr."
+ "Vizitka %s bude čoskoro exportovaná."
+ "Súbor bude čoskoro exportovaný."
+ "Žiadosť o exportovanie vizitky vCard bola odmietnutá. Skúste to znova neskôr."
+ "kontakt"
+ "Prebieha načítavanie vizitiek vCard do vyrovnávacej pamäte miestneho dočasného úložiska. Samotné importovanie začne o chvíľu."
+ "Vizitku vCard sa nepodarilo importovať."
+ "Na SD karte sa nenašla žiadna vizitka vCard."
+ "Kontakt cez NFC"
+ "Exportovať kontakty?"
+ "Ukladanie do vyrovnávacej pamäte..."
+ "SD kartu sa nepodarilo prehľadať. (Dôvod: „%s “)"
+ "Import: %s / %s : %s "
+ "Exportovať do súboru .vcf"
+ "Zoradiť podľa"
+ "Meno"
+ "Priezvisko"
+ "Formát mena"
+ "Najprv meno"
+ "Najprv priezvisko"
+ "Zdieľať viditeľné kontakty"
+ "Nepodarilo sa zdieľať viditeľné kontakty"
+ "Import a export kontaktov"
+ "Importovať kontakty"
+ "Tento kontakt nie je možné zdieľať"
+ "Hľadať"
+ "Kontakty na zobrazenie"
+ "Kontakty na zobrazenie"
+ "Definícia vl. zobrazenia"
+ "Hľadať kontakty"
+ "Obľúbené"
+ "Žiadne kontakty."
+ "Žiadne kontakty nie sú viditeľné."
+ "Žiadne obľúbené"
+ "V skupine %s nie sú žiadne kontakty"
+ "Vymazať často kontaktovaných"
+ "Vybrať SIM kartu"
+ "Účty"
+ "Import a export"
+ "zdroj: %1$s "
+ "%1$s , zdroj: %2$s "
+ "zastaviť vyhľadávanie"
+ "Vymazať vyhľadávanie"
+ "Možnosti zobrazenia kontaktov"
+ "Účet"
+ "Vždy používať pre hovory"
+ "Volať pomocou"
+ "Hovor s poznámkou"
+ "Napíšte poznámku, ktorá sa odošle s hovorom..."
+ "ODOSLAŤ A VOLAŤ"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-sl/strings.xml b/ContactsCommon/res/values-sl/strings.xml
new file mode 100644
index 0000000..3695287
--- /dev/null
+++ b/ContactsCommon/res/values-sl/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Besedilo kopirano"
+ "Kopiraj v odložišče"
+ "Pokliči številko %s "
+ "Pokliči domov"
+ "Pokliči mobilni telefon"
+ "Pokliči v službo"
+ "Pokliči službeni faks"
+ "Pokliči domači faks"
+ "Pokliči pozivnik"
+ "Pokliči"
+ "Pokliči številko za povratni klic"
+ "Pokliči telefon v avtu"
+ "Pokliči glavno telefonsko številko podjetja"
+ "Pokliči številko ISDN"
+ "Pokliči glavno telefonsko številko"
+ "Pokliči faks"
+ "Pokliči radijski telefon"
+ "Pokliči teleks"
+ "Pokliči številko TTY/TDD"
+ "Pokliči službeni mobilni telefon"
+ "Pokliči službeni pozivnik"
+ "Pokliči številko pomočnika %s "
+ "Pokliči telefon MMS"
+ "Pošlji SMS na številko %s "
+ "Pošlji SMS domov"
+ "Pošlji SMS v mobilni telefon"
+ "Pošlji SMS v službeni telefon"
+ "Pošlji SMS v službeni faks"
+ "Pošlji SMS v domači faks"
+ "Pošlji SMS v pozivnik"
+ "SMS"
+ "Pošlji SMS na številko za povratni klic"
+ "Pošlji SMS v telefon v avtu"
+ "Pošlji SMS na glavno telefonsko številko podjetja"
+ "Pošlji SMS na številko ISDN"
+ "Pošlji SMS na glavno telefonsko številko"
+ "Pošlji SMS v faks"
+ "Pošlji SMS v radijski telefon"
+ "Pošlji SMS v teleks"
+ "Pošlji SMS na telefonsko številko TTY/TDD"
+ "Pošlji SMS v službeni mobilni telefon"
+ "Pošlji SMS v službeni pozivnik"
+ "Pošlji SMS pomočniku na številko %s "
+ "Pošlji SMS na telefonsko številko MMS"
+ "Opravi videoklic"
+ "Želite izbrisati seznam pog. stikov?"
+ "Izbrisali boste seznam pogostih stikov v aplikacijah Stiki in Telefon, zato bodo e-poštne aplikacije začele shranjevati pogoste naslovnike od začetka."
+ "Brisanje seznama pogost. stikov ..."
+ "Dosegljiv"
+ "Odsoten"
+ "Zaseden"
+ "Stiki"
+ "Drugo"
+ "Imenik"
+ "Vsi stiki"
+ "Jaz"
+ "Iskanje …"
+ "Najdenih je bilo več kot toliko stikov: %d ."
+ "Ni stikov"
+
+ %d najden
+ %d najdena
+ %d najdeni
+ %d najdenih
+
+ "Hitri stik za %1$s "
+ "(Ni imena)"
+ "Pogosto klicani"
+ "Pogosto uporabljeni stiki"
+ "Ogled stika"
+ "Vsi stiki s telefonskimi številkami"
+ "Prikaži posodobitve"
+ "Samo v telefonu, nesinhroniziran"
+ "Ime"
+ "Vzdevek"
+ "Ime"
+ "Ime"
+ "Priimek"
+ "Naziv"
+ "Drugo ime"
+ "Naziv (za imenom)"
+ "Ime – fonetično"
+ "Ime – fonetično"
+ "Drugo ime – fonetično"
+ "Priimek – fonetično"
+ "Telefon"
+ "E-poštni naslov"
+ "Naslov"
+ "Neposredno sporočanje"
+ "Organizacija"
+ "Razmerje"
+ "Posebni datumi"
+ "SMS"
+ "Naslov"
+ "Podjetje"
+ "Naziv"
+ "Opombe"
+ "SIP"
+ "Spletno mesto"
+ "Skupine"
+ "Pošlji e-poštno sporočilo domov"
+ "Pošlji e-poštno sporočilo v mobilno napravo"
+ "Pošlji e-poštno sporočilo na službeni naslov"
+ "Pošlji e-poštno sporočilo"
+ "Pošlji e-poštno sporočilo na %s "
+ "Pošlji e-poštno sporočilo"
+ "Ulica"
+ "Poštni predal"
+ "Naselje"
+ "Kraj"
+ "Zvezna država"
+ "Poštna številka"
+ "Država"
+ "Prikaži domači naslov"
+ "Prikaži službeni naslov"
+ "Prikaži naslov"
+ "Prikaži naslov %s "
+ "Klepet s storitvijo AIM"
+ "Klepet s storitvijo Windows Live"
+ "Klepet s storitvijo Yahoo"
+ "Klepet s storitvijo Skype"
+ "Klepet s storitvijo QQ"
+ "Klepet s storitvijo Google Talk"
+ "Klepet s storitvijo ICQ"
+ "Klepet s storitvijo Jabber"
+ "Klepet"
+ "izbriši"
+ "Razširi ali strni imenska polja"
+ "Vsi stiki"
+ "Z zvezdico"
+ "Prilagodi"
+ "Stik"
+ "Vsi drugi stiki"
+ "Vsi stiki"
+ "Odstrani skupino za sinhroniziranje"
+ "Dodaj skupino za sinhroniziranje"
+ "Več skupin ..."
+ "Če skupino »%s « odstranite iz sinhronizacije, boste iz sinhronizacije odstranili tudi vse nerazvrščene stike."
+ "Shranjevanje možnosti prikaza ..."
+ "Končano"
+ "Prekliči"
+ "Stiki v %s "
+ "Stiki v pogledu po meri"
+ "Posamezen stik"
+ "Ustvari stik v računu"
+ "Uvoz s kartice SIM"
+ "Uvoz s kartice SIM ^1 – ^2 "
+ "Uvoz s kartice SIM %1$s "
+ "Uvoz iz datoteke .vcf"
+ "Želite preklicati izvoz datoteke %s ?"
+ "Želite preklicati izvoz datoteke %s ?"
+ "Uvoza/izvoza vCard ni mogoče preklicati"
+ "Neznana napaka."
+ "Datoteke »%s « ni bilo mogoče odpreti: %s ."
+ "Funkcije za izvoz ni bilo mogoče zagnati: »%s «."
+ "Ni stikov za izvoz."
+ "Onemogočili ste zahtevano dovoljenje."
+ "Pri izvozu je prišlo do napake: »%s «."
+ "Zahtevano ime datoteke je predolgo (»%s «)."
+ "Na kartici SD je preveč datotek vCard."
+ "Vhodno/izhodna napaka"
+ "Ni dovolj pomnilnika. Morda je datoteka prevelika."
+ "Datoteke vCard iz neznanega razloga ni bilo mogoče razčleniti."
+ "Ta oblika ni podprta."
+ "Metapodatkov za določene datoteke vCard ni bilo mogoče zbrati."
+ "Ene ali več datotek ni bilo mogoče uvoziti (%s)."
+ "Izvoz datoteke %s je končan."
+ "Izvoz stikov je končan."
+ "Izvoz datoteke %s je preklican."
+ "Izvažanje podatkov o stikih"
+ "Podatki o stikih se izvažajo v to datoteko: %s ."
+ "Informacij o zbirki podatkov ni bilo mogoče dobiti."
+ "Ni stikov za izvoz. Če imate v telefonu stike, nekateri ponudniki podatkov morda ne omogočajo izvoza stikov iz telefona."
+ "Urejevalnik za vCard se ni pravilno zagnal."
+ "Izvoz ni mogoč"
+ "Podatki stika niso bili izvoženi.\nRazlog: »%s «"
+ "Uvažanje %s "
+ "Podatkov vCard ni bilo mogoče prebrati"
+ "Branje podatkov vCard je preklicano"
+ "Uvoz datoteke vCard %s je končan"
+ "Uvoz datoteke %s je preklican"
+ "Datoteka %s bo kmalu uvožena."
+ "Datoteka bo kmalu uvožena."
+ "Zahteva za uvoz datoteke vCard je bila zavrnjena. Poskusite znova pozneje."
+ "Datoteka %s bo kmalu izvožena."
+ "Datoteka bo kmalu izvožena."
+ "Zahteva za izvoz datoteke vCard je bila zavrnjena. Poskusite znova pozneje."
+ "stik"
+ "Predpomnjenje datotek vCard v lokalno začasno shrambo. Dejanski uvoz se bo začel kmalu."
+ "Datoteke vCard ni mogoče uvoziti."
+ "Na kartici SD ni mogoče najti datotek vCard."
+ "Stik prejet prek NFC"
+ "Želite izvoziti stike?"
+ "Predpomnjenje"
+ "Kartice SD ni mogoče pregledati. (Razlog: »%s «)"
+ "Uvažanje %s /%s : %s "
+ "Izvoz v datoteko .vcf"
+ "Razvrsti glede na"
+ "Ime"
+ "Priimek"
+ "Oblika imena"
+ "Najprej ime"
+ "Najprej priimek"
+ "Vidne stike deli z drugimi"
+ "Deljenje vidnih stikov z drugimi ni uspelo."
+ "Uvoz/izvoz stikov"
+ "Uvozi stike"
+ "Tega stika ni mogoče dati v skupno rabo."
+ "Išči"
+ "Stiki, ki bodo prikazani"
+ "Stiki, ki bodo prikazani"
+ "Določite pogled po meri"
+ "Najdi stike"
+ "Priljubljeni"
+ "Ni stikov."
+ "Ni vidnih stikov."
+ "Ni priljubljenih."
+ "Ni stikov v kategoriji %s "
+ "Izbriši seznam pogostih stikov"
+ "Izberite kartico SIM"
+ "Računi"
+ "Uvozi/izvozi"
+ "vir: %1$s "
+ "vir: %2$s – %1$s "
+ "ustavitev iskanja"
+ "Počisti iskalno polje"
+ "Možnosti prikaza stikov"
+ "Račun"
+ "Vedno uporabi to možnost za klice"
+ "Klicanje z …"
+ "Klic z zapiskom"
+ "Vnesite zapisek, ki ga želite poslati s klicem ..."
+ "POŠLJI IN KLIČI"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-sq-rAL/strings.xml b/ContactsCommon/res/values-sq-rAL/strings.xml
new file mode 100644
index 0000000..4689dc9
--- /dev/null
+++ b/ContactsCommon/res/values-sq-rAL/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Teksti u kopjua"
+ "Kopjo në kujtesën e fragmenteve"
+ "Telefono %s "
+ "Telefono numrin e shtëpisë"
+ "Telefono numrin celular"
+ "Telefono numrin e punës"
+ "Telefono faksin e punës"
+ "Telefono faksin e shtëpisë"
+ "Telefoni numrin e biperit"
+ "Telefono"
+ "Telefono numrin e kthimit të telefonatave"
+ "Telefono numrin e makinës"
+ "Telefono numrin kryesor të kompanisë"
+ "Telefono numrin ISDN"
+ "Telefono numrin kryesor"
+ "Telefono numrin e faksit"
+ "Telefono numrin e radios"
+ "Telefono numrin e teletekstit"
+ "Telefono numrin e TTY/TDD"
+ "Telefono numrin celular të punës"
+ "Telefono numrin e biperit të punës"
+ "Telefono %s "
+ "Telefono numrin MMS"
+ "Dërgo mesazh te %s "
+ "Dërgo mesazh te numri i shtëpisë"
+ "Dërgo mesazh te numri celular"
+ "Dërgo mesazh te numri i punës"
+ "Dërgo mesazh te faksi i punës"
+ "Dërgo mesazh te faksi i shtëpisë"
+ "Dërgo mesazh te biperi"
+ "Dërgo mesazh"
+ "Dërgo mesazh te numri i kthimit të telefonatave"
+ "Dërgo mesazh te numri i makinës"
+ "Dërgo mesazh te numri kryesor i kompanisë"
+ "Dërgo mesazh te numri ISDN"
+ "Dërgo mesazh te numri kryesor"
+ "Dërgo mesazh te faksi"
+ "Dërgo mesazh te numri i radios"
+ "Dërgo mesazh te numri i teletekstit"
+ "Dërgo mesazh te numri TTY/TDD"
+ "Dërgo mesazh te numri i celularit"
+ "Dërgo mesazh te numri i biperit"
+ "Dërgo mesazh te %s "
+ "Dërgo mesazh te numri i MMS-së"
+ "Bëj një telefonatë me video"
+ "Të pastrohen kontaktet e shpeshta?"
+ "Do ta pastrosh listën e kontakteve të shpeshta në aplikacionet \"Kontaktet\" dhe \"Telefoni\" dhe do t\'i detyrosh aplikacionet e mail-it të mësojnë preferencat e tua të adresimit nga e para."
+ "Po pastron kontaktet e shpeshta…"
+ "I gatshëm"
+ "I larguar"
+ "I zënë"
+ "Kontaktet"
+ "Tjetër"
+ "Direktoria"
+ "Të gjitha kontaktet"
+ "Unë"
+ "Po kërkon..."
+ "U gjetën më shumë se %d ."
+ "Nuk ka asnjë kontakt"
+
+ - U gjetën
%d
+ - U gjet 1
+
+ "Kontakti i shpejtë për %1$s "
+ "(Pa emër)"
+ "Të telefonuara shpesh"
+ "Të kontaktuara shpesh"
+ "Shiko kontaktin"
+ "Të gjitha kontaktet me numra telefoni"
+ "Shiko përditësimet"
+ "Vetëm në telefon, i pasinkronizuar"
+ "Emri"
+ "Pseudonimi"
+ "Emri"
+ "Emri"
+ "Mbiemri"
+ "Parashtesa e emrit"
+ "Emri i dytë"
+ "Prapashtesa e emrit"
+ "Emri fonetik"
+ "Emri fonetik"
+ "Emri i dytë fonetik"
+ "Mbiemri fonetik"
+ "Telefoni"
+ "Mail-i"
+ "Adresa"
+ "IM"
+ "Organizata"
+ "Marrëdhënia"
+ "Datat e veçanta"
+ "Mesazh me tekst"
+ "Adresa"
+ "Kompania"
+ "Titulli"
+ "Shënime"
+ "SIP"
+ "Uebsajti"
+ "Grupet"
+ "Dërgoji mail shtëpisë"
+ "Dërgoji mail celularit"
+ "Dërgoji mail punës"
+ "Dërgo mail"
+ "Dërgo mail në %s "
+ "Mail-i"
+ "Rruga"
+ "Kutia postare"
+ "Lagjja"
+ "Qyteti"
+ "Shteti"
+ "Kodi ZIP"
+ "Shteti"
+ "Shiko adresën e shtëpisë"
+ "Shiko adresën e punës"
+ "Shiko adresën"
+ "Shiko adresën e %s "
+ "Bisedo me AIM"
+ "Bisedo me Windows Live"
+ "Bisedo me Yahoo"
+ "Bisedo me Skype"
+ "Bisedo me QQ"
+ "Bisedo me \"Bisedo me Google\""
+ "Bisedo me ICQ"
+ "Bisedo me Jabber"
+ "Bisedo"
+ "fshi"
+ "Zgjero ose palos fushat e emrit"
+ "Të gjitha kontaktet"
+ "Me yll"
+ "Personalizo"
+ "Kontakti"
+ "Të gjitha kontaktet e tjera"
+ "Të gjitha kontaktet"
+ "Hiq grupin e sinkronizimit"
+ "Shto një grup sinkronizimi"
+ "Më shumë grupe…"
+ "Heqja e \"%s \" nga sinkronizimi do të heqë po ashtu çdo kontakt të pagrupuar nga sinkronizimi."
+ "Po ruan opsionet e paraqitjes…"
+ "U krye!"
+ "Anulo"
+ "Kontaktet në %s "
+ "Pamja e personalizuar"
+ "Një kontakt i vetëm"
+ "Krijo një kontakt nën llogari"
+ "Importo nga karta SIM"
+ "Importo nga karta SIM ^1 - ^2 "
+ "Importo nga karta SIM %1$s "
+ "Importo nga skedar .vcf"
+ "Të anulohet importimi i %s ?"
+ "Të anulohet eksportimi i %s ?"
+ "Importimi/eksportimi i vCard nuk mund të anulohej"
+ "Gabim i panjohur."
+ "\"%s \" nuk mund të hapej: %s ."
+ "Eksportuesi nuk mund të nisej: \"%s \"."
+ "Nuk ka asnjë kontakt që mund të eksportohet."
+ "Ke çaktivizuar një leje e cila është të detyrueshme."
+ "Ndodhi një gabim gjatë eksportimit: \"%s \"."
+ "Emri i kërkuar i skedarit është shumë i gjatë (\"%s \")."
+ "Ka shumë skedarë vCard në kartën SD."
+ "Gabim I/O"
+ "Nuk ka memorie të mjaftueshme. Skedari mund të jetë shumë i madh."
+ "vCard nuk mund të analizohej për një arsye të paparashikuar."
+ "Formati nuk mbështetet."
+ "Informacionet e skedarit(ëve) të dhënë të vCard nuk mund të mblidheshin."
+ "Një ose më shumë skedarë nuk mundën të importoheshin (%s)."
+ "Eksportimi i %s përfundoi."
+ "Eksportimi i kontakteve përfundoi."
+ "Eksportimi i %s u anulua."
+ "Po eksporton të dhënat e kontaktit"
+ "Të dhënat e kontaktit po eksportohen te: %s ."
+ "Informacionet e bazës së të dhënave nuk mund të merreshin."
+ "Nuk ka kontakte që mund të eksportohen. Nëse ke kontakte në telefonin tënd, disa ofrues të të dhënave mund të mos lejojnë që kontaktet të eksportohen nga telefoni."
+ "Kompozitori i vCard nuk u nis si duhet."
+ "Nuk mund të eksportoheshin"
+ "Të dhënat e kontaktit nuk u eksportuan.\nArsyeja: \"%s \""
+ "Po importon %s "
+ "Të dhënat e vCard nuk mund të lexoheshin"
+ "Leximi i të dhënave të vCard u anulua."
+ "Importimi i %s të vCard përfundoi"
+ "Importimi i %s u anulua"
+ "%s do të importohet së shpejti."
+ "Skedari do të importohet së shpejti."
+ "Kërkesa e importit të vCard u refuzua. Provo përsëri më vonë."
+ "%s do të eksportohet së shpejti."
+ "Skedari do të eksportohet së shpejti."
+ "Kërkesa e eksportimit të vCard u refuzua. Provo përsëri më vonë."
+ "kontakti"
+ "Po ruan vCard në hapësirën ruajtëse lokale të përkohshme. Importimi aktual do të nisë së shpejti."
+ "vCard nuk mund të eksportohej."
+ "Nuk u gjet asnjë skedar vCard në kartën SD."
+ "Kontakti u mor nëpërmjet NFC-së"
+ "Të eksportohen kontaktet?"
+ "Po ruan memorien e përkohshme"
+ "Karta SD nuk mund të skanohej. (Arsyeja: \"%s \")"
+ "Po importon %s /%s : %s "
+ "Eksporto në skedar .vcf"
+ "Rendit sipas"
+ "Emri"
+ "Mbiemri"
+ "Formati i emrit"
+ "Emri në fillim"
+ "Mbiemri në fillim"
+ "Shpërnda kontaktet e dukshme"
+ "Ndarja e kontakteve të dukshme dështoi."
+ "Importo/eksporto kontaktet"
+ "Importo kontaktet"
+ "Ky kontakt nuk mund të shpërndahet"
+ "Kërko"
+ "Kontakte për t\'u shfaqur"
+ "Kontakte për t\'u shfaqur"
+ "Pamja e personalizuar"
+ "Gjej kontaktet"
+ "Të preferuarat"
+ "Nuk ka asnjë kontakt."
+ "Nuk ka kontakte të dukshme."
+ "Nuk ka të preferuara."
+ "Nuk ka kontakte në %s "
+ "Pastro kontaktet e shpeshta"
+ "Zgjidh kartën SIM"
+ "Llogaritë"
+ "Importo/eksporto"
+ "nëpërmjet %1$s "
+ "%1$s nëpërmjet %2$s "
+ "ndalo kërkimin"
+ "Pastro kërkimin"
+ "Opsionet e paraqitjes së kontaktit"
+ "Llogaria"
+ "Përdor gjithmonë këtë për telefonatat"
+ "Telefono me"
+ "Telefono me shënim"
+ "Shkruaj një shënim për të dërguar një telefonatë..."
+ "DËRGO DHE TELEFONO"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-sr/strings.xml b/ContactsCommon/res/values-sr/strings.xml
new file mode 100644
index 0000000..edd86c7
--- /dev/null
+++ b/ContactsCommon/res/values-sr/strings.xml
@@ -0,0 +1,254 @@
+
+
+
+
+ "Текст је копиран"
+ "Копирај у привремену меморију"
+ "Позови %s "
+ "Позови кућни телефон"
+ "Позови мобилни телефон"
+ "Позови пословни телефон"
+ "Позови пословни факс"
+ "Позови кућни факс"
+ "Позови пејџер"
+ "Позови"
+ "Позови за повратни позив"
+ "Позови телефон у аутомобилу"
+ "Позови главни телефон предузећа"
+ "Позови ISDN"
+ "Позови главни телефон"
+ "Позови факс"
+ "Позови радио"
+ "Позови телекс"
+ "Позови TTY/TDD"
+ "Позови пословни мобилни телефон"
+ "Позови пословни пејџер"
+ "Позови %s "
+ "Позови број за MMS"
+ "Пошаљи SMS на %s "
+ "Пошаљи SMS на кућни телефон"
+ "Пошаљи SMS на мобилни телефон"
+ "Пошаљи SMS на пословни телефон"
+ "Пошаљи SMS на пословни факс"
+ "Пошаљи SMS на кућни факс"
+ "Пошаљи SMS на пејџер"
+ "Пошаљи SMS"
+ "Пошаљи SMS за повратни позив"
+ "Пошаљи SMS на телефон у аутомобилу"
+ "Пошаљи SMS на главни број телефона предузећа"
+ "Пошаљи SMS на ISDN"
+ "Пошаљи SMS на главни телефон"
+ "Пошаљи SMS на факс"
+ "Пошаљи SMS на радио"
+ "Пошаљи SMS на телекс"
+ "Пошаљи SMS на TTY/TDD"
+ "Пошаљи SMS на пословни мобилни телефон"
+ "Пошаљи SMS на пословни пејџер"
+ "Пошаљи SMS на %s "
+ "Пошаљи SMS на број за MMS"
+ "Упути видео позив"
+ "Бришете често контактиране?"
+ "Обрисаћете листу често контактираних у апликацијама Контакти и Телефон, па ће имејл апликације морати поново да прикупе информације о адресирању."
+ "Брисање често контактираних..."
+ "Доступан/на"
+ "Одсутан/на"
+ "Заузет/а"
+ "Контакти"
+ "Друго"
+ "Директоријум"
+ "Сви контакти"
+ "Ја"
+ "Претражује се..."
+ "Пронађено је више од %d ."
+ "Нема контаката"
+
+ - Пронађен је
%d
+ - Пронађена су
%d
+ - Пронађено је
%d
+
+ "Брзи контакт за корисника %1$s "
+ "(Нема имена)"
+ "Често позивани"
+ "Често контактирани"
+ "Приказивање контакта"
+ "Сви контакти са бројевима телефона"
+ "Прикажи ажурирања"
+ "Само на телефону, без синхронизације"
+ "Име"
+ "Надимак"
+ "Име"
+ "Име"
+ "Презиме"
+ "Префикс за име"
+ "Средње име"
+ "Суфикс имена"
+ "Име – фонетски"
+ "Име – фонетски"
+ "Средње име – фонетски"
+ "Презиме – фонетски"
+ "Телефон"
+ "Имејл адреса"
+ "Адреса"
+ "Размена тренутних порука"
+ "Организација"
+ "Однос"
+ "Специјални датуми"
+ "Текстуална порука"
+ "Адреса"
+ "Предузеће"
+ "Назив"
+ "Белешке"
+ "SIP"
+ "Веб-сајт"
+ "Групе"
+ "Пошаљи имејл на кућну имејл адресу"
+ "Пошаљи имејл на мобилни телефон"
+ "Пошаљи имејл на пословну имејл адресу"
+ "Пошаљи имејл"
+ "Пошаљи имејл на %s "
+ "Пошаљи имејл"
+ "Улица"
+ "Поштански фах"
+ "Крај"
+ "Град"
+ "Држава"
+ "Поштански број"
+ "Земља"
+ "Прикажи кућну адресу"
+ "Прикажи пословну адресу"
+ "Прикажи адресу"
+ "Прикажи адресу %s "
+ "Започни ћаскање преко AIM-а"
+ "Започни ћаскање преко Windows Live-а"
+ "Започни ћаскање преко Yahoo-а"
+ "Започни ћаскање преко Skype-а"
+ "Започни ћаскање преко QQ-а"
+ "Започни ћаскање преко Google Talk-а"
+ "Започни ћаскање преко ICQ-а"
+ "Започни ћаскање преко Jabber-а"
+ "Ћаскање"
+ "избриши"
+ "Проширивање или скупљање поља за називе"
+ "Сви контакти"
+ "Са звездицом"
+ "Прилагоди"
+ "Контакт"
+ "Сви други контакти"
+ "Сви контакти"
+ "Уклони групу за синхронизацију"
+ "Додавање групе за синхронизацију"
+ "Још група…"
+ "Уклањањем групе „%s “ са листе за синхронизацију уклонићете и све негруписане контакте са те листе."
+ "Чување опција приказа..."
+ "Готово"
+ "Откажи"
+ "Контакти у групи %s "
+ "Контакти у прилагођеном приказу"
+ "Појединачни контакт"
+ "Прављење контакта у оквиру налога"
+ "Увези са SIM картице"
+ "Увоз са SIM картице ^1 – ^2 "
+ "Увоз са SIM картице %1$s "
+ "Увези из .vcf датотеке"
+ "Желите ли да откажете увоз датотеке %s ?"
+ "Желите ли да откажете извоз датотеке %s ?"
+ "Није могуће отказати vCard увоз/извоз"
+ "Непозната грешка."
+ "Није могуће отворити датотеку „%s “: %s ."
+ "Није могуће покренути програм за извоз: „%s “"
+ "Нема контаката за извоз."
+ "Онемогућили сте обавезну дозволу."
+ "Дошло је до грешке при извозу: „%s “"
+ "Захтевани назив датотеке је предугачак („%s “)."
+ "На SD картици има превише vCard датотека."
+ "У/И грешка"
+ "Нема довољно меморије. Датотека је можда превелика."
+ "Из неочекиваног разлога није могуће рашчланити vCard датотеку."
+ "Формат није подржан."
+ "Није могуће прикупити метаподатке наведених vCard датотека."
+ "Није могућ увоз једне или више датотека (%s)."
+ "Извоз датотеке %s је завршен."
+ "Извоз контаката је завршен."
+ "Извоз датотеке %s је отказан."
+ "Извоз података о контактима је у току"
+ "Подаци о контактима се извозе у: %s ."
+ "Преузимање информација из базе података није могуће."
+ "Нема контаката за извоз. Ако имате контакте на телефону, неки добављачи података можда неће дозволити да се контакти извозе са телефона."
+ "Програм за израду vCard датотека се није исправно покренуо."
+ "Извоз није могућ"
+ "Подаци о контактима низу извезени.\nРазлог: „%s “"
+ "Увоз контакта %s "
+ "Читање vCard података није могуће"
+ "Читање vCard података је отказано"
+ "Увоз vCard датотеке %s је завршен"
+ "Увоз датотеке %s је отказан"
+ "Датотека %s ће ускоро бити увезена."
+ "Датотека ће ускоро бити увезена."
+ "Захтев за увоз vCard датотеке је одбијен. Покушајте поново касније."
+ "Датотека %s ће ускоро бити извезена."
+ "Датотека ће ускоро бити извезена."
+ "Захтев за извоз vCard датотеке је одбијен. Покушајте поново касније."
+ "контакт"
+ "Кеширање vCard датотекa у локалну привремену меморију. Увоз ће ускоро започети."
+ "Увоз vCard датотеке није могућ."
+ "На SD картици није пронађена ниједна vCard датотека."
+ "Контакт преко NFC-а"
+ "Желите ли да извезете контакте?"
+ "Кеширање"
+ "Није могуће скенирати SD картицу. (Разлог: „%s “)"
+ "Увози се %s /%s : %s "
+ "Извези у .vcf датотеку"
+ "Сортирај према"
+ "Имену"
+ "Презимену"
+ "Формат имена и презимена"
+ "Прво име"
+ "Прво презиме"
+ "Дели видљиве контакте"
+ "Није успело дељење видљивих контаката."
+ "Увоз/извоз контаката"
+ "Увоз контаката"
+ "Овај контакт не може да се дели."
+ "Претражи"
+ "Контакти за приказ"
+ "Контакти за приказ"
+ "Прилагођени приказ"
+ "Пронађите контакте"
+ "Омиљено"
+ "Нема контаката."
+ "Нема видљивих контаката."
+ "Нема омиљених."
+ "Нема контаката у групи %s "
+ "Обриши често контактиране"
+ "Изаберите SIM картицу"
+ "Налози"
+ "Увези/извези"
+ "преко %1$s "
+ "%1$s преко %2$s "
+ "заустављање претраживања"
+ "Брисање претраге"
+ "Опције приказивања контаката"
+ "Налог"
+ "Увек користи ово за позиве"
+ "Позови помоћу"
+ "Позив са белешком"
+ "Унесите белешку коју ћете послати уз позив..."
+ "ПОШАЉИ И ПОЗОВИ"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-sv/strings.xml b/ContactsCommon/res/values-sv/strings.xml
new file mode 100644
index 0000000..3ca8b88
--- /dev/null
+++ b/ContactsCommon/res/values-sv/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Texten har kopierats"
+ "Kopiera till Urklipp"
+ "Ring %s "
+ "Ring hem"
+ "Ring mobilen"
+ "Ring arbete"
+ "Ring upp arbetsfax"
+ "Ring upp hemfax"
+ "Ring personsökare"
+ "Ring"
+ "Ring upp återuppringningsnummer"
+ "Ring bilen"
+ "Ring företag"
+ "Ring upp ISDN"
+ "Ring upp primärt nummer"
+ "Ring upp fax"
+ "Ring radio"
+ "Ring upp telex"
+ "Ring upp TTY/TDD"
+ "Ring upp jobbmobil"
+ "Ring upp jobbpersonsökare"
+ "Ring %s "
+ "Ring upp MMS"
+ "Skicka SMS till %s "
+ "Skicka SMS till hem"
+ "Skicka SMS till mobil"
+ "Skicka SMS till arbete"
+ "Skicka SMS till arbetsfax"
+ "Skicka SMS till hemfax"
+ "Skicka SMS till personsökare"
+ "SMS"
+ "Skicka SMS till återuppringningsnummer"
+ "Skicka SMS till bil"
+ "Skicka SMS till företag"
+ "Skicka SMS till ISDN"
+ "Skicka SMS till primärt nummer"
+ "Skicka SMS till fax"
+ "Skicka SMS till radio"
+ "Skicka SMS till telex"
+ "Skicka SMS till TTY/TDD"
+ "Skicka SMS till jobbmobil"
+ "Skicka SMS till jobbpersonsökare"
+ "Skicka SMS till %s "
+ "Skicka SMS till MMS"
+ "Ring videosamtal"
+ "Vill du rensa listan?"
+ "Du rensar listan över personer som du kontaktar ofta i apparna Kontakter och Telefon. E-postappar tvingas lära sig dina mottagarinställningar från början."
+ "Listan rensas …"
+ "Tillgänglig"
+ "Borta"
+ "Upptagen"
+ "Kontakter"
+ "Annan"
+ "Katalog"
+ "Alla kontakter"
+ "Jag"
+ "Söker ..."
+ "Fler än %d hittades."
+ "Inga kontakter"
+
+ %d hittades
+ - 1 hittades
+
+ "Snabbkontakt för %1$s "
+ "(Inget namn)"
+ "Ringer ofta"
+ "Kontaktar ofta"
+ "Visa kontakt"
+ "Alla kontakter med telefonnummer"
+ "Visa uppdateringar"
+ "Bara telefon (osynkad)"
+ "Namn"
+ "Smeknamn"
+ "Namn"
+ "Förnamn"
+ "Efternamn"
+ "Namnprefix"
+ "Mellannamn"
+ "Namnsuffix"
+ "Fonetiskt namn"
+ "Fonetiskt förnamn"
+ "Fonetiskt mellannamn"
+ "Fonetiskt efternamn"
+ "Telefon"
+ "E-post"
+ "Adress"
+ "Chatt"
+ "Organisation"
+ "Relation"
+ "Särskilda datum"
+ "SMS"
+ "Adress"
+ "Företag"
+ "Befattning"
+ "Kommentarer"
+ "SIP"
+ "Webbplats"
+ "Grupper"
+ "E-postadress – hem"
+ "Skicka e-post till mobil"
+ "E-postadress – arbete"
+ "E-post"
+ "Skicka e-post till %s "
+ "E-post"
+ "Gatuadress"
+ "Postbox"
+ "Område"
+ "Ort"
+ "Delstat"
+ "Postnummer"
+ "Land"
+ "Visa hemadress"
+ "Visa jobbadress"
+ "Visa adress"
+ "Visa adress, %s "
+ "Chatta med AIM"
+ "Chatta med Windows Live"
+ "Chatta med Yahoo"
+ "Chatta med Skype"
+ "Chatta med QQ"
+ "Chatta med Google Talk"
+ "Chatta med ICQ"
+ "Chatta med Jabber"
+ "Chatta"
+ "ta bort"
+ "Expandera eller komprimera namnfält"
+ "Alla kontakter"
+ "Stjärnmärkta"
+ "Anpassa"
+ "Kontakt"
+ "Alla andra kontakter"
+ "Alla kontakter"
+ "Ta bort synkgrupp"
+ "Lägg till synkgrupp"
+ "Fler grupper …"
+ "Om du tar bort %s från synkroniseringen tas även kontakter som inte tillhör grupper bort från synkroniseringen."
+ "Sparar visningsalternativ …"
+ "Klar"
+ "Avbryt"
+ "Kontakter i %s "
+ "Kontakter i anpassad vy"
+ "En kontakt"
+ "Skapa kontakt under konto"
+ "Importera från SIM-kort"
+ "Importera från SIM-kort ^1 – ^2 "
+ "Importera från SIM-kort %1$s "
+ "Importera från VCF-fil"
+ "Vill du avbryta importen av %s ?"
+ "Vill du avbryta exporten av %s ?"
+ "Kunde ej avbryta import/export av vCard"
+ "Okänt fel."
+ "Det gick inte att öppna %s : %s ."
+ "Det gick inte att starta exportverktyget: %s ."
+ "Det finns ingen kontakt att exportera."
+ "Du har inaktiverat en behörighet som krävs."
+ "Ett fel inträffade under exporten: %s ."
+ "Det obligatoriska filnamnet är för långt (%s )."
+ "Det finns för många vCard-filer på SD-kortet."
+ "I/O-fel"
+ "Det finns inte tillräckligt med minne. Filen kan vara för stor."
+ "Det gick inte att analysera vCard av okänd anledning."
+ "Formatet stöds inte."
+ "Det gick inte att samla in metainformation för de angivna vCard-filerna."
+ "En eller flera filer kunde inte importeras (%s)."
+ "%s har exporterats."
+ "Kontakterna har exporterats."
+ "Exporten av %s avbröts."
+ "Kontaktuppgifter exporteras"
+ "Kontaktuppgifterna exporteras till: %s ."
+ "Det gick inte att hämta databasinformation."
+ "Det finns inga kontakter att exportera. Om du har kontakter på mobilen kanske vissa dataleverantörer inte tillåter att kontakterna exporteras från mobilen."
+ "vCard-kompositören initierades inte korrekt."
+ "Kunde inte exportera"
+ "Kontaktuppgifterna exporterades inte.\nOrsak: %s "
+ "Importerar %s "
+ "Det gick inte att läsa vCard-data"
+ "Inläsningen av vCard-data avbröts"
+ "vCard-filen %s har importerats"
+ "Importen av %s avbröts"
+ "%s importeras snart."
+ "Filen kommer snart att importeras."
+ "Begäran om vCard-import avvisades. Försök igen vid ett senare tillfälle."
+ "%s exporteras snart."
+ "Filen kommer snart att exporteras."
+ "Begäran om vCard-export avvisades. Försök igen vid ett senare tillfälle."
+ "kontakt"
+ "vCard-fil(er) cachelagras till en lokal tillfällig lagringsenhet. Den faktiska importen börjar snart."
+ "Det gick inte att importera vCard."
+ "Det finns ingen vCard-fil på SD-kortet."
+ "Mott. v. NFC"
+ "Vill du exportera kontakter?"
+ "Cachelagrar"
+ "Det gick inte att skanna SD-kortet. (Orsak: %s )"
+ "Importerar %s /%s : %s "
+ "Exportera till VCF-fil"
+ "Sortera efter"
+ "Förnamn"
+ "Efternamn"
+ "Namnformat"
+ "Förnamn först"
+ "Efternamn först"
+ "Dela synliga kontakter"
+ "Det gick inte att dela synliga kontakter."
+ "Importera/exportera kontakter"
+ "Importera kontakter"
+ "Den här kontakten kan inte delas."
+ "Sök"
+ "Kontakter som ska visas"
+ "Kontakter som ska visas"
+ "Definiera anpassad vy"
+ "Sök efter kontakter"
+ "Favoriter"
+ "Inga kontakter."
+ "Det finns inga synliga kontakter."
+ "Inga favoriter."
+ "Inga kontakter i %s "
+ "Rensa listan över kontakter"
+ "Välj SIM-kort"
+ "Konton"
+ "Importera/exportera"
+ "via %1$s "
+ "%1$s via %2$s "
+ "avbryt sökning"
+ "Rensa sökning"
+ "Visningsalternativ för kontakter"
+ "Konto"
+ "Använd alltid för samtal"
+ "Ring med"
+ "Ring med anteckning"
+ "Gör en anteckning som skickas när du ringer …"
+ "SKICKA OCH RING"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-sw/strings.xml b/ContactsCommon/res/values-sw/strings.xml
new file mode 100644
index 0000000..398ea1f
--- /dev/null
+++ b/ContactsCommon/res/values-sw/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Maandishi yamenakiliwa"
+ "Nakili kwenye ubao klipu"
+ "Pia simu %s "
+ "Piga simu nyumbani"
+ "Pigia simu ya mkononi"
+ "Piga simu kazini"
+ "Ipigie simu kipepesi cha kazini"
+ "Ipigie simu kipepesi cha nyumbani"
+ "Ipigie peja simu"
+ "Piga simu"
+ "Mpigie simu aliyepiga"
+ "Piga simu ya gari"
+ "Pigia simu kuu ya kampuni"
+ "Piga simu kwa ISDN"
+ "Piga simu kuu"
+ "Ipigie kipepesi simu"
+ "Piga simu redioni"
+ "Piga simu kwa teleksi"
+ "Piga simu TTY/TDD"
+ "Ipigie simu ya mkononi ya kazini"
+ "Ipigie peja ya kazini"
+ "Piga simu %s "
+ "Piga simu kwa MMS"
+ "Tumia ujumbe wa maandishi kwa %s "
+ "Tuma ujumbe wa maandishi nyumbani"
+ "Tumia simu ya mkononi ujumbe wa maandishi"
+ "Tuma ujumbe wa maandishi kazini"
+ "Tumia ujumbe kwenda kipepesi cha kazini"
+ "Tuma ujumbe kwenda kipepesi ch nyumbani"
+ "Tumia peja ujumbe wa maandishi"
+ "Tuma ujumbe wa maandishi"
+ "Tuma ujumbe wa maandishi kwa aliyepiga"
+ "Tuma ujumbe kwa gari"
+ "Tuma ujumbe wa maandishi kwenda simu kuu ya kampuni"
+ "Tumia ISDN ujumbe wa maandishi"
+ "Tumia simu kuu ujumbe wa maandishi"
+ "Tuma ujumbe kwenda katika kipepesi"
+ "Tuma ujumbe wa maandishi redioni"
+ "Tumia teleksi ujumbe wa maandishi"
+ "Tumia TTY/TDD ujumbe wa maandishi"
+ "Itumie simu ya mkononi ya kazini ujumbe"
+ "Itumie peja ya kazini ya kazini ujumbe"
+ "Tuma ujumbe wa maandishi kwa %s "
+ "Tumia MMS ujmbe wa maandishi"
+ "Piga Hangout ya video"
+ "Futa unaowasiliana nao mara kwa mara?"
+ "Utafuta orodha ya unaowasiliana nao mara kwa mara katika programu za Anwani na Simu, na ulazimishe programu za barua pepe zitambue mapendeleo yako ya anwani kutoka mwanzo."
+ "Inafuta unaowasiliana nao mara kwa mara..."
+ "Inapatikana"
+ "Mbali"
+ "Ana shughuli"
+ "Anwani"
+ "Kingine"
+ "Saraka"
+ "Anwani zote"
+ "Mimi"
+ "Inatafuta…"
+ "Zaidi ya %d zimepatikana."
+ "Hakuna anwani"
+
+ - Zimepatikana
%d
+ - Imepatikana 1
+
+ "Anwani ya haraka ya %1$s "
+ "(Hakuna jina)"
+ "Zinazopigwa mara kwa mara"
+ "Unaowasiliana nao zaidi"
+ "Angalia anwani"
+ "Anwani zote zilizo na nambari ya simu"
+ "Ona sasisho"
+ "Simu pekee, haijalandanishwa"
+ "Jina"
+ "Lakabu"
+ "Jina"
+ "Jina la kwanza"
+ "Jina la mwisho"
+ "Herufi za kwanza za jina"
+ "Jina la kati"
+ "Kiambishi tamati cha jina"
+ "Jina la fonetiki"
+ "Jina la kwanza kifonetiki"
+ "Jina la kati la fonetiki"
+ "Jina la mwisho kifonetiki"
+ "Simu"
+ "Barua pepe"
+ "Anwani"
+ "Ujumbe wa Papo Hapo"
+ "Shirika"
+ "Uhusiano"
+ "Tarehe maalum"
+ "SMS, (Ujumbe wa maandishi)"
+ "Anwani"
+ "Kampuni"
+ "Kichwa"
+ "Madokezo"
+ "SIP"
+ "Tovuti"
+ "Vikundi"
+ "Tuma barua pepe nyumbani"
+ "Tuma barua pepe kwenye simu ya mkononi"
+ "Tuma barua pepe kazini"
+ "Tuma barua pepe"
+ "Tuma barua pepe kwenye %s "
+ "Barua pepe"
+ "Barabara"
+ "Sanduku la posta"
+ "Mtaa"
+ "Mji"
+ "Jimbo"
+ "Msimbo wa posta"
+ "Nchi"
+ "Tazama anwani ya nyumbani"
+ "Tazama anwani ya kazini"
+ "Tazama anwani"
+ "Tazama anwani ya %s "
+ "Piga gumzo kutumia AIM"
+ "Piga gumzo kutumia Windows Live"
+ "Piga gumzo kutumia Yahoo"
+ "Piga gumzo kutumia Skype"
+ "Piga gumzo kutumia QQ"
+ "Piga gumzo kutumia Google Talk"
+ "Piga gumzo kutumia ICQ"
+ "Piga gumzo kutumia Jabber"
+ "Gumzo"
+ "futa"
+ "Panua au ukunje sehemu za jina"
+ "Anwani zote"
+ "Yenye nyota"
+ "Badilisha kukufaa"
+ "Anwani"
+ "Anwani zingine zote"
+ "Anwani zote"
+ "Ondoa kikundi cha ulinganishaji"
+ "Ongeza kikundi kilicholinganishwa"
+ "Vikundi zaidi..."
+ "Kuondoa \"%s \" \"kutoka kwenye ulinganishaji pia kutaondoa anwani zozote zisizo katika kundi kutoka kwenye ulinganishaji."
+ "Inahifadhi chaguo za mwonyesho..."
+ "Umekamilisha"
+ "Ghairi"
+ "Anwani kwenye %s "
+ "Anwani katika mwoneko maalum"
+ "Anwani moja"
+ "Weka anwani katika akaunti"
+ "Ingiza kutoka SIM kadi"
+ "Leta kutoka SIM ^1 - ^2 "
+ "Leta kutoka SIM %1$s "
+ "Leta kutoka faili ya .vcf"
+ "Ghairi uhamisho wa %s ?"
+ "Ighairi uhamisho wa %s ?"
+ "Haikuweza kughairi uingizaji/uhamishaji wa vCard"
+ "Hitilafu isiyojulikana."
+ "Haikuweza kufungua \"%s \": %s ."
+ "Haikuweza kuanzisha kihamishaji: \"%s \"."
+ "Hakuna anwani inayoweza kuhamishwa."
+ "Umezima ruhusa inayohitajika."
+ "Hitilafu imetokea wakati wa uhamisho: \"%s \"."
+ "Jina la faili linalohitajika ni ndefu sana (\"%s \")."
+ "Kuna faili nyingi mno za Vcard katika kadi ya SD."
+ "Hitilafu ya I/O"
+ "Hakuna kumbukumbu ya kutosha. Faili inaweza kuwa kubwa mno."
+ "Haikuweza kuchanganua vCard kwa sababu isiyotarajiwa."
+ "Muundo huu hautumiki."
+ "Haikuweza kukusanya maelezo meta ya faili zilizotolewa za vCard."
+
+
+
+ "Imemaliza kuhamisha %s ."
+ "Imekamilisha kuhamisha anwani."
+ "Kuhamisha %s kumeghairiwa."
+ "Inahamisha data ya anwani"
+ "Data ya anwani yako inahamishwa %s ."
+ "Haikupata maelezo ya hifadhidata."
+ "Hakuna anwani zinazohamishika. Kama una anwani kwenye simu yako, baadhi ya watoa huduma za data hawawezi kuruhusu anwani kuhamishwa kutoka kwenye simu."
+ "Kitunzi cha vCard hakikuanza vizuri."
+ "Isingehamishika"
+ "Data ya anwani haikuhamishwa.\nKwa sababu: \"%s \""
+ "Inaleta %s "
+ "Haikuweza kusoma data ya vCard"
+ "Kusoma data ya VCard kumeghairiwa"
+ "Imemaliza kuleta %s ya vCard"
+ "Kuleta %s kumeghairiwa"
+ "%s italetwa hivi karibuni."
+ "Faili italetwa hivi karibuni."
+ "Ombi la kuleta vCard limekataliwa. Tafadhali jaribu baadaye."
+ "%s itahamishwa baada ya muda mfupi."
+ "Faili itahamishwa baada ya dakika chache."
+ "Ombi la kuhamishwa kwa vCard limekataliwa. Jaribu tena baadaye."
+ "anwani"
+ "Vcard Inaakibisha ndani ya hifadhi ya muda mfupi. Uhamisho halisi utaanza hivi karibuni."
+ "Haikuweza kuleta vCard."
+ "Hakuna faili ya vCard iliyopatikana kwenye kadi ya SD."
+ "Anwani imepokewa kupitia NFC"
+ "Anwani Zihamishwe?"
+ "Inaakibisha"
+ "Kadi ya SD haikutambazwa. (Sababu: \"%s \")"
+ "Inaleta %s /%s : %s "
+ "Tuma kwenye faili ya .vcf"
+ "Panga kulingana na"
+ "Jina la kwanza"
+ "Jina la mwisho"
+ "Mpangilio wa majina"
+ "Jina la kwanza liwe mwanzo"
+ "Jina la mwisho kwanza"
+ "Shiriki anwani zinazoonekana"
+ "Imeshindwa kushiriki anwani zinazoonekana."
+ "Ingiza au uhamishe anwani"
+ "Ingiza anwani"
+ "Anwani hii haiwezi kushirikiwa."
+ "Tafuta"
+ "Anwani za kuonyesha"
+ "Anwani za kuonyesha"
+ "Bainisha mwonekano maalum"
+ "Tafuta anwani"
+ "Vipendwa"
+ "Hakuna anwani."
+ "Hakuna anwani zinazoonekana"
+ "Hakuna vipendwa."
+ "Hakuna anwani kwenye %s "
+ "Futa za mara kwa mara"
+ "Chagua SIM kadi"
+ "Akaunti"
+ "Ingiza au uhamishe"
+ "kupitia %1$s "
+ "%1$s kupitia %2$s "
+ "acha kutafuta"
+ "Futa utafutaji"
+ "Chaguo za onyesho la anwani"
+ "Akaunti"
+ "Tumia hii kwa simu wakati wote"
+ "Piga simu ukitumia"
+ "Piga simu inayoambatana na dokezo"
+ "Andika dokezo litakaloambatana na simu utakayopiga ..."
+ "TUMA na UPIGE SIMU"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-sw600dp-land/integers.xml b/ContactsCommon/res/values-sw600dp-land/integers.xml
new file mode 100644
index 0000000..bd9eb0e
--- /dev/null
+++ b/ContactsCommon/res/values-sw600dp-land/integers.xml
@@ -0,0 +1,22 @@
+
+
+
+ 3
+
+
+ 20
+
diff --git a/ContactsCommon/res/values-sw600dp/dimens.xml b/ContactsCommon/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..2e64fd9
--- /dev/null
+++ b/ContactsCommon/res/values-sw600dp/dimens.xml
@@ -0,0 +1,28 @@
+
+
+
+ 0dip
+
+ @dimen/list_visible_scrollbar_padding
+ 24dip
+ 16dip
+
+
+ 32dp
+
+ 32dp
+
diff --git a/ContactsCommon/res/values-sw600dp/integers.xml b/ContactsCommon/res/values-sw600dp/integers.xml
new file mode 100644
index 0000000..5b1c92b
--- /dev/null
+++ b/ContactsCommon/res/values-sw600dp/integers.xml
@@ -0,0 +1,24 @@
+
+
+
+ 3
+
+
+
+ 15
+
diff --git a/ContactsCommon/res/values-sw600dp/styles.xml b/ContactsCommon/res/values-sw600dp/styles.xml
new file mode 100644
index 0000000..2130ce1
--- /dev/null
+++ b/ContactsCommon/res/values-sw600dp/styles.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/values-sw720dp-land/integers.xml b/ContactsCommon/res/values-sw720dp-land/integers.xml
new file mode 100644
index 0000000..a83cdff
--- /dev/null
+++ b/ContactsCommon/res/values-sw720dp-land/integers.xml
@@ -0,0 +1,22 @@
+
+
+
+ 4
+
+
+ 30
+
diff --git a/ContactsCommon/res/values-sw720dp/integers.xml b/ContactsCommon/res/values-sw720dp/integers.xml
new file mode 100644
index 0000000..930adb3
--- /dev/null
+++ b/ContactsCommon/res/values-sw720dp/integers.xml
@@ -0,0 +1,22 @@
+
+
+
+ 2
+
+
+ 20
+
diff --git a/ContactsCommon/res/values-ta-rIN/strings.xml b/ContactsCommon/res/values-ta-rIN/strings.xml
new file mode 100644
index 0000000..0e40386
--- /dev/null
+++ b/ContactsCommon/res/values-ta-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "உரை நகலெடுக்கப்பட்டது"
+ "கிளிப்போர்டுக்கு நகலெடு"
+ "%s மொபைல் எண்ணில் அழை"
+ "வீட்டு தொலைபேசி எண்ணில் அழை"
+ "மொபைல் எண்ணில் அழை"
+ "பணியிடத் தொலைபேசி எண்ணில் அழை"
+ "பணியிடத்தின் தொலைநகல் எண்ணில் அழை"
+ "வீட்டின் தொலைநகல் எண்ணில் அழை"
+ "பேஜர் எண்ணில் அழை"
+ "அழை"
+ "திரும்ப அழை"
+ "காரின் மொபைல் எண்ணில் அழை"
+ "நிறுவனத்தின் முதன்மை மொபைல் எண்ணில் அழை"
+ "ISDN தொலைபேசி எண்ணில் அழை"
+ "முதன்மைத் தொலைபேசி எண்ணில் அழை"
+ "தொலைநகல் எண்ணில் அழை"
+ "ரேடியோ தொலைபேசி எண்ணில் அழை"
+ "டெலக்ஸ் எண்ணில் அழை"
+ "TTY/TDD தொலைபேசி எண்ணில் அழை"
+ "பணியிட மொபைல் எண்ணில் அழை"
+ "பணியிடத்தின் பேஜர் எண்ணில் அழை"
+ "%s ஐ அழை"
+ "MMS தொலைபேசி எண்ணில் அழை"
+ "%s க்கு உரைச்செய்தி அனுப்பு"
+ "வீட்டு தொலைபேசி எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "மொபைல் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "பணியிட தொலைபேசி எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "பணியிட தொலைநகல் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "வீட்டு தொலைநகல் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "பேஜர் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "உரை"
+ "அழைத்த எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "காரின் தொலைபேசி எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "நிறுவனத்தின் முதன்மை மொபைல் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "ISDN தொலைபேசி எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "முதன்மைத் தொலைபேசி எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "தொலைநகல் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "ரேடியோ தொலைபேசி எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "டெலக்ஸ் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "TTY/TDD தொலைபேசி எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "பணியிட மொபைல் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "பணியிட பேஜர் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "%s க்கு உரைச்செய்தி அனுப்பு"
+ "MMS மொபைல் எண்ணிற்கு உரைச்செய்தி அனுப்பு"
+ "வீடியோவில் அழை"
+ "அடிக்கடி தொடர்புகொண்ட தொடர்பை அழிக்கவா?"
+ "தொடர்புகள் மற்றும் ஃபோன் பயன்பாடுகளில் உள்ள அடிக்கடி தொடர்பு கொண்டவர்களின் பட்டியல் அழிக்கப்பட்டு, தொடக்கத்திலிருந்து மீண்டும் உங்கள் முகவரியிடல் விருப்பத்தேர்வுகளை மின்னஞ்சல் பயன்பாடுகள் அறியும்படி செய்யப்படும்."
+ "அடிக்கடித் தொடர்புகொண்ட தொடர்பை அழிக்கிறது…"
+ "இருக்கிறார்"
+ "வெளியே"
+ "பணிமிகுதி"
+ "தொடர்புகள்"
+ "பிற"
+ "கோப்பகம்"
+ "எல்லா தொடர்புகளும்"
+ "நான்"
+ "தேடுகிறது..."
+ "%d க்கும் மேற்பட்டவை கண்டறியப்பட்டன."
+ "தொடர்புகள் இல்லை"
+
+ %d தொடர்புகள் உள்ளன
+ - 1 தொடர்பு உள்ளது
+
+ "%1$s க்கான விரைவு தொடர்பு"
+ "(பெயர் இல்லை)"
+ "அடிக்கடி தொடர்புகொண்டவர்கள்"
+ "அடிக்கடி தொடர்புகொண்டவர்கள்"
+ "தொடர்பைக் காட்டு"
+ "மொபைல் எண்களுடனான எல்லா தொடர்புகளும்"
+ "புதுப்பிப்புகளைக் காட்டு"
+ "மொபைல் மட்டும், ஒத்திசைக்கப்படவில்லை"
+ "பெயர்"
+ "செல்லப்பெயர்"
+ "பெயர்"
+ "முதல் பெயர்"
+ "இறுதிப் பெயர்"
+ "பெயரின் முன்னொட்டு"
+ "பெயரின் நடுப்பகுதி"
+ "பெயரின் பின்னொட்டு"
+ "ஒலிப்புமுறை பெயர்"
+ "ஒலிப்புமுறை முதல் பெயர்"
+ "ஒலிப்புமுறையில் பெயரின் நடுப்பகுதி"
+ "ஒலிப்புமுறை இறுதிப் பெயர்"
+ "தொலைபேசி"
+ "மின்னஞ்சல்"
+ "முகவரி"
+ "IM"
+ "நிறுவனம்"
+ "உறவுமுறை"
+ "முக்கிய தேதிகள்"
+ "உரைச் செய்தி"
+ "முகவரி"
+ "நிறுவனம்"
+ "தலைப்பு"
+ "குறிப்புகள்"
+ "SIP"
+ "இணையதளம்"
+ "குழுக்கள்"
+ "வீட்டு மின்னஞ்சல் முகவரிக்கு மின்னஞ்சல் அனுப்பு"
+ "மொபைல் மின்னஞ்சல் முகவரிக்கு மின்னஞ்சல் அனுப்பு"
+ "பணியிட மின்னஞ்சல் முகவரிக்கு மின்னஞ்சல் அனுப்பு"
+ "மின்னஞ்சல்"
+ "%s க்கு மின்னஞ்சல் அனுப்பு"
+ "மின்னஞ்சல்"
+ "தெரு"
+ "அஞ்சல் பெட்டி"
+ "சுற்றுப்புறங்கள்"
+ "நகரம்"
+ "மாநிலம்"
+ "ஜிப் குறியீடு"
+ "நாடு"
+ "வீட்டு முகவரியைக் காட்டு"
+ "பணியிட முகவரியைக் காட்டு"
+ "முகவரியைக் காட்டு"
+ "%s முகவரியைக் காட்டு"
+ "AIM இல் அரட்டையடி"
+ "Windows Live இல் அரட்டையடி"
+ "Yahoo இல் அரட்டையடி"
+ "Skype இல் அரட்டையடி"
+ "QQ இல் அரட்டையடி"
+ "Google Talk இல் அரட்டையடி"
+ "ICQ இல் அரட்டையடி"
+ "Jabber இல் அரட்டையடி"
+ "அரட்டை"
+ "நீக்கு"
+ "பெயர் புலங்களை விரிவுப்படுத்து அல்லது சுருக்கு"
+ "எல்லா தொடர்புகளும்"
+ "நட்சத்திரமிட்டது"
+ "தனிப்பயனாக்கு"
+ "தொடர்பு"
+ "பிற எல்லா தொடர்புகளும்"
+ "எல்லா தொடர்புகளும்"
+ "ஒத்திசைவுக் குழுவை அகற்று"
+ "ஒத்திசைவு குழுவைச் சேர்"
+ "மேலும் குழுக்கள்…"
+ "ஒத்திசைவிலிருந்து \"%s \" ஐ அகற்றுவது ஒத்திசைவில் உள்ள குழுவாக்கப்படாத தொடர்புகளும் எதையும் அகற்றும்."
+ "காட்சி விருப்பங்களைச் சேமிக்கிறது…"
+ "முடிந்தது"
+ "ரத்துசெய்"
+ "%s இல் உள்ள தொடர்புகள்"
+ "தனிப்பயன் காட்சியில் உள்ள தொடர்புகள்"
+ "ஒரு தொடர்பு"
+ "கணக்கின் கீழ் தொடர்பை உருவாக்கு"
+ "சிம் கார்டிலிருந்து இறக்குமதிசெய்"
+ "^1 - ^2 என்ற SIM இல் இருந்து இறக்குமதிசெய்"
+ "%1$s என்ற SIM இல் இருந்து இறக்குமதிசெய்"
+ ".vcf கோப்பிலிருந்து இறக்கு"
+ "%s இன் இறக்குமதியை ரத்துசெய்யவா?"
+ "%s இன் ஏற்றுமதியை ரத்துசெய்யவா?"
+ "vCard இன் இறக்குமதி/ஏற்றுமதியை ரத்துசெய்ய முடியவில்லை"
+ "தெரியாத பிழை."
+ "\"%s \" ஐத் திறக்க முடியவில்லை: %s ."
+ "ஏற்றுமதியைத் தொடங்க முடியவில்லை: \"%s \"."
+ "ஏற்றுமதி செய்யக்கூடிய தொடர்பு இல்லை."
+ "தேவைப்படும் அனுமதியை முடக்கியுள்ளீர்கள்."
+ "ஏற்றுமதி செய்யும்போது பிழை: \"%s \"."
+ "தேவையான கோப்பின் பெயர் மிகவும் நீளமாக உள்ளது (\"%s \")."
+ "SD கார்டில் அதிகமான vCard கோப்புகள் உள்ளன."
+ "I/O பிழை"
+ "போதுமான நினைவகம் இல்லை. கோப்பு மிகவும் பெரியதாக இருக்கலாம்."
+ "எதிர்பாராதவிதமாக vCard ஐப் பாகுபடுத்த முடியவில்லை."
+ "வடிவம் ஆதரிக்கப்படவில்லை."
+ "வழங்கப்பட்ட vCard கோப்பின்(களின்) மெட்டா தகவலைச் சேகரிக்க முடியவில்லை."
+ "ஒன்று அல்லது அதற்கு மேற்பட்ட கோப்புகளை ஏற்றுமதி செய்ய முடியவில்லை (%s)."
+ "%s ஐ ஏற்றுமதி செய்வது முடிந்தது."
+ "தொடர்புகளை ஏற்றுவது முடிந்தது."
+ "%s ஐ ஏற்றுமதி செய்வது ரத்துசெய்யப்பட்டது."
+ "தொடர்பு தரவை ஏற்றுமதி செய்கிறது"
+ "தொடர்பின் தரவு ஏற்றுமதி செய்யப்படுகிறது: %s ."
+ "தரவுத்தளத் தகவலைப் பெற முடியவில்லை."
+ "ஏற்றுமதி செய்யத்தக்க தொடர்புகள் இல்லை. மொபைலில் தொடர்புகள் இல்லை எனில், மொபைலிலிருந்து தொடர்புகளை ஏற்றுமதி செய்வதற்குச் சில தரவு வழங்குநர்கள் அனுமதிக்காமல் போகலாம்."
+ "vCard தொகுப்பான் முறையாகத் தொடங்கவில்லை."
+ "ஏற்றுமதி செய்ய முடியவில்லை"
+ "தொடர்பு தரவு ஏற்றுமதி செய்யப்படவில்லை.\nகாரணம்: \"%s \""
+ "%s ஐ இறக்குமதி செய்கிறது"
+ "vCard தரவைப் படிக்க முடியவில்லை"
+ "vCard தரவைப் படிப்பது ரத்துசெய்யப்பட்டது"
+ "vCard %s ஐ இறக்குமதிசெய்வது முடிந்தது"
+ "%s ஐ இறக்குமதிசெய்வது ரத்துசெய்யப்பட்டது"
+ "%s விரைவில் இறக்குமதி செய்யப்படும்."
+ "கோப்பு விரைவில் இறக்குமதி செய்யப்படும்."
+ "vCard இன் இறக்குமதி கோரிக்கை நிராகரிக்கப்பட்டது. பிறகு முயற்சிக்கவும்."
+ "%s விரைவில் ஏற்றுமதி செய்யப்படும்."
+ "கோப்பு விரைவில் ஏற்றப்படும்."
+ "vCard இன் ஏற்றுமதி கோரிக்கை நிராகரிக்கப்பட்டது. பிறகு முயற்சிக்கவும்."
+ "தொடர்பு"
+ "vCard(களை) ஐ அகச் சேமிப்பிடத்தில் தற்காலிகமாகச் சேமிக்கிறது. அசல் இறக்குமதி உடனடியாக தொடங்கப்படும்."
+ "vCard ஐ இறக்குமதி செய்ய முடியவில்லை."
+ "SD கார்டில் vCard கோப்பு இல்லை."
+ "NFC வழியாக தொடர்பு பெறப்பட்டது"
+ "தொடர்புகளை இறக்கவா?"
+ "தற்காலிகமாகச் சேமித்தல்"
+ "SD கார்டு ஸ்கேன் செய்யப்படவில்லை. (காரணம்: \"%s \")"
+ "%s /%s ஐ இறக்குமதி செய்கிறது: %s "
+ ".vcf கோப்பிற்கு ஏற்று"
+ "வரிசைப்படுத்து"
+ "முதல் பெயர்"
+ "இறுதிப் பெயர்"
+ "பெயர் வடிவம்"
+ "முதல் பெயர் முதலில்"
+ "இறுதிப் பெயர் முதலில்"
+ "தெரியும் தொடர்புகளைப் பகிர்"
+ "தெரியும் தொடர்புகளைப் பகிர்வதில் தோல்வி."
+ "தொடர்புகளை இறக்கு/ஏற்று"
+ "தொடர்புகளை இறக்குமதி செய்தல்"
+ "தொடர்பைப் பகிர முடியவில்லை."
+ "தேடு"
+ "காட்டுவதற்கான தொடர்புகள்"
+ "காட்டுவதற்கான தொடர்புகள்"
+ "தனிப்பயன் காட்சியை வரையறுக்கவும்"
+ "தொடர்புகளைக் கண்டறி"
+ "பிடித்தவை"
+ "தொடர்புகள் இல்லை."
+ "தெரியும் தொடர்புகள் எதுவுமில்லை."
+ "பிடித்தவை எதுவும் இல்லை."
+ "%s இல் தொடர்புகள் இல்லை"
+ "அடிக்கடி தொடர்புகொண்டவர்களை அழி"
+ "சிம் ஐத் தேர்ந்தெடுக்கவும்"
+ "கணக்குகள்"
+ "இறக்கு/ஏற்று"
+ "%1$s வழியாக"
+ "%2$s வழியாக %1$s "
+ "தேடுவதை நிறுத்து"
+ "தேடலை அழி"
+ "தொடர்பின் காட்சி விருப்பத்தேர்வு"
+ "கணக்கு"
+ "அழைப்புகளுக்கு எப்போதும் இதைப் பயன்படுத்து"
+ "இதன் மூலம் அழை"
+ "குறிப்புடன் அழைக்கவும்"
+ "அழைப்புடன் சேர்த்து அனுப்ப, குறிப்பை உள்ளிடவும்..."
+ "அனுப்பு & அழை"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-te-rIN/strings.xml b/ContactsCommon/res/values-te-rIN/strings.xml
new file mode 100644
index 0000000..c1c8648
--- /dev/null
+++ b/ContactsCommon/res/values-te-rIN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "వచనం కాపీ చేయబడింది"
+ "క్లిప్బోర్డ్కు కాపీ చేయి"
+ "%s కు కాల్ చేయండి"
+ "ఇంటికి కాల్ చేయండి"
+ "మొబైల్కు కాల్ చేయండి"
+ "కార్యాలయానికి కాల్ చేయండి"
+ "కార్యాలయ ఫ్యాక్స్కు కాల్ చేయండి"
+ "ఇంటి ఫ్యాక్స్కు కాల్ చేయండి"
+ "పేజర్కు కాల్ చేయండి"
+ "కాల్ చేయండి"
+ "కాల్బ్యాక్కు కాల్ చేయండి"
+ "కారుకు కాల్ చేయండి"
+ "కంపెనీ ప్రధాన నంబర్కు కాల్ చేయండి"
+ "ISDNకి కాల్ చేయండి"
+ "ప్రధాన నంబర్కు కాల్ చేయండి"
+ "ఫ్యాక్స్కు కాల్ చేయండి"
+ "రేడియోకు కాల్ చేయండి"
+ "టెలెక్స్కు కాల్ చేయండి"
+ "TTY/TDDకి కాల్ చేయండి"
+ "కార్యాలయ మొబైల్కు కాల్ చేయండి"
+ "కార్యాలయ పేజర్కు కాల్ చేయండి"
+ "%s కు కాల్ చేయండి"
+ "MMSకు కాల్ చేయండి"
+ "%s కు వచనం పంపండి"
+ "ఇంటికి వచనం పంపండి"
+ "మొబైల్కు వచనం పంపండి"
+ "కార్యాలయానికి వచనం పంపండి"
+ "కార్యాలయం ఫ్యాక్స్కు వచనం పంపండి"
+ "ఇంటి ఫ్యాక్స్కు వచనం పంపండి"
+ "పేజర్కు వచనం పంపండి"
+ "వచనం పంపండి"
+ "కాల్బ్యాక్కు వచనం పంపండి"
+ "కారుకు వచనం పంపండి"
+ "కంపెనీ ప్రధాన నంబర్కు వచనం పంపండి"
+ "ISDNకి వచనం పంపండి"
+ "ప్రధాన నంబర్కు వచనం పంపండి"
+ "ఫ్యాక్స్కు వచనం పంపండి"
+ "రేడియోకు వచనం పంపండి"
+ "టెలెక్స్కు వచనం పంపండి"
+ "TTY/TDDకి వచనం పంపండి"
+ "కార్యాలయ మొబైల్కు వచనం పంపండి"
+ "కార్యాలయ పేజర్కు వచనం పంపండి"
+ "%s కు వచనం పంపండి"
+ "MMSకు వచనం పంపండి"
+ "వీడియో కాల్ చేయి"
+ "తరచుగా సంప్రదించినవాటిని క్లియర్ చేయాలా?"
+ "మీరు పరిచయాలు మరియు ఫోన్ అనువర్తనాల్లో తరచుగా సంప్రదించిన పరిచయాల జాబితాను తీసివేస్తారు మరియు స్క్రాచ్ నుండి మీ చిరునామా ప్రాధాన్యతలను తెలుసుకునేలా ఇమెయిల్ అనువర్తనాలను నిర్బంధిస్తారు."
+ "తరచుగా సంప్రదించినవాటిని క్లియర్ చేస్తోంది…"
+ "అందుబాటులో ఉన్నారు"
+ "దూరంగా ఉన్నారు"
+ "బిజీగా ఉన్నారు"
+ "పరిచయాలు"
+ "ఇతరం"
+ "డైరెక్టరీ"
+ "అన్ని పరిచయాలు"
+ "నేను"
+ "శోధిస్తోంది..."
+ "%d కంటే ఎక్కువ కనుగొనబడ్డాయి."
+ "పరిచయాలు లేవు"
+
+ %d కనుగొనబడ్డాయి
+ - 1 కనుగొనబడింది
+
+ "%1$s కోసం శీఘ్ర సంప్రదింపు"
+ "(పేరు లేదు)"
+ "తరచుగా కాల్ చేయబడినవి"
+ "తరచుగా సంప్రదించబడినవి"
+ "పరిచయాన్ని వీక్షించండి"
+ "ఫోన్ నంబర్లు గల అన్ని పరిచయాలు"
+ "నవీకరణలను వీక్షించండి"
+ "ఫోన్-మాత్రమే, సమకాలీకరించబడలేదు"
+ "పేరు"
+ "మారుపేరు"
+ "పేరు"
+ "మొదటి పేరు"
+ "చివరి పేరు"
+ "పేరు ఆదిప్రత్యయం"
+ "మధ్య పేరు"
+ "పేరు అంత్యప్రత్యయం"
+ "ఫోనెటిక్ పేరు"
+ "ఫొనెటిక్ మొదటి పేరు"
+ "ఫోనెటిక్ మధ్య పేరు"
+ "ఫొనెటిక్ చివరి పేరు"
+ "ఫోన్"
+ "ఇమెయిల్"
+ "చిరునామా"
+ "IM"
+ "సంస్థ"
+ "సంబంధం"
+ "ప్రత్యేక తేదీలు"
+ "వచన సందేశం"
+ "చిరునామా"
+ "కంపెనీ"
+ "శీర్షిక"
+ "గమనికలు"
+ "SIP"
+ "వెబ్సైట్"
+ "సమూహాలు"
+ "ఇంటికి ఇమెయిల్ చేయండి"
+ "మొబైల్కు ఇమెయిల్ చేయండి"
+ "కార్యాలయానికి ఇమెయిల్ చేయండి"
+ "ఇమెయిల్ చేయండి"
+ "%s కు ఇమెయిల్ చేయండి"
+ "ఇమెయిల్ చేయండి"
+ "వీధి"
+ "PO పెట్టె"
+ "పరిసరాలు"
+ "నగరం"
+ "రాష్ట్రం"
+ "జిప్ కోడ్"
+ "దేశం"
+ "ఇంటి చిరునామాను వీక్షించండి"
+ "కార్యాలయ చిరునామాను వీక్షించండి"
+ "చిరునామాను వీక్షించండి"
+ "%s చిరునామాను వీక్షించండి"
+ "AIMని ఉపయోగించి చాట్ చేయండి"
+ "Windows Liveని ఉపయోగించి చాట్ చేయండి"
+ "Yahooని ఉపయోగించి చాట్ చేయండి"
+ "Skypeని ఉపయోగించి చాట్ చేయండి"
+ "QQని ఉపయోగించి చాట్ చేయండి"
+ "Google Talkని ఉపయోగించి చాట్ చేయండి"
+ "ICQని ఉపయోగించి చాట్ చేయండి"
+ "Jabberని ఉపయోగించి చాట్ చేయండి"
+ "చాట్ చేయండి"
+ "తొలగించు"
+ "పేరు ఫీల్డ్లను విస్తరింపజేయి లేదా కుదించు"
+ "అన్ని పరిచయాలు"
+ "నక్షత్రం గుర్తు ఉన్నవి"
+ "అనుకూలీకరించండి"
+ "పరిచయం"
+ "అన్ని ఇతర పరిచయాలు"
+ "అన్ని పరిచయాలు"
+ "సమకాలీకరణ సమూహాన్ని తీసివేయి"
+ "సమకాలీకరణ సమూహాన్ని జోడించండి"
+ "మరిన్ని సమూహాలు…"
+ "సమకాలీకరణ నుండి \"%s \"ని తీసివేయడం వలన సమకాలీకరణ నుండి సమూహం చేయబడని పరిచయాలు కూడా తీసివేయబడతాయి."
+ "ప్రదర్శన ఎంపికలను సేవ్ చేస్తోంది…"
+ "పూర్తయింది"
+ "రద్దు చేయి"
+ "%s లో పరిచయాలు"
+ "అనుకూల వీక్షణలో పరిచయాలు"
+ "ఒక పరిచయం"
+ "ఖాతాలో పరిచయాన్ని సృష్టించండి"
+ "సిమ్ కార్డు నుండి దిగుమతి చేయండి"
+ "^1 - ^2 SIM నుండి దిగుమతి చేయండి"
+ "%1$s SIM నుండి దిగుమతి చేయండి"
+ ".vcf ఫైల్ నుండి దిగుమతి చేయి"
+ "%s యొక్క దిగుమతిని రద్దు చేయాలా?"
+ "%s యొక్క ఎగుమతిని రద్దు చేయాలా?"
+ "vCard దిగుమతి/ఎగుమతిని రద్దు చేయడం సాధ్యపడలేదు"
+ "తెలియని లోపం."
+ "\"%s \"ని తెరవడం సాధ్యపడలేదు: %s ."
+ "ఎక్స్పోర్టర్ను ప్రారంభించడం సాధ్యపడలేదు: \"%s \"."
+ "ఎగమతి చేయగల పరిచయం లేదు."
+ "మీరు అవసరమయ్యే అనుమతిని నిలిపివేసారు."
+ "ఎగుమతి సమయంలో లోపం సంభవించింది: \"%s \"."
+ "అవసరమైన ఫైల్ పేరు (\"%s \") చాలా పెద్దదిగా ఉంది."
+ "SD కార్డులో చాలా ఎక్కువ vCard ఫైల్లు ఉన్నాయి."
+ "I/O లోపం"
+ "తగినంత మెమరీ లేదు. ఫైల్ చాలా పెద్దదిగా ఉండవచ్చు."
+ "ఊహించని కారణంగా vCardను అన్వయించడం సాధ్యపడలేదు."
+ "ఆకృతికి మద్దతు లేదు."
+ "అందించిన vCard ఫైల్(లు) యొక్క మెటా డేటా సమాచారాన్ని సేకరించడం సాధ్యపడలేదు."
+ "ఒకటి లేదా అంతకంటే ఎక్కువ ఫైల్లను (%s) దిగుమతి చేయడం సాధ్యపడలేదు."
+ "%s ని ఎగుమతి చేయడం పూర్తయింది."
+ "పరిచయాలను ఎగుమతి చేయడం పూర్తయింది."
+ "%s ని ఎగుమతి చేయడం రద్దు చేయబడింది."
+ "పరిచయ డేటాను ఎగుమతి చేస్తోంది"
+ "మీ పరిచయ డేటా దీనికి ఎగుమతి చేయబడుతోంది: %s ."
+ "డేటాబేస్ సమాచారాన్ని పొందడం సాధ్యపడలేదు."
+ "ఎగుమతి చేయదగిన పరిచయాలు ఏవీ లేవు. మీరు మీ ఫోన్లో పరిచయాలు కలిగి ఉన్నప్పటికీ, కొందరు డేటా ప్రదాతలు పరిచయాలను ఫోన్ నుండి ఎగుమతి చేయడానికి అనుమతించకపోవచ్చు."
+ "vCard కంపోజర్ సరిగ్గా ప్రారంభించబడలేదు."
+ "ఎగుమతి చేయడం సాధ్యపడలేదు"
+ "పరిచయ డేటా ఎగుమతి చేయబడలేదు.\nకారణం: \"%s \""
+ "%s ని దిగుమతి చేస్తోంది"
+ "vCard డేటాను చదవడం సాధ్యపడలేదు"
+ "vCard డేటాను చదవడం రద్దయింది"
+ "vCard %s ని దిగుమతి చేయడం పూర్తయింది"
+ "%s ని దిగుమతి చేయడం రద్దయింది"
+ "%s కొద్దిసేపట్లో దిగుమతి చేయబడుతుంది."
+ "ఫైల్ కొద్దిసేపట్లో దిగుమతి చేయబడుతుంది."
+ "vCard దిగుమతి అభ్యర్థన తిరస్కరించబడింది. తర్వాత మళ్లీ ప్రయత్నించండి."
+ "%s కొద్దిసేపట్లో ఎగుమతి చేయబడుతుంది."
+ "ఫైల్ కాసేపట్లో ఎగుమతి చేయబడుతుంది."
+ "vCard ఎగుమతి అభ్యర్థన తిరస్కరించబడింది. తర్వాత మళ్లీ ప్రయత్నించండి."
+ "పరిచయం"
+ "vCard(ల)ను స్థానిక తాత్కాలిక నిల్వకు కాష్ చేస్తోంది. అసలు దిగుమతి కొద్దిసేపట్లో ప్రారంభమవుతుంది."
+ "vCardని దిగుమతి చేయడం సాధ్యపడలేదు."
+ "SD కార్డులో vCard ఫైల్ ఏదీ కనుగొనబడలేదు."
+ "పరిచయం NFC ద్వారా స్వీకరించబడింది"
+ "పరిచయాలను ఎగుమతి చేయాలా?"
+ "కాష్ చేస్తోంది"
+ "SD కార్డును స్కాన్ చేయడం సాధ్యపడలేదు. (కారణం: \"%s \")"
+ "%s లో %s దిగుమతి చేయబడుతోంది: %s "
+ ".vcf ఫైల్కు ఎగుమతి చేయి"
+ "ఇలా క్రమబద్ధీకరించు"
+ "మొదటి పేరు"
+ "చివరి పేరు"
+ "పేరు ఆకృతి"
+ "ముందుగా మొదటి పేరు"
+ "ముందుగా చివరి పేరు"
+ "కనిపించే పరిచయాలను భాగస్వామ్యం చేయండి"
+ "కనిపించే పరిచయాలను భాగస్వామ్యం చేయడం విఫలమైంది."
+ "పరిచయాలను దిగుమతి/ఎగుమతి చేయండి"
+ "పరిచయాలను దిగుమతి చేయండి"
+ "ఈ పరిచయాన్ని భాగస్వామ్యం చేయడం సాధ్యపడదు."
+ "శోధించు"
+ "ప్రదర్శించాల్సిన పరిచయాలు"
+ "ప్రదర్శించాల్సిన పరిచయాలు"
+ "అనుకూల వీక్షణ నిర్వచనం"
+ "పరిచయాలను కనుగొనండి"
+ "ఇష్టమైనవి"
+ "పరిచయాలు లేవు."
+ "కనిపించే పరిచయాలు ఏవీ లేవు."
+ "ఇష్టమైనవి లేవు."
+ "%s లో పరిచయాలు లేవు"
+ "తరచుగా ఉన్నవాటిని క్లియర్ చేయి"
+ "సిమ్ కార్డును ఎంచుకోండి"
+ "ఖాతాలు"
+ "దిగుమతి చేయి/ఎగుమతి చేయి"
+ "%1$s ద్వారా"
+ "%2$s ద్వారా %1$s "
+ "శోధించడం ఆపివేయి"
+ "శోధనను క్లియర్ చేయండి"
+ "పరిచయ ప్రదర్శన ఎంపికలు"
+ "ఖాతా"
+ "కాల్ల కోసం ఎల్లప్పుడూ దీన్ని ఉపయోగించు"
+ "దీనితో కాల్ చేయండి"
+ "గమనికతో కాల్ చేయి"
+ "కాల్తో పాటు పంపడానికి గమనికను టైప్ చేయండి ..."
+ "పంపు & కాల్ చేయి"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-th/strings.xml b/ContactsCommon/res/values-th/strings.xml
new file mode 100644
index 0000000..921053f
--- /dev/null
+++ b/ContactsCommon/res/values-th/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "คัดลอกข้อความแล้ว"
+ "คัดลอกไปยังคลิปบอร์ด"
+ "โทรหา %s "
+ "โทรเข้าบ้าน"
+ "โทรเข้ามือถือ"
+ "โทรหาที่ทำงาน"
+ "โทรหาแฟกซ์ที่ทำงาน"
+ "โทรเข้าหมายเลขแฟกซ์ที่บ้าน"
+ "โทรหาเพจเจอร์"
+ "โทร"
+ "โทรหาหมายเลขติดต่อกลับ"
+ "โทรเข้าโทรศัพท์ในรถ"
+ "โทรหาโทรศัพท์หลักของบริษัท"
+ "โทรหา ISDN"
+ "โทรเข้าโทรศัพท์หลัก"
+ "โทรเข้าหมายเลขแฟกซ์"
+ "โทรเข้าวิทยุ"
+ "โทรเข้าหมายเลขเทเล็กซ์"
+ "โทรหา TTY/TDD"
+ "โทรเข้ามือถือที่ทำงาน"
+ "โทรเข้าเพจเจอร์ที่ทำงาน"
+ "โทรหา %s "
+ "โทรหา MMS"
+ "ส่งข้อความถึง %s "
+ "ส่งข้อความเข้าโทรศัพท์บ้าน"
+ "ส่งข้อความเข้ามือถือ"
+ "ส่งข้อความถึงโทรศัพท์ที่ทำงาน"
+ "ส่งข้อความถึงแฟกซ์ที่ทำงาน"
+ "ส่งข้อความเข้าหมายเลขแฟกซ์บ้าน"
+ "ส่งข้อความถึงเพจเจอร์"
+ "ข้อความ"
+ "ส่งข้อความถึงหมายเลขติดต่อกลับ"
+ "ส่งข้อความถึงรถยนต์"
+ "ส่งข้อความเข้าโทรศัพท์หลักของบริษัท"
+ "ส่งข้อความถึง ISDN"
+ "ส่งข้อความถึงโทรศัพท์หลัก"
+ "ส่งข้อความถึงหมายเลขแฟกซ์"
+ "ส่งข้อความถึงวิทยุ"
+ "ส่งข้อความถึงเทเล็กซ์"
+ "ส่งข้อความถึง TTY/TDD"
+ "ส่งข้อความถึงโทรศัพท์มือถือที่ทำงาน"
+ "ส่งข้อความเข้าเพจเจอร์ที่ทำงาน"
+ "ส่งข้อความถึง %s "
+ "ส่งข้อความถึง MMS"
+ "ใช้แฮงเอาท์วิดีโอ"
+ "ล้างรายชื่อที่ติดต่อบ่อยไหม"
+ "คุณจะล้างรายชื่อของผู้ที่ติดต่อด้วยบ่อยๆ ในแอปพลิเคชัน Contacts และ Phone และบังคับให้แอปพลิเคชันอีเมลเรียนรู้ค่ากำหนดที่อยู่ของคุณใหม่ตั้งแต่ต้น"
+ "กำลังล้างรายชื่อที่ติดต่อบ่อย…"
+ "พร้อมใช้งาน"
+ "ไม่อยู่"
+ "ไม่ว่าง"
+ "สมุดโทรศัพท์"
+ "อื่นๆ"
+ "ไดเรกทอรี"
+ "รายชื่อติดต่อทั้งหมด"
+ "ฉัน"
+ "กำลังค้นหา…"
+ "พบมากกว่า %d รายการ"
+ "ไม่มีรายชื่อติดต่อ"
+
+ - พบ
%d รายการ
+ - พบ 1 รายการ
+
+ "สมุดโทรศัพท์ด่วนสำหรับ %1$s "
+ "(ไม่มีชื่อ)"
+ "โทรติดต่อบ่อย"
+ "ติดต่อบ่อยครั้ง"
+ "ดูรายชื่อติดต่อ"
+ "รายชื่อติดต่อทั้งหมดที่มีหมายเลขโทรศัพท์"
+ "ดูการอัปเดต"
+ "โทรศัพท์เท่านั้น ไม่ซิงค์"
+ "ชื่อ"
+ "ชื่อเล่น"
+ "ชื่อ"
+ "ชื่อ"
+ "นามสกุล"
+ "คำนำหน้าชื่อ"
+ "ชื่อกลาง"
+ "คำต่อท้ายชื่อ"
+ "คำอ่านชื่อ"
+ "การออกเสียงชื่อ"
+ "ชื่อกลางแบบออกเสียง"
+ "การออกเสียงนามสกุล"
+ "โทรศัพท์"
+ "อีเมล"
+ "ที่อยู่"
+ "IM"
+ "องค์กร"
+ "ความเกี่ยวข้อง"
+ "วันพิเศษ"
+ "ข้อความ"
+ "ที่อยู่"
+ "บริษัท"
+ "ชื่อ"
+ "หมายเหตุ"
+ "SIP"
+ "เว็บไซต์"
+ "กลุ่ม"
+ "ส่งไปที่อีเมลส่วนตัว"
+ "ส่งอีเมลเข้ามือถือ"
+ "ส่งอีเมลถึงที่ทำงาน"
+ "อีเมล"
+ "ส่งอีเมลถึง %s "
+ "อีเมล"
+ "ถนน"
+ "ตู้ ปณ."
+ "ย่านใกล้เคียง"
+ "เมือง"
+ "รัฐ"
+ "รหัสไปรษณีย์"
+ "ประเทศ"
+ "ดูที่อยู่บ้าน"
+ "ดูที่อยู่ที่ทำงาน"
+ "ดูที่อยู่"
+ "ดูที่อยู่ %s "
+ "แชทโดยใช้ AIM"
+ "แชทโดยใช้ Windows Live"
+ "แชทโดยใช้ Yahoo"
+ "แชทโดยใช้ Skype"
+ "แชทโดยใช้ QQ"
+ "แชทโดยใช้ Google Talk"
+ "แชทโดยใช้ ICQ"
+ "แชทโดยใช้ Jabber"
+ "แชท"
+ "ลบ"
+ "ขยายหรือยุบฟิลด์ชื่อ"
+ "รายชื่อติดต่อทั้งหมด"
+ "ที่ติดดาว"
+ "กำหนดค่า"
+ "รายชื่อติดต่อ"
+ "รายชื่อติดต่ออื่นทั้งหมด"
+ "รายชื่อติดต่อทั้งหมด"
+ "นำกลุ่มที่ซิงค์ออก"
+ "เพิ่มกลุ่มที่ซิงค์"
+ "กลุ่มเพิ่มเติม…"
+ "การนำ \"%s \" ออกจากการซิงค์จะนำรายชื่อติดต่อที่ไม่ได้จัดกลุ่มไว้ออกจากการซิงค์ด้วย"
+ "กำลังบันทึกตัวเลือกการแสดงผล..."
+ "เสร็จสิ้น"
+ "ยกเลิก"
+ "รายชื่อติดต่อใน %s "
+ "รายชื่อติดต่อในมุมมองที่กำหนดเอง"
+ "รายชื่อติดต่อเดียว"
+ "สร้างรายชื่อภายในบัญชี"
+ "นำเข้าจากซิมการ์ด"
+ "นำเข้าจากซิม ^1 - ^2 "
+ "นำเข้าจากซิม %1$s "
+ "นำเข้าจากไฟล์ .vcf"
+ "ยกเลิกการนำเข้า %s หรือไม่"
+ "ยกเลิกการส่งออก %s หรือไม่"
+ "ไม่สามารถยกเลิกการนำเข้า/ส่งออก vCard"
+ "ข้อผิดพลาดที่ไม่ทราบสาเหตุ"
+ "ไม่สามารถเปิด \"%s \": %s "
+ "เริ่มใช้งานโปรแกรมส่งออกไม่ได้: \"%s \""
+ "ไม่มีรายชื่อติดต่อที่สามารถส่งออกได้"
+ "คุณได้ปิดใช้สิทธิ์ที่จำเป็น"
+ "เกิดข้อผิดพลาดระหว่างส่งออก: \"%s \""
+ "ชื่อไฟล์ที่ต้องระบุยาวเกินไป (\"%s \")"
+ "มีไฟล์ vCard บนการ์ด SD มากเกินไป"
+ "ข้อผิดพลาด I/O"
+ "หน่วยความจำไม่เพียงพอ ไฟล์อาจใหญ่เกินไป"
+ "ไม่สามารถแยกวิเคราะห์ vCard ด้วยเหตุผลที่ไม่คาดคิด"
+ "ไม่สนับสนุนรูปแบบนี้"
+ "ไม่สามารถรวบรวมข้อมูลเมตาของ vCard ที่ระบุ"
+ "ไม่สามารถนำเข้าไฟล์ตั้งแต่หนึ่งไฟล์ขึ้นไป (%s)"
+ "ส่งออก %s เสร็จแล้ว"
+ "ส่งออกรายชื่อติดต่อเรียบร้อยแล้ว"
+ "ยกเลิกการส่งออก %s แล้ว"
+ "กำลังส่งออกข้อมูลสมุดโทรศัพท์"
+ "กำลังส่งออกข้อมูลรายชื่อติดต่อของคุณไปยัง: %s "
+ "ไม่สามารถดึงข้อมูลจากฐานข้อมูล"
+ "ไม่มีรายชื่อติดต่อที่สามารถส่งออกได้ หากคุณมีรายชื่อติดต่ออยู่ในโทรศัพท์ของคุณจริงๆ อาจเป็นเพราะผู้ให้บริการข้อมูลบางรายไม่อนุญาตให้ส่งออกรายชื่อติดต่อจากโทรศัพท์"
+ "โปรแกรมเขียนข้อความ vCard เริ่มการทำงานไม่ถูกต้อง"
+ "ไม่สามารถส่งออก"
+ "ไม่ได้ส่งออกข้อมูลรายชื่อติดต่อ\nสาเหตุ: \"%s \""
+ "กำลังนำเข้า %s "
+ "ไม่สามารถอ่านข้อมูล vCard"
+ "ยกเลิกการอ่านข้อมูล vCard แล้ว"
+ "นำเข้า vCard %s เรียบร้อยแล้ว"
+ "ยกเลิกการนำเข้า %s แล้ว"
+ "การนำเข้า %s จะเกิดขึ้นในไม่ช้า"
+ "ไฟล์จะถูกนำเข้าในไม่ช้า"
+ "คำขอนำเข้า vCard ถูกปฏิเสธ ลองใหม่ภายหลัง"
+ "การส่งออก %s จะเกิดขึ้นในไม่ช้า"
+ "ระบบจะส่งออกไฟล์ในอีกสักครู่"
+ "คำขอส่งออก vCard ถูกปฏิเสธ ลองใหม่ภายหลัง"
+ "รายชื่อติดต่อ"
+ "กำลังแคช vCard ไปยังที่จัดเก็บข้อมูลชั่วคราวในตัวเครื่อง การนำเข้าจริงจะเริ่มต้นในอีกสักครู่"
+ "ไม่สามารถนำเข้า vCard"
+ "ไม่พบไฟล์ vCard บนการ์ด SD"
+ "รับรายชื่อผ่าน NFC แล้ว"
+ "ส่งออกรายชื่อติดต่อ"
+ "กำลังแคช"
+ "ไม่สามารถสแกนการ์ด SD (สาเหตุ: \"%s \")"
+ "กำลังนำเข้า %s /%s : %s "
+ "ส่งออกเป็นไฟล์ .vcf"
+ "จัดเรียงตาม"
+ "ชื่อ"
+ "นามสกุล"
+ "รูปแบบชื่อ"
+ "ชื่อขึ้นก่อน"
+ "นามสกุลขึ้นก่อน"
+ "แชร์รายชื่อติดต่อที่มองเห็น"
+ "ไม่สามารถแชร์รายชื่อติดต่อที่มองเห็นได้"
+ "นำเข้า/ส่งออกรายชื่อติดต่อ"
+ "นำเข้ารายชื่อติดต่อ"
+ "ไม่สามารถแชร์รายชื่อติดต่อนี้ได้"
+ "ค้นหา"
+ "รายชื่อติดต่อที่จะแสดง"
+ "รายชื่อติดต่อที่จะแสดง"
+ "กำหนดมุมมองที่กำหนดเอง"
+ "ค้นหารายชื่อติดต่อ"
+ "รายการโปรด"
+ "ไม่มีรายชื่อติดต่อ"
+ "ไม่มีรายชื่อติดต่อที่แสดงไว้"
+ "ไม่มีรายการโปรด"
+ "ไม่มีรายชื่อติดต่อใน %s "
+ "ล้างรายชื่อที่ติดต่อบ่อย"
+ "เลือกซิมการ์ด"
+ "บัญชี"
+ "นำเข้า/ส่งออก"
+ "ผ่านทาง %1$s "
+ "%1$s ผ่านทาง %2$s "
+ "หยุดการค้นหา"
+ "ล้างการค้นหา"
+ "ตัวเลือกการแสดงรายชื่อผู้ติดต่อ"
+ "บัญชี"
+ "ใช้ในการโทรทุกครั้ง"
+ "โทรด้วย"
+ "โทรพร้อมโน้ต"
+ "พิมพ์โน้ตเพื่อส่งพร้อมการโทร ..."
+ "ส่งและโทร"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-tl/strings.xml b/ContactsCommon/res/values-tl/strings.xml
new file mode 100644
index 0000000..377c4d4
--- /dev/null
+++ b/ContactsCommon/res/values-tl/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Kinopya ang teksto"
+ "Kopyahin sa clipboard"
+ "Tawagan sa %s "
+ "Tawagan sa bahay"
+ "Tawagan sa mobile"
+ "Tawagan sa trabaho"
+ "Tawagan sa fax sa trabaho"
+ "Tawagan sa fax sa bahay"
+ "Tawagan sa pager"
+ "Tawagan"
+ "Tawagan sa callback"
+ "Tawagan sa kotse"
+ "Tawagan sa pangunahing kumpanya"
+ "Tawagan sa ISDN"
+ "Tawagan sa pangunahin"
+ "Tawagan sa fax"
+ "Tawagan sa radyo"
+ "Tawagan sa telex"
+ "Tawagan sa TTY/TDD"
+ "Tawagan sa mobile sa trabaho"
+ "Tawagan sa pager sa trabaho"
+ "Tawagan sa %s "
+ "Tawagan sa MMS"
+ "I-text sa %s "
+ "I-text sa bahay"
+ "I-text sa mobile"
+ "I-text sa trabaho"
+ "I-text sa fax sa trabaho"
+ "I-text sa fax sa bahay"
+ "I-text sa pager"
+ "Mag-text"
+ "I-text sa callback"
+ "I-text sa kotse"
+ "I-text sa pangunahing kumpanya"
+ "I-text sa ISDN"
+ "I-text sa pangunahin"
+ "I-text sa fax"
+ "I-text sa radyo"
+ "I-text sa telex"
+ "I-text sa TTY/TDD"
+ "I-text sa mobile sa trabaho"
+ "I-text sa pager sa trabaho"
+ "I-text sa %s "
+ "I-text sa MMS"
+ "Gumawa ng video call"
+ "I-clear ang madalas na kinontak"
+ "Iki-clear mo ang listahan ng mga madalas na nakakaugnay sa mga app ng Mga Contact at Telepono at pupuwersahin mo ang mga app ng email na matutunan ang iyong mga kagustuhan sa pag-a-address mula sa simula."
+ "Kini-clear madalas na inuugnayan…"
+ "Available"
+ "Wala"
+ "Abala"
+ "Mga Contact"
+ "Iba pa"
+ "Direktoryo"
+ "Lahat ng contact"
+ "Ako"
+ "Naghahanap…"
+ "Higit sa %d ang nakita."
+ "Walang mga contact"
+
+ %d ang nahanap
+ %d ang nahanap
+
+ "Mabilisang contact para kay %1$s "
+ "(Walang pangalan)"
+ "Madalas na tinatawagan"
+ "Madalas na kino-contact"
+ "Tingnan ang contact"
+ "Lahat ng contact na may mga numero ng telepono"
+ "Tingnan ang mga update"
+ "Telepono lang, hindi naka-sync"
+ "Pangalan"
+ "Palayaw"
+ "Pangalan"
+ "Pangalan"
+ "Apelyido"
+ "Prefix ng pangalan"
+ "Gitnang pangalan"
+ "Suffix ng pangalan"
+ "Phonetic na pangalan"
+ "Phonetic na pangalan"
+ "Phonetic na gitnang pangalan"
+ "Phonetic na apelyido"
+ "Telepono"
+ "Email"
+ "Address"
+ "IM"
+ "Samahan"
+ "Kaugnayan"
+ "Mga espesyal na petsa"
+ "Text message"
+ "Address"
+ "Kumpanya"
+ "Pamagat"
+ "Mga Tala"
+ "SIP"
+ "Website"
+ "Mga Pangkat"
+ "Mag-email sa bahay"
+ "Mag-email sa mobile"
+ "Mag-email sa trabaho"
+ "Mag-email"
+ "Mag-email sa %s "
+ "Mag-email"
+ "Kalye"
+ "PO box"
+ "Kapitbahayan"
+ "Lungsod"
+ "Estado"
+ "ZIP code"
+ "Bansa"
+ "Tingnan ang address ng tahanan"
+ "Tingnan ang address sa trabaho"
+ "Tingnan ang address"
+ "Tingnan ang %s na address"
+ "Makipag-chat gamit ang AIM"
+ "Makipag-chat gamit ang Windows Live"
+ "Makipag-chat gamit ang Yahoo"
+ "Makipag-chat gamit ang Skype"
+ "Makipag-chat gamit ang QQ"
+ "Makipag-chat gamit ang Google Talk"
+ "Makipag-chat gamit ang ICQ"
+ "Makipag-chat gamit ang Jabber"
+ "Chat"
+ "tanggalin"
+ "Palawakin o tiklupin ang mga field ng pangalan"
+ "Lahat ng contact"
+ "Naka-star"
+ "I-customize"
+ "Contact"
+ "Lahat ng iba pang contact"
+ "Lahat ng contact"
+ "Alisin ang pangkat sa pag-sync"
+ "Magdagdag ng pangkat sa pag-sync"
+ "Higit pang mga pangkat…"
+ "Aalisin din ng pag-alis sa \"%s \" mula sa sync ang anumang mga hindi nakapangkat na contact mula sa sync."
+ "Sine-save ang mga pagpipilian sa pagpapakita…"
+ "Tapos na"
+ "Kanselahin"
+ "Mga contact sa %s "
+ "Mga contact sa custom na view"
+ "Iisang contact"
+ "Lumikha ng contact sa ilalim ng account"
+ "I-import mula sa SIM card"
+ "I-import mula sa SIM ^1 - ^2 "
+ "I-import mula sa SIM %1$s "
+ "Mag-import mula sa .vcf file"
+ "Kanselahin ang pag-import ng %s ?"
+ "Kanselahin ang pag-export ng %s ?"
+ "Di makansela pag-import/pag-export vCard"
+ "Hindi alam na error."
+ "Hindi mabuksan ang \"%s \": %s ."
+ "Hindi masimulan ang exporter: \"%s \"."
+ "Walang na-e-export na contact."
+ "Na-disable mo ang isang kinakailangang pahintulot."
+ "May naganap na error habang nag-e-export: \"%s \"."
+ "Masyadong mahaba ang kinakailangang filename (\"%s \")."
+ "Masyadong maraming vCard file ang nasa SD card."
+ "I/O na error"
+ "Walang sapat na memory. Maaaring masyadong malaki ang file."
+ "Hindi ma-parse ang vCard dahil sa isang hindi inaasahang dahilan."
+ "Hindi sinusuportahan ang format."
+ "Hindi makakolekta ng impormasyon ng meta ng ibinigay na (mga) vCard file."
+ "Hindi ma-import ang isa o higit pang mga file (%s)."
+ "Tapos na ang pag-export ng %s ."
+ "Tapos nang i-export ang mga contact."
+ "Kinansela ang pag-export ng %s ."
+ "Ine-export ang data ng contact"
+ "Ine-export ang data ng iyong contact sa: %s ."
+ "Hindi makuha ang impormasyon ng database."
+ "Walang mga na-e-export na contact. Kung mayroon kang mga contact sa iyong telepono, maaaring hindi payagan ng ilang provider ng data na ma-export mula sa telepono ang mga contact."
+ "Hindi nagsimula nang maayos ang composer ng vCard."
+ "Hindi ma-export"
+ "Hindi na-export ang data ng contact.\nDahilan: \"%s \""
+ "Ini-import ang %s "
+ "Hindi mabasa ang data ng vCard"
+ "Kinansela ang pagbabasa ng data ng vCard"
+ "Tapos na ang pag-import ng vCard na %s "
+ "Kinansela ang pag-import ng %s "
+ "Ii-import ang %s sa ilang sandali."
+ "Ii-import ang file sa ilang sandali."
+ "Tinanggihan ang kahilingan sa pag-import ng vCard. Subukang muli sa ibang pagkakataon."
+ "I-e-export ang %s sa ilang sandali."
+ "Ie-export ang file sa ilang sandali."
+ "Tinanggihan ang kahilingan sa pag-export ng vCard. Subukang muli sa ibang pagkakataon."
+ "contact"
+ "Kina-cache ang (mga) vCard sa lokal na pansamantalang storage. Magsisimula sa lalong madaling panahon ang aktwal na pag-import."
+ "Hindi ma-import ang vCard."
+ "Walang nahanap na vCard file sa SD card."
+ "Natanggap contact sa NFC"
+ "I-export ang mga contact?"
+ "Nagka-cache"
+ "Hindi ma-scan ang SD card. (Dahilan: \"%s \")"
+ "Nag-i-import ng %s /%s : %s "
+ "I-export sa .vcf file"
+ "Uriin ayon sa"
+ "Pangalan"
+ "Apelyido"
+ "Format ng pangalan"
+ "Pangalan muna"
+ "Apelyido muna"
+ "Magbahagi ng mga nakikitang contact"
+ "Hindi naibahagi ang mga nakikitang contact."
+ "Mag-import/mag-export ng mga contact"
+ "Mag-import ng mga contact"
+ "Hindi maibabahagi ang contact na ito."
+ "Hanapin"
+ "Mga contact na ipapakita"
+ "Mga contact na ipapakita"
+ "Tukuyin ang custom view"
+ "Maghanap ng mga contact"
+ "Mga Paborito"
+ "Walang mga contact."
+ "Walang mga nakikitang contact."
+ "Walang mga paborito."
+ "Walang mga contact sa %s "
+ "I-clear ang mga frequent"
+ "Pumili ng SIM card"
+ "Mga Account"
+ "Mag-import/mag-export"
+ "sa pamamagitan ng %1$s "
+ "%1$s sa pamamagitan ng %2$s "
+ "ihinto ang paghahanap"
+ "I-clear ang paghahanap"
+ "Mga opsyon ng display ng contact"
+ "Account"
+ "Gamitin ito palagi sa mga tawag"
+ "Tumawag gamit ang"
+ "Tumawag nang may kasamang tala"
+ "Mag-type ng isang tala na ipadadala kasama ng tawag ..."
+ "IPADALA & TUMAWAG"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-tr/strings.xml b/ContactsCommon/res/values-tr/strings.xml
new file mode 100644
index 0000000..ba3fafd
--- /dev/null
+++ b/ContactsCommon/res/values-tr/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Metin kopyalandı"
+ "Panoya kopyala"
+ "Sesli arama yap (%s )"
+ "Sesli arama yap (ev)"
+ "Sesli arama yap (mobil)"
+ "Sesli arama yap (iş)"
+ "Sesli arama yap (iş faksı)"
+ "Sesli arama yap (ev faksı)"
+ "Sesli arama yap (çağrı cihazı)"
+ "Sesli arama yap"
+ "Sesli arama yap (geri arama)"
+ "Sesli arama yap (araç)"
+ "Sesli arama yap (şirket santrali)"
+ "Sesli arama yap (ISDN)"
+ "Sesli arama yap (santral)"
+ "Sesli arama yap (faks)"
+ "Sesli arama yap (telsiz)"
+ "Sesli arama yap (teleks)"
+ "Sesli arama yap (TTY/TDD)"
+ "Sesli arama yap (iş cep telefonu)"
+ "Sesli arama yap (iş çağrı cihazı)"
+ "Sesli arama yap (%s )"
+ "Sesli arama yap (MMS)"
+ "SMS gönder (%s )"
+ "SMS gönder (ev)"
+ "SMS gönder (mobil)"
+ "SMS gönder (iş)"
+ "SMS gönder (iş faksı)"
+ "SMS gönder (ev faksı)"
+ "SMS gönder (çağrı cihazı)"
+ "SMS gönder"
+ "SMS gönder (geri arama)"
+ "SMS gönder (araç)"
+ "SMS gönder (şirket santrali)"
+ "SMS gönder (ISDN)"
+ "SMS gönder (santral)"
+ "SMS gönder (faks)"
+ "SMS gönder (telsiz)"
+ "SMS gönder (teleks)"
+ "SMS gönder (TTY/TDD)"
+ "SMS gönder (iş cep telefonu)"
+ "SMS gönder (iş çağrı cihazı)"
+ "SMS gönder (%s )"
+ "SMS gönder (MMS)"
+ "Video görüşmesi yap"
+ "Sık iletişim kurulanlar silinsin mi?"
+ "Kişiler ve Telefon uygulamalarındaki sık iletişim kurulanlar listesini temizleyecek ve e-posta uygulamalarını adres tercihlerinizi en baştan öğrenmeye zorlayacaksınız."
+ "Sık iletişim kurulanlar siliniyor…"
+ "Müsait"
+ "Dışarıda"
+ "Meşgul"
+ "Kişiler"
+ "Diğer"
+ "Dizin"
+ "Tüm kişiler"
+ "Ben"
+ "Aranıyor…"
+ "%d kişiden fazla bulundu."
+ "Kişi yok"
+
+ %d kişi bulundu
+ - 1 kişi bulundu
+
+ "%1$s için hızlı kişi"
+ "(Adsız)"
+ "Sık arananlar"
+ "Sık iletişim kurulanlar"
+ "Kişiyi görüntüle"
+ "Telefon numarası olan tüm kişiler"
+ "Güncellemeleri görüntüle"
+ "Yalnızca telefonda saklanan, senkronize olmayan"
+ "Adı"
+ "Takma ad"
+ "Adı"
+ "Ad"
+ "Soyadı"
+ "Ad öneki"
+ "İkinci adı"
+ "Ad soneki"
+ "Fonetik adı"
+ "Fonetik ad"
+ "Fonetik ikinci adı"
+ "Fonetik soyadı"
+ "Telefon"
+ "E-posta"
+ "Adres"
+ "IM"
+ "Kuruluş"
+ "İlişki"
+ "Özel tarihler"
+ "Kısa mesaj"
+ "Adres"
+ "Şirket"
+ "Unvan"
+ "Notlar"
+ "SIP"
+ "Web sitesi"
+ "Gruplar"
+ "E-posta gönder (ev)"
+ "E-posta gönder (mobil)"
+ "E-posta gönder (iş)"
+ "E-posta gönder"
+ "E-posta gönder (%s )"
+ "E-posta gönder"
+ "Cadde"
+ "Posta kutusu"
+ "Mahalle"
+ "Şehir"
+ "Eyalet"
+ "Posta kodu"
+ "Ülke"
+ "Ev adresini görüntüle"
+ "İş adresini görüntüle"
+ "Adresi görüntüle"
+ "%s adresini görüntüle"
+ "AIM kullanarak sohbet et"
+ "Windows Live kullanarak sohbet et"
+ "Yahoo kullanarak sohbet et"
+ "Skype kullanarak sohbet et"
+ "QQ kullanarak sohbet et"
+ "Google Talk kullanarak sohbet et"
+ "ICQ kullanarak sohbet et"
+ "Jabber kullanarak sohbet et"
+ "Sohbet"
+ "sil"
+ "Ad alanlarını genişlet veya daralt"
+ "Tüm kişiler"
+ "Yıldız işaretli"
+ "Özelleştir"
+ "Kişi"
+ "Diğer tüm kişiler"
+ "Tüm kişiler"
+ "Senkronize grubu kaldır"
+ "Senkronizasyon grubu ekle"
+ "Diğer gruplar..."
+ "\"%s \" adlı grubu senkronizasyondan kaldırmak, gruplanmamış tüm kişilerin de senkronizasyondan kaldırılmasına neden olur."
+ "Görüntüleme seçenekleri kaydediliyor..."
+ "Tamam"
+ "İptal"
+ "%s hesabındaki kişiler"
+ "Özel görünümdeki kişiler"
+ "Tek kişi"
+ "Şu hesap altında kişi oluştur:"
+ "SIM karttan içe aktar"
+ "SIM\'den (^1 - ^2 ) içe aktar"
+ "SIM\'den (%1$s ) içe aktar"
+ ".vcf dosyasından aktar"
+ "%s dosyasının içe aktarılması iptal edilsin mi?"
+ "%s dosyasının dışa aktarılması iptal edilsin mi?"
+ "İçe/dışa aktarma işlemi iptal edilemedi"
+ "Bilinmeyen hata."
+ "\"%s \" açılamadı: %s "
+ "Dışa aktarıcı başlatılamadı: \"%s \""
+ "Dışa aktarabilecek kişi yok."
+ "Gerekli bir izni devre dışı bıraktınız."
+ "Dışa aktarma sırasında bir hata oluştu: \"%s \""
+ "Gereken dosya adı çok uzun (\"%s \")."
+ "SD kartta çok fazla vCard dosyası var."
+ "G/Ç Hatası"
+ "Bellek yetersiz. Dosya çok büyük olabilir."
+ "Beklenmeyen bir nedenden dolayı vCard ayrıştırılamadı."
+ "Biçim desteklenmiyor."
+ "Belirtilen vCard dosyalarının meta bilgileri toplanamadı."
+ "Bir veya daha fazla dosya içe aktarılamadı (%s)."
+ "%s dosyasını dışa aktarma tamamlandı."
+ "Kişileri dışa aktarma işlemi tamamlandı."
+ "%s dosyasını dışa aktarma iptal edildi."
+ "Kişi verileri dışa aktarılıyor"
+ "Kişi verileriniz şu dosyaya aktarılıyor: %s ."
+ "Veritabanı bilgileri alınamadı."
+ "Dışa aktarılabilecek kişi yok. Telefonunuzda kişileriniz varsa, bazı veri sağlayıcıları kişilerin telefondan dışa aktarılmasına izin vermeyebilir."
+ "vCard oluşturucu düzgün başlamadı."
+ "Dışa aktarılamadı"
+ "Kişi verileri dışa aktarılamadı.\nNedeni: \"%s \""
+ "%s içe aktarılıyor"
+ "vCard verileri okunamadı"
+ "vCard verilerini okuma işlemi iptal edildi"
+ "vCard %s dosyasının içe aktarılması tamamlandı"
+ "%s dosyasını içe aktarma iptal edildi"
+ "%s kısa bir süre içinde içe aktarılacak."
+ "Dosya kısa bir süre sonra içe aktarılacak."
+ "vCard\'ı içe aktarma isteği reddedildi. Daha sonra tekrar deneyin."
+ "%s kısa bir süre içinde dışa aktarılacak."
+ "Dosya kısa bir süre sonra dışa aktarılacak."
+ "vCard\'ı dışa aktarma isteği reddedildi. Daha sonra tekrar deneyin."
+ "kişi"
+ "vCard\'lar geçici bir yerel depolama alanında önbelleğe alınıyor. Asıl içe aktarma işlemi kısa süre içinde başlayacak."
+ "vCard içe aktarılamadı."
+ "SD kartta hiç vCard dosyası bulunamadı."
+ "Kişi NFC ile alındı"
+ "Kişiler dışa aktarılsın mı?"
+ "Önbelleğe alınıyor"
+ "SD kart taranamadı. (Nedeni: \"%s \")"
+ "İçe aktarılıyor %s /%s : %s "
+ ".vcf dosyasına aktar"
+ "Sıralama ölçütü"
+ "Ad"
+ "Soyadı"
+ "Ad biçimi"
+ "Önce ad"
+ "Önce soyadı"
+ "Görülebilir kişileri paylaş"
+ "Görünür kişiler paylaşılamadı."
+ "Kişileri içe/dışa aktar"
+ "Kişileri içe aktar"
+ "Bu kişi paylaşılamıyor."
+ "Ara"
+ "Görüntülenecek kişiler"
+ "Görüntülenecek kişiler"
+ "Özel görünüm tanımla"
+ "Kişileri bul"
+ "Favoriler"
+ "Kişi yok."
+ "Görülebilir kişi yok."
+ "Favori yok."
+ "%s içinde kişi yok"
+ "Sık iletişim kurulanları sil"
+ "SIM kart seç"
+ "Hesaplar"
+ "İçe/Dışa aktar"
+ "%1$s üzerinden"
+ "%2$s üzerinden şu zamanda: %1$s "
+ "aramayı durdur"
+ "Aramayı temizle"
+ "Kişi görüntüleme seçenekleri"
+ "Hesap"
+ "Çağrılar için her zaman bunu kullan"
+ "Çağrıyı şununla yap:"
+ "Notla telefon et"
+ "Çağrıyla göndermek için bir not yazın..."
+ "GÖNDER VE TELEFON ET"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-uk/strings.xml b/ContactsCommon/res/values-uk/strings.xml
new file mode 100644
index 0000000..05d1c32
--- /dev/null
+++ b/ContactsCommon/res/values-uk/strings.xml
@@ -0,0 +1,255 @@
+
+
+
+
+ "Текст скопійовано"
+ "Копіювати в буфер обміну"
+ "Набрати %s "
+ "Набрати домашній номер телефону"
+ "Набрати номер мобільного телефону"
+ "Набрати робочий номер телефону"
+ "Набрати робочий номер факсу"
+ "Набрати домашній номер факсу"
+ "Набрати номер пейджера"
+ "Набрати"
+ "Набрати номер зворотного виклику"
+ "Набрати номер в авто"
+ "Набрати основний робочий номер телефону"
+ "Набрати номер ISDN"
+ "Набрати основний номер телефону"
+ "Набрати номер факсу"
+ "Набрати номер радіотелефону"
+ "Набрати номер телексу"
+ "Набрати номер TTY/TDD"
+ "Набрати робочий номер мобільного телефону"
+ "Набрати робочий номер пейджера"
+ "Набрати %s "
+ "Набрати номер MMS"
+ "SMS на %s "
+ "SMS на домашній номер телефону"
+ "SMS на номер мобільного телефону"
+ "SMS на робочий номер телефону"
+ "SMS на робочий номер факсу"
+ "SMS на домашній номер факсу"
+ "SMS на номер пейджера"
+ "SMS"
+ "SMS на номер зворотного виклику"
+ "SMS на номер в авто"
+ "SMS на основний робочий номер телефону"
+ "SMS на номер ISDN"
+ "SMS на основний номер"
+ "SMS на номер факсу"
+ "SMS на номер радіотелефону"
+ "SMS на номер телексу"
+ "SMS на номер TTY/TDD"
+ "SMS на робочий номер мобільного телефону"
+ "SMS на робочий номер пейджера"
+ "SMS на %s "
+ "SMS на номер MMS"
+ "Здійснити відеодзвінок"
+ "Очистити список частих контактів?"
+ "Буде видалено список користувачів, з якими ви часто спілкуєтеся, з додатків Контакти та Телефон і скинуто налаштування адрес у додатках для електронної пошти."
+ "Очищення списку частих контактів…"
+ "На місці"
+ "Не на місці"
+ "Не турбувати"
+ "Контакти"
+ "Інше"
+ "Каталог"
+ "Усі контакти"
+ "Я"
+ "Пошук…"
+ "Знайдено понад %d ."
+ "Контактів немає"
+
+ - Знайдено
%d
+ - Знайдено
%d
+ - Знайдено
%d
+ - Знайдено
%d
+
+ "Швидкий зв’язок, контакт %1$s "
+ "(Без імені)"
+ "Часті виклики"
+ "Часті контакти"
+ "Переглянути контакт"
+ "Усі контакти з номерами телефону"
+ "Переглянути оновлення"
+ "Лише в телефоні, не синхронізується"
+ "Ім’я"
+ "Псевдонім"
+ "Ім’я"
+ "Ім’я"
+ "Прізвище"
+ "Префікс імені"
+ "По батькові"
+ "Суфікс імені"
+ "Вимова імені"
+ "Вимова імені"
+ "Вимова по батькові"
+ "Вимова прізвища"
+ "Телефон"
+ "Електронна адреса"
+ "Адреса"
+ "Чат"
+ "Організація"
+ "Зв’язки"
+ "Особливі дати"
+ "Текстове повідомлення"
+ "Адреса"
+ "Компанія"
+ "Назва"
+ "Примітки"
+ "SIP"
+ "Веб-сайт"
+ "Групи"
+ "Написати на домашню електронну адресу"
+ "Написати на мобільну електронну адресу"
+ "Написати на робочу електронну адресу"
+ "Написати"
+ "Написати на %s "
+ "Написати"
+ "Вулиця"
+ "Поштова скринька"
+ "У районі"
+ "Місто"
+ "Штат/регіон"
+ "Поштовий індекс"
+ "Країна"
+ "Переглянути домашню адресу"
+ "Переглянути робочу адресу"
+ "Переглянути адресу"
+ "Переглянути адресу %s "
+ "Чат через AIM"
+ "Чат через Windows Live"
+ "Чат через Yahoo"
+ "Чат через Skype"
+ "Чат через QQ"
+ "Чат через Google Talk"
+ "Чат через ICQ"
+ "Чат через Jabber"
+ "Чат"
+ "видалити"
+ "Розгорнути або згорнути поля імен"
+ "Усі контакти"
+ "Із зірочками"
+ "Спеціальний"
+ "Контакт"
+ "Усі інші контакти"
+ "Усі контакти"
+ "Вилучити синхронізовану групу"
+ "Додати синхронізовану групу"
+ "Інші групи…"
+ "Вилучення групи \"%s \" із синхронізації призведе до припинення синхронізації незгрупованих контактів."
+ "Збереження параметрів відображення…"
+ "Готово"
+ "Скасувати"
+ "Контакти в обліковому записі %s "
+ "Користувацький фільтр"
+ "Один контакт"
+ "Створити контакт в обліковому записі"
+ "Імпорт із SIM-карти"
+ "Імпортувати із SIM-карти \"^1 \" – ^2 "
+ "Імпортувати із SIM-карти \"%1$s \""
+ "Імпортувати з файлу .vcf"
+ "Скасувати імпорт файлу %s ?"
+ "Скасувати експорт файлу %s ?"
+ "Не вдалося скасув. імпорт/експорт vCard"
+ "Невідома помилка."
+ "Не вдалося відкрити файл \"%s \": %s ."
+ "Не вдалося запустити експортер: \"%s \"."
+ "Немає контактів, які можна експортувати."
+ "Ви вимкнули обов’язковий дозвіл."
+ "Під час експорту сталася помилка: \"%s \"."
+ "Потрібна назва файлу задовга (\"%s \")"
+ "На карті SD забагато файлів vCard."
+ "Помилка вводу/виводу"
+ "Недостатньо пам’яті. Можливо, файл завеликий."
+ "Не вдалося проаналізувати vCard через неочікувану причину."
+ "Формат не підтримується."
+ "Не вдалося зібрати мета-інформацію файлів цієї vCard."
+ "Не вдалось імпортувати один або декілька файлів (%s)."
+ "Експорт файлу %s завершено."
+ "Контакти експортовано."
+ "Експорт файлу %s скасовано."
+ "Експортувати контактні дані"
+ "Ваші контактні дані експортуються у файл: %s ."
+ "Не вдалось отримати інформацію бази даних."
+ "Немає контактів, які можна експортувати. Якщо у вашому телефоні є контакти, можливо, якийсь постачальник даних заборонив експорт контактів із телефону."
+ "Майстер vCard не запущено належним чином."
+ "Помилка експорту"
+ "Контактні дані не експортовано.\nПричина: \"%s \""
+ "Імпорт контакта %s "
+ "Не вдалося прочитати дані vCard"
+ "Читання даних vCard скасовано"
+ "Імпорт файлу %s vCard завершено"
+ "Імпорт файлу %s скасовано"
+ "Файл %s незабаром буде імпортовано."
+ "Файл незабаром буде імпортовано."
+ "Запит на імпорт файлу vCard відхилено. Повторіть спробу пізніше."
+ "Файл %s незабаром буде експортовано."
+ "Невдовзі файл буде експортовано."
+ "Запит на експорт файлу vCard відхилено. Повторіть спробу пізніше."
+ "контакт"
+ "Кешування файлів vCard у локальну тимчасову пам’ять. Імпорт почнеться незабаром."
+ "Не вдалось імпортувати файл vCard."
+ "На карті SD не знайдено жодного файлу vCard."
+ "Контакт через NFC"
+ "Експортувати контакти?"
+ "Кешування"
+ "Не вдалося просканувати карту SD. (Причина: \"%s \")"
+ "Імпорт %s з %s : %s "
+ "Експортувати у файл .vcf"
+ "Параметри сортування"
+ "Ім’я"
+ "Прізвище"
+ "Формат імені"
+ "Ім’я спочатку"
+ "Прізвище спочатку"
+ "Надіслати видимі контакти"
+ "Не вдалося поділитись видимими контактами."
+ "Імпорт/експорт контактів"
+ "Імпорт контактів"
+ "Цей контакт неможливо надіслати."
+ "Пошук"
+ "Контакти для показу"
+ "Контакти для показу"
+ "Вибрати групи"
+ "Пошук контактів"
+ "Вибране"
+ "Контактів немає."
+ "Видимих контактів немає."
+ "Немає вибраних."
+ "%s – контактів немає"
+ "Очистити часті контакти"
+ "Вибрати SIM-карту"
+ "Облікові записи"
+ "Імпорт або експорт"
+ "через %1$s "
+ "%1$s через %2$s "
+ "припинити пошук"
+ "Очистити поле пошуку"
+ "Параметри відображення контактів"
+ "Обліковий запис"
+ "Завжди використовувати для дзвінків"
+ "Телефонувати за допомогою"
+ "Зателефонувати й надіслати нотатку"
+ "Введіть нотатку, яку хочете надіслати під час дзвінка…"
+ "ЗАТЕЛЕФОНУВАТИ Й НАДІСЛАТИ"
+ "%1$s з %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-ur-rPK/strings.xml b/ContactsCommon/res/values-ur-rPK/strings.xml
new file mode 100644
index 0000000..a8e6849
--- /dev/null
+++ b/ContactsCommon/res/values-ur-rPK/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "متن کاپی ہوگیا"
+ "کلپ بورڈ میں کاپی کریں"
+ "%s کو کال کریں"
+ "گھر پر کال کریں"
+ "موبائل پر کال کریں"
+ "دفتر کے نمبر پر کال کریں"
+ "دفتر کے فیکس پر کال کریں"
+ "گھر کے فیکس پر کال کریں"
+ "پیجر پر کال کریں"
+ "کال کریں"
+ "کال بیک نمبر پر کال کریں"
+ "کار کے نمبر پر کال کریں"
+ "کمپنی کے اصل نمبر پر کال کریں"
+ "ISDN پر کال کریں"
+ "اصل نمبر پر کال کریں"
+ "فیکس پر کال کریں"
+ "ریڈیو پر کال کریں"
+ "ٹیلیکس پر کال کریں"
+ "TTY / TDD پر کال کریں"
+ "دفتر کے موبائل پر کال کریں"
+ "دفتر کے پیجر پر کال کریں"
+ "%s کو کال کریں"
+ "MMS نمبر پر کال کریں"
+ "%s پر متن بھیجیں"
+ "گھر کے نمبر پر متن بھیجیں"
+ "موبائل پر متن بھیجیں"
+ "دفتر کے نمبر پر متن بھیجیں"
+ "دفتر کے فیکس نمبر پر متن بھیجیں"
+ "گھر کے فیکس نمبر پر متن بھیجیں"
+ "پیجر پر متن بھیجیں"
+ "متن بھیجیں"
+ "کال بیک نمبر پر متن بھیجیں"
+ "کار کے نمبر پر متن بھیجیں"
+ "کمپنی کے اصل نمبر پر متن بھیجیں"
+ "ISDN نمبر پر متن بھیجیں"
+ "اصل نمبر پر متن بھیجیں"
+ "فیکس پر متن بھیجیں"
+ "ریڈیو پر متن بھیجیں"
+ "ٹیلکس پر متن بھیجیں"
+ "TTY / TDD پر متن بھیجیں"
+ "دفتر کے موبائل پر متن بھیجیں"
+ "دفتر کے پیجر پر متن بھیجیں"
+ "%s پر متن بھیجیں"
+ "MMS نمبر پر متن بھیجیں"
+ "ویڈیو کال کریں"
+ "اکثر رابطہ کردہ کو صاف کریں؟"
+ "آپ رابطے اور فون ایپس میں اکثر رابطہ کردہ فہرست کو صاف کر دیں گے اور ای میل ایپس کو از سر نو اپنے پتے کی ترجیحات جاننے پر مجبور کریں گے۔"
+ "اکثر رابطہ کردہ کو صاف کر رہا ہے…"
+ "دستیاب"
+ "دور"
+ "مصروف"
+ "رابطے"
+ "دیگر"
+ "ڈائریکٹری"
+ "سبھی رابطے"
+ "میں"
+ "تلاش کر رہا ہے…"
+ "%d سے زیادہ ملے۔"
+ "کوئی رابطے نہیں ہیں"
+
+ %d ملے
+ - 1 ملا
+
+ "%1$s کیلئے فوری رابطہ"
+ "(کوئی نام نہیں)"
+ "اکثر کال کردہ"
+ "اکثر رابطہ کردہ"
+ "رابطہ دیکھیں"
+ "فون نمبرز کے ساتھ سبھی رابطے"
+ "اپ ڈیٹس دیکھیں"
+ "صرف فون، غیر مطابقت پذیر"
+ "نام"
+ "عرفی نام"
+ "نام"
+ "پہلا نام"
+ "آخری نام"
+ "نام کا سابقہ"
+ "درمیانی نام"
+ "نام کا لاحقہ"
+ "فونیٹک نام"
+ "فونیٹک پہلا نام"
+ "فونیٹک درمیانی نام"
+ "فونیٹک آخری نام"
+ "فون"
+ "ای میل"
+ "پتہ"
+ "IM"
+ "تنظیم"
+ "تعلق"
+ "خصوصی تاریخیں"
+ "متنی پیغام"
+ "پتہ"
+ "کمپنی"
+ "عنوان"
+ "نوٹس"
+ "SIP"
+ "ویب سائٹ"
+ "گروپس"
+ "گھر کے پتے پر ای میل کریں"
+ "موبائل پر ای میل کریں"
+ "دفتر کو ای میل کریں"
+ "ای میل کریں"
+ "ای میل کریں %s "
+ "ای میل کریں"
+ "اسٹریٹ"
+ "PO باکس"
+ "مضافات"
+ "شہر"
+ "ریاست"
+ "زپ کوڈ"
+ "ملک"
+ "گھر کا پتہ دیکھیں"
+ "دفتر کا پتہ دیکھیں"
+ "پتہ دیکھیں"
+ "%s پتہ دیکھیں"
+ "AIM کے ذریعے چیٹ کریں"
+ "Windows Live کے ذریعے چیٹ کریں"
+ "Yahoo کے ذریعے چیٹ کریں"
+ "Skype کے ذریعے چیٹ کریں"
+ "QQ کے ذریعے چیٹ کریں"
+ "Google Talk کے ذریعے چیٹ کریں"
+ "ICQ کے ذریعے چیٹ کریں"
+ "Jabber کے ذریعے چیٹ کریں"
+ "چیٹ کریں"
+ "حذف کریں"
+ "نام کی فیلڈز کو پھیلائیں یا چھوٹا کریں"
+ "سبھی رابطے"
+ "ستارے کے نشان والے"
+ "حسب ضرورت بنائیں"
+ "رابطہ"
+ "سبھی دیگر رابطے"
+ "سبھی رابطے"
+ "مطابقت پذیر گروپ کو ہٹائیں"
+ "مطابقت پذیر گروپ شامل کریں"
+ "مزید گروپس…"
+ "\"%s \" کو مطابقت پذیری سے ہٹانے سے مطابقت پذیری سے گروپ سے خارج کردہ کوئی رابطے بھی ہٹ جائیں گے۔"
+ "ڈسپلے کے اختیارات محفوظ کر رہا ہے…"
+ "ہو گیا"
+ "منسوخ کریں"
+ "%s میں رابطے"
+ "حسب ضرورت منظر میں رابطے"
+ "واحد رابطہ"
+ "اکاؤنٹ کے تحت رابطہ بنائیں"
+ "SIM کارڈ سے درآمد کریں"
+ "SIM ^1 - ^2 سے درآمد کریں"
+ "SIM %1$s سے درآمد کریں"
+ ".vcf فائل سے درآمد کریں"
+ "%s کی درآمد منسوخ کریں؟"
+ "%s کی برآمد منسوخ کریں؟"
+ "وی کارڈ کی درآمد/برآمد کو منسوخ نہیں کرسکا"
+ "نامعلوم خرابی۔"
+ "\"%s \" کو نہیں کھول سکا: \"%s \"۔"
+ "برآمد کنندہ شروع نہیں ہو سکا: \"%s \"۔"
+ "کوئی قابل برآمد رابطہ نہیں ہے۔"
+ "آپ نے ایک درکار اجازت غیر فعال کر دی ہے۔"
+ "برآمد کرنے کے دوران ایک غلطی ہوگئی: \"%s \"۔"
+ "مطلوبہ فائل کا نام کافی لمبا (\"%s \") ہے۔"
+ "SD کارڈ میں بہت زیادہ وی کارڈ فائلیں ہیں۔"
+ "I/O غلطی"
+ "کافی میموری نہیں ہے۔ فائل کافی بڑی ہو سکتی ہے۔"
+ "ایک غیر متوقع وجہ سے وی کارڈ کو پارس نہیں کرسکا۔"
+ "فارمیٹ تعاون یافتہ نہیں ہے۔"
+ "مقررہ وی کارڈ فائل (فائلوں) کی میٹا معلومات اکٹھا نہیں کرسکا۔"
+ "ایک یا مزید فائلیں درآمد نہیں ہوسکیں (%s)۔"
+ "%s کی برآمد پوری ہوگئی۔"
+ "رابطوں کی برآمدگی مکمل ہو گئی۔"
+ "%s کی برآمد منسوخ ہوگئی۔"
+ "رابطہ کا ڈیٹا برآمد کر رہا ہے"
+ "آپ کا رابطے کا ڈیٹا اس میں برآمد کیا جا رہا ہے: %s ۔"
+ "ڈیٹابیس کی معلومات حاصل نہیں ہو سکی۔"
+ "برآمد کرنے لائق کوئی رابطے نہیں ہیں۔ اگر آپ کے فون پر واقعی رابطے ہیں تو ممکن ہے کچھ ڈیٹا فراہم کنندگان رابطوں کو فون سے برآمد کیے جانے کی اجازت نہ دیتے ہوں۔"
+ "وی کارڈ کمپوزر مناسب طریقے سے شروع نہیں ہوا۔"
+ "برآمد نہیں کیا جاسکا"
+ "رابطہ کا ڈیٹا برآمد نہیں ہوا۔\nوجہ: \"%s \""
+ "%s کو درآمد کر رہا ہے"
+ "وی کارڈ کا ڈیٹا نہیں پڑھ سکا"
+ "وی کارڈ کا ڈیٹا پڑھنا منسوخ ہوگیا"
+ "وی کارڈ %s کی درآمد پوری ہوگئی"
+ "%s کی درآمد منسوخ ہوگئی"
+ "%s جلد ہی درآمد کی جائے گی۔"
+ "فائل جلد ہی درآمد کی جائے گی۔"
+ "وی کارڈ درآمد کرنے کی درخواست مسترد ہوگئی تھی۔ بعد میں دوبارہ کوشش کریں۔"
+ "%s جلد ہی برآمد کی جائے گی۔"
+ "فائل تھوڑی دیر میں برآمد کر دی جائے گی۔"
+ "وی کارڈ برآمد کرنے کی درخواست مسترد ہوگئی تھی۔ بعد میں دوبارہ کوشش کریں۔"
+ "رابطہ"
+ "وی کارڈ (کارڈز) کو مقامی عارضی اسٹوریج میں ذخیرہ کر رہا ہے۔ اصل درآمد جلد ہی شروع ہوگی۔"
+ "وی کارڈ درآمد نہیں کرسکا۔"
+ "SD کارڈ میں کوئی vCard فائل نہیں ملی۔"
+ "NFC پر موصولہ رابطہ"
+ "رابطے برآمد کریں؟"
+ "ذخیرہ کر رہا ہے"
+ "SD کارڈ کو اسکین نہیں کیا جا سکا۔ (وجہ: \"%s \")"
+ "%s /%s : %s درآمد کر رہا ہے"
+ ".vcf فائل میں برآمد کریں"
+ "ترتیب دیں بلحاظ"
+ "پہلا نام"
+ "آخری نام"
+ "نام کا فارمیٹ"
+ "پہلا نام پہلے"
+ "آخری نام پہلے"
+ "مرئی رابطوں کا اشتراک کریں"
+ "مرئی رابطوں کا اشتراک کرنے میں ناکام ہو گیا۔"
+ "رابطے درآمد/برآمد کریں"
+ "رابطے درآمد کریں"
+ "اس رابطہ کا اشتراک نہیں کیا جا سکتا۔"
+ "تلاش کریں"
+ "ڈسپلے کرنے کیلئے رابطے"
+ "ڈسپلے کرنے کیلئے رابطے"
+ "حسب ضرورت منظر بیان کریں"
+ "رابطے تلاش کریں"
+ "پسندیدہ"
+ "کوئی رابطے نہیں ہیں۔"
+ "کوئی مرئی رابطے نہیں ہیں۔"
+ "کوئی پسندیدہ نہیں ہیں۔"
+ "%s میں کوئی رابطے نہیں ہیں"
+ "اکثر و بیشتر کو صاف کریں"
+ "SIM کارڈ منتخب کریں"
+ "اکاؤنٹس"
+ "درآمد/برآمد کریں"
+ "معرفت %1$s "
+ "%1$s معرفت %2$s "
+ "تلاش کرنا بند کریں"
+ "تلاش صاف کریں"
+ "رابطہ کے ڈسپلے کے اختیارات"
+ "اکاؤنٹ"
+ "کالز کیلئے ہمیشہ اس کا استعمال کریں"
+ "کال کریں مع"
+ "ایک نوٹ کے ساتھ کال کریں"
+ "کال کے ساتھ بھیجنے کیلئے ایک نوٹ ٹائپ کریں…"
+ "بھیجیں اور کال کریں"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-uz-rUZ/strings.xml b/ContactsCommon/res/values-uz-rUZ/strings.xml
new file mode 100644
index 0000000..2aa6b3e
--- /dev/null
+++ b/ContactsCommon/res/values-uz-rUZ/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Matndan nusxa olindi"
+ "Vaqtinchalik xotiraga nusxalash"
+ "Qo‘ng‘iroq: %s "
+ "Uy telefoniga qo‘ng‘iroq qilish"
+ "Mobil"
+ "Ish telefoniga qo‘ng‘iroq qilish"
+ "Ish faks raqamiga qo‘ng‘iroq qilish"
+ "Uy faks raqamiga qo‘ng‘iroq qilish"
+ "Peyjerga qo‘ng‘iroq qilish"
+ "Qo‘ng‘iroq qilish"
+ "Qayta qo‘ng‘iroq raqamiga qo‘ng‘iroq qilish"
+ "Mashina telefon raqamiga qo‘ng‘iroq qilish"
+ "Kompaniya asosiy raqamiga qo‘ng‘iroq qilish"
+ "ISDN telefon raqamiga qo‘ng‘iroq qilish"
+ "Asosiy raqamga qo‘ng‘iroq qilish"
+ "Faks raqamiga qo‘ng‘iroq qilish"
+ "Radio telefon raqamiga qo‘ng‘iroq qilish"
+ "Teleks telefon raqamiga qo‘ng‘iroq qilish"
+ "TTY/TDD telefoniga qo‘ng‘iroq qilish"
+ "Ishxona: mobil"
+ "Ish peyjeriga qo‘ng‘iroq qilish"
+ "Qo‘ng‘iroq: %s "
+ "MMS raqamga qo‘ng‘iroq qilish"
+ "%s ga sms jo‘natish"
+ "Uy telefoniga sms jo‘natish"
+ "SMS: mobil"
+ "Ish telefoniga sms yozish"
+ "Ish faksiga sms jo‘natish"
+ "Uy faksiga sms jo‘natish"
+ "Peyjer raqamiga sms jo‘natish"
+ "Boshqa telefonga sms jo‘natish"
+ "Teskari qo‘ng‘iroq qilish raqamiga sms jo‘natish"
+ "Mashina telefoniga sms jo‘natish"
+ "Kompaniya asosiy telefoniga sms jo‘natish"
+ "ISDN telefonga sms jo‘natish"
+ "Asosiy telefonga sms jo‘natish"
+ "Faks raqamiga sms jo‘natish"
+ "Radio telefonga sms jo‘natish"
+ "Teleks telefoniga sms jo‘natish"
+ "TTY/TDD telefoniga sms jo‘natish"
+ "SMS: ishxona (mobil)"
+ "Ish peyjeriga sms jo‘natish"
+ "%s telefoniga qo‘ng‘iroq qilish"
+ "MMS telefonga sms jo‘natish"
+ "Videoqo‘ng‘iroq qilish"
+ "Ko‘p gaplashilganlar tozalansinmi?"
+ "Kontaktlar va Telefon ilovalaridagi tez-tez aloqa qilingan kontaktlar ro‘yxati tozalanadi hamda e-pochta ilovalari sizning shaxsiy sozlamalaringizga moslashadi."
+ "Ko‘p gaplashilganlar tozalanmoqda…"
+ "Aloqada"
+ "Tashqarida"
+ "Band"
+ "Kontaktlar"
+ "Boshqa"
+ "Direktoriya"
+ "Barcha kontaktlar"
+ "Men"
+ "Qidirilmoqda…"
+ "%d dan ko‘proq topildi."
+ "Hech qanday kontakt yo‘q"
+
+ %d ta topildi
+ - 1 found
+
+ "%1$s uchun tez kontakt"
+ "(Ismi yo‘q)"
+ "Ko‘p qo‘ng‘iroq qilingan kontaktlar"
+ "Ko‘p gaplashilgan kontaktlar"
+ "Kontaktni ko‘rish"
+ "Telefon raqamli kontaktlar"
+ "Yangilanishlarni ko‘rish"
+ "Faqat telefonda, sinxronlanmaydi"
+ "Ismi"
+ "Taxallusi"
+ "To‘liq ismi"
+ "Ismi"
+ "Familiyasi"
+ "Ismga old qo‘shimcha"
+ "Otasining ismi"
+ "Ismga qo‘shimcha"
+ "Ism talaffuzi"
+ "Ism talaffuzi"
+ "Otasining ismi talaffuzi"
+ "Familiya talaffuzi"
+ "Telefon"
+ "E-pochta"
+ "Manzil"
+ "IM"
+ "Tashkilot"
+ "Aloqadorligi"
+ "Maxsus sanalar"
+ "Matn xabari"
+ "Manzil"
+ "Kompaniya"
+ "Lavozimi"
+ "Eslatmalar"
+ "SIP"
+ "Veb-sayt"
+ "Guruhlar"
+ "Uy e-pochtasiga xat jo‘natish"
+ "Mobil e-pochtaga xat yozish"
+ "Ish e-pochtasiga xat jo‘natish"
+ "Boshqa e-pochtalarga xat jo‘natish"
+ "%s ga xat jo‘natish"
+ "E-pochta"
+ "Ko‘cha"
+ "Pochta qutisi"
+ "Mahalla"
+ "Shahar"
+ "Shahar"
+ "ZIP kodi"
+ "Davlat"
+ "Uy manzilini ko‘rish"
+ "Ish manzilini ko‘rish"
+ "Manzilni ko‘rish"
+ "%s manzillarni ko‘rish"
+ "AIM’da suhbatlashish"
+ "Windows Live orqali chat"
+ "Yahoo’da suhbatlashish"
+ "Skype’da suhbatlashish"
+ "QQ’da suhbatlashish"
+ "Google Talk’da suhbatlashish"
+ "ICQ’da suhbatlashish"
+ "Jabber’da suhbatlashish"
+ "Suhbatlashish"
+ "o‘chirib tashlash"
+ "Qo‘shimcha maydonlarni ko‘rsatish va berkitish"
+ "Barcha kontaktlar"
+ "Baho berilgan"
+ "Sozlash"
+ "Kontakt"
+ "Boshqa kontaktlar"
+ "Barcha kontaktlar"
+ "Sinx. guruhini o‘chirish"
+ "Sinxronlash guruhini qo‘shish"
+ "Ko‘proq guruhlar…"
+ "\"%s \"ni sinxronlashdan olib tashlash, shuningdek, har qanday guruhlanmagan kontaktlarni sinxronlashdan olib tashlaydi."
+ "Parametrlar saqlanmoqda…"
+ "Tayyor"
+ "Bekor qilish"
+ "%s dagi kontaktlar"
+ "Filtrlangan kontaktlar"
+ "Bitta kontakt"
+ "Hisob ostida kontakt yaratish"
+ "SIM-kartadan import qilish"
+ "Quyidagi SIM-kartadan import qilish: ^1 – ^2 "
+ "Quyidagi SIM-kartadan import qilish: %1$s "
+ "VCF fayldan import qilish"
+ "%s ni import qilish bekor qilinsinmi?"
+ "%s ni eksport qilish bekor qilinsinmi?"
+ "Tashrifnomani import/eksport qilishni bekor qilib bo‘lmadi"
+ "Noma’lum xato."
+ "\"%s \" ochilmadi: %s ."
+ "Eksport qiluvchini ishga tushirib bo‘lmaydi: \"%s \"."
+ "Eksport qilsa bo‘ladigan kontakt mavjud emas."
+ "Siz zarur ruxsatni o‘chirib qo‘ygansiz."
+ "Eksport jarayonida xato yuz berdi: \"%s \"."
+ "So‘ralgan fayl nomi juda uzun (\"%s \")."
+ "SD kartada juda ko‘p tashrifnoma fayllari bor."
+ "I/O xato"
+ "Xotira yetarli emas. Fayl juda katta bo‘lishi mumkin."
+ "Tashrifnomani kutilmagan sabab tufayli tahlil qilib bo‘lmadi."
+ "Ushbu formatda ishlamaydi."
+ "Berilgan tashrifnoma(lar) meta ma’lumotini yig‘ib bo‘lmadi."
+ "Bir yoki bir necha fayllarni import qilib bo‘lmadi (%s)."
+ "%s ni eksport qilish tugadi."
+ "Kontaktlar eksport qilindi."
+ "%s ni eksport qilish bekor qilindi."
+ "Kontakt ma’lumoti eksport qilinmoqda"
+ "Kontakt ma’lumotlaringiz %s ga eksport qilinmoqda."
+ "Ma’lumotlar bazasining ma’lumoti olinmadi."
+ "Eksport qilsa bo‘ladigan kontaktlar mavjud emas. Agar telefoningizda kontaktlar bo‘lsa, ba’zi tarmoq operatorlari telefondan kontaktlarni eksport qilishga ruxsat bermaydi."
+ "Tashrifnoma yaratgich to‘g‘ri ishga tushmagan."
+ "Eksport qilinmadi"
+ "Kontakt ma’lumoti eksport qilinmadi.\nSababi: \"%s \""
+ "%s import qilinmoqda"
+ "Tashrifnoma ma’lumotlari o‘qilmadi"
+ "Tashrifnomani o‘qish bekor qilindi"
+ "%s tashrifnomani import qilish tugadi"
+ "%s ni import qilish bekor qilindi"
+ "%s tezda import qilinadi."
+ "Fayl tezda import qilinadi."
+ "Tashrifnomani import qilish so‘rovi rad qilindi. Keyinroq urinib ko‘ring."
+ "%s tezda eksport qilinadi."
+ "Fayl tez orada eksport qilinadi."
+ "Tashrifnomani eksport qilish rad qilindi. Keyinroq urinib ko‘ring."
+ "kontakt"
+ "Tashrifnoma(lar) telefondagi vaqtinchalik xotira keshiga yuklanmoqda. Import qilish jarayoni tezda boshlanadi."
+ "Tashrifnoma import qilinmadi."
+ "SD-kartada hech qanday vCard fayli yo‘q."
+ "Kontakt NFC orqali olindi"
+ "Kontaktlar eksport qilinsinmi?"
+ "Keshga yuklanmoqda"
+ "SD kartani tekshirib bo‘lmaydi. (Sababi: \"%s \")"
+ "%s /%s : %s import qilinmoqda"
+ "VCF faylga eksport qilish"
+ "Saralash"
+ "Ismi"
+ "Familiyasi"
+ "Ism formati"
+ "Avval ismi"
+ "Avval familiyasi"
+ "Ko‘rinadigan kontaktlarni yuborish"
+ "Ko‘rinadigan kontaktlarni yuborib bo‘lmadi."
+ "Kontaktlarni import/eksport qilish"
+ "Kontaktlarni import qilish"
+ "Kontaktni yuborib bo‘lmadi."
+ "Qidirish"
+ "Kontaktlarni filtrlash"
+ "Kontaktlarni filtrlash"
+ "Kontaktlar guruhini tanlang"
+ "Kontakt qidirish"
+ "Saralar"
+ "Hech qanday kontakt yo‘q."
+ "Ko‘rinadigan kontaktlar yo‘q."
+ "Hech qanday sevimli kontakt yo‘q."
+ "%s guruhida hech qanday kontakt yo‘q"
+ "Muntazam qo‘n-qlarni tozalash"
+ "SIM kartani tanlash"
+ "Hisoblar"
+ "Import/eksport"
+ "%1$s orqali"
+ "%2$s orqali %1$s "
+ "qidiruvni to‘xtatish"
+ "Qidiruvni tozalash"
+ "Kontaktlarni ko‘rsatish"
+ "Hisob"
+ "Har doim qo‘ng‘iroqlar u-n foyd-sin"
+ "Ushbu bilan qo‘ng‘iroq qilish"
+ "Qo‘ng‘iroq va izoh"
+ "Qo‘ng‘iroqqa qo‘shib yuborish uchun izoh yozing ..."
+ "YUBORISH va QO‘NG‘IROQ QILISH"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-vi/strings.xml b/ContactsCommon/res/values-vi/strings.xml
new file mode 100644
index 0000000..17fb1ab
--- /dev/null
+++ b/ContactsCommon/res/values-vi/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Đã sao chép văn bản"
+ "Sao chép vào khay nhớ tạm"
+ "Gọi %s "
+ "Gọi số điện thoại nhà riêng"
+ "Gọi số điện thoại di động"
+ "Gọi số điện thoại cơ quan"
+ "Gọi số fax cơ quan"
+ "Gọi số fax nhà riêng"
+ "Gọi số máy nhắn tin"
+ "Gọi"
+ "Gọi số gọi lại"
+ "Gọi số điện thoại trên ô tô"
+ "Gọi số điện thoại chính của công ty"
+ "Gọi ISDN"
+ "Gọi số điện thoại chính"
+ "Gọi số fax"
+ "Gọi số điện thoại radio"
+ "Gọi số telex"
+ "Gọi số điện thoại TTY/TDD"
+ "Gọi số điện thoại di động tại cơ quan"
+ "Gọi số máy nhắn tin tại cơ quan"
+ "Gọi %s "
+ "Gọi MMS"
+ "Nhắn tin tới %s "
+ "Nhắn tin tới số điện thoại nhà riêng"
+ "Nhắn tin tới số điện thoại di động"
+ "Nhắn tin tới số điện thoại cơ quan"
+ "Nhắn tin tới số fax cơ quan"
+ "Nhắn tin tới số fax nhà riêng"
+ "Nhắn tin tới số máy nhắn tin"
+ "Nhắn tin"
+ "Nhắn tin tới số gọi lại"
+ "Nhắn tin tới số điện thoại trên ô tô"
+ "Nhắn tin tới số điện thoại chính của công ty"
+ "Nhắn tin tới số điện thoại ISDN"
+ "Nhắn tin tới số điện thoại chính"
+ "Nhắn tin tới số fax"
+ "Nhắn tin tới số điện thoại radio"
+ "Nhắn tin tới số telex"
+ "Nhắn tới số TTY/TDD"
+ "Nhắn tới số điện thoại di động tại cơ quan"
+ "Nhắn tin tới số máy nhắn tin tại cơ quan"
+ "Nhắn tin tới %s "
+ "Nhắn tin tới số điện thoại MMS"
+ "Gọi điện video"
+ "Xóa danh sách liên hệ thường xuyên?"
+ "Bạn sẽ xóa danh sách liên hệ thường xuyên trong ứng dụng Danh bạ và điện thoại cũng như buộc các ứng dụng email phải tìm hiểu các tùy chọn gửi của bạn lại từ đầu."
+ "Đang xóa DS liên hệ thường xuyên…"
+ "Có mặt"
+ "Đã ra ngoài"
+ "Bận"
+ "Danh bạ"
+ "Khác"
+ "Thư mục"
+ "Tất cả người liên hệ"
+ "Tôi"
+ "Đang tìm kiếm…"
+ "Đã tìm thấy hơn %d người liên hệ."
+ "Không có người liên hệ nào"
+
+ - Đã tìm thấy
%d
+ - Đã tìm thấy 1
+
+ "Liên hệ nhanh của %1$s "
+ "(Không có tên)"
+ "Thường xuyên được gọi"
+ "Thường xuyên được liên hệ"
+ "Xem người liên hệ"
+ "Tất cả người liên hệ có số điện thoại"
+ "Xem thông tin cập nhật"
+ "Chỉ trên điện thoại, ko đồng bộ"
+ "Tên"
+ "Biệt hiệu"
+ "Tên"
+ "Tên"
+ "Họ"
+ "Danh xưng"
+ "Tên đệm"
+ "Hậu tố tên"
+ "Tên theo phiên âm"
+ "Tên theo phiên âm"
+ "Tên đệm theo phiên âm"
+ "Họ theo phiên âm"
+ "Điện thoại"
+ "Email"
+ "Địa chỉ"
+ "IM"
+ "Tổ chức"
+ "Mối quan hệ"
+ "Ngày đặc biệt"
+ "Tin nhắn văn bản"
+ "Địa chỉ"
+ "Công ty"
+ "Chức danh"
+ "Ghi chú"
+ "SIP"
+ "Trang web"
+ "Nhóm"
+ "Gửi email tới địa chỉ email nhà riêng"
+ "Gửi email tới địa chỉ email thiết bị di động"
+ "Gửi email tới địa chỉ email cơ quan"
+ "Gửi email"
+ "Gửi email cho %s "
+ "Gửi email"
+ "Đường phố"
+ "Hòm thư bưu điện"
+ "Vùng lân cận"
+ "Thành phố"
+ "Tiểu bang"
+ "Mã ZIP"
+ "Quốc gia"
+ "Xem địa chỉ nhà riêng"
+ "Xem địa chỉ cơ quan"
+ "Xem địa chỉ"
+ "Xem địa chỉ %s "
+ "Trò chuyện sử dụng AIM"
+ "Trò chuyện sử dụng Windows Live"
+ "Trò chuyện sử dụng Yahoo"
+ "Trò chuyện sử dụng Skype"
+ "Trò chuyện sử dụng QQ"
+ "Trò chuyện sử dụng Google Talk"
+ "Trò chuyện sử dụng ICQ"
+ "Trò chuyện sử dụng Jabber"
+ "Trò chuyện"
+ "xóa"
+ "Mở rộng hoặc thu gọn trường tên"
+ "Tất cả liên hệ"
+ "Có gắn dấu sao"
+ "Tùy chỉnh"
+ "Liên hệ"
+ "Tất cả liên hệ khác"
+ "Tất cả liên hệ"
+ "Xóa nhóm đồng bộ hóa"
+ "Thêm nhóm đồng bộ hóa"
+ "Nhóm khác…"
+ "Xóa \"%s \" khỏi đồng bộ hóa cũng sẽ xóa bất kỳ liên hệ nào chưa được nhóm khỏi đồng bộ hóa."
+ "Đang lưu tuỳ chọn hiển thị…"
+ "Xong"
+ "Hủy"
+ "Danh bạ trong %s "
+ "Danh bạ ở chế độ xem tùy chỉnh"
+ "Một liên hệ"
+ "Tạo liên hệ trong tài khoản"
+ "Nhập từ thẻ SIM"
+ "Nhập từ SIM ^1 - ^2 "
+ "Nhập từ SIM %1$s "
+ "Nhập từ tệp .vcf"
+ "Hủy nhập %s ?"
+ "Hủy xuất %s ?"
+ "Không thể nhập/xuất vCard"
+ "Lỗi không xác định."
+ "Không thể mở \"%s \": %s ."
+ "Không thể chạy trình xuất: \"%s \"."
+ "Không thể xuất liên hệ nào."
+ "Bạn đã tắt quyền được yêu cầu."
+ "Đã xảy ra lỗi khi xuất: \"%s \"."
+ "Tên tệp yêu cầu quá dài (\"%s \")."
+ "Quá nhiều tệp vCard trên thẻ SD."
+ "Lỗi I/O"
+ "Không đủ bộ nhớ. Tệp có thể quá lớn."
+ "Không thể phân tích cú pháp vCard vì lý do không mong muốn."
+ "Định dạng không được hỗ trợ."
+ "Không thể thu thập thông tin meta của (các) tệp vCard cụ thể."
+ "Không thể nhập một hoặc nhiều tệp (%s)."
+ "Đã xuất xong %s ."
+ "Đã xuất xong danh bạ."
+ "Quá trình xuất %s bị hủy."
+ "Đang xuất dữ liệu liên hệ"
+ "Dữ liệu liên hệ của bạn đang được xuất sang: %s ."
+ "Không thể nhận thông tin cơ sở dữ liệu."
+ "Không thể xuất liên hệ nào. Nếu bạn có danh bạ trên điện thoại của mình, một số nhà cung cấp dữ liệu không cho phép xuất danh bạ từ điện thoại."
+ "Trình soạn vCard không khởi động đúng."
+ "Không thể xuất"
+ "Không xuất được dữ liệu liên hệ.\nLý do: \"%s \""
+ "Đang nhập %s "
+ "Không thể đọc dữ liệu vCard"
+ "Thao tác đọc dữ liệu vCard bị hủy"
+ "Đã nhập xong vCard %s "
+ "Quá trình nhập %s bị hủy"
+ "%s sẽ sớm được nhập."
+ "Tệp sẽ sớm được nhập."
+ "Yêu cầu nhập vCard bị từ chối. Hãy thử lại sau."
+ "%s sẽ sớm được xuất."
+ "Tệp sẽ sớm được xuất."
+ "Yêu cầu xuất vCard bị từ chối. Hãy thử lại sau."
+ "liên hệ"
+ "Đang lưu vào bộ nhớ cache các tệp vCard sẽ được nhập vào bộ nhớ cục bộ tạm thời. Thao tác nhập thực sự sẽ sớm bắt đầu."
+ "Không thể nhập vCard."
+ "Không tìm thấy tệp vCard trên thẻ SD."
+ "L.h nhận qua NFC"
+ "Xuất danh bạ?"
+ "Đang lưu vào bộ nhớ cache"
+ "Không thể quét thẻ SD. (Lý do: \"%s \")"
+ "Đang nhập %s /%s : %s "
+ "Xuất sang tệp .vcf"
+ "Sắp xếp theo"
+ "Tên"
+ "Họ"
+ "Định dạng tên"
+ "Tên trước"
+ "Họ trước"
+ "Chia sẻ liên hệ hiển thị"
+ "Không chia sẻ được liên hệ được hiển thị."
+ "Nhập/xuất danh bạ"
+ "Nhập danh bạ"
+ "Không thể chia sẻ liên hệ này."
+ "Tìm kiếm"
+ "Danh bạ hiển thị"
+ "Danh bạ hiển thị"
+ "Xác định kiểu xem tùy chỉnh"
+ "Tìm liên hệ"
+ "Mục yêu thích"
+ "Không có địa chỉ liên hệ nào."
+ "Không có địa chỉ liên hệ nào hiển thị."
+ "Không có liên hệ yêu thích nào."
+ "Không có địa chỉ liên hệ nào trong %s "
+ "Xóa DS liên hệ thường xuyên"
+ "Chọn thẻ SIM"
+ "Tài khoản"
+ "Nhập/xuất"
+ "qua %1$s "
+ "%1$s qua %2$s "
+ "ngừng tìm kiếm"
+ "Xóa tìm kiếm"
+ "Tùy chọn hiển thị liên hệ"
+ "Tài khoản"
+ "Luôn sử dụng SIM này để gọi"
+ "Gọi bằng"
+ "Gọi điện kèm theo ghi chú"
+ "Nhập ghi chú để gửi kèm cuộc gọi..."
+ "GỬI VÀ GỌI"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-zh-rCN/strings.xml b/ContactsCommon/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..8284dd9
--- /dev/null
+++ b/ContactsCommon/res/values-zh-rCN/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "文本已复制"
+ "复制到剪贴板"
+ "拨打%s 的电话号码"
+ "拨打家庭电话号码"
+ "拨打手机号码"
+ "拨打公司电话号码"
+ "拨打公司传真号码"
+ "拨打家庭传真号码"
+ "拨打寻呼机号码"
+ "拨打电话"
+ "拨打回拨号码"
+ "拨打车载电话号码"
+ "拨打公司总机号码"
+ "拨打 ISDN 电话号码"
+ "拨打主电话号码"
+ "拨打传真号码"
+ "拨打无线通讯电话号码"
+ "拨打电报号码"
+ "拨打 TTY/TDD 号码"
+ "拨打公司手机号码"
+ "拨打公司寻呼机号码"
+ "拨打%s 的电话号码"
+ "拨打彩信电话号码"
+ "向%s 的电话号码发短信"
+ "向家庭电话号码发短信"
+ "向手机发短信"
+ "向公司电话号码发短信"
+ "向公司传真号码发短信"
+ "向家庭传真号码发短信"
+ "向寻呼机号码发短信"
+ "发短信"
+ "向回拨号码发短信"
+ "向车载电话号码发短信"
+ "向公司总机号码发短信"
+ "向 ISDN 电话号码发短信"
+ "向主电话号码发短信"
+ "向传真号码发短信"
+ "向无线通讯电话号码发短信"
+ "向电报号码发短信"
+ "向 TTY/TDD 号码发短信"
+ "向公司手机发短信"
+ "向公司寻呼机号码发短信"
+ "向%s 发短信"
+ "向彩信电话号码发短信"
+ "发起视频通话"
+ "是否清除常用联系人?"
+ "此操作会清除“通讯录”和“电话”应用中的常用联系人列表,并强制电子邮件应用重新获取您最常使用的联系地址。"
+ "正在清除常用联系人…"
+ "在线"
+ "离开"
+ "忙碌"
+ "通讯录"
+ "其他"
+ "目录"
+ "所有联系人"
+ "我"
+ "正在搜索…"
+ "找到超过 %d 位联系人。"
+ "没有联系人"
+
+ - 找到
%d 位联系人
+ - 找到 1 位联系人
+
+ "%1$s 的快速联系人照片"
+ "(无姓名)"
+ "经常呼叫的联系人"
+ "常用联系人"
+ "查看联系人"
+ "所有拥有电话号码的联系人"
+ "查看最新动态"
+ "仅保存在手机中,不同步"
+ "姓名"
+ "昵称"
+ "姓名"
+ "名字"
+ "姓氏"
+ "姓名前缀"
+ "中间名"
+ "姓名后缀"
+ "姓名拼音"
+ "名字拼音"
+ "中间名拼音"
+ "姓氏拼音"
+ "电话"
+ "电子邮件"
+ "地址"
+ "聊天工具"
+ "公司或组织"
+ "关系"
+ "特殊日期"
+ "发送短信"
+ "地址"
+ "公司"
+ "职务"
+ "备注"
+ "SIP"
+ "网站"
+ "群组"
+ "向个人邮箱发送电子邮件"
+ "向手机发送电子邮件"
+ "向工作邮箱发送电子邮件"
+ "发送电子邮件"
+ "向%s 发送电子邮件"
+ "发送电子邮件"
+ "街道"
+ "邮政信箱"
+ "社区"
+ "城市"
+ "省/自治区/直辖市"
+ "邮编"
+ "国家/地区"
+ "查看住宅地址"
+ "查看工作地址"
+ "查看地址"
+ "查看%s 地址"
+ "使用 AIM 聊天"
+ "使用 Windows Live 聊天"
+ "使用雅虎通聊天"
+ "使用 Skype 聊天"
+ "使用 QQ 聊天"
+ "使用Google Talk聊天"
+ "使用 ICQ 聊天"
+ "使用 Jabber 聊天"
+ "聊天"
+ "删除"
+ "展开或折叠姓名字段"
+ "所有联系人"
+ "已加星标"
+ "自定义"
+ "联系人"
+ "其他所有联系人"
+ "所有联系人"
+ "删除同步群组"
+ "添加同步群组"
+ "更多群组..."
+ "如果您停止同步“%s ”,将同时停止同步所有未分组的联系人。"
+ "正在保存显示选项..."
+ "完成"
+ "取消"
+ "%s 中的联系人"
+ "自定义视图中的联系人"
+ "单个联系人"
+ "在以下帐户中创建联系人:"
+ "从 SIM 卡导入"
+ "从 SIM 卡“^1 ” - ^2 导入"
+ "从 SIM 卡“%1$s ”导入"
+ "从 .vcf 文件导入"
+ "要取消导入“%s ”吗?"
+ "要取消导出“%s ”吗?"
+ "无法取消导入/导出 vCard"
+ "未知错误。"
+ "无法打开“%s ”:%s 。"
+ "无法启动导出程序:“%s ”。"
+ "没有可导出的联系人。"
+ "您已停用某项必需权限。"
+ "导出时出错:“%s ”。"
+ "指定的文件名过长(“%s ”)。"
+ "SD卡上的 vCard 文件过多。"
+ "I/O 错误"
+ "内存不足。该文件可能过大。"
+ "由于意外原因而无法解析 vCard。"
+ "不支持此格式。"
+ "无法收集指定 vCard 文件的元信息。"
+ "一个或多个文件无法导入 (%s)。"
+ "已顺利导出“%s ”。"
+ "已成功导出联系人。"
+ "已取消导出“%s ”。"
+ "正在导出联系人数据"
+ "正在将您的联系人数据导出到以下文件:%s 。"
+ "无法获取数据库信息。"
+ "没有可导出的联系人。如果您手机上确实存有联系人信息,则可能是某些数据源不允许您从手机中导出联系人信息。"
+ "vCard 制作程序未正确启动。"
+ "无法导出"
+ "未导出联系人数据。\n原因:“%s ”"
+ "正在导入%s "
+ "无法读取 vCard 数据"
+ "已取消读取 vCard 数据"
+ "已顺利导入 vCard %s "
+ "已取消导入“%s ”"
+ "“%s ”将在稍后导入。"
+ "该文件将在稍后导入。"
+ "vCard 导入请求遭拒,请稍后重试。"
+ "“%s ”将在稍后导出。"
+ "该文件将在稍后导出。"
+ "vCard 导出请求遭拒,请稍后重试。"
+ "联系人"
+ "正在将 vCard 缓存到本地临时存储空间。实际导入操作即将开始。"
+ "无法导入 vCard。"
+ "未在SD卡上找到 vCard 文件。"
+ "通过NFC收到的联系人"
+ "要导出联系人吗?"
+ "正在缓存"
+ "无法扫描SD卡(原因:“%s ”)。"
+ "正在导入 - %s /%s :%s "
+ "导出为 .vcf 文件"
+ "排序方式"
+ "名字"
+ "姓氏"
+ "姓名格式"
+ "名字在前"
+ "姓氏在前"
+ "分享所显示的联系人"
+ "无法分享所显示的联系人。"
+ "导入/导出联系人"
+ "导入联系人"
+ "无法分享此联系人。"
+ "搜索"
+ "要显示的联系人"
+ "要显示的联系人"
+ "自定义视图"
+ "查找联系人"
+ "收藏"
+ "没有联系人。"
+ "没有可显示的联系人。"
+ "没有收藏的联系人。"
+ "%s 中没有联系人"
+ "清除常用联系人"
+ "选择SIM卡"
+ "帐户"
+ "导入/导出"
+ "来源:%1$s "
+ "%1$s ,来源:%2$s "
+ "停止搜索"
+ "清除搜索内容"
+ "联系人显示选项"
+ "帐户"
+ "一律使用这张卡进行通话"
+ "用于外拨电话的帐户"
+ "拨打电话时发送记事"
+ "输入可在拨打电话时发送的记事…"
+ "发送记事并拨打电话"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-zh-rHK/strings.xml b/ContactsCommon/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..4acfab3
--- /dev/null
+++ b/ContactsCommon/res/values-zh-rHK/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "文字已複製"
+ "複製到剪貼簿"
+ "撥打%s 電話號碼"
+ "撥打住家電話號碼"
+ "撥打手機號碼"
+ "撥打公司電話號碼"
+ "撥打公司傳真號碼"
+ "撥打住家傳真號碼"
+ "撥打傳呼機號碼"
+ "撥號"
+ "撥打回撥號碼"
+ "撥打車用電話號碼"
+ "撥打公司總機號碼"
+ "撥打 ISDN 電話號碼"
+ "撥打總機號碼"
+ "撥打傳真號碼"
+ "撥打電台號碼"
+ "撥打 Telex 電話號碼"
+ "撥打 TTY/TDD 電話號碼"
+ "撥打公司手機號碼"
+ "撥打公司傳呼機號碼"
+ "撥打%s 的電話號碼"
+ "撥打 MMS 電話號碼"
+ "傳送短訊至%s 電話"
+ "傳送短訊至住家電話"
+ "傳送短訊至手機"
+ "傳送短訊至公司電話"
+ "傳送短訊至公司傳真"
+ "傳送短訊至住家傳真"
+ "傳送短訊至傳呼機"
+ "傳送短訊"
+ "傳送短訊至回撥號碼"
+ "傳送短訊至車用電話"
+ "傳送短訊至公司總機"
+ "傳送短訊至 ISDN"
+ "傳送短訊至總機"
+ "傳送短訊至傳真"
+ "傳送短訊至電台"
+ "傳送短訊至 Telex"
+ "傳送短訊至 TTY/TDD"
+ "傳送短訊至公司手機"
+ "傳送短訊至公司傳呼機"
+ "傳送短訊至%s 的電話"
+ "傳送短訊至 MMS 電話號碼"
+ "進行視像通話"
+ "清除常用聯絡人?"
+ "您將清除「通訊錄」應用程式和「電話」應用程式中的常用聯絡人名單,並強制電子郵件應用程式重新取得您的寄件喜好設定。"
+ "正在清除常用聯絡人…"
+ "在線"
+ "離開"
+ "忙碌"
+ "通訊錄"
+ "其他"
+ "名錄"
+ "所有聯絡人"
+ "我本人"
+ "正在搜尋..."
+ "找到超過 %d 位聯絡人。"
+ "沒有聯絡人"
+
+ - 找到
%d 位聯絡人
+ - 找到 1 位聯絡人
+
+ "快速聯絡%1$s "
+ "(沒有名稱)"
+ "經常通話的聯絡人"
+ "常用聯絡人"
+ "查看聯絡人資料"
+ "所有附有電話號碼的聯絡人"
+ "瀏覽更新資訊"
+ "只限手機,不會保持同步"
+ "姓名"
+ "暱稱"
+ "姓名"
+ "名字"
+ "姓氏"
+ "姓名前稱謂"
+ "中間名"
+ "姓名後稱謂"
+ "姓名拼音"
+ "名字拼音"
+ "中間名 (拼音)"
+ "姓氏拼音"
+ "電話"
+ "電郵地址"
+ "地址"
+ "即時通訊"
+ "機構"
+ "關係"
+ "特別日子"
+ "傳送短訊"
+ "地址"
+ "公司"
+ "職稱"
+ "附註"
+ "SIP"
+ "網站"
+ "群組"
+ "傳送電郵至住家信箱"
+ "傳送電郵至手機"
+ "傳送電郵至公司信箱"
+ "傳送電郵"
+ "傳送電郵至%s 的信箱"
+ "傳送電郵"
+ "街道"
+ "郵政信箱"
+ "社區"
+ "城市"
+ "州/省"
+ "郵遞區號"
+ "國家/地區"
+ "查看住家地址"
+ "查看公司地址"
+ "查看地址"
+ "查看%s 地址"
+ "使用 AIM 進行即時通訊"
+ "使用 Windows Live 進行即時通訊"
+ "使用 Yahoo 進行即時通訊"
+ "使用 Skype 進行即時通訊"
+ "使用 QQ 進行即時通訊"
+ "使用 Google Talk 進行即時通訊"
+ "使用 ICQ 進行即時通訊"
+ "使用 Jabber 進行即時通訊"
+ "即時通訊"
+ "刪除"
+ "展開或收合名稱欄位"
+ "所有聯絡人"
+ "已加星號"
+ "自訂"
+ "聯絡人"
+ "所有其他聯絡人"
+ "所有聯絡人"
+ "移除同步群組"
+ "新增同步群組"
+ "更多群組…"
+ "如果從同步設定中移除「%s 」群組,也會移除任何未分組的聯絡人。"
+ "正在儲存顯示選項…"
+ "完成"
+ "取消"
+ "%s 中的聯絡人"
+ "聯絡人自訂檢視"
+ "單一聯絡人"
+ "在帳戶中建立聯絡人"
+ "從 SIM 卡匯入"
+ "從 SIM 卡匯入 ^1 - ^2 "
+ "從 SIM 卡匯入 %1$s "
+ "匯入 .vcf 檔案"
+ "要取消匯入 %s 嗎?"
+ "要取消匯出 %s 嗎?"
+ "無法取消匯入/匯出 vCard"
+ "不明錯誤。"
+ "無法開啟 %s :「%s 」。"
+ "無法啟動匯出程式:「%s 」。"
+ "沒有聯絡人資料可以匯出。"
+ "您已停用所需權限。"
+ "匯出時發生錯誤:「%s 」。"
+ "要求的檔案名稱過長 (「%s 」)。"
+ "SD 記憶卡中的 vCard 檔案過多。"
+ "I/O 錯誤"
+ "記憶體不足,檔案可能過大。"
+ "由於未預期的原因,無法剖析 vCard。"
+ "不支援此格式。"
+ "無法從指定的 vCard 檔案收集中繼資料。"
+ "無法匯入一個或多個檔案 (%s)。"
+ "已完成匯出 %s 。"
+ "已匯出聯絡人。"
+ "已取消匯出 %s 。"
+ "正在匯出聯絡人資料"
+ "正在將您的聯絡人資料匯出至:%s 。"
+ "無法取得資料庫資訊。"
+ "沒有聯絡人資料可以匯出。如果您的手機中確實存有聯絡人資料,則可能是部分資料提供者不允許您將聯絡人資料從手機中匯出。"
+ "vCard 編輯器並未正確啟動。"
+ "無法匯出"
+ "聯絡人資料未匯出。\n原因:「%s 」"
+ "正在匯入 %s "
+ "無法讀取 vCard 資料"
+ "已取消讀取 vCard 資料的操作"
+ "已完成匯入 vCard %s "
+ "已取消匯入 %s "
+ "%s 將在稍後匯入。"
+ "稍後即將匯入檔案。"
+ "vCard 匯入要求已被拒,請稍後再試。"
+ "%s 將在稍後匯出。"
+ "檔案即將匯出。"
+ "vCard 匯出要求已被拒,請稍後再試。"
+ "聯絡人"
+ "正在將 vCard 資料快取至本機暫存空間,隨即將開始實際的匯入操作。"
+ "無法匯入 vCard。"
+ "在 SD 記憶卡上找不到 vCard 檔案。"
+ "已透過 NFC 收到聯絡人資料"
+ "要匯出聯絡人資料嗎?"
+ "快取中"
+ "無法掃瞄 SD 記憶卡 (原因:「%s 」)。"
+ "正在匯入第 %s 個:%s ,共 %s 個"
+ "匯出至 .vcf 檔案"
+ "排序方式"
+ "名字"
+ "姓氏"
+ "姓名格式"
+ "名字在前"
+ "姓氏在前"
+ "分享正常顯示的聯絡人"
+ "無法分享顯示的聯絡人"
+ "匯入/匯出聯絡人資料"
+ "匯入聯絡人資料"
+ "無法分享這位聯絡人的資料。"
+ "搜尋"
+ "要顯示的聯絡人"
+ "要顯示的聯絡人"
+ "定義自訂檢視"
+ "尋找聯絡人"
+ "最愛的聯絡人"
+ "沒有聯絡人。"
+ "沒有可顯示的聯絡人。"
+ "沒有最愛的聯絡人。"
+ "「%s 」中沒有聯絡人"
+ "清除常用聯絡人"
+ "選取 SIM 卡"
+ "帳戶"
+ "匯入/匯出"
+ "透過 %1$s "
+ "%1$s (透過 %2$s )"
+ "停止搜尋"
+ "清除搜尋"
+ "聯絡人顯示選項"
+ "帳戶"
+ "永遠使用這張 SIM 卡通話"
+ "選取用於撥號的 SIM 卡:"
+ "撥號時傳送筆記"
+ "撥號時可以書寫和傳送筆記…"
+ "傳送和撥號"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-zh-rTW/strings.xml b/ContactsCommon/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..fa35d1e
--- /dev/null
+++ b/ContactsCommon/res/values-zh-rTW/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "文字已複製"
+ "複製到剪貼簿"
+ "撥打%s 電話號碼"
+ "撥打住家電話號碼"
+ "撥打手機號碼"
+ "撥打公司電話號碼"
+ "撥打公司傳真號碼"
+ "撥打住家傳真號碼"
+ "撥打呼叫器號碼"
+ "撥號"
+ "撥打回撥號碼"
+ "撥打車用電話號碼"
+ "撥打公司代表號"
+ "撥打 ISDN 電話號碼"
+ "撥打代表號"
+ "撥打傳真號碼"
+ "撥打無線電號碼"
+ "撥打電報電話號碼"
+ "撥打 TTY/TDD 電話號碼"
+ "撥打公司手機號碼"
+ "撥打公司呼叫器號碼"
+ "撥打%s 電話號碼"
+ "撥打 MMS 電話號碼"
+ "傳送簡訊至%s 電話"
+ "傳送簡訊至住家電話"
+ "傳送簡訊至手機"
+ "傳送簡訊至公司電話"
+ "傳送簡訊至公司傳真"
+ "傳送簡訊至住家傳真"
+ "傳送簡訊至呼叫器"
+ "傳送簡訊"
+ "傳送簡訊至回撥號碼"
+ "傳送簡訊至車用電話"
+ "傳送簡訊至公司代表號"
+ "傳送簡訊至 ISDN 電話"
+ "傳送簡訊至代表號"
+ "傳送簡訊至傳真"
+ "傳送簡訊至無線電"
+ "傳送簡訊至電報電話"
+ "傳送簡訊至 TTY/TDD 電話"
+ "傳送簡訊至公司手機"
+ "傳送簡訊至公司呼叫器"
+ "傳送簡訊至%s 電話"
+ "傳送簡訊至 MMS 電話"
+ "進行視訊通話"
+ "清除常用聯絡人?"
+ "您即將清除「聯絡人」應用程式和「電話」應用程式中的常用聯絡人清單,並設定讓電子郵件應用程式重新熟悉您的寄件偏好設定。"
+ "正在清除常用聯絡人…"
+ "線上"
+ "離開"
+ "忙碌"
+ "聯絡人"
+ "其他"
+ "目錄"
+ "所有聯絡人"
+ "我"
+ "搜尋中…"
+ "找到 %d 位以上的聯絡人。"
+ "沒有聯絡人"
+
+ - 找到
%d 位聯絡人
+ - 找到 1 位聯絡人
+
+ "%1$s 的快速聯絡人相片"
+ "(無姓名)"
+ "經常通話"
+ "經常聯絡"
+ "查看聯絡人"
+ "所有包含電話號碼的聯絡人資訊"
+ "查看最新動態"
+ "僅儲存在手機中,不同步"
+ "姓名"
+ "暱稱"
+ "姓名"
+ "名字"
+ "姓氏"
+ "姓名前稱銜"
+ "中間名"
+ "姓名後稱銜"
+ "姓名拼音"
+ "名字拼音"
+ "中間名拼音"
+ "姓氏拼音"
+ "電話號碼"
+ "電子郵件"
+ "地址"
+ "即時訊息"
+ "機構"
+ "關係"
+ "特別的日子"
+ "簡訊"
+ "地址"
+ "公司"
+ "職稱"
+ "附註"
+ "SIP"
+ "網站"
+ "群組"
+ "傳送電子郵件至住家電子郵件地址"
+ "傳送電子郵件至行動裝置"
+ "傳送電子郵件至公司電子郵件地址"
+ "電子郵件"
+ "傳送電子郵件至「%s 」"
+ "傳送電子郵件"
+ "街"
+ "郵政信箱"
+ "鄰"
+ "縣市鄉鎮"
+ "州"
+ "郵遞區號"
+ "國家/地區"
+ "檢視住家地址"
+ "檢視公司地址"
+ "檢視地址"
+ "檢視%s 地址"
+ "使用 AIM 進行即時通訊"
+ "使用 Windows Live 進行即時通訊"
+ "使用 Yahoo 進行即時通訊"
+ "使用 Skype 進行即時通訊"
+ "使用 QQ 進行即時通訊"
+ "使用 Google Talk 進行即時通訊"
+ "使用 ICQ 進行即時通訊"
+ "使用 Jabber 進行即時通訊"
+ "即時通訊"
+ "刪除"
+ "展開或收合名稱欄位"
+ "所有聯絡人"
+ "已加星號"
+ "自訂"
+ "聯絡人"
+ "所有其他聯絡人"
+ "所有聯絡人"
+ "移除同步處理群組"
+ "新增同步處理群組"
+ "更多群組…"
+ "如果停用「%s 」群組的同步處理設定,也會停止同步處理任何未分組的聯絡人。"
+ "正在儲存顯示選項…"
+ "完成"
+ "取消"
+ "%s 中的聯絡人"
+ "聯絡人自訂檢視"
+ "單一聯絡人"
+ "在帳戶下建立聯絡人"
+ "從 SIM 卡匯入"
+ "從 SIM 卡 ^1 - ^2 匯入"
+ "從 SIM 卡 %1$s 匯入"
+ "從 .vcf 檔案匯入"
+ "確定要取消匯入 %s ?"
+ "確定要取消匯出 %s ?"
+ "無法取消匯入/匯出 vCard"
+ "未知的錯誤。"
+ "無法開啟「%s 」:%s 。"
+ "無法啟動匯出程式:「%s 」。"
+ "沒有可匯出的聯絡人。"
+ "必要權限已停用。"
+ "匯出時發生錯誤:「%s 」。"
+ "要求的檔案名稱過長 (「%s 」)。"
+ "SD 卡上的 vCard 檔案過多。"
+ "I/O 錯誤"
+ "記憶體不足,檔案可能過大。"
+ "由於意外因素,導致無法剖析 vCard。"
+ "不支援此格式。"
+ "無法從指定的 vCard 檔案收集中繼資料。"
+ "無法匯入一或多個檔案 (%s)。"
+ "已完成 %s 匯出作業。"
+ "匯出聯絡人完成。"
+ "已取消匯出 %s 。"
+ "正在匯出聯絡人資料"
+ "正在將您的聯絡人資料匯出至以下檔案:%s 。"
+ "無法取得資料庫資訊。"
+ "沒有可匯出的聯絡人。如果您的手機中確實有聯絡人資料,那麼可能是部分資料提供者不允許您將聯絡人資料從手機中匯出。"
+ "vCard 編輯器並未正確啟動。"
+ "無法匯出"
+ "聯絡人資料未匯出。\n原因:「%s 」"
+ "正在匯入 %s "
+ "無法讀取 vCard 資料"
+ "已取消讀取 vCard 資料"
+ "已完成匯入 vCard 的 %s "
+ "已取消匯入 %s "
+ "%s 將在稍後匯入。"
+ "稍後即將匯入該檔案。"
+ "vCard 匯入要求遭到拒絕,請稍後再試。"
+ "%s 將在稍後匯出。"
+ "稍後即將匯出該檔案。"
+ "vCard 匯出要求遭到拒絕,請稍後再試。"
+ "聯絡人"
+ "正在將 vCard 資料快取至本機暫存空間,隨即將啟動實際的匯入作業。"
+ "無法匯入 vCard。"
+ "在 SD 卡上找不到 vCard 檔案。"
+ "已透過 NFC 收到聯絡人資訊"
+ "確定要匯出聯絡人?"
+ "快取中"
+ "無法掃描 SD 卡 (原因:「%s 」)。"
+ "正在匯入第 %s 筆資料 (共 %s 筆):%s "
+ "匯出成 .vcf 檔案"
+ "排序依據"
+ "名字"
+ "姓氏"
+ "姓名格式"
+ "名字在前"
+ "姓氏在前"
+ "分享正常顯示的聯絡人"
+ "無法分享目前顯示的聯絡人資料。"
+ "匯入/匯出聯絡人"
+ "匯入聯絡人"
+ "無法分享這位聯絡人的資料。"
+ "搜尋"
+ "要顯示的聯絡人"
+ "要顯示的聯絡人"
+ "定義自訂檢視"
+ "尋找聯絡人"
+ "最愛的聯絡人"
+ "沒有聯絡人。"
+ "沒有可顯示的聯絡人。"
+ "沒有最愛的聯絡人。"
+ "「%s 」中沒有聯絡人"
+ "清除常用聯絡人"
+ "選取 SIM 卡"
+ "帳戶"
+ "匯入/匯出"
+ "透過 %1$s "
+ "%1$s (透過 %2$s )"
+ "停止搜尋"
+ "清除搜尋"
+ "聯絡人顯示選項"
+ "帳戶"
+ "一律使用這張 SIM 卡通話"
+ "選擇通話帳戶"
+ "撥號時傳送備註"
+ "輸入可在撥號時傳送的備註..."
+ "傳送並撥號"
+ "%1$s /%2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values-zu/strings.xml b/ContactsCommon/res/values-zu/strings.xml
new file mode 100644
index 0000000..ac766ac
--- /dev/null
+++ b/ContactsCommon/res/values-zu/strings.xml
@@ -0,0 +1,253 @@
+
+
+
+
+ "Umbhalo okopishiwe"
+ "Kopisha kubhodi lokunamathisela"
+ "Fonela ku-%s "
+ "Fonela ekhaya"
+ "Fonela iselula"
+ "Fonela emsebenzini"
+ "Fonela ifeksi yasemsebenzini"
+ "Fonela ifeksi yasekhaya"
+ "Fonela isicingo"
+ "Fonela"
+ "Fonela ukufonela emuva"
+ "Fonela imoto"
+ "Fonela isisekelo senkampani"
+ "Fonela i-ISDN"
+ "Fonela isisekelo"
+ "Fonela ifeksi"
+ "Fonela umsakazo"
+ "Fonela i-telex"
+ "Fonela i-TTY/TDD"
+ "Fonela iselula yasemsebenzini"
+ "Fonela isicingo sasemsebenzini"
+ "Fonela %s "
+ "Fonela i-MMS"
+ "Bhalela ku-%s "
+ "Bhalela ekhaya"
+ "Bhalela iselula"
+ "Bhalela emsebenzini"
+ "Bhalela ifeksi yasemsebenzini"
+ "Bhalela ifeksi yasekhaya"
+ "Bhalela isicingo"
+ "Umbhalo"
+ "Bhalela ukufonela emuva"
+ "Bhalela imoto"
+ "Bhalela isisekelo senkampani"
+ "Bhalela i-ISDN"
+ "Bhalela isisekelo"
+ "Bhalela ifeksi"
+ "Bhalela umsakazo"
+ "Bhalela i-telex"
+ "Bhalela i-TTY/TDD"
+ "Bhalela iselula yasemsebenzini"
+ "Bhalela isicingo sasemsebenzini"
+ "Bhalela ku-%s "
+ "Bhala i-MMS"
+ "Yenza ikholi yevidiyo"
+ "Sula oxhumana nabo njalo?"
+ "Uzosula uhlu loxhumana nabo kakhulu kuzinhelo zokusebenza zokuxhumana noma zefoni, futhi uphoqelele izinhlelo zokusebenza ze-imeyili ukufunda izintandokazi zakho zekheli kusuka ekuqaleni."
+ "Isula oxhumana nabo njalo…"
+ "Ngiyatholakala"
+ "Ngiphumile"
+ "Ngimatasa"
+ "Oxhumana nabo"
+ "Okunye"
+ "Uhla lwemibhalo"
+ "Bonke oxhumana nabo"
+ "Mina"
+ "Iyasesha…"
+ "Abangaphezu kuka-%d abatholakele."
+ "Abekho oxhumana nabo"
+
+ %d abatholakele
+ %d abatholakele
+
+ "Oxhumene naye ngokushesha ku-%1$s "
+ "(Alikho igama)"
+ "Abashayelwa njalo"
+ "Abathintwa njalo"
+ "Buka oxhumana naye"
+ "Bonke othintana nabo kanye nezinombolo zabo zefoni"
+ "Buka okubuyekeziwe"
+ "Iselula kuphela, ayivumelanisiwe"
+ "Igama"
+ "Isidlaliso"
+ "Igama"
+ "Igama lokuqala"
+ "Isibongo"
+ "Isiqalo segama"
+ "Igama eliphakathi"
+ "Isijobelelo segama"
+ "Igama lefonethikhi"
+ "Igama lokuqala lefonethiki"
+ "Igama lefonethikhi eliphakakathi"
+ "Isibongo sefonethiki"
+ "Ifoni"
+ "I-imeyili"
+ "Ikheli"
+ "I-IM"
+ "Inhlangano"
+ "Ubudlelwane"
+ "Amadethi abalulekile"
+ "Umlayezo wombhalo"
+ "Ikheli"
+ "Inkampani"
+ "Uhlobo lomsebenzi"
+ "Amanothi"
+ "SIP"
+ "Iwebhusayithi"
+ "Izigcawu"
+ "Thumela i-imeyili ekhaya"
+ "Thumela i-imeyili kuselula"
+ "Thumela i-imeyili emsebenzini"
+ "I-imeyili"
+ "I-imeyili %s "
+ "I-imeyili"
+ "Umgwaqo"
+ "PO box"
+ "Indawo yasekhaya"
+ "Idolobha"
+ "Isifunda"
+ "Ikhodi ye-ZIP"
+ "Izwe"
+ "Buka ikheli lasekhaya"
+ "Buka ikheli lasemsebenzini"
+ "Buka ikheli"
+ "Buka ikheli le-%s "
+ "Xoxa usebenzisa i-AIM"
+ "Xoxa usebenzisa i-Windows Live"
+ "Xoxa usebenzisa i-Yahoo"
+ "Xoxa usebenzisa i-Skype"
+ "Xoxa usebenzisa i-QQ"
+ "Xoxa usebenzisa i-Google Talk"
+ "Xoxa usebenzisa i-ICQ"
+ "Xoxa usebenzisa i-Jabber"
+ "Xoxa"
+ "susa"
+ "Nweba noma goqa izinkambu zegama"
+ "Bonke oxhumana nabo"
+ "Okufakwe omakhanya"
+ "Enza ngendlela oyifisayo"
+ "Oxhumana naye"
+ "Bonke abanye oxhumana nabo"
+ "Bonke oxhumana nabo"
+ "Susa iqembu lokuvumelanisa"
+ "Engeza iqembu lokuvumelanisa"
+ "Amaqembu amaningi..."
+ "Isusa i-\"%s \" kokuvumelanisiwe izophinde isuse nanoma yibaphi oxhumana nabo abangekho eqenjini kusukela ekuvumelanisaneni."
+ "Ilondoloza izinketho zokubonisa"
+ "Kwenziwe"
+ "Khansela"
+ "Oxhumana nabo ku-%s "
+ "Oxhumana nabo ekubukweni okwenziwe ngendlela oyifisayo"
+ "Oyedwa oxhumana naye"
+ "Dala oxhumana naye ngaphansi kwe-akhawunti"
+ "Landa kusuka kwikhadi le-SIM"
+ "Ngenisa kusuka ku-SIM ^1 - ^2 "
+ "Ngenisa kusuka ku-SIM %1$s "
+ "Ngenisa kusukela kufayela le-.vcf"
+ "Khansela ukulandwa kwe-%s ?"
+ "Khansela ukuthunyelwa kwe-%s ?"
+ "Yehlulekile ukukhansela ukulanda/ukuthumela i-vCard"
+ "Iphutha elingaziwa"
+ "Ayikwazi ukuvula i-\"%s \": %s "
+ "Ayikwazanga ukuqalisa isithumeli: \"%s \""
+ "Abekho oxhumana nabo abathumelekayo."
+ "Ukhubaze imvume edingekayo."
+ "Kube khona iphutha ngesikhathi kuthunyelwa: \"%s \"."
+ "Igama lefayela elidingekayo lide kakhulu (\"%s \")"
+ "Maningi kakhulu amafayela e-vCard ekhadini le-SD."
+ "Iphutha le-I/O"
+ "Imemori enganele. Ifayela kungenzeka likhulu kakhulu."
+ "Yehlulekile ukunqunta i-vCard ngokwesizathu esingalindelekile"
+ "Ifomethi ayisekelwe."
+ "Yehlulekile ukuqoqa ulwazi lwemetha noma amafayela we-vCard."
+ "Ifayela elilodwa noma amafayela angaphezulu ehlulekile ukulandwa (%s)."
+ "Iqedile ukuthumela i-%s "
+ "Iqedile ukukhipha oxhumana nabo."
+ "Ukulandwa kwe-%s kukhanseliwe."
+ "Kuthunyelwa idatha yoxhumana naye"
+ "Idatha yoxhumana naye ithunyelelwa lapha: %s ."
+ "Yehlulekile ukuthola ulwazi lwemininingo egciniwe."
+ "Abekho oxhumana nabo abathumelekayo. Uma unabo oxhumana nabo kufoni yakho, abanye abahlinzeki bedatha kungenzeka bangavumeli labo oxhumana nabo ukuthi bathunyelwe kusukela kufoni."
+ "Umqambi we-Vcard akazange aqale ngendlela efanele."
+ "Yehlulekile ukuthumela"
+ "Idatha yoxhumana naye ayizange ithunyelwe.\nIsizathu: \"%s \""
+ "Ilanda i-%s "
+ "Yehlulekile ukufunda idatha ye-vCard"
+ "Ukufundwa kwe-vCard kukhanseliwe"
+ "Iqedile ukulanda i-vCard %s "
+ "Ukulanda i-%s kukhanseliwe"
+ "%s izothunyelwa maduzane."
+ "Ifayela lizothunyelwa maduzane."
+ "Isicelo sokungenisa i-vCard sinqatshelwe. Zama futhi emuva kwesikhathi."
+ "%s izothunyelwa maduzane"
+ "Ifayela lizokhishwa maduze."
+ "Isicelo sokuthumela i-vCard sinqatshelwe. Sicela uzame futhi emuva kwesikhathi."
+ "oxhumana naye"
+ "Ifaka kunqolobane ama-vCard kusitoreji sesikhashana. Ukulandwa kwangempela kuzoqala khona maduze."
+ "Yehlulekile ukulanda i-vCard"
+ "Alikho ifayela le-vCard elitholakele ekhadini le-SD."
+ "Othintana naye utholakale nge-NFC"
+ "Thumela oxhumana nabo?"
+ "Ukulondoloza isikhashana"
+ "Ikhadi le-SD aliskenekanga. (Isizathu: \"%s \")"
+ "Ilanda i-%s %s %s "
+ "Thumela kufayela le-.vcf"
+ "Hlunga nge-"
+ "Igama lokuqala"
+ "Isibongo"
+ "Ifomethi yegama"
+ "Igama lokuqala kuqala"
+ "Isibongo kuqala"
+ "Yabelana noxhumana nabo ababonakalayo"
+ "Yehlulekile ukwabelana ngoxhumana nabo ababonakalayo"
+ "Landa/thumela oxhumana nabo"
+ "Landa oxhumana nabo"
+ "Lona oxhumana naye ngeke ukwazi ukwabelana ngaye."
+ "Sesha"
+ "Othintana nabo abazoboniswa"
+ "Othintana nabo abazoboniswa"
+ "Cacisa ukubuka ngokwezifisio"
+ "Thola othintana nabo"
+ "Izindandokazi"
+ "Abekho othintana nabo."
+ "Abekho othintana nabo ababonakalayo."
+ "Azikho izintandokazi."
+ "Abekho othintana nabo ku-%s "
+ "Sula oxhumana nabo njalo"
+ "Khetha ikhadi le-SIM"
+ "Ama-akhawunti"
+ "Ngenisa/ thekelisa"
+ "nge-%1$s "
+ "%1$s nge-%2$s "
+ "misa ukusesha"
+ "Sula usesho"
+ "Izinketho zokubonisa oxhumana naye"
+ "I-Akhawunti"
+ "Njalo sebenzisa lokhu kumakholi"
+ "Shaya nge"
+ "Shaya ngenothi"
+ "Thayipha inothi ukuthumela nekholi ..."
+ "THUMELA FUTHI USHAYE"
+ "%1$s / %2$s "
+ "%1$s • %2$s "
+
diff --git a/ContactsCommon/res/values/animation_constants.xml b/ContactsCommon/res/values/animation_constants.xml
new file mode 100644
index 0000000..9e59194
--- /dev/null
+++ b/ContactsCommon/res/values/animation_constants.xml
@@ -0,0 +1,19 @@
+
+
+
+ 250
+
diff --git a/ContactsCommon/res/values/attrs.xml b/ContactsCommon/res/values/attrs.xml
new file mode 100644
index 0000000..64397ca
--- /dev/null
+++ b/ContactsCommon/res/values/attrs.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/values/colors.xml b/ContactsCommon/res/values/colors.xml
new file mode 100644
index 0000000..0dc85b5
--- /dev/null
+++ b/ContactsCommon/res/values/colors.xml
@@ -0,0 +1,169 @@
+
+
+
+
+ #eeeeee
+
+ #44ff0000
+
+
+ #a0ffffff
+
+
+ #363636
+
+ @color/dialtacts_secondary_text_color
+
+
+ #888888
+
+
+ #AAAAAA
+
+
+ #D0D0D0
+
+
+ #363636
+
+
+ #0288d1
+
+
+ #DDDDDD
+
+
+ #333333
+
+
+ #737373
+
+
+ #7F000000
+
+
+ #CCCCCC
+
+ #7f000000
+
+ #fff
+ #000
+
+
+
+ - #DB4437
+ - #E91E63
+ - #9C27B0
+ - #673AB7
+ - #3F51B5
+ - #4285F4
+ - #039BE5
+ - #0097A7
+ - #009688
+ - #0F9D58
+ - #689F38
+ - #EF6C00
+ - #FF5722
+ - #757575
+
+
+
+
+ - #C53929
+ - #C2185B
+ - #7B1FA2
+ - #512DA8
+ - #303F9F
+ - #3367D6
+ - #0277BD
+ - #006064
+ - #00796B
+ - #0B8043
+ - #33691E
+ - #E65100
+ - #E64A19
+ - #424242
+
+
+
+ #607D8B
+
+ #455A64
+
+
+ #cccccc
+
+ #ffffff
+
+
+ #0fc6dc
+
+ #ffffff
+
+ #008aa1
+
+ @color/tab_accent_color
+ #ffffff
+ @color/tab_accent_color
+
+
+ @color/actionbar_background_color
+
+
+ #ffffff
+ #a6ffffff
+
+
+ #000000
+
+ #ffffff
+
+ #737373
+ @color/searchbox_hint_text_color
+
+ @color/dialtacts_theme_color
+
+
+ #f9f9f9
+ #FFFFFF
+
+
+ #d1041c
+
+
+ #000000
+
+
+ #d8d8d8
+
+
+ #00c853
+
+
+ #ffffff
+
diff --git a/ContactsCommon/res/values/dimens.xml b/ContactsCommon/res/values/dimens.xml
new file mode 100644
index 0000000..8d612ff
--- /dev/null
+++ b/ContactsCommon/res/values/dimens.xml
@@ -0,0 +1,164 @@
+
+
+
+
+
+ 0dip
+
+
+ 32dip
+
+ 18dp
+ 8dp
+
+
+ 23dip
+
+ 16dip
+
+
+ 16dip
+
+
+
+ 48sp
+
+
+ 0dip
+
+
+ 16dip
+ 16dip
+ 48dip
+ 32dip
+
+
+ 32dip
+
+ 16dip
+ @dimen/list_visible_scrollbar_padding
+ 8dip
+
+ 48dp
+
+
+ 0dip
+
+
+ 12dp
+
+
+ 1dp
+
+
+ 32dip
+
+
+ 48dip
+
+
+ 16sp
+ 40dp
+ 15dp
+ 12dp
+
+
+ 20sp
+ 10dip
+
+
+ 40dp
+ 20dp
+ 1dp
+ - 67%
+
+
+ 56dp
+
+ 56dp
+
+ 28dp
+
+ 8dp
+
+ 88dp
+
+ 16dp
+
+ 16dp
+
+ 4dp
+
+
+ 2dp
+
+ 14sp
+ 2dp
+
+
+ 4dp
+
+ 28dp
+
+ 56dp
+
+ 16dp
+
+ 14dp
+
+ 15dp
+
+ 20sp
+
+
+ 16dp
+
+ 57dp
+
+ 24sp
+
+
+ 40dp
+
+ 2dp
+
+
+ 20dp
+
+ 8dp
+
+ 50dp
+
+ 60dp
+
+ 16sp
+
+ 14sp
+
+ 15dp
+
diff --git a/ContactsCommon/res/values/donottranslate_config.xml b/ContactsCommon/res/values/donottranslate_config.xml
new file mode 100644
index 0000000..9d9513b
--- /dev/null
+++ b/ContactsCommon/res/values/donottranslate_config.xml
@@ -0,0 +1,80 @@
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ default
+
+
+ default
+
+
+
+
+
+
+
+
+ vcf
+
+
+ contacts.vcf
+
+
+ 1
+
+
+ 99999
+
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
diff --git a/ContactsCommon/res/values/ids.xml b/ContactsCommon/res/values/ids.xml
new file mode 100644
index 0000000..4ba65e1
--- /dev/null
+++ b/ContactsCommon/res/values/ids.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/values/integers.xml b/ContactsCommon/res/values/integers.xml
new file mode 100644
index 0000000..f3d1e74
--- /dev/null
+++ b/ContactsCommon/res/values/integers.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ 2
+ 3
+
+
+ 30
+
+
+ 0
+
+ 0
+
+
+ 250
+
diff --git a/ContactsCommon/res/values/strings.xml b/ContactsCommon/res/values/strings.xml
new file mode 100644
index 0000000..1942b6f
--- /dev/null
+++ b/ContactsCommon/res/values/strings.xml
@@ -0,0 +1,773 @@
+
+
+
+
+
+ Text copied
+
+ Copy to clipboard
+
+
+ Call
+ %s
+
+
+ Call home
+
+ Call mobile
+
+ Call work
+
+ Call work fax
+
+ Call home fax
+
+ Call pager
+
+ Call
+
+ Call callback
+
+ Call car
+
+ Call company main
+
+ Call ISDN
+
+ Call main
+
+ Call fax
+
+ Call radio
+
+ Call telex
+
+ Call TTY/TDD
+
+ Call work mobile
+
+ Call work pager
+
+ Call
+ %s
+
+
+ Call MMS
+
+
+ Text
+ %s
+
+
+ Text home
+
+ Text mobile
+
+ Text work
+
+ Text work fax
+
+ Text home fax
+
+ Text pager
+
+ Text
+
+ Text callback
+
+ Text car
+
+ Text company main
+
+ Text ISDN
+
+ Text main
+
+ Text fax
+
+ Text radio
+
+ Text telex
+
+ Text TTY/TDD
+
+ Text work mobile
+
+ Text work pager
+
+ Text
+ %s
+
+
+ Text MMS
+
+
+ Make video call
+
+
+ Clear frequently contacted?
+
+
+ You\'ll clear the frequently contacted list in the
+ Contacts and Phone apps, and force email apps to learn your addressing preferences from
+ scratch.
+
+
+
+ Clearing frequently contacted\u2026
+
+
+ Available
+
+
+ Away
+
+
+ Busy
+
+
+ Contacts
+
+
+ Other
+
+
+ Directory
+
+
+ All contacts
+
+
+ Me
+
+
+ Searching\u2026
+
+
+ More than %d found.
+
+
+ No contacts
+
+
+
+ - 1 found
+ %d found
+
+
+
+ Quick contact for %1$s
+
+
+ (No name)
+
+
+ Frequently called
+
+
+ Frequently contacted
+
+
+ View contact
+
+
+ All contacts with phone numbers
+
+
+ View updates
+
+
+
+
+ Phone-only, unsynced
+
+
+ Name
+
+
+ Nickname
+
+
+ Name
+
+ First name
+
+ Last name
+
+ Name prefix
+
+ Middle name
+
+ Name suffix
+
+
+ Phonetic name
+
+
+ Phonetic first name
+
+ Phonetic middle name
+
+ Phonetic last name
+
+
+ Phone
+
+
+ Email
+
+
+ Address
+
+
+ IM
+
+
+ Organization
+
+
+ Relationship
+
+
+ Special dates
+
+
+ Text message
+
+
+ Address
+
+
+ Company
+
+
+ Title
+
+
+ Notes
+
+
+ SIP
+
+
+ Website
+
+
+ Groups
+
+
+ Email home
+
+ Email mobile
+
+ Email work
+
+ Email
+
+ Email %s
+
+
+ Email
+
+
+ Street
+
+ PO box
+
+ Neighborhood
+
+ City
+
+ State
+
+ ZIP code
+
+ Country
+
+
+ View home address
+
+ View work address
+
+ View address
+
+ View %s address
+
+
+ Chat using AIM
+
+ Chat using Windows Live
+
+ Chat using Yahoo
+
+ Chat using Skype
+
+ Chat using QQ
+
+ Chat using Google Talk
+
+ Chat using ICQ
+
+ Chat using Jabber
+
+
+ Chat
+
+
+ delete
+
+
+ Expand or collapse name fields
+
+
+
+ All contacts
+
+
+ Starred
+
+
+ Customize
+
+
+ Contact
+
+
+ All other contacts
+
+
+ All contacts
+
+ Remove sync group
+ Add sync group
+ More groups\u2026
+
+
+ Removing \"%s \" from sync will also remove any ungrouped contacts from sync.
+
+
+ Saving display options\u2026
+
+
+ Done
+
+
+ Cancel
+
+
+ Contacts in %s
+
+
+ Contacts in custom view
+
+
+ Single contact
+
+ Create contact under account
+
+
+ Import from SIM card
+
+
+ Import from SIM ^1 - ^2
+
+
+ Import from SIM %1$s
+
+
+ Import from .vcf file
+
+
+ Cancel import of %s ?
+
+
+ Cancel export of %s ?
+
+
+ Couldn\'t cancel vCard import/export
+
+
+ Unknown error.
+
+
+ Couldn\'t open \"%s \": %s .
+
+
+ Couldn\'t start the exporter: \"%s \".
+
+
+ There is no exportable contact.
+
+
+ You have disabled a required permission.
+
+
+ An error occurred during export: \"%s \".
+
+
+ Required filename is too long (\"%s \").
+
+
+
+ Too many vCard files are on the SD card.
+
+
+ I/O error
+
+
+ Not enough memory. The file may be too large.
+
+
+ Couldn\'t parse vCard for an unexpected reason.
+
+
+ The format isn\'t supported.
+
+
+ Couldn\'t collect meta information of given vCard file(s).
+
+
+ One or more files couldn\'t be imported (%s).
+
+
+ Finished exporting %s .
+
+
+ Finished exporting contacts.
+
+
+ Exporting %s canceled.
+
+
+ Exporting contact data
+
+
+ Your contact data is being exported to: %s .
+
+
+ Couldn\'t get database information.
+
+
+
+ There are no exportable contacts. If you do have contacts on your phone, some data providers may not allow the contacts to be exported from the phone.
+
+
+ The vCard composer didn\'t start properly.
+
+
+ Couldn\'t export
+
+
+ The contact data wasn\'t exported.\nReason: \"%s \"
+
+
+ Importing %s
+
+
+ Couldn\'t read vCard data
+
+
+ Reading vCard data canceled
+
+
+ Finished importing vCard %s
+
+
+ Importing %s canceled
+
+
+ %s will be imported shortly.
+
+ The file will be imported shortly.
+
+ vCard import request was rejected. Try again later.
+
+ %s will be exported shortly.
+
+
+ The file will be exported shortly.
+
+
+ vCard export request was rejected. Try again later.
+
+ contact
+
+
+ Caching vCard(s) to local temporary storage. The actual import will start soon.
+
+
+ Couldn\'t import vCard.
+
+
+
+ No vCard file found on the SD card.
+
+
+ Contact received over NFC
+
+
+ Export contacts?
+
+
+ Caching
+
+
+
+ The SD card couldn\'t be scanned. (Reason: \"%s \")
+
+
+ Importing %s /%s : %s
+
+
+ Export to .vcf file
+
+
+
+
+ Sort by
+
+
+ First name
+
+
+ Last name
+
+
+ Name format
+
+
+ First name first
+
+
+ Last name first
+
+
+ Share visible contacts
+
+
+ Failed to share visible contacts.
+
+
+ Import/export contacts
+
+
+ Import contacts
+
+
+ This contact can\'t be shared.
+
+
+ Search
+
+
+ Contacts to display
+
+
+ Contacts to display
+
+
+ Define custom view
+
+
+ Find contacts
+
+
+ Favorites
+
+
+ No contacts.
+
+
+ No visible contacts.
+
+
+ No favorites.
+
+
+ No contacts in %s
+
+
+ Clear frequents
+
+
+ Select SIM card
+
+
+ Accounts
+
+
+ Import/export
+
+
+ sans-serif
+
+
+ via %1$s
+
+
+ %1$s via %2$s
+
+
+ sans-serif-light
+
+
+ stop searching
+
+
+ Clear search
+
+
+ sans-serif
+
+
+ Contact display options
+
+
+ Account
+
+
+ Always use this for calls
+
+
+ Call with
+
+
+ Call with a note
+
+
+ Type a note to send with call ...
+
+
+ SEND & CALL
+
+
+ %1$s / %2$s
+
+
+ %1$s • %2$s
+
+
diff --git a/ContactsCommon/res/values/styles.xml b/ContactsCommon/res/values/styles.xml
new file mode 100644
index 0000000..77c4677
--- /dev/null
+++ b/ContactsCommon/res/values/styles.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/res/xml/preference_display_options.xml b/ContactsCommon/res/xml/preference_display_options.xml
new file mode 100644
index 0000000..f327fac
--- /dev/null
+++ b/ContactsCommon/res/xml/preference_display_options.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/ContactsCommon/src/com/android/contacts/common/CallUtil.java b/ContactsCommon/src/com/android/contacts/common/CallUtil.java
new file mode 100644
index 0000000..a5587ca
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/CallUtil.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common;
+
+import com.android.contacts.common.util.PhoneNumberHelper;
+import com.android.phone.common.PhoneConstants;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.text.TextUtils;
+
+import java.util.List;
+
+/**
+ * Utilities related to calls that can be used by non system apps. These
+ * use {@link Intent#ACTION_CALL} instead of ACTION_CALL_PRIVILEGED.
+ *
+ * The privileged version of this util exists inside Dialer.
+ */
+public class CallUtil {
+
+ /**
+ * Return an Intent for making a phone call. Scheme (e.g. tel, sip) will be determined
+ * automatically.
+ */
+ public static Intent getCallWithSubjectIntent(String number,
+ PhoneAccountHandle phoneAccountHandle, String callSubject) {
+
+ final Intent intent = getCallIntent(getCallUri(number));
+ intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject);
+ if (phoneAccountHandle != null) {
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ }
+ return intent;
+ }
+
+ /**
+ * Return an Intent for making a phone call. Scheme (e.g. tel, sip) will be determined
+ * automatically.
+ */
+ public static Intent getCallIntent(String number) {
+ return getCallIntent(getCallUri(number));
+ }
+
+ /**
+ * Return an Intent for making a phone call. A given Uri will be used as is (without any
+ * sanity check).
+ */
+ public static Intent getCallIntent(Uri uri) {
+ return new Intent(Intent.ACTION_CALL, uri);
+ }
+
+ /**
+ * A variant of {@link #getCallIntent} for starting a video call.
+ */
+ public static Intent getVideoCallIntent(String number, String callOrigin) {
+ final Intent intent = new Intent(Intent.ACTION_CALL, getCallUri(number));
+ intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+ VideoProfile.STATE_BIDIRECTIONAL);
+ if (!TextUtils.isEmpty(callOrigin)) {
+ intent.putExtra(PhoneConstants.EXTRA_CALL_ORIGIN, callOrigin);
+ }
+ return intent;
+ }
+
+ /**
+ * Return Uri with an appropriate scheme, accepting both SIP and usual phone call
+ * numbers.
+ */
+ public static Uri getCallUri(String number) {
+ if (PhoneNumberHelper.isUriNumber(number)) {
+ return Uri.fromParts(PhoneAccount.SCHEME_SIP, number, null);
+ }
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
+ }
+
+ /**
+ * Determines if one of the call capable phone accounts defined supports video calling.
+ *
+ * @param context The context.
+ * @return {@code true} if one of the call capable phone accounts supports video calling,
+ * {@code false} otherwise.
+ */
+ public static boolean isVideoEnabled(Context context) {
+ TelecomManager telecommMgr = (TelecomManager)
+ context.getSystemService(Context.TELECOM_SERVICE);
+ if (telecommMgr == null) {
+ return false;
+ }
+
+ List accountHandles = telecommMgr.getCallCapablePhoneAccounts();
+ for (PhoneAccountHandle accountHandle : accountHandles) {
+ PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle);
+ if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines if one of the call capable phone accounts defined supports calling with a subject
+ * specified.
+ *
+ * @param context The context.
+ * @return {@code true} if one of the call capable phone accounts supports calling with a
+ * subject specified, {@code false} otherwise.
+ */
+ public static boolean isCallWithSubjectSupported(Context context) {
+ TelecomManager telecommMgr = (TelecomManager)
+ context.getSystemService(Context.TELECOM_SERVICE);
+ if (telecommMgr == null) {
+ return false;
+ }
+
+ List accountHandles = telecommMgr.getCallCapablePhoneAccounts();
+ for (PhoneAccountHandle accountHandle : accountHandles) {
+ PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle);
+ if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/ClipboardUtils.java b/ContactsCommon/src/com/android/contacts/common/ClipboardUtils.java
new file mode 100644
index 0000000..27af963
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/ClipboardUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+public class ClipboardUtils {
+ private static final String TAG = "ClipboardUtils";
+
+ private ClipboardUtils() { }
+
+ /**
+ * Copy a text to clipboard.
+ *
+ * @param context Context
+ * @param label Label to show to the user describing this clip.
+ * @param text Text to copy.
+ * @param showToast If {@code true}, a toast is shown to the user.
+ */
+ public static void copyText(Context context, CharSequence label, CharSequence text,
+ boolean showToast) {
+ if (TextUtils.isEmpty(text)) return;
+
+ ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ ClipData clipData = ClipData.newPlainText(label == null ? "" : label, text);
+ clipboardManager.setPrimaryClip(clipData);
+
+ if (showToast) {
+ String toastText = context.getString(R.string.toast_text_copied);
+ Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/Collapser.java b/ContactsCommon/src/com/android/contacts/common/Collapser.java
new file mode 100644
index 0000000..1ab63c5
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/Collapser.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common;
+
+import android.content.Context;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Class used for collapsing data items into groups of similar items. The data items that should be
+ * collapsible should implement the Collapsible interface. The class also contains a utility
+ * function that takes an ArrayList of items and returns a list of the same items collapsed into
+ * groups.
+ */
+public final class Collapser {
+
+ /*
+ * This utility class cannot be instantiated.
+ */
+ private Collapser() {}
+
+ /*
+ * The Collapser uses an n^2 algorithm so we don't want it to run on
+ * lists beyond a certain size. This specifies the maximum size to collapse.
+ */
+ private static final int MAX_LISTSIZE_TO_COLLAPSE = 20;
+
+ /*
+ * Interface implemented by data types that can be collapsed into groups of similar data. This
+ * can be used for example to collapse similar contact data items into a single item.
+ */
+ public interface Collapsible {
+ public void collapseWith(T t);
+ public boolean shouldCollapseWith(T t, Context context);
+
+ }
+
+ /**
+ * Collapses a list of Collapsible items into a list of collapsed items. Items are collapsed
+ * if {@link Collapsible#shouldCollapseWith(Object)} returns true, and are collapsed
+ * through the {@Link Collapsible#collapseWith(Object)} function implemented by the data item.
+ *
+ * @param list List of Objects of type > to be collapsed.
+ */
+ public static > void collapseList(List list, Context context) {
+
+ int listSize = list.size();
+ // The algorithm below is n^2 so don't run on long lists
+ if (listSize > MAX_LISTSIZE_TO_COLLAPSE) {
+ return;
+ }
+
+ for (int i = 0; i < listSize; i++) {
+ T iItem = list.get(i);
+ if (iItem != null) {
+ for (int j = i + 1; j < listSize; j++) {
+ T jItem = list.get(j);
+ if (jItem != null) {
+ if (iItem.shouldCollapseWith(jItem, context)) {
+ iItem.collapseWith(jItem);
+ list.set(j, null);
+ } else if (jItem.shouldCollapseWith(iItem, context)) {
+ jItem.collapseWith(iItem);
+ list.set(i, null);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Remove the null items
+ Iterator itr = list.iterator();
+ while (itr.hasNext()) {
+ if (itr.next() == null) {
+ itr.remove();
+ }
+ }
+
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/ContactPhotoManager.java b/ContactsCommon/src/com/android/contacts/common/ContactPhotoManager.java
new file mode 100644
index 0000000..deaf40e
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/ContactPhotoManager.java
@@ -0,0 +1,1719 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common;
+
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.media.ThumbnailUtils;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.Photo;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LruCache;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.contacts.common.util.BitmapUtil;
+import com.android.contacts.common.util.PermissionsUtil;
+import com.android.contacts.common.util.TrafficStatsTags;
+import com.android.contacts.common.util.UriUtils;
+import com.android.contacts.commonbind.util.UserAgentGenerator;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Asynchronously loads contact photos and maintains a cache of photos.
+ */
+public abstract class ContactPhotoManager implements ComponentCallbacks2 {
+ static final String TAG = "ContactPhotoManager";
+ static final boolean DEBUG = false; // Don't submit with true
+ static final boolean DEBUG_SIZES = false; // Don't submit with true
+
+ /** Contact type constants used for default letter images */
+ public static final int TYPE_PERSON = LetterTileDrawable.TYPE_PERSON;
+ public static final int TYPE_BUSINESS = LetterTileDrawable.TYPE_BUSINESS;
+ public static final int TYPE_VOICEMAIL = LetterTileDrawable.TYPE_VOICEMAIL;
+ public static final int TYPE_DEFAULT = LetterTileDrawable.TYPE_DEFAULT;
+
+ /** Scale and offset default constants used for default letter images */
+ public static final float SCALE_DEFAULT = 1.0f;
+ public static final float OFFSET_DEFAULT = 0.0f;
+
+ public static final boolean IS_CIRCULAR_DEFAULT = false;
+
+ /** Uri-related constants used for default letter images */
+ private static final String DISPLAY_NAME_PARAM_KEY = "display_name";
+ private static final String IDENTIFIER_PARAM_KEY = "identifier";
+ private static final String CONTACT_TYPE_PARAM_KEY = "contact_type";
+ private static final String SCALE_PARAM_KEY = "scale";
+ private static final String OFFSET_PARAM_KEY = "offset";
+ private static final String IS_CIRCULAR_PARAM_KEY = "is_circular";
+ private static final String DEFAULT_IMAGE_URI_SCHEME = "defaultimage";
+ private static final Uri DEFAULT_IMAGE_URI = Uri.parse(DEFAULT_IMAGE_URI_SCHEME + "://");
+
+ // Static field used to cache the default letter avatar drawable that is created
+ // using a null {@link DefaultImageRequest}
+ private static Drawable sDefaultLetterAvatar = null;
+
+ private static ContactPhotoManager sInstance;
+
+ /**
+ * Given a {@link DefaultImageRequest}, returns a {@link Drawable}, that when drawn, will
+ * draw a letter tile avatar based on the request parameters defined in the
+ * {@link DefaultImageRequest}.
+ */
+ public static Drawable getDefaultAvatarDrawableForContact(Resources resources, boolean hires,
+ DefaultImageRequest defaultImageRequest) {
+ if (defaultImageRequest == null) {
+ if (sDefaultLetterAvatar == null) {
+ // Cache and return the letter tile drawable that is created by a null request,
+ // so that it doesn't have to be recreated every time it is requested again.
+ sDefaultLetterAvatar = LetterTileDefaultImageProvider.getDefaultImageForContact(
+ resources, null);
+ }
+ return sDefaultLetterAvatar;
+ }
+ return LetterTileDefaultImageProvider.getDefaultImageForContact(resources,
+ defaultImageRequest);
+ }
+
+ /**
+ * Given a {@link DefaultImageRequest}, returns an Uri that can be used to request a
+ * letter tile avatar when passed to the {@link ContactPhotoManager}. The internal
+ * implementation of this uri is not guaranteed to remain the same across application
+ * versions, so the actual uri should never be persisted in long-term storage and reused.
+ *
+ * @param request A {@link DefaultImageRequest} object with the fields configured
+ * to return a
+ * @return A Uri that when later passed to the {@link ContactPhotoManager} via
+ * {@link #loadPhoto(ImageView, Uri, int, boolean, DefaultImageRequest)}, can be
+ * used to request a default contact image, drawn as a letter tile using the
+ * parameters as configured in the provided {@link DefaultImageRequest}
+ */
+ public static Uri getDefaultAvatarUriForContact(DefaultImageRequest request) {
+ final Builder builder = DEFAULT_IMAGE_URI.buildUpon();
+ if (request != null) {
+ if (!TextUtils.isEmpty(request.displayName)) {
+ builder.appendQueryParameter(DISPLAY_NAME_PARAM_KEY, request.displayName);
+ }
+ if (!TextUtils.isEmpty(request.identifier)) {
+ builder.appendQueryParameter(IDENTIFIER_PARAM_KEY, request.identifier);
+ }
+ if (request.contactType != TYPE_DEFAULT) {
+ builder.appendQueryParameter(CONTACT_TYPE_PARAM_KEY,
+ String.valueOf(request.contactType));
+ }
+ if (request.scale != SCALE_DEFAULT) {
+ builder.appendQueryParameter(SCALE_PARAM_KEY, String.valueOf(request.scale));
+ }
+ if (request.offset != OFFSET_DEFAULT) {
+ builder.appendQueryParameter(OFFSET_PARAM_KEY, String.valueOf(request.offset));
+ }
+ if (request.isCircular != IS_CIRCULAR_DEFAULT) {
+ builder.appendQueryParameter(IS_CIRCULAR_PARAM_KEY,
+ String.valueOf(request.isCircular));
+ }
+
+ }
+ return builder.build();
+ }
+
+ /**
+ * Adds a business contact type encoded fragment to the URL. Used to ensure photo URLS
+ * from Nearby Places can be identified as business photo URLs rather than URLs for personal
+ * contact photos.
+ *
+ * @param photoUrl The photo URL to modify.
+ * @return URL with the contact type parameter added and set to TYPE_BUSINESS.
+ */
+ public static String appendBusinessContactType(String photoUrl) {
+ Uri uri = Uri.parse(photoUrl);
+ Builder builder = uri.buildUpon();
+ builder.encodedFragment(String.valueOf(TYPE_BUSINESS));
+ return builder.build().toString();
+ }
+
+ /**
+ * Removes the contact type information stored in the photo URI encoded fragment.
+ *
+ * @param photoUri The photo URI to remove the contact type from.
+ * @return The photo URI with contact type removed.
+ */
+ public static Uri removeContactType(Uri photoUri) {
+ String encodedFragment = photoUri.getEncodedFragment();
+ if (!TextUtils.isEmpty(encodedFragment)) {
+ Builder builder = photoUri.buildUpon();
+ builder.encodedFragment(null);
+ return builder.build();
+ }
+ return photoUri;
+ }
+
+ /**
+ * Inspects a photo URI to determine if the photo URI represents a business.
+ *
+ * @param photoUri The URI to inspect.
+ * @return Whether the URI represents a business photo or not.
+ */
+ public static boolean isBusinessContactUri(Uri photoUri) {
+ if (photoUri == null) {
+ return false;
+ }
+
+ String encodedFragment = photoUri.getEncodedFragment();
+ return !TextUtils.isEmpty(encodedFragment)
+ && encodedFragment.equals(String.valueOf(TYPE_BUSINESS));
+ }
+
+ protected static DefaultImageRequest getDefaultImageRequestFromUri(Uri uri) {
+ final DefaultImageRequest request = new DefaultImageRequest(
+ uri.getQueryParameter(DISPLAY_NAME_PARAM_KEY),
+ uri.getQueryParameter(IDENTIFIER_PARAM_KEY), false);
+ try {
+ String contactType = uri.getQueryParameter(CONTACT_TYPE_PARAM_KEY);
+ if (!TextUtils.isEmpty(contactType)) {
+ request.contactType = Integer.valueOf(contactType);
+ }
+
+ String scale = uri.getQueryParameter(SCALE_PARAM_KEY);
+ if (!TextUtils.isEmpty(scale)) {
+ request.scale = Float.valueOf(scale);
+ }
+
+ String offset = uri.getQueryParameter(OFFSET_PARAM_KEY);
+ if (!TextUtils.isEmpty(offset)) {
+ request.offset = Float.valueOf(offset);
+ }
+
+ String isCircular = uri.getQueryParameter(IS_CIRCULAR_PARAM_KEY);
+ if (!TextUtils.isEmpty(isCircular)) {
+ request.isCircular = Boolean.valueOf(isCircular);
+ }
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid DefaultImageRequest image parameters provided, ignoring and using "
+ + "defaults.");
+ }
+
+ return request;
+ }
+
+ protected boolean isDefaultImageUri(Uri uri) {
+ return DEFAULT_IMAGE_URI_SCHEME.equals(uri.getScheme());
+ }
+
+ /**
+ * Contains fields used to contain contact details and other user-defined settings that might
+ * be used by the ContactPhotoManager to generate a default contact image. This contact image
+ * takes the form of a letter or bitmap drawn on top of a colored tile.
+ */
+ public static class DefaultImageRequest {
+ /**
+ * The contact's display name. The display name is used to
+ */
+ public String displayName;
+
+ /**
+ * A unique and deterministic string that can be used to identify this contact. This is
+ * usually the contact's lookup key, but other contact details can be used as well,
+ * especially for non-local or temporary contacts that might not have a lookup key. This
+ * is used to determine the color of the tile.
+ */
+ public String identifier;
+
+ /**
+ * The type of this contact. This contact type may be used to decide the kind of
+ * image to use in the case where a unique letter cannot be generated from the contact's
+ * display name and identifier. See:
+ * {@link #TYPE_PERSON}
+ * {@link #TYPE_BUSINESS}
+ * {@link #TYPE_PERSON}
+ * {@link #TYPE_DEFAULT}
+ */
+ public int contactType = TYPE_DEFAULT;
+
+ /**
+ * The amount to scale the letter or bitmap to, as a ratio of its default size (from a
+ * range of 0.0f to 2.0f). The default value is 1.0f.
+ */
+ public float scale = SCALE_DEFAULT;
+
+ /**
+ * The amount to vertically offset the letter or image to within the tile.
+ * The provided offset must be within the range of -0.5f to 0.5f.
+ * If set to -0.5f, the letter will be shifted upwards by 0.5 times the height of the canvas
+ * it is being drawn on, which means it will be drawn with the center of the letter starting
+ * at the top edge of the canvas.
+ * If set to 0.5f, the letter will be shifted downwards by 0.5 times the height of the
+ * canvas it is being drawn on, which means it will be drawn with the center of the letter
+ * starting at the bottom edge of the canvas.
+ * The default is 0.0f, which means the letter is drawn in the exact vertical center of
+ * the tile.
+ */
+ public float offset = OFFSET_DEFAULT;
+
+ /**
+ * Whether or not to draw the default image as a circle, instead of as a square/rectangle.
+ */
+ public boolean isCircular = false;
+
+ /**
+ * Used to indicate that a drawable that represents a contact without any contact details
+ * should be returned.
+ */
+ public static DefaultImageRequest EMPTY_DEFAULT_IMAGE_REQUEST = new DefaultImageRequest();
+
+ /**
+ * Used to indicate that a drawable that represents a business without a business photo
+ * should be returned.
+ */
+ public static DefaultImageRequest EMPTY_DEFAULT_BUSINESS_IMAGE_REQUEST =
+ new DefaultImageRequest(null, null, TYPE_BUSINESS, false);
+
+ /**
+ * Used to indicate that a circular drawable that represents a contact without any contact
+ * details should be returned.
+ */
+ public static DefaultImageRequest EMPTY_CIRCULAR_DEFAULT_IMAGE_REQUEST =
+ new DefaultImageRequest(null, null, true);
+
+ /**
+ * Used to indicate that a circular drawable that represents a business without a business
+ * photo should be returned.
+ */
+ public static DefaultImageRequest EMPTY_CIRCULAR_BUSINESS_IMAGE_REQUEST =
+ new DefaultImageRequest(null, null, TYPE_BUSINESS, true);
+
+ public DefaultImageRequest() {
+ }
+
+ public DefaultImageRequest(String displayName, String identifier, boolean isCircular) {
+ this(displayName, identifier, TYPE_DEFAULT, SCALE_DEFAULT, OFFSET_DEFAULT, isCircular);
+ }
+
+ public DefaultImageRequest(String displayName, String identifier, int contactType,
+ boolean isCircular) {
+ this(displayName, identifier, contactType, SCALE_DEFAULT, OFFSET_DEFAULT, isCircular);
+ }
+
+ public DefaultImageRequest(String displayName, String identifier, int contactType,
+ float scale, float offset, boolean isCircular) {
+ this.displayName = displayName;
+ this.identifier = identifier;
+ this.contactType = contactType;
+ this.scale = scale;
+ this.offset = offset;
+ this.isCircular = isCircular;
+ }
+ }
+
+ public static abstract class DefaultImageProvider {
+ /**
+ * Applies the default avatar to the ImageView. Extent is an indicator for the size (width
+ * or height). If darkTheme is set, the avatar is one that looks better on dark background
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a
+ * default letter tile avatar should be drawn.
+ */
+ public abstract void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest);
+ }
+
+ /**
+ * A default image provider that applies a letter tile consisting of a colored background
+ * and a letter in the foreground as the default image for a contact. The color of the
+ * background and the type of letter is decided based on the contact's details.
+ */
+ private static class LetterTileDefaultImageProvider extends DefaultImageProvider {
+ @Override
+ public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest) {
+ final Drawable drawable = getDefaultImageForContact(view.getResources(),
+ defaultImageRequest);
+ view.setImageDrawable(drawable);
+ }
+
+ public static Drawable getDefaultImageForContact(Resources resources,
+ DefaultImageRequest defaultImageRequest) {
+ final LetterTileDrawable drawable = new LetterTileDrawable(resources);
+ if (defaultImageRequest != null) {
+ // If the contact identifier is null or empty, fallback to the
+ // displayName. In that case, use {@code null} for the contact's
+ // display name so that a default bitmap will be used instead of a
+ // letter
+ if (TextUtils.isEmpty(defaultImageRequest.identifier)) {
+ drawable.setContactDetails(null, defaultImageRequest.displayName);
+ } else {
+ drawable.setContactDetails(defaultImageRequest.displayName,
+ defaultImageRequest.identifier);
+ }
+ drawable.setContactType(defaultImageRequest.contactType);
+ drawable.setScale(defaultImageRequest.scale);
+ drawable.setOffset(defaultImageRequest.offset);
+ drawable.setIsCircular(defaultImageRequest.isCircular);
+ }
+ return drawable;
+ }
+ }
+
+ private static class BlankDefaultImageProvider extends DefaultImageProvider {
+ private static Drawable sDrawable;
+
+ @Override
+ public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest) {
+ if (sDrawable == null) {
+ Context context = view.getContext();
+ sDrawable = new ColorDrawable(context.getResources().getColor(
+ R.color.image_placeholder));
+ }
+ view.setImageDrawable(sDrawable);
+ }
+ }
+
+ public static DefaultImageProvider DEFAULT_AVATAR = new LetterTileDefaultImageProvider();
+
+ public static final DefaultImageProvider DEFAULT_BLANK = new BlankDefaultImageProvider();
+
+ public static ContactPhotoManager getInstance(Context context) {
+ if (sInstance == null) {
+ Context applicationContext = context.getApplicationContext();
+ sInstance = createContactPhotoManager(applicationContext);
+ applicationContext.registerComponentCallbacks(sInstance);
+ if (PermissionsUtil.hasContactsPermissions(context)) {
+ sInstance.preloadPhotosInBackground();
+ }
+ }
+ return sInstance;
+ }
+
+ public static synchronized ContactPhotoManager createContactPhotoManager(Context context) {
+ return new ContactPhotoManagerImpl(context);
+ }
+
+ @VisibleForTesting
+ public static void injectContactPhotoManagerForTesting(ContactPhotoManager photoManager) {
+ sInstance = photoManager;
+ }
+
+ /**
+ * Load thumbnail image into the supplied image view. If the photo is already cached,
+ * it is displayed immediately. Otherwise a request is sent to load the photo
+ * from the database.
+ */
+ public abstract void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
+ boolean isCircular, DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider);
+
+ /**
+ * Calls {@link #loadThumbnail(ImageView, long, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} using the {@link DefaultImageProvider} {@link #DEFAULT_AVATAR}.
+ */
+ public final void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
+ boolean isCircular, DefaultImageRequest defaultImageRequest) {
+ loadThumbnail(view, photoId, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
+ }
+
+
+ /**
+ * Load photo into the supplied image view. If the photo is already cached,
+ * it is displayed immediately. Otherwise a request is sent to load the photo
+ * from the location specified by the URI.
+ *
+ * @param view The target view
+ * @param photoUri The uri of the photo to load
+ * @param requestedExtent Specifies an approximate Max(width, height) of the targetView.
+ * This is useful if the source image can be a lot bigger that the target, so that the decoding
+ * is done using efficient sampling. If requestedExtent is specified, no sampling of the image
+ * is performed
+ * @param darkTheme Whether the background is dark. This is used for default avatars
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
+ * @param defaultProvider The provider of default avatars (this is used if photoUri doesn't
+ * refer to an existing image)
+ */
+ public abstract void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
+ boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider);
+
+ /**
+ * Calls {@link #loadPhoto(ImageView, Uri, int, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and {@code null} display names and
+ * lookup keys.
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
+ */
+ public final void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
+ boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest) {
+ loadPhoto(view, photoUri, requestedExtent, darkTheme, isCircular,
+ defaultImageRequest, DEFAULT_AVATAR);
+ }
+
+ /**
+ * Calls {@link #loadPhoto(ImageView, Uri, boolean, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and with the assumption, that
+ * the image is a thumbnail.
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
+ */
+ public final void loadDirectoryPhoto(ImageView view, Uri photoUri, boolean darkTheme,
+ boolean isCircular, DefaultImageRequest defaultImageRequest) {
+ loadPhoto(view, photoUri, -1, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
+ }
+
+ /**
+ * Remove photo from the supplied image view. This also cancels current pending load request
+ * inside this photo manager.
+ */
+ public abstract void removePhoto(ImageView view);
+
+ /**
+ * Cancels all pending requests to load photos asynchronously.
+ */
+ public abstract void cancelPendingRequests(View fragmentRootView);
+
+ /**
+ * Temporarily stops loading photos from the database.
+ */
+ public abstract void pause();
+
+ /**
+ * Resumes loading photos from the database.
+ */
+ public abstract void resume();
+
+ /**
+ * Marks all cached photos for reloading. We can continue using cache but should
+ * also make sure the photos haven't changed in the background and notify the views
+ * if so.
+ */
+ public abstract void refreshCache();
+
+ /**
+ * Stores the given bitmap directly in the LRU bitmap cache.
+ * @param photoUri The URI of the photo (for future requests).
+ * @param bitmap The bitmap.
+ * @param photoBytes The bytes that were parsed to create the bitmap.
+ */
+ public abstract void cacheBitmap(Uri photoUri, Bitmap bitmap, byte[] photoBytes);
+
+ /**
+ * Initiates a background process that over time will fill up cache with
+ * preload photos.
+ */
+ public abstract void preloadPhotosInBackground();
+
+ // ComponentCallbacks2
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ }
+
+ // ComponentCallbacks2
+ @Override
+ public void onLowMemory() {
+ }
+
+ // ComponentCallbacks2
+ @Override
+ public void onTrimMemory(int level) {
+ }
+}
+
+class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback {
+ private static final String LOADER_THREAD_NAME = "ContactPhotoLoader";
+
+ private static final int FADE_TRANSITION_DURATION = 200;
+
+ /**
+ * Type of message sent by the UI thread to itself to indicate that some photos
+ * need to be loaded.
+ */
+ private static final int MESSAGE_REQUEST_LOADING = 1;
+
+ /**
+ * Type of message sent by the loader thread to indicate that some photos have
+ * been loaded.
+ */
+ private static final int MESSAGE_PHOTOS_LOADED = 2;
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private static final String[] COLUMNS = new String[] { Photo._ID, Photo.PHOTO };
+
+ /**
+ * Dummy object used to indicate that a bitmap for a given key could not be stored in the
+ * cache.
+ */
+ private static final BitmapHolder BITMAP_UNAVAILABLE;
+
+ static {
+ BITMAP_UNAVAILABLE = new BitmapHolder(new byte[0], 0);
+ BITMAP_UNAVAILABLE.bitmapRef = new SoftReference(null);
+ }
+
+ /**
+ * Maintains the state of a particular photo.
+ */
+ private static class BitmapHolder {
+ final byte[] bytes;
+ final int originalSmallerExtent;
+
+ volatile boolean fresh;
+ Bitmap bitmap;
+ Reference bitmapRef;
+ int decodedSampleSize;
+
+ public BitmapHolder(byte[] bytes, int originalSmallerExtent) {
+ this.bytes = bytes;
+ this.fresh = true;
+ this.originalSmallerExtent = originalSmallerExtent;
+ }
+ }
+
+ private final Context mContext;
+
+ /**
+ * An LRU cache for bitmap holders. The cache contains bytes for photos just
+ * as they come from the database. Each holder has a soft reference to the
+ * actual bitmap.
+ */
+ private final LruCache mBitmapHolderCache;
+
+ /**
+ * {@code true} if ALL entries in {@link #mBitmapHolderCache} are NOT fresh.
+ */
+ private volatile boolean mBitmapHolderCacheAllUnfresh = true;
+
+ /**
+ * Cache size threshold at which bitmaps will not be preloaded.
+ */
+ private final int mBitmapHolderCacheRedZoneBytes;
+
+ /**
+ * Level 2 LRU cache for bitmaps. This is a smaller cache that holds
+ * the most recently used bitmaps to save time on decoding
+ * them from bytes (the bytes are stored in {@link #mBitmapHolderCache}.
+ */
+ private final LruCache mBitmapCache;
+
+ /**
+ * A map from ImageView to the corresponding photo ID or uri, encapsulated in a request.
+ * The request may swapped out before the photo loading request is started.
+ */
+ private final ConcurrentHashMap mPendingRequests =
+ new ConcurrentHashMap();
+
+ /**
+ * Handler for messages sent to the UI thread.
+ */
+ private final Handler mMainThreadHandler = new Handler(this);
+
+ /**
+ * Thread responsible for loading photos from the database. Created upon
+ * the first request.
+ */
+ private LoaderThread mLoaderThread;
+
+ /**
+ * A gate to make sure we only send one instance of MESSAGE_PHOTOS_NEEDED at a time.
+ */
+ private boolean mLoadingRequested;
+
+ /**
+ * Flag indicating if the image loading is paused.
+ */
+ private boolean mPaused;
+
+ /** Cache size for {@link #mBitmapHolderCache} for devices with "large" RAM. */
+ private static final int HOLDER_CACHE_SIZE = 2000000;
+
+ /** Cache size for {@link #mBitmapCache} for devices with "large" RAM. */
+ private static final int BITMAP_CACHE_SIZE = 36864 * 48; // 1728K
+
+ /** Height/width of a thumbnail image */
+ private static int mThumbnailSize;
+
+ /** For debug: How many times we had to reload cached photo for a stale entry */
+ private final AtomicInteger mStaleCacheOverwrite = new AtomicInteger();
+
+ /** For debug: How many times we had to reload cached photo for a fresh entry. Should be 0. */
+ private final AtomicInteger mFreshCacheOverwrite = new AtomicInteger();
+
+ /**
+ * The user agent string to use when loading URI based photos.
+ */
+ private String mUserAgent;
+
+ public ContactPhotoManagerImpl(Context context) {
+ mContext = context;
+
+ final ActivityManager am = ((ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE));
+
+ final float cacheSizeAdjustment = (am.isLowRamDevice()) ? 0.5f : 1.0f;
+
+ final int bitmapCacheSize = (int) (cacheSizeAdjustment * BITMAP_CACHE_SIZE);
+ mBitmapCache = new LruCache(bitmapCacheSize) {
+ @Override protected int sizeOf(Object key, Bitmap value) {
+ return value.getByteCount();
+ }
+
+ @Override protected void entryRemoved(
+ boolean evicted, Object key, Bitmap oldValue, Bitmap newValue) {
+ if (DEBUG) dumpStats();
+ }
+ };
+ final int holderCacheSize = (int) (cacheSizeAdjustment * HOLDER_CACHE_SIZE);
+ mBitmapHolderCache = new LruCache(holderCacheSize) {
+ @Override protected int sizeOf(Object key, BitmapHolder value) {
+ return value.bytes != null ? value.bytes.length : 0;
+ }
+
+ @Override protected void entryRemoved(
+ boolean evicted, Object key, BitmapHolder oldValue, BitmapHolder newValue) {
+ if (DEBUG) dumpStats();
+ }
+ };
+ mBitmapHolderCacheRedZoneBytes = (int) (holderCacheSize * 0.75);
+ Log.i(TAG, "Cache adj: " + cacheSizeAdjustment);
+ if (DEBUG) {
+ Log.d(TAG, "Cache size: " + btk(mBitmapHolderCache.maxSize())
+ + " + " + btk(mBitmapCache.maxSize()));
+ }
+
+ mThumbnailSize = context.getResources().getDimensionPixelSize(
+ R.dimen.contact_browser_list_item_photo_size);
+
+ // Get a user agent string to use for URI photo requests.
+ mUserAgent = UserAgentGenerator.getUserAgent(context);
+ if (mUserAgent == null) {
+ mUserAgent = "";
+ }
+ }
+
+ /** Converts bytes to K bytes, rounding up. Used only for debug log. */
+ private static String btk(int bytes) {
+ return ((bytes + 1023) / 1024) + "K";
+ }
+
+ private static final int safeDiv(int dividend, int divisor) {
+ return (divisor == 0) ? 0 : (dividend / divisor);
+ }
+
+ /**
+ * Dump cache stats on logcat.
+ */
+ private void dumpStats() {
+ if (!DEBUG) return;
+ {
+ int numHolders = 0;
+ int rawBytes = 0;
+ int bitmapBytes = 0;
+ int numBitmaps = 0;
+ for (BitmapHolder h : mBitmapHolderCache.snapshot().values()) {
+ numHolders++;
+ if (h.bytes != null) {
+ rawBytes += h.bytes.length;
+ }
+ Bitmap b = h.bitmapRef != null ? h.bitmapRef.get() : null;
+ if (b != null) {
+ numBitmaps++;
+ bitmapBytes += b.getByteCount();
+ }
+ }
+ Log.d(TAG, "L1: " + btk(rawBytes) + " + " + btk(bitmapBytes) + " = "
+ + btk(rawBytes + bitmapBytes) + ", " + numHolders + " holders, "
+ + numBitmaps + " bitmaps, avg: "
+ + btk(safeDiv(rawBytes, numHolders))
+ + "," + btk(safeDiv(bitmapBytes,numBitmaps)));
+ Log.d(TAG, "L1 Stats: " + mBitmapHolderCache.toString()
+ + ", overwrite: fresh=" + mFreshCacheOverwrite.get()
+ + " stale=" + mStaleCacheOverwrite.get());
+ }
+
+ {
+ int numBitmaps = 0;
+ int bitmapBytes = 0;
+ for (Bitmap b : mBitmapCache.snapshot().values()) {
+ numBitmaps++;
+ bitmapBytes += b.getByteCount();
+ }
+ Log.d(TAG, "L2: " + btk(bitmapBytes) + ", " + numBitmaps + " bitmaps"
+ + ", avg: " + btk(safeDiv(bitmapBytes, numBitmaps)));
+ // We don't get from L2 cache, so L2 stats is meaningless.
+ }
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ if (DEBUG) Log.d(TAG, "onTrimMemory: " + level);
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
+ // Clear the caches. Note all pending requests will be removed too.
+ clear();
+ }
+ }
+
+ @Override
+ public void preloadPhotosInBackground() {
+ ensureLoaderThread();
+ mLoaderThread.requestPreloading();
+ }
+
+ @Override
+ public void loadThumbnail(ImageView view, long photoId, boolean darkTheme, boolean isCircular,
+ DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
+ if (photoId == 0) {
+ // No photo is needed
+ defaultProvider.applyDefaultImage(view, -1, darkTheme, defaultImageRequest);
+ mPendingRequests.remove(view);
+ } else {
+ if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoId);
+ loadPhotoByIdOrUri(view, Request.createFromThumbnailId(photoId, darkTheme, isCircular,
+ defaultProvider));
+ }
+ }
+
+ @Override
+ public void loadPhoto(ImageView view, Uri photoUri, int requestedExtent, boolean darkTheme,
+ boolean isCircular, DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider) {
+ if (photoUri == null) {
+ // No photo is needed
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme,
+ defaultImageRequest);
+ mPendingRequests.remove(view);
+ } else {
+ if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoUri);
+ if (isDefaultImageUri(photoUri)) {
+ createAndApplyDefaultImageForUri(view, photoUri, requestedExtent, darkTheme,
+ isCircular, defaultProvider);
+ } else {
+ loadPhotoByIdOrUri(view, Request.createFromUri(photoUri, requestedExtent,
+ darkTheme, isCircular, defaultProvider));
+ }
+ }
+ }
+
+ private void createAndApplyDefaultImageForUri(ImageView view, Uri uri, int requestedExtent,
+ boolean darkTheme, boolean isCircular, DefaultImageProvider defaultProvider) {
+ DefaultImageRequest request = getDefaultImageRequestFromUri(uri);
+ request.isCircular = isCircular;
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, request);
+ }
+
+ private void loadPhotoByIdOrUri(ImageView view, Request request) {
+ boolean loaded = loadCachedPhoto(view, request, false);
+ if (loaded) {
+ mPendingRequests.remove(view);
+ } else {
+ mPendingRequests.put(view, request);
+ if (!mPaused) {
+ // Send a request to start loading photos
+ requestLoading();
+ }
+ }
+ }
+
+ @Override
+ public void removePhoto(ImageView view) {
+ view.setImageDrawable(null);
+ mPendingRequests.remove(view);
+ }
+
+
+ /**
+ * Cancels pending requests to load photos asynchronously for views inside
+ * {@param fragmentRootView}. If {@param fragmentRootView} is null, cancels all requests.
+ */
+ @Override
+ public void cancelPendingRequests(View fragmentRootView) {
+ if (fragmentRootView == null) {
+ mPendingRequests.clear();
+ return;
+ }
+ ImageView[] requestSetCopy = mPendingRequests.keySet().toArray(new ImageView[
+ mPendingRequests.size()]);
+ for (ImageView imageView : requestSetCopy) {
+ // If an ImageView is orphaned (currently scrap) or a child of fragmentRootView, then
+ // we can safely remove its request.
+ if (imageView.getParent() == null || isChildView(fragmentRootView, imageView)) {
+ mPendingRequests.remove(imageView);
+ }
+ }
+ }
+
+ private static boolean isChildView(View parent, View potentialChild) {
+ return potentialChild.getParent() != null && (potentialChild.getParent() == parent || (
+ potentialChild.getParent() instanceof ViewGroup && isChildView(parent,
+ (ViewGroup) potentialChild.getParent())));
+ }
+
+ @Override
+ public void refreshCache() {
+ if (mBitmapHolderCacheAllUnfresh) {
+ if (DEBUG) Log.d(TAG, "refreshCache -- no fresh entries.");
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "refreshCache");
+ mBitmapHolderCacheAllUnfresh = true;
+ for (BitmapHolder holder : mBitmapHolderCache.snapshot().values()) {
+ if (holder != BITMAP_UNAVAILABLE) {
+ holder.fresh = false;
+ }
+ }
+ }
+
+ /**
+ * Checks if the photo is present in cache. If so, sets the photo on the view.
+ *
+ * @return false if the photo needs to be (re)loaded from the provider.
+ */
+ private boolean loadCachedPhoto(ImageView view, Request request, boolean fadeIn) {
+ BitmapHolder holder = mBitmapHolderCache.get(request.getKey());
+ if (holder == null) {
+ // The bitmap has not been loaded ==> show default avatar
+ request.applyDefaultImage(view, request.mIsCircular);
+ return false;
+ }
+
+ if (holder.bytes == null) {
+ request.applyDefaultImage(view, request.mIsCircular);
+ return holder.fresh;
+ }
+
+ Bitmap cachedBitmap = holder.bitmapRef == null ? null : holder.bitmapRef.get();
+ if (cachedBitmap == null) {
+ if (holder.bytes.length < 8 * 1024) {
+ // Small thumbnails are usually quick to inflate. Let's do that on the UI thread
+ inflateBitmap(holder, request.getRequestedExtent());
+ cachedBitmap = holder.bitmap;
+ if (cachedBitmap == null) return false;
+ } else {
+ // This is bigger data. Let's send that back to the Loader so that we can
+ // inflate this in the background
+ request.applyDefaultImage(view, request.mIsCircular);
+ return false;
+ }
+ }
+
+ final Drawable previousDrawable = view.getDrawable();
+ if (fadeIn && previousDrawable != null) {
+ final Drawable[] layers = new Drawable[2];
+ // Prevent cascade of TransitionDrawables.
+ if (previousDrawable instanceof TransitionDrawable) {
+ final TransitionDrawable previousTransitionDrawable =
+ (TransitionDrawable) previousDrawable;
+ layers[0] = previousTransitionDrawable.getDrawable(
+ previousTransitionDrawable.getNumberOfLayers() - 1);
+ } else {
+ layers[0] = previousDrawable;
+ }
+ layers[1] = getDrawableForBitmap(mContext.getResources(), cachedBitmap, request);
+ TransitionDrawable drawable = new TransitionDrawable(layers);
+ view.setImageDrawable(drawable);
+ drawable.startTransition(FADE_TRANSITION_DURATION);
+ } else {
+ view.setImageDrawable(
+ getDrawableForBitmap(mContext.getResources(), cachedBitmap, request));
+ }
+
+ // Put the bitmap in the LRU cache. But only do this for images that are small enough
+ // (we require that at least six of those can be cached at the same time)
+ if (cachedBitmap.getByteCount() < mBitmapCache.maxSize() / 6) {
+ mBitmapCache.put(request.getKey(), cachedBitmap);
+ }
+
+ // Soften the reference
+ holder.bitmap = null;
+
+ return holder.fresh;
+ }
+
+ /**
+ * Given a bitmap, returns a drawable that is configured to display the bitmap based on the
+ * specified request.
+ */
+ private Drawable getDrawableForBitmap(Resources resources, Bitmap bitmap, Request request) {
+ if (request.mIsCircular) {
+ final RoundedBitmapDrawable drawable =
+ RoundedBitmapDrawableFactory.create(resources, bitmap);
+ drawable.setAntiAlias(true);
+ drawable.setCornerRadius(bitmap.getHeight() / 2);
+ return drawable;
+ } else {
+ return new BitmapDrawable(resources, bitmap);
+ }
+ }
+
+ /**
+ * If necessary, decodes bytes stored in the holder to Bitmap. As long as the
+ * bitmap is held either by {@link #mBitmapCache} or by a soft reference in
+ * the holder, it will not be necessary to decode the bitmap.
+ */
+ private static void inflateBitmap(BitmapHolder holder, int requestedExtent) {
+ final int sampleSize =
+ BitmapUtil.findOptimalSampleSize(holder.originalSmallerExtent, requestedExtent);
+ byte[] bytes = holder.bytes;
+ if (bytes == null || bytes.length == 0) {
+ return;
+ }
+
+ if (sampleSize == holder.decodedSampleSize) {
+ // Check the soft reference. If will be retained if the bitmap is also
+ // in the LRU cache, so we don't need to check the LRU cache explicitly.
+ if (holder.bitmapRef != null) {
+ holder.bitmap = holder.bitmapRef.get();
+ if (holder.bitmap != null) {
+ return;
+ }
+ }
+ }
+
+ try {
+ Bitmap bitmap = BitmapUtil.decodeBitmapFromBytes(bytes, sampleSize);
+
+ // TODO: As a temporary workaround while framework support is being added to
+ // clip non-square bitmaps into a perfect circle, manually crop the bitmap into
+ // into a square if it will be displayed as a thumbnail so that it can be cropped
+ // into a circle.
+ final int height = bitmap.getHeight();
+ final int width = bitmap.getWidth();
+
+ // The smaller dimension of a scaled bitmap can range from anywhere from 0 to just
+ // below twice the length of a thumbnail image due to the way we calculate the optimal
+ // sample size.
+ if (height != width && Math.min(height, width) <= mThumbnailSize * 2) {
+ final int dimension = Math.min(height, width);
+ bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
+ }
+ // make bitmap mutable and draw size onto it
+ if (DEBUG_SIZES) {
+ Bitmap original = bitmap;
+ bitmap = bitmap.copy(bitmap.getConfig(), true);
+ original.recycle();
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setTextSize(16);
+ paint.setColor(Color.BLUE);
+ paint.setStyle(Style.FILL);
+ canvas.drawRect(0.0f, 0.0f, 50.0f, 20.0f, paint);
+ paint.setColor(Color.WHITE);
+ paint.setAntiAlias(true);
+ canvas.drawText(bitmap.getWidth() + "/" + sampleSize, 0, 15, paint);
+ }
+
+ holder.decodedSampleSize = sampleSize;
+ holder.bitmap = bitmap;
+ holder.bitmapRef = new SoftReference(bitmap);
+ if (DEBUG) {
+ Log.d(TAG, "inflateBitmap " + btk(bytes.length) + " -> "
+ + bitmap.getWidth() + "x" + bitmap.getHeight()
+ + ", " + btk(bitmap.getByteCount()));
+ }
+ } catch (OutOfMemoryError e) {
+ // Do nothing - the photo will appear to be missing
+ }
+ }
+
+ public void clear() {
+ if (DEBUG) Log.d(TAG, "clear");
+ mPendingRequests.clear();
+ mBitmapHolderCache.evictAll();
+ mBitmapCache.evictAll();
+ }
+
+ @Override
+ public void pause() {
+ mPaused = true;
+ }
+
+ @Override
+ public void resume() {
+ mPaused = false;
+ if (DEBUG) dumpStats();
+ if (!mPendingRequests.isEmpty()) {
+ requestLoading();
+ }
+ }
+
+ /**
+ * Sends a message to this thread itself to start loading images. If the current
+ * view contains multiple image views, all of those image views will get a chance
+ * to request their respective photos before any of those requests are executed.
+ * This allows us to load images in bulk.
+ */
+ private void requestLoading() {
+ if (!mLoadingRequested) {
+ mLoadingRequested = true;
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
+ }
+ }
+
+ /**
+ * Processes requests on the main thread.
+ */
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_REQUEST_LOADING: {
+ mLoadingRequested = false;
+ if (!mPaused) {
+ ensureLoaderThread();
+ mLoaderThread.requestLoading();
+ }
+ return true;
+ }
+
+ case MESSAGE_PHOTOS_LOADED: {
+ if (!mPaused) {
+ processLoadedImages();
+ }
+ if (DEBUG) dumpStats();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void ensureLoaderThread() {
+ if (mLoaderThread == null) {
+ mLoaderThread = new LoaderThread(mContext.getContentResolver());
+ mLoaderThread.start();
+ }
+ }
+
+ /**
+ * Goes over pending loading requests and displays loaded photos. If some of the
+ * photos still haven't been loaded, sends another request for image loading.
+ */
+ private void processLoadedImages() {
+ Iterator iterator = mPendingRequests.keySet().iterator();
+ while (iterator.hasNext()) {
+ ImageView view = iterator.next();
+ Request key = mPendingRequests.get(view);
+ // TODO: Temporarily disable contact photo fading in, until issues with
+ // RoundedBitmapDrawables overlapping the default image drawables are resolved.
+ boolean loaded = loadCachedPhoto(view, key, false);
+ if (loaded) {
+ iterator.remove();
+ }
+ }
+
+ softenCache();
+
+ if (!mPendingRequests.isEmpty()) {
+ requestLoading();
+ }
+ }
+
+ /**
+ * Removes strong references to loaded bitmaps to allow them to be garbage collected
+ * if needed. Some of the bitmaps will still be retained by {@link #mBitmapCache}.
+ */
+ private void softenCache() {
+ for (BitmapHolder holder : mBitmapHolderCache.snapshot().values()) {
+ holder.bitmap = null;
+ }
+ }
+
+ /**
+ * Stores the supplied bitmap in cache.
+ */
+ private void cacheBitmap(Object key, byte[] bytes, boolean preloading, int requestedExtent) {
+ if (DEBUG) {
+ BitmapHolder prev = mBitmapHolderCache.get(key);
+ if (prev != null && prev.bytes != null) {
+ Log.d(TAG, "Overwriting cache: key=" + key + (prev.fresh ? " FRESH" : " stale"));
+ if (prev.fresh) {
+ mFreshCacheOverwrite.incrementAndGet();
+ } else {
+ mStaleCacheOverwrite.incrementAndGet();
+ }
+ }
+ Log.d(TAG, "Caching data: key=" + key + ", " +
+ (bytes == null ? "" : btk(bytes.length)));
+ }
+ BitmapHolder holder = new BitmapHolder(bytes,
+ bytes == null ? -1 : BitmapUtil.getSmallerExtentFromBytes(bytes));
+
+ // Unless this image is being preloaded, decode it right away while
+ // we are still on the background thread.
+ if (!preloading) {
+ inflateBitmap(holder, requestedExtent);
+ }
+
+ if (bytes != null) {
+ mBitmapHolderCache.put(key, holder);
+ if (mBitmapHolderCache.get(key) != holder) {
+ Log.w(TAG, "Bitmap too big to fit in cache.");
+ mBitmapHolderCache.put(key, BITMAP_UNAVAILABLE);
+ }
+ } else {
+ mBitmapHolderCache.put(key, BITMAP_UNAVAILABLE);
+ }
+
+ mBitmapHolderCacheAllUnfresh = false;
+ }
+
+ @Override
+ public void cacheBitmap(Uri photoUri, Bitmap bitmap, byte[] photoBytes) {
+ final int smallerExtent = Math.min(bitmap.getWidth(), bitmap.getHeight());
+ // We can pretend here that the extent of the photo was the size that we originally
+ // requested
+ Request request = Request.createFromUri(photoUri, smallerExtent, false /* darkTheme */,
+ false /* isCircular */ , DEFAULT_AVATAR);
+ BitmapHolder holder = new BitmapHolder(photoBytes, smallerExtent);
+ holder.bitmapRef = new SoftReference(bitmap);
+ mBitmapHolderCache.put(request.getKey(), holder);
+ mBitmapHolderCacheAllUnfresh = false;
+ mBitmapCache.put(request.getKey(), bitmap);
+ }
+
+ /**
+ * Populates an array of photo IDs that need to be loaded. Also decodes bitmaps that we have
+ * already loaded
+ */
+ private void obtainPhotoIdsAndUrisToLoad(Set photoIds,
+ Set photoIdsAsStrings, Set uris) {
+ photoIds.clear();
+ photoIdsAsStrings.clear();
+ uris.clear();
+
+ boolean jpegsDecoded = false;
+
+ /*
+ * Since the call is made from the loader thread, the map could be
+ * changing during the iteration. That's not really a problem:
+ * ConcurrentHashMap will allow those changes to happen without throwing
+ * exceptions. Since we may miss some requests in the situation of
+ * concurrent change, we will need to check the map again once loading
+ * is complete.
+ */
+ Iterator iterator = mPendingRequests.values().iterator();
+ while (iterator.hasNext()) {
+ Request request = iterator.next();
+ final BitmapHolder holder = mBitmapHolderCache.get(request.getKey());
+ if (holder == BITMAP_UNAVAILABLE) {
+ continue;
+ }
+ if (holder != null && holder.bytes != null && holder.fresh &&
+ (holder.bitmapRef == null || holder.bitmapRef.get() == null)) {
+ // This was previously loaded but we don't currently have the inflated Bitmap
+ inflateBitmap(holder, request.getRequestedExtent());
+ jpegsDecoded = true;
+ } else {
+ if (holder == null || !holder.fresh) {
+ if (request.isUriRequest()) {
+ uris.add(request);
+ } else {
+ photoIds.add(request.getId());
+ photoIdsAsStrings.add(String.valueOf(request.mId));
+ }
+ }
+ }
+ }
+
+ if (jpegsDecoded) mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ }
+
+ /**
+ * The thread that performs loading of photos from the database.
+ */
+ private class LoaderThread extends HandlerThread implements Callback {
+ private static final int BUFFER_SIZE = 1024*16;
+ private static final int MESSAGE_PRELOAD_PHOTOS = 0;
+ private static final int MESSAGE_LOAD_PHOTOS = 1;
+
+ /**
+ * A pause between preload batches that yields to the UI thread.
+ */
+ private static final int PHOTO_PRELOAD_DELAY = 1000;
+
+ /**
+ * Number of photos to preload per batch.
+ */
+ private static final int PRELOAD_BATCH = 25;
+
+ /**
+ * Maximum number of photos to preload. If the cache size is 2Mb and
+ * the expected average size of a photo is 4kb, then this number should be 2Mb/4kb = 500.
+ */
+ private static final int MAX_PHOTOS_TO_PRELOAD = 100;
+
+ private final ContentResolver mResolver;
+ private final StringBuilder mStringBuilder = new StringBuilder();
+ private final Set mPhotoIds = Sets.newHashSet();
+ private final Set mPhotoIdsAsStrings = Sets.newHashSet();
+ private final Set mPhotoUris = Sets.newHashSet();
+ private final List mPreloadPhotoIds = Lists.newArrayList();
+
+ private Handler mLoaderThreadHandler;
+ private byte mBuffer[];
+
+ private static final int PRELOAD_STATUS_NOT_STARTED = 0;
+ private static final int PRELOAD_STATUS_IN_PROGRESS = 1;
+ private static final int PRELOAD_STATUS_DONE = 2;
+
+ private int mPreloadStatus = PRELOAD_STATUS_NOT_STARTED;
+
+ public LoaderThread(ContentResolver resolver) {
+ super(LOADER_THREAD_NAME);
+ mResolver = resolver;
+ }
+
+ public void ensureHandler() {
+ if (mLoaderThreadHandler == null) {
+ mLoaderThreadHandler = new Handler(getLooper(), this);
+ }
+ }
+
+ /**
+ * Kicks off preloading of the next batch of photos on the background thread.
+ * Preloading will happen after a delay: we want to yield to the UI thread
+ * as much as possible.
+ *
+ * If preloading is already complete, does nothing.
+ */
+ public void requestPreloading() {
+ if (mPreloadStatus == PRELOAD_STATUS_DONE) {
+ return;
+ }
+
+ ensureHandler();
+ if (mLoaderThreadHandler.hasMessages(MESSAGE_LOAD_PHOTOS)) {
+ return;
+ }
+
+ mLoaderThreadHandler.sendEmptyMessageDelayed(
+ MESSAGE_PRELOAD_PHOTOS, PHOTO_PRELOAD_DELAY);
+ }
+
+ /**
+ * Sends a message to this thread to load requested photos. Cancels a preloading
+ * request, if any: we don't want preloading to impede loading of the photos
+ * we need to display now.
+ */
+ public void requestLoading() {
+ ensureHandler();
+ mLoaderThreadHandler.removeMessages(MESSAGE_PRELOAD_PHOTOS);
+ mLoaderThreadHandler.sendEmptyMessage(MESSAGE_LOAD_PHOTOS);
+ }
+
+ /**
+ * Receives the above message, loads photos and then sends a message
+ * to the main thread to process them.
+ */
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_PRELOAD_PHOTOS:
+ preloadPhotosInBackground();
+ break;
+ case MESSAGE_LOAD_PHOTOS:
+ loadPhotosInBackground();
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * The first time it is called, figures out which photos need to be preloaded.
+ * Each subsequent call preloads the next batch of photos and requests
+ * another cycle of preloading after a delay. The whole process ends when
+ * we either run out of photos to preload or fill up cache.
+ */
+ private void preloadPhotosInBackground() {
+ if (mPreloadStatus == PRELOAD_STATUS_DONE) {
+ return;
+ }
+
+ if (mPreloadStatus == PRELOAD_STATUS_NOT_STARTED) {
+ queryPhotosForPreload();
+ if (mPreloadPhotoIds.isEmpty()) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ } else {
+ mPreloadStatus = PRELOAD_STATUS_IN_PROGRESS;
+ }
+ requestPreloading();
+ return;
+ }
+
+ if (mBitmapHolderCache.size() > mBitmapHolderCacheRedZoneBytes) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ return;
+ }
+
+ mPhotoIds.clear();
+ mPhotoIdsAsStrings.clear();
+
+ int count = 0;
+ int preloadSize = mPreloadPhotoIds.size();
+ while(preloadSize > 0 && mPhotoIds.size() < PRELOAD_BATCH) {
+ preloadSize--;
+ count++;
+ Long photoId = mPreloadPhotoIds.get(preloadSize);
+ mPhotoIds.add(photoId);
+ mPhotoIdsAsStrings.add(photoId.toString());
+ mPreloadPhotoIds.remove(preloadSize);
+ }
+
+ loadThumbnails(true);
+
+ if (preloadSize == 0) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ }
+
+ Log.v(TAG, "Preloaded " + count + " photos. Cached bytes: "
+ + mBitmapHolderCache.size());
+
+ requestPreloading();
+ }
+
+ private void queryPhotosForPreload() {
+ Cursor cursor = null;
+ try {
+ Uri uri = Contacts.CONTENT_URI.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(MAX_PHOTOS_TO_PRELOAD))
+ .build();
+ cursor = mResolver.query(uri, new String[] { Contacts.PHOTO_ID },
+ Contacts.PHOTO_ID + " NOT NULL AND " + Contacts.PHOTO_ID + "!=0",
+ null,
+ Contacts.STARRED + " DESC, " + Contacts.LAST_TIME_CONTACTED + " DESC");
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ // Insert them in reverse order, because we will be taking
+ // them from the end of the list for loading.
+ mPreloadPhotoIds.add(0, cursor.getLong(0));
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ private void loadPhotosInBackground() {
+ if (!PermissionsUtil.hasPermission(mContext,
+ android.Manifest.permission.READ_CONTACTS)) {
+ return;
+ }
+ obtainPhotoIdsAndUrisToLoad(mPhotoIds, mPhotoIdsAsStrings, mPhotoUris);
+ loadThumbnails(false);
+ loadUriBasedPhotos();
+ requestPreloading();
+ }
+
+ /** Loads thumbnail photos with ids */
+ private void loadThumbnails(boolean preloading) {
+ if (mPhotoIds.isEmpty()) {
+ return;
+ }
+
+ // Remove loaded photos from the preload queue: we don't want
+ // the preloading process to load them again.
+ if (!preloading && mPreloadStatus == PRELOAD_STATUS_IN_PROGRESS) {
+ for (Long id : mPhotoIds) {
+ mPreloadPhotoIds.remove(id);
+ }
+ if (mPreloadPhotoIds.isEmpty()) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ }
+ }
+
+ mStringBuilder.setLength(0);
+ mStringBuilder.append(Photo._ID + " IN(");
+ for (int i = 0; i < mPhotoIds.size(); i++) {
+ if (i != 0) {
+ mStringBuilder.append(',');
+ }
+ mStringBuilder.append('?');
+ }
+ mStringBuilder.append(')');
+
+ Cursor cursor = null;
+ try {
+ if (DEBUG) Log.d(TAG, "Loading " + TextUtils.join(",", mPhotoIdsAsStrings));
+ cursor = mResolver.query(Data.CONTENT_URI,
+ COLUMNS,
+ mStringBuilder.toString(),
+ mPhotoIdsAsStrings.toArray(EMPTY_STRING_ARRAY),
+ null);
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Long id = cursor.getLong(0);
+ byte[] bytes = cursor.getBlob(1);
+ cacheBitmap(id, bytes, preloading, -1);
+ mPhotoIds.remove(id);
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ // Remaining photos were not found in the contacts database (but might be in profile).
+ for (Long id : mPhotoIds) {
+ if (ContactsContract.isProfileId(id)) {
+ Cursor profileCursor = null;
+ try {
+ profileCursor = mResolver.query(
+ ContentUris.withAppendedId(Data.CONTENT_URI, id),
+ COLUMNS, null, null, null);
+ if (profileCursor != null && profileCursor.moveToFirst()) {
+ cacheBitmap(profileCursor.getLong(0), profileCursor.getBlob(1),
+ preloading, -1);
+ } else {
+ // Couldn't load a photo this way either.
+ cacheBitmap(id, null, preloading, -1);
+ }
+ } finally {
+ if (profileCursor != null) {
+ profileCursor.close();
+ }
+ }
+ } else {
+ // Not a profile photo and not found - mark the cache accordingly
+ cacheBitmap(id, null, preloading, -1);
+ }
+ }
+
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ }
+
+ /**
+ * Loads photos referenced with Uris. Those can be remote thumbnails
+ * (from directory searches), display photos etc
+ */
+ private void loadUriBasedPhotos() {
+ for (Request uriRequest : mPhotoUris) {
+ // Keep the original URI and use this to key into the cache. Failure to do so will
+ // result in an image being continually reloaded into cache if the original URI
+ // has a contact type encodedFragment (eg nearby places business photo URLs).
+ Uri originalUri = uriRequest.getUri();
+
+ // Strip off the "contact type" we added to the URI to ensure it was identifiable as
+ // a business photo -- there is no need to pass this on to the server.
+ Uri uri = ContactPhotoManager.removeContactType(originalUri);
+
+ if (mBuffer == null) {
+ mBuffer = new byte[BUFFER_SIZE];
+ }
+ try {
+ if (DEBUG) Log.d(TAG, "Loading " + uri);
+ final String scheme = uri.getScheme();
+ InputStream is = null;
+ if (scheme.equals("http") || scheme.equals("https")) {
+ TrafficStats.setThreadStatsTag(TrafficStatsTags.CONTACT_PHOTO_DOWNLOAD_TAG);
+ final HttpURLConnection connection =
+ (HttpURLConnection) new URL(uri.toString()).openConnection();
+
+ // Include the user agent if it is specified.
+ if (!TextUtils.isEmpty(mUserAgent)) {
+ connection.setRequestProperty("User-Agent", mUserAgent);
+ }
+ try {
+ is = connection.getInputStream();
+ } catch (IOException e) {
+ connection.disconnect();
+ is = null;
+ }
+ TrafficStats.clearThreadStatsTag();
+ } else {
+ is = mResolver.openInputStream(uri);
+ }
+ if (is != null) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ int size;
+ while ((size = is.read(mBuffer)) != -1) {
+ baos.write(mBuffer, 0, size);
+ }
+ } finally {
+ is.close();
+ }
+ cacheBitmap(originalUri, baos.toByteArray(), false,
+ uriRequest.getRequestedExtent());
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ } else {
+ Log.v(TAG, "Cannot load photo " + uri);
+ cacheBitmap(originalUri, null, false, uriRequest.getRequestedExtent());
+ }
+ } catch (final Exception | OutOfMemoryError ex) {
+ Log.v(TAG, "Cannot load photo " + uri, ex);
+ cacheBitmap(originalUri, null, false, uriRequest.getRequestedExtent());
+ }
+ }
+ }
+ }
+
+ /**
+ * A holder for either a Uri or an id and a flag whether this was requested for the dark or
+ * light theme
+ */
+ private static final class Request {
+ private final long mId;
+ private final Uri mUri;
+ private final boolean mDarkTheme;
+ private final int mRequestedExtent;
+ private final DefaultImageProvider mDefaultProvider;
+ /**
+ * Whether or not the contact photo is to be displayed as a circle
+ */
+ private final boolean mIsCircular;
+
+ private Request(long id, Uri uri, int requestedExtent, boolean darkTheme,
+ boolean isCircular, DefaultImageProvider defaultProvider) {
+ mId = id;
+ mUri = uri;
+ mDarkTheme = darkTheme;
+ mIsCircular = isCircular;
+ mRequestedExtent = requestedExtent;
+ mDefaultProvider = defaultProvider;
+ }
+
+ public static Request createFromThumbnailId(long id, boolean darkTheme, boolean isCircular,
+ DefaultImageProvider defaultProvider) {
+ return new Request(id, null /* no URI */, -1, darkTheme, isCircular, defaultProvider);
+ }
+
+ public static Request createFromUri(Uri uri, int requestedExtent, boolean darkTheme,
+ boolean isCircular, DefaultImageProvider defaultProvider) {
+ return new Request(0 /* no ID */, uri, requestedExtent, darkTheme, isCircular,
+ defaultProvider);
+ }
+
+ public boolean isUriRequest() {
+ return mUri != null;
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public int getRequestedExtent() {
+ return mRequestedExtent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (mId ^ (mId >>> 32));
+ result = prime * result + mRequestedExtent;
+ result = prime * result + ((mUri == null) ? 0 : mUri.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ final Request that = (Request) obj;
+ if (mId != that.mId) return false;
+ if (mRequestedExtent != that.mRequestedExtent) return false;
+ if (!UriUtils.areEqual(mUri, that.mUri)) return false;
+ // Don't compare equality of mDarkTheme because it is only used in the default contact
+ // photo case. When the contact does have a photo, the contact photo is the same
+ // regardless of mDarkTheme, so we shouldn't need to put the photo request on the queue
+ // twice.
+ return true;
+ }
+
+ public Object getKey() {
+ return mUri == null ? mId : mUri;
+ }
+
+ /**
+ * Applies the default image to the current view. If the request is URI-based, looks for
+ * the contact type encoded fragment to determine if this is a request for a business photo,
+ * in which case we will load the default business photo.
+ *
+ * @param view The current image view to apply the image to.
+ * @param isCircular Whether the image is circular or not.
+ */
+ public void applyDefaultImage(ImageView view, boolean isCircular) {
+ final DefaultImageRequest request;
+
+ if (isCircular) {
+ request = ContactPhotoManager.isBusinessContactUri(mUri)
+ ? DefaultImageRequest.EMPTY_CIRCULAR_BUSINESS_IMAGE_REQUEST
+ : DefaultImageRequest.EMPTY_CIRCULAR_DEFAULT_IMAGE_REQUEST;
+ } else {
+ request = ContactPhotoManager.isBusinessContactUri(mUri)
+ ? DefaultImageRequest.EMPTY_DEFAULT_BUSINESS_IMAGE_REQUEST
+ : DefaultImageRequest.EMPTY_DEFAULT_IMAGE_REQUEST;
+ }
+ mDefaultProvider.applyDefaultImage(view, mRequestedExtent, mDarkTheme, request);
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/ContactPresenceIconUtil.java b/ContactsCommon/src/com/android/contacts/common/ContactPresenceIconUtil.java
new file mode 100644
index 0000000..2f4c9ee
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/ContactPresenceIconUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.StatusUpdates;
+
+/**
+ * Define the contact present show policy in Contacts
+ */
+public class ContactPresenceIconUtil {
+ /**
+ * Get the presence icon resource according the status.
+ *
+ * @return null means don't show the status icon.
+ */
+ public static Drawable getPresenceIcon (Context context, int status) {
+ // We don't show the offline status in Contacts
+ switch(status) {
+ case StatusUpdates.AVAILABLE:
+ case StatusUpdates.IDLE:
+ case StatusUpdates.AWAY:
+ case StatusUpdates.DO_NOT_DISTURB:
+ case StatusUpdates.INVISIBLE:
+ return context.getResources().getDrawable(
+ StatusUpdates.getPresenceIconResourceId(status));
+ case StatusUpdates.OFFLINE:
+ // The undefined status is treated as OFFLINE in getPresenceIconResourceId();
+ default:
+ return null;
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/ContactStatusUtil.java b/ContactsCommon/src/com/android/contacts/common/ContactStatusUtil.java
new file mode 100644
index 0000000..a7d1925
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/ContactStatusUtil.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.ContactsContract.StatusUpdates;
+
+/**
+ * Provides static function to get default contact status message.
+ */
+public class ContactStatusUtil {
+
+ private static final String TAG = "ContactStatusUtil";
+
+ public static String getStatusString(Context context, int presence) {
+ Resources resources = context.getResources();
+ switch (presence) {
+ case StatusUpdates.AVAILABLE:
+ return resources.getString(R.string.status_available);
+ case StatusUpdates.IDLE:
+ case StatusUpdates.AWAY:
+ return resources.getString(R.string.status_away);
+ case StatusUpdates.DO_NOT_DISTURB:
+ return resources.getString(R.string.status_busy);
+ case StatusUpdates.OFFLINE:
+ case StatusUpdates.INVISIBLE:
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/ContactTileLoaderFactory.java b/ContactsCommon/src/com/android/contacts/common/ContactTileLoaderFactory.java
new file mode 100644
index 0000000..f8b0c35
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/ContactTileLoaderFactory.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.contacts.common;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+
+/**
+ * Used to create {@link CursorLoader}s to load different groups of
+ * {@link com.android.contacts.list.ContactTileView}.
+ */
+public final class ContactTileLoaderFactory {
+
+ public final static int CONTACT_ID = 0;
+ public final static int DISPLAY_NAME = 1;
+ public final static int STARRED = 2;
+ public final static int PHOTO_URI = 3;
+ public final static int LOOKUP_KEY = 4;
+ public final static int CONTACT_PRESENCE = 5;
+ public final static int CONTACT_STATUS = 6;
+
+ // Only used for StrequentPhoneOnlyLoader
+ public final static int PHONE_NUMBER = 5;
+ public final static int PHONE_NUMBER_TYPE = 6;
+ public final static int PHONE_NUMBER_LABEL = 7;
+ public final static int IS_DEFAULT_NUMBER = 8;
+ public final static int PINNED = 9;
+ // The _ID field returned for strequent items actually contains data._id instead of
+ // contacts._id because the query is performed on the data table. In order to obtain the
+ // contact id for strequent items, we thus have to use Phone.contact_id instead.
+ public final static int CONTACT_ID_FOR_DATA = 10;
+
+ private static final String[] COLUMNS = new String[] {
+ Contacts._ID, // ..........................................0
+ Contacts.DISPLAY_NAME, // .................................1
+ Contacts.STARRED, // ......................................2
+ Contacts.PHOTO_URI, // ....................................3
+ Contacts.LOOKUP_KEY, // ...................................4
+ Contacts.CONTACT_PRESENCE, // .............................5
+ Contacts.CONTACT_STATUS, // ...............................6
+ };
+
+ /**
+ * Projection used for the {@link Contacts#CONTENT_STREQUENT_URI}
+ * query when {@link ContactsContract#STREQUENT_PHONE_ONLY} flag
+ * is set to true. The main difference is the lack of presence
+ * and status data and the addition of phone number and label.
+ */
+ @VisibleForTesting
+ public static final String[] COLUMNS_PHONE_ONLY = new String[] {
+ Contacts._ID, // ..........................................0
+ Contacts.DISPLAY_NAME, // .................................1
+ Contacts.STARRED, // ......................................2
+ Contacts.PHOTO_URI, // ....................................3
+ Contacts.LOOKUP_KEY, // ...................................4
+ Phone.NUMBER, // ..........................................5
+ Phone.TYPE, // ............................................6
+ Phone.LABEL, // ...........................................7
+ Phone.IS_SUPER_PRIMARY, //.................................8
+ Contacts.PINNED, // .......................................9
+ Phone.CONTACT_ID //........................................10
+ };
+
+ private static final String STARRED_ORDER = Contacts.DISPLAY_NAME+" COLLATE NOCASE ASC";
+
+ public static CursorLoader createStrequentLoader(Context context) {
+ return new CursorLoader(context, Contacts.CONTENT_STREQUENT_URI, COLUMNS, null, null,
+ STARRED_ORDER);
+ }
+
+ public static CursorLoader createStrequentPhoneOnlyLoader(Context context) {
+ Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon()
+ .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+ return new CursorLoader(context, uri, COLUMNS_PHONE_ONLY, null, null, null);
+ }
+
+ public static CursorLoader createStarredLoader(Context context) {
+ return new CursorLoader(context, Contacts.CONTENT_URI, COLUMNS, Contacts.STARRED + "=?",
+ new String[]{"1"}, STARRED_ORDER);
+ }
+
+ public static CursorLoader createFrequentLoader(Context context) {
+ return new CursorLoader(context, Contacts.CONTENT_FREQUENT_URI, COLUMNS,
+ Contacts.STARRED + "=?", new String[]{"0"}, null);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/ContactsUtils.java b/ContactsCommon/src/com/android/contacts/common/ContactsUtils.java
new file mode 100644
index 0000000..a6e0e0e
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/ContactsUtils.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.DisplayPhoto;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.dataitem.ImDataItem;
+import com.android.contacts.common.testing.NeededForTesting;
+import com.android.contacts.common.model.AccountTypeManager;
+
+import java.util.List;
+
+public class ContactsUtils {
+ private static final String TAG = "ContactsUtils";
+
+ // Telecomm related schemes are in CallUtil
+ public static final String SCHEME_IMTO = "imto";
+ public static final String SCHEME_MAILTO = "mailto";
+ public static final String SCHEME_SMSTO = "smsto";
+
+ private static final int DEFAULT_THUMBNAIL_SIZE = 96;
+
+ private static int sThumbnailSize = -1;
+
+ // TODO find a proper place for the canonical version of these
+ public interface ProviderNames {
+ String YAHOO = "Yahoo";
+ String GTALK = "GTalk";
+ String MSN = "MSN";
+ String ICQ = "ICQ";
+ String AIM = "AIM";
+ String XMPP = "XMPP";
+ String JABBER = "JABBER";
+ String SKYPE = "SKYPE";
+ String QQ = "QQ";
+ }
+
+ /**
+ * This looks up the provider name defined in
+ * ProviderNames from the predefined IM protocol id.
+ * This is used for interacting with the IM application.
+ *
+ * @param protocol the protocol ID
+ * @return the provider name the IM app uses for the given protocol, or null if no
+ * provider is defined for the given protocol
+ * @hide
+ */
+ public static String lookupProviderNameFromId(int protocol) {
+ switch (protocol) {
+ case Im.PROTOCOL_GOOGLE_TALK:
+ return ProviderNames.GTALK;
+ case Im.PROTOCOL_AIM:
+ return ProviderNames.AIM;
+ case Im.PROTOCOL_MSN:
+ return ProviderNames.MSN;
+ case Im.PROTOCOL_YAHOO:
+ return ProviderNames.YAHOO;
+ case Im.PROTOCOL_ICQ:
+ return ProviderNames.ICQ;
+ case Im.PROTOCOL_JABBER:
+ return ProviderNames.JABBER;
+ case Im.PROTOCOL_SKYPE:
+ return ProviderNames.SKYPE;
+ case Im.PROTOCOL_QQ:
+ return ProviderNames.QQ;
+ }
+ return null;
+ }
+
+ /**
+ * Test if the given {@link CharSequence} contains any graphic characters,
+ * first checking {@link TextUtils#isEmpty(CharSequence)} to handle null.
+ */
+ public static boolean isGraphic(CharSequence str) {
+ return !TextUtils.isEmpty(str) && TextUtils.isGraphic(str);
+ }
+
+ /**
+ * Returns true if two objects are considered equal. Two null references are equal here.
+ */
+ @NeededForTesting
+ public static boolean areObjectsEqual(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
+ }
+
+ /**
+ * Returns true if two {@link Intent}s are both null, or have the same action.
+ */
+ public static final boolean areIntentActionEqual(Intent a, Intent b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null || b == null) {
+ return false;
+ }
+ return TextUtils.equals(a.getAction(), b.getAction());
+ }
+
+ public static boolean areContactWritableAccountsAvailable(Context context) {
+ final List accounts =
+ AccountTypeManager.getInstance(context).getAccounts(true /* writeable */);
+ return !accounts.isEmpty();
+ }
+
+ public static boolean areGroupWritableAccountsAvailable(Context context) {
+ final List accounts =
+ AccountTypeManager.getInstance(context).getGroupWritableAccounts();
+ return !accounts.isEmpty();
+ }
+
+ /**
+ * Returns the size (width and height) of thumbnail pictures as configured in the provider. This
+ * can safely be called from the UI thread, as the provider can serve this without performing
+ * a database access
+ */
+ public static int getThumbnailSize(Context context) {
+ if (sThumbnailSize == -1) {
+ final Cursor c = context.getContentResolver().query(
+ DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
+ new String[] { DisplayPhoto.THUMBNAIL_MAX_DIM }, null, null, null);
+ if (c != null) {
+ try {
+ if (c.moveToFirst()) {
+ sThumbnailSize = c.getInt(0);
+ }
+ } finally {
+ c.close();
+ }
+ }
+ }
+ return sThumbnailSize != -1 ? sThumbnailSize : DEFAULT_THUMBNAIL_SIZE;
+ }
+
+ private static Intent getCustomImIntent(ImDataItem im, int protocol) {
+ String host = im.getCustomProtocol();
+ final String data = im.getData();
+ if (TextUtils.isEmpty(data)) {
+ return null;
+ }
+ if (protocol != Im.PROTOCOL_CUSTOM) {
+ // Try bringing in a well-known host for specific protocols
+ host = ContactsUtils.lookupProviderNameFromId(protocol);
+ }
+ if (TextUtils.isEmpty(host)) {
+ return null;
+ }
+ final String authority = host.toLowerCase();
+ final Uri imUri = new Uri.Builder().scheme(SCHEME_IMTO).authority(
+ authority).appendPath(data).build();
+ final Intent intent = new Intent(Intent.ACTION_SENDTO, imUri);
+ return intent;
+ }
+
+ /**
+ * Returns the proper Intent for an ImDatItem. If available, a secondary intent is stored
+ * in the second Pair slot
+ */
+ public static Pair buildImIntent(Context context, ImDataItem im) {
+ Intent intent = null;
+ Intent secondaryIntent = null;
+ final boolean isEmail = im.isCreatedFromEmail();
+
+ if (!isEmail && !im.isProtocolValid()) {
+ return new Pair<>(null, null);
+ }
+
+ final String data = im.getData();
+ if (TextUtils.isEmpty(data)) {
+ return new Pair<>(null, null);
+ }
+
+ final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol();
+
+ if (protocol == Im.PROTOCOL_GOOGLE_TALK) {
+ final int chatCapability = im.getChatCapability();
+ if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
+ intent = new Intent(Intent.ACTION_SENDTO,
+ Uri.parse("xmpp:" + data + "?message"));
+ secondaryIntent = new Intent(Intent.ACTION_SENDTO,
+ Uri.parse("xmpp:" + data + "?call"));
+ } else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) {
+ // Allow Talking and Texting
+ intent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
+ secondaryIntent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
+ } else {
+ intent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
+ }
+ } else {
+ // Build an IM Intent
+ intent = getCustomImIntent(im, protocol);
+ }
+ return new Pair<>(intent, secondaryIntent);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/GeoUtil.java b/ContactsCommon/src/com/android/contacts/common/GeoUtil.java
new file mode 100644
index 0000000..cd0139b
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/GeoUtil.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common;
+
+import android.app.Application;
+import android.content.Context;
+
+import com.android.contacts.common.location.CountryDetector;
+
+import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.Phonenumber;
+
+import java.util.Locale;
+
+/**
+ * Static methods related to Geo.
+ */
+public class GeoUtil {
+
+ /**
+ * Returns the country code of the country the user is currently in. Before calling this
+ * method, make sure that {@link CountryDetector#initialize(Context)} has already been called
+ * in {@link Application#onCreate()}.
+ * @return The ISO 3166-1 two letters country code of the country the user
+ * is in.
+ */
+ public static String getCurrentCountryIso(Context context) {
+ // The {@link CountryDetector} should never return null so this is safe to return as-is.
+ return CountryDetector.getInstance(context).getCurrentCountryIso();
+ }
+
+ public static String getGeocodedLocationFor(Context context, String phoneNumber) {
+ final PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
+ final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+ try {
+ final Phonenumber.PhoneNumber structuredPhoneNumber =
+ phoneNumberUtil.parse(phoneNumber, getCurrentCountryIso(context));
+ final Locale locale = context.getResources().getConfiguration().locale;
+ return geocoder.getDescriptionForNumber(structuredPhoneNumber, locale);
+ } catch (NumberParseException e) {
+ return null;
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/GroupMetaData.java b/ContactsCommon/src/com/android/contacts/common/GroupMetaData.java
new file mode 100644
index 0000000..fa86ae2
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/GroupMetaData.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.contacts.common;
+
+/**
+ * Meta-data for a contact group. We load all groups associated with the contact's
+ * constituent accounts.
+ */
+public final class GroupMetaData {
+ private String mAccountName;
+ private String mAccountType;
+ private String mDataSet;
+ private long mGroupId;
+ private String mTitle;
+ private boolean mDefaultGroup;
+ private boolean mFavorites;
+
+ public GroupMetaData(String accountName, String accountType, String dataSet, long groupId,
+ String title, boolean defaultGroup, boolean favorites) {
+ this.mAccountName = accountName;
+ this.mAccountType = accountType;
+ this.mDataSet = dataSet;
+ this.mGroupId = groupId;
+ this.mTitle = title;
+ this.mDefaultGroup = defaultGroup;
+ this.mFavorites = favorites;
+ }
+
+ public String getAccountName() {
+ return mAccountName;
+ }
+
+ public String getAccountType() {
+ return mAccountType;
+ }
+
+ public String getDataSet() {
+ return mDataSet;
+ }
+
+ public long getGroupId() {
+ return mGroupId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public boolean isDefaultGroup() {
+ return mDefaultGroup;
+ }
+
+ public boolean isFavorites() {
+ return mFavorites;
+ }
+}
\ No newline at end of file
diff --git a/ContactsCommon/src/com/android/contacts/common/MoreContactUtils.java b/ContactsCommon/src/com/android/contacts/common/MoreContactUtils.java
new file mode 100644
index 0000000..9b9f800
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/MoreContactUtils.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common;
+
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.contacts.common.model.account.AccountType;
+
+/**
+ * Shared static contact utility methods.
+ */
+public class MoreContactUtils {
+
+ private static final String WAIT_SYMBOL_AS_STRING = String.valueOf(PhoneNumberUtils.WAIT);
+
+ /**
+ * Returns true if two data with mimetypes which represent values in contact entries are
+ * considered equal for collapsing in the GUI. For caller-id, use
+ * {@link android.telephony.PhoneNumberUtils#compare(android.content.Context, String, String)}
+ * instead
+ */
+ public static boolean shouldCollapse(CharSequence mimetype1, CharSequence data1,
+ CharSequence mimetype2, CharSequence data2) {
+ // different mimetypes? don't collapse
+ if (!TextUtils.equals(mimetype1, mimetype2)) return false;
+
+ // exact same string? good, bail out early
+ if (TextUtils.equals(data1, data2)) return true;
+
+ // so if either is null, these two must be different
+ if (data1 == null || data2 == null) return false;
+
+ // if this is not about phone numbers, we know this is not a match (of course, some
+ // mimetypes could have more sophisticated matching is the future, e.g. addresses)
+ if (!TextUtils.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
+ mimetype1)) {
+ return false;
+ }
+
+ return shouldCollapsePhoneNumbers(data1.toString(), data2.toString());
+ }
+
+ // TODO: Move this to PhoneDataItem.shouldCollapse override
+ private static boolean shouldCollapsePhoneNumbers(String number1, String number2) {
+ // Work around to address b/20724444. We want to distinguish between #555, *555 and 555.
+ // This makes no attempt to distinguish between 555 and 55*5, since 55*5 is an improbable
+ // number. PhoneNumberUtil already distinguishes between 555 and 55#5.
+ if (number1.contains("#") != number2.contains("#")
+ || number1.contains("*") != number2.contains("*")) {
+ return false;
+ }
+
+ // Now do the full phone number thing. split into parts, separated by waiting symbol
+ // and compare them individually
+ final String[] dataParts1 = number1.split(WAIT_SYMBOL_AS_STRING);
+ final String[] dataParts2 = number2.split(WAIT_SYMBOL_AS_STRING);
+ if (dataParts1.length != dataParts2.length) return false;
+ final PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ for (int i = 0; i < dataParts1.length; i++) {
+ // Match phone numbers represented by keypad letters, in which case prefer the
+ // phone number with letters.
+ final String dataPart1 = PhoneNumberUtils.convertKeypadLettersToDigits(dataParts1[i]);
+ final String dataPart2 = dataParts2[i];
+
+ // substrings equal? shortcut, don't parse
+ if (TextUtils.equals(dataPart1, dataPart2)) continue;
+
+ // do a full parse of the numbers
+ final PhoneNumberUtil.MatchType result = util.isNumberMatch(dataPart1, dataPart2);
+ switch (result) {
+ case NOT_A_NUMBER:
+ // don't understand the numbers? let's play it safe
+ return false;
+ case NO_MATCH:
+ return false;
+ case EXACT_MATCH:
+ break;
+ case NSN_MATCH:
+ try {
+ // For NANP phone numbers, match when one has +1 and the other does not.
+ // In this case, prefer the +1 version.
+ if (util.parse(dataPart1, null).getCountryCode() == 1) {
+ // At this point, the numbers can be either case 1 or 2 below....
+ //
+ // case 1)
+ // +14155551212 <--- country code 1
+ // 14155551212 <--- 1 is trunk prefix, not country code
+ //
+ // and
+ //
+ // case 2)
+ // +14155551212
+ // 4155551212
+ //
+ // From b/7519057, case 2 needs to be equal. But also that bug, case 3
+ // below should not be equal.
+ //
+ // case 3)
+ // 14155551212
+ // 4155551212
+ //
+ // So in order to make sure transitive equality is valid, case 1 cannot
+ // be equal. Otherwise, transitive equality breaks and the following
+ // would all be collapsed:
+ // 4155551212 |
+ // 14155551212 |----> +14155551212
+ // +14155551212 |
+ //
+ // With transitive equality, the collapsed values should be:
+ // 4155551212 | 14155551212
+ // 14155551212 |----> +14155551212
+ // +14155551212 |
+
+ // Distinguish between case 1 and 2 by checking for trunk prefix '1'
+ // at the start of number 2.
+ if (dataPart2.trim().charAt(0) == '1') {
+ // case 1
+ return false;
+ }
+ break;
+ }
+ } catch (NumberParseException e) {
+ // This is the case where the first number does not have a country code.
+ // examples:
+ // (123) 456-7890 & 123-456-7890 (collapse)
+ // 0049 (8092) 1234 & +49/80921234 (unit test says do not collapse)
+
+ // Check the second number. If it also does not have a country code, then
+ // we should collapse. If it has a country code, then it's a different
+ // number and we should not collapse (this conclusion is based on an
+ // existing unit test).
+ try {
+ util.parse(dataPart2, null);
+ } catch (NumberParseException e2) {
+ // Number 2 also does not have a country. Collapse.
+ break;
+ }
+ }
+ return false;
+ case SHORT_NSN_MATCH:
+ return false;
+ default:
+ throw new IllegalStateException("Unknown result value from phone number " +
+ "library");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the {@link android.graphics.Rect} with left, top, right, and bottom coordinates
+ * that are equivalent to the given {@link android.view.View}'s bounds. This is equivalent to
+ * how the target {@link android.graphics.Rect} is calculated in
+ * {@link android.provider.ContactsContract.QuickContact#showQuickContact}.
+ */
+ public static Rect getTargetRectFromView(View view) {
+ final int[] pos = new int[2];
+ view.getLocationOnScreen(pos);
+
+ final Rect rect = new Rect();
+ rect.left = pos[0];
+ rect.top = pos[1];
+ rect.right = pos[0] + view.getWidth();
+ rect.bottom = pos[1] + view.getHeight();
+ return rect;
+ }
+
+ /**
+ * Returns a header view based on the R.layout.list_separator, where the
+ * containing {@link android.widget.TextView} is set using the given textResourceId.
+ */
+ public static TextView createHeaderView(Context context, int textResourceId) {
+ final TextView textView = (TextView) View.inflate(context, R.layout.list_separator, null);
+ textView.setText(context.getString(textResourceId));
+ return textView;
+ }
+
+ /**
+ * Set the top padding on the header view dynamically, based on whether the header is in
+ * the first row or not.
+ */
+ public static void setHeaderViewBottomPadding(Context context, TextView textView,
+ boolean isFirstRow) {
+ final int topPadding;
+ if (isFirstRow) {
+ topPadding = (int) context.getResources().getDimension(
+ R.dimen.frequently_contacted_title_top_margin_when_first_row);
+ } else {
+ topPadding = (int) context.getResources().getDimension(
+ R.dimen.frequently_contacted_title_top_margin);
+ }
+ textView.setPaddingRelative(textView.getPaddingStart(), topPadding,
+ textView.getPaddingEnd(), textView.getPaddingBottom());
+ }
+
+
+ /**
+ * Returns the intent to launch for the given invitable account type and contact lookup URI.
+ * This will return null if the account type is not invitable (i.e. there is no
+ * {@link AccountType#getInviteContactActivityClassName()} or
+ * {@link AccountType#syncAdapterPackageName}).
+ */
+ public static Intent getInvitableIntent(AccountType accountType, Uri lookupUri) {
+ String syncAdapterPackageName = accountType.syncAdapterPackageName;
+ String className = accountType.getInviteContactActivityClassName();
+ if (TextUtils.isEmpty(syncAdapterPackageName) || TextUtils.isEmpty(className)) {
+ return null;
+ }
+ Intent intent = new Intent();
+ intent.setClassName(syncAdapterPackageName, className);
+
+ intent.setAction(ContactsContract.Intents.INVITE_CONTACT);
+
+ // Data is the lookup URI.
+ intent.setData(lookupUri);
+ return intent;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/activity/RequestImportVCardPermissionsActivity.java b/ContactsCommon/src/com/android/contacts/common/activity/RequestImportVCardPermissionsActivity.java
new file mode 100644
index 0000000..f3baf0b
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/activity/RequestImportVCardPermissionsActivity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.activity;
+
+import android.Manifest.permission;
+import android.app.Activity;
+
+/**
+ * Activity that requests permissions needed for ImportVCardActivity.
+ */
+public class RequestImportVCardPermissionsActivity extends RequestPermissionsActivityBase {
+
+ private static final String[] REQUIRED_PERMISSIONS = new String[] {
+ permission.READ_CONTACTS,
+ permission.READ_EXTERNAL_STORAGE,
+ };
+
+ @Override
+ protected String[] getRequiredPermissions() {
+ return REQUIRED_PERMISSIONS;
+ }
+
+ @Override
+ protected String[] getDesiredPermissions() {
+ // Since this is used as an ostensible part of Dialer, lets be less pushy about asking for
+ // unnecessary permissions here.
+ return REQUIRED_PERMISSIONS;
+ }
+
+ /**
+ * If any permissions the Contacts app needs are missing, open an Activity
+ * to prompt the user for these permissions. Moreover, finish the current activity.
+ *
+ * This is designed to be called inside {@link android.app.Activity#onCreate}
+ */
+ public static boolean startPermissionActivity(Activity activity) {
+ return startPermissionActivity(activity, REQUIRED_PERMISSIONS,
+ RequestImportVCardPermissionsActivity.class);
+ }
+}
\ No newline at end of file
diff --git a/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivity.java b/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivity.java
new file mode 100644
index 0000000..8591c2f
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.activity;
+
+import android.Manifest.permission;
+import android.app.Activity;
+
+/**
+ * Activity that requests permissions needed for activities exported from Contacts.
+ */
+public class RequestPermissionsActivity extends RequestPermissionsActivityBase {
+
+ private static final String[] REQUIRED_PERMISSIONS = new String[]{
+ // "Contacts" group. Without this permission, the Contacts app is useless.
+ permission.READ_CONTACTS,
+ // "Phone" group. This is only used in a few places such as QuickContactActivity and
+ // ImportExportDialogFragment. We could work around missing this permission with a bit
+ // of work.
+ permission.READ_CALL_LOG,
+ };
+
+ @Override
+ protected String[] getRequiredPermissions() {
+ return REQUIRED_PERMISSIONS;
+ }
+
+ @Override
+ protected String[] getDesiredPermissions() {
+ return new String[]{
+ permission.ACCESS_FINE_LOCATION, // Location Group
+ permission.READ_CONTACTS, // Contacts group
+ permission.READ_CALL_LOG, // Permission group phone
+ permission.READ_CALENDAR, // Calendar group
+ permission.READ_SMS, // SMS group
+ };
+ }
+ public static boolean startPermissionActivity(Activity activity) {
+ return startPermissionActivity(activity, REQUIRED_PERMISSIONS,
+ RequestPermissionsActivity.class);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivityBase.java b/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivityBase.java
new file mode 100644
index 0000000..5f78ec7
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivityBase.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.activity;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.AccountTypeManager;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Trace;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Activity that asks the user for all {@link #getDesiredPermissions} if any of
+ * {@link #getRequiredPermissions} are missing.
+ *
+ * NOTE: As a result of b/22095159, this can behave oddly in the case where the final permission
+ * you are requesting causes an application restart.
+ */
+public abstract class RequestPermissionsActivityBase extends Activity {
+ public static final String PREVIOUS_ACTIVITY_INTENT = "previous_intent";
+ private static final int PERMISSIONS_REQUEST_ALL_PERMISSIONS = 1;
+
+ /**
+ * @return list of permissions that are needed in order for {@link #PREVIOUS_ACTIVITY_INTENT} to
+ * operate. You only need to return a single permission per permission group you care about.
+ */
+ protected abstract String[] getRequiredPermissions();
+
+ /**
+ * @return list of permissions that would be useful for {@link #PREVIOUS_ACTIVITY_INTENT} to
+ * operate. You only need to return a single permission per permission group you care about.
+ */
+ protected abstract String[] getDesiredPermissions();
+
+ private Intent mPreviousActivityIntent;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPreviousActivityIntent = (Intent) getIntent().getExtras().get(PREVIOUS_ACTIVITY_INTENT);
+
+ // Only start a requestPermissions() flow when first starting this activity the first time.
+ // The process is likely to be restarted during the permission flow (necessary to enable
+ // permissions) so this is important to track.
+ if (savedInstanceState == null) {
+ requestPermissions();
+ }
+ }
+
+ /**
+ * If any permissions the Contacts app needs are missing, open an Activity
+ * to prompt the user for these permissions. Moreover, finish the current activity.
+ *
+ * This is designed to be called inside {@link android.app.Activity#onCreate}
+ */
+ protected static boolean startPermissionActivity(Activity activity,
+ String[] requiredPermissions, Class> newActivityClass) {
+ if (!RequestPermissionsActivity.hasPermissions(activity, requiredPermissions)) {
+ final Intent intent = new Intent(activity, newActivityClass);
+ intent.putExtra(PREVIOUS_ACTIVITY_INTENT, activity.getIntent());
+ activity.startActivity(intent);
+ activity.finish();
+ return true;
+ }
+
+ // Account type initialization must be delayed until the Contacts permission group
+ // has been granted (since GET_ACCOUNTS) falls under that groups. Previously it
+ // was initialized in ContactApplication which would cause problems as
+ // AccountManager.getAccounts would return an empty array. See b/22690336
+ AccountTypeManager.getInstance(activity);
+
+ return false;
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[],
+ int[] grantResults) {
+ if (permissions != null && permissions.length > 0
+ && isAllGranted(permissions, grantResults)) {
+ mPreviousActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(mPreviousActivityIntent);
+ finish();
+ overridePendingTransition(0, 0);
+ } else {
+ Toast.makeText(this, R.string.missing_required_permission, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ }
+
+ private boolean isAllGranted(String permissions[], int[] grantResult) {
+ for (int i = 0; i < permissions.length; i++) {
+ if (grantResult[i] != PackageManager.PERMISSION_GRANTED
+ && isPermissionRequired(permissions[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isPermissionRequired(String p) {
+ return Arrays.asList(getRequiredPermissions()).contains(p);
+ }
+
+ private void requestPermissions() {
+ Trace.beginSection("requestPermissions");
+ try {
+ // Construct a list of missing permissions
+ final ArrayList unsatisfiedPermissions = new ArrayList<>();
+ for (String permission : getDesiredPermissions()) {
+ if (checkSelfPermission(permission)
+ != PackageManager.PERMISSION_GRANTED) {
+ unsatisfiedPermissions.add(permission);
+ }
+ }
+ if (unsatisfiedPermissions.size() == 0) {
+ throw new RuntimeException("Request permission activity was called even"
+ + " though all permissions are satisfied.");
+ }
+ requestPermissions(
+ unsatisfiedPermissions.toArray(new String[unsatisfiedPermissions.size()]),
+ PERMISSIONS_REQUEST_ALL_PERMISSIONS);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ protected static boolean hasPermissions(Context context, String[] permissions) {
+ Trace.beginSection("hasPermission");
+ try {
+ for (String permission : permissions) {
+ if (context.checkSelfPermission(permission)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ } finally {
+ Trace.endSection();
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/activity/TransactionSafeActivity.java b/ContactsCommon/src/com/android/contacts/common/activity/TransactionSafeActivity.java
new file mode 100644
index 0000000..6c2e4fe
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/activity/TransactionSafeActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.activity;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A common superclass that keeps track of whether an {@link Activity} has saved its state yet or
+ * not.
+ */
+public abstract class TransactionSafeActivity extends Activity {
+
+ private boolean mIsSafeToCommitTransactions;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mIsSafeToCommitTransactions = false;
+ }
+
+ /**
+ * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
+ * whether {@link Activity#onSaveInstanceState} has been called or not.
+ *
+ * Make sure that the current activity calls into
+ * {@link super.onSaveInstanceState(Bundle outState)} (if that method is overridden),
+ * so the flag is properly set.
+ */
+ public boolean isSafeToCommitTransactions() {
+ return mIsSafeToCommitTransactions;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/database/ContactUpdateUtils.java b/ContactsCommon/src/com/android/contacts/common/database/ContactUpdateUtils.java
new file mode 100644
index 0000000..1bd08aa
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/database/ContactUpdateUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.database;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+/**
+ * Static methods to update contact information.
+ */
+public class ContactUpdateUtils {
+
+ private static final String TAG = ContactUpdateUtils.class.getSimpleName();
+
+ public static void setSuperPrimary(Context context, long dataId) {
+ if (dataId == -1) {
+ Log.e(TAG, "Invalid arguments for setSuperPrimary request");
+ return;
+ }
+
+ // Update the primary values in the data record.
+ ContentValues values = new ContentValues(2);
+ values.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
+ values.put(ContactsContract.Data.IS_PRIMARY, 1);
+
+ context.getContentResolver().update(
+ ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, dataId),
+ values, null, null);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/database/EmptyCursor.java b/ContactsCommon/src/com/android/contacts/common/database/EmptyCursor.java
new file mode 100644
index 0000000..ad00eff
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/database/EmptyCursor.java
@@ -0,0 +1,84 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License
+*/
+
+package com.android.contacts.common.database;
+
+import android.database.AbstractCursor;
+import android.database.CursorIndexOutOfBoundsException;
+
+/**
+ * A cursor that is empty.
+ *
+ * If you want an empty cursor, this class is better than a MatrixCursor because it has less
+ * overhead.
+ */
+final public class EmptyCursor extends AbstractCursor {
+
+ private String[] mColumns;
+
+ public EmptyCursor(String[] columns) {
+ this.mColumns = columns;
+ }
+
+ @Override
+ public int getCount() {
+ return 0;
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return mColumns;
+ }
+
+ @Override
+ public String getString(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public short getShort(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public int getInt(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public long getLong(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public float getFloat(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public double getDouble(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public boolean isNull(int column) {
+ throw cursorException();
+ }
+
+ private CursorIndexOutOfBoundsException cursorException() {
+ return new CursorIndexOutOfBoundsException("Operation not permitted on an empty cursor.");
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/database/NoNullCursorAsyncQueryHandler.java b/ContactsCommon/src/com/android/contacts/common/database/NoNullCursorAsyncQueryHandler.java
new file mode 100644
index 0000000..aefc0fd
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/database/NoNullCursorAsyncQueryHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.database;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * An {@AsyncQueryHandler} that will never return a null cursor.
+ *
+ * Instead, will return a {@link Cursor} with 0 records.
+ */
+public abstract class NoNullCursorAsyncQueryHandler extends AsyncQueryHandler {
+
+ public NoNullCursorAsyncQueryHandler(ContentResolver cr) {
+ super(cr);
+ }
+
+ @Override
+ public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String orderBy) {
+ final CookieWithProjection projectionCookie = new CookieWithProjection(cookie, projection);
+ super.startQuery(token, projectionCookie, uri, projection, selection, selectionArgs,
+ orderBy);
+ }
+
+ @Override
+ protected final void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ CookieWithProjection projectionCookie = (CookieWithProjection) cookie;
+
+ super.onQueryComplete(token, projectionCookie.originalCookie, cursor);
+
+ if (cursor == null) {
+ cursor = new EmptyCursor(projectionCookie.projection);
+ }
+ onNotNullableQueryComplete(token, projectionCookie.originalCookie, cursor);
+ }
+
+ protected abstract void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor);
+
+ /**
+ * Class to add projection to an existing cookie.
+ */
+ private static class CookieWithProjection {
+ public final Object originalCookie;
+ public final String[] projection;
+
+ public CookieWithProjection(Object cookie, String[] projection) {
+ this.originalCookie = cookie;
+ this.projection = projection;
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/dialog/CallSubjectDialog.java b/ContactsCommon/src/com/android/contacts/common/dialog/CallSubjectDialog.java
new file mode 100644
index 0000000..fd041dd
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/dialog/CallSubjectDialog.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.dialog;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ResultReceiver;
+import android.preference.PreferenceManager;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+
+import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.UriUtils;
+import com.android.phone.common.animation.AnimUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implements a dialog which prompts for a call subject for an outgoing call. The dialog includes
+ * a pop up list of historical call subjects.
+ */
+public class CallSubjectDialog extends Activity {
+ private static final String TAG = "CallSubjectDialog";
+ private static final int CALL_SUBJECT_LIMIT = 16;
+ private static final int CALL_SUBJECT_HISTORY_SIZE = 5;
+
+ private static final int REQUEST_SUBJECT = 1001;
+
+ public static final String PREF_KEY_SUBJECT_HISTORY_COUNT = "subject_history_count";
+ public static final String PREF_KEY_SUBJECT_HISTORY_ITEM = "subject_history_item";
+
+ /**
+ * Activity intent argument bundle keys:
+ */
+ public static final String ARG_PHOTO_ID = "PHOTO_ID";
+ public static final String ARG_PHOTO_URI = "PHOTO_URI";
+ public static final String ARG_CONTACT_URI = "CONTACT_URI";
+ public static final String ARG_NAME_OR_NUMBER = "NAME_OR_NUMBER";
+ public static final String ARG_IS_BUSINESS = "IS_BUSINESS";
+ public static final String ARG_NUMBER = "NUMBER";
+ public static final String ARG_DISPLAY_NUMBER = "DISPLAY_NUMBER";
+ public static final String ARG_NUMBER_LABEL = "NUMBER_LABEL";
+ public static final String ARG_PHONE_ACCOUNT_HANDLE = "PHONE_ACCOUNT_HANDLE";
+
+ private int mAnimationDuration;
+ private View mBackgroundView;
+ private View mDialogView;
+ private QuickContactBadge mContactPhoto;
+ private TextView mNameView;
+ private TextView mNumberView;
+ private EditText mCallSubjectView;
+ private TextView mCharacterLimitView;
+ private View mHistoryButton;
+ private View mSendAndCallButton;
+ private ListView mSubjectList;
+
+ private int mLimit = CALL_SUBJECT_LIMIT;
+ private int mPhotoSize;
+ private SharedPreferences mPrefs;
+ private List mSubjectHistory;
+
+ private long mPhotoID;
+ private Uri mPhotoUri;
+ private Uri mContactUri;
+ private String mNameOrNumber;
+ private boolean mIsBusiness;
+ private String mNumber;
+ private String mDisplayNumber;
+ private String mNumberLabel;
+ private PhoneAccountHandle mPhoneAccountHandle;
+
+ /**
+ * Handles changes to the text in the subject box. Ensures the character limit is updated.
+ */
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // no-op
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ updateCharacterLimit();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // no-op
+ }
+ };
+
+ /**
+ * Click listener which handles user clicks outside of the dialog.
+ */
+ private View.OnClickListener mBackgroundListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ };
+
+ /**
+ * Handles displaying the list of past call subjects.
+ */
+ private final View.OnClickListener mHistoryOnClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hideSoftKeyboard(CallSubjectDialog.this, mCallSubjectView);
+ showCallHistory(mSubjectList.getVisibility() == View.GONE);
+ }
+ };
+
+ /**
+ * Handles starting a call with a call subject specified.
+ */
+ private final View.OnClickListener mSendAndCallOnClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String subject = mCallSubjectView.getText().toString();
+ Intent intent = CallUtil.getCallWithSubjectIntent(mNumber, mPhoneAccountHandle,
+ subject);
+
+ final TelecomManager tm =
+ (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
+ tm.placeCall(intent.getData(), intent.getExtras());
+
+ mSubjectHistory.add(subject);
+ saveSubjectHistory(mSubjectHistory);
+ finish();
+ }
+ };
+
+ /**
+ * Handles auto-hiding the call history when user clicks in the call subject field to give it
+ * focus.
+ */
+ private final View.OnClickListener mCallSubjectClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mSubjectList.getVisibility() == View.VISIBLE) {
+ showCallHistory(false);
+ }
+ }
+ };
+
+ /**
+ * Item click listener which handles user clicks on the items in the list view. Dismisses
+ * the activity, returning the subject to the caller and closing the activity with the
+ * {@link Activity#RESULT_OK} result code.
+ */
+ private AdapterView.OnItemClickListener mItemClickListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> arg0, View view, int position, long arg3) {
+ mCallSubjectView.setText(mSubjectHistory.get(position));
+ showCallHistory(false);
+ }
+ };
+
+ /**
+ * Show the call subhect dialog given a phone number to dial (e.g. from the dialpad).
+ *
+ * @param activity The activity.
+ * @param number The number to dial.
+ */
+ public static void start(Activity activity, String number) {
+ start(activity,
+ -1 /* photoId */,
+ null /* photoUri */,
+ null /* contactUri */,
+ number /* nameOrNumber */,
+ false /* isBusiness */,
+ number /* number */,
+ null /* displayNumber */,
+ null /* numberLabel */,
+ null /* phoneAccountHandle */);
+ }
+
+ /**
+ * Creates a call subject dialog.
+ *
+ * @param activity The current activity.
+ * @param photoId The photo ID (used to populate contact photo).
+ * @param photoUri The photo Uri (used to populate contact photo).
+ * @param contactUri The Contact URI (used so quick contact can be invoked from contact photo).
+ * @param nameOrNumber The name or number of the callee.
+ * @param isBusiness {@code true} if a business is being called (used for contact photo).
+ * @param number The raw number to dial.
+ * @param displayNumber The number to dial, formatted for display.
+ * @param numberLabel The label for the number (if from a contact).
+ * @param phoneAccountHandle The phone account handle.
+ */
+ public static void start(Activity activity, long photoId, Uri photoUri, Uri contactUri,
+ String nameOrNumber, boolean isBusiness, String number, String displayNumber,
+ String numberLabel, PhoneAccountHandle phoneAccountHandle) {
+ Bundle arguments = new Bundle();
+ arguments.putLong(ARG_PHOTO_ID, photoId);
+ arguments.putParcelable(ARG_PHOTO_URI, photoUri);
+ arguments.putParcelable(ARG_CONTACT_URI, contactUri);
+ arguments.putString(ARG_NAME_OR_NUMBER, nameOrNumber);
+ arguments.putBoolean(ARG_IS_BUSINESS, isBusiness);
+ arguments.putString(ARG_NUMBER, number);
+ arguments.putString(ARG_DISPLAY_NUMBER, displayNumber);
+ arguments.putString(ARG_NUMBER_LABEL, numberLabel);
+ arguments.putParcelable(ARG_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ start(activity, arguments);
+ }
+
+ /**
+ * Shows the call subject dialog given a Bundle containing all the arguments required to
+ * display the dialog (e.g. from Quick Contacts).
+ *
+ * @param activity The activity.
+ * @param arguments The arguments bundle.
+ */
+ public static void start(Activity activity, Bundle arguments) {
+ Intent intent = new Intent(activity, CallSubjectDialog.class);
+ intent.putExtras(arguments);
+ activity.startActivity(intent);
+ }
+
+ /**
+ * Creates the dialog, inflating the layout and populating it with the name and phone number.
+ *
+ * @param savedInstanceState The last saved instance state of the Fragment,
+ * or null if this is a freshly created Fragment.
+ *
+ * @return Dialog instance.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAnimationDuration = getResources().getInteger(R.integer.call_subject_animation_duration);
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ mPhotoSize = getResources().getDimensionPixelSize(
+ R.dimen.call_subject_dialog_contact_photo_size);
+ readArguments();
+ mSubjectHistory = loadSubjectHistory(mPrefs);
+
+ setContentView(R.layout.dialog_call_subject);
+ getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mBackgroundView = findViewById(R.id.call_subject_dialog);
+ mBackgroundView.setOnClickListener(mBackgroundListener);
+ mDialogView = findViewById(R.id.dialog_view);
+ mContactPhoto = (QuickContactBadge) findViewById(R.id.contact_photo);
+ mNameView = (TextView) findViewById(R.id.name);
+ mNumberView = (TextView) findViewById(R.id.number);
+ mCallSubjectView = (EditText) findViewById(R.id.call_subject);
+ mCallSubjectView.addTextChangedListener(mTextWatcher);
+ mCallSubjectView.setOnClickListener(mCallSubjectClickListener);
+ InputFilter[] filters = new InputFilter[1];
+ filters[0] = new InputFilter.LengthFilter(mLimit);
+ mCallSubjectView.setFilters(filters);
+ mCharacterLimitView = (TextView) findViewById(R.id.character_limit);
+ mHistoryButton = findViewById(R.id.history_button);
+ mHistoryButton.setOnClickListener(mHistoryOnClickListener);
+ mHistoryButton.setVisibility(mSubjectHistory.isEmpty() ? View.GONE : View.VISIBLE);
+ mSendAndCallButton = findViewById(R.id.send_and_call_button);
+ mSendAndCallButton.setOnClickListener(mSendAndCallOnClickListener);
+ mSubjectList = (ListView) findViewById(R.id.subject_list);
+ mSubjectList.setOnItemClickListener(mItemClickListener);
+ mSubjectList.setVisibility(View.GONE);
+
+ updateContactInfo();
+ updateCharacterLimit();
+ }
+
+ /**
+ * Populates the contact info fields based on the current contact information.
+ */
+ private void updateContactInfo() {
+ if (mContactUri != null) {
+ setPhoto(mPhotoID, mPhotoUri, mContactUri, mNameOrNumber, mIsBusiness);
+ } else {
+ mContactPhoto.setVisibility(View.GONE);
+ }
+ mNameView.setText(mNameOrNumber);
+ if (!TextUtils.isEmpty(mNumberLabel) && !TextUtils.isEmpty(mDisplayNumber)) {
+ mNumberView.setVisibility(View.VISIBLE);
+ mNumberView.setText(getString(R.string.call_subject_type_and_number,
+ mNumberLabel, mDisplayNumber));
+ } else {
+ mNumberView.setVisibility(View.GONE);
+ mNumberView.setText(null);
+ }
+ }
+
+ /**
+ * Reads arguments from the fragment arguments and populates the necessary instance variables.
+ */
+ private void readArguments() {
+ Bundle arguments = getIntent().getExtras();
+ if (arguments == null) {
+ Log.e(TAG, "Arguments cannot be null.");
+ return;
+ }
+ mPhotoID = arguments.getLong(ARG_PHOTO_ID);
+ mPhotoUri = arguments.getParcelable(ARG_PHOTO_URI);
+ mContactUri = arguments.getParcelable(ARG_CONTACT_URI);
+ mNameOrNumber = arguments.getString(ARG_NAME_OR_NUMBER);
+ mIsBusiness = arguments.getBoolean(ARG_IS_BUSINESS);
+ mNumber = arguments.getString(ARG_NUMBER);
+ mDisplayNumber = arguments.getString(ARG_DISPLAY_NUMBER);
+ mNumberLabel = arguments.getString(ARG_NUMBER_LABEL);
+ mPhoneAccountHandle = arguments.getParcelable(ARG_PHONE_ACCOUNT_HANDLE);
+ }
+
+ /**
+ * Updates the character limit display, coloring the text RED when the limit is reached or
+ * exceeded.
+ */
+ private void updateCharacterLimit() {
+ int length = mCallSubjectView.length();
+ mCharacterLimitView.setText(
+ getString(R.string.call_subject_limit, length, mLimit));
+ if (length >= mLimit) {
+ mCharacterLimitView.setTextColor(getResources().getColor(
+ R.color.call_subject_limit_exceeded));
+ } else {
+ mCharacterLimitView.setTextColor(getResources().getColor(
+ R.color.dialtacts_secondary_text_color));
+ }
+ }
+
+ /**
+ * Sets the photo on the quick contact photo.
+ *
+ * @param photoId
+ * @param photoUri
+ * @param contactUri
+ * @param displayName
+ * @param isBusiness
+ */
+ private void setPhoto(long photoId, Uri photoUri, Uri contactUri, String displayName,
+ boolean isBusiness) {
+ mContactPhoto.assignContactUri(contactUri);
+ mContactPhoto.setOverlay(null);
+
+ int contactType;
+ if (isBusiness) {
+ contactType = ContactPhotoManager.TYPE_BUSINESS;
+ } else {
+ contactType = ContactPhotoManager.TYPE_DEFAULT;
+ }
+
+ String lookupKey = null;
+ if (contactUri != null) {
+ lookupKey = UriUtils.getLookupKeyFromUri(contactUri);
+ }
+
+ ContactPhotoManager.DefaultImageRequest
+ request = new ContactPhotoManager.DefaultImageRequest(
+ displayName, lookupKey, contactType, true /* isCircular */);
+
+ if (photoId == 0 && photoUri != null) {
+ ContactPhotoManager.getInstance(this).loadPhoto(mContactPhoto, photoUri,
+ mPhotoSize, false /* darkTheme */, true /* isCircular */, request);
+ } else {
+ ContactPhotoManager.getInstance(this).loadThumbnail(mContactPhoto, photoId,
+ false /* darkTheme */, true /* isCircular */, request);
+ }
+ }
+
+ /**
+ * Loads the subject history from shared preferences.
+ *
+ * @param prefs Shared preferences.
+ * @return List of subject history strings.
+ */
+ public static List loadSubjectHistory(SharedPreferences prefs) {
+ int historySize = prefs.getInt(PREF_KEY_SUBJECT_HISTORY_COUNT, 0);
+ List subjects = new ArrayList(historySize);
+
+ for (int ix = 0 ; ix < historySize; ix++) {
+ String historyItem = prefs.getString(PREF_KEY_SUBJECT_HISTORY_ITEM + ix, null);
+ if (!TextUtils.isEmpty(historyItem)) {
+ subjects.add(historyItem);
+ }
+ }
+
+ return subjects;
+ }
+
+ /**
+ * Saves the subject history list to shared prefs, removing older items so that there are only
+ * {@link #CALL_SUBJECT_HISTORY_SIZE} items at most.
+ *
+ * @param history The history.
+ */
+ private void saveSubjectHistory(List history) {
+ // Remove oldest subject(s).
+ while (history.size() > CALL_SUBJECT_HISTORY_SIZE) {
+ history.remove(0);
+ }
+
+ SharedPreferences.Editor editor = mPrefs.edit();
+ int historyCount = 0;
+ for (String subject : history) {
+ if (!TextUtils.isEmpty(subject)) {
+ editor.putString(PREF_KEY_SUBJECT_HISTORY_ITEM + historyCount,
+ subject);
+ historyCount++;
+ }
+ }
+ editor.putInt(PREF_KEY_SUBJECT_HISTORY_COUNT, historyCount);
+ editor.apply();
+ }
+
+ /**
+ * Hide software keyboard for the given {@link View}.
+ */
+ public void hideSoftKeyboard(Context context, View view) {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ /**
+ * Hides or shows the call history list.
+ *
+ * @param show {@code true} if the call history should be shown, {@code false} otherwise.
+ */
+ private void showCallHistory(final boolean show) {
+ // Bail early if the visibility has not changed.
+ if ((show && mSubjectList.getVisibility() == View.VISIBLE) ||
+ (!show && mSubjectList.getVisibility() == View.GONE)) {
+ return;
+ }
+
+ final int dialogStartingBottom = mDialogView.getBottom();
+ if (show) {
+ // Showing the subject list; bind the list of history items to the list and show it.
+ ArrayAdapter adapter = new ArrayAdapter(CallSubjectDialog.this,
+ R.layout.call_subject_history_list_item, mSubjectHistory);
+ mSubjectList.setAdapter(adapter);
+ mSubjectList.setVisibility(View.VISIBLE);
+ } else {
+ // Hiding the subject list.
+ mSubjectList.setVisibility(View.GONE);
+ }
+
+ // Use a ViewTreeObserver so that we can animate between the pre-layout and post-layout
+ // states.
+ final ViewTreeObserver observer = mBackgroundView.getViewTreeObserver();
+ observer.addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ // We don't want to continue getting called.
+ if (observer.isAlive()) {
+ observer.removeOnPreDrawListener(this);
+ }
+
+ // Determine the amount the dialog has shifted due to the relayout.
+ int shiftAmount = dialogStartingBottom - mDialogView.getBottom();
+
+ // If the dialog needs to be shifted, do that now.
+ if (shiftAmount != 0) {
+ // Start animation in translated state and animate to translationY 0.
+ mDialogView.setTranslationY(shiftAmount);
+ mDialogView.animate()
+ .translationY(0)
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mAnimationDuration)
+ .start();
+ }
+
+ if (show) {
+ // Show the subhect list.
+ mSubjectList.setTranslationY(mSubjectList.getHeight());
+
+ mSubjectList.animate()
+ .translationY(0)
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mAnimationDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mSubjectList.setVisibility(View.VISIBLE);
+ }
+ })
+ .start();
+ } else {
+ // Hide the subject list.
+ mSubjectList.setTranslationY(0);
+
+ mSubjectList.animate()
+ .translationY(mSubjectList.getHeight())
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mAnimationDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mSubjectList.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ }
+ })
+ .start();
+ }
+ return true;
+ }
+ }
+ );
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/dialog/ClearFrequentsDialog.java b/ContactsCommon/src/com/android/contacts/common/dialog/ClearFrequentsDialog.java
new file mode 100644
index 0000000..2fab3e1
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/dialog/ClearFrequentsDialog.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.PermissionsUtil;
+
+/**
+ * Dialog that clears the frequently contacted list after confirming with the user.
+ */
+public class ClearFrequentsDialog extends DialogFragment {
+ /** Preferred way to show this dialog */
+ public static void show(FragmentManager fragmentManager) {
+ ClearFrequentsDialog dialog = new ClearFrequentsDialog();
+ dialog.show(fragmentManager, "clearFrequents");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity().getApplicationContext();
+ final ContentResolver resolver = getActivity().getContentResolver();
+ final OnClickListener okListener = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (!PermissionsUtil.hasContactsPermissions(context)) {
+ return;
+ }
+ final IndeterminateProgressDialog progressDialog = IndeterminateProgressDialog.show(
+ getFragmentManager(), getString(R.string.clearFrequentsProgress_title),
+ null, 500);
+ final AsyncTask task = new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ resolver.delete(ContactsContract.DataUsageFeedback.DELETE_USAGE_URI,
+ null, null);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ progressDialog.dismiss();
+ }
+ };
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ };
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.clearFrequentsConfirmation_title)
+ .setMessage(R.string.clearFrequentsConfirmation)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, okListener)
+ .setCancelable(true)
+ .create();
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/dialog/IndeterminateProgressDialog.java b/ContactsCommon/src/com/android/contacts/common/dialog/IndeterminateProgressDialog.java
new file mode 100644
index 0000000..2fe059f
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/dialog/IndeterminateProgressDialog.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.dialog;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+
+/**
+ * Indeterminate progress dialog wrapped up in a DialogFragment to work even when the device
+ * orientation is changed. Currently, only supports adding a title and/or message to the progress
+ * dialog. There is an additional parameter of the minimum amount of time to display the progress
+ * dialog even after a call to dismiss the dialog {@link #dismiss()} or
+ * {@link #dismissAllowingStateLoss()}.
+ *
+ * To create and show the progress dialog, use
+ * {@link #show(FragmentManager, CharSequence, CharSequence, long)} and retain the reference to the
+ * IndeterminateProgressDialog instance.
+ *
+ * To dismiss the dialog, use {@link #dismiss()} or {@link #dismissAllowingStateLoss()} on the
+ * instance. The instance returned by
+ * {@link #show(FragmentManager, CharSequence, CharSequence, long)} is guaranteed to be valid
+ * after a device orientation change because the {@link #setRetainInstance(boolean)} is called
+ * internally with true.
+ */
+public class IndeterminateProgressDialog extends DialogFragment {
+ private static final String TAG = IndeterminateProgressDialog.class.getSimpleName();
+
+ private CharSequence mTitle;
+ private CharSequence mMessage;
+ private long mMinDisplayTime;
+ private long mShowTime = 0;
+ private boolean mActivityReady = false;
+ private Dialog mOldDialog;
+ private final Handler mHandler = new Handler();
+ private boolean mCalledSuperDismiss = false;
+ private boolean mAllowStateLoss;
+ private final Runnable mDismisser = new Runnable() {
+ @Override
+ public void run() {
+ superDismiss();
+ }
+ };
+
+ /**
+ * Creates and shows an indeterminate progress dialog. Once the progress dialog is shown, it
+ * will be shown for at least the minDisplayTime (in milliseconds), so that the progress dialog
+ * does not flash in and out to quickly.
+ */
+ public static IndeterminateProgressDialog show(FragmentManager fragmentManager,
+ CharSequence title, CharSequence message, long minDisplayTime) {
+ IndeterminateProgressDialog dialogFragment = new IndeterminateProgressDialog();
+ dialogFragment.mTitle = title;
+ dialogFragment.mMessage = message;
+ dialogFragment.mMinDisplayTime = minDisplayTime;
+ dialogFragment.show(fragmentManager, TAG);
+ dialogFragment.mShowTime = System.currentTimeMillis();
+ dialogFragment.setCancelable(false);
+
+ return dialogFragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Create the progress dialog and set its properties
+ final ProgressDialog dialog = new ProgressDialog(getActivity());
+ dialog.setIndeterminate(true);
+ dialog.setIndeterminateDrawable(null);
+ dialog.setTitle(mTitle);
+ dialog.setMessage(mMessage);
+
+ return dialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mActivityReady = true;
+
+ // Check if superDismiss() had been called before. This can happen if in a long
+ // running operation, the user hits the home button and closes this fragment's activity.
+ // Upon returning, we want to dismiss this progress dialog fragment.
+ if (mCalledSuperDismiss) {
+ superDismiss();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mActivityReady = false;
+ }
+
+ /**
+ * There is a race condition that is not handled properly by the DialogFragment class.
+ * If we don't check that this onDismiss callback isn't for the old progress dialog from before
+ * the device orientation change, then this will cause the newly created dialog after the
+ * orientation change to be dismissed immediately.
+ */
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (mOldDialog != null && mOldDialog == dialog) {
+ // This is the callback from the old progress dialog that was already dismissed before
+ // the device orientation change, so just ignore it.
+ return;
+ }
+ super.onDismiss(dialog);
+ }
+
+ /**
+ * Save the old dialog that is about to get destroyed in case this is due to a change
+ * in device orientation. This will allow us to intercept the callback to
+ * {@link #onDismiss(DialogInterface)} in case the callback happens after a new progress dialog
+ * instance was created.
+ */
+ @Override
+ public void onDestroyView() {
+ mOldDialog = getDialog();
+ super.onDestroyView();
+ }
+
+ /**
+ * This tells the progress dialog to dismiss itself after guaranteeing to be shown for the
+ * specified time in {@link #show(FragmentManager, CharSequence, CharSequence, long)}.
+ */
+ @Override
+ public void dismiss() {
+ mAllowStateLoss = false;
+ dismissWhenReady();
+ }
+
+ /**
+ * This tells the progress dialog to dismiss itself (with state loss) after guaranteeing to be
+ * shown for the specified time in
+ * {@link #show(FragmentManager, CharSequence, CharSequence, long)}.
+ */
+ @Override
+ public void dismissAllowingStateLoss() {
+ mAllowStateLoss = true;
+ dismissWhenReady();
+ }
+
+ /**
+ * Tells the progress dialog to dismiss itself after guaranteeing that the dialog had been
+ * showing for at least the minimum display time as set in
+ * {@link #show(FragmentManager, CharSequence, CharSequence, long)}.
+ */
+ private void dismissWhenReady() {
+ // Compute how long the dialog has been showing
+ final long shownTime = System.currentTimeMillis() - mShowTime;
+ if (shownTime >= mMinDisplayTime) {
+ // dismiss immediately
+ mHandler.post(mDismisser);
+ } else {
+ // Need to wait some more, so compute the amount of time to sleep.
+ final long sleepTime = mMinDisplayTime - shownTime;
+ mHandler.postDelayed(mDismisser, sleepTime);
+ }
+ }
+
+ /**
+ * Actually dismiss the dialog fragment.
+ */
+ private void superDismiss() {
+ mCalledSuperDismiss = true;
+ if (mActivityReady) {
+ // The fragment is either in onStart or past it, but has not gotten to onStop yet.
+ // It is safe to dismiss this dialog fragment.
+ if (mAllowStateLoss) {
+ super.dismissAllowingStateLoss();
+ } else {
+ super.dismiss();
+ }
+ }
+ // If mActivityReady is false, then this dialog fragment has already passed the onStop
+ // state. This can happen if the user hit the 'home' button before this dialog fragment was
+ // dismissed or if there is a configuration change.
+ // In the event that this dialog fragment is re-attached and reaches onStart (e.g.,
+ // because the user returns to this fragment's activity or the device configuration change
+ // has re-attached this dialog fragment), because the mCalledSuperDismiss flag was set to
+ // true, this dialog fragment will be dismissed within onStart. So, there's nothing else
+ // that needs to be done.
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/editor/SelectAccountDialogFragment.java b/ContactsCommon/src/com/android/contacts/common/editor/SelectAccountDialogFragment.java
new file mode 100644
index 0000000..c2ebbbf
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/editor/SelectAccountDialogFragment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.editor;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.util.AccountsListAdapter;
+import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
+
+/**
+ * Shows a dialog asking the user which account to chose.
+ *
+ * The result is passed to {@code targetFragment} passed to {@link #show}.
+ */
+public final class SelectAccountDialogFragment extends DialogFragment {
+ public static final String TAG = "SelectAccountDialogFragment";
+
+ private static final String KEY_TITLE_RES_ID = "title_res_id";
+ private static final String KEY_LIST_FILTER = "list_filter";
+ private static final String KEY_EXTRA_ARGS = "extra_args";
+
+ public SelectAccountDialogFragment() { // All fragments must have a public default constructor.
+ }
+
+ /**
+ * Show the dialog.
+ *
+ * @param fragmentManager {@link FragmentManager}.
+ * @param targetFragment {@link Fragment} that implements {@link Listener}.
+ * @param titleResourceId resource ID to use as the title.
+ * @param accountListFilter account filter.
+ * @param extraArgs Extra arguments, which will later be passed to
+ * {@link Listener#onAccountChosen}. {@code null} will be converted to
+ * {@link Bundle#EMPTY}.
+ */
+ public static void show(FragmentManager fragmentManager,
+ F targetFragment, int titleResourceId,
+ AccountListFilter accountListFilter, Bundle extraArgs) {
+ final Bundle args = new Bundle();
+ args.putInt(KEY_TITLE_RES_ID, titleResourceId);
+ args.putSerializable(KEY_LIST_FILTER, accountListFilter);
+ args.putBundle(KEY_EXTRA_ARGS, (extraArgs == null) ? Bundle.EMPTY : extraArgs);
+
+ final SelectAccountDialogFragment instance = new SelectAccountDialogFragment();
+ instance.setArguments(args);
+ instance.setTargetFragment(targetFragment, 0);
+ instance.show(fragmentManager, null);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final Bundle args = getArguments();
+
+ final AccountListFilter filter = (AccountListFilter) args.getSerializable(KEY_LIST_FILTER);
+ final AccountsListAdapter accountAdapter = new AccountsListAdapter(builder.getContext(),
+ filter);
+
+ final DialogInterface.OnClickListener clickListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ onAccountSelected(accountAdapter.getItem(which));
+ }
+ };
+
+ builder.setTitle(args.getInt(KEY_TITLE_RES_ID));
+ builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
+ final AlertDialog result = builder.create();
+ return result;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null && targetFragment instanceof Listener) {
+ final Listener target = (Listener) targetFragment;
+ target.onAccountSelectorCancelled();
+ }
+ }
+
+ /**
+ * Calls {@link Listener#onAccountChosen} of {@code targetFragment}.
+ */
+ private void onAccountSelected(AccountWithDataSet account) {
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null && targetFragment instanceof Listener) {
+ final Listener target = (Listener) targetFragment;
+ target.onAccountChosen(account, getArguments().getBundle(KEY_EXTRA_ARGS));
+ }
+ }
+
+ public interface Listener {
+ void onAccountChosen(AccountWithDataSet account, Bundle extraArgs);
+ void onAccountSelectorCancelled();
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/extensions/ExtendedPhoneDirectoriesManager.java b/ContactsCommon/src/com/android/contacts/common/extensions/ExtendedPhoneDirectoriesManager.java
new file mode 100644
index 0000000..eb25934
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/extensions/ExtendedPhoneDirectoriesManager.java
@@ -0,0 +1,26 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+
+package com.android.contacts.common.extensions;
+
+import android.content.Context;
+
+import com.android.contacts.common.list.DirectoryPartition;
+
+import java.util.List;
+
+/**
+ * An interface for adding extended phone directories to
+ * {@link com.android.contacts.common.list.PhoneNumberListAdapter}.
+ * An app that wishes to add custom phone directories should implement this class and advertise it
+ * in assets/contacts_extensions.properties. {@link ExtensionsFactory} will load the implementation
+ * and the extended directories will be added by
+ * {@link com.android.contacts.common.list.PhoneNumberListAdapter}.
+ */
+public interface ExtendedPhoneDirectoriesManager {
+
+ /**
+ * Return a list of extended directories to add. May return null if no directories are to be
+ * added.
+ */
+ List getExtendedDirectories(Context context);
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/extensions/ExtensionsFactory.java b/ContactsCommon/src/com/android/contacts/common/extensions/ExtensionsFactory.java
new file mode 100644
index 0000000..d52429e
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/extensions/ExtensionsFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.extensions;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+
+/*
+ * A framework for adding extensions to Dialer. This class reads a property file from
+ * assets/contacts_extensions.properties and loads extension classes that an app has defined. If
+ * an extension class was not defined, null is returned.
+ */
+public class ExtensionsFactory {
+
+ private static String TAG = "ExtensionsFactory";
+
+ // Config filename for mappings of various class names to their custom
+ // implementations.
+ private static final String EXTENSIONS_PROPERTIES = "contacts_extensions.properties";
+
+ private static final String EXTENDED_PHONE_DIRECTORIES_KEY = "extendedPhoneDirectories";
+
+ private static Properties sProperties = null;
+ private static ExtendedPhoneDirectoriesManager mExtendedPhoneDirectoriesManager = null;
+
+ public static void init(Context context) {
+ if (sProperties != null) {
+ return;
+ }
+ try {
+ final InputStream fileStream = context.getAssets().open(EXTENSIONS_PROPERTIES);
+ sProperties = new Properties();
+ sProperties.load(fileStream);
+ fileStream.close();
+
+ final String className = sProperties.getProperty(EXTENDED_PHONE_DIRECTORIES_KEY);
+ if (className != null) {
+ mExtendedPhoneDirectoriesManager = createInstance(className);
+ } else {
+ Log.d(TAG, EXTENDED_PHONE_DIRECTORIES_KEY + " not found in properties file.");
+ }
+
+ } catch (FileNotFoundException e) {
+ // No custom extensions. Ignore.
+ Log.d(TAG, "No custom extensions.");
+ } catch (IOException e) {
+ Log.d(TAG, e.toString());
+ }
+ }
+
+ private static T createInstance(String className) {
+ try {
+ Class> c = Class.forName(className);
+ //noinspection unchecked
+ return (T) c.newInstance();
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, className + ": unable to create instance.", e);
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, className + ": unable to create instance.", e);
+ } catch (InstantiationException e) {
+ Log.e(TAG, className + ": unable to create instance.", e);
+ }
+ return null;
+ }
+
+ public static ExtendedPhoneDirectoriesManager getExtendedPhoneDirectoriesManager() {
+ return mExtendedPhoneDirectoriesManager;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/format/FormatUtils.java b/ContactsCommon/src/com/android/contacts/common/format/FormatUtils.java
new file mode 100644
index 0000000..376ff13
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/format/FormatUtils.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.format;
+
+import android.database.CharArrayBuffer;
+import android.graphics.Typeface;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+
+/**
+ * Assorted utility methods related to text formatting in Contacts.
+ */
+public class FormatUtils {
+
+ /**
+ * Finds the earliest point in buffer1 at which the first part of buffer2 matches. For example,
+ * overlapPoint("abcd", "cdef") == 2.
+ */
+ public static int overlapPoint(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
+ if (buffer1 == null || buffer2 == null) {
+ return -1;
+ }
+ return overlapPoint(Arrays.copyOfRange(buffer1.data, 0, buffer1.sizeCopied),
+ Arrays.copyOfRange(buffer2.data, 0, buffer2.sizeCopied));
+ }
+
+ /**
+ * Finds the earliest point in string1 at which the first part of string2 matches. For example,
+ * overlapPoint("abcd", "cdef") == 2.
+ */
+ @VisibleForTesting
+ public static int overlapPoint(String string1, String string2) {
+ if (string1 == null || string2 == null) {
+ return -1;
+ }
+ return overlapPoint(string1.toCharArray(), string2.toCharArray());
+ }
+
+ /**
+ * Finds the earliest point in array1 at which the first part of array2 matches. For example,
+ * overlapPoint("abcd", "cdef") == 2.
+ */
+ public static int overlapPoint(char[] array1, char[] array2) {
+ if (array1 == null || array2 == null) {
+ return -1;
+ }
+ int count1 = array1.length;
+ int count2 = array2.length;
+
+ // Ignore matching tails of the two arrays.
+ while (count1 > 0 && count2 > 0 && array1[count1 - 1] == array2[count2 - 1]) {
+ count1--;
+ count2--;
+ }
+
+ int size = count2;
+ for (int i = 0; i < count1; i++) {
+ if (i + size > count1) {
+ size = count1 - i;
+ }
+ int j;
+ for (j = 0; j < size; j++) {
+ if (array1[i+j] != array2[j]) {
+ break;
+ }
+ }
+ if (j == size) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Applies the given style to a range of the input CharSequence.
+ * @param style The style to apply (see the style constants in {@link Typeface}).
+ * @param input The CharSequence to style.
+ * @param start Starting index of the range to style (will be clamped to be a minimum of 0).
+ * @param end Ending index of the range to style (will be clamped to a maximum of the input
+ * length).
+ * @param flags Bitmask for configuring behavior of the span. See {@link android.text.Spanned}.
+ * @return The styled CharSequence.
+ */
+ public static CharSequence applyStyleToSpan(int style, CharSequence input, int start, int end,
+ int flags) {
+ // Enforce bounds of the char sequence.
+ start = Math.max(0, start);
+ end = Math.min(input.length(), end);
+ SpannableString text = new SpannableString(input);
+ text.setSpan(new StyleSpan(style), start, end, flags);
+ return text;
+ }
+
+ @VisibleForTesting
+ public static void copyToCharArrayBuffer(String text, CharArrayBuffer buffer) {
+ if (text != null) {
+ char[] data = buffer.data;
+ if (data == null || data.length < text.length()) {
+ buffer.data = text.toCharArray();
+ } else {
+ text.getChars(0, text.length(), data, 0);
+ }
+ buffer.sizeCopied = text.length();
+ } else {
+ buffer.sizeCopied = 0;
+ }
+ }
+
+ /** Returns a String that represents the content of the given {@link CharArrayBuffer}. */
+ @VisibleForTesting
+ public static String charArrayBufferToString(CharArrayBuffer buffer) {
+ return new String(buffer.data, 0, buffer.sizeCopied);
+ }
+
+ /**
+ * Finds the index of the first word that starts with the given prefix.
+ *
+ * If not found, returns -1.
+ *
+ * @param text the text in which to search for the prefix
+ * @param prefix the text to find, in upper case letters
+ */
+ public static int indexOfWordPrefix(CharSequence text, String prefix) {
+ if (prefix == null || text == null) {
+ return -1;
+ }
+
+ int textLength = text.length();
+ int prefixLength = prefix.length();
+
+ if (prefixLength == 0 || textLength < prefixLength) {
+ return -1;
+ }
+
+ int i = 0;
+ while (i < textLength) {
+ // Skip non-word characters
+ while (i < textLength && !Character.isLetterOrDigit(text.charAt(i))) {
+ i++;
+ }
+
+ if (i + prefixLength > textLength) {
+ return -1;
+ }
+
+ // Compare the prefixes
+ int j;
+ for (j = 0; j < prefixLength; j++) {
+ if (Character.toUpperCase(text.charAt(i + j)) != prefix.charAt(j)) {
+ break;
+ }
+ }
+ if (j == prefixLength) {
+ return i;
+ }
+
+ // Skip this word
+ while (i < textLength && Character.isLetterOrDigit(text.charAt(i))) {
+ i++;
+ }
+ }
+
+ return -1;
+ }
+
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/format/SpannedTestUtils.java b/ContactsCommon/src/com/android/contacts/common/format/SpannedTestUtils.java
new file mode 100644
index 0000000..463d7a8
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/format/SpannedTestUtils.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.format;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
+import android.widget.TextView;
+
+import junit.framework.Assert;
+
+/**
+ * Utility class to check the value of spanned text in text views.
+ */
+@SmallTest
+public class SpannedTestUtils {
+ /**
+ * Checks that the text contained in the text view matches the given HTML text.
+ *
+ * @param expectedHtmlText the expected text to be in the text view
+ * @param textView the text view from which to get the text
+ */
+ public static void checkHtmlText(String expectedHtmlText, TextView textView) {
+ String actualHtmlText = Html.toHtml((Spanned) textView.getText());
+ if (TextUtils.isEmpty(expectedHtmlText)) {
+ // If the text is empty, it does not add the
bits to it.
+ Assert.assertEquals("", actualHtmlText);
+ } else {
+ Assert.assertEquals("" + expectedHtmlText + "
\n", actualHtmlText);
+ }
+ }
+
+
+ /**
+ * Assert span exists in the correct location.
+ *
+ * @param seq The spannable string to check.
+ * @param start The starting index.
+ * @param end The ending index.
+ */
+ public static void assertPrefixSpan(CharSequence seq, int start, int end) {
+ Assert.assertTrue(seq instanceof Spanned);
+ Spanned spannable = (Spanned) seq;
+
+ if (start > 0) {
+ Assert.assertEquals(0, getNumForegroundColorSpansBetween(spannable, 0, start - 1));
+ }
+ Assert.assertEquals(1, getNumForegroundColorSpansBetween(spannable, start, end));
+ Assert.assertEquals(0, getNumForegroundColorSpansBetween(spannable, end + 1,
+ spannable.length() - 1));
+ }
+
+ private static int getNumForegroundColorSpansBetween(Spanned value, int start, int end) {
+ return value.getSpans(start, end, StyleSpan.class).length;
+ }
+
+ /**
+ * Asserts that the given character sequence is not a Spanned object and text is correct.
+ *
+ * @param seq The sequence to check.
+ * @param expected The expected text.
+ */
+ public static void assertNotSpanned(CharSequence seq, String expected) {
+ Assert.assertFalse(seq instanceof Spanned);
+ Assert.assertEquals(expected, seq);
+ }
+
+ public static int getNextTransition(SpannableString seq, int start) {
+ return seq.nextSpanTransition(start, seq.length(), StyleSpan.class);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/format/TextHighlighter.java b/ContactsCommon/src/com/android/contacts/common/format/TextHighlighter.java
new file mode 100644
index 0000000..496dcda
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/format/TextHighlighter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.format;
+
+import android.graphics.Typeface;
+import android.text.SpannableString;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+import android.widget.TextView;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Highlights the text in a text field.
+ */
+public class TextHighlighter {
+ private final String TAG = TextHighlighter.class.getSimpleName();
+ private final static boolean DEBUG = false;
+
+ private int mTextStyle;
+
+ private CharacterStyle mTextStyleSpan;
+
+ public TextHighlighter(int textStyle) {
+ mTextStyle = textStyle;
+ mTextStyleSpan = getStyleSpan();
+ }
+
+ /**
+ * Sets the text on the given text view, highlighting the word that matches the given prefix.
+ *
+ * @param view the view on which to set the text
+ * @param text the string to use as the text
+ * @param prefix the prefix to look for
+ */
+ public void setPrefixText(TextView view, String text, String prefix) {
+ view.setText(applyPrefixHighlight(text, prefix));
+ }
+
+ private CharacterStyle getStyleSpan() {
+ return new StyleSpan(mTextStyle);
+ }
+
+ /**
+ * Applies highlight span to the text.
+ * @param text Text sequence to be highlighted.
+ * @param start Start position of the highlight sequence.
+ * @param end End position of the highlight sequence.
+ */
+ public void applyMaskingHighlight(SpannableString text, int start, int end) {
+ /** Sets text color of the masked locations to be highlighted. */
+ text.setSpan(getStyleSpan(), start, end, 0);
+ }
+
+ /**
+ * Returns a CharSequence which highlights the given prefix if found in the given text.
+ *
+ * @param text the text to which to apply the highlight
+ * @param prefix the prefix to look for
+ */
+ public CharSequence applyPrefixHighlight(CharSequence text, String prefix) {
+ if (prefix == null) {
+ return text;
+ }
+
+ // Skip non-word characters at the beginning of prefix.
+ int prefixStart = 0;
+ while (prefixStart < prefix.length() &&
+ !Character.isLetterOrDigit(prefix.charAt(prefixStart))) {
+ prefixStart++;
+ }
+ final String trimmedPrefix = prefix.substring(prefixStart);
+
+ int index = FormatUtils.indexOfWordPrefix(text, trimmedPrefix);
+ if (index != -1) {
+ final SpannableString result = new SpannableString(text);
+ result.setSpan(mTextStyleSpan, index, index + trimmedPrefix.length(), 0 /* flags */);
+ return result;
+ } else {
+ return text;
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java b/ContactsCommon/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java
new file mode 100644
index 0000000..7306fa7
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.interactions;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.editor.SelectAccountDialogFragment;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.util.AccountSelectionUtil;
+import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
+import com.android.contacts.common.vcard.ExportVCardActivity;
+import com.android.contacts.common.vcard.VCardCommonArguments;
+import com.android.contacts.commonbind.analytics.AnalyticsUtil;
+
+import java.util.List;
+
+/**
+ * An dialog invoked to import/export contacts.
+ */
+public class ImportExportDialogFragment extends DialogFragment
+ implements SelectAccountDialogFragment.Listener {
+ public static final String TAG = "ImportExportDialogFragment";
+
+ private static final String KEY_RES_ID = "resourceId";
+ private static final String KEY_SUBSCRIPTION_ID = "subscriptionId";
+ private static final String ARG_CONTACTS_ARE_AVAILABLE = "CONTACTS_ARE_AVAILABLE";
+
+ private final String[] LOOKUP_PROJECTION = new String[] {
+ Contacts.LOOKUP_KEY
+ };
+
+ private SubscriptionManager mSubscriptionManager;
+
+ /** Preferred way to show this dialog */
+ public static void show(FragmentManager fragmentManager, boolean contactsAreAvailable,
+ Class callingActivity) {
+ final ImportExportDialogFragment fragment = new ImportExportDialogFragment();
+ Bundle args = new Bundle();
+ args.putBoolean(ARG_CONTACTS_ARE_AVAILABLE, contactsAreAvailable);
+ args.putString(VCardCommonArguments.ARG_CALLING_ACTIVITY, callingActivity.getName());
+ fragment.setArguments(args);
+ fragment.show(fragmentManager, ImportExportDialogFragment.TAG);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ AnalyticsUtil.sendScreenView(this);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Wrap our context to inflate list items using the correct theme
+ final Resources res = getActivity().getResources();
+ final LayoutInflater dialogInflater = (LayoutInflater)getActivity()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ final boolean contactsAreAvailable = getArguments().getBoolean(ARG_CONTACTS_ARE_AVAILABLE);
+ final String callingActivity = getArguments().getString(
+ VCardCommonArguments.ARG_CALLING_ACTIVITY);
+
+ // Adapter that shows a list of string resources
+ final ArrayAdapter adapter = new ArrayAdapter(getActivity(),
+ R.layout.select_dialog_item) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final TextView result = (TextView)(convertView != null ? convertView :
+ dialogInflater.inflate(R.layout.select_dialog_item, parent, false));
+
+ result.setText(getItem(position).mLabel);
+ return result;
+ }
+ };
+
+ final TelephonyManager manager =
+ (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+
+ mSubscriptionManager = SubscriptionManager.from(getActivity());
+
+ if (res.getBoolean(R.bool.config_allow_import_from_vcf_file)) {
+ adapter.add(new AdapterEntry(getString(R.string.import_from_vcf_file),
+ R.string.import_from_vcf_file));
+ }
+ if (manager != null && res.getBoolean(R.bool.config_allow_sim_import)) {
+ List subInfoRecords = null;
+ try {
+ subInfoRecords = mSubscriptionManager.getActiveSubscriptionInfoList();
+ } catch (SecurityException e) {
+ Log.w(TAG, "SecurityException thrown, lack permission for"
+ + " getActiveSubscriptionInfoList", e);
+ }
+ if (subInfoRecords != null) {
+ if (subInfoRecords.size() == 1) {
+ adapter.add(new AdapterEntry(getString(R.string.import_from_sim),
+ R.string.import_from_sim, subInfoRecords.get(0).getSubscriptionId()));
+ } else {
+ for (SubscriptionInfo record : subInfoRecords) {
+ adapter.add(new AdapterEntry(getSubDescription(record),
+ R.string.import_from_sim, record.getSubscriptionId()));
+ }
+ }
+ }
+ }
+ if (res.getBoolean(R.bool.config_allow_export)) {
+ if (contactsAreAvailable) {
+ adapter.add(new AdapterEntry(getString(R.string.export_to_vcf_file),
+ R.string.export_to_vcf_file));
+ }
+ }
+ if (res.getBoolean(R.bool.config_allow_share_visible_contacts)) {
+ if (contactsAreAvailable) {
+ adapter.add(new AdapterEntry(getString(R.string.share_visible_contacts),
+ R.string.share_visible_contacts));
+ }
+ }
+
+ final DialogInterface.OnClickListener clickListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ boolean dismissDialog;
+ final int resId = adapter.getItem(which).mChoiceResourceId;
+ if (resId == R.string.import_from_sim || resId == R.string.import_from_vcf_file) {
+ dismissDialog = handleImportRequest(resId,
+ adapter.getItem(which).mSubscriptionId);
+ } else if (resId == R.string.export_to_vcf_file) {
+ dismissDialog = true;
+ Intent exportIntent = new Intent(getActivity(), ExportVCardActivity.class);
+ exportIntent.putExtra(VCardCommonArguments.ARG_CALLING_ACTIVITY,
+ callingActivity);
+ getActivity().startActivity(exportIntent);
+ } else if (resId == R.string.share_visible_contacts) {
+ dismissDialog = true;
+ doShareVisibleContacts();
+ } else {
+ dismissDialog = true;
+ Log.e(TAG, "Unexpected resource: "
+ + getActivity().getResources().getResourceEntryName(resId));
+ }
+ if (dismissDialog) {
+ dialog.dismiss();
+ }
+ }
+ };
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(contactsAreAvailable
+ ? R.string.dialog_import_export
+ : R.string.dialog_import)
+ .setSingleChoiceItems(adapter, -1, clickListener)
+ .create();
+ }
+
+ private void doShareVisibleContacts() {
+ try {
+ // TODO move the query into a loader and do this in a background thread
+ final Cursor cursor = getActivity().getContentResolver().query(Contacts.CONTENT_URI,
+ LOOKUP_PROJECTION, Contacts.IN_VISIBLE_GROUP + "!=0", null, null);
+ if (cursor != null) {
+ try {
+ if (!cursor.moveToFirst()) {
+ Toast.makeText(getActivity(), R.string.share_error, Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ StringBuilder uriListBuilder = new StringBuilder();
+ int index = 0;
+ do {
+ if (index != 0)
+ uriListBuilder.append(':');
+ uriListBuilder.append(cursor.getString(0));
+ index++;
+ } while (cursor.moveToNext());
+ Uri uri = Uri.withAppendedPath(
+ Contacts.CONTENT_MULTI_VCARD_URI,
+ Uri.encode(uriListBuilder.toString()));
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType(Contacts.CONTENT_VCARD_TYPE);
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ ImplicitIntentsUtil.startActivityOutsideApp(getActivity(), intent);
+ } finally {
+ cursor.close();
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Sharing visible contacts failed", e);
+ Toast.makeText(getContext(), R.string.share_visible_contacts_failure,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ /**
+ * Handle "import from SIM" and "import from SD".
+ *
+ * @return {@code true} if the dialog show be closed. {@code false} otherwise.
+ */
+ private boolean handleImportRequest(int resId, int subscriptionId) {
+ // There are three possibilities:
+ // - more than one accounts -> ask the user
+ // - just one account -> use the account without asking the user
+ // - no account -> use phone-local storage without asking the user
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
+ final List accountList = accountTypes.getAccounts(true);
+ final int size = accountList.size();
+ if (size > 1) {
+ // Send over to the account selector
+ final Bundle args = new Bundle();
+ args.putInt(KEY_RES_ID, resId);
+ args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
+ SelectAccountDialogFragment.show(
+ getFragmentManager(), this,
+ R.string.dialog_new_contact_account,
+ AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, args);
+
+ // In this case, because this DialogFragment is used as a target fragment to
+ // SelectAccountDialogFragment, we can't close it yet. We close the dialog when
+ // we get a callback from it.
+ return false;
+ }
+
+ AccountSelectionUtil.doImport(getActivity(), resId,
+ (size == 1 ? accountList.get(0) : null), subscriptionId);
+ return true; // Close the dialog.
+ }
+
+ /**
+ * Called when an account is selected on {@link SelectAccountDialogFragment}.
+ */
+ @Override
+ public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) {
+ AccountSelectionUtil.doImport(getActivity(), extraArgs.getInt(KEY_RES_ID),
+ account, extraArgs.getInt(KEY_SUBSCRIPTION_ID));
+
+ // At this point the dialog is still showing (which is why we can use getActivity() above)
+ // So close it.
+ dismiss();
+ }
+
+ @Override
+ public void onAccountSelectorCancelled() {
+ // See onAccountChosen() -- at this point the dialog is still showing. Close it.
+ dismiss();
+ }
+
+ private CharSequence getSubDescription(SubscriptionInfo record) {
+ CharSequence name = record.getDisplayName();
+ if (TextUtils.isEmpty(record.getNumber())) {
+ // Don't include the phone number in the description, since we don't know the number.
+ return getString(R.string.import_from_sim_summary_no_number, name);
+ }
+ return TextUtils.expandTemplate(
+ getString(R.string.import_from_sim_summary),
+ name,
+ PhoneNumberUtils.createTtsSpannable(record.getNumber()));
+ }
+
+ private static class AdapterEntry {
+ public final CharSequence mLabel;
+ public final int mChoiceResourceId;
+ public final int mSubscriptionId;
+
+ public AdapterEntry(CharSequence label, int resId, int subId) {
+ mLabel = label;
+ mChoiceResourceId = resId;
+ mSubscriptionId = subId;
+ }
+
+ public AdapterEntry(String label, int resId) {
+ // Store a nonsense value for mSubscriptionId. If this constructor is used,
+ // the mSubscriptionId value should not be read later.
+ this(label, resId, /* subId = */ -1);
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/interactions/TouchPointManager.java b/ContactsCommon/src/com/android/contacts/common/interactions/TouchPointManager.java
new file mode 100644
index 0000000..4c38e22
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/interactions/TouchPointManager.java
@@ -0,0 +1,46 @@
+package com.android.contacts.common.interactions;
+
+import android.graphics.Point;
+
+/**
+ * Singleton class to keep track of where the user last touched the screen.
+ *
+ * Used to pass on to the InCallUI for animation.
+ */
+public class TouchPointManager {
+ public static final String TOUCH_POINT = "touchPoint";
+
+ private static TouchPointManager sInstance = new TouchPointManager();
+
+ private Point mPoint = new Point();
+
+ /**
+ * Private constructor. Instance should only be acquired through getInstance().
+ */
+ private TouchPointManager() {
+ }
+
+ public static TouchPointManager getInstance() {
+ return sInstance;
+ }
+
+ public Point getPoint() {
+ return mPoint;
+ }
+
+ public void setPoint(int x, int y) {
+ mPoint.set(x, y);
+ }
+
+ /**
+ * When a point is initialized, its value is (0,0). Since it is highly unlikely a user will
+ * touch at that exact point, if the point in TouchPointManager is (0,0), it is safe to assume
+ * that the TouchPointManager has not yet collected a touch.
+ *
+ * @return True if there is a valid point saved. Define a valid point as any point that is
+ * not (0,0).
+ */
+ public boolean hasValidPoint() {
+ return mPoint.x != 0 || mPoint.y != 0;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/lettertiles/LetterTileDrawable.java b/ContactsCommon/src/com/android/contacts/common/lettertiles/LetterTileDrawable.java
new file mode 100644
index 0000000..e62d421
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/lettertiles/LetterTileDrawable.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.lettertiles;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.BitmapUtil;
+
+import junit.framework.Assert;
+
+/**
+ * A drawable that encapsulates all the functionality needed to display a letter tile to
+ * represent a contact image.
+ */
+public class LetterTileDrawable extends Drawable {
+
+ private final String TAG = LetterTileDrawable.class.getSimpleName();
+
+ private final Paint mPaint;
+
+ /** Letter tile */
+ private static TypedArray sColors;
+ private static int sDefaultColor;
+ private static int sTileFontColor;
+ private static float sLetterToTileRatio;
+ private static Bitmap DEFAULT_PERSON_AVATAR;
+ private static Bitmap DEFAULT_BUSINESS_AVATAR;
+ private static Bitmap DEFAULT_VOICEMAIL_AVATAR;
+
+ /** Reusable components to avoid new allocations */
+ private static final Paint sPaint = new Paint();
+ private static final Rect sRect = new Rect();
+ private static final char[] sFirstChar = new char[1];
+
+ /** Contact type constants */
+ public static final int TYPE_PERSON = 1;
+ public static final int TYPE_BUSINESS = 2;
+ public static final int TYPE_VOICEMAIL = 3;
+ public static final int TYPE_DEFAULT = TYPE_PERSON;
+
+ private String mDisplayName;
+ private String mIdentifier;
+ private int mContactType = TYPE_DEFAULT;
+ private float mScale = 1.0f;
+ private float mOffset = 0.0f;
+ private boolean mIsCircle = false;
+
+ public LetterTileDrawable(final Resources res) {
+ mPaint = new Paint();
+ mPaint.setFilterBitmap(true);
+ mPaint.setDither(true);
+
+ if (sColors == null) {
+ sColors = res.obtainTypedArray(R.array.letter_tile_colors);
+ sDefaultColor = res.getColor(R.color.letter_tile_default_color);
+ sTileFontColor = res.getColor(R.color.letter_tile_font_color);
+ sLetterToTileRatio = res.getFraction(R.dimen.letter_to_tile_ratio, 1, 1);
+ DEFAULT_PERSON_AVATAR = BitmapFactory.decodeResource(res,
+ R.drawable.ic_person_white_120dp);
+ DEFAULT_BUSINESS_AVATAR = BitmapFactory.decodeResource(res,
+ R.drawable.ic_business_white_120dp);
+ DEFAULT_VOICEMAIL_AVATAR = BitmapFactory.decodeResource(res,
+ R.drawable.ic_voicemail_avatar);
+ sPaint.setTypeface(Typeface.create(
+ res.getString(R.string.letter_tile_letter_font_family), Typeface.NORMAL));
+ sPaint.setTextAlign(Align.CENTER);
+ sPaint.setAntiAlias(true);
+ }
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final Rect bounds = getBounds();
+ if (!isVisible() || bounds.isEmpty()) {
+ return;
+ }
+ // Draw letter tile.
+ drawLetterTile(canvas);
+ }
+
+ /**
+ * Draw the bitmap onto the canvas at the current bounds taking into account the current scale.
+ */
+ private void drawBitmap(final Bitmap bitmap, final int width, final int height,
+ final Canvas canvas) {
+ // The bitmap should be drawn in the middle of the canvas without changing its width to
+ // height ratio.
+ final Rect destRect = copyBounds();
+
+ // Crop the destination bounds into a square, scaled and offset as appropriate
+ final int halfLength = (int) (mScale * Math.min(destRect.width(), destRect.height()) / 2);
+
+ destRect.set(destRect.centerX() - halfLength,
+ (int) (destRect.centerY() - halfLength + mOffset * destRect.height()),
+ destRect.centerX() + halfLength,
+ (int) (destRect.centerY() + halfLength + mOffset * destRect.height()));
+
+ // Source rectangle remains the entire bounds of the source bitmap.
+ sRect.set(0, 0, width, height);
+
+ canvas.drawBitmap(bitmap, sRect, destRect, mPaint);
+ }
+
+ private void drawLetterTile(final Canvas canvas) {
+ // Draw background color.
+ sPaint.setColor(pickColor(mIdentifier));
+
+ sPaint.setAlpha(mPaint.getAlpha());
+ final Rect bounds = getBounds();
+ final int minDimension = Math.min(bounds.width(), bounds.height());
+
+ if (mIsCircle) {
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), minDimension / 2, sPaint);
+ } else {
+ canvas.drawRect(bounds, sPaint);
+ }
+
+ // Draw letter/digit only if the first character is an english letter
+ if (mDisplayName != null && isEnglishLetter(mDisplayName.charAt(0))) {
+ // Draw letter or digit.
+ sFirstChar[0] = Character.toUpperCase(mDisplayName.charAt(0));
+
+ // Scale text by canvas bounds and user selected scaling factor
+ sPaint.setTextSize(mScale * sLetterToTileRatio * minDimension);
+ //sPaint.setTextSize(sTileLetterFontSize);
+ sPaint.getTextBounds(sFirstChar, 0, 1, sRect);
+ sPaint.setColor(sTileFontColor);
+
+ // Draw the letter in the canvas, vertically shifted up or down by the user-defined
+ // offset
+ canvas.drawText(sFirstChar, 0, 1, bounds.centerX(),
+ bounds.centerY() + mOffset * bounds.height() + sRect.height() / 2,
+ sPaint);
+ } else {
+ // Draw the default image if there is no letter/digit to be drawn
+ final Bitmap bitmap = getBitmapForContactType(mContactType);
+ drawBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(),
+ canvas);
+ }
+ }
+
+ public int getColor() {
+ return pickColor(mIdentifier);
+ }
+
+ /**
+ * Returns a deterministic color based on the provided contact identifier string.
+ */
+ private int pickColor(final String identifier) {
+ if (TextUtils.isEmpty(identifier) || mContactType == TYPE_VOICEMAIL) {
+ return sDefaultColor;
+ }
+ // String.hashCode() implementation is not supposed to change across java versions, so
+ // this should guarantee the same email address always maps to the same color.
+ // The email should already have been normalized by the ContactRequest.
+ final int color = Math.abs(identifier.hashCode()) % sColors.length();
+ return sColors.getColor(color, sDefaultColor);
+ }
+
+ private static Bitmap getBitmapForContactType(int contactType) {
+ switch (contactType) {
+ case TYPE_PERSON:
+ return DEFAULT_PERSON_AVATAR;
+ case TYPE_BUSINESS:
+ return DEFAULT_BUSINESS_AVATAR;
+ case TYPE_VOICEMAIL:
+ return DEFAULT_VOICEMAIL_AVATAR;
+ default:
+ return DEFAULT_PERSON_AVATAR;
+ }
+ }
+
+ private static boolean isEnglishLetter(final char c) {
+ return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
+ }
+
+ @Override
+ public void setAlpha(final int alpha) {
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(final ColorFilter cf) {
+ mPaint.setColorFilter(cf);
+ }
+
+ @Override
+ public int getOpacity() {
+ return android.graphics.PixelFormat.OPAQUE;
+ }
+
+ /**
+ * Scale the drawn letter tile to a ratio of its default size
+ *
+ * @param scale The ratio the letter tile should be scaled to as a percentage of its default
+ * size, from a scale of 0 to 2.0f. The default is 1.0f.
+ */
+ public void setScale(float scale) {
+ mScale = scale;
+ }
+
+ /**
+ * Assigns the vertical offset of the position of the letter tile to the ContactDrawable
+ *
+ * @param offset The provided offset must be within the range of -0.5f to 0.5f.
+ * If set to -0.5f, the letter will be shifted upwards by 0.5 times the height of the canvas
+ * it is being drawn on, which means it will be drawn with the center of the letter starting
+ * at the top edge of the canvas.
+ * If set to 0.5f, the letter will be shifted downwards by 0.5 times the height of the canvas
+ * it is being drawn on, which means it will be drawn with the center of the letter starting
+ * at the bottom edge of the canvas.
+ * The default is 0.0f.
+ */
+ public void setOffset(float offset) {
+ Assert.assertTrue(offset >= -0.5f && offset <= 0.5f);
+ mOffset = offset;
+ }
+
+ public void setContactDetails(final String displayName, final String identifier) {
+ mDisplayName = displayName;
+ mIdentifier = identifier;
+ }
+
+ public void setContactType(int contactType) {
+ mContactType = contactType;
+ }
+
+ public void setIsCircular(boolean isCircle) {
+ mIsCircle = isCircle;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/AccountFilterActivity.java b/ContactsCommon/src/com/android/contacts/common/list/AccountFilterActivity.java
new file mode 100644
index 0000000..58450c6
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/AccountFilterActivity.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Shows a list of all available accounts, letting the user select under which account to view
+ * contacts.
+ */
+public class AccountFilterActivity extends Activity implements AdapterView.OnItemClickListener {
+
+ private static final String TAG = AccountFilterActivity.class.getSimpleName();
+
+ private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 0;
+
+ public static final String KEY_EXTRA_CONTACT_LIST_FILTER = "contactListFilter";
+ public static final String KEY_EXTRA_CURRENT_FILTER = "currentFilter";
+
+ private static final int FILTER_LOADER_ID = 0;
+
+ private ListView mListView;
+
+ private ContactListFilter mCurrentFilter;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.contact_list_filter);
+
+ mListView = (ListView) findViewById(android.R.id.list);
+ mListView.setOnItemClickListener(this);
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ mCurrentFilter = getIntent().getParcelableExtra(KEY_EXTRA_CURRENT_FILTER);
+
+ getLoaderManager().initLoader(FILTER_LOADER_ID, null, new MyLoaderCallbacks());
+ }
+
+ private static class FilterLoader extends AsyncTaskLoader> {
+ private Context mContext;
+
+ public FilterLoader(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public List loadInBackground() {
+ return loadAccountFilters(mContext);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ forceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ onStopLoading();
+ }
+ }
+
+ private static List loadAccountFilters(Context context) {
+ final ArrayList result = Lists.newArrayList();
+ final ArrayList accountFilters = Lists.newArrayList();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
+ List accounts = accountTypes.getAccounts(false);
+ for (AccountWithDataSet account : accounts) {
+ AccountType accountType = accountTypes.getAccountType(account.type, account.dataSet);
+ if (accountType.isExtension() && !account.hasData(context)) {
+ // Hide extensions with no raw_contacts.
+ continue;
+ }
+ Drawable icon = accountType != null ? accountType.getDisplayIcon(context) : null;
+ accountFilters.add(ContactListFilter.createAccountFilter(
+ account.type, account.name, account.dataSet, icon));
+ }
+
+ // Always show "All", even when there's no accounts. (We may have local contacts)
+ result.add(ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+
+ final int count = accountFilters.size();
+ if (count >= 1) {
+ // If we only have one account, don't show it as "account", instead show it as "all"
+ if (count > 1) {
+ result.addAll(accountFilters);
+ }
+ result.add(ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_CUSTOM));
+ }
+ return result;
+ }
+
+ private class MyLoaderCallbacks implements LoaderCallbacks> {
+ @Override
+ public Loader> onCreateLoader(int id, Bundle args) {
+ return new FilterLoader(AccountFilterActivity.this);
+ }
+
+ @Override
+ public void onLoadFinished(
+ Loader> loader, List data) {
+ if (data == null) { // Just in case...
+ Log.e(TAG, "Failed to load filters");
+ return;
+ }
+ mListView.setAdapter(
+ new FilterListAdapter(AccountFilterActivity.this, data, mCurrentFilter));
+ }
+
+ @Override
+ public void onLoaderReset(Loader> loader) {
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ final ContactListFilter filter = (ContactListFilter) view.getTag();
+ if (filter == null) return; // Just in case
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ final Intent intent = new Intent(this,
+ CustomContactListFilterActivity.class);
+ startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER);
+ } else {
+ final Intent intent = new Intent();
+ intent.putExtra(KEY_EXTRA_CONTACT_LIST_FILTER, filter);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ switch (requestCode) {
+ case SUBACTIVITY_CUSTOMIZE_FILTER: {
+ final Intent intent = new Intent();
+ ContactListFilter filter = ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_CUSTOM);
+ intent.putExtra(KEY_EXTRA_CONTACT_LIST_FILTER, filter);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ break;
+ }
+ }
+ }
+
+ private static class FilterListAdapter extends BaseAdapter {
+ private final List mFilters;
+ private final LayoutInflater mLayoutInflater;
+ private final AccountTypeManager mAccountTypes;
+ private final ContactListFilter mCurrentFilter;
+
+ public FilterListAdapter(
+ Context context, List filters, ContactListFilter current) {
+ mLayoutInflater = (LayoutInflater) context.getSystemService
+ (Context.LAYOUT_INFLATER_SERVICE);
+ mFilters = filters;
+ mCurrentFilter = current;
+ mAccountTypes = AccountTypeManager.getInstance(context);
+ }
+
+ @Override
+ public int getCount() {
+ return mFilters.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public ContactListFilter getItem(int position) {
+ return mFilters.get(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final ContactListFilterView view;
+ if (convertView != null) {
+ view = (ContactListFilterView) convertView;
+ } else {
+ view = (ContactListFilterView) mLayoutInflater.inflate(
+ R.layout.contact_list_filter_item, parent, false);
+ }
+ view.setSingleAccount(mFilters.size() == 1);
+ final ContactListFilter filter = mFilters.get(position);
+ view.setContactListFilter(filter);
+ view.bindView(mAccountTypes);
+ view.setTag(filter);
+ view.setActivated(filter.equals(mCurrentFilter));
+ return view;
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // We have two logical "up" Activities: People and Phone.
+ // Instead of having one static "up" direction, behave like back as an
+ // exceptional case.
+ onBackPressed();
+ return true;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/AutoScrollListView.java b/ContactsCommon/src/com/android/contacts/common/list/AutoScrollListView.java
new file mode 100644
index 0000000..ae7ca17
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/AutoScrollListView.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * A ListView that can be asked to scroll (smoothly or otherwise) to a specific
+ * position. This class takes advantage of similar functionality that exists
+ * in {@link ListView} and enhances it.
+ */
+public class AutoScrollListView extends ListView {
+
+ /**
+ * Position the element at about 1/3 of the list height
+ */
+ private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;
+
+ private int mRequestedScrollPosition = -1;
+ private boolean mSmoothScrollRequested;
+
+ public AutoScrollListView(Context context) {
+ super(context);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Brings the specified position to view by optionally performing a jump-scroll maneuver:
+ * first it jumps to some position near the one requested and then does a smooth
+ * scroll to the requested position. This creates an impression of full smooth
+ * scrolling without actually traversing the entire list. If smooth scrolling is
+ * not requested, instantly positions the requested item at a preferred offset.
+ */
+ public void requestPositionToScreen(int position, boolean smoothScroll) {
+ mRequestedScrollPosition = position;
+ mSmoothScrollRequested = smoothScroll;
+ requestLayout();
+ }
+
+ @Override
+ protected void layoutChildren() {
+ super.layoutChildren();
+ if (mRequestedScrollPosition == -1) {
+ return;
+ }
+
+ final int position = mRequestedScrollPosition;
+ mRequestedScrollPosition = -1;
+
+ int firstPosition = getFirstVisiblePosition() + 1;
+ int lastPosition = getLastVisiblePosition();
+ if (position >= firstPosition && position <= lastPosition) {
+ return; // Already on screen
+ }
+
+ final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
+ if (!mSmoothScrollRequested) {
+ setSelectionFromTop(position, offset);
+
+ // Since we have changed the scrolling position, we need to redo child layout
+ // Calling "requestLayout" in the middle of a layout pass has no effect,
+ // so we call layoutChildren explicitly
+ super.layoutChildren();
+
+ } else {
+ // We will first position the list a couple of screens before or after
+ // the new selection and then scroll smoothly to it.
+ int twoScreens = (lastPosition - firstPosition) * 2;
+ int preliminaryPosition;
+ if (position < firstPosition) {
+ preliminaryPosition = position + twoScreens;
+ if (preliminaryPosition >= getCount()) {
+ preliminaryPosition = getCount() - 1;
+ }
+ if (preliminaryPosition < firstPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ } else {
+ preliminaryPosition = position - twoScreens;
+ if (preliminaryPosition < 0) {
+ preliminaryPosition = 0;
+ }
+ if (preliminaryPosition > lastPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ }
+
+
+ smoothScrollToPositionFromTop(position, offset);
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactEntry.java b/ContactsCommon/src/com/android/contacts/common/list/ContactEntry.java
new file mode 100644
index 0000000..43fc19d
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactEntry.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.ContactsContract.PinnedPositions;
+
+/**
+ * Class to hold contact information
+ */
+public class ContactEntry {
+ public String name;
+ public String status;
+ public String phoneLabel;
+ public String phoneNumber;
+ public Uri photoUri;
+ public Uri lookupUri;
+ public String lookupKey;
+ public Drawable presenceIcon;
+ public long id;
+ public int pinned = PinnedPositions.UNPINNED;
+ public boolean isFavorite = false;
+ public boolean isDefaultNumber = false;
+
+ public static final ContactEntry BLANK_ENTRY = new ContactEntry();
+}
\ No newline at end of file
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListAdapter.java b/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListAdapter.java
new file mode 100644
index 0000000..8500d81
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListAdapter.java
@@ -0,0 +1,798 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.QuickContactBadge;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.SearchUtil;
+
+import java.util.HashSet;
+
+/**
+ * Common base class for various contact-related lists, e.g. contact list, phone number list
+ * etc.
+ */
+public abstract class ContactEntryListAdapter extends IndexerListAdapter {
+
+ private static final String TAG = "ContactEntryListAdapter";
+
+ /**
+ * Indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should
+ * be included in the search.
+ */
+ public static final boolean LOCAL_INVISIBLE_DIRECTORY_ENABLED = false;
+
+ private int mDisplayOrder;
+ private int mSortOrder;
+
+ private boolean mDisplayPhotos;
+ private boolean mCircularPhotos = true;
+ private boolean mQuickContactEnabled;
+ private boolean mAdjustSelectionBoundsEnabled;
+
+ /**
+ * indicates if contact queries include profile
+ */
+ private boolean mIncludeProfile;
+
+ /**
+ * indicates if query results includes a profile
+ */
+ private boolean mProfileExists;
+
+ /**
+ * The root view of the fragment that this adapter is associated with.
+ */
+ private View mFragmentRootView;
+
+ private ContactPhotoManager mPhotoLoader;
+
+ private String mQueryString;
+ private String mUpperCaseQueryString;
+ private boolean mSearchMode;
+ private int mDirectorySearchMode;
+ private int mDirectoryResultLimit = Integer.MAX_VALUE;
+
+ private boolean mEmptyListEnabled = true;
+
+ private boolean mSelectionVisible;
+
+ private ContactListFilter mFilter;
+ private boolean mDarkTheme = false;
+
+ /** Resource used to provide header-text for default filter. */
+ private CharSequence mDefaultFilterHeaderText;
+
+ public ContactEntryListAdapter(Context context) {
+ super(context);
+ setDefaultFilterHeaderText(R.string.local_search_label);
+ addPartitions();
+ }
+
+ /**
+ * @param fragmentRootView Root view of the fragment. This is used to restrict the scope of
+ * image loading requests that get cancelled on cursor changes.
+ */
+ protected void setFragmentRootView(View fragmentRootView) {
+ mFragmentRootView = fragmentRootView;
+ }
+
+ protected void setDefaultFilterHeaderText(int resourceId) {
+ mDefaultFilterHeaderText = getContext().getResources().getText(resourceId);
+ }
+
+ @Override
+ protected ContactListItemView newView(
+ Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled());
+ view.setAdjustSelectionBoundsEnabled(isAdjustSelectionBoundsEnabled());
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ final ContactListItemView view = (ContactListItemView) itemView;
+ view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled());
+ }
+
+ @Override
+ protected View createPinnedSectionHeaderView(Context context, ViewGroup parent) {
+ return new ContactListPinnedHeaderView(context, null, parent);
+ }
+
+ @Override
+ protected void setPinnedSectionTitle(View pinnedHeaderView, String title) {
+ ((ContactListPinnedHeaderView) pinnedHeaderView).setSectionHeaderTitle(title);
+ }
+
+ protected void addPartitions() {
+ addPartition(createDefaultDirectoryPartition());
+ }
+
+ protected DirectoryPartition createDefaultDirectoryPartition() {
+ DirectoryPartition partition = new DirectoryPartition(true, true);
+ partition.setDirectoryId(Directory.DEFAULT);
+ partition.setDirectoryType(getContext().getString(R.string.contactsList));
+ partition.setPriorityDirectory(true);
+ partition.setPhotoSupported(true);
+ partition.setLabel(mDefaultFilterHeaderText.toString());
+ return partition;
+ }
+
+ /**
+ * Remove all directories after the default directory. This is typically used when contacts
+ * list screens are asked to exit the search mode and thus need to remove all remote directory
+ * results for the search.
+ *
+ * This code assumes that the default directory and directories before that should not be
+ * deleted (e.g. Join screen has "suggested contacts" directory before the default director,
+ * and we should not remove the directory).
+ */
+ public void removeDirectoriesAfterDefault() {
+ final int partitionCount = getPartitionCount();
+ for (int i = partitionCount - 1; i >= 0; i--) {
+ final Partition partition = getPartition(i);
+ if ((partition instanceof DirectoryPartition)
+ && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) {
+ break;
+ } else {
+ removePartition(i);
+ }
+ }
+ }
+
+ protected int getPartitionByDirectoryId(long id) {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ if (((DirectoryPartition)partition).getDirectoryId() == id) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ protected DirectoryPartition getDirectoryById(long id) {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ final DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ if (directoryPartition.getDirectoryId() == id) {
+ return directoryPartition;
+ }
+ }
+ }
+ return null;
+ }
+
+ public abstract String getContactDisplayName(int position);
+ public abstract void configureLoader(CursorLoader loader, long directoryId);
+
+ /**
+ * Marks all partitions as "loading"
+ */
+ public void onDataReload() {
+ boolean notify = false;
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ if (!directoryPartition.isLoading()) {
+ notify = true;
+ }
+ directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED);
+ }
+ }
+ if (notify) {
+ notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void clearPartitions() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED);
+ }
+ }
+ super.clearPartitions();
+ }
+
+ public boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ public void setSearchMode(boolean flag) {
+ mSearchMode = flag;
+ }
+
+ public String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String queryString) {
+ mQueryString = queryString;
+ if (TextUtils.isEmpty(queryString)) {
+ mUpperCaseQueryString = null;
+ } else {
+ mUpperCaseQueryString = SearchUtil
+ .cleanStartAndEndOfSearchQuery(queryString.toUpperCase()) ;
+ }
+ }
+
+ public String getUpperCaseQueryString() {
+ return mUpperCaseQueryString;
+ }
+
+ public int getDirectorySearchMode() {
+ return mDirectorySearchMode;
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ public int getDirectoryResultLimit() {
+ return mDirectoryResultLimit;
+ }
+
+ public int getDirectoryResultLimit(DirectoryPartition directoryPartition) {
+ final int limit = directoryPartition.getResultLimit();
+ return limit == DirectoryPartition.RESULT_LIMIT_DEFAULT ? mDirectoryResultLimit : limit;
+ }
+
+ public void setDirectoryResultLimit(int limit) {
+ this.mDirectoryResultLimit = limit;
+ }
+
+ public int getContactNameDisplayOrder() {
+ return mDisplayOrder;
+ }
+
+ public void setContactNameDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ }
+
+ public int getSortOrder() {
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ }
+
+ public void setPhotoLoader(ContactPhotoManager photoLoader) {
+ mPhotoLoader = photoLoader;
+ }
+
+ protected ContactPhotoManager getPhotoLoader() {
+ return mPhotoLoader;
+ }
+
+ public boolean getDisplayPhotos() {
+ return mDisplayPhotos;
+ }
+
+ public void setDisplayPhotos(boolean displayPhotos) {
+ mDisplayPhotos = displayPhotos;
+ }
+
+ public boolean getCircularPhotos() {
+ return mCircularPhotos;
+ }
+
+ public void setCircularPhotos(boolean circularPhotos) {
+ mCircularPhotos = circularPhotos;
+ }
+
+ public boolean isEmptyListEnabled() {
+ return mEmptyListEnabled;
+ }
+
+ public void setEmptyListEnabled(boolean flag) {
+ mEmptyListEnabled = flag;
+ }
+
+ public boolean isSelectionVisible() {
+ return mSelectionVisible;
+ }
+
+ public void setSelectionVisible(boolean flag) {
+ this.mSelectionVisible = flag;
+ }
+
+ public boolean isQuickContactEnabled() {
+ return mQuickContactEnabled;
+ }
+
+ public void setQuickContactEnabled(boolean quickContactEnabled) {
+ mQuickContactEnabled = quickContactEnabled;
+ }
+
+ public boolean isAdjustSelectionBoundsEnabled() {
+ return mAdjustSelectionBoundsEnabled;
+ }
+
+ public void setAdjustSelectionBoundsEnabled(boolean enabled) {
+ mAdjustSelectionBoundsEnabled = enabled;
+ }
+
+ public boolean shouldIncludeProfile() {
+ return mIncludeProfile;
+ }
+
+ public void setIncludeProfile(boolean includeProfile) {
+ mIncludeProfile = includeProfile;
+ }
+
+ public void setProfileExists(boolean exists) {
+ mProfileExists = exists;
+ // Stick the "ME" header for the profile
+ if (exists) {
+ SectionIndexer indexer = getIndexer();
+ if (indexer != null) {
+ ((ContactsSectionIndexer) indexer).setProfileHeader(
+ getContext().getString(R.string.user_profile_contacts_list_header));
+ }
+ }
+ }
+
+ public boolean hasProfile() {
+ return mProfileExists;
+ }
+
+ public void setDarkTheme(boolean value) {
+ mDarkTheme = value;
+ }
+
+ /**
+ * Updates partitions according to the directory meta-data contained in the supplied
+ * cursor.
+ */
+ public void changeDirectories(Cursor cursor) {
+ if (cursor.getCount() == 0) {
+ // Directory table must have at least local directory, without which this adapter will
+ // enter very weird state.
+ Log.e(TAG, "Directory search loader returned an empty cursor, which implies we have " +
+ "no directory entries.", new RuntimeException());
+ return;
+ }
+ HashSet directoryIds = new HashSet();
+
+ int idColumnIndex = cursor.getColumnIndex(Directory._ID);
+ int directoryTypeColumnIndex = cursor.getColumnIndex(DirectoryListLoader.DIRECTORY_TYPE);
+ int displayNameColumnIndex = cursor.getColumnIndex(Directory.DISPLAY_NAME);
+ int photoSupportColumnIndex = cursor.getColumnIndex(Directory.PHOTO_SUPPORT);
+
+ // TODO preserve the order of partition to match those of the cursor
+ // Phase I: add new directories
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ long id = cursor.getLong(idColumnIndex);
+ directoryIds.add(id);
+ if (getPartitionByDirectoryId(id) == -1) {
+ DirectoryPartition partition = new DirectoryPartition(false, true);
+ partition.setDirectoryId(id);
+ if (isRemoteDirectory(id)) {
+ partition.setLabel(mContext.getString(R.string.directory_search_label));
+ } else {
+ partition.setLabel(mDefaultFilterHeaderText.toString());
+ }
+ partition.setDirectoryType(cursor.getString(directoryTypeColumnIndex));
+ partition.setDisplayName(cursor.getString(displayNameColumnIndex));
+ int photoSupport = cursor.getInt(photoSupportColumnIndex);
+ partition.setPhotoSupported(photoSupport == Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY
+ || photoSupport == Directory.PHOTO_SUPPORT_FULL);
+ addPartition(partition);
+ }
+ }
+
+ // Phase II: remove deleted directories
+ int count = getPartitionCount();
+ for (int i = count; --i >= 0; ) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ long id = ((DirectoryPartition)partition).getDirectoryId();
+ if (!directoryIds.contains(id)) {
+ removePartition(i);
+ }
+ }
+ }
+
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void changeCursor(int partitionIndex, Cursor cursor) {
+ if (partitionIndex >= getPartitionCount()) {
+ // There is no partition for this data
+ return;
+ }
+
+ Partition partition = getPartition(partitionIndex);
+ if (partition instanceof DirectoryPartition) {
+ ((DirectoryPartition)partition).setStatus(DirectoryPartition.STATUS_LOADED);
+ }
+
+ if (mDisplayPhotos && mPhotoLoader != null && isPhotoSupported(partitionIndex)) {
+ mPhotoLoader.refreshCache();
+ }
+
+ super.changeCursor(partitionIndex, cursor);
+
+ if (isSectionHeaderDisplayEnabled() && partitionIndex == getIndexedPartition()) {
+ updateIndexer(cursor);
+ }
+
+ // When the cursor changes, cancel any pending asynchronous photo loads.
+ mPhotoLoader.cancelPendingRequests(mFragmentRootView);
+ }
+
+ public void changeCursor(Cursor cursor) {
+ changeCursor(0, cursor);
+ }
+
+ /**
+ * Updates the indexer, which is used to produce section headers.
+ */
+ private void updateIndexer(Cursor cursor) {
+ if (cursor == null) {
+ setIndexer(null);
+ return;
+ }
+
+ Bundle bundle = cursor.getExtras();
+ if (bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES) &&
+ bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) {
+ String sections[] =
+ bundle.getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
+ int counts[] = bundle.getIntArray(
+ Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
+
+ if (getExtraStartingSection()) {
+ // Insert an additional unnamed section at the top of the list.
+ String allSections[] = new String[sections.length + 1];
+ int allCounts[] = new int[counts.length + 1];
+ for (int i = 0; i < sections.length; i++) {
+ allSections[i + 1] = sections[i];
+ allCounts[i + 1] = counts[i];
+ }
+ allCounts[0] = 1;
+ allSections[0] = "";
+ setIndexer(new ContactsSectionIndexer(allSections, allCounts));
+ } else {
+ setIndexer(new ContactsSectionIndexer(sections, counts));
+ }
+ } else {
+ setIndexer(null);
+ }
+ }
+
+ protected boolean getExtraStartingSection() {
+ return false;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ // We need a separate view type for each item type, plus another one for
+ // each type with header, plus one for "other".
+ return getItemViewTypeCount() * 2 + 1;
+ }
+
+ @Override
+ public int getItemViewType(int partitionIndex, int position) {
+ int type = super.getItemViewType(partitionIndex, position);
+ if (!isUserProfile(position)
+ && isSectionHeaderDisplayEnabled()
+ && partitionIndex == getIndexedPartition()) {
+ Placement placement = getItemPlacementInSection(position);
+ return placement.firstInSection ? type : getItemViewTypeCount() + type;
+ } else {
+ return type;
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // TODO
+// if (contactsListActivity.mProviderStatus != ProviderStatus.STATUS_NORMAL) {
+// return true;
+// }
+
+ if (!mEmptyListEnabled) {
+ return false;
+ } else if (isSearchMode()) {
+ return TextUtils.isEmpty(getQueryString());
+ } else {
+ return super.isEmpty();
+ }
+ }
+
+ public boolean isLoading() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition
+ && ((DirectoryPartition) partition).isLoading()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean areAllPartitionsEmpty() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ if (!isPartitionEmpty(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Changes visibility parameters for the default directory partition.
+ */
+ public void configureDefaultPartition(boolean showIfEmpty, boolean hasHeader) {
+ int defaultPartitionIndex = -1;
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition &&
+ ((DirectoryPartition)partition).getDirectoryId() == Directory.DEFAULT) {
+ defaultPartitionIndex = i;
+ break;
+ }
+ }
+ if (defaultPartitionIndex != -1) {
+ setShowIfEmpty(defaultPartitionIndex, showIfEmpty);
+ setHasHeader(defaultPartitionIndex, hasHeader);
+ }
+ }
+
+ @Override
+ protected View newHeaderView(Context context, int partition, Cursor cursor,
+ ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View view = inflater.inflate(R.layout.directory_header, parent, false);
+ if (!getPinnedPartitionHeadersEnabled()) {
+ // If the headers are unpinned, there is no need for their background
+ // color to be non-transparent. Setting this transparent reduces maintenance for
+ // non-pinned headers. We don't need to bother synchronizing the activity's
+ // background color with the header background color.
+ view.setBackground(null);
+ }
+ return view;
+ }
+
+ @Override
+ protected void bindHeaderView(View view, int partitionIndex, Cursor cursor) {
+ Partition partition = getPartition(partitionIndex);
+ if (!(partition instanceof DirectoryPartition)) {
+ return;
+ }
+
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ long directoryId = directoryPartition.getDirectoryId();
+ TextView labelTextView = (TextView)view.findViewById(R.id.label);
+ TextView displayNameTextView = (TextView)view.findViewById(R.id.display_name);
+ labelTextView.setText(directoryPartition.getLabel());
+ if (!isRemoteDirectory(directoryId)) {
+ displayNameTextView.setText(null);
+ } else {
+ String directoryName = directoryPartition.getDisplayName();
+ String displayName = !TextUtils.isEmpty(directoryName)
+ ? directoryName
+ : directoryPartition.getDirectoryType();
+ displayNameTextView.setText(displayName);
+ }
+
+ final Resources res = getContext().getResources();
+ final int headerPaddingTop = partitionIndex == 1 && getPartition(0).isEmpty()?
+ 0 : res.getDimensionPixelOffset(R.dimen.directory_header_extra_top_padding);
+ // There should be no extra padding at the top of the first directory header
+ view.setPaddingRelative(view.getPaddingStart(), headerPaddingTop, view.getPaddingEnd(),
+ view.getPaddingBottom());
+ }
+
+ // Default implementation simply returns number of rows in the cursor.
+ // Broken out into its own routine so can be overridden by child classes
+ // for eg number of unique contacts for a phone list.
+ protected int getResultCount(Cursor cursor) {
+ return cursor == null ? 0 : cursor.getCount();
+ }
+
+ /**
+ * Checks whether the contact entry at the given position represents the user's profile.
+ */
+ protected boolean isUserProfile(int position) {
+ // The profile only ever appears in the first position if it is present. So if the position
+ // is anything beyond 0, it can't be the profile.
+ boolean isUserProfile = false;
+ if (position == 0) {
+ int partition = getPartitionForPosition(position);
+ if (partition >= 0) {
+ // Save the old cursor position - the call to getItem() may modify the cursor
+ // position.
+ int offset = getCursor(partition).getPosition();
+ Cursor cursor = (Cursor) getItem(position);
+ if (cursor != null) {
+ int profileColumnIndex = cursor.getColumnIndex(Contacts.IS_USER_PROFILE);
+ if (profileColumnIndex != -1) {
+ isUserProfile = cursor.getInt(profileColumnIndex) == 1;
+ }
+ // Restore the old cursor position.
+ cursor.moveToPosition(offset);
+ }
+ }
+ }
+ return isUserProfile;
+ }
+
+ // TODO: fix PluralRules to handle zero correctly and use Resources.getQuantityText directly
+ public String getQuantityText(int count, int zeroResourceId, int pluralResourceId) {
+ if (count == 0) {
+ return getContext().getString(zeroResourceId);
+ } else {
+ String format = getContext().getResources()
+ .getQuantityText(pluralResourceId, count).toString();
+ return String.format(format, count);
+ }
+ }
+
+ public boolean isPhotoSupported(int partitionIndex) {
+ Partition partition = getPartition(partitionIndex);
+ if (partition instanceof DirectoryPartition) {
+ return ((DirectoryPartition) partition).isPhotoSupported();
+ }
+ return true;
+ }
+
+ /**
+ * Returns the currently selected filter.
+ */
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public void setFilter(ContactListFilter filter) {
+ mFilter = filter;
+ }
+
+ // TODO: move sharable logic (bindXX() methods) to here with extra arguments
+
+ /**
+ * Loads the photo for the quick contact view and assigns the contact uri.
+ * @param photoIdColumn Index of the photo id column
+ * @param photoUriColumn Index of the photo uri column. Optional: Can be -1
+ * @param contactIdColumn Index of the contact id column
+ * @param lookUpKeyColumn Index of the lookup key column
+ * @param displayNameColumn Index of the display name column
+ */
+ protected void bindQuickContact(final ContactListItemView view, int partitionIndex,
+ Cursor cursor, int photoIdColumn, int photoUriColumn, int contactIdColumn,
+ int lookUpKeyColumn, int displayNameColumn) {
+ long photoId = 0;
+ if (!cursor.isNull(photoIdColumn)) {
+ photoId = cursor.getLong(photoIdColumn);
+ }
+
+ QuickContactBadge quickContact = view.getQuickContact();
+ quickContact.assignContactUri(
+ getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn));
+ // The Contacts app never uses the QuickContactBadge. Therefore, it is safe to assume
+ // that only Dialer will use this QuickContact badge. This means prioritizing the phone
+ // mimetype here is reasonable.
+ quickContact.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
+
+ if (photoId != 0 || photoUriColumn == -1) {
+ getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme, mCircularPhotos,
+ null);
+ } else {
+ final String photoUriString = cursor.getString(photoUriColumn);
+ final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ request = getDefaultImageRequestFromCursor(cursor, displayNameColumn,
+ lookUpKeyColumn);
+ }
+ getPhotoLoader().loadPhoto(quickContact, photoUri, -1, mDarkTheme, mCircularPhotos,
+ request);
+ }
+
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ // Whenever bindViewId() is called, the values passed into setId() are stable or
+ // stable-ish. For example, when one contact is modified we don't expect a second
+ // contact's Contact._ID values to change.
+ return true;
+ }
+
+ protected void bindViewId(final ContactListItemView view, Cursor cursor, int idColumn) {
+ // Set a semi-stable id, so that talkback won't get confused when the list gets
+ // refreshed. There is little harm in inserting the same ID twice.
+ long contactId = cursor.getLong(idColumn);
+ view.setId((int) (contactId % Integer.MAX_VALUE));
+
+ }
+
+ protected Uri getContactUri(int partitionIndex, Cursor cursor,
+ int contactIdColumn, int lookUpKeyColumn) {
+ long contactId = cursor.getLong(contactIdColumn);
+ String lookupKey = cursor.getString(lookUpKeyColumn);
+ long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
+ Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+ if (uri != null && directoryId != Directory.DEFAULT) {
+ uri = uri.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
+ }
+ return uri;
+ }
+
+ public static boolean isRemoteDirectory(long directoryId) {
+ return directoryId != Directory.DEFAULT
+ && directoryId != Directory.LOCAL_INVISIBLE;
+ }
+
+ /**
+ * Retrieves the lookup key and display name from a cursor, and returns a
+ * {@link DefaultImageRequest} containing these contact details
+ *
+ * @param cursor Contacts cursor positioned at the current row to retrieve contact details for
+ * @param displayNameColumn Column index of the display name
+ * @param lookupKeyColumn Column index of the lookup key
+ * @return {@link DefaultImageRequest} with the displayName and identifier fields set to the
+ * display name and lookup key of the contact.
+ */
+ public DefaultImageRequest getDefaultImageRequestFromCursor(Cursor cursor,
+ int displayNameColumn, int lookupKeyColumn) {
+ final String displayName = cursor.getString(displayNameColumn);
+ final String lookupKey = cursor.getString(lookupKeyColumn);
+ return new DefaultImageRequest(displayName, lookupKey, mCircularPhotos);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListFragment.java b/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListFragment.java
new file mode 100644
index 0000000..8d1cede
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListFragment.java
@@ -0,0 +1,922 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.ListView;
+
+import com.android.common.widget.CompositeCursorAdapter.Partition;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.ContactListViewUtils;
+
+import java.util.Locale;
+
+/**
+ * Common base class for various contact-related list fragments.
+ */
+public abstract class ContactEntryListFragment
+ extends Fragment
+ implements OnItemClickListener, OnScrollListener, OnFocusChangeListener, OnTouchListener,
+ OnItemLongClickListener, LoaderCallbacks {
+ private static final String TAG = "ContactEntryListFragment";
+
+ // TODO: Make this protected. This should not be used from the PeopleActivity but
+ // instead use the new startActivityWithResultFromFragment API
+ public static final int ACTIVITY_REQUEST_CODE_PICKER = 1;
+
+ private static final String KEY_LIST_STATE = "liststate";
+ private static final String KEY_SECTION_HEADER_DISPLAY_ENABLED = "sectionHeaderDisplayEnabled";
+ private static final String KEY_PHOTO_LOADER_ENABLED = "photoLoaderEnabled";
+ private static final String KEY_QUICK_CONTACT_ENABLED = "quickContactEnabled";
+ private static final String KEY_ADJUST_SELECTION_BOUNDS_ENABLED =
+ "adjustSelectionBoundsEnabled";
+ private static final String KEY_INCLUDE_PROFILE = "includeProfile";
+ private static final String KEY_SEARCH_MODE = "searchMode";
+ private static final String KEY_VISIBLE_SCROLLBAR_ENABLED = "visibleScrollbarEnabled";
+ private static final String KEY_SCROLLBAR_POSITION = "scrollbarPosition";
+ private static final String KEY_QUERY_STRING = "queryString";
+ private static final String KEY_DIRECTORY_SEARCH_MODE = "directorySearchMode";
+ private static final String KEY_SELECTION_VISIBLE = "selectionVisible";
+ private static final String KEY_REQUEST = "request";
+ private static final String KEY_DARK_THEME = "darkTheme";
+ private static final String KEY_LEGACY_COMPATIBILITY = "legacyCompatibility";
+ private static final String KEY_DIRECTORY_RESULT_LIMIT = "directoryResultLimit";
+
+ private static final String DIRECTORY_ID_ARG_KEY = "directoryId";
+
+ private static final int DIRECTORY_LOADER_ID = -1;
+
+ private static final int DIRECTORY_SEARCH_DELAY_MILLIS = 300;
+ private static final int DIRECTORY_SEARCH_MESSAGE = 1;
+
+ private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20;
+
+ private boolean mSectionHeaderDisplayEnabled;
+ private boolean mPhotoLoaderEnabled;
+ private boolean mQuickContactEnabled = true;
+ private boolean mAdjustSelectionBoundsEnabled = true;
+ private boolean mIncludeProfile;
+ private boolean mSearchMode;
+ private boolean mVisibleScrollbarEnabled;
+ private boolean mShowEmptyListForEmptyQuery;
+ private int mVerticalScrollbarPosition = getDefaultVerticalScrollbarPosition();
+ private String mQueryString;
+ private int mDirectorySearchMode = DirectoryListLoader.SEARCH_MODE_NONE;
+ private boolean mSelectionVisible;
+ private boolean mLegacyCompatibility;
+
+ private boolean mEnabled = true;
+
+ private T mAdapter;
+ private View mView;
+ private ListView mListView;
+
+ /**
+ * Used for keeping track of the scroll state of the list.
+ */
+ private Parcelable mListState;
+
+ private int mDisplayOrder;
+ private int mSortOrder;
+ private int mDirectoryResultLimit = DEFAULT_DIRECTORY_RESULT_LIMIT;
+
+ private ContactPhotoManager mPhotoManager;
+ private ContactsPreferences mContactsPrefs;
+
+ private boolean mForceLoad;
+
+ private boolean mDarkTheme;
+
+ protected boolean mUserProfileExists;
+
+ private static final int STATUS_NOT_LOADED = 0;
+ private static final int STATUS_LOADING = 1;
+ private static final int STATUS_LOADED = 2;
+
+ private int mDirectoryListStatus = STATUS_NOT_LOADED;
+
+ /**
+ * Indicates whether we are doing the initial complete load of data (false) or
+ * a refresh caused by a change notification (true)
+ */
+ private boolean mLoadPriorityDirectoriesOnly;
+
+ private Context mContext;
+
+ private LoaderManager mLoaderManager;
+
+ private Handler mDelayedDirectorySearchHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == DIRECTORY_SEARCH_MESSAGE) {
+ loadDirectoryPartition(msg.arg1, (DirectoryPartition) msg.obj);
+ }
+ }
+ };
+ private int defaultVerticalScrollbarPosition;
+
+ protected abstract View inflateView(LayoutInflater inflater, ViewGroup container);
+ protected abstract T createListAdapter();
+
+ /**
+ * @param position Please note that the position is already adjusted for
+ * header views, so "0" means the first list item below header
+ * views.
+ */
+ protected abstract void onItemClick(int position, long id);
+
+ /**
+ * @param position Please note that the position is already adjusted for
+ * header views, so "0" means the first list item below header
+ * views.
+ */
+ protected boolean onItemLongClick(int position, long id) {
+ return false;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ setContext(activity);
+ setLoaderManager(super.getLoaderManager());
+ }
+
+ /**
+ * Sets a context for the fragment in the unit test environment.
+ */
+ public void setContext(Context context) {
+ mContext = context;
+ configurePhotoLoader();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ if (mAdapter != null) {
+ if (mEnabled) {
+ reloadData();
+ } else {
+ mAdapter.clearPartitions();
+ }
+ }
+ }
+ }
+
+ /**
+ * Overrides a loader manager for use in unit tests.
+ */
+ public void setLoaderManager(LoaderManager loaderManager) {
+ mLoaderManager = loaderManager;
+ }
+
+ @Override
+ public LoaderManager getLoaderManager() {
+ return mLoaderManager;
+ }
+
+ public T getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED, mSectionHeaderDisplayEnabled);
+ outState.putBoolean(KEY_PHOTO_LOADER_ENABLED, mPhotoLoaderEnabled);
+ outState.putBoolean(KEY_QUICK_CONTACT_ENABLED, mQuickContactEnabled);
+ outState.putBoolean(KEY_ADJUST_SELECTION_BOUNDS_ENABLED, mAdjustSelectionBoundsEnabled);
+ outState.putBoolean(KEY_INCLUDE_PROFILE, mIncludeProfile);
+ outState.putBoolean(KEY_SEARCH_MODE, mSearchMode);
+ outState.putBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED, mVisibleScrollbarEnabled);
+ outState.putInt(KEY_SCROLLBAR_POSITION, mVerticalScrollbarPosition);
+ outState.putInt(KEY_DIRECTORY_SEARCH_MODE, mDirectorySearchMode);
+ outState.putBoolean(KEY_SELECTION_VISIBLE, mSelectionVisible);
+ outState.putBoolean(KEY_LEGACY_COMPATIBILITY, mLegacyCompatibility);
+ outState.putString(KEY_QUERY_STRING, mQueryString);
+ outState.putInt(KEY_DIRECTORY_RESULT_LIMIT, mDirectoryResultLimit);
+ outState.putBoolean(KEY_DARK_THEME, mDarkTheme);
+
+ if (mListView != null) {
+ outState.putParcelable(KEY_LIST_STATE, mListView.onSaveInstanceState());
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ restoreSavedState(savedState);
+ mAdapter = createListAdapter();
+ mContactsPrefs = new ContactsPreferences(mContext);
+ }
+
+ public void restoreSavedState(Bundle savedState) {
+ if (savedState == null) {
+ return;
+ }
+
+ mSectionHeaderDisplayEnabled = savedState.getBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED);
+ mPhotoLoaderEnabled = savedState.getBoolean(KEY_PHOTO_LOADER_ENABLED);
+ mQuickContactEnabled = savedState.getBoolean(KEY_QUICK_CONTACT_ENABLED);
+ mAdjustSelectionBoundsEnabled = savedState.getBoolean(KEY_ADJUST_SELECTION_BOUNDS_ENABLED);
+ mIncludeProfile = savedState.getBoolean(KEY_INCLUDE_PROFILE);
+ mSearchMode = savedState.getBoolean(KEY_SEARCH_MODE);
+ mVisibleScrollbarEnabled = savedState.getBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED);
+ mVerticalScrollbarPosition = savedState.getInt(KEY_SCROLLBAR_POSITION);
+ mDirectorySearchMode = savedState.getInt(KEY_DIRECTORY_SEARCH_MODE);
+ mSelectionVisible = savedState.getBoolean(KEY_SELECTION_VISIBLE);
+ mLegacyCompatibility = savedState.getBoolean(KEY_LEGACY_COMPATIBILITY);
+ mQueryString = savedState.getString(KEY_QUERY_STRING);
+ mDirectoryResultLimit = savedState.getInt(KEY_DIRECTORY_RESULT_LIMIT);
+ mDarkTheme = savedState.getBoolean(KEY_DARK_THEME);
+
+ // Retrieve list state. This will be applied in onLoadFinished
+ mListState = savedState.getParcelable(KEY_LIST_STATE);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ mContactsPrefs.registerChangeListener(mPreferencesChangeListener);
+
+ mForceLoad = loadPreferences();
+
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ mLoadPriorityDirectoriesOnly = true;
+
+ startLoading();
+ }
+
+ protected void startLoading() {
+ if (mAdapter == null) {
+ // The method was called before the fragment was started
+ return;
+ }
+
+ configureAdapter();
+ int partitionCount = mAdapter.getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ Partition partition = mAdapter.getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ if (directoryPartition.getStatus() == DirectoryPartition.STATUS_NOT_LOADED) {
+ if (directoryPartition.isPriorityDirectory() || !mLoadPriorityDirectoriesOnly) {
+ startLoadingDirectoryPartition(i);
+ }
+ }
+ } else {
+ getLoaderManager().initLoader(i, null, this);
+ }
+ }
+
+ // Next time this method is called, we should start loading non-priority directories
+ mLoadPriorityDirectoriesOnly = false;
+ }
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ if (id == DIRECTORY_LOADER_ID) {
+ DirectoryListLoader loader = new DirectoryListLoader(mContext);
+ loader.setDirectorySearchMode(mAdapter.getDirectorySearchMode());
+ loader.setLocalInvisibleDirectoryEnabled(
+ ContactEntryListAdapter.LOCAL_INVISIBLE_DIRECTORY_ENABLED);
+ return loader;
+ } else {
+ CursorLoader loader = createCursorLoader(mContext);
+ long directoryId = args != null && args.containsKey(DIRECTORY_ID_ARG_KEY)
+ ? args.getLong(DIRECTORY_ID_ARG_KEY)
+ : Directory.DEFAULT;
+ mAdapter.configureLoader(loader, directoryId);
+ return loader;
+ }
+ }
+
+ public CursorLoader createCursorLoader(Context context) {
+ return new CursorLoader(context, null, null, null, null, null) {
+ @Override
+ protected Cursor onLoadInBackground() {
+ try {
+ return super.onLoadInBackground();
+ } catch (RuntimeException e) {
+ // We don't even know what the projection should be, so no point trying to
+ // return an empty MatrixCursor with the correct projection here.
+ Log.w(TAG, "RuntimeException while trying to query ContactsProvider.");
+ return null;
+ }
+ }
+ };
+ }
+
+ private void startLoadingDirectoryPartition(int partitionIndex) {
+ DirectoryPartition partition = (DirectoryPartition)mAdapter.getPartition(partitionIndex);
+ partition.setStatus(DirectoryPartition.STATUS_LOADING);
+ long directoryId = partition.getDirectoryId();
+ if (mForceLoad) {
+ if (directoryId == Directory.DEFAULT) {
+ loadDirectoryPartition(partitionIndex, partition);
+ } else {
+ loadDirectoryPartitionDelayed(partitionIndex, partition);
+ }
+ } else {
+ Bundle args = new Bundle();
+ args.putLong(DIRECTORY_ID_ARG_KEY, directoryId);
+ getLoaderManager().initLoader(partitionIndex, args, this);
+ }
+ }
+
+ /**
+ * Queues up a delayed request to search the specified directory. Since
+ * directory search will likely introduce a lot of network traffic, we want
+ * to wait for a pause in the user's typing before sending a directory request.
+ */
+ private void loadDirectoryPartitionDelayed(int partitionIndex, DirectoryPartition partition) {
+ mDelayedDirectorySearchHandler.removeMessages(DIRECTORY_SEARCH_MESSAGE, partition);
+ Message msg = mDelayedDirectorySearchHandler.obtainMessage(
+ DIRECTORY_SEARCH_MESSAGE, partitionIndex, 0, partition);
+ mDelayedDirectorySearchHandler.sendMessageDelayed(msg, DIRECTORY_SEARCH_DELAY_MILLIS);
+ }
+
+ /**
+ * Loads the directory partition.
+ */
+ protected void loadDirectoryPartition(int partitionIndex, DirectoryPartition partition) {
+ Bundle args = new Bundle();
+ args.putLong(DIRECTORY_ID_ARG_KEY, partition.getDirectoryId());
+ getLoaderManager().restartLoader(partitionIndex, args, this);
+ }
+
+ /**
+ * Cancels all queued directory loading requests.
+ */
+ private void removePendingDirectorySearchRequests() {
+ mDelayedDirectorySearchHandler.removeMessages(DIRECTORY_SEARCH_MESSAGE);
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data) {
+ if (!mEnabled) {
+ return;
+ }
+
+ int loaderId = loader.getId();
+ if (loaderId == DIRECTORY_LOADER_ID) {
+ mDirectoryListStatus = STATUS_LOADED;
+ mAdapter.changeDirectories(data);
+ startLoading();
+ } else {
+ onPartitionLoaded(loaderId, data);
+ if (isSearchMode()) {
+ int directorySearchMode = getDirectorySearchMode();
+ if (directorySearchMode != DirectoryListLoader.SEARCH_MODE_NONE) {
+ if (mDirectoryListStatus == STATUS_NOT_LOADED) {
+ mDirectoryListStatus = STATUS_LOADING;
+ getLoaderManager().initLoader(DIRECTORY_LOADER_ID, null, this);
+ } else {
+ startLoading();
+ }
+ }
+ } else {
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ getLoaderManager().destroyLoader(DIRECTORY_LOADER_ID);
+ }
+ }
+ }
+
+ public void onLoaderReset(Loader loader) {
+ }
+
+ protected void onPartitionLoaded(int partitionIndex, Cursor data) {
+ if (partitionIndex >= mAdapter.getPartitionCount()) {
+ // When we get unsolicited data, ignore it. This could happen
+ // when we are switching from search mode to the default mode.
+ return;
+ }
+
+ mAdapter.changeCursor(partitionIndex, data);
+ setProfileHeader();
+
+ if (!isLoading()) {
+ completeRestoreInstanceState();
+ }
+ }
+
+ public boolean isLoading() {
+ if (mAdapter != null && mAdapter.isLoading()) {
+ return true;
+ }
+
+ if (isLoadingDirectoryList()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean isLoadingDirectoryList() {
+ return isSearchMode() && getDirectorySearchMode() != DirectoryListLoader.SEARCH_MODE_NONE
+ && (mDirectoryListStatus == STATUS_NOT_LOADED
+ || mDirectoryListStatus == STATUS_LOADING);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mContactsPrefs.unregisterChangeListener();
+ mAdapter.clearPartitions();
+ }
+
+ protected void reloadData() {
+ removePendingDirectorySearchRequests();
+ mAdapter.onDataReload();
+ mLoadPriorityDirectoriesOnly = true;
+ mForceLoad = true;
+ startLoading();
+ }
+
+ /**
+ * Shows a view at the top of the list with a pseudo local profile prompting the user to add
+ * a local profile. Default implementation does nothing.
+ */
+ protected void setProfileHeader() {
+ mUserProfileExists = false;
+ }
+
+ /**
+ * Provides logic that dismisses this fragment. The default implementation
+ * does nothing.
+ */
+ protected void finish() {
+ }
+
+ public void setSectionHeaderDisplayEnabled(boolean flag) {
+ if (mSectionHeaderDisplayEnabled != flag) {
+ mSectionHeaderDisplayEnabled = flag;
+ if (mAdapter != null) {
+ mAdapter.setSectionHeaderDisplayEnabled(flag);
+ }
+ configureVerticalScrollbar();
+ }
+ }
+
+ public boolean isSectionHeaderDisplayEnabled() {
+ return mSectionHeaderDisplayEnabled;
+ }
+
+ public void setVisibleScrollbarEnabled(boolean flag) {
+ if (mVisibleScrollbarEnabled != flag) {
+ mVisibleScrollbarEnabled = flag;
+ configureVerticalScrollbar();
+ }
+ }
+
+ public boolean isVisibleScrollbarEnabled() {
+ return mVisibleScrollbarEnabled;
+ }
+
+ public void setVerticalScrollbarPosition(int position) {
+ if (mVerticalScrollbarPosition != position) {
+ mVerticalScrollbarPosition = position;
+ configureVerticalScrollbar();
+ }
+ }
+
+ private void configureVerticalScrollbar() {
+ boolean hasScrollbar = isVisibleScrollbarEnabled() && isSectionHeaderDisplayEnabled();
+
+ if (mListView != null) {
+ mListView.setFastScrollEnabled(hasScrollbar);
+ mListView.setFastScrollAlwaysVisible(hasScrollbar);
+ mListView.setVerticalScrollbarPosition(mVerticalScrollbarPosition);
+ mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+ }
+ }
+
+ public void setPhotoLoaderEnabled(boolean flag) {
+ mPhotoLoaderEnabled = flag;
+ configurePhotoLoader();
+ }
+
+ public boolean isPhotoLoaderEnabled() {
+ return mPhotoLoaderEnabled;
+ }
+
+ /**
+ * Returns true if the list is supposed to visually highlight the selected item.
+ */
+ public boolean isSelectionVisible() {
+ return mSelectionVisible;
+ }
+
+ public void setSelectionVisible(boolean flag) {
+ this.mSelectionVisible = flag;
+ }
+
+ public void setQuickContactEnabled(boolean flag) {
+ this.mQuickContactEnabled = flag;
+ }
+
+ public void setAdjustSelectionBoundsEnabled(boolean flag) {
+ mAdjustSelectionBoundsEnabled = flag;
+ }
+
+ public void setIncludeProfile(boolean flag) {
+ mIncludeProfile = flag;
+ if(mAdapter != null) {
+ mAdapter.setIncludeProfile(flag);
+ }
+ }
+
+ /**
+ * Enter/exit search mode. This is method is tightly related to the current query, and should
+ * only be called by {@link #setQueryString}.
+ *
+ * Also note this method doesn't call {@link #reloadData()}; {@link #setQueryString} does it.
+ */
+ protected void setSearchMode(boolean flag) {
+ if (mSearchMode != flag) {
+ mSearchMode = flag;
+ setSectionHeaderDisplayEnabled(!mSearchMode);
+
+ if (!flag) {
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ getLoaderManager().destroyLoader(DIRECTORY_LOADER_ID);
+ }
+
+ if (mAdapter != null) {
+ mAdapter.setSearchMode(flag);
+
+ mAdapter.clearPartitions();
+ if (!flag) {
+ // If we are switching from search to regular display, remove all directory
+ // partitions after default one, assuming they are remote directories which
+ // should be cleaned up on exiting the search mode.
+ mAdapter.removeDirectoriesAfterDefault();
+ }
+ mAdapter.configureDefaultPartition(false, flag);
+ }
+
+ if (mListView != null) {
+ mListView.setFastScrollEnabled(!flag);
+ }
+ }
+ }
+
+ public final boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ public final String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String queryString, boolean delaySelection) {
+ if (!TextUtils.equals(mQueryString, queryString)) {
+ if (mShowEmptyListForEmptyQuery && mAdapter != null && mListView != null) {
+ if (TextUtils.isEmpty(mQueryString)) {
+ // Restore the adapter if the query used to be empty.
+ mListView.setAdapter(mAdapter);
+ } else if (TextUtils.isEmpty(queryString)) {
+ // Instantly clear the list view if the new query is empty.
+ mListView.setAdapter(null);
+ }
+ }
+
+ mQueryString = queryString;
+ setSearchMode(!TextUtils.isEmpty(mQueryString) || mShowEmptyListForEmptyQuery);
+
+ if (mAdapter != null) {
+ mAdapter.setQueryString(queryString);
+ reloadData();
+ }
+ }
+ }
+
+ public void setShowEmptyListForNullQuery(boolean show) {
+ mShowEmptyListForEmptyQuery = show;
+ }
+
+ public int getDirectoryLoaderId() {
+ return DIRECTORY_LOADER_ID;
+ }
+
+ public int getDirectorySearchMode() {
+ return mDirectorySearchMode;
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ public boolean isLegacyCompatibilityMode() {
+ return mLegacyCompatibility;
+ }
+
+ public void setLegacyCompatibilityMode(boolean flag) {
+ mLegacyCompatibility = flag;
+ }
+
+ protected int getContactNameDisplayOrder() {
+ return mDisplayOrder;
+ }
+
+ protected void setContactNameDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ if (mAdapter != null) {
+ mAdapter.setContactNameDisplayOrder(displayOrder);
+ }
+ }
+
+ public int getSortOrder() {
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ if (mAdapter != null) {
+ mAdapter.setSortOrder(sortOrder);
+ }
+ }
+
+ public void setDirectoryResultLimit(int limit) {
+ mDirectoryResultLimit = limit;
+ }
+
+ protected boolean loadPreferences() {
+ boolean changed = false;
+ if (getContactNameDisplayOrder() != mContactsPrefs.getDisplayOrder()) {
+ setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
+ changed = true;
+ }
+
+ if (getSortOrder() != mContactsPrefs.getSortOrder()) {
+ setSortOrder(mContactsPrefs.getSortOrder());
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ onCreateView(inflater, container);
+
+ boolean searchMode = isSearchMode();
+ mAdapter.setSearchMode(searchMode);
+ mAdapter.configureDefaultPartition(false, searchMode);
+ mAdapter.setPhotoLoader(mPhotoManager);
+ mListView.setAdapter(mAdapter);
+
+ if (!isSearchMode()) {
+ mListView.setFocusableInTouchMode(true);
+ mListView.requestFocus();
+ }
+
+ return mView;
+ }
+
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ mView = inflateView(inflater, container);
+
+ mListView = (ListView)mView.findViewById(android.R.id.list);
+ if (mListView == null) {
+ throw new RuntimeException(
+ "Your content must have a ListView whose id attribute is " +
+ "'android.R.id.list'");
+ }
+
+ View emptyView = mView.findViewById(android.R.id.empty);
+ if (emptyView != null) {
+ mListView.setEmptyView(emptyView);
+ }
+
+ mListView.setOnItemClickListener(this);
+ mListView.setOnItemLongClickListener(this);
+ mListView.setOnFocusChangeListener(this);
+ mListView.setOnTouchListener(this);
+ mListView.setFastScrollEnabled(!isSearchMode());
+
+ // Tell list view to not show dividers. We'll do it ourself so that we can *not* show
+ // them when an A-Z headers is visible.
+ mListView.setDividerHeight(0);
+
+ // We manually save/restore the listview state
+ mListView.setSaveEnabled(false);
+
+ configureVerticalScrollbar();
+ configurePhotoLoader();
+
+ getAdapter().setFragmentRootView(getView());
+
+ ContactListViewUtils.applyCardPaddingToView(getResources(), mListView, mView);
+ }
+
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ if (getActivity() != null && getView() != null && !hidden) {
+ // If the padding was last applied when in a hidden state, it may have been applied
+ // incorrectly. Therefore we need to reapply it.
+ ContactListViewUtils.applyCardPaddingToView(getResources(), mListView, getView());
+ }
+ }
+
+ protected void configurePhotoLoader() {
+ if (isPhotoLoaderEnabled() && mContext != null) {
+ if (mPhotoManager == null) {
+ mPhotoManager = ContactPhotoManager.getInstance(mContext);
+ }
+ if (mListView != null) {
+ mListView.setOnScrollListener(this);
+ }
+ if (mAdapter != null) {
+ mAdapter.setPhotoLoader(mPhotoManager);
+ }
+ }
+ }
+
+ protected void configureAdapter() {
+ if (mAdapter == null) {
+ return;
+ }
+
+ mAdapter.setQuickContactEnabled(mQuickContactEnabled);
+ mAdapter.setAdjustSelectionBoundsEnabled(mAdjustSelectionBoundsEnabled);
+ mAdapter.setIncludeProfile(mIncludeProfile);
+ mAdapter.setQueryString(mQueryString);
+ mAdapter.setDirectorySearchMode(mDirectorySearchMode);
+ mAdapter.setPinnedPartitionHeadersEnabled(false);
+ mAdapter.setContactNameDisplayOrder(mDisplayOrder);
+ mAdapter.setSortOrder(mSortOrder);
+ mAdapter.setSectionHeaderDisplayEnabled(mSectionHeaderDisplayEnabled);
+ mAdapter.setSelectionVisible(mSelectionVisible);
+ mAdapter.setDirectoryResultLimit(mDirectoryResultLimit);
+ mAdapter.setDarkTheme(mDarkTheme);
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
+ mPhotoManager.pause();
+ } else if (isPhotoLoaderEnabled()) {
+ mPhotoManager.resume();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ hideSoftKeyboard();
+
+ int adjPosition = position - mListView.getHeaderViewsCount();
+ if (adjPosition >= 0) {
+ onItemClick(adjPosition, id);
+ }
+ }
+
+ @Override
+ public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
+ int adjPosition = position - mListView.getHeaderViewsCount();
+
+ if (adjPosition >= 0) {
+ return onItemLongClick(adjPosition, id);
+ }
+ return false;
+ }
+
+ private void hideSoftKeyboard() {
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager = (InputMethodManager)
+ mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list takes focus.
+ */
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (view == mListView && hasFocus) {
+ hideSoftKeyboard();
+ }
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list is touched.
+ */
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view == mListView) {
+ hideSoftKeyboard();
+ }
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ removePendingDirectorySearchRequests();
+ }
+
+ /**
+ * Restore the list state after the adapter is populated.
+ */
+ protected void completeRestoreInstanceState() {
+ if (mListState != null) {
+ mListView.onRestoreInstanceState(mListState);
+ mListState = null;
+ }
+ }
+
+ public void setDarkTheme(boolean value) {
+ mDarkTheme = value;
+ if (mAdapter != null) mAdapter.setDarkTheme(value);
+ }
+
+ /**
+ * Processes a result returned by the contact picker.
+ */
+ public void onPickerResult(Intent data) {
+ throw new UnsupportedOperationException("Picker result handler is not implemented.");
+ }
+
+ private ContactsPreferences.ChangeListener mPreferencesChangeListener =
+ new ContactsPreferences.ChangeListener() {
+ @Override
+ public void onChange() {
+ loadPreferences();
+ reloadData();
+ }
+ };
+
+ private int getDefaultVerticalScrollbarPosition() {
+ final Locale locale = Locale.getDefault();
+ final int layoutDirection = TextUtils.getLayoutDirectionFromLocale(locale);
+ switch (layoutDirection) {
+ case View.LAYOUT_DIRECTION_RTL:
+ return View.SCROLLBAR_POSITION_LEFT;
+ case View.LAYOUT_DIRECTION_LTR:
+ default:
+ return View.SCROLLBAR_POSITION_RIGHT;
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactListAdapter.java b/ContactsCommon/src/com/android/contacts/common/list/ContactListAdapter.java
new file mode 100644
index 0000000..600d731
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactListAdapter.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.SearchSnippets;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.R;
+import com.android.contacts.common.preference.ContactsPreferences;
+
+/**
+ * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
+ * Also includes support for including the {@link ContactsContract.Profile} record in the
+ * list.
+ */
+public abstract class ContactListAdapter extends ContactEntryListAdapter {
+
+ protected static class ContactQuery {
+ private static final String[] CONTACT_PROJECTION_PRIMARY = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_PRIMARY, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ };
+
+ private static final String[] CONTACT_PROJECTION_ALTERNATIVE = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_ALTERNATIVE, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ };
+
+ private static final String[] FILTER_PROJECTION_PRIMARY = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_PRIMARY, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ SearchSnippets.SNIPPET, // 8
+ };
+
+ private static final String[] FILTER_PROJECTION_ALTERNATIVE = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_ALTERNATIVE, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ SearchSnippets.SNIPPET, // 8
+ };
+
+ public static final int CONTACT_ID = 0;
+ public static final int CONTACT_DISPLAY_NAME = 1;
+ public static final int CONTACT_PRESENCE_STATUS = 2;
+ public static final int CONTACT_CONTACT_STATUS = 3;
+ public static final int CONTACT_PHOTO_ID = 4;
+ public static final int CONTACT_PHOTO_URI = 5;
+ public static final int CONTACT_LOOKUP_KEY = 6;
+ public static final int CONTACT_IS_USER_PROFILE = 7;
+ public static final int CONTACT_SNIPPET = 8;
+ }
+
+ private CharSequence mUnknownNameText;
+
+ private long mSelectedContactDirectoryId;
+ private String mSelectedContactLookupKey;
+ private long mSelectedContactId;
+ private ContactListItemView.PhotoPosition mPhotoPosition;
+
+ public ContactListAdapter(Context context) {
+ super(context);
+
+ mUnknownNameText = context.getText(R.string.missing_name);
+ }
+
+ public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+ }
+
+ public ContactListItemView.PhotoPosition getPhotoPosition() {
+ return mPhotoPosition;
+ }
+
+ public CharSequence getUnknownNameText() {
+ return mUnknownNameText;
+ }
+
+ public long getSelectedContactDirectoryId() {
+ return mSelectedContactDirectoryId;
+ }
+
+ public String getSelectedContactLookupKey() {
+ return mSelectedContactLookupKey;
+ }
+
+ public long getSelectedContactId() {
+ return mSelectedContactId;
+ }
+
+ public void setSelectedContact(long selectedDirectoryId, String lookupKey, long contactId) {
+ mSelectedContactDirectoryId = selectedDirectoryId;
+ mSelectedContactLookupKey = lookupKey;
+ mSelectedContactId = contactId;
+ }
+
+ protected static Uri buildSectionIndexerUri(Uri uri) {
+ return uri.buildUpon()
+ .appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true").build();
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor) getItem(position)).getString(ContactQuery.CONTACT_DISPLAY_NAME);
+ }
+
+ /**
+ * Builds the {@link Contacts#CONTENT_LOOKUP_URI} for the given
+ * {@link ListView} position.
+ */
+ public Uri getContactUri(int position) {
+ int partitionIndex = getPartitionForPosition(position);
+ Cursor item = (Cursor)getItem(position);
+ return item != null ? getContactUri(partitionIndex, item) : null;
+ }
+
+ public Uri getContactUri(int partitionIndex, Cursor cursor) {
+ long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
+ String lookupKey = cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY);
+ Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+ long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
+ if (uri != null && directoryId != Directory.DEFAULT) {
+ uri = uri.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
+ }
+ return uri;
+ }
+
+ /**
+ * Returns true if the specified contact is selected in the list. For a
+ * contact to be shown as selected, we need both the directory and and the
+ * lookup key to be the same. We are paying no attention to the contactId,
+ * because it is volatile, especially in the case of directories.
+ */
+ public boolean isSelectedContact(int partitionIndex, Cursor cursor) {
+ long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
+ if (getSelectedContactDirectoryId() != directoryId) {
+ return false;
+ }
+ String lookupKey = getSelectedContactLookupKey();
+ if (lookupKey != null && TextUtils.equals(lookupKey,
+ cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY))) {
+ return true;
+ }
+
+ return directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE
+ && getSelectedContactId() == cursor.getLong(ContactQuery.CONTACT_ID);
+ }
+
+ @Override
+ protected ContactListItemView newView(
+ Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
+ ContactListItemView view = super.newView(context, partition, cursor, position, parent);
+ view.setUnknownNameText(mUnknownNameText);
+ view.setQuickContactEnabled(isQuickContactEnabled());
+ view.setAdjustSelectionBoundsEnabled(isAdjustSelectionBoundsEnabled());
+ view.setActivatedStateSupported(isSelectionVisible());
+ if (mPhotoPosition != null) {
+ view.setPhotoPosition(mPhotoPosition);
+ }
+ return view;
+ }
+
+ protected void bindSectionHeaderAndDivider(ContactListItemView view, int position,
+ Cursor cursor) {
+ view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled());
+ if (isSectionHeaderDisplayEnabled()) {
+ Placement placement = getItemPlacementInSection(position);
+ view.setSectionHeader(placement.sectionHeader);
+ } else {
+ view.setSectionHeader(null);
+ }
+ }
+
+ protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) {
+ if (!isPhotoSupported(partitionIndex)) {
+ view.removePhotoView();
+ return;
+ }
+
+ // Set the photo, if available
+ long photoId = 0;
+ if (!cursor.isNull(ContactQuery.CONTACT_PHOTO_ID)) {
+ photoId = cursor.getLong(ContactQuery.CONTACT_PHOTO_ID);
+ }
+
+ if (photoId != 0) {
+ getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false,
+ getCircularPhotos(), null);
+ } else {
+ final String photoUriString = cursor.getString(ContactQuery.CONTACT_PHOTO_URI);
+ final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ request = getDefaultImageRequestFromCursor(cursor,
+ ContactQuery.CONTACT_DISPLAY_NAME,
+ ContactQuery.CONTACT_LOOKUP_KEY);
+ }
+ getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false,
+ getCircularPhotos(), request);
+ }
+ }
+
+ protected void bindNameAndViewId(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(
+ cursor, ContactQuery.CONTACT_DISPLAY_NAME, getContactNameDisplayOrder());
+ // Note: we don't show phonetic any more (See issue 5265330)
+
+ bindViewId(view, cursor, ContactQuery.CONTACT_ID);
+ }
+
+ protected void bindPresenceAndStatusMessage(final ContactListItemView view, Cursor cursor) {
+ view.showPresenceAndStatusMessage(cursor, ContactQuery.CONTACT_PRESENCE_STATUS,
+ ContactQuery.CONTACT_CONTACT_STATUS);
+ }
+
+ protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
+ view.showSnippet(cursor, ContactQuery.CONTACT_SNIPPET);
+ }
+
+ public int getSelectedContactPosition() {
+ if (mSelectedContactLookupKey == null && mSelectedContactId == 0) {
+ return -1;
+ }
+
+ Cursor cursor = null;
+ int partitionIndex = -1;
+ int partitionCount = getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ if (partition.getDirectoryId() == mSelectedContactDirectoryId) {
+ partitionIndex = i;
+ break;
+ }
+ }
+ if (partitionIndex == -1) {
+ return -1;
+ }
+
+ cursor = getCursor(partitionIndex);
+ if (cursor == null) {
+ return -1;
+ }
+
+ cursor.moveToPosition(-1); // Reset cursor
+ int offset = -1;
+ while (cursor.moveToNext()) {
+ if (mSelectedContactLookupKey != null) {
+ String lookupKey = cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY);
+ if (mSelectedContactLookupKey.equals(lookupKey)) {
+ offset = cursor.getPosition();
+ break;
+ }
+ }
+ if (mSelectedContactId != 0 && (mSelectedContactDirectoryId == Directory.DEFAULT
+ || mSelectedContactDirectoryId == Directory.LOCAL_INVISIBLE)) {
+ long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
+ if (contactId == mSelectedContactId) {
+ offset = cursor.getPosition();
+ break;
+ }
+ }
+ }
+ if (offset == -1) {
+ return -1;
+ }
+
+ int position = getPositionForPartition(partitionIndex) + offset;
+ if (hasHeader(partitionIndex)) {
+ position++;
+ }
+ return position;
+ }
+
+ public boolean hasValidSelection() {
+ return getSelectedContactPosition() != -1;
+ }
+
+ public Uri getFirstContactUri() {
+ int partitionCount = getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ if (partition.isLoading()) {
+ continue;
+ }
+
+ Cursor cursor = getCursor(i);
+ if (cursor == null) {
+ continue;
+ }
+
+ if (!cursor.moveToFirst()) {
+ continue;
+ }
+
+ return getContactUri(i, cursor);
+ }
+
+ return null;
+ }
+
+ @Override
+ public void changeCursor(int partitionIndex, Cursor cursor) {
+ super.changeCursor(partitionIndex, cursor);
+
+ // Check if a profile exists
+ if (cursor != null && cursor.moveToFirst()) {
+ setProfileExists(cursor.getInt(ContactQuery.CONTACT_IS_USER_PROFILE) == 1);
+ }
+ }
+
+ /**
+ * @return Projection useful for children.
+ */
+ protected final String[] getProjection(boolean forSearch) {
+ final int sortOrder = getContactNameDisplayOrder();
+ if (forSearch) {
+ if (sortOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
+ return ContactQuery.FILTER_PROJECTION_PRIMARY;
+ } else {
+ return ContactQuery.FILTER_PROJECTION_ALTERNATIVE;
+ }
+ } else {
+ if (sortOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
+ return ContactQuery.CONTACT_PROJECTION_PRIMARY;
+ } else {
+ return ContactQuery.CONTACT_PROJECTION_ALTERNATIVE;
+ }
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactListFilter.java b/ContactsCommon/src/com/android/contacts/common/list/ContactListFilter.java
new file mode 100644
index 0000000..f81ea74
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactListFilter.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+
+/**
+ * Contact list filter parameters.
+ */
+public final class ContactListFilter implements Comparable, Parcelable {
+
+ public static final int FILTER_TYPE_DEFAULT = -1;
+ public static final int FILTER_TYPE_ALL_ACCOUNTS = -2;
+ public static final int FILTER_TYPE_CUSTOM = -3;
+ public static final int FILTER_TYPE_STARRED = -4;
+ public static final int FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY = -5;
+ public static final int FILTER_TYPE_SINGLE_CONTACT = -6;
+
+ public static final int FILTER_TYPE_ACCOUNT = 0;
+
+ /**
+ * Obsolete filter which had been used in Honeycomb. This may be stored in
+ * {@link SharedPreferences}, but should be replaced with ALL filter when it is found.
+ *
+ * TODO: "group" filter and relevant variables are all obsolete. Remove them.
+ */
+ private static final int FILTER_TYPE_GROUP = 1;
+
+ private static final String KEY_FILTER_TYPE = "filter.type";
+ private static final String KEY_ACCOUNT_NAME = "filter.accountName";
+ private static final String KEY_ACCOUNT_TYPE = "filter.accountType";
+ private static final String KEY_DATA_SET = "filter.dataSet";
+
+ public final int filterType;
+ public final String accountType;
+ public final String accountName;
+ public final String dataSet;
+ public final Drawable icon;
+ private String mId;
+
+ public ContactListFilter(int filterType, String accountType, String accountName, String dataSet,
+ Drawable icon) {
+ this.filterType = filterType;
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.dataSet = dataSet;
+ this.icon = icon;
+ }
+
+ public static ContactListFilter createFilterWithType(int filterType) {
+ return new ContactListFilter(filterType, null, null, null, null);
+ }
+
+ public static ContactListFilter createAccountFilter(String accountType, String accountName,
+ String dataSet, Drawable icon) {
+ return new ContactListFilter(ContactListFilter.FILTER_TYPE_ACCOUNT, accountType,
+ accountName, dataSet, icon);
+ }
+
+ /**
+ * Returns true if this filter is based on data and may become invalid over time.
+ */
+ public boolean isValidationRequired() {
+ return filterType == FILTER_TYPE_ACCOUNT;
+ }
+
+ @Override
+ public String toString() {
+ switch (filterType) {
+ case FILTER_TYPE_DEFAULT:
+ return "default";
+ case FILTER_TYPE_ALL_ACCOUNTS:
+ return "all_accounts";
+ case FILTER_TYPE_CUSTOM:
+ return "custom";
+ case FILTER_TYPE_STARRED:
+ return "starred";
+ case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ return "with_phones";
+ case FILTER_TYPE_SINGLE_CONTACT:
+ return "single";
+ case FILTER_TYPE_ACCOUNT:
+ return "account: " + accountType + (dataSet != null ? "/" + dataSet : "")
+ + " " + accountName;
+ }
+ return super.toString();
+ }
+
+ @Override
+ public int compareTo(ContactListFilter another) {
+ int res = accountName.compareTo(another.accountName);
+ if (res != 0) {
+ return res;
+ }
+
+ res = accountType.compareTo(another.accountType);
+ if (res != 0) {
+ return res;
+ }
+
+ return filterType - another.filterType;
+ }
+
+ @Override
+ public int hashCode() {
+ int code = filterType;
+ if (accountType != null) {
+ code = code * 31 + accountType.hashCode();
+ code = code * 31 + accountName.hashCode();
+ }
+ if (dataSet != null) {
+ code = code * 31 + dataSet.hashCode();
+ }
+ return code;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof ContactListFilter)) {
+ return false;
+ }
+
+ ContactListFilter otherFilter = (ContactListFilter) other;
+ if (filterType != otherFilter.filterType
+ || !TextUtils.equals(accountName, otherFilter.accountName)
+ || !TextUtils.equals(accountType, otherFilter.accountType)
+ || !TextUtils.equals(dataSet, otherFilter.dataSet)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Store the given {@link ContactListFilter} to preferences. If the requested filter is
+ * of type {@link #FILTER_TYPE_SINGLE_CONTACT} then do not save it to preferences because
+ * it is a temporary state.
+ */
+ public static void storeToPreferences(SharedPreferences prefs, ContactListFilter filter) {
+ if (filter != null && filter.filterType == FILTER_TYPE_SINGLE_CONTACT) {
+ return;
+ }
+ prefs.edit()
+ .putInt(KEY_FILTER_TYPE, filter == null ? FILTER_TYPE_DEFAULT : filter.filterType)
+ .putString(KEY_ACCOUNT_NAME, filter == null ? null : filter.accountName)
+ .putString(KEY_ACCOUNT_TYPE, filter == null ? null : filter.accountType)
+ .putString(KEY_DATA_SET, filter == null ? null : filter.dataSet)
+ .apply();
+ }
+
+ /**
+ * Try to obtain ContactListFilter object saved in SharedPreference.
+ * If there's no info there, return ALL filter instead.
+ */
+ public static ContactListFilter restoreDefaultPreferences(SharedPreferences prefs) {
+ ContactListFilter filter = restoreFromPreferences(prefs);
+ if (filter == null) {
+ filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS);
+ }
+ // "Group" filter is obsolete and thus is not exposed anymore. The "single contact mode"
+ // should also not be stored in preferences anymore since it is a temporary state.
+ if (filter.filterType == FILTER_TYPE_GROUP ||
+ filter.filterType == FILTER_TYPE_SINGLE_CONTACT) {
+ filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS);
+ }
+ return filter;
+ }
+
+ private static ContactListFilter restoreFromPreferences(SharedPreferences prefs) {
+ int filterType = prefs.getInt(KEY_FILTER_TYPE, FILTER_TYPE_DEFAULT);
+ if (filterType == FILTER_TYPE_DEFAULT) {
+ return null;
+ }
+
+ String accountName = prefs.getString(KEY_ACCOUNT_NAME, null);
+ String accountType = prefs.getString(KEY_ACCOUNT_TYPE, null);
+ String dataSet = prefs.getString(KEY_DATA_SET, null);
+ return new ContactListFilter(filterType, accountType, accountName, dataSet, null);
+ }
+
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(filterType);
+ dest.writeString(accountName);
+ dest.writeString(accountType);
+ dest.writeString(dataSet);
+ }
+
+ public static final Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ @Override
+ public ContactListFilter createFromParcel(Parcel source) {
+ int filterType = source.readInt();
+ String accountName = source.readString();
+ String accountType = source.readString();
+ String dataSet = source.readString();
+ return new ContactListFilter(filterType, accountType, accountName, dataSet, null);
+ }
+
+ @Override
+ public ContactListFilter[] newArray(int size) {
+ return new ContactListFilter[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns a string that can be used as a stable persistent identifier for this filter.
+ */
+ public String getId() {
+ if (mId == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(filterType);
+ if (accountType != null) {
+ sb.append('-').append(accountType);
+ }
+ if (dataSet != null) {
+ sb.append('/').append(dataSet);
+ }
+ if (accountName != null) {
+ sb.append('-').append(accountName.replace('-', '_'));
+ }
+ mId = sb.toString();
+ }
+ return mId;
+ }
+
+ /**
+ * Adds the account query parameters to the given {@code uriBuilder}.
+ *
+ * @throws IllegalStateException if the filter type is not {@link #FILTER_TYPE_ACCOUNT}.
+ */
+ public Uri.Builder addAccountQueryParameterToUrl(Uri.Builder uriBuilder) {
+ if (filterType != FILTER_TYPE_ACCOUNT) {
+ throw new IllegalStateException("filterType must be FILTER_TYPE_ACCOUNT");
+ }
+ uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
+ uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
+ if (!TextUtils.isEmpty(dataSet)) {
+ uriBuilder.appendQueryParameter(RawContacts.DATA_SET, dataSet);
+ }
+ return uriBuilder;
+ }
+
+ public String toDebugString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("[filter type: " + filterType + " (" + filterTypeToString(filterType) + ")");
+ if (filterType == FILTER_TYPE_ACCOUNT) {
+ builder.append(", accountType: " + accountType)
+ .append(", accountName: " + accountName)
+ .append(", dataSet: " + dataSet);
+ }
+ builder.append(", icon: " + icon + "]");
+ return builder.toString();
+ }
+
+ public static final String filterTypeToString(int filterType) {
+ switch (filterType) {
+ case FILTER_TYPE_DEFAULT:
+ return "FILTER_TYPE_DEFAULT";
+ case FILTER_TYPE_ALL_ACCOUNTS:
+ return "FILTER_TYPE_ALL_ACCOUNTS";
+ case FILTER_TYPE_CUSTOM:
+ return "FILTER_TYPE_CUSTOM";
+ case FILTER_TYPE_STARRED:
+ return "FILTER_TYPE_STARRED";
+ case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ return "FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY";
+ case FILTER_TYPE_SINGLE_CONTACT:
+ return "FILTER_TYPE_SINGLE_CONTACT";
+ case FILTER_TYPE_ACCOUNT:
+ return "FILTER_TYPE_ACCOUNT";
+ default:
+ return "(unknown)";
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactListFilterController.java b/ContactsCommon/src/com/android/contacts/common/list/ContactListFilterController.java
new file mode 100644
index 0000000..fcd4c89
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactListFilterController.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages {@link ContactListFilter}. All methods must be called from UI thread.
+ */
+public abstract class ContactListFilterController {
+
+ // singleton to cache the filter controller
+ private static ContactListFilterControllerImpl sFilterController = null;
+
+ public interface ContactListFilterListener {
+ void onContactListFilterChanged();
+ }
+
+ public static ContactListFilterController getInstance(Context context) {
+ // We may need to synchronize this in the future if background task will call this.
+ if (sFilterController == null) {
+ sFilterController = new ContactListFilterControllerImpl(context);
+ }
+ return sFilterController;
+ }
+
+ public abstract void addListener(ContactListFilterListener listener);
+
+ public abstract void removeListener(ContactListFilterListener listener);
+
+ /**
+ * Return the currently-active filter.
+ */
+ public abstract ContactListFilter getFilter();
+
+ /**
+ * @param filter the filter
+ * @param persistent True when the given filter should be saved soon. False when the filter
+ * should not be saved. The latter case may happen when some Intent requires a certain type of
+ * UI (e.g. single contact) temporarily.
+ */
+ public abstract void setContactListFilter(ContactListFilter filter, boolean persistent);
+
+ public abstract void selectCustomFilter();
+
+ /**
+ * Checks if the current filter is valid and reset the filter if not. It may happen when
+ * an account is removed while the filter points to the account with
+ * {@link ContactListFilter#FILTER_TYPE_ACCOUNT} type, for example. It may also happen if
+ * the current filter is {@link ContactListFilter#FILTER_TYPE_SINGLE_CONTACT}, in
+ * which case, we should switch to the last saved filter in {@link SharedPreferences}.
+ */
+ public abstract void checkFilterValidity(boolean notifyListeners);
+}
+
+/**
+ * Stores the {@link ContactListFilter} selected by the user and saves it to
+ * {@link SharedPreferences} if necessary.
+ */
+class ContactListFilterControllerImpl extends ContactListFilterController {
+ private final Context mContext;
+ private final List mListeners =
+ new ArrayList();
+ private ContactListFilter mFilter;
+
+ public ContactListFilterControllerImpl(Context context) {
+ mContext = context;
+ mFilter = ContactListFilter.restoreDefaultPreferences(getSharedPreferences());
+ checkFilterValidity(true /* notify listeners */);
+ }
+
+ @Override
+ public void addListener(ContactListFilterListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeListener(ContactListFilterListener listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ private SharedPreferences getSharedPreferences() {
+ return PreferenceManager.getDefaultSharedPreferences(mContext);
+ }
+
+ @Override
+ public void setContactListFilter(ContactListFilter filter, boolean persistent) {
+ setContactListFilter(filter, persistent, true);
+ }
+
+ private void setContactListFilter(ContactListFilter filter, boolean persistent,
+ boolean notifyListeners) {
+ if (!filter.equals(mFilter)) {
+ mFilter = filter;
+ if (persistent) {
+ ContactListFilter.storeToPreferences(getSharedPreferences(), mFilter);
+ }
+ if (notifyListeners && !mListeners.isEmpty()) {
+ notifyContactListFilterChanged();
+ }
+ }
+ }
+
+ @Override
+ public void selectCustomFilter() {
+ setContactListFilter(ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_CUSTOM), true);
+ }
+
+ private void notifyContactListFilterChanged() {
+ for (ContactListFilterListener listener : mListeners) {
+ listener.onContactListFilterChanged();
+ }
+ }
+
+ @Override
+ public void checkFilterValidity(boolean notifyListeners) {
+ if (mFilter == null) {
+ return;
+ }
+
+ switch (mFilter.filterType) {
+ case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT:
+ setContactListFilter(
+ ContactListFilter.restoreDefaultPreferences(getSharedPreferences()),
+ false, notifyListeners);
+ break;
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ if (!filterAccountExists()) {
+ // The current account filter points to invalid account. Use "all" filter
+ // instead.
+ setContactListFilter(ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), true, notifyListeners);
+ }
+ }
+ }
+
+ /**
+ * @return true if the Account for the current filter exists.
+ */
+ private boolean filterAccountExists() {
+ final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
+ final AccountWithDataSet filterAccount = new AccountWithDataSet(
+ mFilter.accountName, mFilter.accountType, mFilter.dataSet);
+ return accountTypeManager.contains(filterAccount, false);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactListFilterView.java b/ContactsCommon/src/com/android/contacts/common/list/ContactListFilterView.java
new file mode 100644
index 0000000..4cea755
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactListFilterView.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+
+/**
+ * Contact list filter parameters.
+ */
+public class ContactListFilterView extends LinearLayout {
+
+ private static final String TAG = ContactListFilterView.class.getSimpleName();
+
+ private ImageView mIcon;
+ private TextView mAccountType;
+ private TextView mAccountUserName;
+ private RadioButton mRadioButton;
+ private ContactListFilter mFilter;
+ private boolean mSingleAccount;
+
+ public ContactListFilterView(Context context) {
+ super(context);
+ }
+
+ public ContactListFilterView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setContactListFilter(ContactListFilter filter) {
+ mFilter = filter;
+ }
+
+ public ContactListFilter getContactListFilter() {
+ return mFilter;
+ }
+
+ public void setSingleAccount(boolean flag) {
+ this.mSingleAccount = flag;
+ }
+
+ @Override
+ public void setActivated(boolean activated) {
+ super.setActivated(activated);
+ if (mRadioButton != null) {
+ mRadioButton.setChecked(activated);
+ } else {
+ // We're guarding against null-pointer exceptions,
+ // but otherwise this code is not expected to work
+ // properly if the button hasn't been initialized.
+ Log.wtf(TAG, "radio-button cannot be activated because it is null");
+ }
+ }
+
+ public void bindView(AccountTypeManager accountTypes) {
+ if (mAccountType == null) {
+ mIcon = (ImageView) findViewById(R.id.icon);
+ mAccountType = (TextView) findViewById(R.id.accountType);
+ mAccountUserName = (TextView) findViewById(R.id.accountUserName);
+ mRadioButton = (RadioButton) findViewById(R.id.radioButton);
+ mRadioButton.setChecked(isActivated());
+ }
+
+ if (mFilter == null) {
+ mAccountType.setText(R.string.contactsList);
+ return;
+ }
+
+ mAccountUserName.setVisibility(View.GONE);
+ switch (mFilter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: {
+ bindView(0, R.string.list_filter_all_accounts);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_STARRED: {
+ bindView(R.drawable.ic_menu_star_holo_light, R.string.list_filter_all_starred);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_CUSTOM: {
+ bindView(R.drawable.ic_menu_settings_holo_light, R.string.list_filter_customize);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: {
+ bindView(0, R.string.list_filter_phones);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT: {
+ bindView(0, R.string.list_filter_single);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT: {
+ mAccountUserName.setVisibility(View.VISIBLE);
+ mIcon.setVisibility(View.VISIBLE);
+ if (mFilter.icon != null) {
+ mIcon.setImageDrawable(mFilter.icon);
+ } else {
+ mIcon.setImageResource(R.drawable.unknown_source);
+ }
+ final AccountType accountType =
+ accountTypes.getAccountType(mFilter.accountType, mFilter.dataSet);
+ mAccountUserName.setText(mFilter.accountName);
+ mAccountType.setText(accountType.getDisplayLabel(getContext()));
+ break;
+ }
+ }
+ }
+
+ private void bindView(int iconResource, int textResource) {
+ if (iconResource != 0) {
+ mIcon.setVisibility(View.VISIBLE);
+ mIcon.setImageResource(iconResource);
+ } else {
+ mIcon.setVisibility(View.GONE);
+ }
+
+ mAccountType.setText(textResource);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactListItemView.java b/ContactsCommon/src/com/android/contacts/common/list/ContactListItemView.java
new file mode 100644
index 0000000..d70aeb5
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactListItemView.java
@@ -0,0 +1,1532 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.database.CharArrayBuffer;
+import android.database.Cursor;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.telephony.PhoneNumberUtils;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView.SelectionBoundsAdjuster;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+
+import com.android.contacts.common.ContactPresenceIconUtil;
+import com.android.contacts.common.ContactStatusUtil;
+import com.android.contacts.common.R;
+import com.android.contacts.common.format.TextHighlighter;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.contacts.common.util.SearchUtil;
+import com.android.contacts.common.util.ViewUtil;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A custom view for an item in the contact list.
+ * The view contains the contact's photo, a set of text views (for name, status, etc...) and
+ * icons for presence and call.
+ * The view uses no XML file for layout and all the measurements and layouts are done
+ * in the onMeasure and onLayout methods.
+ *
+ * The layout puts the contact's photo on the right side of the view, the call icon (if present)
+ * to the left of the photo, the text lines are aligned to the left and the presence icon (if
+ * present) is set to the left of the status line.
+ *
+ * The layout also supports a header (used as a header of a group of contacts) that is above the
+ * contact's data and a divider between contact view.
+ */
+
+public class ContactListItemView extends ViewGroup
+ implements SelectionBoundsAdjuster {
+
+ // Style values for layout and appearance
+ // The initialized values are defaults if none is provided through xml.
+ private int mPreferredHeight = 0;
+ private int mGapBetweenImageAndText = 0;
+ private int mGapBetweenLabelAndData = 0;
+ private int mPresenceIconMargin = 4;
+ private int mPresenceIconSize = 16;
+ private int mTextIndent = 0;
+ private int mTextOffsetTop;
+ private int mNameTextViewTextSize;
+ private int mHeaderWidth;
+ private Drawable mActivatedBackgroundDrawable;
+
+ // Set in onLayout. Represent left and right position of the View on the screen.
+ private int mLeftOffset;
+ private int mRightOffset;
+
+ /**
+ * Used with {@link #mLabelView}, specifying the width ratio between label and data.
+ */
+ private int mLabelViewWidthWeight = 3;
+ /**
+ * Used with {@link #mDataView}, specifying the width ratio between label and data.
+ */
+ private int mDataViewWidthWeight = 5;
+
+ protected static class HighlightSequence {
+ private final int start;
+ private final int end;
+
+ HighlightSequence(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+ }
+
+ private ArrayList mNameHighlightSequence;
+ private ArrayList mNumberHighlightSequence;
+
+ // Highlighting prefix for names.
+ private String mHighlightedPrefix;
+
+ /**
+ * Where to put contact photo. This affects the other Views' layout or look-and-feel.
+ *
+ * TODO: replace enum with int constants
+ */
+ public enum PhotoPosition {
+ LEFT,
+ RIGHT
+ }
+
+ static public final PhotoPosition getDefaultPhotoPosition(boolean opposite) {
+ final Locale locale = Locale.getDefault();
+ final int layoutDirection = TextUtils.getLayoutDirectionFromLocale(locale);
+ switch (layoutDirection) {
+ case View.LAYOUT_DIRECTION_RTL:
+ return (opposite ? PhotoPosition.LEFT : PhotoPosition.RIGHT);
+ case View.LAYOUT_DIRECTION_LTR:
+ default:
+ return (opposite ? PhotoPosition.RIGHT : PhotoPosition.LEFT);
+ }
+ }
+
+ private PhotoPosition mPhotoPosition = getDefaultPhotoPosition(false /* normal/non opposite */);
+
+ // Header layout data
+ private TextView mHeaderTextView;
+ private boolean mIsSectionHeaderEnabled;
+
+ // The views inside the contact view
+ private boolean mQuickContactEnabled = true;
+ private QuickContactBadge mQuickContact;
+ private ImageView mPhotoView;
+ private TextView mNameTextView;
+ private TextView mPhoneticNameTextView;
+ private TextView mLabelView;
+ private TextView mDataView;
+ private TextView mSnippetView;
+ private TextView mStatusView;
+ private ImageView mPresenceIcon;
+ private CheckBox mCheckBox;
+
+ private ColorStateList mSecondaryTextColor;
+
+
+
+ private int mDefaultPhotoViewSize = 0;
+ /**
+ * Can be effective even when {@link #mPhotoView} is null, as we want to have horizontal padding
+ * to align other data in this View.
+ */
+ private int mPhotoViewWidth;
+ /**
+ * Can be effective even when {@link #mPhotoView} is null, as we want to have vertical padding.
+ */
+ private int mPhotoViewHeight;
+
+ /**
+ * Only effective when {@link #mPhotoView} is null.
+ * When true all the Views on the right side of the photo should have horizontal padding on
+ * those left assuming there is a photo.
+ */
+ private boolean mKeepHorizontalPaddingForPhotoView;
+ /**
+ * Only effective when {@link #mPhotoView} is null.
+ */
+ private boolean mKeepVerticalPaddingForPhotoView;
+
+ /**
+ * True when {@link #mPhotoViewWidth} and {@link #mPhotoViewHeight} are ready for being used.
+ * False indicates those values should be updated before being used in position calculation.
+ */
+ private boolean mPhotoViewWidthAndHeightAreReady = false;
+
+ private int mNameTextViewHeight;
+ private int mNameTextViewTextColor = Color.BLACK;
+ private int mPhoneticNameTextViewHeight;
+ private int mLabelViewHeight;
+ private int mDataViewHeight;
+ private int mSnippetTextViewHeight;
+ private int mStatusTextViewHeight;
+ private int mCheckBoxHeight;
+ private int mCheckBoxWidth;
+
+ // Holds Math.max(mLabelTextViewHeight, mDataViewHeight), assuming Label and Data share the
+ // same row.
+ private int mLabelAndDataViewMaxHeight;
+
+ // TODO: some TextView fields are using CharArrayBuffer while some are not. Determine which is
+ // more efficient for each case or in general, and simplify the whole implementation.
+ // Note: if we're sure MARQUEE will be used every time, there's no reason to use
+ // CharArrayBuffer, since MARQUEE requires Span and thus we need to copy characters inside the
+ // buffer to Spannable once, while CharArrayBuffer is for directly applying char array to
+ // TextView without any modification.
+ private final CharArrayBuffer mDataBuffer = new CharArrayBuffer(128);
+ private final CharArrayBuffer mPhoneticNameBuffer = new CharArrayBuffer(128);
+
+ private boolean mActivatedStateSupported;
+ private boolean mAdjustSelectionBoundsEnabled = true;
+
+ private Rect mBoundsWithoutHeader = new Rect();
+
+ /** A helper used to highlight a prefix in a text field. */
+ private final TextHighlighter mTextHighlighter;
+ private CharSequence mUnknownNameText;
+
+ public ContactListItemView(Context context) {
+ super(context);
+
+ mTextHighlighter = new TextHighlighter(Typeface.BOLD);
+ mNameHighlightSequence = new ArrayList();
+ mNumberHighlightSequence = new ArrayList();
+ }
+
+ public ContactListItemView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Read all style values
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+ mPreferredHeight = a.getDimensionPixelSize(
+ R.styleable.ContactListItemView_list_item_height, mPreferredHeight);
+ mActivatedBackgroundDrawable = a.getDrawable(
+ R.styleable.ContactListItemView_activated_background);
+
+ mGapBetweenImageAndText = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_gap_between_image_and_text,
+ mGapBetweenImageAndText);
+ mGapBetweenLabelAndData = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_gap_between_label_and_data,
+ mGapBetweenLabelAndData);
+ mPresenceIconMargin = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_presence_icon_margin,
+ mPresenceIconMargin);
+ mPresenceIconSize = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_presence_icon_size, mPresenceIconSize);
+ mDefaultPhotoViewSize = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_photo_size, mDefaultPhotoViewSize);
+ mTextIndent = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_text_indent, mTextIndent);
+ mTextOffsetTop = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_text_offset_top, mTextOffsetTop);
+ mDataViewWidthWeight = a.getInteger(
+ R.styleable.ContactListItemView_list_item_data_width_weight, mDataViewWidthWeight);
+ mLabelViewWidthWeight = a.getInteger(
+ R.styleable.ContactListItemView_list_item_label_width_weight,
+ mLabelViewWidthWeight);
+ mNameTextViewTextColor = a.getColor(
+ R.styleable.ContactListItemView_list_item_name_text_color, mNameTextViewTextColor);
+ mNameTextViewTextSize = (int) a.getDimension(
+ R.styleable.ContactListItemView_list_item_name_text_size,
+ (int) getResources().getDimension(R.dimen.contact_browser_list_item_text_size));
+
+ setPaddingRelative(
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_left, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_top, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_right, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_bottom, 0));
+
+ mTextHighlighter = new TextHighlighter(Typeface.BOLD);
+
+ a.recycle();
+
+ a = getContext().obtainStyledAttributes(R.styleable.Theme);
+ mSecondaryTextColor = a.getColorStateList(R.styleable.Theme_android_textColorSecondary);
+ a.recycle();
+
+ mHeaderWidth =
+ getResources().getDimensionPixelSize(R.dimen.contact_list_section_header_width);
+
+ if (mActivatedBackgroundDrawable != null) {
+ mActivatedBackgroundDrawable.setCallback(this);
+ }
+
+ mNameHighlightSequence = new ArrayList();
+ mNumberHighlightSequence = new ArrayList();
+
+ setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
+ }
+
+ public void setUnknownNameText(CharSequence unknownNameText) {
+ mUnknownNameText = unknownNameText;
+ }
+
+ public void setQuickContactEnabled(boolean flag) {
+ mQuickContactEnabled = flag;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // We will match parent's width and wrap content vertically, but make sure
+ // height is no less than listPreferredItemHeight.
+ final int specWidth = resolveSize(0, widthMeasureSpec);
+ final int preferredHeight = mPreferredHeight;
+
+ mNameTextViewHeight = 0;
+ mPhoneticNameTextViewHeight = 0;
+ mLabelViewHeight = 0;
+ mDataViewHeight = 0;
+ mLabelAndDataViewMaxHeight = 0;
+ mSnippetTextViewHeight = 0;
+ mStatusTextViewHeight = 0;
+ mCheckBoxWidth = 0;
+ mCheckBoxHeight = 0;
+
+ ensurePhotoViewSize();
+
+ // Width each TextView is able to use.
+ int effectiveWidth;
+ // All the other Views will honor the photo, so available width for them may be shrunk.
+ if (mPhotoViewWidth > 0 || mKeepHorizontalPaddingForPhotoView) {
+ effectiveWidth = specWidth - getPaddingLeft() - getPaddingRight()
+ - (mPhotoViewWidth + mGapBetweenImageAndText);
+ } else {
+ effectiveWidth = specWidth - getPaddingLeft() - getPaddingRight();
+ }
+
+ if (mIsSectionHeaderEnabled) {
+ effectiveWidth -= mHeaderWidth + mGapBetweenImageAndText;
+ }
+
+ // Go over all visible text views and measure actual width of each of them.
+ // Also calculate their heights to get the total height for this entire view.
+
+ if (isVisible(mCheckBox)) {
+ mCheckBox.measure(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mCheckBoxWidth = mCheckBox.getMeasuredWidth();
+ mCheckBoxHeight = mCheckBox.getMeasuredHeight();
+ effectiveWidth -= mCheckBoxWidth + mGapBetweenImageAndText;
+ }
+
+ if (isVisible(mNameTextView)) {
+ // Calculate width for name text - this parallels similar measurement in onLayout.
+ int nameTextWidth = effectiveWidth;
+ if (mPhotoPosition != PhotoPosition.LEFT) {
+ nameTextWidth -= mTextIndent;
+ }
+ mNameTextView.measure(
+ MeasureSpec.makeMeasureSpec(nameTextWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mNameTextViewHeight = mNameTextView.getMeasuredHeight();
+ }
+
+ if (isVisible(mPhoneticNameTextView)) {
+ mPhoneticNameTextView.measure(
+ MeasureSpec.makeMeasureSpec(effectiveWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mPhoneticNameTextViewHeight = mPhoneticNameTextView.getMeasuredHeight();
+ }
+
+ // If both data (phone number/email address) and label (type like "MOBILE") are quite long,
+ // we should ellipsize both using appropriate ratio.
+ final int dataWidth;
+ final int labelWidth;
+ if (isVisible(mDataView)) {
+ if (isVisible(mLabelView)) {
+ final int totalWidth = effectiveWidth - mGapBetweenLabelAndData;
+ dataWidth = ((totalWidth * mDataViewWidthWeight)
+ / (mDataViewWidthWeight + mLabelViewWidthWeight));
+ labelWidth = ((totalWidth * mLabelViewWidthWeight) /
+ (mDataViewWidthWeight + mLabelViewWidthWeight));
+ } else {
+ dataWidth = effectiveWidth;
+ labelWidth = 0;
+ }
+ } else {
+ dataWidth = 0;
+ if (isVisible(mLabelView)) {
+ labelWidth = effectiveWidth;
+ } else {
+ labelWidth = 0;
+ }
+ }
+
+ if (isVisible(mDataView)) {
+ mDataView.measure(MeasureSpec.makeMeasureSpec(dataWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mDataViewHeight = mDataView.getMeasuredHeight();
+ }
+
+ if (isVisible(mLabelView)) {
+ // For performance reason we don't want AT_MOST usually, but when the picture is
+ // on right, we need to use it anyway because mDataView is next to mLabelView.
+ final int mode = (mPhotoPosition == PhotoPosition.LEFT
+ ? MeasureSpec.EXACTLY : MeasureSpec.AT_MOST);
+ mLabelView.measure(MeasureSpec.makeMeasureSpec(labelWidth, mode),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mLabelViewHeight = mLabelView.getMeasuredHeight();
+ }
+ mLabelAndDataViewMaxHeight = Math.max(mLabelViewHeight, mDataViewHeight);
+
+ if (isVisible(mSnippetView)) {
+ mSnippetView.measure(
+ MeasureSpec.makeMeasureSpec(effectiveWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mSnippetTextViewHeight = mSnippetView.getMeasuredHeight();
+ }
+
+ // Status view height is the biggest of the text view and the presence icon
+ if (isVisible(mPresenceIcon)) {
+ mPresenceIcon.measure(
+ MeasureSpec.makeMeasureSpec(mPresenceIconSize, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mPresenceIconSize, MeasureSpec.EXACTLY));
+ mStatusTextViewHeight = mPresenceIcon.getMeasuredHeight();
+ }
+
+ if (isVisible(mStatusView)) {
+ // Presence and status are in a same row, so status will be affected by icon size.
+ final int statusWidth;
+ if (isVisible(mPresenceIcon)) {
+ statusWidth = (effectiveWidth - mPresenceIcon.getMeasuredWidth()
+ - mPresenceIconMargin);
+ } else {
+ statusWidth = effectiveWidth;
+ }
+ mStatusView.measure(MeasureSpec.makeMeasureSpec(statusWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mStatusTextViewHeight =
+ Math.max(mStatusTextViewHeight, mStatusView.getMeasuredHeight());
+ }
+
+ // Calculate height including padding.
+ int height = (mNameTextViewHeight + mPhoneticNameTextViewHeight +
+ mLabelAndDataViewMaxHeight +
+ mSnippetTextViewHeight + mStatusTextViewHeight);
+
+ // Make sure the height is at least as high as the photo
+ height = Math.max(height, mPhotoViewHeight + getPaddingBottom() + getPaddingTop());
+
+ // Make sure height is at least the preferred height
+ height = Math.max(height, preferredHeight);
+
+ // Measure the header if it is visible.
+ if (mHeaderTextView != null && mHeaderTextView.getVisibility() == VISIBLE) {
+ mHeaderTextView.measure(
+ MeasureSpec.makeMeasureSpec(mHeaderWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ }
+
+ setMeasuredDimension(specWidth, height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int height = bottom - top;
+ final int width = right - left;
+
+ // Determine the vertical bounds by laying out the header first.
+ int topBound = 0;
+ int bottomBound = height;
+ int leftBound = getPaddingLeft();
+ int rightBound = width - getPaddingRight();
+
+ final boolean isLayoutRtl = ViewUtil.isViewLayoutRtl(this);
+
+ // Put the section header on the left side of the contact view.
+ if (mIsSectionHeaderEnabled) {
+ if (mHeaderTextView != null) {
+ int headerHeight = mHeaderTextView.getMeasuredHeight();
+ int headerTopBound = (bottomBound + topBound - headerHeight) / 2 + mTextOffsetTop;
+
+ mHeaderTextView.layout(
+ isLayoutRtl ? rightBound - mHeaderWidth : leftBound,
+ headerTopBound,
+ isLayoutRtl ? rightBound : leftBound + mHeaderWidth,
+ headerTopBound + headerHeight);
+ }
+ if (isLayoutRtl) {
+ rightBound -= mHeaderWidth;
+ } else {
+ leftBound += mHeaderWidth;
+ }
+ }
+
+ mBoundsWithoutHeader.set(left + leftBound, topBound, left + rightBound, bottomBound);
+ mLeftOffset = left + leftBound;
+ mRightOffset = left + rightBound;
+ if (mIsSectionHeaderEnabled) {
+ if (isLayoutRtl) {
+ rightBound -= mGapBetweenImageAndText;
+ } else {
+ leftBound += mGapBetweenImageAndText;
+ }
+ }
+
+ if (mActivatedStateSupported && isActivated()) {
+ mActivatedBackgroundDrawable.setBounds(mBoundsWithoutHeader);
+ }
+
+ if (isVisible(mCheckBox)) {
+ final int photoTop = topBound + (bottomBound - topBound - mCheckBoxHeight) / 2;
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ mCheckBox.layout(rightBound - mCheckBoxWidth,
+ photoTop,
+ rightBound,
+ photoTop + mCheckBoxHeight);
+ } else {
+ mCheckBox.layout(leftBound,
+ photoTop,
+ leftBound + mCheckBoxWidth,
+ photoTop + mCheckBoxHeight);
+ }
+ }
+
+ final View photoView = mQuickContact != null ? mQuickContact : mPhotoView;
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ // Photo is the left most view. All the other Views should on the right of the photo.
+ if (photoView != null) {
+ // Center the photo vertically
+ final int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
+ photoView.layout(
+ leftBound,
+ photoTop,
+ leftBound + mPhotoViewWidth,
+ photoTop + mPhotoViewHeight);
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ } else if (mKeepHorizontalPaddingForPhotoView) {
+ // Draw nothing but keep the padding.
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ }
+ } else {
+ // Photo is the right most view. Right bound should be adjusted that way.
+ if (photoView != null) {
+ // Center the photo vertically
+ final int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
+ photoView.layout(
+ rightBound - mPhotoViewWidth,
+ photoTop,
+ rightBound,
+ photoTop + mPhotoViewHeight);
+ rightBound -= (mPhotoViewWidth + mGapBetweenImageAndText);
+ } else if (mKeepHorizontalPaddingForPhotoView) {
+ // Draw nothing but keep the padding.
+ rightBound -= (mPhotoViewWidth + mGapBetweenImageAndText);
+ }
+
+ // Add indent between left-most padding and texts.
+ leftBound += mTextIndent;
+ }
+
+ // Center text vertically, then apply the top offset.
+ final int totalTextHeight = mNameTextViewHeight + mPhoneticNameTextViewHeight +
+ mLabelAndDataViewMaxHeight + mSnippetTextViewHeight + mStatusTextViewHeight;
+ int textTopBound = (bottomBound + topBound - totalTextHeight) / 2 + mTextOffsetTop;
+
+ // Layout all text view and presence icon
+ // Put name TextView first
+ if (isVisible(mNameTextView)) {
+ final int distanceFromEnd = mCheckBoxWidth > 0
+ ? mCheckBoxWidth + mGapBetweenImageAndText : 0;
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ mNameTextView.layout(leftBound,
+ textTopBound,
+ rightBound - distanceFromEnd,
+ textTopBound + mNameTextViewHeight);
+ } else {
+ mNameTextView.layout(leftBound + distanceFromEnd,
+ textTopBound,
+ rightBound,
+ textTopBound + mNameTextViewHeight);
+ }
+ textTopBound += mNameTextViewHeight;
+ }
+
+ // Presence and status
+ if (isLayoutRtl) {
+ int statusRightBound = rightBound;
+ if (isVisible(mPresenceIcon)) {
+ int iconWidth = mPresenceIcon.getMeasuredWidth();
+ mPresenceIcon.layout(
+ rightBound - iconWidth,
+ textTopBound,
+ rightBound,
+ textTopBound + mStatusTextViewHeight);
+ statusRightBound -= (iconWidth + mPresenceIconMargin);
+ }
+
+ if (isVisible(mStatusView)) {
+ mStatusView.layout(leftBound,
+ textTopBound,
+ statusRightBound,
+ textTopBound + mStatusTextViewHeight);
+ }
+ } else {
+ int statusLeftBound = leftBound;
+ if (isVisible(mPresenceIcon)) {
+ int iconWidth = mPresenceIcon.getMeasuredWidth();
+ mPresenceIcon.layout(
+ leftBound,
+ textTopBound,
+ leftBound + iconWidth,
+ textTopBound + mStatusTextViewHeight);
+ statusLeftBound += (iconWidth + mPresenceIconMargin);
+ }
+
+ if (isVisible(mStatusView)) {
+ mStatusView.layout(statusLeftBound,
+ textTopBound,
+ rightBound,
+ textTopBound + mStatusTextViewHeight);
+ }
+ }
+
+ if (isVisible(mStatusView) || isVisible(mPresenceIcon)) {
+ textTopBound += mStatusTextViewHeight;
+ }
+
+ // Rest of text views
+ int dataLeftBound = leftBound;
+ if (isVisible(mPhoneticNameTextView)) {
+ mPhoneticNameTextView.layout(leftBound,
+ textTopBound,
+ rightBound,
+ textTopBound + mPhoneticNameTextViewHeight);
+ textTopBound += mPhoneticNameTextViewHeight;
+ }
+
+ // Label and Data align bottom.
+ if (isVisible(mLabelView)) {
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ // When photo is on left, label is placed on the right edge of the list item.
+ mLabelView.layout(rightBound - mLabelView.getMeasuredWidth(),
+ textTopBound + mLabelAndDataViewMaxHeight - mLabelViewHeight,
+ rightBound,
+ textTopBound + mLabelAndDataViewMaxHeight);
+ rightBound -= mLabelView.getMeasuredWidth();
+ } else {
+ // When photo is on right, label is placed on the left of data view.
+ dataLeftBound = leftBound + mLabelView.getMeasuredWidth();
+ mLabelView.layout(leftBound,
+ textTopBound + mLabelAndDataViewMaxHeight - mLabelViewHeight,
+ dataLeftBound,
+ textTopBound + mLabelAndDataViewMaxHeight);
+ dataLeftBound += mGapBetweenLabelAndData;
+ }
+ }
+
+ if (isVisible(mDataView)) {
+ mDataView.layout(dataLeftBound,
+ textTopBound + mLabelAndDataViewMaxHeight - mDataViewHeight,
+ rightBound,
+ textTopBound + mLabelAndDataViewMaxHeight);
+ }
+ if (isVisible(mLabelView) || isVisible(mDataView)) {
+ textTopBound += mLabelAndDataViewMaxHeight;
+ }
+
+ if (isVisible(mSnippetView)) {
+ mSnippetView.layout(leftBound,
+ textTopBound,
+ rightBound,
+ textTopBound + mSnippetTextViewHeight);
+ }
+ }
+
+ @Override
+ public void adjustListItemSelectionBounds(Rect bounds) {
+ if (mAdjustSelectionBoundsEnabled) {
+ bounds.top += mBoundsWithoutHeader.top;
+ bounds.bottom = bounds.top + mBoundsWithoutHeader.height();
+ bounds.left = mBoundsWithoutHeader.left;
+ bounds.right = mBoundsWithoutHeader.right;
+ }
+ }
+
+ protected boolean isVisible(View view) {
+ return view != null && view.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Extracts width and height from the style
+ */
+ private void ensurePhotoViewSize() {
+ if (!mPhotoViewWidthAndHeightAreReady) {
+ mPhotoViewWidth = mPhotoViewHeight = getDefaultPhotoViewSize();
+ if (!mQuickContactEnabled && mPhotoView == null) {
+ if (!mKeepHorizontalPaddingForPhotoView) {
+ mPhotoViewWidth = 0;
+ }
+ if (!mKeepVerticalPaddingForPhotoView) {
+ mPhotoViewHeight = 0;
+ }
+ }
+
+ mPhotoViewWidthAndHeightAreReady = true;
+ }
+ }
+
+ protected int getDefaultPhotoViewSize() {
+ return mDefaultPhotoViewSize;
+ }
+
+ /**
+ * Gets a LayoutParam that corresponds to the default photo size.
+ *
+ * @return A new LayoutParam.
+ */
+ private LayoutParams getDefaultPhotoLayoutParams() {
+ LayoutParams params = generateDefaultLayoutParams();
+ params.width = getDefaultPhotoViewSize();
+ params.height = params.width;
+ return params;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.setState(getDrawableState());
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mActivatedBackgroundDrawable || super.verifyDrawable(who);
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ public void dispatchDraw(Canvas canvas) {
+ if (mActivatedStateSupported && isActivated()) {
+ mActivatedBackgroundDrawable.draw(canvas);
+ }
+
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * Sets section header or makes it invisible if the title is null.
+ */
+ public void setSectionHeader(String title) {
+ if (!TextUtils.isEmpty(title)) {
+ if (mHeaderTextView == null) {
+ mHeaderTextView = new TextView(getContext());
+ mHeaderTextView.setTextAppearance(getContext(), R.style.SectionHeaderStyle);
+ mHeaderTextView.setGravity(
+ ViewUtil.isViewLayoutRtl(this) ? Gravity.RIGHT : Gravity.LEFT);
+ addView(mHeaderTextView);
+ }
+ setMarqueeText(mHeaderTextView, title);
+ mHeaderTextView.setVisibility(View.VISIBLE);
+ mHeaderTextView.setAllCaps(true);
+ } else if (mHeaderTextView != null) {
+ mHeaderTextView.setVisibility(View.GONE);
+ }
+ }
+
+ public void setIsSectionHeaderEnabled(boolean isSectionHeaderEnabled) {
+ mIsSectionHeaderEnabled = isSectionHeaderEnabled;
+ }
+
+ /**
+ * Returns the quick contact badge, creating it if necessary.
+ */
+ public QuickContactBadge getQuickContact() {
+ if (!mQuickContactEnabled) {
+ throw new IllegalStateException("QuickContact is disabled for this view");
+ }
+ if (mQuickContact == null) {
+ mQuickContact = new QuickContactBadge(getContext());
+ mQuickContact.setOverlay(null);
+ mQuickContact.setLayoutParams(getDefaultPhotoLayoutParams());
+ if (mNameTextView != null) {
+ mQuickContact.setContentDescription(getContext().getString(
+ R.string.description_quick_contact_for, mNameTextView.getText()));
+ }
+
+ addView(mQuickContact);
+ mPhotoViewWidthAndHeightAreReady = false;
+ }
+ return mQuickContact;
+ }
+
+ /**
+ * Returns the photo view, creating it if necessary.
+ */
+ public ImageView getPhotoView() {
+ if (mPhotoView == null) {
+ mPhotoView = new ImageView(getContext());
+ mPhotoView.setLayoutParams(getDefaultPhotoLayoutParams());
+ // Quick contact style used above will set a background - remove it
+ mPhotoView.setBackground(null);
+ addView(mPhotoView);
+ mPhotoViewWidthAndHeightAreReady = false;
+ }
+ return mPhotoView;
+ }
+
+ /**
+ * Removes the photo view.
+ */
+ public void removePhotoView() {
+ removePhotoView(false, true);
+ }
+
+ /**
+ * Removes the photo view.
+ *
+ * @param keepHorizontalPadding True means data on the right side will have
+ * padding on left, pretending there is still a photo view.
+ * @param keepVerticalPadding True means the View will have some height
+ * enough for accommodating a photo view.
+ */
+ public void removePhotoView(boolean keepHorizontalPadding, boolean keepVerticalPadding) {
+ mPhotoViewWidthAndHeightAreReady = false;
+ mKeepHorizontalPaddingForPhotoView = keepHorizontalPadding;
+ mKeepVerticalPaddingForPhotoView = keepVerticalPadding;
+ if (mPhotoView != null) {
+ removeView(mPhotoView);
+ mPhotoView = null;
+ }
+ if (mQuickContact != null) {
+ removeView(mQuickContact);
+ mQuickContact = null;
+ }
+ }
+
+ /**
+ * Sets a word prefix that will be highlighted if encountered in fields like
+ * name and search snippet. This will disable the mask highlighting for names.
+ *
+ * NOTE: must be all upper-case
+ */
+ public void setHighlightedPrefix(String upperCasePrefix) {
+ mHighlightedPrefix = upperCasePrefix;
+ }
+
+ /**
+ * Clears previously set highlight sequences for the view.
+ */
+ public void clearHighlightSequences() {
+ mNameHighlightSequence.clear();
+ mNumberHighlightSequence.clear();
+ mHighlightedPrefix = null;
+ }
+
+ /**
+ * Adds a highlight sequence to the name highlighter.
+ * @param start The start position of the highlight sequence.
+ * @param end The end position of the highlight sequence.
+ */
+ public void addNameHighlightSequence(int start, int end) {
+ mNameHighlightSequence.add(new HighlightSequence(start, end));
+ }
+
+ /**
+ * Adds a highlight sequence to the number highlighter.
+ * @param start The start position of the highlight sequence.
+ * @param end The end position of the highlight sequence.
+ */
+ public void addNumberHighlightSequence(int start, int end) {
+ mNumberHighlightSequence.add(new HighlightSequence(start, end));
+ }
+
+ /**
+ * Returns the text view for the contact name, creating it if necessary.
+ */
+ public TextView getNameTextView() {
+ if (mNameTextView == null) {
+ mNameTextView = new TextView(getContext());
+ mNameTextView.setSingleLine(true);
+ mNameTextView.setEllipsize(getTextEllipsis());
+ mNameTextView.setTextColor(mNameTextViewTextColor);
+ mNameTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ mNameTextViewTextSize);
+ // Manually call setActivated() since this view may be added after the first
+ // setActivated() call toward this whole item view.
+ mNameTextView.setActivated(isActivated());
+ mNameTextView.setGravity(Gravity.CENTER_VERTICAL);
+ mNameTextView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ mNameTextView.setId(R.id.cliv_name_textview);
+ mNameTextView.setElegantTextHeight(false);
+ addView(mNameTextView);
+ }
+ return mNameTextView;
+ }
+
+ /**
+ * Adds or updates a text view for the phonetic name.
+ */
+ public void setPhoneticName(char[] text, int size) {
+ if (text == null || size == 0) {
+ if (mPhoneticNameTextView != null) {
+ mPhoneticNameTextView.setVisibility(View.GONE);
+ }
+ } else {
+ getPhoneticNameTextView();
+ setMarqueeText(mPhoneticNameTextView, text, size);
+ mPhoneticNameTextView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Returns the text view for the phonetic name, creating it if necessary.
+ */
+ public TextView getPhoneticNameTextView() {
+ if (mPhoneticNameTextView == null) {
+ mPhoneticNameTextView = new TextView(getContext());
+ mPhoneticNameTextView.setSingleLine(true);
+ mPhoneticNameTextView.setEllipsize(getTextEllipsis());
+ mPhoneticNameTextView.setTextAppearance(getContext(), android.R.style.TextAppearance_Small);
+ mPhoneticNameTextView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ mPhoneticNameTextView.setTypeface(mPhoneticNameTextView.getTypeface(), Typeface.BOLD);
+ mPhoneticNameTextView.setActivated(isActivated());
+ mPhoneticNameTextView.setId(R.id.cliv_phoneticname_textview);
+ addView(mPhoneticNameTextView);
+ }
+ return mPhoneticNameTextView;
+ }
+
+ /**
+ * Adds or updates a text view for the data label.
+ */
+ public void setLabel(CharSequence text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mLabelView != null) {
+ mLabelView.setVisibility(View.GONE);
+ }
+ } else {
+ getLabelView();
+ setMarqueeText(mLabelView, text);
+ mLabelView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Returns the text view for the data label, creating it if necessary.
+ */
+ public TextView getLabelView() {
+ if (mLabelView == null) {
+ mLabelView = new TextView(getContext());
+ mLabelView.setSingleLine(true);
+ mLabelView.setEllipsize(getTextEllipsis());
+ mLabelView.setTextAppearance(getContext(), R.style.TextAppearanceSmall);
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ mLabelView.setAllCaps(true);
+ mLabelView.setGravity(Gravity.END);
+ } else {
+ mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
+ }
+ mLabelView.setActivated(isActivated());
+ mLabelView.setId(R.id.cliv_label_textview);
+ addView(mLabelView);
+ }
+ return mLabelView;
+ }
+
+ /**
+ * Adds or updates a text view for the data element.
+ */
+ public void setData(char[] text, int size) {
+ if (text == null || size == 0) {
+ if (mDataView != null) {
+ mDataView.setVisibility(View.GONE);
+ }
+ } else {
+ getDataView();
+ setMarqueeText(mDataView, text, size);
+ mDataView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Sets phone number for a list item. This takes care of number highlighting if the highlight
+ * mask exists.
+ */
+ public void setPhoneNumber(String text, String countryIso) {
+ if (text == null) {
+ if (mDataView != null) {
+ mDataView.setVisibility(View.GONE);
+ }
+ } else {
+ getDataView();
+
+ // TODO: Format number using PhoneNumberUtils.formatNumber before assigning it to
+ // mDataView. Make sure that determination of the highlight sequences are done only
+ // after number formatting.
+
+ // Sets phone number texts for display after highlighting it, if applicable.
+ // CharSequence textToSet = text;
+ final SpannableString textToSet = new SpannableString(text);
+
+ if (mNumberHighlightSequence.size() != 0) {
+ final HighlightSequence highlightSequence = mNumberHighlightSequence.get(0);
+ mTextHighlighter.applyMaskingHighlight(textToSet, highlightSequence.start,
+ highlightSequence.end);
+ }
+
+ setMarqueeText(mDataView, textToSet);
+ mDataView.setVisibility(VISIBLE);
+
+ // We have a phone number as "mDataView" so make it always LTR and VIEW_START
+ mDataView.setTextDirection(View.TEXT_DIRECTION_LTR);
+ mDataView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ }
+ }
+
+ private void setMarqueeText(TextView textView, char[] text, int size) {
+ if (getTextEllipsis() == TruncateAt.MARQUEE) {
+ setMarqueeText(textView, new String(text, 0, size));
+ } else {
+ textView.setText(text, 0, size);
+ }
+ }
+
+ private void setMarqueeText(TextView textView, CharSequence text) {
+ if (getTextEllipsis() == TruncateAt.MARQUEE) {
+ // To show MARQUEE correctly (with END effect during non-active state), we need
+ // to build Spanned with MARQUEE in addition to TextView's ellipsize setting.
+ final SpannableString spannable = new SpannableString(text);
+ spannable.setSpan(TruncateAt.MARQUEE, 0, spannable.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ textView.setText(spannable);
+ } else {
+ textView.setText(text);
+ }
+ }
+
+ /**
+ * Returns the {@link CheckBox} view, creating it if necessary.
+ */
+ public CheckBox getCheckBox() {
+ if (mCheckBox == null) {
+ mCheckBox = new CheckBox(getContext());
+ // Make non-focusable, so the rest of the ContactListItemView can be clicked.
+ mCheckBox.setFocusable(false);
+ addView(mCheckBox);
+ }
+ return mCheckBox;
+ }
+
+ /**
+ * Returns the text view for the data text, creating it if necessary.
+ */
+ public TextView getDataView() {
+ if (mDataView == null) {
+ mDataView = new TextView(getContext());
+ mDataView.setSingleLine(true);
+ mDataView.setEllipsize(getTextEllipsis());
+ mDataView.setTextAppearance(getContext(), R.style.TextAppearanceSmall);
+ mDataView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ mDataView.setActivated(isActivated());
+ mDataView.setId(R.id.cliv_data_view);
+ mDataView.setElegantTextHeight(false);
+ addView(mDataView);
+ }
+ return mDataView;
+ }
+
+ /**
+ * Adds or updates a text view for the search snippet.
+ */
+ public void setSnippet(String text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mSnippetView != null) {
+ mSnippetView.setVisibility(View.GONE);
+ }
+ } else {
+ mTextHighlighter.setPrefixText(getSnippetView(), text, mHighlightedPrefix);
+ mSnippetView.setVisibility(VISIBLE);
+ if (ContactDisplayUtils.isPossiblePhoneNumber(text)) {
+ // Give the text-to-speech engine a hint that it's a phone number
+ mSnippetView.setContentDescription(PhoneNumberUtils.createTtsSpannable(text));
+ } else {
+ mSnippetView.setContentDescription(null);
+ }
+ }
+ }
+
+ /**
+ * Returns the text view for the search snippet, creating it if necessary.
+ */
+ public TextView getSnippetView() {
+ if (mSnippetView == null) {
+ mSnippetView = new TextView(getContext());
+ mSnippetView.setSingleLine(true);
+ mSnippetView.setEllipsize(getTextEllipsis());
+ mSnippetView.setTextAppearance(getContext(), android.R.style.TextAppearance_Small);
+ mSnippetView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ mSnippetView.setActivated(isActivated());
+ addView(mSnippetView);
+ }
+ return mSnippetView;
+ }
+
+ /**
+ * Returns the text view for the status, creating it if necessary.
+ */
+ public TextView getStatusView() {
+ if (mStatusView == null) {
+ mStatusView = new TextView(getContext());
+ mStatusView.setSingleLine(true);
+ mStatusView.setEllipsize(getTextEllipsis());
+ mStatusView.setTextAppearance(getContext(), android.R.style.TextAppearance_Small);
+ mStatusView.setTextColor(mSecondaryTextColor);
+ mStatusView.setActivated(isActivated());
+ mStatusView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ addView(mStatusView);
+ }
+ return mStatusView;
+ }
+
+ /**
+ * Adds or updates a text view for the status.
+ */
+ public void setStatus(CharSequence text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mStatusView != null) {
+ mStatusView.setVisibility(View.GONE);
+ }
+ } else {
+ getStatusView();
+ setMarqueeText(mStatusView, text);
+ mStatusView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Adds or updates the presence icon view.
+ */
+ public void setPresence(Drawable icon) {
+ if (icon != null) {
+ if (mPresenceIcon == null) {
+ mPresenceIcon = new ImageView(getContext());
+ addView(mPresenceIcon);
+ }
+ mPresenceIcon.setImageDrawable(icon);
+ mPresenceIcon.setScaleType(ScaleType.CENTER);
+ mPresenceIcon.setVisibility(View.VISIBLE);
+ } else {
+ if (mPresenceIcon != null) {
+ mPresenceIcon.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private TruncateAt getTextEllipsis() {
+ return TruncateAt.MARQUEE;
+ }
+
+ public void showDisplayName(Cursor cursor, int nameColumnIndex, int displayOrder) {
+ CharSequence name = cursor.getString(nameColumnIndex);
+ setDisplayName(name);
+
+ // Since the quick contact content description is derived from the display name and there is
+ // no guarantee that when the quick contact is initialized the display name is already set,
+ // do it here too.
+ if (mQuickContact != null) {
+ mQuickContact.setContentDescription(getContext().getString(
+ R.string.description_quick_contact_for, mNameTextView.getText()));
+ }
+ }
+
+ public void setDisplayName(CharSequence name, boolean highlight) {
+ if (!TextUtils.isEmpty(name) && highlight) {
+ clearHighlightSequences();
+ addNameHighlightSequence(0, name.length());
+ }
+ setDisplayName(name);
+ }
+
+ public void setDisplayName(CharSequence name) {
+ if (!TextUtils.isEmpty(name)) {
+ // Chooses the available highlighting method for highlighting.
+ if (mHighlightedPrefix != null) {
+ name = mTextHighlighter.applyPrefixHighlight(name, mHighlightedPrefix);
+ } else if (mNameHighlightSequence.size() != 0) {
+ final SpannableString spannableName = new SpannableString(name);
+ for (HighlightSequence highlightSequence : mNameHighlightSequence) {
+ mTextHighlighter.applyMaskingHighlight(spannableName, highlightSequence.start,
+ highlightSequence.end);
+ }
+ name = spannableName;
+ }
+ } else {
+ name = mUnknownNameText;
+ }
+ setMarqueeText(getNameTextView(), name);
+
+ if (ContactDisplayUtils.isPossiblePhoneNumber(name)) {
+ // Give the text-to-speech engine a hint that it's a phone number
+ mNameTextView.setContentDescription(
+ PhoneNumberUtils.createTtsSpannable(name.toString()));
+ } else {
+ mNameTextView.setContentDescription(null);
+ }
+ }
+
+ public void hideCheckBox() {
+ if (mCheckBox != null) {
+ removeView(mCheckBox);
+ mCheckBox = null;
+ }
+ }
+
+ public void hideDisplayName() {
+ if (mNameTextView != null) {
+ removeView(mNameTextView);
+ mNameTextView = null;
+ }
+ }
+
+ public void showPhoneticName(Cursor cursor, int phoneticNameColumnIndex) {
+ cursor.copyStringToBuffer(phoneticNameColumnIndex, mPhoneticNameBuffer);
+ int phoneticNameSize = mPhoneticNameBuffer.sizeCopied;
+ if (phoneticNameSize != 0) {
+ setPhoneticName(mPhoneticNameBuffer.data, phoneticNameSize);
+ } else {
+ setPhoneticName(null, 0);
+ }
+ }
+
+ public void hidePhoneticName() {
+ if (mPhoneticNameTextView != null) {
+ removeView(mPhoneticNameTextView);
+ mPhoneticNameTextView = null;
+ }
+ }
+
+ /**
+ * Sets the proper icon (star or presence or nothing) and/or status message.
+ */
+ public void showPresenceAndStatusMessage(Cursor cursor, int presenceColumnIndex,
+ int contactStatusColumnIndex) {
+ Drawable icon = null;
+ int presence = 0;
+ if (!cursor.isNull(presenceColumnIndex)) {
+ presence = cursor.getInt(presenceColumnIndex);
+ icon = ContactPresenceIconUtil.getPresenceIcon(getContext(), presence);
+ }
+ setPresence(icon);
+
+ String statusMessage = null;
+ if (contactStatusColumnIndex != 0 && !cursor.isNull(contactStatusColumnIndex)) {
+ statusMessage = cursor.getString(contactStatusColumnIndex);
+ }
+ // If there is no status message from the contact, but there was a presence value, then use
+ // the default status message string
+ if (statusMessage == null && presence != 0) {
+ statusMessage = ContactStatusUtil.getStatusString(getContext(), presence);
+ }
+ setStatus(statusMessage);
+ }
+
+ /**
+ * Shows search snippet.
+ */
+ public void showSnippet(Cursor cursor, int summarySnippetColumnIndex) {
+ if (cursor.getColumnCount() <= summarySnippetColumnIndex) {
+ setSnippet(null);
+ return;
+ }
+
+ String snippet = cursor.getString(summarySnippetColumnIndex);
+
+ // Do client side snippeting if provider didn't do it
+ final Bundle extras = cursor.getExtras();
+ if (extras.getBoolean(ContactsContract.DEFERRED_SNIPPETING)) {
+
+ final String query = extras.getString(ContactsContract.DEFERRED_SNIPPETING_QUERY);
+
+ String displayName = null;
+ int displayNameIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
+ if (displayNameIndex >= 0) {
+ displayName = cursor.getString(displayNameIndex);
+ }
+
+ snippet = updateSnippet(snippet, query, displayName);
+
+ } else {
+ if (snippet != null) {
+ int from = 0;
+ int to = snippet.length();
+ int start = snippet.indexOf(DefaultContactListAdapter.SNIPPET_START_MATCH);
+ if (start == -1) {
+ snippet = null;
+ } else {
+ int firstNl = snippet.lastIndexOf('\n', start);
+ if (firstNl != -1) {
+ from = firstNl + 1;
+ }
+ int end = snippet.lastIndexOf(DefaultContactListAdapter.SNIPPET_END_MATCH);
+ if (end != -1) {
+ int lastNl = snippet.indexOf('\n', end);
+ if (lastNl != -1) {
+ to = lastNl;
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = from; i < to; i++) {
+ char c = snippet.charAt(i);
+ if (c != DefaultContactListAdapter.SNIPPET_START_MATCH &&
+ c != DefaultContactListAdapter.SNIPPET_END_MATCH) {
+ sb.append(c);
+ }
+ }
+ snippet = sb.toString();
+ }
+ }
+ }
+
+ setSnippet(snippet);
+ }
+
+ /**
+ * Used for deferred snippets from the database. The contents come back as large strings which
+ * need to be extracted for display.
+ *
+ * @param snippet The snippet from the database.
+ * @param query The search query substring.
+ * @param displayName The contact display name.
+ * @return The proper snippet to display.
+ */
+ private String updateSnippet(String snippet, String query, String displayName) {
+
+ if (TextUtils.isEmpty(snippet) || TextUtils.isEmpty(query)) {
+ return null;
+ }
+ query = SearchUtil.cleanStartAndEndOfSearchQuery(query.toLowerCase());
+
+ // If the display name already contains the query term, return empty - snippets should
+ // not be needed in that case.
+ if (!TextUtils.isEmpty(displayName)) {
+ final String lowerDisplayName = displayName.toLowerCase();
+ final List nameTokens = split(lowerDisplayName);
+ for (String nameToken : nameTokens) {
+ if (nameToken.startsWith(query)) {
+ return null;
+ }
+ }
+ }
+
+ // The snippet may contain multiple data lines.
+ // Show the first line that matches the query.
+ final SearchUtil.MatchedLine matched = SearchUtil.findMatchingLine(snippet, query);
+
+ if (matched != null && matched.line != null) {
+ // Tokenize for long strings since the match may be at the end of it.
+ // Skip this part for short strings since the whole string will be displayed.
+ // Most contact strings are short so the snippetize method will be called infrequently.
+ final int lengthThreshold = getResources().getInteger(
+ R.integer.snippet_length_before_tokenize);
+ if (matched.line.length() > lengthThreshold) {
+ return snippetize(matched.line, matched.startIndex, lengthThreshold);
+ } else {
+ return matched.line;
+ }
+ }
+
+ // No match found.
+ return null;
+ }
+
+ private String snippetize(String line, int matchIndex, int maxLength) {
+ // Show up to maxLength characters. But we only show full tokens so show the last full token
+ // up to maxLength characters. So as many starting tokens as possible before trying ending
+ // tokens.
+ int remainingLength = maxLength;
+ int tempRemainingLength = remainingLength;
+
+ // Start the end token after the matched query.
+ int index = matchIndex;
+ int endTokenIndex = index;
+
+ // Find the match token first.
+ while (index < line.length()) {
+ if (!Character.isLetterOrDigit(line.charAt(index))) {
+ endTokenIndex = index;
+ remainingLength = tempRemainingLength;
+ break;
+ }
+ tempRemainingLength--;
+ index++;
+ }
+
+ // Find as much content before the match.
+ index = matchIndex - 1;
+ tempRemainingLength = remainingLength;
+ int startTokenIndex = matchIndex;
+ while (index > -1 && tempRemainingLength > 0) {
+ if (!Character.isLetterOrDigit(line.charAt(index))) {
+ startTokenIndex = index;
+ remainingLength = tempRemainingLength;
+ }
+ tempRemainingLength--;
+ index--;
+ }
+
+ index = endTokenIndex;
+ tempRemainingLength = remainingLength;
+ // Find remaining content at after match.
+ while (index < line.length() && tempRemainingLength > 0) {
+ if (!Character.isLetterOrDigit(line.charAt(index))) {
+ endTokenIndex = index;
+ }
+ tempRemainingLength--;
+ index++;
+ }
+ // Append ellipse if there is content before or after.
+ final StringBuilder sb = new StringBuilder();
+ if (startTokenIndex > 0) {
+ sb.append("...");
+ }
+ sb.append(line.substring(startTokenIndex, endTokenIndex));
+ if (endTokenIndex < line.length()) {
+ sb.append("...");
+ }
+ return sb.toString();
+ }
+
+ private static final Pattern SPLIT_PATTERN = Pattern.compile(
+ "([\\w-\\.]+)@((?:[\\w]+\\.)+)([a-zA-Z]{2,4})|[\\w]+");
+
+ /**
+ * Helper method for splitting a string into tokens. The lists passed in are populated with
+ * the
+ * tokens and offsets into the content of each token. The tokenization function parses e-mail
+ * addresses as a single token; otherwise it splits on any non-alphanumeric character.
+ *
+ * @param content Content to split.
+ * @return List of token strings.
+ */
+ private static List split(String content) {
+ final Matcher matcher = SPLIT_PATTERN.matcher(content);
+ final ArrayList tokens = Lists.newArrayList();
+ while (matcher.find()) {
+ tokens.add(matcher.group());
+ }
+ return tokens;
+ }
+
+ /**
+ * Shows data element.
+ */
+ public void showData(Cursor cursor, int dataColumnIndex) {
+ cursor.copyStringToBuffer(dataColumnIndex, mDataBuffer);
+ setData(mDataBuffer.data, mDataBuffer.sizeCopied);
+ }
+
+ public void setActivatedStateSupported(boolean flag) {
+ this.mActivatedStateSupported = flag;
+ }
+
+ public void setAdjustSelectionBoundsEnabled(boolean enabled) {
+ mAdjustSelectionBoundsEnabled = enabled;
+ }
+
+ @Override
+ public void requestLayout() {
+ // We will assume that once measured this will not need to resize
+ // itself, so there is no need to pass the layout request to the parent
+ // view (ListView).
+ forceLayout();
+ }
+
+ public void setPhotoPosition(PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+ }
+
+ public PhotoPosition getPhotoPosition() {
+ return mPhotoPosition;
+ }
+
+ /**
+ * Set drawable resources directly for the drawable resource of the photo view.
+ *
+ * @param drawableId Id of drawable resource.
+ */
+ public void setDrawableResource(int drawableId) {
+ ImageView photo = getPhotoView();
+ photo.setScaleType(ImageView.ScaleType.CENTER);
+ photo.setImageDrawable(getContext().getDrawable(drawableId));
+ photo.setImageTintList(ColorStateList.valueOf(
+ getContext().getColor(R.color.search_shortcut_icon_color)));
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final float x = event.getX();
+ final float y = event.getY();
+ // If the touch event's coordinates are not within the view's header, then delegate
+ // to super.onTouchEvent so that regular view behavior is preserved. Otherwise, consume
+ // and ignore the touch event.
+ if (mBoundsWithoutHeader.contains((int) x, (int) y) || !pointIsInView(x, y)) {
+ return super.onTouchEvent(event);
+ } else {
+ return true;
+ }
+ }
+
+ private final boolean pointIsInView(float localX, float localY) {
+ return localX >= mLeftOffset && localX < mRightOffset
+ && localY >= 0 && localY < (getBottom() - getTop());
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactListPinnedHeaderView.java b/ContactsCommon/src/com/android/contacts/common/list/ContactListPinnedHeaderView.java
new file mode 100644
index 0000000..7b4a3cd
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactListPinnedHeaderView.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.ViewUtil;
+
+/**
+ * A custom view for the pinned section header shown at the top of the contact list.
+ */
+public class ContactListPinnedHeaderView extends TextView {
+
+ public ContactListPinnedHeaderView(Context context, AttributeSet attrs, View parent) {
+ super(context, attrs);
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+ int backgroundColor = a.getColor(
+ R.styleable.ContactListItemView_list_item_background_color, Color.WHITE);
+ int textOffsetTop = a.getDimensionPixelSize(
+ R.styleable.ContactListItemView_list_item_text_offset_top, 0);
+ int paddingStartOffset = a.getDimensionPixelSize(
+ R.styleable.ContactListItemView_list_item_padding_left, 0);
+ int textWidth = getResources().getDimensionPixelSize(
+ R.dimen.contact_list_section_header_width);
+ int widthIncludingPadding = paddingStartOffset + textWidth;
+ a.recycle();
+
+ setBackgroundColor(backgroundColor);
+ setTextAppearance(getContext(), R.style.SectionHeaderStyle);
+ setLayoutParams(new LayoutParams(widthIncludingPadding, LayoutParams.WRAP_CONTENT));
+ setLayoutDirection(parent.getLayoutDirection());
+ setGravity(Gravity.CENTER_VERTICAL |
+ (ViewUtil.isViewLayoutRtl(this) ? Gravity.RIGHT : Gravity.LEFT));
+
+ // Apply text top offset. Multiply by two, because we are implementing this by padding for a
+ // vertically centered view, rather than adjusting the position directly via a layout.
+ setPaddingRelative(
+ getPaddingStart() + paddingStartOffset,
+ getPaddingTop() + (textOffsetTop * 2),
+ getPaddingEnd(),
+ getPaddingBottom());
+ }
+
+ /**
+ * Sets section header or makes it invisible if the title is null.
+ */
+ public void setSectionHeaderTitle(String title) {
+ if (!TextUtils.isEmpty(title)) {
+ setText(title);
+ setVisibility(View.VISIBLE);
+ } else {
+ setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactTileAdapter.java b/ContactsCommon/src/com/android/contacts/common/list/ContactTileAdapter.java
new file mode 100644
index 0000000..ac10f38
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactTileAdapter.java
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPresenceIconUtil;
+import com.android.contacts.common.ContactStatusUtil;
+import com.android.contacts.common.ContactTileLoaderFactory;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.ViewUtil;
+
+import java.util.ArrayList;
+
+/**
+ * Arranges contacts favorites according to provided {@link DisplayType}.
+ * Also allows for a configurable number of columns and {@link DisplayType}
+ */
+public class ContactTileAdapter extends BaseAdapter {
+ private static final String TAG = ContactTileAdapter.class.getSimpleName();
+
+ private DisplayType mDisplayType;
+ private ContactTileView.Listener mListener;
+ private Context mContext;
+ private Resources mResources;
+ protected Cursor mContactCursor = null;
+ private ContactPhotoManager mPhotoManager;
+ protected int mNumFrequents;
+
+ /**
+ * Index of the first NON starred contact in the {@link Cursor}
+ * Only valid when {@link DisplayType#STREQUENT} is true
+ */
+ private int mDividerPosition;
+ protected int mColumnCount;
+ private int mStarredIndex;
+
+ protected int mIdIndex;
+ protected int mLookupIndex;
+ protected int mPhotoUriIndex;
+ protected int mNameIndex;
+ protected int mPresenceIndex;
+ protected int mStatusIndex;
+
+ private boolean mIsQuickContactEnabled = false;
+ private final int mPaddingInPixels;
+ private final int mWhitespaceStartEnd;
+
+ /**
+ * Configures the adapter to filter and display contacts using different view types.
+ * TODO: Create Uris to support getting Starred_only and Frequent_only cursors.
+ */
+ public enum DisplayType {
+ /**
+ * Displays a mixed view type of starred and frequent contacts
+ */
+ STREQUENT,
+
+ /**
+ * Display only starred contacts
+ */
+ STARRED_ONLY,
+
+ /**
+ * Display only most frequently contacted
+ */
+ FREQUENT_ONLY,
+
+ /**
+ * Display all contacts from a group in the cursor
+ * Use {@link com.android.contacts.GroupMemberLoader}
+ * when passing {@link Cursor} into loadFromCusor method.
+ *
+ * Group member logic has been moved into GroupMemberTileAdapter. This constant is still
+ * needed by calling classes.
+ */
+ GROUP_MEMBERS
+ }
+
+ public ContactTileAdapter(Context context, ContactTileView.Listener listener, int numCols,
+ DisplayType displayType) {
+ mListener = listener;
+ mContext = context;
+ mResources = context.getResources();
+ mColumnCount = (displayType == DisplayType.FREQUENT_ONLY ? 1 : numCols);
+ mDisplayType = displayType;
+ mNumFrequents = 0;
+
+ // Converting padding in dips to padding in pixels
+ mPaddingInPixels = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.contact_tile_divider_padding);
+ mWhitespaceStartEnd = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.contact_tile_start_end_whitespace);
+
+ bindColumnIndices();
+ }
+
+ public void setPhotoLoader(ContactPhotoManager photoLoader) {
+ mPhotoManager = photoLoader;
+ }
+
+ public void setColumnCount(int columnCount) {
+ mColumnCount = columnCount;
+ }
+
+ public void setDisplayType(DisplayType displayType) {
+ mDisplayType = displayType;
+ }
+
+ public void enableQuickContact(boolean enableQuickContact) {
+ mIsQuickContactEnabled = enableQuickContact;
+ }
+
+ /**
+ * Sets the column indices for expected {@link Cursor}
+ * based on {@link DisplayType}.
+ */
+ protected void bindColumnIndices() {
+ mIdIndex = ContactTileLoaderFactory.CONTACT_ID;
+ mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
+ mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
+ mNameIndex = ContactTileLoaderFactory.DISPLAY_NAME;
+ mStarredIndex = ContactTileLoaderFactory.STARRED;
+ mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
+ mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
+ }
+
+ private static boolean cursorIsValid(Cursor cursor) {
+ return cursor != null && !cursor.isClosed();
+ }
+
+ /**
+ * Gets the number of frequents from the passed in cursor.
+ *
+ * This methods is needed so the GroupMemberTileAdapter can override this.
+ *
+ * @param cursor The cursor to get number of frequents from.
+ */
+ protected void saveNumFrequentsFromCursor(Cursor cursor) {
+
+ // count the number of frequents
+ switch (mDisplayType) {
+ case STARRED_ONLY:
+ mNumFrequents = 0;
+ break;
+ case STREQUENT:
+ mNumFrequents = cursorIsValid(cursor) ?
+ cursor.getCount() - mDividerPosition : 0;
+ break;
+ case FREQUENT_ONLY:
+ mNumFrequents = cursorIsValid(cursor) ? cursor.getCount() : 0;
+ break;
+ default:
+ throw new IllegalArgumentException("Unrecognized DisplayType " + mDisplayType);
+ }
+ }
+
+ /**
+ * Creates {@link ContactTileView}s for each item in {@link Cursor}.
+ *
+ * Else use {@link ContactTileLoaderFactory}
+ */
+ public void setContactCursor(Cursor cursor) {
+ mContactCursor = cursor;
+ mDividerPosition = getDividerPosition(cursor);
+
+ saveNumFrequentsFromCursor(cursor);
+
+ // cause a refresh of any views that rely on this data
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Iterates over the {@link Cursor}
+ * Returns position of the first NON Starred Contact
+ * Returns -1 if {@link DisplayType#STARRED_ONLY}
+ * Returns 0 if {@link DisplayType#FREQUENT_ONLY}
+ */
+ protected int getDividerPosition(Cursor cursor) {
+ switch (mDisplayType) {
+ case STREQUENT:
+ if (!cursorIsValid(cursor)) {
+ return 0;
+ }
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ if (cursor.getInt(mStarredIndex) == 0) {
+ return cursor.getPosition();
+ }
+ }
+
+ // There are not NON Starred contacts in cursor
+ // Set divider positon to end
+ return cursor.getCount();
+ case STARRED_ONLY:
+ // There is no divider
+ return -1;
+ case FREQUENT_ONLY:
+ // Divider is first
+ return 0;
+ default:
+ throw new IllegalStateException("Unrecognized DisplayType " + mDisplayType);
+ }
+ }
+
+ protected ContactEntry createContactEntryFromCursor(Cursor cursor, int position) {
+ // If the loader was canceled we will be given a null cursor.
+ // In that case, show an empty list of contacts.
+ if (!cursorIsValid(cursor) || cursor.getCount() <= position) {
+ return null;
+ }
+
+ cursor.moveToPosition(position);
+ long id = cursor.getLong(mIdIndex);
+ String photoUri = cursor.getString(mPhotoUriIndex);
+ String lookupKey = cursor.getString(mLookupIndex);
+
+ ContactEntry contact = new ContactEntry();
+ String name = cursor.getString(mNameIndex);
+ contact.name = (name != null) ? name : mResources.getString(R.string.missing_name);
+ contact.status = cursor.getString(mStatusIndex);
+ contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
+ contact.lookupKey = lookupKey;
+ contact.lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
+ contact.isFavorite = cursor.getInt(mStarredIndex) > 0;
+
+ // Set presence icon and status message
+ Drawable icon = null;
+ int presence = 0;
+ if (!cursor.isNull(mPresenceIndex)) {
+ presence = cursor.getInt(mPresenceIndex);
+ icon = ContactPresenceIconUtil.getPresenceIcon(mContext, presence);
+ }
+ contact.presenceIcon = icon;
+
+ String statusMessage = null;
+ if (mStatusIndex != 0 && !cursor.isNull(mStatusIndex)) {
+ statusMessage = cursor.getString(mStatusIndex);
+ }
+ // If there is no status message from the contact, but there was a presence value,
+ // then use the default status message string
+ if (statusMessage == null && presence != 0) {
+ statusMessage = ContactStatusUtil.getStatusString(mContext, presence);
+ }
+ contact.status = statusMessage;
+
+ return contact;
+ }
+
+ /**
+ * Returns the number of frequents that will be displayed in the list.
+ */
+ public int getNumFrequents() {
+ return mNumFrequents;
+ }
+
+ @Override
+ public int getCount() {
+ if (!cursorIsValid(mContactCursor)) {
+ return 0;
+ }
+
+ switch (mDisplayType) {
+ case STARRED_ONLY:
+ return getRowCount(mContactCursor.getCount());
+ case STREQUENT:
+ // Takes numbers of rows the Starred Contacts Occupy
+ int starredRowCount = getRowCount(mDividerPosition);
+
+ // Compute the frequent row count which is 1 plus the number of frequents
+ // (to account for the divider) or 0 if there are no frequents.
+ int frequentRowCount = mNumFrequents == 0 ? 0 : mNumFrequents + 1;
+
+ // Return the number of starred plus frequent rows
+ return starredRowCount + frequentRowCount;
+ case FREQUENT_ONLY:
+ // Number of frequent contacts
+ return mContactCursor.getCount();
+ default:
+ throw new IllegalArgumentException("Unrecognized DisplayType " + mDisplayType);
+ }
+ }
+
+ /**
+ * Returns the number of rows required to show the provided number of entries
+ * with the current number of columns.
+ */
+ protected int getRowCount(int entryCount) {
+ return entryCount == 0 ? 0 : ((entryCount - 1) / mColumnCount) + 1;
+ }
+
+ public int getColumnCount() {
+ return mColumnCount;
+ }
+
+ /**
+ * Returns an ArrayList of the {@link ContactEntry}s that are to appear
+ * on the row for the given position.
+ */
+ @Override
+ public ArrayList getItem(int position) {
+ ArrayList resultList = new ArrayList(mColumnCount);
+ int contactIndex = position * mColumnCount;
+
+ switch (mDisplayType) {
+ case FREQUENT_ONLY:
+ resultList.add(createContactEntryFromCursor(mContactCursor, position));
+ break;
+ case STARRED_ONLY:
+ for (int columnCounter = 0; columnCounter < mColumnCount; columnCounter++) {
+ resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
+ contactIndex++;
+ }
+ break;
+ case STREQUENT:
+ if (position < getRowCount(mDividerPosition)) {
+ for (int columnCounter = 0; columnCounter < mColumnCount &&
+ contactIndex != mDividerPosition; columnCounter++) {
+ resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
+ contactIndex++;
+ }
+ } else {
+ /*
+ * Current position minus how many rows are before the divider and
+ * Minus 1 for the divider itself provides the relative index of the frequent
+ * contact being displayed. Then add the dividerPostion to give the offset
+ * into the contacts cursor to get the absoulte index.
+ */
+ contactIndex = position - getRowCount(mDividerPosition) - 1 + mDividerPosition;
+ resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unrecognized DisplayType " + mDisplayType);
+ }
+ return resultList;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ // As we show several selectable items for each ListView row,
+ // we can not determine a stable id. But as we don't rely on ListView's selection,
+ // this should not be a problem.
+ return position;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return (mDisplayType != DisplayType.STREQUENT);
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position != getRowCount(mDividerPosition);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ int itemViewType = getItemViewType(position);
+
+ if (itemViewType == ViewTypes.DIVIDER) {
+ // Checking For Divider First so not to cast convertView
+ final TextView textView = (TextView) (convertView == null ? getDivider() : convertView);
+ setDividerPadding(textView, position == 0);
+ return textView;
+ }
+
+ ContactTileRow contactTileRowView = (ContactTileRow) convertView;
+ ArrayList contactList = getItem(position);
+
+ if (contactTileRowView == null) {
+ // Creating new row if needed
+ contactTileRowView = new ContactTileRow(mContext, itemViewType);
+ }
+
+ contactTileRowView.configureRow(contactList, position == getCount() - 1);
+ return contactTileRowView;
+ }
+
+ /**
+ * Divider uses a list_seperator.xml along with text to denote
+ * the most frequently contacted contacts.
+ */
+ private TextView getDivider() {
+ return MoreContactUtils.createHeaderView(mContext, R.string.favoritesFrequentContacted);
+ }
+
+ private void setDividerPadding(TextView headerTextView, boolean isFirstRow) {
+ MoreContactUtils.setHeaderViewBottomPadding(mContext, headerTextView, isFirstRow);
+ }
+
+ private int getLayoutResourceId(int viewType) {
+ switch (viewType) {
+ case ViewTypes.STARRED:
+ return mIsQuickContactEnabled ?
+ R.layout.contact_tile_starred_quick_contact : R.layout.contact_tile_starred;
+ case ViewTypes.FREQUENT:
+ return R.layout.contact_tile_frequent;
+ default:
+ throw new IllegalArgumentException("Unrecognized viewType " + viewType);
+ }
+ }
+ @Override
+ public int getViewTypeCount() {
+ return ViewTypes.COUNT;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ /*
+ * Returns view type based on {@link DisplayType}.
+ * {@link DisplayType#STARRED_ONLY} and {@link DisplayType#GROUP_MEMBERS}
+ * are {@link ViewTypes#STARRED}.
+ * {@link DisplayType#FREQUENT_ONLY} is {@link ViewTypes#FREQUENT}.
+ * {@link DisplayType#STREQUENT} mixes both {@link ViewTypes}
+ * and also adds in {@link ViewTypes#DIVIDER}.
+ */
+ switch (mDisplayType) {
+ case STREQUENT:
+ if (position < getRowCount(mDividerPosition)) {
+ return ViewTypes.STARRED;
+ } else if (position == getRowCount(mDividerPosition)) {
+ return ViewTypes.DIVIDER;
+ } else {
+ return ViewTypes.FREQUENT;
+ }
+ case STARRED_ONLY:
+ return ViewTypes.STARRED;
+ case FREQUENT_ONLY:
+ return ViewTypes.FREQUENT;
+ default:
+ throw new IllegalStateException("Unrecognized DisplayType " + mDisplayType);
+ }
+ }
+
+ /**
+ * Returns the "frequent header" position. Only available when STREQUENT or
+ * STREQUENT_PHONE_ONLY is used for its display type.
+ */
+ public int getFrequentHeaderPosition() {
+ return getRowCount(mDividerPosition);
+ }
+
+ /**
+ * Acts as a row item composed of {@link ContactTileView}
+ *
+ * TODO: FREQUENT doesn't really need it. Just let {@link #getView} return
+ */
+ private class ContactTileRow extends FrameLayout {
+ private int mItemViewType;
+ private int mLayoutResId;
+
+ public ContactTileRow(Context context, int itemViewType) {
+ super(context);
+ mItemViewType = itemViewType;
+ mLayoutResId = getLayoutResourceId(mItemViewType);
+
+ // Remove row (but not children) from accessibility node tree.
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+
+ /**
+ * Configures the row to add {@link ContactEntry}s information to the views
+ */
+ public void configureRow(ArrayList list, boolean isLastRow) {
+ int columnCount = mItemViewType == ViewTypes.FREQUENT ? 1 : mColumnCount;
+
+ // Adding tiles to row and filling in contact information
+ for (int columnCounter = 0; columnCounter < columnCount; columnCounter++) {
+ ContactEntry entry =
+ columnCounter < list.size() ? list.get(columnCounter) : null;
+ addTileFromEntry(entry, columnCounter, isLastRow);
+ }
+ }
+
+ private void addTileFromEntry(ContactEntry entry, int childIndex, boolean isLastRow) {
+ final ContactTileView contactTile;
+
+ if (getChildCount() <= childIndex) {
+ contactTile = (ContactTileView) inflate(mContext, mLayoutResId, null);
+ // Note: the layoutparam set here is only actually used for FREQUENT.
+ // We override onMeasure() for STARRED and we don't care the layout param there.
+ Resources resources = mContext.getResources();
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ params.setMargins(
+ mWhitespaceStartEnd,
+ 0,
+ mWhitespaceStartEnd,
+ 0);
+ contactTile.setLayoutParams(params);
+ contactTile.setPhotoManager(mPhotoManager);
+ contactTile.setListener(mListener);
+ addView(contactTile);
+ } else {
+ contactTile = (ContactTileView) getChildAt(childIndex);
+ }
+ contactTile.loadFromContact(entry);
+
+ switch (mItemViewType) {
+ case ViewTypes.STARRED:
+ // Set padding between tiles. Divide mPaddingInPixels between left and right
+ // tiles as evenly as possible.
+ contactTile.setPaddingRelative(
+ (mPaddingInPixels + 1) / 2, 0,
+ mPaddingInPixels
+ / 2, 0);
+ break;
+ case ViewTypes.FREQUENT:
+ contactTile.setHorizontalDividerVisibility(
+ isLastRow ? View.GONE : View.VISIBLE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ switch (mItemViewType) {
+ case ViewTypes.STARRED:
+ onLayoutForTiles();
+ return;
+ default:
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+ }
+
+ private void onLayoutForTiles() {
+ final int count = getChildCount();
+
+ // Amount of margin needed on the left is based on difference between offset and padding
+ int childLeft = mWhitespaceStartEnd - (mPaddingInPixels + 1) / 2;
+
+ // Just line up children horizontally.
+ for (int i = 0; i < count; i++) {
+ final int rtlAdjustedIndex = ViewUtil.isViewLayoutRtl(this) ? count - i - 1 : i;
+ final View child = getChildAt(rtlAdjustedIndex);
+
+ // Note MeasuredWidth includes the padding.
+ final int childWidth = child.getMeasuredWidth();
+ child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
+ childLeft += childWidth;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ switch (mItemViewType) {
+ case ViewTypes.STARRED:
+ onMeasureForTiles(widthMeasureSpec);
+ return;
+ default:
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+ }
+
+ private void onMeasureForTiles(int widthMeasureSpec) {
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ // Just in case...
+ setMeasuredDimension(width, 0);
+ return;
+ }
+
+ // 1. Calculate image size.
+ // = ([total width] - [total whitespace]) / [child count]
+ //
+ // 2. Set it to width/height of each children.
+ // If we have a remainder, some tiles will have 1 pixel larger width than its height.
+ //
+ // 3. Set the dimensions of itself.
+ // Let width = given width.
+ // Let height = wrap content.
+
+ final int totalWhitespaceInPixels = (mColumnCount - 1) * mPaddingInPixels
+ + mWhitespaceStartEnd * 2;
+
+ // Preferred width / height for images (excluding the padding).
+ // The actual width may be 1 pixel larger than this if we have a remainder.
+ final int imageSize = (width - totalWhitespaceInPixels) / mColumnCount;
+ final int remainder = width - (imageSize * mColumnCount) - totalWhitespaceInPixels;
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final int childWidth = imageSize + child.getPaddingRight() + child.getPaddingLeft()
+ // Compensate for the remainder
+ + (i < remainder ? 1 : 0);
+
+ child.measure(
+ MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
+ );
+ }
+ setMeasuredDimension(width, getChildAt(0).getMeasuredHeight());
+ }
+ }
+
+ protected static class ViewTypes {
+ public static final int COUNT = 4;
+ public static final int STARRED = 0;
+ public static final int DIVIDER = 1;
+ public static final int FREQUENT = 2;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactTileFrequentView.java b/ContactsCommon/src/com/android/contacts/common/list/ContactTileFrequentView.java
new file mode 100644
index 0000000..7dcd0a1
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactTileFrequentView.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.contacts.common.util.ViewUtil;
+
+/**
+ * A {@link com.android.contacts.common.list.ContactTileView} that is used for most frequently contacted in the People app
+ */
+public class ContactTileFrequentView extends ContactTileView {
+ public ContactTileFrequentView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean isDarkTheme() {
+ return false;
+ }
+
+ @Override
+ protected int getApproximateImageSize() {
+ return ViewUtil.getConstantPreLayoutWidth(getPhotoView());
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactTilePhoneFrequentView.java b/ContactsCommon/src/com/android/contacts/common/list/ContactTilePhoneFrequentView.java
new file mode 100644
index 0000000..aec93ab
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactTilePhoneFrequentView.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.util.ViewUtil;
+
+/**
+ * A dark version of the {@link com.android.contacts.common.list.ContactTileView} that is used in Dialtacts
+ * for frequently called contacts. Slightly different behavior from superclass...
+ * when you tap it, you want to call the frequently-called number for the
+ * contact, even if that is not the default number for that contact.
+ */
+public class ContactTilePhoneFrequentView extends ContactTileView {
+ private String mPhoneNumberString;
+
+ public ContactTilePhoneFrequentView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean isDarkTheme() {
+ return true;
+ }
+
+ @Override
+ protected int getApproximateImageSize() {
+ return ViewUtil.getConstantPreLayoutWidth(getQuickContact());
+ }
+
+ @Override
+ public void loadFromContact(ContactEntry entry) {
+ super.loadFromContact(entry);
+ mPhoneNumberString = null; // ... in case we're reusing the view
+ if (entry != null) {
+ // Grab the phone-number to call directly... see {@link onClick()}
+ mPhoneNumberString = entry.phoneNumber;
+ }
+ }
+
+ @Override
+ protected OnClickListener createClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) return;
+ if (TextUtils.isEmpty(mPhoneNumberString)) {
+ // Copy "superclass" implementation
+ mListener.onContactSelected(getLookupUri(), MoreContactUtils
+ .getTargetRectFromView(ContactTilePhoneFrequentView.this));
+ } else {
+ // When you tap a frequently-called contact, you want to
+ // call them at the number that you usually talk to them
+ // at (i.e. the one displayed in the UI), regardless of
+ // whether that's their default number.
+ mListener.onCallNumberDirectly(mPhoneNumberString);
+ }
+ }
+ };
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactTileStarredView.java b/ContactsCommon/src/com/android/contacts/common/list/ContactTileStarredView.java
new file mode 100644
index 0000000..59ef81e
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactTileStarredView.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * A {@link ContactTileStarredView} displays the contact's picture overlayed with their name
+ * in a square. The actual dimensions are set by
+ * {@link com.android.contacts.common.list.ContactTileAdapter.ContactTileRow}.
+ */
+public class ContactTileStarredView extends ContactTileView {
+
+ /**
+ * The photo manager should display the default image/letter at 80% of its normal size.
+ */
+ private static final float DEFAULT_IMAGE_LETTER_SCALE = 0.8f;
+
+ public ContactTileStarredView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean isDarkTheme() {
+ return false;
+ }
+
+ @Override
+ protected int getApproximateImageSize() {
+ // The picture is the full size of the tile (minus some padding, but we can be generous)
+ return mListener.getApproximateTileWidth();
+ }
+
+ @Override
+ protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
+ return new DefaultImageRequest(displayName, lookupKey, ContactPhotoManager.TYPE_DEFAULT,
+ DEFAULT_IMAGE_LETTER_SCALE, /* offset = */ 0, /* isCircular = */ true);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactTileView.java b/ContactsCommon/src/com/android/contacts/common/list/ContactTileView.java
new file mode 100644
index 0000000..56552bb
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactTileView.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.R;
+
+/**
+ * A ContactTile displays a contact's picture and name
+ */
+public abstract class ContactTileView extends FrameLayout {
+ private final static String TAG = ContactTileView.class.getSimpleName();
+
+ private Uri mLookupUri;
+ private ImageView mPhoto;
+ private QuickContactBadge mQuickContact;
+ private TextView mName;
+ private TextView mStatus;
+ private TextView mPhoneLabel;
+ private TextView mPhoneNumber;
+ private ContactPhotoManager mPhotoManager = null;
+ private View mPushState;
+ private View mHorizontalDivider;
+ protected Listener mListener;
+
+ public ContactTileView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mName = (TextView) findViewById(R.id.contact_tile_name);
+
+ mQuickContact = (QuickContactBadge) findViewById(R.id.contact_tile_quick);
+ mPhoto = (ImageView) findViewById(R.id.contact_tile_image);
+ mStatus = (TextView) findViewById(R.id.contact_tile_status);
+ mPhoneLabel = (TextView) findViewById(R.id.contact_tile_phone_type);
+ mPhoneNumber = (TextView) findViewById(R.id.contact_tile_phone_number);
+ mPushState = findViewById(R.id.contact_tile_push_state);
+ mHorizontalDivider = findViewById(R.id.contact_tile_horizontal_divider);
+
+ OnClickListener listener = createClickListener();
+ setOnClickListener(listener);
+ }
+
+ protected OnClickListener createClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) return;
+ mListener.onContactSelected(
+ getLookupUri(),
+ MoreContactUtils.getTargetRectFromView(ContactTileView.this));
+ }
+ };
+ }
+
+ public void setPhotoManager(ContactPhotoManager photoManager) {
+ mPhotoManager = photoManager;
+ }
+
+ /**
+ * Populates the data members to be displayed from the
+ * fields in {@link com.android.contacts.common.list.ContactEntry}
+ */
+ public void loadFromContact(ContactEntry entry) {
+
+ if (entry != null) {
+ mName.setText(getNameForView(entry.name));
+ mLookupUri = entry.lookupUri;
+
+ if (mStatus != null) {
+ if (entry.status == null) {
+ mStatus.setVisibility(View.GONE);
+ } else {
+ mStatus.setText(entry.status);
+ mStatus.setCompoundDrawablesWithIntrinsicBounds(entry.presenceIcon,
+ null, null, null);
+ mStatus.setVisibility(View.VISIBLE);
+ }
+ }
+
+ if (mPhoneLabel != null) {
+ if (TextUtils.isEmpty(entry.phoneLabel)) {
+ mPhoneLabel.setVisibility(View.GONE);
+ } else {
+ mPhoneLabel.setVisibility(View.VISIBLE);
+ mPhoneLabel.setText(entry.phoneLabel);
+ }
+ }
+
+ if (mPhoneNumber != null) {
+ // TODO: Format number correctly
+ mPhoneNumber.setText(entry.phoneNumber);
+ }
+
+ setVisibility(View.VISIBLE);
+
+ if (mPhotoManager != null) {
+ DefaultImageRequest request = getDefaultImageRequest(entry.name, entry.lookupKey);
+ configureViewForImage(entry.photoUri == null);
+ if (mPhoto != null) {
+ mPhotoManager.loadPhoto(mPhoto, entry.photoUri, getApproximateImageSize(),
+ isDarkTheme(), isContactPhotoCircular(), request);
+
+ if (mQuickContact != null) {
+ mQuickContact.assignContactUri(mLookupUri);
+ }
+ } else if (mQuickContact != null) {
+ mQuickContact.assignContactUri(mLookupUri);
+ mPhotoManager.loadPhoto(mQuickContact, entry.photoUri,
+ getApproximateImageSize(), isDarkTheme(), isContactPhotoCircular(),
+ request);
+ }
+ } else {
+ Log.w(TAG, "contactPhotoManager not set");
+ }
+
+ if (mPushState != null) {
+ mPushState.setContentDescription(entry.name);
+ } else if (mQuickContact != null) {
+ mQuickContact.setContentDescription(entry.name);
+ }
+ } else {
+ setVisibility(View.INVISIBLE);
+ }
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ public void setHorizontalDividerVisibility(int visibility) {
+ if (mHorizontalDivider != null) mHorizontalDivider.setVisibility(visibility);
+ }
+
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ protected QuickContactBadge getQuickContact() {
+ return mQuickContact;
+ }
+
+ protected View getPhotoView() {
+ return mPhoto;
+ }
+
+ /**
+ * Returns the string that should actually be displayed as the contact's name. Subclasses
+ * can override this to return formatted versions of the name - i.e. first name only.
+ */
+ protected String getNameForView(String name) {
+ return name;
+ }
+
+ /**
+ * Implemented by subclasses to estimate the size of the picture. This can return -1 if only
+ * a thumbnail is shown anyway
+ */
+ protected abstract int getApproximateImageSize();
+
+ protected abstract boolean isDarkTheme();
+
+ /**
+ * Implemented by subclasses to reconfigure the view's layout and subviews, based on whether
+ * or not the contact has a user-defined photo.
+ *
+ * @param isDefaultImage True if the contact does not have a user-defined contact photo
+ * (which means a default contact image will be applied by the {@link ContactPhotoManager}
+ */
+ protected void configureViewForImage(boolean isDefaultImage) {
+ // No-op by default.
+ }
+
+ /**
+ * Implemented by subclasses to allow them to return a {@link DefaultImageRequest} with the
+ * various image parameters defined to match their own layouts.
+ *
+ * @param displayName The display name of the contact
+ * @param lookupKey The lookup key of the contact
+ * @return A {@link DefaultImageRequest} object with each field configured by the subclass
+ * as desired, or {@code null}.
+ */
+ protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
+ return new DefaultImageRequest(displayName, lookupKey, isContactPhotoCircular());
+ }
+
+ /**
+ * Whether contact photo should be displayed as a circular image. Implemented by subclasses
+ * so they can change which drawables to fetch.
+ */
+ protected boolean isContactPhotoCircular() {
+ return true;
+ }
+
+ public interface Listener {
+ /**
+ * Notification that the contact was selected; no specific action is dictated.
+ */
+ void onContactSelected(Uri contactLookupUri, Rect viewRect);
+ /**
+ * Notification that the specified number is to be called.
+ */
+ void onCallNumberDirectly(String phoneNumber);
+ /**
+ * @return The width of each tile. This doesn't have to be a precise number (e.g. paddings
+ * can be ignored), but is used to load the correct picture size from the database
+ */
+ int getApproximateTileWidth();
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ContactsSectionIndexer.java b/ContactsCommon/src/com/android/contacts/common/list/ContactsSectionIndexer.java
new file mode 100644
index 0000000..8d1c9e1
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ContactsSectionIndexer.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.text.TextUtils;
+import android.widget.SectionIndexer;
+
+import java.util.Arrays;
+
+/**
+ * A section indexer that is configured with precomputed section titles and
+ * their respective counts.
+ */
+public class ContactsSectionIndexer implements SectionIndexer {
+
+ private String[] mSections;
+ private int[] mPositions;
+ private int mCount;
+ private static final String BLANK_HEADER_STRING = " ";
+
+ /**
+ * Constructor.
+ *
+ * @param sections a non-null array
+ * @param counts a non-null array of the same size as sections
+ */
+ public ContactsSectionIndexer(String[] sections, int[] counts) {
+ if (sections == null || counts == null) {
+ throw new NullPointerException();
+ }
+
+ if (sections.length != counts.length) {
+ throw new IllegalArgumentException(
+ "The sections and counts arrays must have the same length");
+ }
+
+ // TODO process sections/counts based on current locale and/or specific section titles
+
+ this.mSections = sections;
+ mPositions = new int[counts.length];
+ int position = 0;
+ for (int i = 0; i < counts.length; i++) {
+ if (TextUtils.isEmpty(mSections[i])) {
+ mSections[i] = BLANK_HEADER_STRING;
+ } else if (!mSections[i].equals(BLANK_HEADER_STRING)) {
+ mSections[i] = mSections[i].trim();
+ }
+
+ mPositions[i] = position;
+ position += counts[i];
+ }
+ mCount = position;
+ }
+
+ public Object[] getSections() {
+ return mSections;
+ }
+
+ public int getPositionForSection(int section) {
+ if (section < 0 || section >= mSections.length) {
+ return -1;
+ }
+
+ return mPositions[section];
+ }
+
+ public int getSectionForPosition(int position) {
+ if (position < 0 || position >= mCount) {
+ return -1;
+ }
+
+ int index = Arrays.binarySearch(mPositions, position);
+
+ /*
+ * Consider this example: section positions are 0, 3, 5; the supplied
+ * position is 4. The section corresponding to position 4 starts at
+ * position 3, so the expected return value is 1. Binary search will not
+ * find 4 in the array and thus will return -insertPosition-1, i.e. -3.
+ * To get from that number to the expected value of 1 we need to negate
+ * and subtract 2.
+ */
+ return index >= 0 ? index : -index - 2;
+ }
+
+ public void setProfileHeader(String header) {
+ if (mSections != null) {
+ // Don't do anything if the header is already set properly.
+ if (mSections.length > 0 && header.equals(mSections[0])) {
+ return;
+ }
+
+ // Since the section indexer isn't aware of the profile at the top, we need to add a
+ // special section at the top for it and shift everything else down.
+ String[] tempSections = new String[mSections.length + 1];
+ int[] tempPositions = new int[mPositions.length + 1];
+ tempSections[0] = header;
+ tempPositions[0] = 0;
+ for (int i = 1; i <= mPositions.length; i++) {
+ tempSections[i] = mSections[i - 1];
+ tempPositions[i] = mPositions[i - 1] + 1;
+ }
+ mSections = tempSections;
+ mPositions = tempPositions;
+ mCount++;
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/CustomContactListFilterActivity.java b/ContactsCommon/src/com/android/contacts/common/list/CustomContactListFilterActivity.java
new file mode 100644
index 0000000..5543bdb
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/CustomContactListFilterActivity.java
@@ -0,0 +1,922 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.app.ProgressDialog;
+import android.content.AsyncTaskLoader;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.OperationApplicationException;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.Settings;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.CheckBox;
+import android.widget.ExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.TextView;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.GoogleAccountType;
+import com.android.contacts.common.util.EmptyService;
+import com.android.contacts.common.util.LocalizedNameResolver;
+import com.android.contacts.common.util.WeakAsyncTask;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+
+/**
+ * Shows a list of all available {@link Groups} available, letting the user
+ * select which ones they want to be visible.
+ */
+public class CustomContactListFilterActivity extends Activity
+ implements View.OnClickListener, ExpandableListView.OnChildClickListener,
+ LoaderCallbacks
+{
+ private static final String TAG = "CustomContactListFilterActivity";
+
+ private static final int ACCOUNT_SET_LOADER_ID = 1;
+
+ private ExpandableListView mList;
+ private DisplayAdapter mAdapter;
+
+ private SharedPreferences mPrefs;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.contact_list_filter_custom);
+
+ mList = (ExpandableListView) findViewById(android.R.id.list);
+ mList.setOnChildClickListener(this);
+ mList.setHeaderDividersEnabled(true);
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ mAdapter = new DisplayAdapter(this);
+
+ final LayoutInflater inflater = getLayoutInflater();
+
+ findViewById(R.id.btn_done).setOnClickListener(this);
+ findViewById(R.id.btn_discard).setOnClickListener(this);
+
+ mList.setOnCreateContextMenuListener(this);
+
+ mList.setAdapter(mAdapter);
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ // android.R.id.home will be triggered in onOptionsItemSelected()
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ public static class CustomFilterConfigurationLoader extends AsyncTaskLoader {
+
+ private AccountSet mAccountSet;
+
+ public CustomFilterConfigurationLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AccountSet loadInBackground() {
+ Context context = getContext();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
+ final ContentResolver resolver = context.getContentResolver();
+
+ final AccountSet accounts = new AccountSet();
+ for (AccountWithDataSet account : accountTypes.getAccounts(false)) {
+ final AccountType accountType = accountTypes.getAccountTypeForAccount(account);
+ if (accountType.isExtension() && !account.hasData(context)) {
+ // Extension with no data -- skip.
+ continue;
+ }
+
+ AccountDisplay accountDisplay =
+ new AccountDisplay(resolver, account.name, account.type, account.dataSet);
+
+ final Uri.Builder groupsUri = Groups.CONTENT_URI.buildUpon()
+ .appendQueryParameter(Groups.ACCOUNT_NAME, account.name)
+ .appendQueryParameter(Groups.ACCOUNT_TYPE, account.type);
+ if (account.dataSet != null) {
+ groupsUri.appendQueryParameter(Groups.DATA_SET, account.dataSet).build();
+ }
+ final Cursor cursor = resolver.query(groupsUri.build(), null, null, null, null);
+ if (cursor == null) {
+ continue;
+ }
+ android.content.EntityIterator iterator =
+ ContactsContract.Groups.newEntityIterator(cursor);
+ try {
+ boolean hasGroups = false;
+
+ // Create entries for each known group
+ while (iterator.hasNext()) {
+ final ContentValues values = iterator.next().getEntityValues();
+ final GroupDelta group = GroupDelta.fromBefore(values);
+ accountDisplay.addGroup(group);
+ hasGroups = true;
+ }
+ // Create single entry handling ungrouped status
+ accountDisplay.mUngrouped =
+ GroupDelta.fromSettings(resolver, account.name, account.type,
+ account.dataSet, hasGroups);
+ accountDisplay.addGroup(accountDisplay.mUngrouped);
+ } finally {
+ iterator.close();
+ }
+
+ accounts.add(accountDisplay);
+ }
+
+ return accounts;
+ }
+
+ @Override
+ public void deliverResult(AccountSet cursor) {
+ if (isReset()) {
+ return;
+ }
+
+ mAccountSet = cursor;
+
+ if (isStarted()) {
+ super.deliverResult(cursor);
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mAccountSet != null) {
+ deliverResult(mAccountSet);
+ }
+ if (takeContentChanged() || mAccountSet == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ onStopLoading();
+ mAccountSet = null;
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ getLoaderManager().initLoader(ACCOUNT_SET_LOADER_ID, null, this);
+ super.onStart();
+ }
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ return new CustomFilterConfigurationLoader(this);
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, AccountSet data) {
+ mAdapter.setAccounts(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ mAdapter.setAccounts(null);
+ }
+
+ private static final int DEFAULT_SHOULD_SYNC = 1;
+ private static final int DEFAULT_VISIBLE = 0;
+
+ /**
+ * Entry holding any changes to {@link Groups} or {@link Settings} rows,
+ * such as {@link Groups#SHOULD_SYNC} or {@link Groups#GROUP_VISIBLE}.
+ */
+ protected static class GroupDelta extends ValuesDelta {
+ private boolean mUngrouped = false;
+ private boolean mAccountHasGroups;
+
+ private GroupDelta() {
+ super();
+ }
+
+ /**
+ * Build {@link GroupDelta} from the {@link Settings} row for the given
+ * {@link Settings#ACCOUNT_NAME}, {@link Settings#ACCOUNT_TYPE}, and
+ * {@link Settings#DATA_SET}.
+ */
+ public static GroupDelta fromSettings(ContentResolver resolver, String accountName,
+ String accountType, String dataSet, boolean accountHasGroups) {
+ final Uri.Builder settingsUri = Settings.CONTENT_URI.buildUpon()
+ .appendQueryParameter(Settings.ACCOUNT_NAME, accountName)
+ .appendQueryParameter(Settings.ACCOUNT_TYPE, accountType);
+ if (dataSet != null) {
+ settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
+ }
+ final Cursor cursor = resolver.query(settingsUri.build(), new String[] {
+ Settings.SHOULD_SYNC, Settings.UNGROUPED_VISIBLE
+ }, null, null, null);
+
+ try {
+ final ContentValues values = new ContentValues();
+ values.put(Settings.ACCOUNT_NAME, accountName);
+ values.put(Settings.ACCOUNT_TYPE, accountType);
+ values.put(Settings.DATA_SET, dataSet);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ // Read existing values when present
+ values.put(Settings.SHOULD_SYNC, cursor.getInt(0));
+ values.put(Settings.UNGROUPED_VISIBLE, cursor.getInt(1));
+ return fromBefore(values).setUngrouped(accountHasGroups);
+ } else {
+ // Nothing found, so treat as create
+ values.put(Settings.SHOULD_SYNC, DEFAULT_SHOULD_SYNC);
+ values.put(Settings.UNGROUPED_VISIBLE, DEFAULT_VISIBLE);
+ return fromAfter(values).setUngrouped(accountHasGroups);
+ }
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ }
+
+ public static GroupDelta fromBefore(ContentValues before) {
+ final GroupDelta entry = new GroupDelta();
+ entry.mBefore = before;
+ entry.mAfter = new ContentValues();
+ return entry;
+ }
+
+ public static GroupDelta fromAfter(ContentValues after) {
+ final GroupDelta entry = new GroupDelta();
+ entry.mBefore = null;
+ entry.mAfter = after;
+ return entry;
+ }
+
+ protected GroupDelta setUngrouped(boolean accountHasGroups) {
+ mUngrouped = true;
+ mAccountHasGroups = accountHasGroups;
+ return this;
+ }
+
+ @Override
+ public boolean beforeExists() {
+ return mBefore != null;
+ }
+
+ public boolean getShouldSync() {
+ return getAsInteger(mUngrouped ? Settings.SHOULD_SYNC : Groups.SHOULD_SYNC,
+ DEFAULT_SHOULD_SYNC) != 0;
+ }
+
+ public boolean getVisible() {
+ return getAsInteger(mUngrouped ? Settings.UNGROUPED_VISIBLE : Groups.GROUP_VISIBLE,
+ DEFAULT_VISIBLE) != 0;
+ }
+
+ public void putShouldSync(boolean shouldSync) {
+ put(mUngrouped ? Settings.SHOULD_SYNC : Groups.SHOULD_SYNC, shouldSync ? 1 : 0);
+ }
+
+ public void putVisible(boolean visible) {
+ put(mUngrouped ? Settings.UNGROUPED_VISIBLE : Groups.GROUP_VISIBLE, visible ? 1 : 0);
+ }
+
+ private String getAccountType() {
+ return (mBefore == null ? mAfter : mBefore).getAsString(Settings.ACCOUNT_TYPE);
+ }
+
+ public CharSequence getTitle(Context context) {
+ if (mUngrouped) {
+ final String customAllContactsName =
+ LocalizedNameResolver.getAllContactsName(context, getAccountType());
+ if (customAllContactsName != null) {
+ return customAllContactsName;
+ }
+ if (mAccountHasGroups) {
+ return context.getText(R.string.display_ungrouped);
+ } else {
+ return context.getText(R.string.display_all_contacts);
+ }
+ } else {
+ final Integer titleRes = getAsInteger(Groups.TITLE_RES);
+ if (titleRes != null) {
+ final String packageName = getAsString(Groups.RES_PACKAGE);
+ return context.getPackageManager().getText(packageName, titleRes, null);
+ } else {
+ return getAsString(Groups.TITLE);
+ }
+ }
+ }
+
+ /**
+ * Build a possible {@link ContentProviderOperation} to persist any
+ * changes to the {@link Groups} or {@link Settings} row described by
+ * this {@link GroupDelta}.
+ */
+ public ContentProviderOperation buildDiff() {
+ if (isInsert()) {
+ // Only allow inserts for Settings
+ if (mUngrouped) {
+ mAfter.remove(mIdColumn);
+ return ContentProviderOperation.newInsert(Settings.CONTENT_URI)
+ .withValues(mAfter)
+ .build();
+ }
+ else {
+ throw new IllegalStateException("Unexpected diff");
+ }
+ } else if (isUpdate()) {
+ if (mUngrouped) {
+ String accountName = this.getAsString(Settings.ACCOUNT_NAME);
+ String accountType = this.getAsString(Settings.ACCOUNT_TYPE);
+ String dataSet = this.getAsString(Settings.DATA_SET);
+ StringBuilder selection = new StringBuilder(Settings.ACCOUNT_NAME + "=? AND "
+ + Settings.ACCOUNT_TYPE + "=?");
+ String[] selectionArgs;
+ if (dataSet == null) {
+ selection.append(" AND " + Settings.DATA_SET + " IS NULL");
+ selectionArgs = new String[] {accountName, accountType};
+ } else {
+ selection.append(" AND " + Settings.DATA_SET + "=?");
+ selectionArgs = new String[] {accountName, accountType, dataSet};
+ }
+ return ContentProviderOperation.newUpdate(Settings.CONTENT_URI)
+ .withSelection(selection.toString(), selectionArgs)
+ .withValues(mAfter)
+ .build();
+ } else {
+ return ContentProviderOperation.newUpdate(
+ addCallerIsSyncAdapterParameter(Groups.CONTENT_URI))
+ .withSelection(Groups._ID + "=" + this.getId(), null)
+ .withValues(mAfter)
+ .build();
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private static Uri addCallerIsSyncAdapterParameter(Uri uri) {
+ return uri.buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .build();
+ }
+
+ /**
+ * {@link Comparator} to sort by {@link Groups#_ID}.
+ */
+ private static Comparator sIdComparator = new Comparator() {
+ public int compare(GroupDelta object1, GroupDelta object2) {
+ final Long id1 = object1.getId();
+ final Long id2 = object2.getId();
+ if (id1 == null && id2 == null) {
+ return 0;
+ } else if (id1 == null) {
+ return -1;
+ } else if (id2 == null) {
+ return 1;
+ } else if (id1 < id2) {
+ return -1;
+ } else if (id1 > id2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+ /**
+ * Set of all {@link AccountDisplay} entries, one for each source.
+ */
+ protected static class AccountSet extends ArrayList {
+ public ArrayList buildDiff() {
+ final ArrayList diff = Lists.newArrayList();
+ for (AccountDisplay account : this) {
+ account.buildDiff(diff);
+ }
+ return diff;
+ }
+ }
+
+ /**
+ * {@link GroupDelta} details for a single {@link AccountWithDataSet}, usually shown as
+ * children under a single expandable group.
+ */
+ protected static class AccountDisplay {
+ public final String mName;
+ public final String mType;
+ public final String mDataSet;
+
+ public GroupDelta mUngrouped;
+ public ArrayList mSyncedGroups = Lists.newArrayList();
+ public ArrayList mUnsyncedGroups = Lists.newArrayList();
+
+ /**
+ * Build an {@link AccountDisplay} covering all {@link Groups} under the
+ * given {@link AccountWithDataSet}.
+ */
+ public AccountDisplay(ContentResolver resolver, String accountName, String accountType,
+ String dataSet) {
+ mName = accountName;
+ mType = accountType;
+ mDataSet = dataSet;
+ }
+
+ /**
+ * Add the given {@link GroupDelta} internally, filing based on its
+ * {@link GroupDelta#getShouldSync()} status.
+ */
+ private void addGroup(GroupDelta group) {
+ if (group.getShouldSync()) {
+ mSyncedGroups.add(group);
+ } else {
+ mUnsyncedGroups.add(group);
+ }
+ }
+
+ /**
+ * Set the {@link GroupDelta#putShouldSync(boolean)} value for all
+ * children {@link GroupDelta} rows.
+ */
+ public void setShouldSync(boolean shouldSync) {
+ final Iterator oppositeChildren = shouldSync ?
+ mUnsyncedGroups.iterator() : mSyncedGroups.iterator();
+ while (oppositeChildren.hasNext()) {
+ final GroupDelta child = oppositeChildren.next();
+ setShouldSync(child, shouldSync, false);
+ oppositeChildren.remove();
+ }
+ }
+
+ public void setShouldSync(GroupDelta child, boolean shouldSync) {
+ setShouldSync(child, shouldSync, true);
+ }
+
+ /**
+ * Set {@link GroupDelta#putShouldSync(boolean)}, and file internally
+ * based on updated state.
+ */
+ public void setShouldSync(GroupDelta child, boolean shouldSync, boolean attemptRemove) {
+ child.putShouldSync(shouldSync);
+ if (shouldSync) {
+ if (attemptRemove) {
+ mUnsyncedGroups.remove(child);
+ }
+ mSyncedGroups.add(child);
+ Collections.sort(mSyncedGroups, sIdComparator);
+ } else {
+ if (attemptRemove) {
+ mSyncedGroups.remove(child);
+ }
+ mUnsyncedGroups.add(child);
+ }
+ }
+
+ /**
+ * Build set of {@link ContentProviderOperation} to persist any user
+ * changes to {@link GroupDelta} rows under this {@link AccountWithDataSet}.
+ */
+ public void buildDiff(ArrayList diff) {
+ for (GroupDelta group : mSyncedGroups) {
+ final ContentProviderOperation oper = group.buildDiff();
+ if (oper != null) diff.add(oper);
+ }
+ for (GroupDelta group : mUnsyncedGroups) {
+ final ContentProviderOperation oper = group.buildDiff();
+ if (oper != null) diff.add(oper);
+ }
+ }
+ }
+
+ /**
+ * {@link ExpandableListAdapter} that shows {@link GroupDelta} settings,
+ * grouped by {@link AccountWithDataSet} type. Shows footer row when any groups are
+ * unsynced, as determined through {@link AccountDisplay#mUnsyncedGroups}.
+ */
+ protected static class DisplayAdapter extends BaseExpandableListAdapter {
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private AccountTypeManager mAccountTypes;
+ private AccountSet mAccounts;
+
+ private boolean mChildWithPhones = false;
+
+ public DisplayAdapter(Context context) {
+ mContext = context;
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mAccountTypes = AccountTypeManager.getInstance(context);
+ }
+
+ public void setAccounts(AccountSet accounts) {
+ mAccounts = accounts;
+ notifyDataSetChanged();
+ }
+
+ /**
+ * In group descriptions, show the number of contacts with phone
+ * numbers, in addition to the total contacts.
+ */
+ public void setChildDescripWithPhones(boolean withPhones) {
+ mChildWithPhones = withPhones;
+ }
+
+ @Override
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(
+ R.layout.custom_contact_list_filter_account, parent, false);
+ }
+
+ final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
+
+ final AccountDisplay account = (AccountDisplay)this.getGroup(groupPosition);
+
+ final AccountType accountType = mAccountTypes.getAccountType(
+ account.mType, account.mDataSet);
+
+ text1.setText(account.mName);
+ text1.setVisibility(account.mName == null ? View.GONE : View.VISIBLE);
+ text2.setText(accountType.getDisplayLabel(mContext));
+
+ return convertView;
+ }
+
+ @Override
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(
+ R.layout.custom_contact_list_filter_group, parent, false);
+ }
+
+ final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
+ final CheckBox checkbox = (CheckBox)convertView.findViewById(android.R.id.checkbox);
+
+ final AccountDisplay account = mAccounts.get(groupPosition);
+ final GroupDelta child = (GroupDelta)this.getChild(groupPosition, childPosition);
+ if (child != null) {
+ // Handle normal group, with title and checkbox
+ final boolean groupVisible = child.getVisible();
+ checkbox.setVisibility(View.VISIBLE);
+ checkbox.setChecked(groupVisible);
+
+ final CharSequence groupTitle = child.getTitle(mContext);
+ text1.setText(groupTitle);
+ text2.setVisibility(View.GONE);
+ } else {
+ // When unknown child, this is "more" footer view
+ checkbox.setVisibility(View.GONE);
+ text1.setText(R.string.display_more_groups);
+ text2.setVisibility(View.GONE);
+ }
+
+ return convertView;
+ }
+
+ @Override
+ public Object getChild(int groupPosition, int childPosition) {
+ final AccountDisplay account = mAccounts.get(groupPosition);
+ final boolean validChild = childPosition >= 0
+ && childPosition < account.mSyncedGroups.size();
+ if (validChild) {
+ return account.mSyncedGroups.get(childPosition);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ final GroupDelta child = (GroupDelta)getChild(groupPosition, childPosition);
+ if (child != null) {
+ final Long childId = child.getId();
+ return childId != null ? childId : Long.MIN_VALUE;
+ } else {
+ return Long.MIN_VALUE;
+ }
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ // Count is any synced groups, plus possible footer
+ final AccountDisplay account = mAccounts.get(groupPosition);
+ final boolean anyHidden = account.mUnsyncedGroups.size() > 0;
+ return account.mSyncedGroups.size() + (anyHidden ? 1 : 0);
+ }
+
+ @Override
+ public Object getGroup(int groupPosition) {
+ return mAccounts.get(groupPosition);
+ }
+
+ @Override
+ public int getGroupCount() {
+ if (mAccounts == null) {
+ return 0;
+ }
+ return mAccounts.size();
+ }
+
+ @Override
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onClick(View view) {
+ int i = view.getId();
+ if (i == R.id.btn_done) {
+ this.doSaveAction();
+ } else if (i == R.id.btn_discard) {
+ this.finish();
+ }
+ }
+
+ /**
+ * Handle any clicks on {@link ExpandableListAdapter} children, which
+ * usually mean toggling its visible state.
+ */
+ @Override
+ public boolean onChildClick(ExpandableListView parent, View view, int groupPosition,
+ int childPosition, long id) {
+ final CheckBox checkbox = (CheckBox)view.findViewById(android.R.id.checkbox);
+
+ final AccountDisplay account = (AccountDisplay)mAdapter.getGroup(groupPosition);
+ final GroupDelta child = (GroupDelta)mAdapter.getChild(groupPosition, childPosition);
+ if (child != null) {
+ checkbox.toggle();
+ child.putVisible(checkbox.isChecked());
+ } else {
+ // Open context menu for bringing back unsynced
+ this.openContextMenu(view);
+ }
+ return true;
+ }
+
+ // TODO: move these definitions to framework constants when we begin
+ // defining this mode through tags
+ private static final int SYNC_MODE_UNSUPPORTED = 0;
+ private static final int SYNC_MODE_UNGROUPED = 1;
+ private static final int SYNC_MODE_EVERYTHING = 2;
+
+ protected int getSyncMode(AccountDisplay account) {
+ // TODO: read sync mode through definition
+ if (GoogleAccountType.ACCOUNT_TYPE.equals(account.mType) && account.mDataSet == null) {
+ return SYNC_MODE_EVERYTHING;
+ } else {
+ return SYNC_MODE_UNSUPPORTED;
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view,
+ ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ // Bail if not working with expandable long-press, or if not child
+ if (!(menuInfo instanceof ExpandableListContextMenuInfo)) return;
+
+ final ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
+ final int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+ final int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
+
+ // Skip long-press on expandable parents
+ if (childPosition == -1) return;
+
+ final AccountDisplay account = (AccountDisplay)mAdapter.getGroup(groupPosition);
+ final GroupDelta child = (GroupDelta)mAdapter.getChild(groupPosition, childPosition);
+
+ // Ignore when selective syncing unsupported
+ final int syncMode = getSyncMode(account);
+ if (syncMode == SYNC_MODE_UNSUPPORTED) return;
+
+ if (child != null) {
+ showRemoveSync(menu, account, child, syncMode);
+ } else {
+ showAddSync(menu, account, syncMode);
+ }
+ }
+
+ protected void showRemoveSync(ContextMenu menu, final AccountDisplay account,
+ final GroupDelta child, final int syncMode) {
+ final CharSequence title = child.getTitle(this);
+
+ menu.setHeaderTitle(title);
+ menu.add(R.string.menu_sync_remove).setOnMenuItemClickListener(
+ new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ handleRemoveSync(account, child, syncMode, title);
+ return true;
+ }
+ });
+ }
+
+ protected void handleRemoveSync(final AccountDisplay account, final GroupDelta child,
+ final int syncMode, CharSequence title) {
+ final boolean shouldSyncUngrouped = account.mUngrouped.getShouldSync();
+ if (syncMode == SYNC_MODE_EVERYTHING && shouldSyncUngrouped
+ && !child.equals(account.mUngrouped)) {
+ // Warn before removing this group when it would cause ungrouped to stop syncing
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ final CharSequence removeMessage = this.getString(
+ R.string.display_warn_remove_ungrouped, title);
+ builder.setTitle(R.string.menu_sync_remove);
+ builder.setMessage(removeMessage);
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // Mark both this group and ungrouped to stop syncing
+ account.setShouldSync(account.mUngrouped, false);
+ account.setShouldSync(child, false);
+ mAdapter.notifyDataSetChanged();
+ }
+ });
+ builder.show();
+ } else {
+ // Mark this group to not sync
+ account.setShouldSync(child, false);
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ protected void showAddSync(ContextMenu menu, final AccountDisplay account, final int syncMode) {
+ menu.setHeaderTitle(R.string.dialog_sync_add);
+
+ // Create item for each available, unsynced group
+ for (final GroupDelta child : account.mUnsyncedGroups) {
+ if (!child.getShouldSync()) {
+ final CharSequence title = child.getTitle(this);
+ menu.add(title).setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ // Adding specific group for syncing
+ if (child.mUngrouped && syncMode == SYNC_MODE_EVERYTHING) {
+ account.setShouldSync(true);
+ } else {
+ account.setShouldSync(child, true);
+ }
+ mAdapter.notifyDataSetChanged();
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void doSaveAction() {
+ if (mAdapter == null || mAdapter.mAccounts == null) {
+ finish();
+ return;
+ }
+
+ setResult(RESULT_OK);
+
+ final ArrayList diff = mAdapter.mAccounts.buildDiff();
+ if (diff.isEmpty()) {
+ finish();
+ return;
+ }
+
+ new UpdateTask(this).execute(diff);
+ }
+
+ /**
+ * Background task that persists changes to {@link Groups#GROUP_VISIBLE},
+ * showing spinner dialog to user while updating.
+ */
+ public static class UpdateTask extends
+ WeakAsyncTask, Void, Void, Activity> {
+ private ProgressDialog mProgress;
+
+ public UpdateTask(Activity target) {
+ super(target);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPreExecute(Activity target) {
+ final Context context = target;
+
+ mProgress = ProgressDialog.show(
+ context, null, context.getText(R.string.savingDisplayGroups));
+
+ // Before starting this task, start an empty service to protect our
+ // process from being reclaimed by the system.
+ context.startService(new Intent(context, EmptyService.class));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Void doInBackground(
+ Activity target, ArrayList... params) {
+ final Context context = target;
+ final ContentValues values = new ContentValues();
+ final ContentResolver resolver = context.getContentResolver();
+
+ try {
+ final ArrayList diff = params[0];
+ resolver.applyBatch(ContactsContract.AUTHORITY, diff);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Problem saving display groups", e);
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, "Problem saving display groups", e);
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPostExecute(Activity target, Void result) {
+ final Context context = target;
+
+ try {
+ mProgress.dismiss();
+ } catch (Exception e) {
+ Log.e(TAG, "Error dismissing progress dialog", e);
+ }
+
+ target.finish();
+
+ // Stop the service that was protecting us
+ context.stopService(new Intent(context, EmptyService.class));
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // Pretend cancel.
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return true;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/DefaultContactListAdapter.java b/ContactsCommon/src/com/android/contacts/common/list/DefaultContactListAdapter.java
new file mode 100644
index 0000000..8774777
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/DefaultContactListAdapter.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.SearchSnippets;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.contacts.common.preference.ContactsPreferences;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
+ */
+public class DefaultContactListAdapter extends ContactListAdapter {
+
+ public static final char SNIPPET_START_MATCH = '[';
+ public static final char SNIPPET_END_MATCH = ']';
+
+ public DefaultContactListAdapter(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ if (loader instanceof ProfileAndContactsLoader) {
+ ((ProfileAndContactsLoader) loader).setLoadProfile(shouldIncludeProfile());
+ }
+
+ ContactListFilter filter = getFilter();
+ if (isSearchMode()) {
+ String query = getQueryString();
+ if (query == null) {
+ query = "";
+ }
+ query = query.trim();
+ if (TextUtils.isEmpty(query)) {
+ // Regardless of the directory, we don't want anything returned,
+ // so let's just send a "nothing" query to the local directory.
+ loader.setUri(Contacts.CONTENT_URI);
+ loader.setProjection(getProjection(false));
+ loader.setSelection("0");
+ } else {
+ Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon();
+ builder.appendPath(query); // Builder will encode the query
+ builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId));
+ if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE) {
+ builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId))));
+ }
+ builder.appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY,"1");
+ loader.setUri(builder.build());
+ loader.setProjection(getProjection(true));
+ }
+ } else {
+ configureUri(loader, directoryId, filter);
+ loader.setProjection(getProjection(false));
+ configureSelection(loader, directoryId, filter);
+ }
+
+ String sortOrder;
+ if (getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
+ sortOrder = Contacts.SORT_KEY_PRIMARY;
+ } else {
+ sortOrder = Contacts.SORT_KEY_ALTERNATIVE;
+ }
+
+ loader.setSortOrder(sortOrder);
+ }
+
+ protected void configureUri(CursorLoader loader, long directoryId, ContactListFilter filter) {
+ Uri uri = Contacts.CONTENT_URI;
+ if (filter != null && filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ String lookupKey = getSelectedContactLookupKey();
+ if (lookupKey != null) {
+ uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
+ } else {
+ uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, getSelectedContactId());
+ }
+ }
+
+ if (directoryId == Directory.DEFAULT && isSectionHeaderDisplayEnabled()) {
+ uri = ContactListAdapter.buildSectionIndexerUri(uri);
+ }
+
+ // The "All accounts" filter is the same as the entire contents of Directory.DEFAULT
+ if (filter != null
+ && filter.filterType != ContactListFilter.FILTER_TYPE_CUSTOM
+ && filter.filterType != ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ final Uri.Builder builder = uri.buildUpon();
+ builder.appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT));
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ filter.addAccountQueryParameterToUrl(builder);
+ }
+ uri = builder.build();
+ }
+
+ loader.setUri(uri);
+ }
+
+ private void configureSelection(
+ CursorLoader loader, long directoryId, ContactListFilter filter) {
+ if (filter == null) {
+ return;
+ }
+
+ if (directoryId != Directory.DEFAULT) {
+ return;
+ }
+
+ StringBuilder selection = new StringBuilder();
+ List selectionArgs = new ArrayList();
+
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: {
+ // We have already added directory=0 to the URI, which takes care of this
+ // filter
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT: {
+ // We have already added the lookup key to the URI, which takes care of this
+ // filter
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_STARRED: {
+ selection.append(Contacts.STARRED + "!=0");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: {
+ selection.append(Contacts.HAS_PHONE_NUMBER + "=1");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_CUSTOM: {
+ selection.append(Contacts.IN_VISIBLE_GROUP + "=1");
+ if (isCustomFilterForPhoneNumbersOnly()) {
+ selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
+ }
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT: {
+ // We use query parameters for account filter, so no selection to add here.
+ break;
+ }
+ }
+ loader.setSelection(selection.toString());
+ loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ super.bindView(itemView, partition, cursor, position);
+ final ContactListItemView view = (ContactListItemView)itemView;
+
+ view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null);
+
+ if (isSelectionVisible()) {
+ view.setActivated(isSelectedContact(partition, cursor));
+ }
+
+ bindSectionHeaderAndDivider(view, position, cursor);
+
+ if (isQuickContactEnabled()) {
+ bindQuickContact(view, partition, cursor, ContactQuery.CONTACT_PHOTO_ID,
+ ContactQuery.CONTACT_PHOTO_URI, ContactQuery.CONTACT_ID,
+ ContactQuery.CONTACT_LOOKUP_KEY, ContactQuery.CONTACT_DISPLAY_NAME);
+ } else {
+ if (getDisplayPhotos()) {
+ bindPhoto(view, partition, cursor);
+ }
+ }
+
+ bindNameAndViewId(view, cursor);
+ bindPresenceAndStatusMessage(view, cursor);
+
+ if (isSearchMode()) {
+ bindSearchSnippet(view, cursor);
+ } else {
+ view.setSnippet(null);
+ }
+ }
+
+ private boolean isCustomFilterForPhoneNumbersOnly() {
+ // TODO: this flag should not be stored in shared prefs. It needs to be in the db.
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ return prefs.getBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES,
+ ContactsPreferences.PREF_DISPLAY_ONLY_PHONES_DEFAULT);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/DirectoryListLoader.java b/ContactsCommon/src/com/android/contacts/common/list/DirectoryListLoader.java
new file mode 100644
index 0000000..b327361
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/DirectoryListLoader.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.contacts.common.R;
+
+/**
+ * A specialized loader for the list of directories, see {@link Directory}.
+ */
+public class DirectoryListLoader extends AsyncTaskLoader {
+
+ private static final String TAG = "ContactEntryListAdapter";
+
+ public static final int SEARCH_MODE_NONE = 0;
+ public static final int SEARCH_MODE_DEFAULT = 1;
+ public static final int SEARCH_MODE_CONTACT_SHORTCUT = 2;
+ public static final int SEARCH_MODE_DATA_SHORTCUT = 3;
+
+ private static final class DirectoryQuery {
+ public static final Uri URI = Directory.CONTENT_URI;
+ public static final String ORDER_BY = Directory._ID;
+
+ public static final String[] PROJECTION = {
+ Directory._ID,
+ Directory.PACKAGE_NAME,
+ Directory.TYPE_RESOURCE_ID,
+ Directory.DISPLAY_NAME,
+ Directory.PHOTO_SUPPORT,
+ };
+
+ public static final int ID = 0;
+ public static final int PACKAGE_NAME = 1;
+ public static final int TYPE_RESOURCE_ID = 2;
+ public static final int DISPLAY_NAME = 3;
+ public static final int PHOTO_SUPPORT = 4;
+ }
+
+ // This is a virtual column created for a MatrixCursor.
+ public static final String DIRECTORY_TYPE = "directoryType";
+
+ private static final String[] RESULT_PROJECTION = {
+ Directory._ID,
+ DIRECTORY_TYPE,
+ Directory.DISPLAY_NAME,
+ Directory.PHOTO_SUPPORT,
+ };
+
+ private final ContentObserver mObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ forceLoad();
+ }
+ };
+
+ private int mDirectorySearchMode;
+ private boolean mLocalInvisibleDirectoryEnabled;
+
+ private MatrixCursor mDefaultDirectoryList;
+
+ public DirectoryListLoader(Context context) {
+ super(context);
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ /**
+ * A flag that indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should
+ * be included in the results.
+ */
+ public void setLocalInvisibleDirectoryEnabled(boolean flag) {
+ this.mLocalInvisibleDirectoryEnabled = flag;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ getContext().getContentResolver().
+ registerContentObserver(Directory.CONTENT_URI, false, mObserver);
+ forceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ if (mDirectorySearchMode == SEARCH_MODE_NONE) {
+ return getDefaultDirectories();
+ }
+
+ MatrixCursor result = new MatrixCursor(RESULT_PROJECTION);
+ Context context = getContext();
+ PackageManager pm = context.getPackageManager();
+ String selection;
+ switch (mDirectorySearchMode) {
+ case SEARCH_MODE_DEFAULT:
+ selection = mLocalInvisibleDirectoryEnabled ? null
+ : (Directory._ID + "!=" + Directory.LOCAL_INVISIBLE);
+ break;
+
+ case SEARCH_MODE_CONTACT_SHORTCUT:
+ selection = Directory.SHORTCUT_SUPPORT + "=" + Directory.SHORTCUT_SUPPORT_FULL
+ + (mLocalInvisibleDirectoryEnabled ? ""
+ : (" AND " + Directory._ID + "!=" + Directory.LOCAL_INVISIBLE));
+ break;
+
+ case SEARCH_MODE_DATA_SHORTCUT:
+ selection = Directory.SHORTCUT_SUPPORT + " IN ("
+ + Directory.SHORTCUT_SUPPORT_FULL + ", "
+ + Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY + ")"
+ + (mLocalInvisibleDirectoryEnabled ? ""
+ : (" AND " + Directory._ID + "!=" + Directory.LOCAL_INVISIBLE));
+ break;
+
+ default:
+ throw new RuntimeException(
+ "Unsupported directory search mode: " + mDirectorySearchMode);
+ }
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(DirectoryQuery.URI,
+ DirectoryQuery.PROJECTION, selection, null, DirectoryQuery.ORDER_BY);
+
+ if (cursor == null) {
+ return result;
+ }
+
+ while(cursor.moveToNext()) {
+ long directoryId = cursor.getLong(DirectoryQuery.ID);
+ String directoryType = null;
+
+ String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
+ int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
+ if (!TextUtils.isEmpty(packageName) && typeResourceId != 0) {
+ try {
+ directoryType = pm.getResourcesForApplication(packageName)
+ .getString(typeResourceId);
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot obtain directory type from package: " + packageName);
+ }
+ }
+ String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
+ int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT);
+ result.addRow(new Object[]{directoryId, directoryType, displayName, photoSupport});
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Runtime Exception when querying directory");
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return result;
+ }
+
+ private Cursor getDefaultDirectories() {
+ if (mDefaultDirectoryList == null) {
+ mDefaultDirectoryList = new MatrixCursor(RESULT_PROJECTION);
+ mDefaultDirectoryList.addRow(new Object[] {
+ Directory.DEFAULT,
+ getContext().getString(R.string.contactsList),
+ null
+ });
+ mDefaultDirectoryList.addRow(new Object[] {
+ Directory.LOCAL_INVISIBLE,
+ getContext().getString(R.string.local_invisible_directory),
+ null
+ });
+ }
+ return mDefaultDirectoryList;
+ }
+
+ @Override
+ protected void onReset() {
+ stopLoading();
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/DirectoryPartition.java b/ContactsCommon/src/com/android/contacts/common/list/DirectoryPartition.java
new file mode 100644
index 0000000..ca0dc11
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/DirectoryPartition.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.provider.ContactsContract.Directory;
+
+import com.android.common.widget.CompositeCursorAdapter;
+
+/**
+ * Model object for a {@link Directory} row.
+ */
+public final class DirectoryPartition extends CompositeCursorAdapter.Partition {
+
+ public static final int STATUS_NOT_LOADED = 0;
+ public static final int STATUS_LOADING = 1;
+ public static final int STATUS_LOADED = 2;
+
+ public static final int RESULT_LIMIT_DEFAULT = -1;
+
+ private long mDirectoryId;
+ private String mContentUri;
+ private String mDirectoryType;
+ private String mDisplayName;
+ private int mStatus;
+ private boolean mPriorityDirectory;
+ private boolean mPhotoSupported;
+ private int mResultLimit = RESULT_LIMIT_DEFAULT;
+ private boolean mDisplayNumber = true;
+
+ private String mLabel;
+
+ public DirectoryPartition(boolean showIfEmpty, boolean hasHeader) {
+ super(showIfEmpty, hasHeader);
+ }
+
+ /**
+ * Directory ID, see {@link Directory}.
+ */
+ public long getDirectoryId() {
+ return mDirectoryId;
+ }
+
+ public void setDirectoryId(long directoryId) {
+ this.mDirectoryId = directoryId;
+ }
+
+ /**
+ * Directory type resolved from {@link Directory#PACKAGE_NAME} and
+ * {@link Directory#TYPE_RESOURCE_ID};
+ */
+ public String getDirectoryType() {
+ return mDirectoryType;
+ }
+
+ public void setDirectoryType(String directoryType) {
+ this.mDirectoryType = directoryType;
+ }
+
+ /**
+ * See {@link Directory#DISPLAY_NAME}.
+ */
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.mDisplayName = displayName;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public void setStatus(int status) {
+ mStatus = status;
+ }
+
+ public boolean isLoading() {
+ return mStatus == STATUS_NOT_LOADED || mStatus == STATUS_LOADING;
+ }
+
+ /**
+ * Returns true if this directory should be loaded before non-priority directories.
+ */
+ public boolean isPriorityDirectory() {
+ return mPriorityDirectory;
+ }
+
+ public void setPriorityDirectory(boolean priorityDirectory) {
+ mPriorityDirectory = priorityDirectory;
+ }
+
+ /**
+ * Returns true if this directory supports photos.
+ */
+ public boolean isPhotoSupported() {
+ return mPhotoSupported;
+ }
+
+ public void setPhotoSupported(boolean flag) {
+ this.mPhotoSupported = flag;
+ }
+
+ /**
+ * Max number of results for this directory. Defaults to {@link #RESULT_LIMIT_DEFAULT} which
+ * implies using the adapter's
+ * {@link com.android.contacts.common.list.ContactListAdapter#getDirectoryResultLimit()}
+ */
+ public int getResultLimit() {
+ return mResultLimit;
+ }
+
+ public void setResultLimit(int resultLimit) {
+ mResultLimit = resultLimit;
+ }
+
+ /**
+ * Used by extended directories to specify a custom content URI. Extended directories MUST have
+ * a content URI
+ */
+ public String getContentUri() {
+ return mContentUri;
+ }
+
+ public void setContentUri(String contentUri) {
+ mContentUri = contentUri;
+ }
+
+ /**
+ * A label to display in the header next to the display name.
+ */
+ public String getLabel() {
+ return mLabel;
+ }
+
+ public void setLabel(String label) {
+ mLabel = label;
+ }
+
+ @Override
+ public String toString() {
+ return "DirectoryPartition{" +
+ "mDirectoryId=" + mDirectoryId +
+ ", mContentUri='" + mContentUri + '\'' +
+ ", mDirectoryType='" + mDirectoryType + '\'' +
+ ", mDisplayName='" + mDisplayName + '\'' +
+ ", mStatus=" + mStatus +
+ ", mPriorityDirectory=" + mPriorityDirectory +
+ ", mPhotoSupported=" + mPhotoSupported +
+ ", mResultLimit=" + mResultLimit +
+ ", mLabel='" + mLabel + '\'' +
+ '}';
+ }
+
+ /**
+ * Whether or not to display the phone number in app that have that option - Dialer. If false,
+ * Phone Label should be used instead of Phone Number.
+ */
+ public boolean isDisplayNumber() {
+ return mDisplayNumber;
+ }
+
+ public void setDisplayNumber(boolean displayNumber) {
+ mDisplayNumber = displayNumber;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/IndexerListAdapter.java b/ContactsCommon/src/com/android/contacts/common/list/IndexerListAdapter.java
new file mode 100644
index 0000000..032bb53
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/IndexerListAdapter.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+
+/**
+ * A list adapter that supports section indexer and a pinned header.
+ */
+public abstract class IndexerListAdapter extends PinnedHeaderListAdapter implements SectionIndexer {
+
+ protected Context mContext;
+ private SectionIndexer mIndexer;
+ private int mIndexedPartition = 0;
+ private boolean mSectionHeaderDisplayEnabled;
+ private View mHeader;
+
+ /**
+ * An item view is displayed differently depending on whether it is placed
+ * at the beginning, middle or end of a section. It also needs to know the
+ * section header when it is at the beginning of a section. This object
+ * captures all this configuration.
+ */
+ public static final class Placement {
+ private int position = ListView.INVALID_POSITION;
+ public boolean firstInSection;
+ public boolean lastInSection;
+ public String sectionHeader;
+
+ public void invalidate() {
+ position = ListView.INVALID_POSITION;
+ }
+ }
+
+ private Placement mPlacementCache = new Placement();
+
+ /**
+ * Constructor.
+ */
+ public IndexerListAdapter(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * Creates a section header view that will be pinned at the top of the list
+ * as the user scrolls.
+ */
+ protected abstract View createPinnedSectionHeaderView(Context context, ViewGroup parent);
+
+ /**
+ * Sets the title in the pinned header as the user scrolls.
+ */
+ protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title);
+
+ public boolean isSectionHeaderDisplayEnabled() {
+ return mSectionHeaderDisplayEnabled;
+ }
+
+ public void setSectionHeaderDisplayEnabled(boolean flag) {
+ this.mSectionHeaderDisplayEnabled = flag;
+ }
+
+ public int getIndexedPartition() {
+ return mIndexedPartition;
+ }
+
+ public void setIndexedPartition(int partition) {
+ this.mIndexedPartition = partition;
+ }
+
+ public SectionIndexer getIndexer() {
+ return mIndexer;
+ }
+
+ public void setIndexer(SectionIndexer indexer) {
+ mIndexer = indexer;
+ mPlacementCache.invalidate();
+ }
+
+ public Object[] getSections() {
+ if (mIndexer == null) {
+ return new String[] { " " };
+ } else {
+ return mIndexer.getSections();
+ }
+ }
+
+ /**
+ * @return relative position of the section in the indexed partition
+ */
+ public int getPositionForSection(int sectionIndex) {
+ if (mIndexer == null) {
+ return -1;
+ }
+
+ return mIndexer.getPositionForSection(sectionIndex);
+ }
+
+ /**
+ * @param position relative position in the indexed partition
+ */
+ public int getSectionForPosition(int position) {
+ if (mIndexer == null) {
+ return -1;
+ }
+
+ return mIndexer.getSectionForPosition(position);
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ if (isSectionHeaderDisplayEnabled()) {
+ return super.getPinnedHeaderCount() + 1;
+ } else {
+ return super.getPinnedHeaderCount();
+ }
+ }
+
+ @Override
+ public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
+ if (isSectionHeaderDisplayEnabled() && viewIndex == getPinnedHeaderCount() - 1) {
+ if (mHeader == null) {
+ mHeader = createPinnedSectionHeaderView(mContext, parent);
+ }
+ return mHeader;
+ } else {
+ return super.getPinnedHeaderView(viewIndex, convertView, parent);
+ }
+ }
+
+ @Override
+ public void configurePinnedHeaders(PinnedHeaderListView listView) {
+ super.configurePinnedHeaders(listView);
+
+ if (!isSectionHeaderDisplayEnabled()) {
+ return;
+ }
+
+ int index = getPinnedHeaderCount() - 1;
+ if (mIndexer == null || getCount() == 0) {
+ listView.setHeaderInvisible(index, false);
+ } else {
+ int listPosition = listView.getPositionAt(listView.getTotalTopPinnedHeaderHeight());
+ int position = listPosition - listView.getHeaderViewsCount();
+
+ int section = -1;
+ int partition = getPartitionForPosition(position);
+ if (partition == mIndexedPartition) {
+ int offset = getOffsetInPartition(position);
+ if (offset != -1) {
+ section = getSectionForPosition(offset);
+ }
+ }
+
+ if (section == -1) {
+ listView.setHeaderInvisible(index, false);
+ } else {
+ View topChild = listView.getChildAt(listPosition);
+ if (topChild != null) {
+ // Match the pinned header's height to the height of the list item.
+ mHeader.setMinimumHeight(topChild.getMeasuredHeight());
+ }
+ setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]);
+
+ // Compute the item position where the current partition begins
+ int partitionStart = getPositionForPartition(mIndexedPartition);
+ if (hasHeader(mIndexedPartition)) {
+ partitionStart++;
+ }
+
+ // Compute the item position where the next section begins
+ int nextSectionPosition = partitionStart + getPositionForSection(section + 1);
+ boolean isLastInSection = position == nextSectionPosition - 1;
+ listView.setFadingHeader(index, listPosition, isLastInSection);
+ }
+ }
+ }
+
+ /**
+ * Computes the item's placement within its section and populates the {@code placement}
+ * object accordingly. Please note that the returned object is volatile and should be
+ * copied if the result needs to be used later.
+ */
+ public Placement getItemPlacementInSection(int position) {
+ if (mPlacementCache.position == position) {
+ return mPlacementCache;
+ }
+
+ mPlacementCache.position = position;
+ if (isSectionHeaderDisplayEnabled()) {
+ int section = getSectionForPosition(position);
+ if (section != -1 && getPositionForSection(section) == position) {
+ mPlacementCache.firstInSection = true;
+ mPlacementCache.sectionHeader = (String)getSections()[section];
+ } else {
+ mPlacementCache.firstInSection = false;
+ mPlacementCache.sectionHeader = null;
+ }
+
+ mPlacementCache.lastInSection = (getPositionForSection(section + 1) - 1 == position);
+ } else {
+ mPlacementCache.firstInSection = false;
+ mPlacementCache.lastInSection = false;
+ mPlacementCache.sectionHeader = null;
+ }
+ return mPlacementCache;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/OnPhoneNumberPickerActionListener.java b/ContactsCommon/src/com/android/contacts/common/list/OnPhoneNumberPickerActionListener.java
new file mode 100644
index 0000000..f69df9d
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/OnPhoneNumberPickerActionListener.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.app.ActionBar;
+import android.content.Intent;
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a phone number picker.
+ */
+public interface OnPhoneNumberPickerActionListener {
+
+ /**
+ * Returns the selected phone number to the requester.
+ */
+ void onPickPhoneNumberAction(Uri dataUri);
+
+ /**
+ * Calls the specified phone number audio call.
+ */
+ void onCallNumberDirectly(String phoneNumber);
+
+ /**
+ * Calls the specified phone number, either as an audio or video call.
+ */
+ void onCallNumberDirectly(String phoneNumber, boolean isVideoCall);
+
+ /**
+ * Returns the selected number as a shortcut intent.
+ */
+ void onShortcutIntentCreated(Intent intent);
+
+ /**
+ * Called when home menu in {@link ActionBar} is clicked by the user.
+ */
+ void onHomeInActionBarSelected();
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/PhoneNumberListAdapter.java b/ContactsCommon/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
new file mode 100644
index 0000000..fee0a6a
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Callable;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.R;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.extensions.ExtendedPhoneDirectoriesManager;
+import com.android.contacts.common.extensions.ExtensionsFactory;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.Constants;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A cursor adapter for the {@link Phone#CONTENT_ITEM_TYPE} and
+ * {@link SipAddress#CONTENT_ITEM_TYPE}.
+ *
+ * By default this adapter just handles phone numbers. When {@link #setUseCallableUri(boolean)} is
+ * called with "true", this adapter starts handling SIP addresses too, by using {@link Callable}
+ * API instead of {@link Phone}.
+ */
+public class PhoneNumberListAdapter extends ContactEntryListAdapter {
+
+ private static final String TAG = PhoneNumberListAdapter.class.getSimpleName();
+
+ // A list of extended directories to add to the directories from the database
+ private final List mExtendedDirectories;
+
+ // Extended directories will have ID's that are higher than any of the id's from the database.
+ // Thi sis so that we can identify them and set them up properly. If no extended directories
+ // exist, this will be Long.MAX_VALUE
+ private long mFirstExtendedDirectoryId = Long.MAX_VALUE;
+
+ public static class PhoneQuery {
+
+ /**
+ * Optional key used as part of a JSON lookup key to specify an analytics category
+ * associated with the row.
+ */
+ public static final String ANALYTICS_CATEGORY = "analytics_category";
+
+ /**
+ * Optional key used as part of a JSON lookup key to specify an analytics action associated
+ * with the row.
+ */
+ public static final String ANALYTICS_ACTION = "analytics_action";
+
+ /**
+ * Optional key used as part of a JSON lookup key to specify an analytics value associated
+ * with the row.
+ */
+ public static final String ANALYTICS_VALUE = "analytics_value";
+
+ public static final String[] PROJECTION_PRIMARY = new String[] {
+ Phone._ID, // 0
+ Phone.TYPE, // 1
+ Phone.LABEL, // 2
+ Phone.NUMBER, // 3
+ Phone.CONTACT_ID, // 4
+ Phone.LOOKUP_KEY, // 5
+ Phone.PHOTO_ID, // 6
+ Phone.DISPLAY_NAME_PRIMARY, // 7
+ Phone.PHOTO_THUMBNAIL_URI, // 8
+ };
+
+ public static final String[] PROJECTION_ALTERNATIVE = new String[] {
+ Phone._ID, // 0
+ Phone.TYPE, // 1
+ Phone.LABEL, // 2
+ Phone.NUMBER, // 3
+ Phone.CONTACT_ID, // 4
+ Phone.LOOKUP_KEY, // 5
+ Phone.PHOTO_ID, // 6
+ Phone.DISPLAY_NAME_ALTERNATIVE, // 7
+ Phone.PHOTO_THUMBNAIL_URI, // 8
+ };
+
+ public static final int PHONE_ID = 0;
+ public static final int PHONE_TYPE = 1;
+ public static final int PHONE_LABEL = 2;
+ public static final int PHONE_NUMBER = 3;
+ public static final int CONTACT_ID = 4;
+ public static final int LOOKUP_KEY = 5;
+ public static final int PHOTO_ID = 6;
+ public static final int DISPLAY_NAME = 7;
+ public static final int PHOTO_URI = 8;
+ }
+
+ private static final String IGNORE_NUMBER_TOO_LONG_CLAUSE =
+ "length(" + Phone.NUMBER + ") < 1000";
+
+ private final CharSequence mUnknownNameText;
+ private final String mCountryIso;
+
+ private ContactListItemView.PhotoPosition mPhotoPosition;
+
+ private boolean mUseCallableUri;
+
+ public PhoneNumberListAdapter(Context context) {
+ super(context);
+ setDefaultFilterHeaderText(R.string.list_filter_phones);
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ mCountryIso = GeoUtil.getCurrentCountryIso(context);
+
+ final ExtendedPhoneDirectoriesManager manager
+ = ExtensionsFactory.getExtendedPhoneDirectoriesManager();
+ if (manager != null) {
+ mExtendedDirectories = manager.getExtendedDirectories(mContext);
+ } else {
+ // Empty list to avoid sticky NPE's
+ mExtendedDirectories = new ArrayList();
+ }
+ }
+
+ protected CharSequence getUnknownNameText() {
+ return mUnknownNameText;
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ String query = getQueryString();
+ if (query == null) {
+ query = "";
+ }
+ if (isExtendedDirectory(directoryId)) {
+ final DirectoryPartition directory = getExtendedDirectoryFromId(directoryId);
+ final String contentUri = directory.getContentUri();
+ if (contentUri == null) {
+ throw new IllegalStateException("Extended directory must have a content URL: "
+ + directory);
+ }
+ final Builder builder = Uri.parse(contentUri).buildUpon();
+ builder.appendPath(query);
+ builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit(directory)));
+ loader.setUri(builder.build());
+ loader.setProjection(PhoneQuery.PROJECTION_PRIMARY);
+ } else {
+ final boolean isRemoteDirectoryQuery = isRemoteDirectory(directoryId);
+ final Builder builder;
+ if (isSearchMode()) {
+ final Uri baseUri;
+ if (isRemoteDirectoryQuery) {
+ baseUri = Phone.CONTENT_FILTER_URI;
+ } else if (mUseCallableUri) {
+ baseUri = Callable.CONTENT_FILTER_URI;
+ } else {
+ baseUri = Phone.CONTENT_FILTER_URI;
+ }
+ builder = baseUri.buildUpon();
+ builder.appendPath(query); // Builder will encode the query
+ builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId));
+ if (isRemoteDirectoryQuery) {
+ builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId))));
+ }
+ } else {
+ final Uri baseUri = mUseCallableUri ? Callable.CONTENT_URI : Phone.CONTENT_URI;
+ builder = baseUri.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT));
+ if (isSectionHeaderDisplayEnabled()) {
+ builder.appendQueryParameter(Phone.EXTRA_ADDRESS_BOOK_INDEX, "true");
+ }
+ applyFilter(loader, builder, directoryId, getFilter());
+ }
+
+ // Ignore invalid phone numbers that are too long. These can potentially cause freezes
+ // in the UI and there is no reason to display them.
+ final String prevSelection = loader.getSelection();
+ final String newSelection;
+ if (!TextUtils.isEmpty(prevSelection)) {
+ newSelection = prevSelection + " AND " + IGNORE_NUMBER_TOO_LONG_CLAUSE;
+ } else {
+ newSelection = IGNORE_NUMBER_TOO_LONG_CLAUSE;
+ }
+ loader.setSelection(newSelection);
+
+ // Remove duplicates when it is possible.
+ builder.appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true");
+ loader.setUri(builder.build());
+
+ // TODO a projection that includes the search snippet
+ if (getContactNameDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
+ loader.setProjection(PhoneQuery.PROJECTION_PRIMARY);
+ } else {
+ loader.setProjection(PhoneQuery.PROJECTION_ALTERNATIVE);
+ }
+
+ if (getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
+ loader.setSortOrder(Phone.SORT_KEY_PRIMARY);
+ } else {
+ loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE);
+ }
+ }
+ }
+
+ protected boolean isExtendedDirectory(long directoryId) {
+ return directoryId >= mFirstExtendedDirectoryId;
+ }
+
+ private DirectoryPartition getExtendedDirectoryFromId(long directoryId) {
+ final int directoryIndex = (int) (directoryId - mFirstExtendedDirectoryId);
+ return mExtendedDirectories.get(directoryIndex);
+ }
+
+ /**
+ * Configure {@code loader} and {@code uriBuilder} according to {@code directoryId} and {@code
+ * filter}.
+ */
+ private void applyFilter(CursorLoader loader, Uri.Builder uriBuilder, long directoryId,
+ ContactListFilter filter) {
+ if (filter == null || directoryId != Directory.DEFAULT) {
+ return;
+ }
+
+ final StringBuilder selection = new StringBuilder();
+ final List selectionArgs = new ArrayList();
+
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_CUSTOM: {
+ selection.append(Contacts.IN_VISIBLE_GROUP + "=1");
+ selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT: {
+ filter.addAccountQueryParameterToUrl(uriBuilder);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS:
+ case ContactListFilter.FILTER_TYPE_DEFAULT:
+ break; // No selection needed.
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ break; // This adapter is always "phone only", so no selection needed either.
+ default:
+ Log.w(TAG, "Unsupported filter type came " +
+ "(type: " + filter.filterType + ", toString: " + filter + ")" +
+ " showing all contacts.");
+ // No selection.
+ break;
+ }
+ loader.setSelection(selection.toString());
+ loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor) getItem(position)).getString(PhoneQuery.DISPLAY_NAME);
+ }
+
+ public String getPhoneNumber(int position) {
+ final Cursor item = (Cursor)getItem(position);
+ return item != null ? item.getString(PhoneQuery.PHONE_NUMBER) : null;
+ }
+
+ /**
+ * Builds a {@link Data#CONTENT_URI} for the given cursor position.
+ *
+ * @return Uri for the data. may be null if the cursor is not ready.
+ */
+ public Uri getDataUri(int position) {
+ final int partitionIndex = getPartitionForPosition(position);
+ final Cursor item = (Cursor)getItem(position);
+ return item != null ? getDataUri(partitionIndex, item) : null;
+ }
+
+ public Uri getDataUri(int partitionIndex, Cursor cursor) {
+ final long directoryId =
+ ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
+ if (!isRemoteDirectory(directoryId)) {
+ final long phoneId = cursor.getLong(PhoneQuery.PHONE_ID);
+ return ContentUris.withAppendedId(Data.CONTENT_URI, phoneId);
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves the lookup key for the given cursor position.
+ *
+ * @param position The cursor position.
+ * @return The lookup key.
+ */
+ public String getLookupKey(int position) {
+ final Cursor item = (Cursor)getItem(position);
+ return item != null ? item.getString(PhoneQuery.LOOKUP_KEY) : null;
+ }
+
+ @Override
+ protected ContactListItemView newView(
+ Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
+ ContactListItemView view = super.newView(context, partition, cursor, position, parent);
+ view.setUnknownNameText(mUnknownNameText);
+ view.setQuickContactEnabled(isQuickContactEnabled());
+ view.setPhotoPosition(mPhotoPosition);
+ return view;
+ }
+
+ protected void setHighlight(ContactListItemView view, Cursor cursor) {
+ view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null);
+ }
+
+ // Override default, which would return number of phone numbers, so we
+ // instead return number of contacts.
+ @Override
+ protected int getResultCount(Cursor cursor) {
+ if (cursor == null) {
+ return 0;
+ }
+ cursor.moveToPosition(-1);
+ long curContactId = -1;
+ int numContacts = 0;
+ while(cursor.moveToNext()) {
+ final long contactId = cursor.getLong(PhoneQuery.CONTACT_ID);
+ if (contactId != curContactId) {
+ curContactId = contactId;
+ ++numContacts;
+ }
+ }
+ return numContacts;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ super.bindView(itemView, partition, cursor, position);
+ ContactListItemView view = (ContactListItemView)itemView;
+
+ setHighlight(view, cursor);
+
+ // Look at elements before and after this position, checking if contact IDs are same.
+ // If they have one same contact ID, it means they can be grouped.
+ //
+ // In one group, only the first entry will show its photo and its name, and the other
+ // entries in the group show just their data (e.g. phone number, email address).
+ cursor.moveToPosition(position);
+ boolean isFirstEntry = true;
+ boolean showBottomDivider = true;
+ final long currentContactId = cursor.getLong(PhoneQuery.CONTACT_ID);
+ if (cursor.moveToPrevious() && !cursor.isBeforeFirst()) {
+ final long previousContactId = cursor.getLong(PhoneQuery.CONTACT_ID);
+ if (currentContactId == previousContactId) {
+ isFirstEntry = false;
+ }
+ }
+ cursor.moveToPosition(position);
+ if (cursor.moveToNext() && !cursor.isAfterLast()) {
+ final long nextContactId = cursor.getLong(PhoneQuery.CONTACT_ID);
+ if (currentContactId == nextContactId) {
+ // The following entry should be in the same group, which means we don't want a
+ // divider between them.
+ // TODO: we want a different divider than the divider between groups. Just hiding
+ // this divider won't be enough.
+ showBottomDivider = false;
+ }
+ }
+ cursor.moveToPosition(position);
+
+ bindViewId(view, cursor, PhoneQuery.PHONE_ID);
+
+ bindSectionHeaderAndDivider(view, position);
+ if (isFirstEntry) {
+ bindName(view, cursor);
+ if (isQuickContactEnabled()) {
+ bindQuickContact(view, partition, cursor, PhoneQuery.PHOTO_ID,
+ PhoneQuery.PHOTO_URI, PhoneQuery.CONTACT_ID,
+ PhoneQuery.LOOKUP_KEY, PhoneQuery.DISPLAY_NAME);
+ } else {
+ if (getDisplayPhotos()) {
+ bindPhoto(view, partition, cursor);
+ }
+ }
+ } else {
+ unbindName(view);
+
+ view.removePhotoView(true, false);
+ }
+
+ final DirectoryPartition directory = (DirectoryPartition) getPartition(partition);
+ bindPhoneNumber(view, cursor, directory.isDisplayNumber());
+ }
+
+ protected void bindPhoneNumber(ContactListItemView view, Cursor cursor, boolean displayNumber) {
+ CharSequence label = null;
+ if (displayNumber && !cursor.isNull(PhoneQuery.PHONE_TYPE)) {
+ final int type = cursor.getInt(PhoneQuery.PHONE_TYPE);
+ final String customLabel = cursor.getString(PhoneQuery.PHONE_LABEL);
+
+ // TODO cache
+ label = Phone.getTypeLabel(getContext().getResources(), type, customLabel);
+ }
+ view.setLabel(label);
+ final String text;
+ if (displayNumber) {
+ text = cursor.getString(PhoneQuery.PHONE_NUMBER);
+ } else {
+ // Display phone label. If that's null, display geocoded location for the number
+ final String phoneLabel = cursor.getString(PhoneQuery.PHONE_LABEL);
+ if (phoneLabel != null) {
+ text = phoneLabel;
+ } else {
+ final String phoneNumber = cursor.getString(PhoneQuery.PHONE_NUMBER);
+ text = GeoUtil.getGeocodedLocationFor(mContext, phoneNumber);
+ }
+ }
+ view.setPhoneNumber(text, mCountryIso);
+ }
+
+ protected void bindSectionHeaderAndDivider(final ContactListItemView view, int position) {
+ if (isSectionHeaderDisplayEnabled()) {
+ Placement placement = getItemPlacementInSection(position);
+ view.setSectionHeader(placement.firstInSection ? placement.sectionHeader : null);
+ } else {
+ view.setSectionHeader(null);
+ }
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, PhoneQuery.DISPLAY_NAME, getContactNameDisplayOrder());
+ // Note: we don't show phonetic names any more (see issue 5265330)
+ }
+
+ protected void unbindName(final ContactListItemView view) {
+ view.hideDisplayName();
+ }
+
+ protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) {
+ if (!isPhotoSupported(partitionIndex)) {
+ view.removePhotoView();
+ return;
+ }
+
+ long photoId = 0;
+ if (!cursor.isNull(PhoneQuery.PHOTO_ID)) {
+ photoId = cursor.getLong(PhoneQuery.PHOTO_ID);
+ }
+
+ if (photoId != 0) {
+ getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false,
+ getCircularPhotos(), null);
+ } else {
+ final String photoUriString = cursor.getString(PhoneQuery.PHOTO_URI);
+ final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ final String displayName = cursor.getString(PhoneQuery.DISPLAY_NAME);
+ final String lookupKey = cursor.getString(PhoneQuery.LOOKUP_KEY);
+ request = new DefaultImageRequest(displayName, lookupKey, getCircularPhotos());
+ }
+ getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false,
+ getCircularPhotos(), request);
+ }
+ }
+
+ public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+ }
+
+ public ContactListItemView.PhotoPosition getPhotoPosition() {
+ return mPhotoPosition;
+ }
+
+ public void setUseCallableUri(boolean useCallableUri) {
+ mUseCallableUri = useCallableUri;
+ }
+
+ public boolean usesCallableUri() {
+ return mUseCallableUri;
+ }
+
+ /**
+ * Override base implementation to inject extended directories between local & remote
+ * directories. This is done in the following steps:
+ * 1. Call base implementation to add directories from the cursor.
+ * 2. Iterate all base directories and establish the following information:
+ * a. The highest directory id so that we can assign unused id's to the extended directories.
+ * b. The index of the last non-remote directory. This is where we will insert extended
+ * directories.
+ * 3. Iterate the extended directories and for each one, assign an ID and insert it in the
+ * proper location.
+ */
+ @Override
+ public void changeDirectories(Cursor cursor) {
+ super.changeDirectories(cursor);
+ if (getDirectorySearchMode() == DirectoryListLoader.SEARCH_MODE_NONE) {
+ return;
+ }
+ final int numExtendedDirectories = mExtendedDirectories.size();
+ if (getPartitionCount() == cursor.getCount() + numExtendedDirectories) {
+ // already added all directories;
+ return;
+ }
+ //
+ mFirstExtendedDirectoryId = Long.MAX_VALUE;
+ if (numExtendedDirectories > 0) {
+ // The Directory.LOCAL_INVISIBLE is not in the cursor but we can't reuse it's
+ // "special" ID.
+ long maxId = Directory.LOCAL_INVISIBLE;
+ int insertIndex = 0;
+ for (int i = 0, n = getPartitionCount(); i < n; i++) {
+ final DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ final long id = partition.getDirectoryId();
+ if (id > maxId) {
+ maxId = id;
+ }
+ if (!isRemoteDirectory(id)) {
+ // assuming remote directories come after local, we will end up with the index
+ // where we should insert extended directories. This also works if there are no
+ // remote directories at all.
+ insertIndex = i + 1;
+ }
+ }
+ // Extended directories ID's cannot collide with base directories
+ mFirstExtendedDirectoryId = maxId + 1;
+ for (int i = 0; i < numExtendedDirectories; i++) {
+ final long id = mFirstExtendedDirectoryId + i;
+ final DirectoryPartition directory = mExtendedDirectories.get(i);
+ if (getPartitionByDirectoryId(id) == -1) {
+ addPartition(insertIndex, directory);
+ directory.setDirectoryId(id);
+ }
+ }
+ }
+ }
+
+ protected Uri getContactUri(int partitionIndex, Cursor cursor,
+ int contactIdColumn, int lookUpKeyColumn) {
+ final DirectoryPartition directory = (DirectoryPartition) getPartition(partitionIndex);
+ final long directoryId = directory.getDirectoryId();
+ if (!isExtendedDirectory(directoryId)) {
+ return super.getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn);
+ }
+ return Contacts.CONTENT_LOOKUP_URI.buildUpon()
+ .appendPath(Constants.LOOKUP_URI_ENCODED)
+ .appendQueryParameter(Directory.DISPLAY_NAME, directory.getLabel())
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId))
+ .encodedFragment(cursor.getString(lookUpKeyColumn))
+ .build();
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/PhoneNumberPickerFragment.java b/ContactsCommon/src/com/android/contacts/common/list/PhoneNumberPickerFragment.java
new file mode 100644
index 0000000..7f98fde
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/PhoneNumberPickerFragment.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+import com.android.contacts.common.util.AccountFilterUtil;
+import com.android.contacts.commonbind.analytics.AnalyticsUtil;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Fragment containing a phone number list for picking.
+ */
+public class PhoneNumberPickerFragment extends ContactEntryListFragment
+ implements OnShortcutIntentCreatedListener {
+ private static final String TAG = PhoneNumberPickerFragment.class.getSimpleName();
+
+ private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
+
+ private static final String KEY_SHORTCUT_ACTION = "shortcutAction";
+
+ private OnPhoneNumberPickerActionListener mListener;
+ private String mShortcutAction;
+
+ private ContactListFilter mFilter;
+
+ private View mAccountFilterHeader;
+ /**
+ * Lives as ListView's header and is shown when {@link #mAccountFilterHeader} is set
+ * to View.GONE.
+ */
+ private View mPaddingView;
+
+ private static final String KEY_FILTER = "filter";
+
+ /** true if the loader has started at least once. */
+ private boolean mLoaderStarted;
+
+ private boolean mUseCallableUri;
+
+ private ContactListItemView.PhotoPosition mPhotoPosition =
+ ContactListItemView.getDefaultPhotoPosition(false /* normal/non opposite */);
+
+ private class FilterHeaderClickListener implements OnClickListener {
+ @Override
+ public void onClick(View view) {
+ AccountFilterUtil.startAccountFilterActivityForResult(
+ PhoneNumberPickerFragment.this,
+ REQUEST_CODE_ACCOUNT_FILTER,
+ mFilter);
+ }
+ }
+ private OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
+
+ public PhoneNumberPickerFragment() {
+ setQuickContactEnabled(false);
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
+
+ // Show nothing instead of letting caller Activity show something.
+ setHasOptionsMenu(true);
+ }
+
+ public void setDirectorySearchEnabled(boolean flag) {
+ setDirectorySearchMode(flag ? DirectoryListLoader.SEARCH_MODE_DEFAULT
+ : DirectoryListLoader.SEARCH_MODE_NONE);
+ }
+
+ public void setOnPhoneNumberPickerActionListener(OnPhoneNumberPickerActionListener listener) {
+ this.mListener = listener;
+ }
+
+ public OnPhoneNumberPickerActionListener getOnPhoneNumberPickerListener() {
+ return mListener;
+ }
+
+ @Override
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ super.onCreateView(inflater, container);
+
+ View paddingView = inflater.inflate(R.layout.contact_detail_list_padding, null, false);
+ mPaddingView = paddingView.findViewById(R.id.contact_detail_list_padding);
+ getListView().addHeaderView(paddingView);
+
+ mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
+ mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
+ updateFilterHeaderView();
+
+ setVisibleScrollbarEnabled(getVisibleScrollbarEnabled());
+ }
+
+ protected boolean getVisibleScrollbarEnabled() {
+ return true;
+ }
+
+ @Override
+ protected void setSearchMode(boolean flag) {
+ super.setSearchMode(flag);
+ updateFilterHeaderView();
+ }
+
+ private void updateFilterHeaderView() {
+ final ContactListFilter filter = getFilter();
+ if (mAccountFilterHeader == null || filter == null) {
+ return;
+ }
+ final boolean shouldShowHeader =
+ !isSearchMode() &&
+ AccountFilterUtil.updateAccountFilterTitleForPhone(
+ mAccountFilterHeader, filter, false);
+ if (shouldShowHeader) {
+ mPaddingView.setVisibility(View.GONE);
+ mAccountFilterHeader.setVisibility(View.VISIBLE);
+ } else {
+ mPaddingView.setVisibility(View.VISIBLE);
+ mAccountFilterHeader.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void restoreSavedState(Bundle savedState) {
+ super.restoreSavedState(savedState);
+
+ if (savedState == null) {
+ return;
+ }
+
+ mFilter = savedState.getParcelable(KEY_FILTER);
+ mShortcutAction = savedState.getString(KEY_SHORTCUT_ACTION);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(KEY_FILTER, mFilter);
+ outState.putString(KEY_SHORTCUT_ACTION, mShortcutAction);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled()
+ if (mListener != null) {
+ mListener.onHomeInActionBarSelected();
+ }
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * @param shortcutAction either {@link Intent#ACTION_CALL} or
+ * {@link Intent#ACTION_SENDTO} or null.
+ */
+ public void setShortcutAction(String shortcutAction) {
+ this.mShortcutAction = shortcutAction;
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ final Uri phoneUri = getPhoneUri(position);
+
+ if (phoneUri != null) {
+ pickPhoneNumber(phoneUri);
+ } else {
+ final String number = getPhoneNumber(position);
+ if (!TextUtils.isEmpty(number)) {
+ cacheContactInfo(position);
+ mListener.onCallNumberDirectly(number);
+ } else {
+ Log.w(TAG, "Item at " + position + " was clicked before"
+ + " adapter is ready. Ignoring");
+ }
+ }
+
+ // Get the lookup key and track any analytics
+ final String lookupKey = getLookupKey(position);
+ if (!TextUtils.isEmpty(lookupKey)) {
+ maybeTrackAnalytics(lookupKey);
+ }
+ }
+
+ protected void cacheContactInfo(int position) {
+ // Not implemented. Hook for child classes
+ }
+
+ protected String getPhoneNumber(int position) {
+ final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
+ return adapter.getPhoneNumber(position);
+ }
+
+ protected Uri getPhoneUri(int position) {
+ final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
+ return adapter.getDataUri(position);
+ }
+
+ protected String getLookupKey(int position) {
+ final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
+ return adapter.getLookupKey(position);
+ }
+
+ @Override
+ protected void startLoading() {
+ mLoaderStarted = true;
+ super.startLoading();
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data) {
+ super.onLoadFinished(loader, data);
+
+ // disable scroll bar if there is no data
+ setVisibleScrollbarEnabled(data != null && data.getCount() > 0);
+ }
+
+ public void setUseCallableUri(boolean useCallableUri) {
+ mUseCallableUri = useCallableUri;
+ }
+
+ public boolean usesCallableUri() {
+ return mUseCallableUri;
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ PhoneNumberListAdapter adapter = new PhoneNumberListAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ adapter.setUseCallableUri(mUseCallableUri);
+ return adapter;
+ }
+
+ @Override
+ protected void configureAdapter() {
+ super.configureAdapter();
+
+ final ContactEntryListAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return;
+ }
+
+ if (!isSearchMode() && mFilter != null) {
+ adapter.setFilter(mFilter);
+ }
+
+ setPhotoPosition(adapter);
+ }
+
+ protected void setPhotoPosition(ContactEntryListAdapter adapter) {
+ ((PhoneNumberListAdapter) adapter).setPhotoPosition(mPhotoPosition);
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contact_list_content, null);
+ }
+
+ public void pickPhoneNumber(Uri uri) {
+ if (mShortcutAction == null) {
+ mListener.onPickPhoneNumberAction(uri);
+ } else {
+ startPhoneNumberShortcutIntent(uri);
+ }
+ }
+
+ protected void startPhoneNumberShortcutIntent(Uri uri) {
+ ShortcutIntentBuilder builder = new ShortcutIntentBuilder(getActivity(), this);
+ builder.createPhoneNumberShortcutIntent(uri, mShortcutAction);
+ }
+
+ public void onShortcutIntentCreated(Uri uri, Intent shortcutIntent) {
+ mListener.onShortcutIntentCreated(shortcutIntent);
+ }
+
+ @Override
+ public void onPickerResult(Intent data) {
+ mListener.onPickPhoneNumberAction(data.getData());
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
+ if (getActivity() != null) {
+ AccountFilterUtil.handleAccountFilterResult(
+ ContactListFilterController.getInstance(getActivity()), resultCode, data);
+ } else {
+ Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
+ }
+ }
+ }
+
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public void setFilter(ContactListFilter filter) {
+ if ((mFilter == null && filter == null) ||
+ (mFilter != null && mFilter.equals(filter))) {
+ return;
+ }
+
+ mFilter = filter;
+ if (mLoaderStarted) {
+ reloadData();
+ }
+ updateFilterHeaderView();
+ }
+
+ public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+
+ final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
+ if (adapter != null) {
+ adapter.setPhotoPosition(photoPosition);
+ }
+ }
+
+ /**
+ * Where a lookup key contains analytic event information, logs the associated analytics event.
+ *
+ * @param lookupKey The lookup key JSON object.
+ */
+ private void maybeTrackAnalytics(String lookupKey) {
+ try {
+ JSONObject json = new JSONObject(lookupKey);
+
+ String analyticsCategory = json.getString(
+ PhoneNumberListAdapter.PhoneQuery.ANALYTICS_CATEGORY);
+ String analyticsAction = json.getString(
+ PhoneNumberListAdapter.PhoneQuery.ANALYTICS_ACTION);
+ String analyticsValue = json.getString(
+ PhoneNumberListAdapter.PhoneQuery.ANALYTICS_VALUE);
+
+ if (TextUtils.isEmpty(analyticsCategory) || TextUtils.isEmpty(analyticsAction) ||
+ TextUtils.isEmpty(analyticsValue)) {
+ return;
+ }
+
+ // Assume that the analytic value being tracked could be a float value, but just cast
+ // to a long so that the analytic server can handle it.
+ long value;
+ try {
+ float floatValue = Float.parseFloat(analyticsValue);
+ value = (long) floatValue;
+ } catch (NumberFormatException nfe) {
+ return;
+ }
+
+ AnalyticsUtil.sendEvent(getActivity().getApplication(), analyticsCategory,
+ analyticsAction, "" /* label */, value);
+ } catch (JSONException e) {
+ // Not an error; just a lookup key that doesn't have the right information.
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/PinnedHeaderListAdapter.java b/ContactsCommon/src/com/android/contacts/common/list/PinnedHeaderListAdapter.java
new file mode 100644
index 0000000..72f3f19
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/PinnedHeaderListAdapter.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.common.widget.CompositeCursorAdapter;
+
+/**
+ * A subclass of {@link CompositeCursorAdapter} that manages pinned partition headers.
+ */
+public abstract class PinnedHeaderListAdapter extends CompositeCursorAdapter
+ implements PinnedHeaderListView.PinnedHeaderAdapter {
+
+ public static final int PARTITION_HEADER_TYPE = 0;
+
+ private boolean mPinnedPartitionHeadersEnabled;
+ private boolean mHeaderVisibility[];
+
+ public PinnedHeaderListAdapter(Context context) {
+ super(context);
+ }
+
+ public PinnedHeaderListAdapter(Context context, int initialCapacity) {
+ super(context, initialCapacity);
+ }
+
+ public boolean getPinnedPartitionHeadersEnabled() {
+ return mPinnedPartitionHeadersEnabled;
+ }
+
+ public void setPinnedPartitionHeadersEnabled(boolean flag) {
+ this.mPinnedPartitionHeadersEnabled = flag;
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ if (mPinnedPartitionHeadersEnabled) {
+ return getPartitionCount();
+ } else {
+ return 0;
+ }
+ }
+
+ protected boolean isPinnedPartitionHeaderVisible(int partition) {
+ return getPinnedPartitionHeadersEnabled() && hasHeader(partition)
+ && !isPartitionEmpty(partition);
+ }
+
+ /**
+ * The default implementation creates the same type of view as a normal
+ * partition header.
+ */
+ @Override
+ public View getPinnedHeaderView(int partition, View convertView, ViewGroup parent) {
+ if (hasHeader(partition)) {
+ View view = null;
+ if (convertView != null) {
+ Integer headerType = (Integer)convertView.getTag();
+ if (headerType != null && headerType == PARTITION_HEADER_TYPE) {
+ view = convertView;
+ }
+ }
+ if (view == null) {
+ view = newHeaderView(getContext(), partition, null, parent);
+ view.setTag(PARTITION_HEADER_TYPE);
+ view.setFocusable(false);
+ view.setEnabled(false);
+ }
+ bindHeaderView(view, partition, getCursor(partition));
+ view.setLayoutDirection(parent.getLayoutDirection());
+ return view;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void configurePinnedHeaders(PinnedHeaderListView listView) {
+ if (!getPinnedPartitionHeadersEnabled()) {
+ return;
+ }
+
+ int size = getPartitionCount();
+
+ // Cache visibility bits, because we will need them several times later on
+ if (mHeaderVisibility == null || mHeaderVisibility.length != size) {
+ mHeaderVisibility = new boolean[size];
+ }
+ for (int i = 0; i < size; i++) {
+ boolean visible = isPinnedPartitionHeaderVisible(i);
+ mHeaderVisibility[i] = visible;
+ if (!visible) {
+ listView.setHeaderInvisible(i, true);
+ }
+ }
+
+ int headerViewsCount = listView.getHeaderViewsCount();
+
+ // Starting at the top, find and pin headers for partitions preceding the visible one(s)
+ int maxTopHeader = -1;
+ int topHeaderHeight = 0;
+ for (int i = 0; i < size; i++) {
+ if (mHeaderVisibility[i]) {
+ int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount;
+ int partition = getPartitionForPosition(position);
+ if (i > partition) {
+ break;
+ }
+
+ listView.setHeaderPinnedAtTop(i, topHeaderHeight, false);
+ topHeaderHeight += listView.getPinnedHeaderHeight(i);
+ maxTopHeader = i;
+ }
+ }
+
+ // Starting at the bottom, find and pin headers for partitions following the visible one(s)
+ int maxBottomHeader = size;
+ int bottomHeaderHeight = 0;
+ int listHeight = listView.getHeight();
+ for (int i = size; --i > maxTopHeader;) {
+ if (mHeaderVisibility[i]) {
+ int position = listView.getPositionAt(listHeight - bottomHeaderHeight)
+ - headerViewsCount;
+ if (position < 0) {
+ break;
+ }
+
+ int partition = getPartitionForPosition(position - 1);
+ if (partition == -1 || i <= partition) {
+ break;
+ }
+
+ int height = listView.getPinnedHeaderHeight(i);
+ bottomHeaderHeight += height;
+
+ listView.setHeaderPinnedAtBottom(i, listHeight - bottomHeaderHeight, false);
+ maxBottomHeader = i;
+ }
+ }
+
+ // Headers in between the top-pinned and bottom-pinned should be hidden
+ for (int i = maxTopHeader + 1; i < maxBottomHeader; i++) {
+ if (mHeaderVisibility[i]) {
+ listView.setHeaderInvisible(i, isPartitionEmpty(i));
+ }
+ }
+ }
+
+ @Override
+ public int getScrollPositionForHeader(int viewIndex) {
+ return getPositionForPartition(viewIndex);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/PinnedHeaderListView.java b/ContactsCommon/src/com/android/contacts/common/list/PinnedHeaderListView.java
new file mode 100644
index 0000000..930f0d0
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/PinnedHeaderListView.java
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ListAdapter;
+
+import com.android.contacts.common.util.ViewUtil;
+
+/**
+ * A ListView that maintains a header pinned at the top of the list. The
+ * pinned header can be pushed up and dissolved as needed.
+ */
+public class PinnedHeaderListView extends AutoScrollListView
+ implements OnScrollListener, OnItemSelectedListener {
+
+ /**
+ * Adapter interface. The list adapter must implement this interface.
+ */
+ public interface PinnedHeaderAdapter {
+
+ /**
+ * Returns the overall number of pinned headers, visible or not.
+ */
+ int getPinnedHeaderCount();
+
+ /**
+ * Creates or updates the pinned header view.
+ */
+ View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent);
+
+ /**
+ * Configures the pinned headers to match the visible list items. The
+ * adapter should call {@link PinnedHeaderListView#setHeaderPinnedAtTop},
+ * {@link PinnedHeaderListView#setHeaderPinnedAtBottom},
+ * {@link PinnedHeaderListView#setFadingHeader} or
+ * {@link PinnedHeaderListView#setHeaderInvisible}, for each header that
+ * needs to change its position or visibility.
+ */
+ void configurePinnedHeaders(PinnedHeaderListView listView);
+
+ /**
+ * Returns the list position to scroll to if the pinned header is touched.
+ * Return -1 if the list does not need to be scrolled.
+ */
+ int getScrollPositionForHeader(int viewIndex);
+ }
+
+ private static final int MAX_ALPHA = 255;
+ private static final int TOP = 0;
+ private static final int BOTTOM = 1;
+ private static final int FADING = 2;
+
+ private static final int DEFAULT_ANIMATION_DURATION = 20;
+
+ private static final int DEFAULT_SMOOTH_SCROLL_DURATION = 100;
+
+ private static final class PinnedHeader {
+ View view;
+ boolean visible;
+ int y;
+ int height;
+ int alpha;
+ int state;
+
+ boolean animating;
+ boolean targetVisible;
+ int sourceY;
+ int targetY;
+ long targetTime;
+ }
+
+ private PinnedHeaderAdapter mAdapter;
+ private int mSize;
+ private PinnedHeader[] mHeaders;
+ private RectF mBounds = new RectF();
+ private OnScrollListener mOnScrollListener;
+ private OnItemSelectedListener mOnItemSelectedListener;
+ private int mScrollState;
+
+ private boolean mScrollToSectionOnHeaderTouch = false;
+ private boolean mHeaderTouched = false;
+
+ private int mAnimationDuration = DEFAULT_ANIMATION_DURATION;
+ private boolean mAnimating;
+ private long mAnimationTargetTime;
+ private int mHeaderPaddingStart;
+ private int mHeaderWidth;
+
+ public PinnedHeaderListView(Context context) {
+ this(context, null, android.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ super.setOnScrollListener(this);
+ super.setOnItemSelectedListener(this);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mHeaderPaddingStart = getPaddingStart();
+ mHeaderWidth = r - l - mHeaderPaddingStart - getPaddingEnd();
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ mAdapter = (PinnedHeaderAdapter)adapter;
+ super.setAdapter(adapter);
+ }
+
+ @Override
+ public void setOnScrollListener(OnScrollListener onScrollListener) {
+ mOnScrollListener = onScrollListener;
+ super.setOnScrollListener(this);
+ }
+
+ @Override
+ public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+ mOnItemSelectedListener = listener;
+ super.setOnItemSelectedListener(this);
+ }
+
+ public void setScrollToSectionOnHeaderTouch(boolean value) {
+ mScrollToSectionOnHeaderTouch = value;
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if (mAdapter != null) {
+ int count = mAdapter.getPinnedHeaderCount();
+ if (count != mSize) {
+ mSize = count;
+ if (mHeaders == null) {
+ mHeaders = new PinnedHeader[mSize];
+ } else if (mHeaders.length < mSize) {
+ PinnedHeader[] headers = mHeaders;
+ mHeaders = new PinnedHeader[mSize];
+ System.arraycopy(headers, 0, mHeaders, 0, headers.length);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i] == null) {
+ mHeaders[i] = new PinnedHeader();
+ }
+ mHeaders[i].view = mAdapter.getPinnedHeaderView(i, mHeaders[i].view, this);
+ }
+
+ mAnimationTargetTime = System.currentTimeMillis() + mAnimationDuration;
+ mAdapter.configurePinnedHeaders(this);
+ invalidateIfAnimating();
+ }
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScroll(this, firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+ }
+
+ @Override
+ protected float getTopFadingEdgeStrength() {
+ // Disable vertical fading at the top when the pinned header is present
+ return mSize > 0 ? 0 : super.getTopFadingEdgeStrength();
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mScrollState = scrollState;
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScrollStateChanged(this, scrollState);
+ }
+ }
+
+ /**
+ * Ensures that the selected item is positioned below the top-pinned headers
+ * and above the bottom-pinned ones.
+ */
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ int height = getHeight();
+
+ int windowTop = 0;
+ int windowBottom = height;
+
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ if (header.state == TOP) {
+ windowTop = header.y + header.height;
+ } else if (header.state == BOTTOM) {
+ windowBottom = header.y;
+ break;
+ }
+ }
+ }
+
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ if (selectedView.getTop() < windowTop) {
+ setSelectionFromTop(position, windowTop);
+ } else if (selectedView.getBottom() > windowBottom) {
+ setSelectionFromTop(position, windowBottom - selectedView.getHeight());
+ }
+ }
+
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onItemSelected(parent, view, position, id);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onNothingSelected(parent);
+ }
+ }
+
+ public int getPinnedHeaderHeight(int viewIndex) {
+ ensurePinnedHeaderLayout(viewIndex);
+ return mHeaders[viewIndex].view.getHeight();
+ }
+
+ /**
+ * Set header to be pinned at the top.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtTop(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.y = y;
+ header.state = TOP;
+
+ // TODO perhaps we should animate at the top as well
+ header.animating = false;
+ }
+
+ /**
+ * Set header to be pinned at the bottom.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtBottom(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.state = BOTTOM;
+ if (header.animating) {
+ header.targetTime = mAnimationTargetTime;
+ header.sourceY = header.y;
+ header.targetY = y;
+ } else if (animate && (header.y != y || !header.visible)) {
+ if (header.visible) {
+ header.sourceY = header.y;
+ } else {
+ header.visible = true;
+ header.sourceY = y + header.height;
+ }
+ header.animating = true;
+ header.targetVisible = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetY = y;
+ } else {
+ header.visible = true;
+ header.y = y;
+ }
+ }
+
+ /**
+ * Set header to be pinned at the top of the first visible item.
+ *
+ * @param viewIndex index of the header view
+ * @param position is position of the header in pixels.
+ */
+ public void setFadingHeader(int viewIndex, int position, boolean fade) {
+ ensurePinnedHeaderLayout(viewIndex);
+
+ View child = getChildAt(position - getFirstVisiblePosition());
+ if (child == null) return;
+
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.state = FADING;
+ header.alpha = MAX_ALPHA;
+ header.animating = false;
+
+ int top = getTotalTopPinnedHeaderHeight();
+ header.y = top;
+ if (fade) {
+ int bottom = child.getBottom() - top;
+ int headerHeight = header.height;
+ if (bottom < headerHeight) {
+ int portion = bottom - headerHeight;
+ header.alpha = MAX_ALPHA * (headerHeight + portion) / headerHeight;
+ header.y = top + portion;
+ }
+ }
+ }
+
+ /**
+ * Makes header invisible.
+ *
+ * @param viewIndex index of the header view
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderInvisible(int viewIndex, boolean animate) {
+ PinnedHeader header = mHeaders[viewIndex];
+ if (header.visible && (animate || header.animating) && header.state == BOTTOM) {
+ header.sourceY = header.y;
+ if (!header.animating) {
+ header.visible = true;
+ header.targetY = getBottom() + header.height;
+ }
+ header.animating = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetVisible = false;
+ } else {
+ header.visible = false;
+ }
+ }
+
+ private void ensurePinnedHeaderLayout(int viewIndex) {
+ View view = mHeaders[viewIndex].view;
+ if (view.isLayoutRequested()) {
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ int widthSpec;
+ int heightSpec;
+
+ if (layoutParams != null && layoutParams.width > 0) {
+ widthSpec = View.MeasureSpec
+ .makeMeasureSpec(layoutParams.width, View.MeasureSpec.EXACTLY);
+ } else {
+ widthSpec = View.MeasureSpec
+ .makeMeasureSpec(mHeaderWidth, View.MeasureSpec.EXACTLY);
+ }
+
+ if (layoutParams != null && layoutParams.height > 0) {
+ heightSpec = View.MeasureSpec
+ .makeMeasureSpec(layoutParams.height, View.MeasureSpec.EXACTLY);
+ } else {
+ heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ }
+ view.measure(widthSpec, heightSpec);
+ int height = view.getMeasuredHeight();
+ mHeaders[viewIndex].height = height;
+ view.layout(0, 0, view.getMeasuredWidth(), height);
+ }
+ }
+
+ /**
+ * Returns the sum of heights of headers pinned to the top.
+ */
+ public int getTotalTopPinnedHeaderHeight() {
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == TOP) {
+ return header.y + header.height;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the list item position at the specified y coordinate.
+ */
+ public int getPositionAt(int y) {
+ do {
+ int position = pointToPosition(getPaddingLeft() + 1, y);
+ if (position != -1) {
+ return position;
+ }
+ // If position == -1, we must have hit a separator. Let's examine
+ // a nearby pixel
+ y--;
+ } while (y > 0);
+ return 0;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ mHeaderTouched = false;
+ if (super.onInterceptTouchEvent(ev)) {
+ return true;
+ }
+
+ if (mScrollState == SCROLL_STATE_IDLE) {
+ final int y = (int)ev.getY();
+ final int x = (int)ev.getX();
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ // For RTL layouts, this also takes into account that the scrollbar is on the left
+ // side.
+ final int padding = getPaddingLeft();
+ if (header.visible && header.y <= y && header.y + header.height > y &&
+ x >= padding && padding + header.view.getWidth() >= x) {
+ mHeaderTouched = true;
+ if (mScrollToSectionOnHeaderTouch &&
+ ev.getAction() == MotionEvent.ACTION_DOWN) {
+ return smoothScrollToPartition(i);
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mHeaderTouched) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mHeaderTouched = false;
+ }
+ return true;
+ }
+ return super.onTouchEvent(ev);
+ };
+
+ private boolean smoothScrollToPartition(int partition) {
+ if (mAdapter == null) {
+ return false;
+ }
+ final int position = mAdapter.getScrollPositionForHeader(partition);
+ if (position == -1) {
+ return false;
+ }
+
+ int offset = 0;
+ for (int i = 0; i < partition; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ offset += header.height;
+ }
+ }
+ smoothScrollToPositionFromTop(position + getHeaderViewsCount(), offset,
+ DEFAULT_SMOOTH_SCROLL_DURATION);
+ return true;
+ }
+
+ private void invalidateIfAnimating() {
+ mAnimating = false;
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i].animating) {
+ mAnimating = true;
+ invalidate();
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ long currentTime = mAnimating ? System.currentTimeMillis() : 0;
+
+ int top = 0;
+ int right = 0;
+ int bottom = getBottom();
+ boolean hasVisibleHeaders = false;
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ hasVisibleHeaders = true;
+ if (header.state == BOTTOM && header.y < bottom) {
+ bottom = header.y;
+ } else if (header.state == TOP || header.state == FADING) {
+ int newTop = header.y + header.height;
+ if (newTop > top) {
+ top = newTop;
+ }
+ }
+ }
+ }
+
+ if (hasVisibleHeaders) {
+ canvas.save();
+ }
+
+ super.dispatchDraw(canvas);
+
+ if (hasVisibleHeaders) {
+ canvas.restore();
+
+ // If the first item is visible and if it has a positive top that is greater than the
+ // first header's assigned y-value, use that for the first header's y value. This way,
+ // the header inherits any padding applied to the list view.
+ if (mSize > 0 && getFirstVisiblePosition() == 0) {
+ View firstChild = getChildAt(0);
+ PinnedHeader firstHeader = mHeaders[0];
+
+ if (firstHeader != null) {
+ int firstHeaderTop = firstChild != null ? firstChild.getTop() : 0;
+ firstHeader.y = Math.max(firstHeader.y, firstHeaderTop);
+ }
+ }
+
+ // First draw top headers, then the bottom ones to handle the Z axis correctly
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && (header.state == TOP || header.state == FADING)) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == BOTTOM) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+ }
+
+ invalidateIfAnimating();
+ }
+
+ private void drawHeader(Canvas canvas, PinnedHeader header, long currentTime) {
+ if (header.animating) {
+ int timeLeft = (int)(header.targetTime - currentTime);
+ if (timeLeft <= 0) {
+ header.y = header.targetY;
+ header.visible = header.targetVisible;
+ header.animating = false;
+ } else {
+ header.y = header.targetY + (header.sourceY - header.targetY) * timeLeft
+ / mAnimationDuration;
+ }
+ }
+ if (header.visible) {
+ View view = header.view;
+ int saveCount = canvas.save();
+ int translateX = ViewUtil.isViewLayoutRtl(this) ?
+ getWidth() - mHeaderPaddingStart - view.getWidth() :
+ mHeaderPaddingStart;
+ canvas.translate(translateX, header.y);
+ if (header.state == FADING) {
+ mBounds.set(0, 0, view.getWidth(), view.getHeight());
+ canvas.saveLayerAlpha(mBounds, header.alpha, Canvas.ALL_SAVE_FLAG);
+ }
+ view.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ProfileAndContactsLoader.java b/ContactsCommon/src/com/android/contacts/common/list/ProfileAndContactsLoader.java
new file mode 100644
index 0000000..698ef96
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ProfileAndContactsLoader.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
+import android.os.Bundle;
+import android.provider.ContactsContract.Profile;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * A loader for use in the default contact list, which will also query for the user's profile
+ * if configured to do so.
+ */
+public class ProfileAndContactsLoader extends CursorLoader {
+
+ private boolean mLoadProfile;
+ private String[] mProjection;
+
+ public ProfileAndContactsLoader(Context context) {
+ super(context);
+ }
+
+ public void setLoadProfile(boolean flag) {
+ mLoadProfile = flag;
+ }
+
+ public void setProjection(String[] projection) {
+ super.setProjection(projection);
+ mProjection = projection;
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ // First load the profile, if enabled.
+ List cursors = Lists.newArrayList();
+ if (mLoadProfile) {
+ cursors.add(loadProfile());
+ }
+ // ContactsCursor.loadInBackground() can return null; MergeCursor
+ // correctly handles null cursors.
+ Cursor cursor = null;
+ try {
+ cursor = super.loadInBackground();
+ } catch (NullPointerException | SecurityException e) {
+ // Ignore NPEs and SecurityExceptions thrown by providers
+ }
+ final Cursor contactsCursor = cursor;
+ cursors.add(contactsCursor);
+ return new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) {
+ @Override
+ public Bundle getExtras() {
+ // Need to get the extras from the contacts cursor.
+ return contactsCursor == null ? new Bundle() : contactsCursor.getExtras();
+ }
+ };
+ }
+
+ /**
+ * Loads the profile into a MatrixCursor. On failure returns null, which
+ * matches the behavior of CursorLoader.loadInBackground().
+ *
+ * @return MatrixCursor containing profile or null on query failure.
+ */
+ private MatrixCursor loadProfile() {
+ Cursor cursor = getContext().getContentResolver().query(Profile.CONTENT_URI, mProjection,
+ null, null, null);
+ if (cursor == null) {
+ return null;
+ }
+ try {
+ MatrixCursor matrix = new MatrixCursor(mProjection);
+ Object[] row = new Object[mProjection.length];
+ while (cursor.moveToNext()) {
+ for (int i = 0; i < row.length; i++) {
+ row[i] = cursor.getString(i);
+ }
+ matrix.addRow(row);
+ }
+ return matrix;
+ } finally {
+ cursor.close();
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ShortcutIntentBuilder.java b/ContactsCommon/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
new file mode 100644
index 0000000..9e6b5e9
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.telecom.PhoneAccount;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.TextUtils.TruncateAt;
+
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.R;
+
+/**
+ * Constructs shortcut intents.
+ */
+public class ShortcutIntentBuilder {
+
+ private static final String[] CONTACT_COLUMNS = {
+ Contacts.DISPLAY_NAME,
+ Contacts.PHOTO_ID,
+ Contacts.LOOKUP_KEY
+ };
+
+ private static final int CONTACT_DISPLAY_NAME_COLUMN_INDEX = 0;
+ private static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 1;
+ private static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 2;
+
+ private static final String[] PHONE_COLUMNS = {
+ Phone.DISPLAY_NAME,
+ Phone.PHOTO_ID,
+ Phone.NUMBER,
+ Phone.TYPE,
+ Phone.LABEL,
+ Phone.LOOKUP_KEY
+ };
+
+ private static final int PHONE_DISPLAY_NAME_COLUMN_INDEX = 0;
+ private static final int PHONE_PHOTO_ID_COLUMN_INDEX = 1;
+ private static final int PHONE_NUMBER_COLUMN_INDEX = 2;
+ private static final int PHONE_TYPE_COLUMN_INDEX = 3;
+ private static final int PHONE_LABEL_COLUMN_INDEX = 4;
+ private static final int PHONE_LOOKUP_KEY_COLUMN_INDEX = 5;
+
+ private static final String[] PHOTO_COLUMNS = {
+ Photo.PHOTO,
+ };
+
+ private static final int PHOTO_PHOTO_COLUMN_INDEX = 0;
+
+ private static final String PHOTO_SELECTION = Photo._ID + "=?";
+
+ private final OnShortcutIntentCreatedListener mListener;
+ private final Context mContext;
+ private int mIconSize;
+ private final int mIconDensity;
+ private final int mOverlayTextBackgroundColor;
+ private final Resources mResources;
+
+ /**
+ * This is a hidden API of the launcher in JellyBean that allows us to disable the animation
+ * that it would usually do, because it interferes with our own animation for QuickContact.
+ * This is needed since some versions of the launcher override the intent flags and therefore
+ * ignore Intent.FLAG_ACTIVITY_NO_ANIMATION.
+ */
+ public static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
+ "com.android.launcher.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
+
+ /**
+ * Listener interface.
+ */
+ public interface OnShortcutIntentCreatedListener {
+
+ /**
+ * Callback for shortcut intent creation.
+ *
+ * @param uri the original URI for which the shortcut intent has been
+ * created.
+ * @param shortcutIntent resulting shortcut intent.
+ */
+ void onShortcutIntentCreated(Uri uri, Intent shortcutIntent);
+ }
+
+ public ShortcutIntentBuilder(Context context, OnShortcutIntentCreatedListener listener) {
+ mContext = context;
+ mListener = listener;
+
+ mResources = context.getResources();
+ final ActivityManager am = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ mIconSize = mResources.getDimensionPixelSize(R.dimen.shortcut_icon_size);
+ if (mIconSize == 0) {
+ mIconSize = am.getLauncherLargeIconSize();
+ }
+ mIconDensity = am.getLauncherLargeIconDensity();
+ mOverlayTextBackgroundColor = mResources.getColor(R.color.shortcut_overlay_text_background);
+ }
+
+ public void createContactShortcutIntent(Uri contactUri) {
+ new ContactLoadingAsyncTask(contactUri).execute();
+ }
+
+ public void createPhoneNumberShortcutIntent(Uri dataUri, String shortcutAction) {
+ new PhoneNumberLoadingAsyncTask(dataUri, shortcutAction).execute();
+ }
+
+ /**
+ * An asynchronous task that loads name, photo and other data from the database.
+ */
+ private abstract class LoadingAsyncTask extends AsyncTask {
+ protected Uri mUri;
+ protected String mContentType;
+ protected String mDisplayName;
+ protected String mLookupKey;
+ protected byte[] mBitmapData;
+ protected long mPhotoId;
+
+ public LoadingAsyncTask(Uri uri) {
+ mUri = uri;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ mContentType = mContext.getContentResolver().getType(mUri);
+ loadData();
+ loadPhoto();
+ return null;
+ }
+
+ protected abstract void loadData();
+
+ private void loadPhoto() {
+ if (mPhotoId == 0) {
+ return;
+ }
+
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(Data.CONTENT_URI, PHOTO_COLUMNS, PHOTO_SELECTION,
+ new String[] { String.valueOf(mPhotoId) }, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mBitmapData = cursor.getBlob(PHOTO_PHOTO_COLUMN_INDEX);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ }
+
+ private final class ContactLoadingAsyncTask extends LoadingAsyncTask {
+ public ContactLoadingAsyncTask(Uri uri) {
+ super(uri);
+ }
+
+ @Override
+ protected void loadData() {
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(mUri, CONTACT_COLUMNS, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mDisplayName = cursor.getString(CONTACT_DISPLAY_NAME_COLUMN_INDEX);
+ mPhotoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
+ mLookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ @Override
+ protected void onPostExecute(Void result) {
+ createContactShortcutIntent(mUri, mContentType, mDisplayName, mLookupKey, mBitmapData);
+ }
+ }
+
+ private final class PhoneNumberLoadingAsyncTask extends LoadingAsyncTask {
+ private final String mShortcutAction;
+ private String mPhoneNumber;
+ private int mPhoneType;
+ private String mPhoneLabel;
+
+ public PhoneNumberLoadingAsyncTask(Uri uri, String shortcutAction) {
+ super(uri);
+ mShortcutAction = shortcutAction;
+ }
+
+ @Override
+ protected void loadData() {
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(mUri, PHONE_COLUMNS, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mDisplayName = cursor.getString(PHONE_DISPLAY_NAME_COLUMN_INDEX);
+ mPhotoId = cursor.getLong(PHONE_PHOTO_ID_COLUMN_INDEX);
+ mPhoneNumber = cursor.getString(PHONE_NUMBER_COLUMN_INDEX);
+ mPhoneType = cursor.getInt(PHONE_TYPE_COLUMN_INDEX);
+ mPhoneLabel = cursor.getString(PHONE_LABEL_COLUMN_INDEX);
+ mLookupKey = cursor.getString(PHONE_LOOKUP_KEY_COLUMN_INDEX);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ createPhoneNumberShortcutIntent(mUri, mDisplayName, mLookupKey, mBitmapData,
+ mPhoneNumber, mPhoneType, mPhoneLabel, mShortcutAction);
+ }
+ }
+
+ private Drawable getPhotoDrawable(byte[] bitmapData, String displayName, String lookupKey) {
+ if (bitmapData != null) {
+ Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
+ return new BitmapDrawable(mContext.getResources(), bitmap);
+ } else {
+ return ContactPhotoManager.getDefaultAvatarDrawableForContact(mContext.getResources(),
+ false, new DefaultImageRequest(displayName, lookupKey, false));
+ }
+ }
+
+ private void createContactShortcutIntent(Uri contactUri, String contentType, String displayName,
+ String lookupKey, byte[] bitmapData) {
+ Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+
+ // Use an implicit intent without a package name set. It is reasonable for a disambiguation
+ // dialog to appear when opening QuickContacts from the launcher. Plus, this will be more
+ // resistant to future package name changes done to Contacts.
+ Intent shortcutIntent = new Intent(ContactsContract.QuickContact.ACTION_QUICK_CONTACT);
+
+ // When starting from the launcher, start in a new, cleared task.
+ // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
+ // clear the whole thing preemptively here since QuickContactActivity will
+ // finish itself when launching other detail activities. We need to use
+ // Intent.FLAG_ACTIVITY_NO_ANIMATION since not all versions of launcher will respect
+ // the INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION intent extra.
+ shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+ // Tell the launcher to not do its animation, because we are doing our own
+ shortcutIntent.putExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
+
+ shortcutIntent.setDataAndType(contactUri, contentType);
+ shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_EXCLUDE_MIMES,
+ (String[]) null);
+
+ final Bitmap icon = generateQuickContactIcon(drawable);
+
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ if (TextUtils.isEmpty(displayName)) {
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, mContext.getResources().getString(
+ R.string.missing_name));
+ } else {
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
+ }
+
+ mListener.onShortcutIntentCreated(contactUri, intent);
+ }
+
+ private void createPhoneNumberShortcutIntent(Uri uri, String displayName, String lookupKey,
+ byte[] bitmapData, String phoneNumber, int phoneType, String phoneLabel,
+ String shortcutAction) {
+ Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+
+ Bitmap bitmap;
+ Uri phoneUri;
+ if (Intent.ACTION_CALL.equals(shortcutAction)) {
+ // Make the URI a direct tel: URI so that it will always continue to work
+ phoneUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
+ bitmap = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
+ R.drawable.ic_call);
+ } else {
+ phoneUri = Uri.fromParts(ContactsUtils.SCHEME_SMSTO, phoneNumber, null);
+ bitmap = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
+ R.drawable.ic_message_24dp);
+ }
+
+ Intent shortcutIntent = new Intent(shortcutAction, phoneUri);
+ shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
+
+ mListener.onShortcutIntentCreated(uri, intent);
+ }
+
+ private Bitmap generateQuickContactIcon(Drawable photo) {
+
+ // Setup the drawing classes
+ Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ // Copy in the photo
+ Rect dst = new Rect(0,0, mIconSize, mIconSize);
+ photo.setBounds(dst);
+ photo.draw(canvas);
+
+ // Draw the icon with a rounded border
+ RoundedBitmapDrawable roundedDrawable =
+ RoundedBitmapDrawableFactory.create(mResources, bitmap);
+ roundedDrawable.setAntiAlias(true);
+ roundedDrawable.setCornerRadius(mIconSize / 2);
+ Bitmap roundedBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
+ canvas.setBitmap(roundedBitmap);
+ roundedDrawable.setBounds(dst);
+ roundedDrawable.draw(canvas);
+ canvas.setBitmap(null);
+
+ return roundedBitmap;
+ }
+
+ /**
+ * Generates a phone number shortcut icon. Adds an overlay describing the type of the phone
+ * number, and if there is a photo also adds the call action icon.
+ */
+ private Bitmap generatePhoneNumberIcon(Drawable photo, int phoneType, String phoneLabel,
+ int actionResId) {
+ final Resources r = mContext.getResources();
+ final float density = r.getDisplayMetrics().density;
+
+ Bitmap phoneIcon = ((BitmapDrawable) r.getDrawableForDensity(actionResId, mIconDensity))
+ .getBitmap();
+
+ Bitmap icon = generateQuickContactIcon(photo);
+ Canvas canvas = new Canvas(icon);
+
+ // Copy in the photo
+ Paint photoPaint = new Paint();
+ photoPaint.setDither(true);
+ photoPaint.setFilterBitmap(true);
+ Rect dst = new Rect(0, 0, mIconSize, mIconSize);
+
+ // Create an overlay for the phone number type
+ CharSequence overlay = Phone.getTypeLabel(r, phoneType, phoneLabel);
+
+ if (overlay != null) {
+ TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
+ textPaint.setTextSize(r.getDimension(R.dimen.shortcut_overlay_text_size));
+ textPaint.setColor(r.getColor(R.color.textColorIconOverlay));
+ textPaint.setShadowLayer(4f, 0, 2f, r.getColor(R.color.textColorIconOverlayShadow));
+
+ final FontMetricsInt fmi = textPaint.getFontMetricsInt();
+
+ // First fill in a darker background around the text to be drawn
+ final Paint workPaint = new Paint();
+ workPaint.setColor(mOverlayTextBackgroundColor);
+ workPaint.setStyle(Paint.Style.FILL);
+ final int textPadding = r
+ .getDimensionPixelOffset(R.dimen.shortcut_overlay_text_background_padding);
+ final int textBandHeight = (fmi.descent - fmi.ascent) + textPadding * 2;
+ dst.set(0, mIconSize - textBandHeight, mIconSize, mIconSize);
+ canvas.drawRect(dst, workPaint);
+
+ overlay = TextUtils.ellipsize(overlay, textPaint, mIconSize, TruncateAt.END);
+ final float textWidth = textPaint.measureText(overlay, 0, overlay.length());
+ canvas.drawText(overlay, 0, overlay.length(), (mIconSize - textWidth) / 2, mIconSize
+ - fmi.descent - textPadding, textPaint);
+ }
+
+ // Draw the phone action icon as an overlay
+ Rect src = new Rect(0, 0, phoneIcon.getWidth(), phoneIcon.getHeight());
+ int iconWidth = icon.getWidth();
+ dst.set(iconWidth - ((int) (20 * density)), -1,
+ iconWidth, ((int) (19 * density)));
+ canvas.drawBitmap(phoneIcon, src, dst, photoPaint);
+
+ canvas.setBitmap(null);
+
+ return icon;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ViewPagerTabStrip.java b/ContactsCommon/src/com/android/contacts/common/list/ViewPagerTabStrip.java
new file mode 100644
index 0000000..c8ae21a
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ViewPagerTabStrip.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.contacts.common.R;
+
+public class ViewPagerTabStrip extends LinearLayout {
+ private int mSelectedUnderlineThickness;
+ private final Paint mSelectedUnderlinePaint;
+
+ private int mIndexForSelection;
+ private float mSelectionOffset;
+
+ public ViewPagerTabStrip(Context context) {
+ this(context, null);
+ }
+
+ public ViewPagerTabStrip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final Resources res = context.getResources();
+
+ mSelectedUnderlineThickness =
+ res.getDimensionPixelSize(R.dimen.tab_selected_underline_height);
+ int underlineColor = res.getColor(R.color.tab_selected_underline_color);
+ int backgroundColor = res.getColor(R.color.actionbar_background_color);
+
+ mSelectedUnderlinePaint = new Paint();
+ mSelectedUnderlinePaint.setColor(underlineColor);
+
+ setBackgroundColor(backgroundColor);
+ setWillNotDraw(false);
+ }
+
+ /**
+ * Notifies this view that view pager has been scrolled. We save the tab index
+ * and selection offset for interpolating the position and width of selection
+ * underline.
+ */
+ void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ mIndexForSelection = position;
+ mSelectionOffset = positionOffset;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ int childCount = getChildCount();
+
+ // Thick colored underline below the current selection
+ if (childCount > 0) {
+ View selectedTitle = getChildAt(mIndexForSelection);
+
+ if (selectedTitle == null) {
+ // The view pager's tab count changed but we weren't notified yet. Ignore this draw
+ // pass, when we get a new selection we will update and draw the selection strip in
+ // the correct place.
+ return;
+ }
+ int selectedLeft = selectedTitle.getLeft();
+ int selectedRight = selectedTitle.getRight();
+ final boolean isRtl = isRtl();
+ final boolean hasNextTab = isRtl ? mIndexForSelection > 0
+ : (mIndexForSelection < (getChildCount() - 1));
+ if ((mSelectionOffset > 0.0f) && hasNextTab) {
+ // Draw the selection partway between the tabs
+ View nextTitle = getChildAt(mIndexForSelection + (isRtl ? -1 : 1));
+ int nextLeft = nextTitle.getLeft();
+ int nextRight = nextTitle.getRight();
+
+ selectedLeft = (int) (mSelectionOffset * nextLeft +
+ (1.0f - mSelectionOffset) * selectedLeft);
+ selectedRight = (int) (mSelectionOffset * nextRight +
+ (1.0f - mSelectionOffset) * selectedRight);
+ }
+
+ int height = getHeight();
+ canvas.drawRect(selectedLeft, height - mSelectedUnderlineThickness,
+ selectedRight, height, mSelectedUnderlinePaint);
+ }
+ }
+
+ private boolean isRtl() {
+ return getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ }
+}
\ No newline at end of file
diff --git a/ContactsCommon/src/com/android/contacts/common/list/ViewPagerTabs.java b/ContactsCommon/src/com/android/contacts/common/list/ViewPagerTabs.java
new file mode 100644
index 0000000..1ecbb4c
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/list/ViewPagerTabs.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.common.list;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Outline;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.contacts.common.R;
+
+/**
+ * Lightweight implementation of ViewPager tabs. This looks similar to traditional actionBar tabs,
+ * but allows for the view containing the tabs to be placed anywhere on screen. Text-related
+ * attributes can also be assigned in XML - these will get propogated to the child TextViews
+ * automatically.
+ */
+public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
+
+ ViewPager mPager;
+ private ViewPagerTabStrip mTabStrip;
+
+ /**
+ * Linearlayout that will contain the TextViews serving as tabs. This is the only child
+ * of the parent HorizontalScrollView.
+ */
+ final int mTextStyle;
+ final ColorStateList mTextColor;
+ final int mTextSize;
+ final boolean mTextAllCaps;
+ int mPrevSelected = -1;
+ int mSidePadding;
+
+ private int[] mTabIcons;
+
+ private static final ViewOutlineProvider VIEW_BOUNDS_OUTLINE_PROVIDER =
+ new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRect(0, 0, view.getWidth(), view.getHeight());
+ }
+ };
+
+ private static final int TAB_SIDE_PADDING_IN_DPS = 10;
+
+ // TODO: This should use in the future
+ private static final int[] ATTRS = new int[] {
+ android.R.attr.textSize,
+ android.R.attr.textStyle,
+ android.R.attr.textColor,
+ android.R.attr.textAllCaps
+ };
+
+ /**
+ * Simulates actionbar tab behavior by showing a toast with the tab title when long clicked.
+ */
+ private class OnTabLongClickListener implements OnLongClickListener {
+ final int mPosition;
+
+ public OnTabLongClickListener(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ final int[] screenPos = new int[2];
+ getLocationOnScreen(screenPos);
+
+ final Context context = getContext();
+ final int width = getWidth();
+ final int height = getHeight();
+ final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+ Toast toast = Toast.makeText(context, mPager.getAdapter().getPageTitle(mPosition),
+ Toast.LENGTH_SHORT);
+
+ // Show the toast under the tab
+ toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+ (screenPos[0] + width / 2) - screenWidth / 2, screenPos[1] + height);
+
+ toast.show();
+ return true;
+ }
+ }
+
+ public ViewPagerTabs(Context context) {
+ this(context, null);
+ }
+
+ public ViewPagerTabs(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ViewPagerTabs(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setFillViewport(true);
+
+ mSidePadding = (int) (getResources().getDisplayMetrics().density * TAB_SIDE_PADDING_IN_DPS);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
+ mTextSize = a.getDimensionPixelSize(0, 0);
+ mTextStyle = a.getInt(1, 0);
+ mTextColor = a.getColorStateList(2);
+ mTextAllCaps = a.getBoolean(3, false);
+
+ mTabStrip = new ViewPagerTabStrip(context);
+ addView(mTabStrip,
+ new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
+ a.recycle();
+
+ // enable shadow casting from view bounds
+ setOutlineProvider(VIEW_BOUNDS_OUTLINE_PROVIDER);
+ }
+
+ public void setViewPager(ViewPager viewPager) {
+ mPager = viewPager;
+ addTabs(mPager.getAdapter());
+ }
+
+ public void setTabIcons(int [] tabIcons) {
+ mTabIcons = tabIcons;
+ }
+
+ private void addTabs(PagerAdapter adapter) {
+ mTabStrip.removeAllViews();
+
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ addTab(adapter.getPageTitle(i), i);
+ }
+ }
+
+ private void addTab(CharSequence tabTitle, final int position) {
+ View tabView;
+ if (mTabIcons != null && position < mTabIcons.length) {
+ View iconView = new View(getContext());
+ iconView.setBackgroundResource(mTabIcons[position]);
+ iconView.setContentDescription(tabTitle);
+
+ tabView = iconView;
+ } else {
+ final TextView textView = new TextView(getContext());
+ textView.setText(tabTitle);
+ textView.setBackgroundResource(R.drawable.view_pager_tab_background);
+
+ // Assign various text appearance related attributes to child views.
+ if (mTextStyle > 0) {
+ textView.setTypeface(textView.getTypeface(), mTextStyle);
+ }
+ if (mTextSize > 0) {
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
+ }
+ if (mTextColor != null) {
+ textView.setTextColor(mTextColor);
+ }
+ textView.setAllCaps(mTextAllCaps);
+ textView.setGravity(Gravity.CENTER);
+
+ tabView = textView;
+ }
+
+ tabView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPager.setCurrentItem(getRtlPosition(position));
+ }
+ });
+
+ tabView.setOnLongClickListener(new OnTabLongClickListener(position));
+
+ tabView.setPadding(mSidePadding, 0, mSidePadding, 0);
+ mTabStrip.addView(tabView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, 1));
+
+ // Default to the first child being selected
+ if (position == 0) {
+ mPrevSelected = 0;
+ tabView.setSelected(true);
+ }
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ position = getRtlPosition(position);
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ mTabStrip.onPageScrolled(position, positionOffset, positionOffsetPixels);
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ position = getRtlPosition(position);
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ if (mPrevSelected >= 0 && mPrevSelected < tabStripChildCount) {
+ mTabStrip.getChildAt(mPrevSelected).setSelected(false);
+ }
+ final View selectedChild = mTabStrip.getChildAt(position);
+ selectedChild.setSelected(true);
+
+ // Update scroll position
+ final int scrollPos = selectedChild.getLeft() - (getWidth() - selectedChild.getWidth()) / 2;
+ smoothScrollTo(scrollPos, 0);
+ mPrevSelected = position;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+
+ private int getRtlPosition(int position) {
+ if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ return mTabStrip.getChildCount() - 1 - position;
+ }
+ return position;
+ }
+}
+
diff --git a/ContactsCommon/src/com/android/contacts/common/location/CountryDetector.java b/ContactsCommon/src/com/android/contacts/common/location/CountryDetector.java
new file mode 100644
index 0000000..129effd
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/location/CountryDetector.java
@@ -0,0 +1,215 @@
+package com.android.contacts.common.location;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationManager;
+import android.preference.PreferenceManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.contacts.common.testing.NeededForTesting;
+import com.android.contacts.common.util.PermissionsUtil;
+
+import java.util.Locale;
+
+/**
+ * This class is used to detect the country where the user is. It is a simplified version of the
+ * country detector service in the framework. The sources of country location are queried in the
+ * following order of reliability:
+ *
+ * Mobile network
+ * Location manager
+ * SIM's country
+ * User's default locale
+ *
+ *
+ * As far as possible this class tries to replicate the behavior of the system's country detector
+ * service:
+ * 1) Order in priority of sources of country location
+ * 2) Mobile network information provided by CDMA phones is ignored
+ * 3) Location information is updated every 12 hours (instead of 24 hours in the system)
+ * 4) Location updates only uses the {@link LocationManager#PASSIVE_PROVIDER} to avoid active use
+ * of the GPS
+ * 5) If a location is successfully obtained and geocoded, we never fall back to use of the
+ * SIM's country (for the system, the fallback never happens without a reboot)
+ * 6) Location is not used if the device does not implement a {@link android.location.Geocoder}
+*/
+public class CountryDetector {
+ private static final String TAG = "CountryDetector";
+
+ public static final String KEY_PREFERENCE_TIME_UPDATED = "preference_time_updated";
+ public static final String KEY_PREFERENCE_CURRENT_COUNTRY = "preference_current_country";
+
+ private static CountryDetector sInstance;
+
+ private final TelephonyManager mTelephonyManager;
+ private final LocationManager mLocationManager;
+ private final LocaleProvider mLocaleProvider;
+
+ // Used as a default country code when all the sources of country data have failed in the
+ // exceedingly rare event that the device does not have a default locale set for some reason.
+ private final String DEFAULT_COUNTRY_ISO = "US";
+
+ // Wait 12 hours between updates
+ private static final long TIME_BETWEEN_UPDATES_MS = 1000L * 60 * 60 * 12;
+
+ // Minimum distance before an update is triggered, in meters. We don't need this to be too
+ // exact because all we care about is what country the user is in.
+ private static final long DISTANCE_BETWEEN_UPDATES_METERS = 5000;
+
+ private final Context mContext;
+
+ /**
+ * Class that can be used to return the user's default locale. This is in its own class so that
+ * it can be mocked out.
+ */
+ public static class LocaleProvider {
+ public Locale getDefaultLocale() {
+ return Locale.getDefault();
+ }
+ }
+
+ private CountryDetector(Context context) {
+ this (context, (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
+ (LocationManager) context.getSystemService(Context.LOCATION_SERVICE),
+ new LocaleProvider());
+ }
+
+ private CountryDetector(Context context, TelephonyManager telephonyManager,
+ LocationManager locationManager, LocaleProvider localeProvider) {
+ mTelephonyManager = telephonyManager;
+ mLocationManager = locationManager;
+ mLocaleProvider = localeProvider;
+ mContext = context;
+
+ registerForLocationUpdates(context, mLocationManager);
+ }
+
+ public static void registerForLocationUpdates(Context context,
+ LocationManager locationManager) {
+ if (!PermissionsUtil.hasLocationPermissions(context)) {
+ Log.w(TAG, "No location permissions, not registering for location updates.");
+ return;
+ }
+
+ if (!Geocoder.isPresent()) {
+ // Certain devices do not have an implementation of a geocoder - in that case there is
+ // no point trying to get location updates because we cannot retrieve the country based
+ // on the location anyway.
+ return;
+ }
+ final Intent activeIntent = new Intent(context, LocationChangedReceiver.class);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, activeIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+ TIME_BETWEEN_UPDATES_MS, DISTANCE_BETWEEN_UPDATES_METERS, pendingIntent);
+ }
+
+ /**
+ * Factory method for {@link CountryDetector} that allows the caller to provide mock objects.
+ */
+ @NeededForTesting
+ public CountryDetector getInstanceForTest(Context context, TelephonyManager telephonyManager,
+ LocationManager locationManager, LocaleProvider localeProvider, Geocoder geocoder) {
+ return new CountryDetector(context, telephonyManager, locationManager, localeProvider);
+ }
+
+ /**
+ * Returns the instance of the country detector. {@link #initialize(Context)} must have been
+ * called previously.
+ *
+ * @return the initialized country detector.
+ */
+ public synchronized static CountryDetector getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new CountryDetector(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ public String getCurrentCountryIso() {
+ String result = null;
+ if (isNetworkCountryCodeAvailable()) {
+ result = getNetworkBasedCountryIso();
+ }
+ if (TextUtils.isEmpty(result)) {
+ result = getLocationBasedCountryIso();
+ }
+ if (TextUtils.isEmpty(result)) {
+ result = getSimBasedCountryIso();
+ }
+ if (TextUtils.isEmpty(result)) {
+ result = getLocaleBasedCountryIso();
+ }
+ if (TextUtils.isEmpty(result)) {
+ result = DEFAULT_COUNTRY_ISO;
+ }
+ return result.toUpperCase(Locale.US);
+ }
+
+ /**
+ * @return the country code of the current telephony network the user is connected to.
+ */
+ private String getNetworkBasedCountryIso() {
+ return mTelephonyManager.getNetworkCountryIso();
+ }
+
+ /**
+ * @return the geocoded country code detected by the {@link LocationManager}.
+ */
+ private String getLocationBasedCountryIso() {
+ if (!Geocoder.isPresent() || !PermissionsUtil.hasLocationPermissions(mContext)) {
+ return null;
+ }
+ final SharedPreferences sharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(mContext);
+ return sharedPreferences.getString(KEY_PREFERENCE_CURRENT_COUNTRY, null);
+ }
+
+ /**
+ * @return the country code of the SIM card currently inserted in the device.
+ */
+ private String getSimBasedCountryIso() {
+ return mTelephonyManager.getSimCountryIso();
+ }
+
+ /**
+ * @return the country code of the user's currently selected locale.
+ */
+ private String getLocaleBasedCountryIso() {
+ Locale defaultLocale = mLocaleProvider.getDefaultLocale();
+ if (defaultLocale != null) {
+ return defaultLocale.getCountry();
+ }
+ return null;
+ }
+
+ private boolean isNetworkCountryCodeAvailable() {
+ // On CDMA TelephonyManager.getNetworkCountryIso() just returns the SIM's country code.
+ // In this case, we want to ignore the value returned and fallback to location instead.
+ return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM;
+ }
+
+ public static class LocationChangedReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ if (!intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) {
+ return;
+ }
+
+ final Location location = (Location)intent.getExtras().get(
+ LocationManager.KEY_LOCATION_CHANGED);
+
+ UpdateCountryService.updateCountry(context, location);
+ }
+ }
+
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/location/UpdateCountryService.java b/ContactsCommon/src/com/android/contacts/common/location/UpdateCountryService.java
new file mode 100644
index 0000000..9403187
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/location/UpdateCountryService.java
@@ -0,0 +1,83 @@
+package com.android.contacts.common.location;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Service used to perform asynchronous geocoding from within a broadcast receiver. Given a
+ * {@link Location}, convert it into a country code, and save it in shared preferences.
+ */
+public class UpdateCountryService extends IntentService {
+ private static final String TAG = UpdateCountryService.class.getSimpleName();
+
+ private static final String ACTION_UPDATE_COUNTRY = "saveCountry";
+
+ private static final String KEY_INTENT_LOCATION = "location";
+
+ public UpdateCountryService() {
+ super(TAG);
+ }
+
+ public static void updateCountry(Context context, Location location) {
+ final Intent serviceIntent = new Intent(context, UpdateCountryService.class);
+ serviceIntent.setAction(ACTION_UPDATE_COUNTRY);
+ serviceIntent.putExtra(UpdateCountryService.KEY_INTENT_LOCATION, location);
+ context.startService(serviceIntent);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (intent == null) {
+ Log.d(TAG, "onHandleIntent: could not handle null intent");
+ return;
+ }
+ if (ACTION_UPDATE_COUNTRY.equals(intent.getAction())) {
+ final Location location = (Location) intent.getParcelableExtra(KEY_INTENT_LOCATION);
+ final String country = getCountryFromLocation(getApplicationContext(), location);
+
+ if (country == null) {
+ return;
+ }
+
+ final SharedPreferences prefs =
+ PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+
+ final Editor editor = prefs.edit();
+ editor.putLong(CountryDetector.KEY_PREFERENCE_TIME_UPDATED,
+ System.currentTimeMillis());
+ editor.putString(CountryDetector.KEY_PREFERENCE_CURRENT_COUNTRY, country);
+ editor.commit();
+ }
+ }
+
+ /**
+ * Given a {@link Location}, return a country code.
+ *
+ * @return the ISO 3166-1 two letter country code
+ */
+ private String getCountryFromLocation(Context context, Location location) {
+ final Geocoder geocoder = new Geocoder(context);
+ String country = null;
+ try {
+ final List addresses = geocoder.getFromLocation(
+ location.getLatitude(), location.getLongitude(), 1);
+ if (addresses != null && addresses.size() > 0) {
+ country = addresses.get(0).getCountryCode();
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Exception occurred when getting geocoded country from location");
+ }
+ return country;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/model/AccountTypeManager.java b/ContactsCommon/src/com/android/contacts/common/model/AccountTypeManager.java
new file mode 100644
index 0000000..3339597
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/model/AccountTypeManager.java
@@ -0,0 +1,813 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.model;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.accounts.OnAccountsUpdateListener;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SyncAdapterType;
+import android.content.SyncStatusObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.ContactsContract;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TimingLogger;
+
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.list.ContactListFilterController;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountTypeWithDataSet;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.ExchangeAccountType;
+import com.android.contacts.common.model.account.ExternalAccountType;
+import com.android.contacts.common.model.account.FallbackAccountType;
+import com.android.contacts.common.model.account.GoogleAccountType;
+import com.android.contacts.common.model.account.SamsungAccountType;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.testing.NeededForTesting;
+import com.android.contacts.common.util.Constants;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Singleton holder for all parsed {@link AccountType} available on the
+ * system, typically filled through {@link PackageManager} queries.
+ */
+public abstract class AccountTypeManager {
+ static final String TAG = "AccountTypeManager";
+
+ private static final Object mInitializationLock = new Object();
+ private static AccountTypeManager mAccountTypeManager;
+
+ /**
+ * Requests the singleton instance of {@link AccountTypeManager} with data bound from
+ * the available authenticators. This method can safely be called from the UI thread.
+ */
+ public static AccountTypeManager getInstance(Context context) {
+ synchronized (mInitializationLock) {
+ if (mAccountTypeManager == null) {
+ context = context.getApplicationContext();
+ mAccountTypeManager = new AccountTypeManagerImpl(context);
+ }
+ }
+ return mAccountTypeManager;
+ }
+
+ /**
+ * Set the instance of account type manager. This is only for and should only be used by unit
+ * tests. While having this method is not ideal, it's simpler than the alternative of
+ * holding this as a service in the ContactsApplication context class.
+ *
+ * @param mockManager The mock AccountTypeManager.
+ */
+ @NeededForTesting
+ public static void setInstanceForTest(AccountTypeManager mockManager) {
+ synchronized (mInitializationLock) {
+ mAccountTypeManager = mockManager;
+ }
+ }
+
+ /**
+ * Returns the list of all accounts (if contactWritableOnly is false) or just the list of
+ * contact writable accounts (if contactWritableOnly is true).
+ */
+ // TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts()
+ public abstract List getAccounts(boolean contactWritableOnly);
+
+ /**
+ * Returns the list of accounts that are group writable.
+ */
+ public abstract List getGroupWritableAccounts();
+
+ public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet);
+
+ public final AccountType getAccountType(String accountType, String dataSet) {
+ return getAccountType(AccountTypeWithDataSet.get(accountType, dataSet));
+ }
+
+ public final AccountType getAccountTypeForAccount(AccountWithDataSet account) {
+ if (account != null) {
+ return getAccountType(account.getAccountTypeWithDataSet());
+ }
+ return getAccountType(null, null);
+ }
+
+ /**
+ * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s
+ * which support the "invite" feature and have one or more account.
+ *
+ * This is a filtered down and more "usable" list compared to
+ * {@link #getAllInvitableAccountTypes}, where usable is defined as:
+ * (1) making sure that the app that contributed the account type is not disabled
+ * (in order to avoid presenting the user with an option that does nothing), and
+ * (2) that there is at least one raw contact with that account type in the database
+ * (assuming that the user probably doesn't use that account type).
+ *
+ * Warning: Don't use on the UI thread because this can scan the database.
+ */
+ public abstract Map getUsableInvitableAccountTypes();
+
+ /**
+ * Find the best {@link DataKind} matching the requested
+ * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
+ * If no direct match found, we try searching {@link FallbackAccountType}.
+ */
+ public DataKind getKindOrFallback(AccountType type, String mimeType) {
+ return type == null ? null : type.getKindForMimetype(mimeType);
+ }
+
+ /**
+ * Returns all registered {@link AccountType}s, including extension ones.
+ *
+ * @param contactWritableOnly if true, it only returns ones that support writing contacts.
+ */
+ public abstract List getAccountTypes(boolean contactWritableOnly);
+
+ /**
+ * @param contactWritableOnly if true, it only returns ones that support writing contacts.
+ * @return true when this instance contains the given account.
+ */
+ public boolean contains(AccountWithDataSet account, boolean contactWritableOnly) {
+ for (AccountWithDataSet account_2 : getAccounts(false)) {
+ if (account.equals(account_2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+class AccountTypeManagerImpl extends AccountTypeManager
+ implements OnAccountsUpdateListener, SyncStatusObserver {
+
+ private static final Map
+ EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP =
+ Collections.unmodifiableMap(new HashMap());
+
+ /**
+ * A sample contact URI used to test whether any activities will respond to an
+ * invitable intent with the given URI as the intent data. This doesn't need to be
+ * specific to a real contact because an app that intercepts the intent should probably do so
+ * for all types of contact URIs.
+ */
+ private static final Uri SAMPLE_CONTACT_URI = ContactsContract.Contacts.getLookupUri(
+ 1, "xxx");
+
+ private Context mContext;
+ private AccountManager mAccountManager;
+
+ private AccountType mFallbackAccountType;
+
+ private List mAccounts = Lists.newArrayList();
+ private List mContactWritableAccounts = Lists.newArrayList();
+ private List mGroupWritableAccounts = Lists.newArrayList();
+ private Map mAccountTypesWithDataSets = Maps.newHashMap();
+ private Map mInvitableAccountTypes =
+ EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
+
+ private final InvitableAccountTypeCache mInvitableAccountTypeCache;
+
+ /**
+ * The boolean value is equal to true if the {@link InvitableAccountTypeCache} has been
+ * initialized. False otherwise.
+ */
+ private final AtomicBoolean mInvitablesCacheIsInitialized = new AtomicBoolean(false);
+
+ /**
+ * The boolean value is equal to true if the {@link FindInvitablesTask} is still executing.
+ * False otherwise.
+ */
+ private final AtomicBoolean mInvitablesTaskIsRunning = new AtomicBoolean(false);
+
+ private static final int MESSAGE_LOAD_DATA = 0;
+ private static final int MESSAGE_PROCESS_BROADCAST_INTENT = 1;
+
+ private HandlerThread mListenerThread;
+ private Handler mListenerHandler;
+
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+ private final Runnable mCheckFilterValidityRunnable = new Runnable () {
+ @Override
+ public void run() {
+ ContactListFilterController.getInstance(mContext).checkFilterValidity(true);
+ }
+ };
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Message msg = mListenerHandler.obtainMessage(MESSAGE_PROCESS_BROADCAST_INTENT, intent);
+ mListenerHandler.sendMessage(msg);
+ }
+
+ };
+
+ /* A latch that ensures that asynchronous initialization completes before data is used */
+ private volatile CountDownLatch mInitializationLatch = new CountDownLatch(1);
+
+ private static final Comparator ACCOUNT_COMPARATOR =
+ new Comparator() {
+ @Override
+ public int compare(AccountWithDataSet a, AccountWithDataSet b) {
+ if (Objects.equal(a.name, b.name) && Objects.equal(a.type, b.type)
+ && Objects.equal(a.dataSet, b.dataSet)) {
+ return 0;
+ } else if (b.name == null || b.type == null) {
+ return -1;
+ } else if (a.name == null || a.type == null) {
+ return 1;
+ } else {
+ int diff = a.name.compareTo(b.name);
+ if (diff != 0) {
+ return diff;
+ }
+ diff = a.type.compareTo(b.type);
+ if (diff != 0) {
+ return diff;
+ }
+
+ // Accounts without data sets get sorted before those that have them.
+ if (a.dataSet != null) {
+ return b.dataSet == null ? 1 : a.dataSet.compareTo(b.dataSet);
+ } else {
+ return -1;
+ }
+ }
+ }
+ };
+
+ /**
+ * Internal constructor that only performs initial parsing.
+ */
+ public AccountTypeManagerImpl(Context context) {
+ mContext = context;
+ mFallbackAccountType = new FallbackAccountType(context);
+
+ mAccountManager = AccountManager.get(mContext);
+
+ mListenerThread = new HandlerThread("AccountChangeListener");
+ mListenerThread.start();
+ mListenerHandler = new Handler(mListenerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_LOAD_DATA:
+ loadAccountsInBackground();
+ break;
+ case MESSAGE_PROCESS_BROADCAST_INTENT:
+ processBroadcastIntent((Intent) msg.obj);
+ break;
+ }
+ }
+ };
+
+ mInvitableAccountTypeCache = new InvitableAccountTypeCache();
+
+ // Request updates when packages or accounts change
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+
+ // Request updates when locale is changed so that the order of each field will
+ // be able to be changed on the locale change.
+ filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ mAccountManager.addOnAccountsUpdatedListener(this, mListenerHandler, false);
+
+ ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this);
+
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ @Override
+ public void onStatusChanged(int which) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ public void processBroadcastIntent(Intent intent) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ /* This notification will arrive on the background thread */
+ public void onAccountsUpdated(Account[] accounts) {
+ // Refresh to catch any changed accounts
+ loadAccountsInBackground();
+ }
+
+ /**
+ * Returns instantly if accounts and account types have already been loaded.
+ * Otherwise waits for the background thread to complete the loading.
+ */
+ void ensureAccountsLoaded() {
+ CountDownLatch latch = mInitializationLatch;
+ if (latch == null) {
+ return;
+ }
+ while (true) {
+ try {
+ latch.await();
+ return;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * Loads account list and corresponding account types (potentially with data sets). Always
+ * called on a background thread.
+ */
+ protected void loadAccountsInBackground() {
+ if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
+ Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground start");
+ }
+ TimingLogger timings = new TimingLogger(TAG, "loadAccountsInBackground");
+ final long startTime = SystemClock.currentThreadTimeMillis();
+ final long startTimeWall = SystemClock.elapsedRealtime();
+
+ // Account types, keyed off the account type and data set concatenation.
+ final Map accountTypesByTypeAndDataSet =
+ Maps.newHashMap();
+
+ // The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}. Since there can
+ // be multiple account types (with different data sets) for the same type of account, each
+ // type string may have multiple AccountType entries.
+ final Map> accountTypesByType = Maps.newHashMap();
+
+ final List allAccounts = Lists.newArrayList();
+ final List contactWritableAccounts = Lists.newArrayList();
+ final List groupWritableAccounts = Lists.newArrayList();
+ final Set extensionPackages = Sets.newHashSet();
+
+ final AccountManager am = mAccountManager;
+
+ final SyncAdapterType[] syncs = ContentResolver.getSyncAdapterTypes();
+ final AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
+
+ // First process sync adapters to find any that provide contact data.
+ for (SyncAdapterType sync : syncs) {
+ if (!ContactsContract.AUTHORITY.equals(sync.authority)) {
+ // Skip sync adapters that don't provide contact data.
+ continue;
+ }
+
+ // Look for the formatting details provided by each sync
+ // adapter, using the authenticator to find general resources.
+ final String type = sync.accountType;
+ final AuthenticatorDescription auth = findAuthenticator(auths, type);
+ if (auth == null) {
+ Log.w(TAG, "No authenticator found for type=" + type + ", ignoring it.");
+ continue;
+ }
+
+ AccountType accountType;
+ if (GoogleAccountType.ACCOUNT_TYPE.equals(type)) {
+ accountType = new GoogleAccountType(mContext, auth.packageName);
+ } else if (ExchangeAccountType.isExchangeType(type)) {
+ accountType = new ExchangeAccountType(mContext, auth.packageName, type);
+ } else if (SamsungAccountType.isSamsungAccountType(mContext, type,
+ auth.packageName)) {
+ accountType = new SamsungAccountType(mContext, auth.packageName, type);
+ } else {
+ Log.d(TAG, "Registering external account type=" + type
+ + ", packageName=" + auth.packageName);
+ accountType = new ExternalAccountType(mContext, auth.packageName, false);
+ }
+ if (!accountType.isInitialized()) {
+ if (accountType.isEmbedded()) {
+ throw new IllegalStateException("Problem initializing embedded type "
+ + accountType.getClass().getCanonicalName());
+ } else {
+ // Skip external account types that couldn't be initialized.
+ continue;
+ }
+ }
+
+ accountType.accountType = auth.type;
+ accountType.titleRes = auth.labelId;
+ accountType.iconRes = auth.iconId;
+
+ addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
+
+ // Check to see if the account type knows of any other non-sync-adapter packages
+ // that may provide other data sets of contact data.
+ extensionPackages.addAll(accountType.getExtensionPackageNames());
+ }
+
+ // If any extension packages were specified, process them as well.
+ if (!extensionPackages.isEmpty()) {
+ Log.d(TAG, "Registering " + extensionPackages.size() + " extension packages");
+ for (String extensionPackage : extensionPackages) {
+ ExternalAccountType accountType =
+ new ExternalAccountType(mContext, extensionPackage, true);
+ if (!accountType.isInitialized()) {
+ // Skip external account types that couldn't be initialized.
+ continue;
+ }
+ if (!accountType.hasContactsMetadata()) {
+ Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+ + " it doesn't have the CONTACTS_STRUCTURE metadata");
+ continue;
+ }
+ if (TextUtils.isEmpty(accountType.accountType)) {
+ Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+ + " the CONTACTS_STRUCTURE metadata doesn't have the accountType"
+ + " attribute");
+ continue;
+ }
+ Log.d(TAG, "Registering extension package account type="
+ + accountType.accountType + ", dataSet=" + accountType.dataSet
+ + ", packageName=" + extensionPackage);
+
+ addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
+ }
+ }
+ timings.addSplit("Loaded account types");
+
+ // Map in accounts to associate the account names with each account type entry.
+ Account[] accounts = mAccountManager.getAccounts();
+ for (Account account : accounts) {
+ boolean syncable =
+ ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
+
+ if (syncable) {
+ List accountTypes = accountTypesByType.get(account.type);
+ if (accountTypes != null) {
+ // Add an account-with-data-set entry for each account type that is
+ // authenticated by this account.
+ for (AccountType accountType : accountTypes) {
+ AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
+ account.name, account.type, accountType.dataSet);
+ allAccounts.add(accountWithDataSet);
+ if (accountType.areContactsWritable()) {
+ contactWritableAccounts.add(accountWithDataSet);
+ }
+ if (accountType.isGroupMembershipEditable()) {
+ groupWritableAccounts.add(accountWithDataSet);
+ }
+ }
+ }
+ }
+ }
+
+ Collections.sort(allAccounts, ACCOUNT_COMPARATOR);
+ Collections.sort(contactWritableAccounts, ACCOUNT_COMPARATOR);
+ Collections.sort(groupWritableAccounts, ACCOUNT_COMPARATOR);
+
+ timings.addSplit("Loaded accounts");
+
+ synchronized (this) {
+ mAccountTypesWithDataSets = accountTypesByTypeAndDataSet;
+ mAccounts = allAccounts;
+ mContactWritableAccounts = contactWritableAccounts;
+ mGroupWritableAccounts = groupWritableAccounts;
+ mInvitableAccountTypes = findAllInvitableAccountTypes(
+ mContext, allAccounts, accountTypesByTypeAndDataSet);
+ }
+
+ timings.dumpToLog();
+ final long endTimeWall = SystemClock.elapsedRealtime();
+ final long endTime = SystemClock.currentThreadTimeMillis();
+
+ Log.i(TAG, "Loaded meta-data for " + mAccountTypesWithDataSets.size() + " account types, "
+ + mAccounts.size() + " accounts in " + (endTimeWall - startTimeWall) + "ms(wall) "
+ + (endTime - startTime) + "ms(cpu)");
+
+ if (mInitializationLatch != null) {
+ mInitializationLatch.countDown();
+ mInitializationLatch = null;
+ }
+ if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
+ Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground finish");
+ }
+
+ // Check filter validity since filter may become obsolete after account update. It must be
+ // done from UI thread.
+ mMainThreadHandler.post(mCheckFilterValidityRunnable);
+ }
+
+ // Bookkeeping method for tracking the known account types in the given maps.
+ private void addAccountType(AccountType accountType,
+ Map accountTypesByTypeAndDataSet,
+ Map> accountTypesByType) {
+ accountTypesByTypeAndDataSet.put(accountType.getAccountTypeAndDataSet(), accountType);
+ List accountsForType = accountTypesByType.get(accountType.accountType);
+ if (accountsForType == null) {
+ accountsForType = Lists.newArrayList();
+ }
+ accountsForType.add(accountType);
+ accountTypesByType.put(accountType.accountType, accountsForType);
+ }
+
+ /**
+ * Find a specific {@link AuthenticatorDescription} in the provided list
+ * that matches the given account type.
+ */
+ protected static AuthenticatorDescription findAuthenticator(AuthenticatorDescription[] auths,
+ String accountType) {
+ for (AuthenticatorDescription auth : auths) {
+ if (accountType.equals(auth.type)) {
+ return auth;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return list of all known, contact writable {@link AccountWithDataSet}'s.
+ */
+ @Override
+ public List getAccounts(boolean contactWritableOnly) {
+ ensureAccountsLoaded();
+ return contactWritableOnly ? mContactWritableAccounts : mAccounts;
+ }
+
+ /**
+ * Return the list of all known, group writable {@link AccountWithDataSet}'s.
+ */
+ public List getGroupWritableAccounts() {
+ ensureAccountsLoaded();
+ return mGroupWritableAccounts;
+ }
+
+ /**
+ * Find the best {@link DataKind} matching the requested
+ * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
+ * If no direct match found, we try searching {@link FallbackAccountType}.
+ */
+ @Override
+ public DataKind getKindOrFallback(AccountType type, String mimeType) {
+ ensureAccountsLoaded();
+ DataKind kind = null;
+
+ // Try finding account type and kind matching request
+ if (type != null) {
+ kind = type.getKindForMimetype(mimeType);
+ }
+
+ if (kind == null) {
+ // Nothing found, so try fallback as last resort
+ kind = mFallbackAccountType.getKindForMimetype(mimeType);
+ }
+
+ if (kind == null) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Unknown type=" + type + ", mime=" + mimeType);
+ }
+ }
+
+ return kind;
+ }
+
+ /**
+ * Return {@link AccountType} for the given account type and data set.
+ */
+ @Override
+ public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
+ ensureAccountsLoaded();
+ synchronized (this) {
+ AccountType type = mAccountTypesWithDataSets.get(accountTypeWithDataSet);
+ return type != null ? type : mFallbackAccountType;
+ }
+ }
+
+ /**
+ * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s
+ * which support the "invite" feature and have one or more account. This is an unfiltered
+ * list. See {@link #getUsableInvitableAccountTypes()}.
+ */
+ private Map getAllInvitableAccountTypes() {
+ ensureAccountsLoaded();
+ return mInvitableAccountTypes;
+ }
+
+ @Override
+ public Map getUsableInvitableAccountTypes() {
+ ensureAccountsLoaded();
+ // Since this method is not thread-safe, it's possible for multiple threads to encounter
+ // the situation where (1) the cache has not been initialized yet or
+ // (2) an async task to refresh the account type list in the cache has already been
+ // started. Hence we use {@link AtomicBoolean}s and return cached values immediately
+ // while we compute the actual result in the background. We use this approach instead of
+ // using "synchronized" because computing the account type list involves a DB read, and
+ // can potentially cause a deadlock situation if this method is called from code which
+ // holds the DB lock. The trade-off of potentially having an incorrect list of invitable
+ // account types for a short period of time seems more manageable than enforcing the
+ // context in which this method is called.
+
+ // Computing the list of usable invitable account types is done on the fly as requested.
+ // If this method has never been called before, then block until the list has been computed.
+ if (!mInvitablesCacheIsInitialized.get()) {
+ mInvitableAccountTypeCache.setCachedValue(findUsableInvitableAccountTypes(mContext));
+ mInvitablesCacheIsInitialized.set(true);
+ } else {
+ // Otherwise, there is a value in the cache. If the value has expired and
+ // an async task has not already been started by another thread, then kick off a new
+ // async task to compute the list.
+ if (mInvitableAccountTypeCache.isExpired() &&
+ mInvitablesTaskIsRunning.compareAndSet(false, true)) {
+ new FindInvitablesTask().execute();
+ }
+ }
+
+ return mInvitableAccountTypeCache.getCachedValue();
+ }
+
+ /**
+ * Return all {@link AccountType}s with at least one account which supports "invite", i.e.
+ * its {@link AccountType#getInviteContactActivityClassName()} is not empty.
+ */
+ @VisibleForTesting
+ static Map findAllInvitableAccountTypes(Context context,
+ Collection accounts,
+ Map accountTypesByTypeAndDataSet) {
+ HashMap result = Maps.newHashMap();
+ for (AccountWithDataSet account : accounts) {
+ AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeWithDataSet();
+ AccountType type = accountTypesByTypeAndDataSet.get(accountTypeWithDataSet);
+ if (type == null) continue; // just in case
+ if (result.containsKey(accountTypeWithDataSet)) continue;
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Type " + accountTypeWithDataSet
+ + " inviteClass=" + type.getInviteContactActivityClassName());
+ }
+ if (!TextUtils.isEmpty(type.getInviteContactActivityClassName())) {
+ result.put(accountTypeWithDataSet, type);
+ }
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
+ /**
+ * Return all usable {@link AccountType}s that support the "invite" feature from the
+ * list of all potential invitable account types (retrieved from
+ * {@link #getAllInvitableAccountTypes}). A usable invitable account type means:
+ * (1) there is at least 1 raw contact in the database with that account type, and
+ * (2) the app contributing the account type is not disabled.
+ *
+ * Warning: Don't use on the UI thread because this can scan the database.
+ */
+ private Map findUsableInvitableAccountTypes(
+ Context context) {
+ Map allInvitables = getAllInvitableAccountTypes();
+ if (allInvitables.isEmpty()) {
+ return EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
+ }
+
+ final HashMap result = Maps.newHashMap();
+ result.putAll(allInvitables);
+
+ final PackageManager packageManager = context.getPackageManager();
+ for (AccountTypeWithDataSet accountTypeWithDataSet : allInvitables.keySet()) {
+ AccountType accountType = allInvitables.get(accountTypeWithDataSet);
+
+ // Make sure that account types don't come from apps that are disabled.
+ Intent invitableIntent = MoreContactUtils.getInvitableIntent(accountType,
+ SAMPLE_CONTACT_URI);
+ if (invitableIntent == null) {
+ result.remove(accountTypeWithDataSet);
+ continue;
+ }
+ ResolveInfo resolveInfo = packageManager.resolveActivity(invitableIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (resolveInfo == null) {
+ // If we can't find an activity to start for this intent, then there's no point in
+ // showing this option to the user.
+ result.remove(accountTypeWithDataSet);
+ continue;
+ }
+
+ // Make sure that there is at least 1 raw contact with this account type. This check
+ // is non-trivial and should not be done on the UI thread.
+ if (!accountTypeWithDataSet.hasData(context)) {
+ result.remove(accountTypeWithDataSet);
+ }
+ }
+
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public List getAccountTypes(boolean contactWritableOnly) {
+ ensureAccountsLoaded();
+ final List accountTypes = Lists.newArrayList();
+ synchronized (this) {
+ for (AccountType type : mAccountTypesWithDataSets.values()) {
+ if (!contactWritableOnly || type.areContactsWritable()) {
+ accountTypes.add(type);
+ }
+ }
+ }
+ return accountTypes;
+ }
+
+ /**
+ * Background task to find all usable {@link AccountType}s that support the "invite" feature
+ * from the list of all potential invitable account types. Once the work is completed,
+ * the list of account types is stored in the {@link AccountTypeManager}'s
+ * {@link InvitableAccountTypeCache}.
+ */
+ private class FindInvitablesTask extends AsyncTask> {
+
+ @Override
+ protected Map doInBackground(Void... params) {
+ return findUsableInvitableAccountTypes(mContext);
+ }
+
+ @Override
+ protected void onPostExecute(Map accountTypes) {
+ mInvitableAccountTypeCache.setCachedValue(accountTypes);
+ mInvitablesTaskIsRunning.set(false);
+ }
+ }
+
+ /**
+ * This cache holds a list of invitable {@link AccountTypeWithDataSet}s, in the form of a
+ * {@link Map}. Note that the cached value is valid only
+ * for {@link #TIME_TO_LIVE} milliseconds.
+ */
+ private static final class InvitableAccountTypeCache {
+
+ /**
+ * The cached {@link #mInvitableAccountTypes} list expires after this number of milliseconds
+ * has elapsed.
+ */
+ private static final long TIME_TO_LIVE = 60000;
+
+ private Map mInvitableAccountTypes;
+
+ private long mTimeLastSet;
+
+ /**
+ * Returns true if the data in this cache is stale and needs to be refreshed. Returns false
+ * otherwise.
+ */
+ public boolean isExpired() {
+ return SystemClock.elapsedRealtime() - mTimeLastSet > TIME_TO_LIVE;
+ }
+
+ /**
+ * Returns the cached value. Note that the caller is responsible for checking
+ * {@link #isExpired()} to ensure that the value is not stale.
+ */
+ public Map getCachedValue() {
+ return mInvitableAccountTypes;
+ }
+
+ public void setCachedValue(Map map) {
+ mInvitableAccountTypes = map;
+ mTimeLastSet = SystemClock.elapsedRealtime();
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/model/Contact.java b/ContactsCommon/src/com/android/contacts/common/model/Contact.java
new file mode 100644
index 0000000..9b96f86
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/model/Contact.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.model;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.DisplayNameSources;
+
+import com.android.contacts.common.GroupMetaData;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.util.DataStatus;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * A Contact represents a single person or logical entity as perceived by the user. The information
+ * about a contact can come from multiple data sources, which are each represented by a RawContact
+ * object. Thus, a Contact is associated with a collection of RawContact objects.
+ *
+ * The aggregation of raw contacts into a single contact is performed automatically, and it is
+ * also possible for users to manually split and join raw contacts into various contacts.
+ *
+ * Only the {@link ContactLoader} class can create a Contact object with various flags to allow
+ * partial loading of contact data. Thus, an instance of this class should be treated as
+ * a read-only object.
+ */
+public class Contact {
+ private enum Status {
+ /** Contact is successfully loaded */
+ LOADED,
+ /** There was an error loading the contact */
+ ERROR,
+ /** Contact is not found */
+ NOT_FOUND,
+ }
+
+ private final Uri mRequestedUri;
+ private final Uri mLookupUri;
+ private final Uri mUri;
+ private final long mDirectoryId;
+ private final String mLookupKey;
+ private final long mId;
+ private final long mNameRawContactId;
+ private final int mDisplayNameSource;
+ private final long mPhotoId;
+ private final String mPhotoUri;
+ private final String mDisplayName;
+ private final String mAltDisplayName;
+ private final String mPhoneticName;
+ private final boolean mStarred;
+ private final Integer mPresence;
+ private ImmutableList mRawContacts;
+ private ImmutableMap mStatuses;
+ private ImmutableList mInvitableAccountTypes;
+
+ private String mDirectoryDisplayName;
+ private String mDirectoryType;
+ private String mDirectoryAccountType;
+ private String mDirectoryAccountName;
+ private int mDirectoryExportSupport;
+
+ private ImmutableList mGroups;
+
+ private byte[] mPhotoBinaryData;
+ /**
+ * Small version of the contact photo loaded from a blob instead of from a file. If a large
+ * contact photo is not available yet, then this has the same value as mPhotoBinaryData.
+ */
+ private byte[] mThumbnailPhotoBinaryData;
+ private final boolean mSendToVoicemail;
+ private final String mCustomRingtone;
+ private final boolean mIsUserProfile;
+
+ private final Contact.Status mStatus;
+ private final Exception mException;
+
+ /**
+ * Constructor for special results, namely "no contact found" and "error".
+ */
+ private Contact(Uri requestedUri, Contact.Status status, Exception exception) {
+ if (status == Status.ERROR && exception == null) {
+ throw new IllegalArgumentException("ERROR result must have exception");
+ }
+ mStatus = status;
+ mException = exception;
+ mRequestedUri = requestedUri;
+ mLookupUri = null;
+ mUri = null;
+ mDirectoryId = -1;
+ mLookupKey = null;
+ mId = -1;
+ mRawContacts = null;
+ mStatuses = null;
+ mNameRawContactId = -1;
+ mDisplayNameSource = DisplayNameSources.UNDEFINED;
+ mPhotoId = -1;
+ mPhotoUri = null;
+ mDisplayName = null;
+ mAltDisplayName = null;
+ mPhoneticName = null;
+ mStarred = false;
+ mPresence = null;
+ mInvitableAccountTypes = null;
+ mSendToVoicemail = false;
+ mCustomRingtone = null;
+ mIsUserProfile = false;
+ }
+
+ public static Contact forError(Uri requestedUri, Exception exception) {
+ return new Contact(requestedUri, Status.ERROR, exception);
+ }
+
+ public static Contact forNotFound(Uri requestedUri) {
+ return new Contact(requestedUri, Status.NOT_FOUND, null);
+ }
+
+ /**
+ * Constructor to call when contact was found
+ */
+ public Contact(Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey,
+ long id, long nameRawContactId, int displayNameSource, long photoId,
+ String photoUri, String displayName, String altDisplayName, String phoneticName,
+ boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
+ boolean isUserProfile) {
+ mStatus = Status.LOADED;
+ mException = null;
+ mRequestedUri = requestedUri;
+ mLookupUri = lookupUri;
+ mUri = uri;
+ mDirectoryId = directoryId;
+ mLookupKey = lookupKey;
+ mId = id;
+ mRawContacts = null;
+ mStatuses = null;
+ mNameRawContactId = nameRawContactId;
+ mDisplayNameSource = displayNameSource;
+ mPhotoId = photoId;
+ mPhotoUri = photoUri;
+ mDisplayName = displayName;
+ mAltDisplayName = altDisplayName;
+ mPhoneticName = phoneticName;
+ mStarred = starred;
+ mPresence = presence;
+ mInvitableAccountTypes = null;
+ mSendToVoicemail = sendToVoicemail;
+ mCustomRingtone = customRingtone;
+ mIsUserProfile = isUserProfile;
+ }
+
+ public Contact(Uri requestedUri, Contact from) {
+ mRequestedUri = requestedUri;
+
+ mStatus = from.mStatus;
+ mException = from.mException;
+ mLookupUri = from.mLookupUri;
+ mUri = from.mUri;
+ mDirectoryId = from.mDirectoryId;
+ mLookupKey = from.mLookupKey;
+ mId = from.mId;
+ mNameRawContactId = from.mNameRawContactId;
+ mDisplayNameSource = from.mDisplayNameSource;
+ mPhotoId = from.mPhotoId;
+ mPhotoUri = from.mPhotoUri;
+ mDisplayName = from.mDisplayName;
+ mAltDisplayName = from.mAltDisplayName;
+ mPhoneticName = from.mPhoneticName;
+ mStarred = from.mStarred;
+ mPresence = from.mPresence;
+ mRawContacts = from.mRawContacts;
+ mStatuses = from.mStatuses;
+ mInvitableAccountTypes = from.mInvitableAccountTypes;
+
+ mDirectoryDisplayName = from.mDirectoryDisplayName;
+ mDirectoryType = from.mDirectoryType;
+ mDirectoryAccountType = from.mDirectoryAccountType;
+ mDirectoryAccountName = from.mDirectoryAccountName;
+ mDirectoryExportSupport = from.mDirectoryExportSupport;
+
+ mGroups = from.mGroups;
+
+ mPhotoBinaryData = from.mPhotoBinaryData;
+ mSendToVoicemail = from.mSendToVoicemail;
+ mCustomRingtone = from.mCustomRingtone;
+ mIsUserProfile = from.mIsUserProfile;
+ }
+
+ /**
+ * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
+ */
+ public void setDirectoryMetaData(String displayName, String directoryType,
+ String accountType, String accountName, int exportSupport) {
+ mDirectoryDisplayName = displayName;
+ mDirectoryType = directoryType;
+ mDirectoryAccountType = accountType;
+ mDirectoryAccountName = accountName;
+ mDirectoryExportSupport = exportSupport;
+ }
+
+ /* package */ void setPhotoBinaryData(byte[] photoBinaryData) {
+ mPhotoBinaryData = photoBinaryData;
+ }
+
+ /* package */ void setThumbnailPhotoBinaryData(byte[] photoBinaryData) {
+ mThumbnailPhotoBinaryData = photoBinaryData;
+ }
+
+ /**
+ * Returns the URI for the contact that contains both the lookup key and the ID. This is
+ * the best URI to reference a contact.
+ * For directory contacts, this is the same a the URI as returned by {@link #getUri()}
+ */
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ public String getLookupKey() {
+ return mLookupKey;
+ }
+
+ /**
+ * Returns the contact Uri that was passed to the provider to make the query. This is
+ * the same as the requested Uri, unless the requested Uri doesn't specify a Contact:
+ * If it either references a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will
+ * always reference the full aggregate contact.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Returns the URI for which this {@link ContactLoader) was initially requested.
+ */
+ public Uri getRequestedUri() {
+ return mRequestedUri;
+ }
+
+ /**
+ * Instantiate a new RawContactDeltaList for this contact.
+ */
+ public RawContactDeltaList createRawContactDeltaList() {
+ return RawContactDeltaList.fromIterator(getRawContacts().iterator());
+ }
+
+ /**
+ * Returns the contact ID.
+ */
+ @VisibleForTesting
+ /* package */ long getId() {
+ return mId;
+ }
+
+ /**
+ * @return true when an exception happened during loading, in which case
+ * {@link #getException} returns the actual exception object.
+ * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
+ * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
+ * and vice versa.
+ */
+ public boolean isError() {
+ return mStatus == Status.ERROR;
+ }
+
+ public Exception getException() {
+ return mException;
+ }
+
+ /**
+ * @return true when the specified contact is not found.
+ * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
+ * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
+ * and vice versa.
+ */
+ public boolean isNotFound() {
+ return mStatus == Status.NOT_FOUND;
+ }
+
+ /**
+ * @return true if the specified contact is successfully loaded.
+ * i.e. neither {@link #isError()} nor {@link #isNotFound()}.
+ */
+ public boolean isLoaded() {
+ return mStatus == Status.LOADED;
+ }
+
+ public long getNameRawContactId() {
+ return mNameRawContactId;
+ }
+
+ public int getDisplayNameSource() {
+ return mDisplayNameSource;
+ }
+
+ /**
+ * Used by various classes to determine whether or not this contact should be displayed as
+ * a business rather than a person.
+ */
+ public boolean isDisplayNameFromOrganization() {
+ return DisplayNameSources.ORGANIZATION == mDisplayNameSource;
+ }
+
+ public long getPhotoId() {
+ return mPhotoId;
+ }
+
+ public String getPhotoUri() {
+ return mPhotoUri;
+ }
+
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public String getAltDisplayName() {
+ return mAltDisplayName;
+ }
+
+ public String getPhoneticName() {
+ return mPhoneticName;
+ }
+
+ public boolean getStarred() {
+ return mStarred;
+ }
+
+ public Integer getPresence() {
+ return mPresence;
+ }
+
+ /**
+ * This can return non-null invitable account types only if the {@link ContactLoader} was
+ * configured to load invitable account types in its constructor.
+ * @return
+ */
+ public ImmutableList getInvitableAccountTypes() {
+ return mInvitableAccountTypes;
+ }
+
+ public ImmutableList getRawContacts() {
+ return mRawContacts;
+ }
+
+ public ImmutableMap getStatuses() {
+ return mStatuses;
+ }
+
+ public long getDirectoryId() {
+ return mDirectoryId;
+ }
+
+ public boolean isDirectoryEntry() {
+ return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
+ && mDirectoryId != Directory.LOCAL_INVISIBLE;
+ }
+
+ /**
+ * @return true if this is a contact (not group, etc.) with at least one
+ * writable raw-contact, and false otherwise.
+ */
+ public boolean isWritableContact(final Context context) {
+ return getFirstWritableRawContactId(context) != -1;
+ }
+
+ /**
+ * Return the ID of the first raw-contact in the contact data that belongs to a
+ * contact-writable account, or -1 if no such entity exists.
+ */
+ public long getFirstWritableRawContactId(final Context context) {
+ // Directory entries are non-writable
+ if (isDirectoryEntry()) return -1;
+
+ // Iterate through raw-contacts; if we find a writable on, return its ID.
+ for (RawContact rawContact : getRawContacts()) {
+ AccountType accountType = rawContact.getAccountType(context);
+ if (accountType != null && accountType.areContactsWritable()) {
+ return rawContact.getId();
+ }
+ }
+ // No writable raw-contact was found.
+ return -1;
+ }
+
+ public int getDirectoryExportSupport() {
+ return mDirectoryExportSupport;
+ }
+
+ public String getDirectoryDisplayName() {
+ return mDirectoryDisplayName;
+ }
+
+ public String getDirectoryType() {
+ return mDirectoryType;
+ }
+
+ public String getDirectoryAccountType() {
+ return mDirectoryAccountType;
+ }
+
+ public String getDirectoryAccountName() {
+ return mDirectoryAccountName;
+ }
+
+ public byte[] getPhotoBinaryData() {
+ return mPhotoBinaryData;
+ }
+
+ public byte[] getThumbnailPhotoBinaryData() {
+ return mThumbnailPhotoBinaryData;
+ }
+
+ public ArrayList getContentValues() {
+ if (mRawContacts.size() != 1) {
+ throw new IllegalStateException(
+ "Cannot extract content values from an aggregated contact");
+ }
+
+ RawContact rawContact = mRawContacts.get(0);
+ ArrayList result = rawContact.getContentValues();
+
+ // If the photo was loaded using the URI, create an entry for the photo
+ // binary data.
+ if (mPhotoId == 0 && mPhotoBinaryData != null) {
+ ContentValues photo = new ContentValues();
+ photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ photo.put(Photo.PHOTO, mPhotoBinaryData);
+ result.add(photo);
+ }
+
+ return result;
+ }
+
+ /**
+ * This can return non-null group meta-data only if the {@link ContactLoader} was configured to
+ * load group metadata in its constructor.
+ * @return
+ */
+ public ImmutableList getGroupMetaData() {
+ return mGroups;
+ }
+
+ public boolean isSendToVoicemail() {
+ return mSendToVoicemail;
+ }
+
+ public String getCustomRingtone() {
+ return mCustomRingtone;
+ }
+
+ public boolean isUserProfile() {
+ return mIsUserProfile;
+ }
+
+ @Override
+ public String toString() {
+ return "{requested=" + mRequestedUri + ",lookupkey=" + mLookupKey +
+ ",uri=" + mUri + ",status=" + mStatus + "}";
+ }
+
+ /* package */ void setRawContacts(ImmutableList rawContacts) {
+ mRawContacts = rawContacts;
+ }
+
+ /* package */ void setStatuses(ImmutableMap statuses) {
+ mStatuses = statuses;
+ }
+
+ /* package */ void setInvitableAccountTypes(ImmutableList accountTypes) {
+ mInvitableAccountTypes = accountTypes;
+ }
+
+ /* package */ void setGroupMetaData(ImmutableList groups) {
+ mGroups = groups;
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/model/ContactLoader.java b/ContactsCommon/src/com/android/contacts/common/model/ContactLoader.java
new file mode 100644
index 0000000..59ab292
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/model/ContactLoader.java
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.common.model;
+
+import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.GroupMetaData;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountTypeWithDataSet;
+import com.android.contacts.common.util.Constants;
+import com.android.contacts.common.util.ContactLoaderUtils;
+import com.android.contacts.common.util.DataStatus;
+import com.android.contacts.common.util.UriUtils;
+import com.android.contacts.common.model.dataitem.DataItem;
+import com.android.contacts.common.model.dataitem.PhoneDataItem;
+import com.android.contacts.common.model.dataitem.PhotoDataItem;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Loads a single Contact and all it constituent RawContacts.
+ */
+public class ContactLoader extends AsyncTaskLoader {
+
+ private static final String TAG = ContactLoader.class.getSimpleName();
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /** A short-lived cache that can be set by {@link #cacheResult()} */
+ private static Contact sCachedResult = null;
+
+ private final Uri mRequestedUri;
+ private Uri mLookupUri;
+ private boolean mLoadGroupMetaData;
+ private boolean mLoadInvitableAccountTypes;
+ private boolean mPostViewNotification;
+ private boolean mComputeFormattedPhoneNumber;
+ private Contact mContact;
+ private ForceLoadContentObserver mObserver;
+ private final Set mNotifiedRawContactIds = Sets.newHashSet();
+
+ public ContactLoader(Context context, Uri lookupUri, boolean postViewNotification) {
+ this(context, lookupUri, false, false, postViewNotification, false);
+ }
+
+ public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
+ boolean loadInvitableAccountTypes,
+ boolean postViewNotification, boolean computeFormattedPhoneNumber) {
+ super(context);
+ mLookupUri = lookupUri;
+ mRequestedUri = lookupUri;
+ mLoadGroupMetaData = loadGroupMetaData;
+ mLoadInvitableAccountTypes = loadInvitableAccountTypes;
+ mPostViewNotification = postViewNotification;
+ mComputeFormattedPhoneNumber = computeFormattedPhoneNumber;
+ }
+
+ /**
+ * Projection used for the query that loads all data for the entire contact (except for
+ * social stream items).
+ */
+ private static class ContactQuery {
+ static final String[] COLUMNS = new String[] {
+ Contacts.NAME_RAW_CONTACT_ID,
+ Contacts.DISPLAY_NAME_SOURCE,
+ Contacts.LOOKUP_KEY,
+ Contacts.DISPLAY_NAME,
+ Contacts.DISPLAY_NAME_ALTERNATIVE,
+ Contacts.PHONETIC_NAME,
+ Contacts.PHOTO_ID,
+ Contacts.STARRED,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_STATUS,
+ Contacts.CONTACT_STATUS_TIMESTAMP,
+ Contacts.CONTACT_STATUS_RES_PACKAGE,
+ Contacts.CONTACT_STATUS_LABEL,
+ Contacts.Entity.CONTACT_ID,
+ Contacts.Entity.RAW_CONTACT_ID,
+
+ RawContacts.ACCOUNT_NAME,
+ RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
+ RawContacts.DIRTY,
+ RawContacts.VERSION,
+ RawContacts.SOURCE_ID,
+ RawContacts.SYNC1,
+ RawContacts.SYNC2,
+ RawContacts.SYNC3,
+ RawContacts.SYNC4,
+ RawContacts.DELETED,
+
+ Contacts.Entity.DATA_ID,
+ Data.DATA1,
+ Data.DATA2,
+ Data.DATA3,
+ Data.DATA4,
+ Data.DATA5,
+ Data.DATA6,
+ Data.DATA7,
+ Data.DATA8,
+ Data.DATA9,
+ Data.DATA10,
+ Data.DATA11,
+ Data.DATA12,
+ Data.DATA13,
+ Data.DATA14,
+ Data.DATA15,
+ Data.SYNC1,
+ Data.SYNC2,
+ Data.SYNC3,
+ Data.SYNC4,
+ Data.DATA_VERSION,
+ Data.IS_PRIMARY,
+ Data.IS_SUPER_PRIMARY,
+ Data.MIMETYPE,
+
+ GroupMembership.GROUP_SOURCE_ID,
+
+ Data.PRESENCE,
+ Data.CHAT_CAPABILITY,
+ Data.STATUS,
+ Data.STATUS_RES_PACKAGE,
+ Data.STATUS_ICON,
+ Data.STATUS_LABEL,
+ Data.STATUS_TIMESTAMP,
+
+ Contacts.PHOTO_URI,
+ Contacts.SEND_TO_VOICEMAIL,
+ Contacts.CUSTOM_RINGTONE,
+ Contacts.IS_USER_PROFILE,
+
+ Data.TIMES_USED,
+ Data.LAST_TIME_USED,
+ };
+
+ public static final int NAME_RAW_CONTACT_ID = 0;
+ public static final int DISPLAY_NAME_SOURCE = 1;
+ public static final int LOOKUP_KEY = 2;
+ public static final int DISPLAY_NAME = 3;
+ public static final int ALT_DISPLAY_NAME = 4;
+ public static final int PHONETIC_NAME = 5;
+ public static final int PHOTO_ID = 6;
+ public static final int STARRED = 7;
+ public static final int CONTACT_PRESENCE = 8;
+ public static final int CONTACT_STATUS = 9;
+ public static final int CONTACT_STATUS_TIMESTAMP = 10;
+ public static final int CONTACT_STATUS_RES_PACKAGE = 11;
+ public static final int CONTACT_STATUS_LABEL = 12;
+ public static final int CONTACT_ID = 13;
+ public static final int RAW_CONTACT_ID = 14;
+
+ public static final int ACCOUNT_NAME = 15;
+ public static final int ACCOUNT_TYPE = 16;
+ public static final int DATA_SET = 17;
+ public static final int DIRTY = 18;
+ public static final int VERSION = 19;
+ public static final int SOURCE_ID = 20;
+ public static final int SYNC1 = 21;
+ public static final int SYNC2 = 22;
+ public static final int SYNC3 = 23;
+ public static final int SYNC4 = 24;
+ public static final int DELETED = 25;
+
+ public static final int DATA_ID = 26;
+ public static final int DATA1 = 27;
+ public static final int DATA2 = 28;
+ public static final int DATA3 = 29;
+ public static final int DATA4 = 30;
+ public static final int DATA5 = 31;
+ public static final int DATA6 = 32;
+ public static final int DATA7 = 33;
+ public static final int DATA8 = 34;
+ public static final int DATA9 = 35;
+ public static final int DATA10 = 36;
+ public static final int DATA11 = 37;
+ public static final int DATA12 = 38;
+ public static final int DATA13 = 39;
+ public static final int DATA14 = 40;
+ public static final int DATA15 = 41;
+ public static final int DATA_SYNC1 = 42;
+ public static final int DATA_SYNC2 = 43;
+ public static final int DATA_SYNC3 = 44;
+ public static final int DATA_SYNC4 = 45;
+ public static final int DATA_VERSION = 46;
+ public static final int IS_PRIMARY = 47;
+ public static final int IS_SUPERPRIMARY = 48;
+ public static final int MIMETYPE = 49;
+
+ public static final int GROUP_SOURCE_ID = 50;
+
+ public static final int PRESENCE = 51;
+ public static final int CHAT_CAPABILITY = 52;
+ public static final int STATUS = 53;
+ public static final int STATUS_RES_PACKAGE = 54;
+ public static final int STATUS_ICON = 55;
+ public static final int STATUS_LABEL = 56;
+ public static final int STATUS_TIMESTAMP = 57;
+
+ public static final int PHOTO_URI = 58;
+ public static final int SEND_TO_VOICEMAIL = 59;
+ public static final int CUSTOM_RINGTONE = 60;
+ public static final int IS_USER_PROFILE = 61;
+
+ public static final int TIMES_USED = 62;
+ public static final int LAST_TIME_USED = 63;
+ }
+
+ /**
+ * Projection used for the query that loads all data for the entire contact.
+ */
+ private static class DirectoryQuery {
+ static final String[] COLUMNS = new String[] {
+ Directory.DISPLAY_NAME,
+ Directory.PACKAGE_NAME,
+ Directory.TYPE_RESOURCE_ID,
+ Directory.ACCOUNT_TYPE,
+ Directory.ACCOUNT_NAME,
+ Directory.EXPORT_SUPPORT,
+ };
+
+ public static final int DISPLAY_NAME = 0;
+ public static final int PACKAGE_NAME = 1;
+ public static final int TYPE_RESOURCE_ID = 2;
+ public static final int ACCOUNT_TYPE = 3;
+ public static final int ACCOUNT_NAME = 4;
+ public static final int EXPORT_SUPPORT = 5;
+ }
+
+ private static class GroupQuery {
+ static final String[] COLUMNS = new String[] {
+ Groups.ACCOUNT_NAME,
+ Groups.ACCOUNT_TYPE,
+ Groups.DATA_SET,
+ Groups._ID,
+ Groups.TITLE,
+ Groups.AUTO_ADD,
+ Groups.FAVORITES,
+ };
+
+ public static final int ACCOUNT_NAME = 0;
+ public static final int ACCOUNT_TYPE = 1;
+ public static final int DATA_SET = 2;
+ public static final int ID = 3;
+ public static final int TITLE = 4;
+ public static final int AUTO_ADD = 5;
+ public static final int FAVORITES = 6;
+ }
+
+ @Override
+ public Contact loadInBackground() {
+ try {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(
+ resolver, mLookupUri);
+ final Contact cachedResult = sCachedResult;
+ sCachedResult = null;
+ // Is this the same Uri as what we had before already? In that case, reuse that result
+ final Contact result;
+ final boolean resultIsCached;
+ if (cachedResult != null &&
+ UriUtils.areEqual(cachedResult.getLookupUri(), mLookupUri)) {
+ // We are using a cached result from earlier. Below, we should make sure
+ // we are not doing any more network or disc accesses
+ result = new Contact(mRequestedUri, cachedResult);
+ resultIsCached = true;
+ } else {
+ if (uriCurrentFormat.getLastPathSegment().equals(Constants.LOOKUP_URI_ENCODED)) {
+ result = loadEncodedContactEntity(uriCurrentFormat, mLookupUri);
+ } else {
+ result = loadContactEntity(resolver, uriCurrentFormat);
+ }
+ resultIsCached = false;
+ }
+ if (result.isLoaded()) {
+ if (result.isDirectoryEntry()) {
+ if (!resultIsCached) {
+ loadDirectoryMetaData(result);
+ }
+ } else if (mLoadGroupMetaData) {
+ if (result.getGroupMetaData() == null) {
+ loadGroupMetaData(result);
+ }
+ }
+ if (mComputeFormattedPhoneNumber) {
+ computeFormattedPhoneNumbers(result);
+ }
+ if (!resultIsCached) loadPhotoBinaryData(result);
+
+ // Note ME profile should never have "Add connection"
+ if (mLoadInvitableAccountTypes && result.getInvitableAccountTypes() == null) {
+ loadInvitableAccountTypes(result);
+ }
+ }
+ return result;
+ } catch (Exception e) {
+ Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
+ return Contact.forError(mRequestedUri, e);
+ }
+ }
+
+ /**
+ * Parses a {@link Contact} stored as a JSON string in a lookup URI.
+ *
+ * @param lookupUri The contact information to parse .
+ * @return The parsed {@code Contact} information.
+ * @throws JSONException
+ */
+ public static Contact parseEncodedContactEntity(Uri lookupUri) {
+ try {
+ return loadEncodedContactEntity(lookupUri, lookupUri);
+ } catch (JSONException je) {
+ return null;
+ }
+ }
+
+ private static Contact loadEncodedContactEntity(Uri uri, Uri lookupUri) throws JSONException {
+ final String jsonString = uri.getEncodedFragment();
+ final JSONObject json = new JSONObject(jsonString);
+
+ final long directoryId =
+ Long.valueOf(uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY));
+
+ final String displayName = json.optString(Contacts.DISPLAY_NAME);
+ final String altDisplayName = json.optString(
+ Contacts.DISPLAY_NAME_ALTERNATIVE, displayName);
+ final int displayNameSource = json.getInt(Contacts.DISPLAY_NAME_SOURCE);
+ final String photoUri = json.optString(Contacts.PHOTO_URI, null);
+ final Contact contact = new Contact(
+ uri, uri,
+ lookupUri,
+ directoryId,
+ null /* lookupKey */,
+ -1 /* id */,
+ -1 /* nameRawContactId */,
+ displayNameSource,
+ 0 /* photoId */,
+ photoUri,
+ displayName,
+ altDisplayName,
+ null /* phoneticName */,
+ false /* starred */,
+ null /* presence */,
+ false /* sendToVoicemail */,
+ null /* customRingtone */,
+ false /* isUserProfile */);
+
+ contact.setStatuses(new ImmutableMap.Builder().build());
+
+ final String accountName = json.optString(RawContacts.ACCOUNT_NAME, null);
+ final String directoryName = uri.getQueryParameter(Directory.DISPLAY_NAME);
+ if (accountName != null) {
+ final String accountType = json.getString(RawContacts.ACCOUNT_TYPE);
+ contact.setDirectoryMetaData(directoryName, null, accountName, accountType,
+ json.optInt(Directory.EXPORT_SUPPORT,
+ Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY));
+ } else {
+ contact.setDirectoryMetaData(directoryName, null, null, null,
+ json.optInt(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_ANY_ACCOUNT));
+ }
+
+ final ContentValues values = new ContentValues();
+ values.put(Data._ID, -1);
+ values.put(Data.CONTACT_ID, -1);
+ final RawContact rawContact = new RawContact(values);
+
+ final JSONObject items = json.getJSONObject(Contacts.CONTENT_ITEM_TYPE);
+ final Iterator keys = items.keys();
+ while (keys.hasNext()) {
+ final String mimetype = (String) keys.next();
+
+ // Could be single object or array.
+ final JSONObject obj = items.optJSONObject(mimetype);
+ if (obj == null) {
+ final JSONArray array = items.getJSONArray(mimetype);
+ for (int i = 0; i < array.length(); i++) {
+ final JSONObject item = array.getJSONObject(i);
+ processOneRecord(rawContact, item, mimetype);
+ }
+ } else {
+ processOneRecord(rawContact, obj, mimetype);
+ }
+ }
+
+ contact.setRawContacts(new ImmutableList.Builder()
+ .add(rawContact)
+ .build());
+ return contact;
+ }
+
+ private static void processOneRecord(RawContact rawContact, JSONObject item, String mimetype)
+ throws JSONException {
+ final ContentValues itemValues = new ContentValues();
+ itemValues.put(Data.MIMETYPE, mimetype);
+ itemValues.put(Data._ID, -1);
+
+ final Iterator iterator = item.keys();
+ while (iterator.hasNext()) {
+ String name = (String) iterator.next();
+ final Object o = item.get(name);
+ if (o instanceof String) {
+ itemValues.put(name, (String) o);
+ } else if (o instanceof Integer) {
+ itemValues.put(name, (Integer) o);
+ }
+ }
+ rawContact.addDataItemValues(itemValues);
+ }
+
+ private Contact loadContactEntity(ContentResolver resolver, Uri contactUri) {
+ Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
+ Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
+ Contacts.Entity.RAW_CONTACT_ID);
+ if (cursor == null) {
+ Log.e(TAG, "No cursor returned in loadContactEntity");
+ return Contact.forNotFound(mRequestedUri);
+ }
+
+ try {
+ if (!cursor.moveToFirst()) {
+ cursor.close();
+ return Contact.forNotFound(mRequestedUri);
+ }
+
+ // Create the loaded contact starting with the header data.
+ Contact contact = loadContactHeaderData(cursor, contactUri);
+
+ // Fill in the raw contacts, which is wrapped in an Entity and any
+ // status data. Initially, result has empty entities and statuses.
+ long currentRawContactId = -1;
+ RawContact rawContact = null;
+ ImmutableList.Builder rawContactsBuilder =
+ new ImmutableList.Builder();
+ ImmutableMap.Builder statusesBuilder =
+ new ImmutableMap.Builder();
+ do {
+ long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
+ if (rawContactId != currentRawContactId) {
+ // First time to see this raw contact id, so create a new entity, and
+ // add it to the result's entities.
+ currentRawContactId = rawContactId;
+ rawContact = new RawContact(loadRawContactValues(cursor));
+ rawContactsBuilder.add(rawContact);
+ }
+ if (!cursor.isNull(ContactQuery.DATA_ID)) {
+ ContentValues data = loadDataValues(cursor);
+ rawContact.addDataItemValues(data);
+
+ if (!cursor.isNull(ContactQuery.PRESENCE)
+ || !cursor.isNull(ContactQuery.STATUS)) {
+ final DataStatus status = new DataStatus(cursor);
+ final long dataId = cursor.getLong(ContactQuery.DATA_ID);
+ statusesBuilder.put(dataId, status);
+ }
+ }
+ } while (cursor.moveToNext());
+
+ contact.setRawContacts(rawContactsBuilder.build());
+ contact.setStatuses(statusesBuilder.build());
+
+ return contact;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Looks for the photo data item in entities. If found, a thumbnail will be stored. A larger
+ * photo will also be stored if available.
+ */
+ private void loadPhotoBinaryData(Contact contactData) {
+ loadThumbnailBinaryData(contactData);
+
+ // Try to load the large photo from a file using the photo URI.
+ String photoUri = contactData.getPhotoUri();
+ if (photoUri != null) {
+ try {
+ final InputStream inputStream;
+ final AssetFileDescriptor fd;
+ final Uri uri = Uri.parse(photoUri);
+ final String scheme = uri.getScheme();
+ if ("http".equals(scheme) || "https".equals(scheme)) {
+ // Support HTTP urls that might come from extended directories
+ inputStream = new URL(photoUri).openStream();
+ fd = null;
+ } else {
+ fd = getContext().getContentResolver().openAssetFileDescriptor(uri, "r");
+ inputStream = fd.createInputStream();
+ }
+ byte[] buffer = new byte[16 * 1024];
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ int size;
+ while ((size = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, size);
+ }
+ contactData.setPhotoBinaryData(baos.toByteArray());
+ } finally {
+ inputStream.close();
+ if (fd != null) {
+ fd.close();
+ }
+ }
+ return;
+ } catch (IOException ioe) {
+ // Just fall back to the case below.
+ }
+ }
+
+ // If we couldn't load from a file, fall back to the data blob.
+ contactData.setPhotoBinaryData(contactData.getThumbnailPhotoBinaryData());
+ }
+
+ private void loadThumbnailBinaryData(Contact contactData) {
+ final long photoId = contactData.getPhotoId();
+ if (photoId <= 0) {
+ // No photo ID
+ return;
+ }
+
+ for (RawContact rawContact : contactData.getRawContacts()) {
+ for (DataItem dataItem : rawContact.getDataItems()) {
+ if (dataItem.getId() == photoId) {
+ if (!(dataItem instanceof PhotoDataItem)) {
+ break;
+ }
+
+ final PhotoDataItem photo = (PhotoDataItem) dataItem;
+ contactData.setThumbnailPhotoBinaryData(photo.getPhoto());
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the "invitable" account types to {@link Contact#mInvitableAccountTypes}.
+ */
+ private void loadInvitableAccountTypes(Contact contactData) {
+ final ImmutableList.Builder resultListBuilder =
+ new ImmutableList.Builder();
+ if (!contactData.isUserProfile()) {
+ Map invitables =
+ AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
+ if (!invitables.isEmpty()) {
+ final Map resultMap =
+ Maps.newHashMap(invitables);
+
+ // Remove the ones that already have a raw contact in the current contact
+ for (RawContact rawContact : contactData.getRawContacts()) {
+ final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
+ rawContact.getAccountTypeString(),
+ rawContact.getDataSet());
+ resultMap.remove(type);
+ }
+
+ resultListBuilder.addAll(resultMap.values());
+ }
+ }
+
+ // Set to mInvitableAccountTypes
+ contactData.setInvitableAccountTypes(resultListBuilder.build());
+ }
+
+ /**
+ * Extracts Contact level columns from the cursor.
+ */
+ private Contact loadContactHeaderData(final Cursor cursor, Uri contactUri) {
+ final String directoryParameter =
+ contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ final long directoryId = directoryParameter == null
+ ? Directory.DEFAULT
+ : Long.parseLong(directoryParameter);
+ final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
+ final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
+ final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
+ final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
+ final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
+ final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
+ final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
+ final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
+ final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
+ final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
+ final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
+ ? null
+ : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
+ final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
+ final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
+ final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
+
+ Uri lookupUri;
+ if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
+ lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
+ } else {
+ lookupUri = contactUri;
+ }
+
+ return new Contact(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
+ contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
+ altDisplayName, phoneticName, starred, presence, sendToVoicemail,
+ customRingtone, isUserProfile);
+ }
+
+ /**
+ * Extracts RawContact level columns from the cursor.
+ */
+ private ContentValues loadRawContactValues(Cursor cursor) {
+ ContentValues cv = new ContentValues();
+
+ cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
+
+ cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
+
+ return cv;
+ }
+
+ /**
+ * Extracts Data level columns from the cursor.
+ */
+ private ContentValues loadDataValues(Cursor cursor) {
+ ContentValues cv = new ContentValues();
+
+ cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
+
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.TIMES_USED);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.LAST_TIME_USED);
+
+ return cv;
+ }
+
+ private void cursorColumnToContentValues(
+ Cursor cursor, ContentValues values, int index) {
+ switch (cursor.getType(index)) {
+ case Cursor.FIELD_TYPE_NULL:
+ // don't put anything in the content values
+ break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
+ break;
+ case Cursor.FIELD_TYPE_STRING:
+ values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
+ break;
+ case Cursor.FIELD_TYPE_BLOB:
+ values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
+ break;
+ default:
+ throw new IllegalStateException("Invalid or unhandled data type");
+ }
+ }
+
+ private void loadDirectoryMetaData(Contact result) {
+ long directoryId = result.getDirectoryId();
+
+ Cursor cursor = getContext().getContentResolver().query(
+ ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
+ DirectoryQuery.COLUMNS, null, null, null);
+ if (cursor == null) {
+ return;
+ }
+ try {
+ if (cursor.moveToFirst()) {
+ final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
+ final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
+ final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
+ final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
+ final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
+ final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
+ String directoryType = null;
+ if (!TextUtils.isEmpty(packageName)) {
+ PackageManager pm = getContext().getPackageManager();
+ try {
+ Resources resources = pm.getResourcesForApplication(packageName);
+ directoryType = resources.getString(typeResourceId);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Contact directory resource not found: "
+ + packageName + "." + typeResourceId);
+ }
+ }
+
+ result.setDirectoryMetaData(
+ displayName, directoryType, accountType, accountName, exportSupport);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ static private class AccountKey {
+ private final String mAccountName;
+ private final String mAccountType;
+ private final String mDataSet;
+
+ public AccountKey(String accountName, String accountType, String dataSet) {
+ mAccountName = accountName;
+ mAccountType = accountType;
+ mDataSet = dataSet;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAccountName, mAccountType, mDataSet);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof AccountKey)) {
+ return false;
+ }
+ final AccountKey other = (AccountKey) obj;
+ return Objects.equals(mAccountName, other.mAccountName)
+ && Objects.equals(mAccountType, other.mAccountType)
+ && Objects.equals(mDataSet, other.mDataSet);
+ }
+ }
+
+ /**
+ * Loads groups meta-data for all groups associated with all constituent raw contacts'
+ * accounts.
+ */
+ private void loadGroupMetaData(Contact result) {
+ StringBuilder selection = new StringBuilder();
+ ArrayList selectionArgs = new ArrayList();
+ final HashSet accountsSeen = new HashSet<>();
+ for (RawContact rawContact : result.getRawContacts()) {
+ final String accountName = rawContact.getAccountName();
+ final String accountType = rawContact.getAccountTypeString();
+ final String dataSet = rawContact.getDataSet();
+ final AccountKey accountKey = new AccountKey(accountName, accountType, dataSet);
+ if (accountName != null && accountType != null &&
+ !accountsSeen.contains(accountKey)) {
+ accountsSeen.add(accountKey);
+ if (selection.length() != 0) {
+ selection.append(" OR ");
+ }
+ selection.append(
+ "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
+ selectionArgs.add(accountName);
+ selectionArgs.add(accountType);
+
+ if (dataSet != null) {
+ selection.append(" AND " + Groups.DATA_SET + "=?");
+ selectionArgs.add(dataSet);
+ } else {
+ selection.append(" AND " + Groups.DATA_SET + " IS NULL");
+ }
+ selection.append(")");
+ }
+ }
+ final ImmutableList.Builder groupListBuilder =
+ new ImmutableList.Builder();
+ final Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
+ GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
+ null);
+ if (cursor != null) {
+ try {
+ while (cursor.moveToNext()) {
+ final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
+ final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
+ final String dataSet = cursor.getString(GroupQuery.DATA_SET);
+ final long groupId = cursor.getLong(GroupQuery.ID);
+ final String title = cursor.getString(GroupQuery.TITLE);
+ final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
+ ? false
+ : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
+ final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
+ ? false
+ : cursor.getInt(GroupQuery.FAVORITES) != 0;
+
+ groupListBuilder.add(new GroupMetaData(
+ accountName, accountType, dataSet, groupId, title, defaultGroup,
+ favorites));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ result.setGroupMetaData(groupListBuilder.build());
+ }
+
+ /**
+ * Iterates over all data items that represent phone numbers are tries to calculate a formatted
+ * number. This function can safely be called several times as no unformatted data is
+ * overwritten
+ */
+ private void computeFormattedPhoneNumbers(Contact contactData) {
+ final String countryIso = GeoUtil.getCurrentCountryIso(getContext());
+ final ImmutableList rawContacts = contactData.getRawContacts();
+ final int rawContactCount = rawContacts.size();
+ for (int rawContactIndex = 0; rawContactIndex < rawContactCount; rawContactIndex++) {
+ final RawContact rawContact = rawContacts.get(rawContactIndex);
+ final List dataItems = rawContact.getDataItems();
+ final int dataCount = dataItems.size();
+ for (int dataIndex = 0; dataIndex < dataCount; dataIndex++) {
+ final DataItem dataItem = dataItems.get(dataIndex);
+ if (dataItem instanceof PhoneDataItem) {
+ final PhoneDataItem phoneDataItem = (PhoneDataItem) dataItem;
+ phoneDataItem.computeFormattedPhoneNumber(countryIso);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void deliverResult(Contact result) {
+ unregisterObserver();
+
+ // The creator isn't interested in any further updates
+ if (isReset() || result == null) {
+ return;
+ }
+
+ mContact = result;
+
+ if (result.isLoaded()) {
+ mLookupUri = result.getLookupUri();
+
+ if (!result.isDirectoryEntry()) {
+ Log.i(TAG, "Registering content observer for " + mLookupUri);
+ if (mObserver == null) {
+ mObserver = new ForceLoadContentObserver();
+ }
+ getContext().getContentResolver().registerContentObserver(
+ mLookupUri, true, mObserver);
+ }
+
+ if (mPostViewNotification) {
+ // inform the source of the data that this contact is being looked at
+ postViewNotificationToSyncAdapter();
+ }
+ }
+
+ super.deliverResult(mContact);
+ }
+
+ /**
+ * Posts a message to the contributing sync adapters that have opted-in, notifying them
+ * that the contact has just been loaded
+ */
+ private void postViewNotificationToSyncAdapter() {
+ Context context = getContext();
+ for (RawContact rawContact : mContact.getRawContacts()) {
+ final long rawContactId = rawContact.getId();
+ if (mNotifiedRawContactIds.contains(rawContactId)) {
+ continue; // Already notified for this raw contact.
+ }
+ mNotifiedRawContactIds.add(rawContactId);
+ final AccountType accountType = rawContact.getAccountType(context);
+ final String serviceName = accountType.getViewContactNotifyServiceClassName();
+ final String servicePackageName = accountType.getViewContactNotifyServicePackageName();
+ if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(servicePackageName)) {
+ final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+ final Intent intent = new Intent();
+ intent.setClassName(servicePackageName, serviceName);
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
+ try {
+ context.startService(intent);
+ } catch (Exception e) {
+ Log.e(TAG, "Error sending message to source-app", e);
+ }
+ }
+ }
+ }
+
+ private void unregisterObserver() {
+ if (mObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ mObserver = null;
+ }
+ }
+
+ /**
+ * Fully upgrades this ContactLoader to one with all lists fully loaded. When done, the
+ * new result will be delivered
+ */
+ public void upgradeToFullContact() {
+ // Everything requested already? Nothing to do, so let's bail out
+ if (mLoadGroupMetaData && mLoadInvitableAccountTypes
+ && mPostViewNotification && mComputeFormattedPhoneNumber) return;
+
+ mLoadGroupMetaData = true;
+ mLoadInvitableAccountTypes = true;
+ mPostViewNotification = true;
+ mComputeFormattedPhoneNumber = true;
+
+ // Cache the current result, so that we only load the "missing" parts of the contact.
+ cacheResult();
+
+ // Our load parameters have changed, so let's pretend the data has changed. Its the same
+ // thing, essentially.
+ onContentChanged();
+ }
+
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mContact != null) {
+ deliverResult(mContact);
+ }
+
+ if (takeContentChanged() || mContact == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ unregisterObserver();
+ mContact = null;
+ }
+
+ /**
+ * Caches the result, which is useful when we switch from activity to activity, using the same
+ * contact. If the next load is for a different contact, the cached result will be dropped
+ */
+ public void cacheResult() {
+ if (mContact == null || !mContact.isLoaded()) {
+ sCachedResult = null;
+ } else {
+ sCachedResult = mContact;
+ }
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/model/RawContact.java b/ContactsCommon/src/com/android/contacts/common/model/RawContact.java
new file mode 100644
index 0000000..3d8db85
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/model/RawContact.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.model;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.dataitem.DataItem;
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * RawContact represents a single raw contact in the raw contacts database.
+ * It has specialized getters/setters for raw contact
+ * items, and also contains a collection of DataItem objects. A RawContact contains the information
+ * from a single account.
+ *
+ * This allows RawContact objects to be thought of as a class with raw contact
+ * fields (like account type, name, data set, sync state, etc.) and a list of
+ * DataItem objects that represent contact information elements (like phone
+ * numbers, email, address, etc.).
+ */
+final public class RawContact implements Parcelable {
+
+ private AccountTypeManager mAccountTypeManager;
+ private final ContentValues mValues;
+ private final ArrayList mDataItems;
+
+ final public static class NamedDataItem implements Parcelable {
+ public final Uri mUri;
+
+ // This use to be a DataItem. DataItem creation is now delayed until the point of request
+ // since there is no benefit to storing them here due to the multiple inheritance.
+ // Eventually instanceof still has to be used anyways to determine which sub-class of
+ // DataItem it is. And having parent DataItem's here makes it very difficult to serialize or
+ // parcelable.
+ //
+ // Instead of having a common DataItem super class, we should refactor this to be a generic
+ // Object where the object is a concrete class that no longer relies on ContentValues.
+ // (this will also make the classes easier to use).
+ // Since instanceof is used later anyways, having a list of Objects won't hurt and is no
+ // worse than having a DataItem.
+ public final ContentValues mContentValues;
+
+ public NamedDataItem(Uri uri, ContentValues values) {
+ this.mUri = uri;
+ this.mContentValues = values;
+ }
+
+ public NamedDataItem(Parcel parcel) {
+ this.mUri = parcel.readParcelable(Uri.class.getClassLoader());
+ this.mContentValues = parcel.readParcelable(ContentValues.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeParcelable(mUri, i);
+ parcel.writeParcelable(mContentValues, i);
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+
+ @Override
+ public NamedDataItem createFromParcel(Parcel parcel) {
+ return new NamedDataItem(parcel);
+ }
+
+ @Override
+ public NamedDataItem[] newArray(int i) {
+ return new NamedDataItem[i];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mUri, mContentValues);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ final NamedDataItem other = (NamedDataItem) obj;
+ return Objects.equal(mUri, other.mUri) &&
+ Objects.equal(mContentValues, other.mContentValues);
+ }
+ }
+
+ public static RawContact createFrom(Entity entity) {
+ final ContentValues values = entity.getEntityValues();
+ final ArrayList subValues = entity.getSubValues();
+
+ RawContact rawContact = new RawContact(values);
+ for (Entity.NamedContentValues subValue : subValues) {
+ rawContact.addNamedDataItemValues(subValue.uri, subValue.values);
+ }
+ return rawContact;
+ }
+
+ /**
+ * A RawContact object can be created with or without a context.
+ */
+ public RawContact() {
+ this(new ContentValues());
+ }
+
+ public RawContact(ContentValues values) {
+ mValues = values;
+ mDataItems = new ArrayList();
+ }
+
+ /**
+ * Constructor for the parcelable.
+ *
+ * @param parcel The parcel to de-serialize from.
+ */
+ private RawContact(Parcel parcel) {
+ mValues = parcel.readParcelable(ContentValues.class.getClassLoader());
+ mDataItems = Lists.newArrayList();
+ parcel.readTypedList(mDataItems, NamedDataItem.CREATOR);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeParcelable(mValues, i);
+ parcel.writeTypedList(mDataItems);
+ }
+
+ /**
+ * Create for building the parcelable.
+ */
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+
+ @Override
+ public RawContact createFromParcel(Parcel parcel) {
+ return new RawContact(parcel);
+ }
+
+ @Override
+ public RawContact[] newArray(int i) {
+ return new RawContact[i];
+ }
+ };
+
+ public AccountTypeManager getAccountTypeManager(Context context) {
+ if (mAccountTypeManager == null) {
+ mAccountTypeManager = AccountTypeManager.getInstance(context);
+ }
+ return mAccountTypeManager;
+ }
+
+ public ContentValues getValues() {
+ return mValues;
+ }
+
+ /**
+ * Returns the id of the raw contact.
+ */
+ public Long getId() {
+ return getValues().getAsLong(RawContacts._ID);
+ }
+
+ /**
+ * Returns the account name of the raw contact.
+ */
+ public String getAccountName() {
+ return getValues().getAsString(RawContacts.ACCOUNT_NAME);
+ }
+
+ /**
+ * Returns the account type of the raw contact.
+ */
+ public String getAccountTypeString() {
+ return getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ }
+
+ /**
+ * Returns the data set of the raw contact.
+ */
+ public String getDataSet() {
+ return getValues().getAsString(RawContacts.DATA_SET);
+ }
+
+ public boolean isDirty() {
+ return getValues().getAsBoolean(RawContacts.DIRTY);
+ }
+
+ public String getSourceId() {
+ return getValues().getAsString(RawContacts.SOURCE_ID);
+ }
+
+ public String getSync1() {
+ return getValues().getAsString(RawContacts.SYNC1);
+ }
+
+ public String getSync2() {
+ return getValues().getAsString(RawContacts.SYNC2);
+ }
+
+ public String getSync3() {
+ return getValues().getAsString(RawContacts.SYNC3);
+ }
+
+ public String getSync4() {
+ return getValues().getAsString(RawContacts.SYNC4);
+ }
+
+ public boolean isDeleted() {
+ return getValues().getAsBoolean(RawContacts.DELETED);
+ }
+
+ public long getContactId() {
+ return getValues().getAsLong(Contacts.Entity.CONTACT_ID);
+ }
+
+ public boolean isStarred() {
+ return getValues().getAsBoolean(Contacts.STARRED);
+ }
+
+ public AccountType getAccountType(Context context) {
+ return getAccountTypeManager(context).getAccountType(getAccountTypeString(), getDataSet());
+ }
+
+ /**
+ * Sets the account name, account type, and data set strings.
+ * Valid combinations for account-name, account-type, data-set
+ * 1) null, null, null (local account)
+ * 2) non-null, non-null, null (valid account without data-set)
+ * 3) non-null, non-null, non-null (valid account with data-set)
+ */
+ private void setAccount(String accountName, String accountType, String dataSet) {
+ final ContentValues values = getValues();
+ if (accountName == null) {
+ if (accountType == null && dataSet == null) {
+ // This is a local account
+ values.putNull(RawContacts.ACCOUNT_NAME);
+ values.putNull(RawContacts.ACCOUNT_TYPE);
+ values.putNull(RawContacts.DATA_SET);
+ return;
+ }
+ } else {
+ if (accountType != null) {
+ // This is a valid account, either with or without a dataSet.
+ values.put(RawContacts.ACCOUNT_NAME, accountName);
+ values.put(RawContacts.ACCOUNT_TYPE, accountType);
+ if (dataSet == null) {
+ values.putNull(RawContacts.DATA_SET);
+ } else {
+ values.put(RawContacts.DATA_SET, dataSet);
+ }
+ return;
+ }
+ }
+ throw new IllegalArgumentException(
+ "Not a valid combination of account name, type, and data set.");
+ }
+
+ public void setAccount(AccountWithDataSet accountWithDataSet) {
+ if (accountWithDataSet != null) {
+ setAccount(accountWithDataSet.name, accountWithDataSet.type,
+ accountWithDataSet.dataSet);
+ } else {
+ setAccount(null, null, null);
+ }
+ }
+
+ public void setAccountToLocal() {
+ setAccount(null, null, null);
+ }
+
+ /**
+ * Creates and inserts a DataItem object that wraps the content values, and returns it.
+ */
+ public void addDataItemValues(ContentValues values) {
+ addNamedDataItemValues(Data.CONTENT_URI, values);
+ }
+
+ public NamedDataItem addNamedDataItemValues(Uri uri, ContentValues values) {
+ final NamedDataItem namedItem = new NamedDataItem(uri, values);
+ mDataItems.add(namedItem);
+ return namedItem;
+ }
+
+ public ArrayList getContentValues() {
+ final ArrayList list = Lists.newArrayListWithCapacity(mDataItems.size());
+ for (NamedDataItem dataItem : mDataItems) {
+ if (Data.CONTENT_URI.equals(dataItem.mUri)) {
+ list.add(dataItem.mContentValues);
+ }
+ }
+ return list;
+ }
+
+ public List getDataItems() {
+ final ArrayList list = Lists.newArrayListWithCapacity(mDataItems.size());
+ for (NamedDataItem dataItem : mDataItems) {
+ if (Data.CONTENT_URI.equals(dataItem.mUri)) {
+ list.add(DataItem.createFrom(dataItem.mContentValues));
+ }
+ }
+ return list;
+ }
+
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("RawContact: ").append(mValues);
+ for (RawContact.NamedDataItem namedDataItem : mDataItems) {
+ sb.append("\n ").append(namedDataItem.mUri);
+ sb.append("\n -> ").append(namedDataItem.mContentValues);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mValues, mDataItems);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ RawContact other = (RawContact) obj;
+ return Objects.equal(mValues, other.mValues) &&
+ Objects.equal(mDataItems, other.mDataItems);
+ }
+}
diff --git a/ContactsCommon/src/com/android/contacts/common/model/RawContactDelta.java b/ContactsCommon/src/com/android/contacts/common/model/RawContactDelta.java
new file mode 100644
index 0000000..7304f02
--- /dev/null
+++ b/ContactsCommon/src/com/android/contacts/common/model/RawContactDelta.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.model;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderOperation.Builder;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Profile;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.testing.NeededForTesting;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Contains a {@link RawContact} and records any modifications separately so the
+ * original {@link RawContact} can be swapped out with a newer version and the
+ * changes still cleanly applied.
+ *
+ * One benefit of this approach is that we can build changes entirely on an
+ * empty {@link RawContact}, which then becomes an insert {@link RawContacts} case.
+ *
+ * When applying modifications over an {@link RawContact}, we try finding the
+ * original {@link Data#_ID} rows where the modifications took place. If those
+ * rows are missing from the new {@link RawContact}, we know the original data must
+ * be deleted, but to preserve the user modifications we treat as an insert.
+ */
+public class RawContactDelta implements Parcelable {
+ // TODO: optimize by using contentvalues pool, since we allocate so many of them
+
+ private static final String TAG = "EntityDelta";
+ private static final boolean LOGV = false;
+
+ /**
+ * Direct values from {@link Entity#getEntityValues()}.
+ */
+ private ValuesDelta mValues;
+
+ /**
+ * URI used for contacts queries, by default it is set to query raw contacts.
+ * It can be set to query the profile's raw contact(s).
+ */
+ private Uri mContactsQueryUri = RawContacts.CONTENT_URI;
+
+ /**
+ * Internal map of children values from {@link Entity#getSubValues()}, which
+ * we store here sorted into {@link Data#MIMETYPE} bins.
+ */
+ private final HashMap> mEntries = Maps.newHashMap();
+
+ public RawContactDelta() {
+ }
+
+ public RawContactDelta(ValuesDelta values) {
+ mValues = values;
+ }
+
+ /**
+ * Build an {@link RawContactDelta} using the given {@link RawContact} as a
+ * starting point; the "before" snapshot.
+ */
+ public static RawContactDelta fromBefore(RawContact before) {
+ final RawContactDelta rawContactDelta = new RawContactDelta();
+ rawContactDelta.mValues = ValuesDelta.fromBefore(before.getValues());
+ rawContactDelta.mValues.setIdColumn(RawContacts._ID);
+ for (final ContentValues values : before.getContentValues()) {
+ rawContactDelta.addEntry(ValuesDelta.fromBefore(values));
+ }
+ return rawContactDelta;
+ }
+
+ /**
+ * Merge the "after" values from the given {@link RawContactDelta} onto the
+ * "before" state represented by this {@link RawContactDelta}, discarding any
+ * existing "after" states. This is typically used when re-parenting changes
+ * onto an updated {@link Entity}.
+ */
+ public static RawContactDelta mergeAfter(RawContactDelta local, RawContactDelta remote) {
+ // Bail early if trying to merge delete with missing local
+ final ValuesDelta remoteValues = remote.mValues;
+ if (local == null && (remoteValues.isDelete() || remoteValues.isTransient())) return null;
+
+ // Create local version if none exists yet
+ if (local == null) local = new RawContactDelta();
+
+ if (LOGV) {
+ final Long localVersion = (local.mValues == null) ? null : local.mValues
+ .getAsLong(RawContacts.VERSION);
+ final Long remoteVersion = remote.mValues.getAsLong(RawContacts.VERSION);
+ Log.d(TAG, "Re-parenting from original version " + remoteVersion + " to "
+ + localVersion);
+ }
+
+ // Create values if needed, and merge "after" changes
+ local.mValues = ValuesDelta.mergeAfter(local.mValues, remote.mValues);
+
+ // Find matching local entry for each remote values, or create
+ for (ArrayList mimeEntries : remote.mEntries.values()) {
+ for (ValuesDelta remoteEntry : mimeEntries) {
+ final Long childId = remoteEntry.getId();
+
+ // Find or create local match and merge
+ final ValuesDelta localEntry = local.getEntry(childId);
+ final ValuesDelta merged = ValuesDelta.mergeAfter(localEntry, remoteEntry);
+
+ if (localEntry == null && merged != null) {
+ // No local entry before, so insert
+ local.addEntry(merged);
+ }
+ }
+ }
+
+ return local;
+ }
+
+ public ValuesDelta getValues() {
+ return mValues;
+ }
+
+ public boolean isContactInsert() {
+ return mValues.isInsert();
+ }
+
+ /**
+ * Get the {@link ValuesDelta} child marked as {@link Data#IS_PRIMARY},
+ * which may return null when no entry exists.
+ */
+ public ValuesDelta getPrimaryEntry(String mimeType) {
+ final ArrayList mimeEntries = getMimeEntries(mimeType, false);
+ if (mimeEntries == null) return null;
+
+ for (ValuesDelta entry : mimeEntries) {
+ if (entry.isPrimary()) {
+ return entry;
+ }
+ }
+
+ // When no direct primary, return something
+ return mimeEntries.size() > 0 ? mimeEntries.get(0) : null;
+ }
+
+ /**
+ * calls {@link #getSuperPrimaryEntry(String, boolean)} with true
+ * @see #getSuperPrimaryEntry(String, boolean)
+ */
+ public ValuesDelta getSuperPrimaryEntry(String mimeType) {
+ return getSuperPrimaryEntry(mimeType, true);
+ }
+
+ /**
+ * Returns the super-primary entry for the given mime type
+ * @param forceSelection if true, will try to return some value even if a super-primary
+ * doesn't exist (may be a primary, or just a random item
+ * @return
+ */
+ @NeededForTesting
+ public ValuesDelta getSuperPrimaryEntry(String mimeType, boolean forceSelection) {
+ final ArrayList