1
0
Fork 0
mirror of https://github.com/MatomoCamp/matomocamp-companion-android.git synced 2024-09-19 16:13:46 +02:00

Improved accessibility throughout the app

This commit is contained in:
Christophe Beyls 2016-11-21 19:26:32 +01:00
parent 2ba817a56d
commit 295a70d441
13 changed files with 142 additions and 62 deletions

View file

@ -46,9 +46,12 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity"/>
</activity>
<activity android:name=".activities.TrackScheduleEventActivity"/>
<activity
android:name=".activities.TrackScheduleEventActivity"
android:label="@string/event_details"/>
<activity
android:name=".activities.EventDetailsActivity"
android:label="@string/event_details"
android:parentActivityName=".activities.TrackScheduleActivity">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
@ -66,6 +69,7 @@
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".activities.SearchResultActivity"
android:label="@string/search_events"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>

View file

@ -16,8 +16,6 @@ import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
@ -71,7 +69,7 @@ import be.digitalia.fosdem.widgets.AdapterLinearLayout;
*
* @author Christophe Beyls
*/
public class MainActivity extends AppCompatActivity implements Handler.Callback {
public class MainActivity extends AppCompatActivity {
private enum Section {
TRACKS(TracksFragment.class, R.string.menu_tracks, R.drawable.ic_event_grey600_24dp, true, true),
@ -118,11 +116,6 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
}
}
static final int SELECT_MENU_SECTION_WHAT = 1;
static final int SELECT_MENU_FOOTER_WHAT = 2;
static final long MENU_ACTION_DELAY = 400L;
private static final long DATABASE_VALIDITY_DURATION = DateUtils.DAY_IN_MILLIS;
private static final long DOWNLOAD_REMINDER_SNOOZE_DURATION = DateUtils.DAY_IN_MILLIS;
private static final String PREF_LAST_DOWNLOAD_REMINDER_TIME = "last_download_reminder_time";
@ -131,12 +124,13 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
private static final String LAST_UPDATE_DATE_FORMAT = "d MMM yyyy kk:mm:ss";
Handler handler;
private Toolbar toolbar;
ProgressBar progressBar;
// Main menu
Section currentSection;
int pendingMenuSection = -1;
int pendingMenuFooter = -1;
DrawerLayout drawerLayout;
private ActionBarDrawerToggle drawerToggle;
View mainMenu;
@ -216,8 +210,6 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
handler = new Handler(this);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
@ -229,12 +221,34 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
drawerLayout.setDrawerShadow(ContextCompat.getDrawable(this, R.drawable.drawer_shadow), GravityCompat.START);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.main_menu, R.string.close_menu) {
@Override
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
if (newState == DrawerLayout.STATE_DRAGGING) {
pendingMenuSection = -1;
pendingMenuFooter = -1;
}
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
// Make keypad navigation easier
mainMenu.requestFocus();
}
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
if (pendingMenuSection != -1) {
selectMenuSection(pendingMenuSection);
pendingMenuSection = -1;
}
if (pendingMenuFooter != -1) {
selectMenuFooter(pendingMenuFooter);
pendingMenuFooter = -1;
}
}
};
drawerToggle.setDrawerIndicatorEnabled(true);
drawerLayout.addDrawerListener(drawerToggle);
@ -282,7 +296,7 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
}
private void updateActionBar() {
getSupportActionBar().setTitle(currentSection.getTitleResId());
setTitle(currentSection.getTitleResId());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
toolbar.setElevation(currentSection.extendsAppBar()
? 0f : getResources().getDimension(R.dimen.toolbar_elevation));
@ -315,7 +329,8 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
@Override
protected void onSaveInstanceState(Bundle outState) {
// Ensure no fragment transaction attempt will occur after onSaveInstanceState()
handler.removeCallbacksAndMessages(null);
pendingMenuSection = -1;
pendingMenuFooter = -1;
super.onSaveInstanceState(outState);
outState.putInt(STATE_CURRENT_SECTION, currentSection.ordinal());
}
@ -505,15 +520,12 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
final View.OnClickListener sectionClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
int sectionIndex = ((ViewGroup) view.getParent()).indexOfChild(view);
// Cancel pending section selection, if any
handler.removeMessages(SELECT_MENU_SECTION_WHAT);
handler.sendMessageDelayed(handler.obtainMessage(SELECT_MENU_SECTION_WHAT, sectionIndex, 0), MENU_ACTION_DELAY);
pendingMenuSection = ((ViewGroup) view.getParent()).indexOfChild(view);
drawerLayout.closeDrawer(mainMenu);
}
};
private void selectMenuSection(int position) {
void selectMenuSection(int position) {
Section section = menuAdapter.getItem(position);
if (section != currentSection) {
// Switch to new section
@ -546,12 +558,12 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
@Override
public void onClick(View view) {
handler.sendMessageDelayed(handler.obtainMessage(SELECT_MENU_FOOTER_WHAT, view.getId(), 0), MENU_ACTION_DELAY);
pendingMenuFooter = view.getId();
drawerLayout.closeDrawer(mainMenu);
}
};
private void selectMenuFooter(int id) {
void selectMenuFooter(int id) {
switch (id) {
case R.id.settings:
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
@ -563,19 +575,6 @@ public class MainActivity extends AppCompatActivity implements Handler.Callback
}
}
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case SELECT_MENU_SECTION_WHAT:
selectMenuSection(message.arg1);
return true;
case SELECT_MENU_FOOTER_WHAT:
selectMenuFooter(message.arg1);
return true;
}
return false;
}
public static class AboutDialogFragment extends DialogFragment {
@NonNull

View file

@ -28,6 +28,7 @@ public class PersonInfoActivity extends AppCompatActivity {
bar.setDisplayHomeAsUpEnabled(true);
bar.setDisplayShowTitleEnabled(false);
((TextView) findViewById(R.id.title)).setText(person.getName());
setTitle(person.getName());
if (savedInstanceState == null) {
Fragment f = PersonInfoListFragment.newInstance(person);

View file

@ -26,6 +26,7 @@ public class RoomImageDialogActivity extends AppCompatActivity {
ImageView imageView = new ImageView(this);
imageView.setImageResource(intent.getIntExtra(EXTRA_ROOM_IMAGE_RESOURCE_ID, 0));
imageView.setContentDescription(getString(R.string.room_map));
int padding = getResources().getDimensionPixelSize(R.dimen.content_margin);
imageView.setPadding(padding, padding, padding, padding);

View file

@ -7,7 +7,6 @@ import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.view.Menu;
@ -33,9 +32,7 @@ public class SearchResultActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.content);
ActionBar bar = getSupportActionBar();
bar.setDisplayHomeAsUpEnabled(true);
bar.setTitle(R.string.search_events);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
handleIntent(getIntent(), false);
@ -125,17 +122,17 @@ public class SearchResultActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.search:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
return false;
} else {
// Legacy search mode for Eclair
onSearchRequested();
case android.R.id.home:
finish();
return true;
}
case R.id.search:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
return false;
} else {
// Legacy search mode for Eclair
onSearchRequested();
return true;
}
}
return false;
}

