Accessibility improvements.
- Do not use a list for a single row.
- Use content description in a few more places.
Bug: 4989128
Change-Id: I056237843dc6bca7987b18543f18f917babe2a4f
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index 987a787..99186e5 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -109,21 +109,87 @@
android:layout_alignBottom="@id/contact_background_sizer"
android:background="?android:attr/selectableItemBackground"
/>
- <ListView
- android:id="@android:id/list"
+ <FrameLayout android:id="@+id/call_and_sms_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/voicemail_container"
android:layout_marginTop="@dimen/call_log_icon_margin"
- android:background="?attr/call_log_primary_background_color"
- />
+ >
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/call_and_sms"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/call_log_list_item_height"
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/call_log_icon_margin"
+ android:gravity="center_vertical"
+ android:background="@drawable/dialpad_background"
+ >
+
+ <LinearLayout android:id="@+id/call_and_sms_main_action"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingLeft="@dimen/call_log_indent_margin"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:background="@drawable/btn_dial"
+ >
+
+ <TextView android:id="@+id/call_and_sms_text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <LinearLayout android:id="@+id/call_and_sms_line2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/call_and_sms_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="5dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ />
+
+ <TextView android:id="@+id/call_and_sms_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <View android:id="@+id/call_and_sms_divider"
+ android:layout_width="1px"
+ android:layout_height="32dip"
+ android:background="@drawable/ic_divider_dashed_holo_dark"
+ android:layout_gravity="center_vertical"
+ />
+
+ <ImageView android:id="@+id/call_and_sms_icon"
+ android:layout_width="@color/call_log_voicemail_highlight_color"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/call_log_inner_margin"
+ android:paddingRight="@dimen/call_log_outer_margin"
+ android:gravity="center"
+ android:scaleType="centerInside"
+ android:focusable="true"
+ android:background="@drawable/btn_dial"
+ />
+ </LinearLayout>
+ </FrameLayout>
<ListView
android:id="@+id/history"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/call_log_icon_margin"
android:layout_alignParentLeft="true"
- android:layout_below="@android:id/list"
+ android:layout_below="@id/call_and_sms_container"
android:background="@android:color/black"
/>
</RelativeLayout>
diff --git a/res/layout/call_detail_list_item.xml b/res/layout/call_detail_list_item.xml
deleted file mode 100644
index e2bf83c..0000000
--- a/res/layout/call_detail_list_item.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/call_log_list_item_height"
- android:orientation="horizontal"
- android:layout_marginTop="@dimen/call_log_icon_margin"
- android:gravity="center_vertical"
- android:background="@drawable/dialpad_background"
->
-
- <LinearLayout android:id="@+id/main_action"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:paddingLeft="@dimen/call_log_indent_margin"
- android:orientation="vertical"
- android:gravity="center_vertical"
- android:focusable="true"
- android:background="@drawable/btn_dial"
- >
-
- <TextView android:id="@android:id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- />
-
-
- <LinearLayout android:id="@+id/line2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
- <TextView android:id="@+id/label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="5dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textStyle="bold"
- />
-
- <TextView android:id="@+id/number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
- </LinearLayout>
-
- </LinearLayout>
-
- <View android:id="@+id/divider"
- android:layout_width="1px"
- android:layout_height="32dip"
- android:background="@drawable/ic_divider_dashed_holo_dark"
- android:layout_gravity="center_vertical"
- />
-
- <ImageView android:id="@+id/icon"
- android:layout_width="@color/call_log_voicemail_highlight_color"
- android:layout_height="match_parent"
- android:paddingLeft="@dimen/call_log_inner_margin"
- android:paddingRight="@dimen/call_log_outer_margin"
- android:gravity="center"
- android:scaleType="centerInside"
- android:focusable="true"
- android:background="@drawable/btn_dial"
- />
-
-</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2e8983f..9460a9f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1732,6 +1732,34 @@
-->
<string name="description_call_log_voicemail">Voicemail</string>
+ <!-- String describing the button to add a contact for the current number.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_add_contact">Add contact</string>
+
+ <!-- String describing the button to view the contact for the current number.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_view_contact">View contact <xliff:g id="name">%1$s</xliff:g></string>
+
+ <!-- String describing the button to call a number or contact.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_call">Call <xliff:g id="name">%1$s</xliff:g></string>
+
+ <!-- String describing the button to SMS a number or contact.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_send_text_message">Send text message to <xliff:g id="name">%1$s</xliff:g></string>
+
<!-- String describing the icon in the call log used to represent an unheard voicemail left to
the user.
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 5a2e9ea..5f682fa 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -28,7 +28,7 @@
import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
import android.app.ActionBar;
-import android.app.ListActivity;
+import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -54,15 +54,12 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -71,7 +68,7 @@
* This activity can be either started with the URI of a single call log entry, or with the
* {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
*/
-public class CallDetailActivity extends ListActivity {
+public class CallDetailActivity extends Activity {
private static final String TAG = "CallDetail";
/** A long array extra containing ids of call log entries to display. */
@@ -142,6 +139,20 @@
static final int COLUMN_INDEX_NORMALIZED_NUMBER = 5;
static final int COLUMN_INDEX_PHOTO_URI = 6;
+ private final View.OnClickListener mPrimaryActionListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(((ViewEntry) view.getTag()).primaryIntent);
+ }
+ };
+
+ private final View.OnClickListener mSecondaryActionListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(((ViewEntry) view.getTag()).secondaryIntent);
+ }
+ };
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -322,14 +333,25 @@
// contact from this number.
final Intent mainActionIntent;
final int mainActionIcon;
+ final String mainActionDescription;
+
+ final CharSequence nameOrNumber;
+ if (!TextUtils.isEmpty(firstDetails.name)) {
+ nameOrNumber = firstDetails.name;
+ } else {
+ nameOrNumber = firstDetails.number;
+ }
if (firstDetails.personId != -1) {
Uri personUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, personId);
mainActionIntent = new Intent(Intent.ACTION_VIEW, personUri);
mainActionIcon = R.drawable.ic_contacts_holo_dark;
+ mainActionDescription =
+ getString(R.string.description_view_contact, nameOrNumber);
} else if (isVoicemailNumber) {
mainActionIntent = null;
mainActionIcon = 0;
+ mainActionDescription = null;
} else if (isSipNumber) {
// TODO: This item is currently disabled for SIP addresses, because
// the Insert.PHONE extra only works correctly for PSTN numbers.
@@ -342,16 +364,19 @@
// and then we can remove the "!isSipNumber" check above.
mainActionIntent = null;
mainActionIcon = 0;
+ mainActionDescription = null;
} else if (canPlaceCallsTo) {
mainActionIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
mainActionIntent.setType(Contacts.CONTENT_ITEM_TYPE);
mainActionIntent.putExtra(Insert.PHONE, mNumber);
mainActionIcon = R.drawable.ic_add_contact_holo_dark;
+ mainActionDescription = getString(R.string.description_add_contact);
} else {
// If we cannot call the number, when we probably cannot add it as a contact either.
// This is usually the case of private, unknown, or payphone numbers.
mainActionIntent = null;
mainActionIcon = 0;
+ mainActionDescription = null;
}
if (mainActionIntent == null) {
@@ -367,11 +392,9 @@
startActivity(mainActionIntent);
}
});
+ mMainActionPushLayerView.setContentDescription(mainActionDescription);
}
- // Build list of various available actions.
- final List<ViewEntry> actions = new ArrayList<ViewEntry>();
-
// This action allows to call the number that places the call.
if (canPlaceCallsTo) {
final CharSequence displayNumber =
@@ -380,7 +403,8 @@
ViewEntry entry = new ViewEntry(
getString(R.string.menu_callNumber, displayNumber),
- new Intent(Intent.ACTION_CALL_PRIVILEGED, numberCallUri));
+ new Intent(Intent.ACTION_CALL_PRIVILEGED, numberCallUri),
+ getString(R.string.description_call, nameOrNumber));
// Only show a label if the number is shown and it is not a SIP address.
if (!TextUtils.isEmpty(firstDetails.number)
@@ -392,25 +416,20 @@
// The secondary action allows to send an SMS to the number that placed the
// call.
if (mPhoneNumberHelper.canSendSmsTo(mNumber)) {
- entry.setSecondaryAction(R.drawable.ic_text_holo_dark,
+ entry.setSecondaryAction(
+ R.drawable.ic_text_holo_dark,
new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts("sms", mNumber, null)));
+ Uri.fromParts("sms", mNumber, null)),
+ getString(R.string.description_send_text_message, nameOrNumber));
}
- actions.add(entry);
+ configureCallButton(entry);
+ } else {
+ disableCallButton();
}
mHasEditNumberBeforeCall = canPlaceCallsTo && !isSipNumber && !isVoicemailNumber;
- if (actions.size() != 0) {
- // Set the actions for this phone number.
- setListAdapter(new ViewAdapter(CallDetailActivity.this, actions));
- getListView().setVisibility(View.VISIBLE);
- getListView().setItemsCanFocus(true);
- } else {
- getListView().setVisibility(View.GONE);
- }
-
ListView historyList = (ListView) findViewById(R.id.history);
historyList.setAdapter(
new CallDetailHistoryAdapter(CallDetailActivity.this, mInflater,
@@ -506,6 +525,8 @@
static final class ViewEntry {
public final String text;
public final Intent primaryIntent;
+ /** The description for accessibility of the primary action. */
+ public final String primaryDescription;
public CharSequence label = null;
public String number = null;
@@ -513,111 +534,72 @@
public int secondaryIcon = 0;
/** Intent for the secondary action. If not null, an icon must be defined. */
public Intent secondaryIntent = null;
+ /** The description for accessibility of the secondary action. */
+ public String secondaryDescription = null;
- public ViewEntry(String text, Intent intent) {
+ public ViewEntry(String text, Intent intent, String description) {
this.text = text;
- this.primaryIntent = intent;
+ primaryIntent = intent;
+ primaryDescription = description;
}
- public void setSecondaryAction(int icon, Intent intent) {
+ public void setSecondaryAction(int icon, Intent intent, String description) {
secondaryIcon = icon;
secondaryIntent = intent;
+ secondaryDescription = description;
}
}
- private static final class ViewAdapter extends BaseAdapter {
- private final Context mContext;
- private final List<ViewEntry> mActions;
- private final LayoutInflater mInflater;
+ /** Disables the call button area, e.g., for private numbers. */
+ private void disableCallButton() {
+ findViewById(R.id.call_and_sms).setVisibility(View.GONE);
+ }
- private final View.OnClickListener mPrimaryActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- ViewEntry entry = (ViewEntry) view.getTag();
- mContext.startActivity(entry.primaryIntent);
- }
- };
+ /** Configures the call button area using the given entry. */
+ private void configureCallButton(ViewEntry entry) {
+ View convertView = findViewById(R.id.call_and_sms);
+ convertView.setVisibility(View.VISIBLE);
- private final View.OnClickListener mSecondaryActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- ViewEntry entry = (ViewEntry) view.getTag();
- mContext.startActivity(entry.secondaryIntent);
- }
- };
+ ImageView icon = (ImageView) convertView.findViewById(R.id.call_and_sms_icon);
+ View divider = convertView.findViewById(R.id.call_and_sms_divider);
+ TextView text = (TextView) convertView.findViewById(R.id.call_and_sms_text1);
- public ViewAdapter(Context context, List<ViewEntry> actions) {
- mContext = context;
- mActions = actions;
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View mainAction = convertView.findViewById(R.id.call_and_sms_main_action);
+ mainAction.setOnClickListener(mPrimaryActionListener);
+ mainAction.setTag(entry);
+ mainAction.setContentDescription(entry.primaryDescription);
+
+ if (entry.secondaryIntent != null) {
+ icon.setOnClickListener(mSecondaryActionListener);
+ icon.setImageResource(entry.secondaryIcon);
+ icon.setVisibility(View.VISIBLE);
+ icon.setTag(entry);
+ icon.setContentDescription(entry.secondaryDescription);
+ divider.setVisibility(View.VISIBLE);
+ } else {
+ icon.setVisibility(View.GONE);
+ divider.setVisibility(View.GONE);
}
+ text.setText(entry.text);
- @Override
- public int getCount() {
- return mActions.size();
- }
+ View line2 = convertView.findViewById(R.id.call_and_sms_line2);
+ boolean numberEmpty = TextUtils.isEmpty(entry.number);
+ boolean labelEmpty = TextUtils.isEmpty(entry.label) || numberEmpty;
+ if (labelEmpty && numberEmpty) {
+ line2.setVisibility(View.GONE);
+ } else {
+ line2.setVisibility(View.VISIBLE);
- @Override
- public Object getItem(int position) {
- return mActions.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // Make sure we have a valid convertView to start with
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.call_detail_list_item, parent, false);
- }
-
- // Fill action with icon and text.
- ViewEntry entry = mActions.get(position);
-
- ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
- View divider = convertView.findViewById(R.id.divider);
- TextView text = (TextView) convertView.findViewById(android.R.id.text1);
-
- View mainAction = convertView.findViewById(R.id.main_action);
- mainAction.setOnClickListener(mPrimaryActionListener);
- mainAction.setTag(entry);
-
- if (entry.secondaryIntent != null) {
- icon.setOnClickListener(mSecondaryActionListener);
- icon.setImageResource(entry.secondaryIcon);
- icon.setVisibility(View.VISIBLE);
- icon.setTag(entry);
- divider.setVisibility(View.VISIBLE);
+ TextView label = (TextView) convertView.findViewById(R.id.call_and_sms_label);
+ if (labelEmpty) {
+ label.setVisibility(View.GONE);
} else {
- icon.setVisibility(View.GONE);
- divider.setVisibility(View.GONE);
- }
- text.setText(entry.text);
-
- View line2 = convertView.findViewById(R.id.line2);
- boolean numberEmpty = TextUtils.isEmpty(entry.number);
- boolean labelEmpty = TextUtils.isEmpty(entry.label) || numberEmpty;
- if (labelEmpty && numberEmpty) {
- line2.setVisibility(View.GONE);
- } else {
- line2.setVisibility(View.VISIBLE);
-
- TextView label = (TextView) convertView.findViewById(R.id.label);
- if (labelEmpty) {
- label.setVisibility(View.GONE);
- } else {
- label.setText(entry.label);
- label.setVisibility(View.VISIBLE);
- }
-
- TextView number = (TextView) convertView.findViewById(R.id.number);
- number.setText(entry.number);
+ label.setText(entry.label);
+ label.setVisibility(View.VISIBLE);
}
- return convertView;
+ TextView number = (TextView) convertView.findViewById(R.id.call_and_sms_number);
+ number.setText(entry.number);
}
}