Merge "[Phone] New phone favorite tab."
diff --git a/res/drawable-hdpi/sym_action_email_holo_light.png b/res/drawable-hdpi/sym_action_email_holo_light.png
deleted file mode 100644
index e1ff65f..0000000
--- a/res/drawable-hdpi/sym_action_email_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_goto_website_holo_light.png b/res/drawable-hdpi/sym_action_goto_website_holo_light.png
deleted file mode 100644
index 4c5c614..0000000
--- a/res/drawable-hdpi/sym_action_goto_website_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_show_map_holo_light.png b/res/drawable-hdpi/sym_action_show_map_holo_light.png
deleted file mode 100644
index 4c6ba98..0000000
--- a/res/drawable-hdpi/sym_action_show_map_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_talk_holo_light.png b/res/drawable-hdpi/sym_action_talk_holo_light.png
deleted file mode 100644
index 62ad687..0000000
--- a/res/drawable-hdpi/sym_action_talk_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_email_holo_light.png b/res/drawable-mdpi/sym_action_email_holo_light.png
deleted file mode 100644
index 3160adb..0000000
--- a/res/drawable-mdpi/sym_action_email_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_goto_website_holo_light.png b/res/drawable-mdpi/sym_action_goto_website_holo_light.png
deleted file mode 100644
index 5b17bd2..0000000
--- a/res/drawable-mdpi/sym_action_goto_website_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_show_map_holo_light.png b/res/drawable-mdpi/sym_action_show_map_holo_light.png
deleted file mode 100644
index 059e821..0000000
--- a/res/drawable-mdpi/sym_action_show_map_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_talk_holo_light.png b/res/drawable-mdpi/sym_action_talk_holo_light.png
deleted file mode 100644
index ff444d8..0000000
--- a/res/drawable-mdpi/sym_action_talk_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/sym_action_email_holo_light.png b/res/drawable-xhdpi/sym_action_email_holo_light.png
deleted file mode 100644
index e64a7ae..0000000
--- a/res/drawable-xhdpi/sym_action_email_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/sym_action_goto_website_holo_light.png b/res/drawable-xhdpi/sym_action_goto_website_holo_light.png
deleted file mode 100644
index 48873f4..0000000
--- a/res/drawable-xhdpi/sym_action_goto_website_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/sym_action_show_map_holo_light.png b/res/drawable-xhdpi/sym_action_show_map_holo_light.png
deleted file mode 100644
index 1c948f6..0000000
--- a/res/drawable-xhdpi/sym_action_show_map_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/sym_action_talk_holo_light.png b/res/drawable-xhdpi/sym_action_talk_holo_light.png
deleted file mode 100644
index 759c7e8..0000000
--- a/res/drawable-xhdpi/sym_action_talk_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/quickcontact_photo_container.xml b/res/layout/quickcontact_photo_container.xml
index 1ba939a..e970934 100644
--- a/res/layout/quickcontact_photo_container.xml
+++ b/res/layout/quickcontact_photo_container.xml
@@ -61,30 +61,6 @@
android:ellipsize="end"
android:textColor="@android:color/white"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
- android:id="@+id/status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="15dip"
- android:layout_marginTop="-3dip" />
- <TextView
- android:id="@+id/timestamp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="12dip"
- android:layout_marginTop="-2dip" />
- <ImageView
- android:id="@+id/presence"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="15dip"
- android:scaleType="centerInside" />
<ImageButton
android:id="@+id/open_details_push_layer"
android:layout_width="match_parent"
diff --git a/src/com/android/contacts/ContactPresenceIconUtil.java b/src/com/android/contacts/ContactPresenceIconUtil.java
index 8e1af72..0cb5b93 100644
--- a/src/com/android/contacts/ContactPresenceIconUtil.java
+++ b/src/com/android/contacts/ContactPresenceIconUtil.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.StatusUpdates;
/**
@@ -46,43 +45,4 @@
return null;
}
}
-
- public static Drawable getChatCapabilityIcon(Context context, int status, int chatCapability) {
- int resourceId = 0;
- if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
- switch(status) {
- case StatusUpdates.AVAILABLE:
- resourceId = android.R.drawable.presence_video_online;
- break;
- case StatusUpdates.IDLE:
- case StatusUpdates.AWAY:
- resourceId = android.R.drawable.presence_video_away;
- break;
- case StatusUpdates.DO_NOT_DISTURB:
- resourceId = android.R.drawable.presence_video_busy;
- break;
- }
- } else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) {
- switch(status) {
- case StatusUpdates.AVAILABLE:
- resourceId = android.R.drawable.presence_audio_online;
- break;
- case StatusUpdates.IDLE:
- case StatusUpdates.AWAY:
- resourceId = android.R.drawable.presence_audio_away;
- break;
- case StatusUpdates.DO_NOT_DISTURB:
- resourceId = android.R.drawable.presence_audio_busy;
- break;
- }
- } else if (status != StatusUpdates.OFFLINE) {
- resourceId = StatusUpdates.getPresenceIconResourceId(status);
- }
-
- if (resourceId != 0) {
- return context.getResources().getDrawable(resourceId);
- }
-
- return null;
- }
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index aaeacab..aa49481 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -593,19 +593,18 @@
final Intent smsIntent = mHasSms ? new Intent(Intent.ACTION_SENDTO,
Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null)) : null;
- // Configure Icons and Intents. Notice actionIcon is already set to the phone
+ // Configure Icons and Intents.
if (mHasPhone && mHasSms) {
entry.intent = phoneIntent;
entry.secondaryIntent = smsIntent;
entry.secondaryActionIcon = kind.iconAltRes;
+ entry.secondaryActionDescription = kind.iconAltDescriptionRes;
} else if (mHasPhone) {
entry.intent = phoneIntent;
} else if (mHasSms) {
entry.intent = smsIntent;
- entry.actionIcon = kind.iconAltRes;
} else {
entry.intent = null;
- entry.actionIcon = -1;
}
// Remember super-primary phone
@@ -690,7 +689,6 @@
Uri.fromParts(Constants.SCHEME_SIP, entry.data, null));
} else {
entry.intent = null;
- entry.actionIcon = -1;
}
mSipEntries.add(entry);
// TODO: Now that SipAddress is in its own list of entries
@@ -983,20 +981,17 @@
entry.typeString = Im.getProtocolLabel(context.getResources(), Im.PROTOCOL_GOOGLE_TALK,
null).toString();
if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
- entry.actionIcon = R.drawable.sym_action_talk_holo_light;
entry.intent =
new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
entry.secondaryIntent =
new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
} else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) {
// Allow Talking and Texting
- entry.actionIcon = R.drawable.sym_action_talk_holo_light;
entry.intent =
new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
entry.secondaryIntent =
new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
} else {
- entry.actionIcon = R.drawable.sym_action_talk_holo_light;
entry.intent =
new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
}
@@ -1013,7 +1008,6 @@
final String authority = host.toLowerCase();
final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
authority).appendPath(data).build();
- entry.actionIcon = R.drawable.sym_action_talk_holo_light;
entry.intent = new Intent(Intent.ACTION_SENDTO, imUri);
}
}
@@ -1210,9 +1204,9 @@
public Context context = null;
public String resPackageName = null;
- public int actionIcon = -1;
public boolean isPrimary = false;
public int secondaryActionIcon = -1;
+ public int secondaryActionDescription = -1;
public Intent intent;
public Intent secondaryIntent = null;
public ArrayList<Long> ids = new ArrayList<Long>();
@@ -1247,6 +1241,7 @@
entry.kind = (kind.titleRes == -1 || kind.titleRes == 0) ? ""
: context.getString(kind.titleRes);
entry.data = buildDataString(kind, values, context);
+ entry.resPackageName = kind.resPackageName;
if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
entry.type = values.getAsInteger(kind.typeColumn);
@@ -1269,11 +1264,6 @@
entry.typeString = "";
}
- if (kind.iconRes > 0) {
- entry.resPackageName = kind.resPackageName;
- entry.actionIcon = kind.iconRes;
- }
-
return entry;
}
@@ -1349,8 +1339,8 @@
if (!TextUtils.equals(mimetype, entry.mimetype)
|| !ContactsUtils.areIntentActionEqual(intent, entry.intent)
- || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
- || actionIcon != entry.actionIcon) {
+ || !ContactsUtils.areIntentActionEqual(
+ secondaryIntent, entry.secondaryIntent)) {
return false;
}
@@ -1633,19 +1623,24 @@
// Set the secondary action button
final ImageView secondaryActionView = views.secondaryActionButton;
Drawable secondaryActionIcon = null;
+ String secondaryActionDescription = null;
if (entry.secondaryActionIcon != -1) {
secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
+ secondaryActionDescription = resources.getString(entry.secondaryActionDescription);
} else if ((entry.chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
secondaryActionIcon =
resources.getDrawable(R.drawable.sym_action_videochat_holo_light);
+ secondaryActionDescription = resources.getString(R.string.video_chat);
} else if ((entry.chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) {
secondaryActionIcon =
resources.getDrawable(R.drawable.sym_action_audiochat_holo_light);
+ secondaryActionDescription = resources.getString(R.string.audio_chat);
}
final View secondaryActionViewContainer = views.secondaryActionViewContainer;
if (entry.secondaryIntent != null && secondaryActionIcon != null) {
secondaryActionView.setImageDrawable(secondaryActionIcon);
+ secondaryActionView.setContentDescription(secondaryActionDescription);
secondaryActionViewContainer.setTag(entry);
secondaryActionViewContainer.setVisibility(View.VISIBLE);
views.secondaryActionDivider.setVisibility(View.VISIBLE);
diff --git a/src/com/android/contacts/editor/ContactEditorUtils.java b/src/com/android/contacts/editor/ContactEditorUtils.java
index 0e27223..05a041d 100644
--- a/src/com/android/contacts/editor/ContactEditorUtils.java
+++ b/src/com/android/contacts/editor/ContactEditorUtils.java
@@ -19,7 +19,6 @@
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.AccountWithDataSet;
-import com.android.contacts.test.NeededForTesting;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
@@ -32,6 +31,7 @@
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.text.TextUtils;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -39,9 +39,6 @@
/**
* Utility methods for the "account changed" notification in the new contact creation flow.
- *
- * TODO Remove all the "@VisibleForTesting"s once they're actually used in the app.
- * (Until then we need them to avoid "no such method" in tests)
*/
public class ContactEditorUtils {
private static final String TAG = "ContactEditorUtils";
@@ -82,6 +79,18 @@
.remove(KEY_ANYTHING_SAVED).apply();
}
+ void removeDefaultAccountForTest() {
+ mPrefs.edit().remove(KEY_DEFAULT_ACCOUNT).apply();
+ }
+
+ /**
+ * Sets the {@link #KEY_KNOWN_ACCOUNTS} and {@link #KEY_DEFAULT_ACCOUNT} preference values to
+ * empty strings to reset the state of the preferences file.
+ */
+ private void resetPreferenceValues() {
+ mPrefs.edit().putString(KEY_KNOWN_ACCOUNTS, "").putString(KEY_DEFAULT_ACCOUNT, "").apply();
+ }
+
private List<AccountWithDataSet> getWritableAccounts() {
return mAccountTypes.getAccounts(true);
}
@@ -103,15 +112,23 @@
* @param defaultAccount the account used to save a newly created contact. Or pass {@code null}
* If the user selected "local only".
*/
- @NeededForTesting
public void saveDefaultAndAllAccounts(AccountWithDataSet defaultAccount) {
- mPrefs.edit()
- .putBoolean(KEY_ANYTHING_SAVED, true)
- .putString(
- KEY_KNOWN_ACCOUNTS,AccountWithDataSet.stringifyList(getWritableAccounts()))
- .putString(KEY_DEFAULT_ACCOUNT,
- (defaultAccount == null) ? "" : defaultAccount.stringify())
- .apply();
+ final SharedPreferences.Editor editor = mPrefs.edit()
+ .putBoolean(KEY_ANYTHING_SAVED, true);
+
+ if (defaultAccount == null) {
+ // If the default is "local only", there should be no writable accounts.
+ // This should always be the case with our spec, but because we load the account list
+ // asynchronously using a worker thread, it is possible that there are accounts at this
+ // point. So if the default is null always clear the account list.
+ editor.putString(KEY_KNOWN_ACCOUNTS, "");
+ editor.putString(KEY_DEFAULT_ACCOUNT, "");
+ } else {
+ editor.putString(KEY_KNOWN_ACCOUNTS,
+ AccountWithDataSet.stringifyList(getWritableAccounts()));
+ editor.putString(KEY_DEFAULT_ACCOUNT, defaultAccount.stringify());
+ }
+ editor.apply();
}
/**
@@ -123,13 +140,20 @@
*
* Also note that the returned account may have been removed already.
*/
- @NeededForTesting
public AccountWithDataSet getDefaultAccount() {
final String saved = mPrefs.getString(KEY_DEFAULT_ACCOUNT, null);
if (TextUtils.isEmpty(saved)) {
return null;
}
- return AccountWithDataSet.unstringify(saved);
+ try {
+ return AccountWithDataSet.unstringify(saved);
+ } catch (IllegalArgumentException exception) {
+ Log.e(TAG, "Error with retrieving default account " + exception.toString());
+ // unstringify()can throw an exception if the string is not in an expected format.
+ // Hence, if the preferences file is corrupt, just reset the preference values
+ resetPreferenceValues();
+ return null;
+ }
}
/**
@@ -153,7 +177,15 @@
if (TextUtils.isEmpty(saved)) {
return EMPTY_ACCOUNTS;
}
- return AccountWithDataSet.unstringifyList(saved);
+ try {
+ return AccountWithDataSet.unstringifyList(saved);
+ } catch (IllegalArgumentException exception) {
+ Log.e(TAG, "Error with retrieving saved accounts " + exception.toString());
+ // unstringifyList()can throw an exception if the string is not in an expected format.
+ // Hence, if the preferences file is corrupt, just reset the preference values
+ resetPreferenceValues();
+ return EMPTY_ACCOUNTS;
+ }
}
/**
@@ -161,12 +193,12 @@
* - If it's the first launch.
* - Or, if an account has been added.
* - Or, if the default account has been removed.
+ * (And some extra sanity check)
*
* Note if this method returns {@code false}, the caller can safely assume that
* {@link #getDefaultAccount} will return a valid account. (Either an account which still
* exists, or {@code null} which should be interpreted as "local only".)
*/
- @NeededForTesting
public boolean shouldShowAccountChangedNotification() {
if (isFirstLaunch()) {
return true;
@@ -174,14 +206,27 @@
// Account added?
final List<AccountWithDataSet> savedAccounts = getSavedAccounts();
- for (AccountWithDataSet account : getWritableAccounts()) {
+ final List<AccountWithDataSet> currentWritableAccounts = getWritableAccounts();
+ for (AccountWithDataSet account : currentWritableAccounts) {
if (!savedAccounts.contains(account)) {
return true; // New account found.
}
}
+ final AccountWithDataSet defaultAccount = getDefaultAccount();
+
// Does default account still exist?
- if (!isValidAccount(getDefaultAccount())) {
+ if (!isValidAccount(defaultAccount)) {
+ return true;
+ }
+
+ // If there is an inconsistent state in the preferences file - default account is null
+ // ("local" account) while there are multiple accounts, then show the notification dialog.
+ // This shouldn't ever happen, but this should allow the user can get back into a normal
+ // state after they respond to the notification.
+ if (defaultAccount == null && currentWritableAccounts.size() > 0) {
+ Log.e(TAG, "Preferences file in an inconsistent state, request that the default account"
+ + " and current writable accounts be saved again");
return true;
}
@@ -207,7 +252,6 @@
* {@link Activity#onActivityResult} or {@link android.app.Fragment#onActivityResult} to
* get the result.
*/
- @NeededForTesting
public Intent createAddWritableAccountIntent() {
return AccountManager.newChooseAccountIntent(
null, // selectedAccount
@@ -231,7 +275,6 @@
* will never have {@link AccountWithDataSet#dataSet} set, as there's no way to create an
* extension package account from setup wizard.
*/
- @NeededForTesting
public AccountWithDataSet getCreatedAccount(int resultCode, Intent resultData) {
// Javadoc doesn't say anything about resultCode but that the data intent will be non null
// on success.
@@ -246,4 +289,3 @@
return new AccountWithDataSet(accountName, accountType, null);
}
}
-
diff --git a/src/com/android/contacts/model/AccountWithDataSet.java b/src/com/android/contacts/model/AccountWithDataSet.java
index e379346..784e870 100644
--- a/src/com/android/contacts/model/AccountWithDataSet.java
+++ b/src/com/android/contacts/model/AccountWithDataSet.java
@@ -150,11 +150,13 @@
/**
* Unpack a string created by {@link #stringify}.
+ *
+ * @throws IllegalArgumentException if it's an invalid string.
*/
public static AccountWithDataSet unstringify(String s) {
final String[] array = STRINGIFY_SEPARATOR_PAT.split(s, 3);
if (array.length < 3) {
- throw new IllegalArgumentException("Invalid string");
+ throw new IllegalArgumentException("Invalid string " + s);
}
return new AccountWithDataSet(array[0], array[1],
TextUtils.isEmpty(array[2]) ? null : array[2]);
@@ -178,6 +180,8 @@
/**
* Unpack a list of {@link AccountWithDataSet} into a string.
+ *
+ * @throws IllegalArgumentException if it's an invalid string.
*/
public static List<AccountWithDataSet> unstringifyList(String s) {
final ArrayList<AccountWithDataSet> ret = Lists.newArrayList();
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index b599c66..6c9e9c8 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -95,7 +95,7 @@
protected DataKind addDataKindStructuredName(Context context) {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, -1, true, R.layout.structured_name_editor_view,
+ R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view,
android.R.style.TextAppearance_Medium));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -125,7 +125,7 @@
protected DataKind addDataKindDisplayName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
- R.string.nameLabelsGroup, -1, -1, true, R.layout.text_fields_editor_view,
+ R.string.nameLabelsGroup, -1, true, R.layout.text_fields_editor_view,
android.R.style.TextAppearance_Medium));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -166,7 +166,7 @@
protected DataKind addDataKindPhoneticName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
- R.string.name_phonetic, -1, -1, true, R.layout.phonetic_name_editor_view,
+ R.string.name_phonetic, -1, true, R.layout.phonetic_name_editor_view,
android.R.style.TextAppearance_Medium));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -186,7 +186,7 @@
protected DataKind addDataKindNickname(Context context) {
DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
- R.string.nicknameLabelsGroup, -1, 115, true,
+ R.string.nicknameLabelsGroup, 115, true,
R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
kind.isList = false;
kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
@@ -203,9 +203,9 @@
protected DataKind addDataKindPhone(Context context) {
DataKind kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup,
- android.R.drawable.sym_action_call, 10, true,
- R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
+ 10, true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
kind.iconAltRes = R.drawable.ic_text_holo_light;
+ kind.iconAltDescriptionRes = R.string.sms;
kind.actionHeader = new PhoneActionInflater();
kind.actionAltHeader = new PhoneActionAltInflater();
kind.actionBody = new SimpleInflater(Phone.NUMBER);
@@ -243,8 +243,7 @@
protected DataKind addDataKindEmail(Context context) {
DataKind kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup,
- R.drawable.sym_action_email_holo_light, 15, true,
- R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
+ 15, true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new EmailActionInflater();
kind.actionBody = new SimpleInflater(Email.DATA);
kind.typeColumn = Email.TYPE;
@@ -264,8 +263,8 @@
protected DataKind addDataKindStructuredPostal(Context context) {
DataKind kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
- R.string.postalLabelsGroup, R.drawable.sym_action_show_map_holo_light, 25,
- true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
+ R.string.postalLabelsGroup, 25, true, R.layout.text_fields_editor_view,
+ android.R.style.TextAppearance_Medium));
kind.actionHeader = new PostalActionInflater();
kind.actionBody = new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS);
kind.typeColumn = StructuredPostal.TYPE;
@@ -285,8 +284,7 @@
}
protected DataKind addDataKindIm(Context context) {
- DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup,
- R.drawable.sym_action_talk_holo_light, 20, true,
+ DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup, 20, true,
R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new ImActionInflater();
kind.actionBody = new SimpleInflater(Im.DATA);
@@ -318,7 +316,7 @@
protected DataKind addDataKindOrganization(Context context) {
DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
- R.string.organizationLabelsGroup, -1, 5, true,
+ R.string.organizationLabelsGroup, 5, true,
R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new SimpleInflater(Organization.COMPANY);
kind.actionBody = new SimpleInflater(Organization.TITLE);
@@ -334,7 +332,7 @@
}
protected DataKind addDataKindPhoto(Context context) {
- DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, -1, true, -1,
+ DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, true, -1,
android.R.style.TextAppearance_Medium));
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
@@ -343,7 +341,7 @@
protected DataKind addDataKindNote(Context context) {
DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
- R.string.label_notes, -1, 110, true,
+ R.string.label_notes, 110, true,
R.layout.text_fields_editor_view, android.R.style.TextAppearance_Small));
kind.isList = false;
kind.actionHeader = new SimpleInflater(R.string.label_notes);
@@ -356,8 +354,8 @@
protected DataKind addDataKindWebsite(Context context) {
DataKind kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE,
- R.string.websiteLabelsGroup, R.drawable.sym_action_goto_website_holo_light, 120,
- true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
+ R.string.websiteLabelsGroup, 120, true, R.layout.text_fields_editor_view,
+ android.R.style.TextAppearance_Medium));
kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
kind.actionBody = new SimpleInflater(Website.URL);
kind.defaultValues = new ContentValues();
@@ -379,8 +377,8 @@
// the android:icon attribute of the SIP-related
// intent-filters in the Phone app's manifest.
DataKind kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
- R.string.label_sip_address, android.R.drawable.sym_action_call, 130, true,
- R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
+ R.string.label_sip_address, 130, true, R.layout.text_fields_editor_view,
+ android.R.style.TextAppearance_Medium));
kind.isList = false;
kind.actionHeader = new SimpleInflater(R.string.label_sip_address);
@@ -395,8 +393,7 @@
protected DataKind addDataKindGroupMembership(Context context) {
DataKind kind = getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE);
kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE,
- R.string.groupsLabel, android.R.drawable.sym_contact_card, 999, true, -1,
- android.R.style.TextAppearance_Medium));
+ R.string.groupsLabel, 999, true, -1, android.R.style.TextAppearance_Medium));
kind.isList = false;
kind.fieldList = Lists.newArrayList();
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
index b0b3f38..f99eef9 100644
--- a/src/com/android/contacts/model/DataKind.java
+++ b/src/com/android/contacts/model/DataKind.java
@@ -42,8 +42,8 @@
public String resPackageName;
public String mimeType;
public int titleRes;
- public int iconRes;
public int iconAltRes;
+ public int iconAltDescriptionRes;
public int weight;
public boolean editable;
@@ -97,11 +97,10 @@
textAppearanceResourceId = android.R.style.TextAppearance_Medium;
}
- public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable,
+ public DataKind(String mimeType, int titleRes, int weight, boolean editable,
int editorLayoutResourceId, int textAppearanceResourceId) {
this.mimeType = mimeType;
this.titleRes = titleRes;
- this.iconRes = iconRes;
this.weight = weight;
this.editable = editable;
this.isList = true;
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index 4a0e7a0..f4aac7b 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -64,7 +64,7 @@
@Override
protected DataKind addDataKindStructuredName(Context context) {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, -1, true,
+ R.string.nameLabelsGroup, -1, true,
R.layout.structured_name_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -94,7 +94,7 @@
@Override
protected DataKind addDataKindDisplayName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
- R.string.nameLabelsGroup, -1, -1, true,
+ R.string.nameLabelsGroup, -1, true,
R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
boolean displayOrderPrimary =
@@ -128,7 +128,7 @@
@Override
protected DataKind addDataKindPhoneticName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
- R.string.name_phonetic, -1, -1, true,
+ R.string.name_phonetic, -1, true,
R.layout.phonetic_name_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -294,7 +294,7 @@
protected DataKind addDataKindEvent(Context context) {
DataKind kind = addKind(
- new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, -1, 150, true,
+ new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, 150, true,
R.layout.event_field_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new EventActionInflater();
kind.actionBody = new SimpleInflater(Event.START_DATE);
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 0518ea5..73aa773 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -121,11 +121,13 @@
iconRes = resolveExternalResId(context, mAccountTypeIconAttribute,
this.resPackageName, ATTR_ACCOUNT_ICON);
- // Bring in name and photo from fallback source, which are non-optional
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindPhoto(context);
+ if (!mHasEditSchema) {
+ // Bring in name and photo from fallback source, which are non-optional
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindPhoto(context);
+ }
// If we reach this point, the account type has been successfully initialized.
mInitSuccessful = true;
@@ -287,8 +289,6 @@
kind.mimeType = a
.getString(com.android.internal.R.styleable.ContactsDataKind_mimeType);
- kind.iconRes = a.getResourceId(
- com.android.internal.R.styleable.ContactsDataKind_icon, -1);
final String summaryColumn = a.getString(
com.android.internal.R.styleable.ContactsDataKind_summaryColumn);
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index cee43dd..d94dd39 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -109,7 +109,7 @@
private DataKind addDataKindRelation(Context context) {
DataKind kind = addKind(new DataKind(Relation.CONTENT_ITEM_TYPE,
- R.string.relationLabelsGroup, -1, 160, true,
+ R.string.relationLabelsGroup, 160, true,
R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new RelationActionInflater();
kind.actionBody = new SimpleInflater(Relation.NAME);
@@ -145,7 +145,7 @@
private DataKind addDataKindEvent(Context context) {
DataKind kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
- R.string.eventLabelsGroup, -1, 150, true,
+ R.string.eventLabelsGroup, 150, true,
R.layout.event_field_editor_view, android.R.style.TextAppearance_Medium));
kind.actionHeader = new EventActionInflater();
kind.actionBody = new SimpleInflater(Event.START_DATE);
diff --git a/src/com/android/contacts/quickcontact/Action.java b/src/com/android/contacts/quickcontact/Action.java
index f6e282c..b2d869d 100644
--- a/src/com/android/contacts/quickcontact/Action.java
+++ b/src/com/android/contacts/quickcontact/Action.java
@@ -35,6 +35,9 @@
/** Returns an icon that can be clicked for the alternate action. */
public Drawable getAlternateIcon();
+ /** Returns the content description of the icon for the alternate action. */
+ public String getAlternateIconDescription();
+
/** Build an {@link Intent} that will perform this action. */
public Intent getIntent();
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index 84a34bd..266fd02 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -57,6 +57,7 @@
private CharSequence mSubtitle;
private Intent mIntent;
private Intent mAlternateIntent;
+ private int mAlternateIconDescriptionRes;
private int mAlternateIconRes;
private Uri mDataUri;
@@ -125,6 +126,7 @@
mIntent = phoneIntent;
mAlternateIntent = smsIntent;
mAlternateIconRes = kind.iconAltRes;
+ mAlternateIconDescriptionRes = kind.iconAltDescriptionRes;
} else if (hasPhone) {
mIntent = phoneIntent;
} else if (hasSms) {
@@ -198,9 +200,13 @@
if (isVideoChatCapable || isAudioChatCapable) {
mAlternateIntent = new Intent(
Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
- mAlternateIconRes = (isVideoChatCapable
- ? R.drawable.sym_action_videochat_holo_light
- : R.drawable.sym_action_audiochat_holo_light);
+ if (isVideoChatCapable) {
+ mAlternateIconRes = R.drawable.sym_action_videochat_holo_light;
+ mAlternateIconDescriptionRes = R.string.video_chat;
+ } else {
+ mAlternateIconRes = R.drawable.sym_action_audiochat_holo_light;
+ mAlternateIconDescriptionRes = R.string.audio_chat;
+ }
}
}
}
@@ -290,6 +296,12 @@
}
@Override
+ public String getAlternateIconDescription() {
+ if (mAlternateIconDescriptionRes == 0) return null;
+ return mContext.getResources().getString(mAlternateIconDescriptionRes);
+ }
+
+ @Override
public Intent getIntent() {
return mIntent;
}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index fbca129..0588c7d 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -225,9 +225,6 @@
// find and prepare correct header view
mPhotoContainer = findViewById(R.id.photo_container);
setHeaderNameText(R.id.name, R.string.missing_name);
- setHeaderText(R.id.status, null);
- setHeaderText(R.id.timestamp, null);
- setHeaderImage(R.id.presence, null);
// Start background query for data, but only select photo rows when they
// directly match the super-primary PHOTO_ID.
@@ -477,16 +474,9 @@
}
if (cursor.moveToLast()) {
- // Read contact information from last data row
+ // Read contact name from last data row
final String name = cursor.getString(DataQuery.DISPLAY_NAME);
- final int presence = cursor.getInt(DataQuery.CONTACT_PRESENCE);
- final int chatCapability = cursor.getInt(DataQuery.CONTACT_CHAT_CAPABILITY);
- final Drawable statusIcon = ContactPresenceIconUtil.getChatCapabilityIcon(
- context, presence, chatCapability);
-
setHeaderNameText(R.id.name, name);
- // TODO: Bring this back once we have a design
-// setHeaderImage(R.id.presence, statusIcon);
}
if (photoView != null) {
@@ -498,13 +488,6 @@
}
}
- // TODO: Bring this back once we have a design
-// if (status.isValid()) {
-// // Update status when valid was found
-// setHeaderText(R.id.status, status.getStatus());
-// setHeaderText(R.id.timestamp, status.getTimestampLabel(context));
-// }
-
// All the mime-types to add.
final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
mSortedActionMimeTypes.clear();
@@ -680,8 +663,6 @@
RawContacts.DATA_SET,
Contacts.STARRED,
Contacts.DISPLAY_NAME,
- Contacts.CONTACT_PRESENCE,
- Contacts.CONTACT_CHAT_CAPABILITY,
Data.STATUS,
Data.STATUS_RES_PACKAGE,
@@ -708,20 +689,18 @@
final int DATA_SET = 2;
final int STARRED = 3;
final int DISPLAY_NAME = 4;
- final int CONTACT_PRESENCE = 5;
- final int CONTACT_CHAT_CAPABILITY = 6;
- final int STATUS = 7;
- final int STATUS_RES_PACKAGE = 8;
- final int STATUS_ICON = 9;
- final int STATUS_LABEL = 10;
- final int STATUS_TIMESTAMP = 11;
- final int PRESENCE = 12;
- final int CHAT_CAPABILITY = 13;
+ final int STATUS = 5;
+ final int STATUS_RES_PACKAGE = 6;
+ final int STATUS_ICON = 7;
+ final int STATUS_LABEL = 8;
+ final int STATUS_TIMESTAMP = 9;
+ final int PRESENCE = 10;
+ final int CHAT_CAPABILITY = 11;
- final int RES_PACKAGE = 14;
- final int MIMETYPE = 15;
- final int IS_PRIMARY = 16;
- final int IS_SUPER_PRIMARY = 17;
+ final int RES_PACKAGE = 12;
+ final int MIMETYPE = 13;
+ final int IS_PRIMARY = 14;
+ final int IS_SUPER_PRIMARY = 15;
}
}
diff --git a/src/com/android/contacts/quickcontact/QuickContactListFragment.java b/src/com/android/contacts/quickcontact/QuickContactListFragment.java
index 0d3b644..c6187e9 100644
--- a/src/com/android/contacts/quickcontact/QuickContactListFragment.java
+++ b/src/com/android/contacts/quickcontact/QuickContactListFragment.java
@@ -101,7 +101,6 @@
R.layout.quickcontact_list_item,
parent, false);
-
// TODO: Put those findViewByIds in a container
final TextView text1 = (TextView) resultView.findViewById(
android.R.id.text1);
@@ -121,6 +120,7 @@
final boolean hasAlternateAction = action.getAlternateIntent() != null;
alternateActionDivider.setVisibility(hasAlternateAction ? View.VISIBLE : View.GONE);
alternateActionButton.setImageDrawable(action.getAlternateIcon());
+ alternateActionButton.setContentDescription(action.getAlternateIconDescription());
alternateActionButton.setVisibility(hasAlternateAction ? View.VISIBLE : View.GONE);
// Special case for phone numbers in accessibility mode
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 76d3d84..e45cea1 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -90,14 +90,14 @@
this.accountType = TEST_ACCOUNT_TYPE;
final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, -1, true, -1, -1);
+ R.string.nameLabelsGroup, -1, true, -1, -1);
nameKind.typeOverallMax = 1;
addKind(nameKind);
// Phone allows maximum 2 home, 1 work, and unlimited other, with
// constraint of 5 numbers maximum.
final DataKind phoneKind = new DataKind(
- Phone.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1);
+ Phone.CONTENT_ITEM_TYPE, -1, 10, true, -1, -1);
phoneKind.typeOverallMax = 5;
phoneKind.typeColumn = Phone.TYPE;
@@ -115,14 +115,14 @@
// Email is unlimited
final DataKind emailKind = new DataKind(
- Email.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1);
+ Email.CONTENT_ITEM_TYPE, -1, 10, true, -1, -1);
emailKind.typeOverallMax = -1;
emailKind.fieldList = Lists.newArrayList();
emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
addKind(emailKind);
// IM is only one
- final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, -1, 10,
+ final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10,
true, -1, -1);
imKind.typeOverallMax = 1;
imKind.fieldList = Lists.newArrayList();
@@ -131,7 +131,7 @@
// Organization is only one
final DataKind orgKind = new DataKind(
- Organization.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1);
+ Organization.CONTENT_ITEM_TYPE, -1, 10, true, -1, -1);
orgKind.typeOverallMax = 1;
orgKind.fieldList = Lists.newArrayList();
orgKind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
diff --git a/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java b/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java
index 9f4e487..b2cb39c 100644
--- a/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java
+++ b/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java
@@ -132,7 +132,6 @@
Sets.newHashSet(mAccountTypes.mAccounts),
toSet(mTarget.getSavedAccounts()));
-
// 1 account
mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A};
mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
@@ -141,13 +140,19 @@
Sets.newHashSet(mAccountTypes.mAccounts),
toSet(mTarget.getSavedAccounts()));
- // 2 account
+ // 2 accounts
mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A, ACCOUNT_1_B};
mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_B);
assertEquals(ACCOUNT_1_B, mTarget.getDefaultAccount());
MoreAsserts.assertEquals(
Sets.newHashSet(mAccountTypes.mAccounts),
toSet(mTarget.getSavedAccounts()));
+
+ // 2 accounts, and save null as the default. Even though there are accounts, the saved
+ // account list should be empty in this case.
+ mTarget.saveDefaultAndAllAccounts(null);
+ assertNull(mTarget.getDefaultAccount());
+ assertEquals(0, mTarget.getSavedAccounts().size());
}
public void testIsAccountValid() {
@@ -272,6 +277,21 @@
assertFalse(mTarget.shouldShowAccountChangedNotification());
}
+ public void testShouldShowAccountChangedNotification_sanity_check() {
+ // Prepare 1 account and save it as the default.
+ setAccountTypes(TYPE1);
+ setAccounts(ACCOUNT_1_A);
+
+ mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
+
+ // Right after a save, the dialog shouldn't show up.
+ assertFalse(mTarget.shouldShowAccountChangedNotification());
+
+ // Remove the default account to emulate broken preferences.
+ mTarget.removeDefaultAccountForTest();
+ assertTrue(mTarget.shouldShowAccountChangedNotification());
+ }
+
private static <T> Set<T> toSet(Collection<T> collection) {
Set<T> ret = Sets.newHashSet();
ret.addAll(collection);