Fix [a11y] Wi-Fi Easy Connect Talkback issues

1. Remove android:contentDescription from ButtonPreference layout file
   'wifi_button_preference_widget.xml' because it should be a more
   general purpose component. Add ButtonPreference#setButtonContentDescription
   for this change.
2. Add a LinearLayout to group title & summary for better Talkback UX
3. Set android:contentDescription for Wi-Fi enrollee scan button
4. setTitle for Talkback actionbar back button of WifiDppQrCodeGeneratorFragment &
   WifiDppQrCodeScannerFragment
5. Auto trigger Talkback to speak title & summary in WifiDppAddDeviceFragment &
   WifiDppChooseSavedWifiNetworkFragment
6. Auto trigger Talkback to speak summary change in WifiDppAddDeviceFragment
7. Auto trigger Talkback to speak error message in WifiDppQrCodeScannerFragment

Bug: 126007405
Bug: 124424996
Bug: 124424445
Test: manual test

Change-Id: I54a3f033bb8871c47be12115ae8f97691fd83302
This commit is contained in:
Arc Wang
2019-03-06 12:29:45 +08:00
parent b249fb7d13
commit 70e3580f5d
10 changed files with 104 additions and 33 deletions

View File

@@ -23,5 +23,4 @@
android:minHeight="@dimen/min_tap_target_size" android:minHeight="@dimen/min_tap_target_size"
android:layout_gravity="center" android:layout_gravity="center"
android:background="@null" android:background="@null"
android:visibility="gone" android:visibility="gone"/>
android:contentDescription="@string/wifi_add_network" />

View File

@@ -71,7 +71,7 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:background="@null" android:background="@null"
android:src="@drawable/ic_scan_24dp" android:src="@drawable/ic_scan_24dp"
android:contentDescription="@string/wifi_add_network" /> android:contentDescription="@string/wifi_dpp_scan_qr_code"/>
</RelativeLayout> </RelativeLayout>
<LinearLayout android:id="@+id/ssid_too_long_warning" <LinearLayout android:id="@+id/ssid_too_long_warning"
@@ -307,7 +307,7 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:background="@null" android:background="@null"
android:src="@drawable/ic_scan_24dp" android:src="@drawable/ic_scan_24dp"
android:contentDescription="@string/wifi_add_network" /> android:contentDescription="@string/wifi_dpp_scan_qr_code"/>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@@ -40,6 +40,14 @@
android:src="@drawable/ic_devices_check_circle_green" android:src="@drawable/ic_devices_check_circle_green"
android:scaleType="fitCenter"/> android:scaleType="fitCenter"/>
<!-- Add title_summary_container to group content for Talkback -->
<LinearLayout
android:id="@+id/title_summary_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:focusable="true">
<TextView <TextView
android:id="@android:id/title" android:id="@android:id/title"
style="@style/TextAppearance.EntityHeaderTitle" style="@style/TextAppearance.EntityHeaderTitle"
@@ -62,5 +70,5 @@
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:paddingStart="32dp" android:paddingStart="32dp"
android:paddingEnd="32dp"/> android:paddingEnd="32dp"/>
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -49,6 +49,7 @@ public class ButtonPreference extends Preference {
private ImageButton mImageButton; private ImageButton mImageButton;
private Drawable mButtonIcon; private Drawable mButtonIcon;
private View.OnClickListener mClickListener; private View.OnClickListener mClickListener;
private String mContentDescription;
// Used for dummy pref. // Used for dummy pref.
public ButtonPreference(Context context, AttributeSet attrs) { public ButtonPreference(Context context, AttributeSet attrs) {
@@ -57,6 +58,7 @@ public class ButtonPreference extends Preference {
mImageButton = null; mImageButton = null;
mButtonIcon = null; mButtonIcon = null;
mClickListener = null; mClickListener = null;
mContentDescription = null;
} }
public ButtonPreference(Context context) { public ButtonPreference(Context context) {
@@ -83,6 +85,7 @@ public class ButtonPreference extends Preference {
if (mImageButton != null) { if (mImageButton != null) {
mImageButton.setImageDrawable(mButtonIcon); mImageButton.setImageDrawable(mButtonIcon);
mImageButton.setOnClickListener(mClickListener); mImageButton.setOnClickListener(mClickListener);
mImageButton.setContentDescription(mContentDescription);
} }
setButtonVisibility(); setButtonVisibility();
} }
@@ -96,9 +99,9 @@ public class ButtonPreference extends Preference {
/** /**
* Sets the drawable to be displayed in button. * Sets the drawable to be displayed in button.
*/ */
public ButtonPreference setButtonIcon(@DrawableRes int iconResId) { public void setButtonIcon(@DrawableRes int iconResId) {
if (iconResId == 0) { if (iconResId == 0) {
return this; return;
} }
try { try {
@@ -107,17 +110,26 @@ public class ButtonPreference extends Preference {
} catch (Resources.NotFoundException exception) { } catch (Resources.NotFoundException exception) {
Log.e(TAG, "Resource does not exist: " + iconResId); Log.e(TAG, "Resource does not exist: " + iconResId);
} }
return this;
} }
/** /**
* Register a callback to be invoked when button is clicked. * Register a callback to be invoked when button is clicked.
*/ */
public ButtonPreference setButtonOnClickListener(View.OnClickListener listener) { public void setButtonOnClickListener(View.OnClickListener listener) {
if (listener != mClickListener) { if (listener != mClickListener) {
mClickListener = listener; mClickListener = listener;
notifyChanged(); notifyChanged();
} }
return this; }
/**
* A content description briefly describes the button and is primarily used for accessibility
* support to determine how a button should be presented to the user.
*/
public void setButtonContentDescription(String contentDescription) {
if (contentDescription != mContentDescription) {
mContentDescription = contentDescription;
notifyChanged();
}
} }
} }

View File

@@ -247,6 +247,7 @@ public class WifiSettings extends RestrictedSettingsFragment
getContext().startActivity( getContext().startActivity(
WifiDppUtils.getEnrolleeQrCodeScannerIntent(/* ssid */ null)); WifiDppUtils.getEnrolleeQrCodeScannerIntent(/* ssid */ null));
}); });
mAddPreference.setButtonContentDescription(getString(R.string.wifi_dpp_scan_qr_code));
mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE); mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE);
mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager()); mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());