View file

@ -60,6 +60,7 @@ public class TrackScheduleActivity extends AppCompatActivity
bar.setDisplayHomeAsUpEnabled(true);
bar.setTitle(track.toString());
bar.setSubtitle(day.toString());
setTitle(String.format("%1$s, %2$s", track.toString(), day.toString()));
isTabletLandscape = getResources().getBoolean(R.bool.tablet_landscape);

View file

@ -39,12 +39,14 @@ public class BookmarksAdapter extends EventsAdapter {
holder.persons.setText(personsSummary);
holder.persons.setVisibility(TextUtils.isEmpty(personsSummary) ? View.GONE : View.VISIBLE);
holder.trackName.setText(event.getTrack().getName());
holder.trackName.setContentDescription(context.getString(R.string.track_content_description, event.getTrack().getName()));
Date startTime = event.getStartTime();
Date endTime = event.getEndTime();
String startTimeString = (startTime != null) ? timeDateFormat.format(startTime) : "?";
String endTimeString = (endTime != null) ? timeDateFormat.format(endTime) : "?";
String details = String.format("%1$s, %2$s ― %3$s | %4$s", event.getDay().getShortName(), startTimeString, endTimeString, event.getRoomName());
String detailsContentDescription = details;
// Highlight the date and time with error color in case of conflicting schedules
if (isOverlapping(cursor, startTime, endTime)) {
@ -53,9 +55,11 @@ public class BookmarksAdapter extends EventsAdapter {
detailsSpannable.setSpan(new ForegroundColorSpan(errorColor), 0, endPosition, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
detailsSpannable.setSpan(new StyleSpan(Typeface.BOLD), 0, endPosition, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
holder.details.setText(detailsSpannable);
detailsContentDescription = context.getString(R.string.bookmark_conflict_content_description, detailsContentDescription);
} else {
holder.details.setText(details);
}
holder.details.setContentDescription(context.getString(R.string.details_content_description, detailsContentDescription));
}
/**

View file

@ -63,14 +63,20 @@ public class EventsAdapter extends CursorAdapter {
holder.event = event;
holder.title.setText(event.getTitle());
Drawable bookmarkDrawable = DatabaseManager.toBookmarkStatus(cursor)
boolean isBookmarked = DatabaseManager.toBookmarkStatus(cursor);
Drawable bookmarkDrawable = isBookmarked
? AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_bookmark_grey600_24dp)
: null;
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(holder.title, null, null, bookmarkDrawable, null);
holder.title.setContentDescription(isBookmarked
? context.getString(R.string.in_bookmarks_content_description, event.getTitle())
: null
);
String personsSummary = event.getPersonsSummary();
holder.persons.setText(personsSummary);
holder.persons.setVisibility(TextUtils.isEmpty(personsSummary) ? View.GONE : View.VISIBLE);
holder.trackName.setText(event.getTrack().getName());
holder.trackName.setContentDescription(context.getString(R.string.track_content_description, event.getTrack().getName()));
Date startTime = event.getStartTime();
Date endTime = event.getEndTime();
@ -83,6 +89,7 @@ public class EventsAdapter extends CursorAdapter {
details = String.format("%1$s ― %2$s | %3$s", startTimeString, endTimeString, event.getRoomName());
}
holder.details.setText(details);
holder.details.setContentDescription(context.getString(R.string.details_content_description, details));
}
protected static class ViewHolder {

View file

@ -130,7 +130,13 @@ public class EventDetailsFragment extends Fragment {
holder.personsTextView.setVisibility(View.VISIBLE);
}
((TextView) view.findViewById(R.id.track)).setText(event.getTrack().getName());
textView = ((TextView) view.findViewById(R.id.track));
text = event.getTrack().getName();
textView.setText(text);
textView.setContentDescription(getString(R.string.track_content_description, text));
textView = ((TextView) view.findViewById(R.id.time));
Date startTime = event.getStartTime();
Date endTime = event.getEndTime();
DateFormat timeDateFormat = DateUtils.getTimeDateFormat(getActivity());
@ -138,24 +144,28 @@ public class EventDetailsFragment extends Fragment {
event.getDay().toString(),
(startTime != null) ? timeDateFormat.format(startTime) : "?",
(endTime != null) ? timeDateFormat.format(endTime) : "?");
((TextView) view.findViewById(R.id.time)).setText(text);
textView.setText(text);
textView.setContentDescription(getString(R.string.time_content_description, text));
textView = (TextView) view.findViewById(R.id.room);
final String roomName = event.getRoomName();
TextView roomTextView = (TextView) view.findViewById(R.id.room);
Spannable roomText = new SpannableString(String.format("%1$s (Building %2$s)", roomName, Building.fromRoomName(roomName)));
final int roomImageResId = getResources().getIdentifier(StringUtils.roomNameToResourceName(roomName), "drawable", getActivity().getPackageName());
// If the room image exists, make the room text clickable to display it
if (roomImageResId != 0) {
roomText.setSpan(new UnderlineSpan(), 0, roomText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
roomTextView.setOnClickListener(new View.OnClickListener() {
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
RoomImageDialogFragment.newInstance(roomName, roomImageResId).show(getFragmentManager());
}
});
roomTextView.setFocusable(true);
textView.setFocusable(true);
}
roomTextView.setText(roomText);
textView.setText(roomText);
textView.setContentDescription(getString(R.string.room_content_description, roomText));
textView = (TextView) view.findViewById(R.id.abstract_text);
text = event.getAbstractText();

View file

@ -31,6 +31,7 @@ public class RoomImageDialogFragment extends DialogFragment {
ImageView imageView = new ImageView(getActivity());
imageView.setImageResource(args.getInt("imageResId"));
imageView.setContentDescription(getString(R.string.room_map));
int padding = getResources().getDimensionPixelSize(R.dimen.content_margin);
imageView.setPadding(padding, padding, padding, padding);

View file

@ -291,26 +291,36 @@ public class TrackScheduleListFragment extends SmoothListFragment implements Han
Event event = DatabaseManager.toEvent(cursor, holder.event);
holder.event = event;
holder.time.setText(timeDateFormat.format(event.getStartTime()));
String formattedTime = timeDateFormat.format(event.getStartTime());
holder.time.setText(formattedTime);
if ((currentTime != -1L) && event.isRunningAtTime(currentTime)) {
// Contrast colors for running event
holder.time.setBackgroundColor(timeRunningBackgroundColor);
holder.time.setTextColor(timeRunningForegroundColor);
holder.time.setContentDescription(context.getString(R.string.in_progress_content_description, formattedTime));
} else {
// Normal colors
holder.time.setBackgroundColor(timeBackgroundColor);
holder.time.setTextColor(timeForegroundColor);
// Use text as content description
holder.time.setContentDescription(null);
}
holder.title.setText(event.getTitle());
Drawable bookmarkDrawable = DatabaseManager.toBookmarkStatus(cursor)
boolean isBookmarked = DatabaseManager.toBookmarkStatus(cursor);
Drawable bookmarkDrawable = isBookmarked
? AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_bookmark_grey600_24dp)
: null;
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(holder.title, null, null, bookmarkDrawable, null);
holder.title.setContentDescription(isBookmarked
? context.getString(R.string.in_bookmarks_content_description, event.getTitle())
: null
);
String personsSummary = event.getPersonsSummary();
holder.persons.setText(personsSummary);
holder.persons.setVisibility(TextUtils.isEmpty(personsSummary) ? View.GONE : View.VISIBLE);
holder.room.setText(event.getRoomName());
holder.room.setContentDescription(context.getString(R.string.room_content_description, event.getRoomName()));
}
static class ViewHolder {

View file

@ -17,6 +17,7 @@
package be.digitalia.fosdem.widgets;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@ -30,9 +31,11 @@ import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
@ -62,7 +65,7 @@ public class SlidingTabLayout extends HorizontalScrollView {
private ColorStateList mTextColor;
ViewPager mViewPager;
private PagerAdapter mAdapter;
PagerAdapter mAdapter;
private final InternalViewPagerListener mPageChangeListener = new InternalViewPagerListener();
private final PagerAdapterObserver mPagerAdapterObserver = new PagerAdapterObserver();
@ -186,6 +189,15 @@ public class SlidingTabLayout extends HorizontalScrollView {
}
}
static void setSelectedCompat(View view, boolean selected) {
final boolean becomeSelected = selected && !view.isSelected();
view.setSelected(selected);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && becomeSelected) {
// Pre-JB we need to manually send the TYPE_VIEW_SELECTED event
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
}
private void populateTabStrip() {
final int adapterCount = mAdapter.getCount();
final View.OnClickListener tabClickListener = new TabClickListener();
@ -228,7 +240,7 @@ public class SlidingTabLayout extends HorizontalScrollView {
mTabStrip.addView(tabView);
if (i == currentItem) {
tabView.setSelected(true);
setSelectedCompat(tabView, true);
}
}
}
@ -236,10 +248,33 @@ public class SlidingTabLayout extends HorizontalScrollView {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mViewPager != null) {
scrollToTab(mViewPager.getCurrentItem(), 0);
}
announceSelectedTab(hasWindowFocus());
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
announceSelectedTab(hasWindowFocus);
}
private void announceSelectedTab(boolean hasWindowFocus) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && hasWindowFocus) {
getHandler().post(new Runnable() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void run() {
if (mViewPager != null && mAdapter != null && mAdapter.getCount() > 0) {
CharSequence pageTitle = mAdapter.getPageTitle(mViewPager.getCurrentItem());
if (!TextUtils.isEmpty(pageTitle)) {
announceForAccessibility(pageTitle);
}
}
}
});
}
}
void scrollToTab(int tabIndex, float positionOffset) {
@ -286,7 +321,7 @@ public class SlidingTabLayout extends HorizontalScrollView {
}
final int childCount = mTabStrip.getChildCount();
for (int i = 0; i < childCount; i++) {
mTabStrip.getChildAt(i).setSelected(position == i);
setSelectedCompat(mTabStrip.getChildAt(i), position == i);
}
}
@ -331,7 +366,7 @@ public class SlidingTabLayout extends HorizontalScrollView {
}
class SlidingTabStrip extends LinearLayout {
static class SlidingTabStrip extends LinearLayout {
private int mSelectedIndicatorHeight;
private final Paint mSelectedIndicatorPaint;

View file

@ -30,6 +30,7 @@
<string name="upcoming_only">Upcoming only</string>
<string name="no_bookmark">No bookmark.</string>
<string name="remove_bookmarks">Remove bookmarks</string>
<string name="bookmark_conflict_content_description">%1$s\n Other bookmarks are scheduled at the same time.</string>
<plurals name="selected">
<item quantity="other">%1$d selected</item>
@ -89,7 +90,16 @@
<string name="settings_notifications_delay_summary">Delay before getting notified of an upcoming bookmarked event</string>
<!-- Track schedule -->
<string name="in_progress_content_description">%1$s (in progress)</string>
<!-- Event details -->
<string name="event_details">Event details</string>
<string name="track_content_description">Track: %1$s</string>
<string name="time_content_description">Time: %1$s</string>
<string name="room_content_description">Room: %1$s</string>
<string name="details_content_description">Details: %1$s</string>
<string name="in_bookmarks_content_description">In bookmarks: %1$s</string>
<string name="related_links_header">Related Links</string>
<string name="add_bookmark">Add to bookmarks</string>
<string name="remove_bookmark">Remove from bookmarks</string>