View File

@@ -29,6 +29,7 @@ import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
@@ -99,6 +100,7 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment {
if (!isConfigurationChange) { if (!isConfigurationChange) {
mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS; mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS;
changeFocusAndAnnounceChange(mButtonRight, mTitle);
} }
} }
@@ -168,15 +170,17 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment {
mButtonLeft.setVisibility(View.INVISIBLE); mButtonLeft.setVisibility(View.INVISIBLE);
} }
if (!isConfigurationChange) {
mLatestStatusCode = code;
}
if (isGoingInitiator()) { if (isGoingInitiator()) {
mSummary.setText(R.string.wifi_dpp_sharing_wifi_with_this_device); mSummary.setText(R.string.wifi_dpp_sharing_wifi_with_this_device);
} }
mProgressBar.setVisibility(isGoingInitiator() ? View.VISIBLE : View.INVISIBLE); mProgressBar.setVisibility(isGoingInitiator() ? View.VISIBLE : View.INVISIBLE);
mButtonRight.setVisibility(isGoingInitiator() ? View.INVISIBLE : View.VISIBLE); mButtonRight.setVisibility(isGoingInitiator() ? View.INVISIBLE : View.VISIBLE);
if (!isConfigurationChange) {
mLatestStatusCode = code;
changeFocusAndAnnounceChange(mButtonRight, mSummary);
}
} }
private boolean hasRetryButton(int code) { private boolean hasRetryButton(int code) {
@@ -277,6 +281,7 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment {
mButtonRight.setVisibility(View.INVISIBLE); mButtonRight.setVisibility(View.INVISIBLE);
startWifiDppConfiguratorInitiator(); startWifiDppConfiguratorInitiator();
updateSummary(); updateSummary();
mTitleSummaryContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}); });
if (savedInstanceState != null) { if (savedInstanceState != null) {
@@ -288,6 +293,8 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment {
} else { } else {
showErrorUi(mLatestStatusCode, /* isConfigurationChange */ true); showErrorUi(mLatestStatusCode, /* isConfigurationChange */ true);
} }
} else {
changeFocusAndAnnounceChange(mButtonRight, mTitleSummaryContainer);
} }
} }
@@ -354,4 +361,17 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment {
mSummary.setText(getString(R.string.wifi_dpp_add_device_to_wifi, getSsid())); mSummary.setText(getString(R.string.wifi_dpp_add_device_to_wifi, getSsid()));
} }
} }
/**
* This fragment will change UI display and text messages for events. To improve Talkback user
* experienience, using this method to focus on a right component and announce a changed text
* after an UI changing event.
*
* @param focusView The UI component which will be focused
* @param announceView The UI component's text will be talked
*/
private void changeFocusAndAnnounceChange(View focusView, View announceView) {
focusView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
announceView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
} }

View File

@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Button; import android.widget.Button;
import android.widget.ListView; import android.widget.ListView;
@@ -105,5 +106,11 @@ public class WifiDppChooseSavedWifiNetworkFragment extends WifiDppQrCodeBaseFrag
mButtonRight = view.findViewById(R.id.button_right); mButtonRight = view.findViewById(R.id.button_right);
mButtonRight.setVisibility(View.GONE); mButtonRight.setVisibility(View.GONE);
if (savedInstanceState == null) {
// For Talkback to describe this fragment
mTitleSummaryContainer.sendAccessibilityEvent(
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
} }
} }

View File

@@ -38,6 +38,7 @@ public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
private ImageView mDevicesCheckCircleGreenHeaderIcon; private ImageView mDevicesCheckCircleGreenHeaderIcon;
protected TextView mTitle; protected TextView mTitle;
protected TextView mSummary; protected TextView mSummary;
protected View mTitleSummaryContainer;
@Override @Override
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -48,6 +49,10 @@ public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
view.findViewById(R.id.devices_check_circle_green_icon); view.findViewById(R.id.devices_check_circle_green_icon);
mTitle = view.findViewById(android.R.id.title); mTitle = view.findViewById(android.R.id.title);
mSummary = view.findViewById(android.R.id.summary); mSummary = view.findViewById(android.R.id.summary);
// This is the LinearLayout which groups mTitle and mSummary for Talkback to announce the
// content in a way that reflects its natural groupings.
mTitleSummaryContainer = view.findViewById(R.id.title_summary_container);
} }
protected void setHeaderIconImageResource(int resId) { protected void setHeaderIconImageResource(int resId) {

View File

@@ -60,6 +60,14 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
// setTitle for Talkback
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
if (wifiNetworkConfig.isHotspot()) {
getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
} else {
getActivity().setTitle(R.string.wifi_dpp_share_wifi);
}
setHasOptionsMenu(true); setHasOptionsMenu(true);
final ActionBar actionBar = getActivity().getActionBar(); final ActionBar actionBar = getActivity().getActionBar();
if (actionBar != null) { if (actionBar != null) {

View File

@@ -41,6 +41,7 @@ import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener; import android.view.TextureView.SurfaceTextureListener;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
@@ -174,6 +175,13 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
// setTitle for Talkback
if (mIsConfiguratorMode) {
getActivity().setTitle(R.string.wifi_dpp_add_device_to_network);
} else {
getActivity().setTitle(R.string.wifi_dpp_scan_qr_code);
}
final ActionBar actionBar = getActivity().getActionBar(); final ActionBar actionBar = getActivity().getActionBar();
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
@@ -380,6 +388,7 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
public void showErrorMessage(String message) { public void showErrorMessage(String message) {
mErrorMessage.setVisibility(View.VISIBLE); mErrorMessage.setVisibility(View.VISIBLE);
mErrorMessage.setText(message); mErrorMessage.setText(message);
mErrorMessage.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
mHandler.removeMessages(MESSAGE_HIDE_ERROR_MESSAGE); mHandler.removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE, mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
@@ -410,6 +419,8 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
mProgressBar.setVisibility(View.VISIBLE); mProgressBar.setVisibility(View.VISIBLE);
startWifiDppEnrolleeInitiator((WifiQrCode)msg.obj); startWifiDppEnrolleeInitiator((WifiQrCode)msg.obj);
updateEnrolleeSummary(); updateEnrolleeSummary();
mSummary.sendAccessibilityEvent(
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
} }
break; break;