Java tutorial
/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.android.screenspeak.formatter; import android.os.Build; import android.support.v4.view.accessibility.AccessibilityEventCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.view.accessibility.AccessibilityRecordCompat; import android.text.TextUtils; import android.view.accessibility.AccessibilityEvent; import android.widget.Switch; import android.widget.ToggleButton; import com.android.screenspeak.R; import com.google.android.marvin.screenspeak.ScreenSpeakService; import com.android.screenspeak.Utterance; import com.android.utils.AccessibilityEventUtils; import com.android.utils.AccessibilityNodeInfoUtils; /** * Filters and formats click events from checkable items. */ public class CheckableClickedFormatter implements EventSpeechRule.AccessibilityEventFilter, EventSpeechRule.AccessibilityEventFormatter { private long mClickedTime = -1; private AccessibilityNodeInfoCompat mClickedNode; // accept and format are not called from the same class instance private static AccessibilityNodeInfoCompat sCachedCheckableNode; private boolean findClickedCheckableNode(AccessibilityNodeInfoCompat source) { if (source == null) return false; if (source.equals(mClickedNode)) { boolean ret = findCheckableNode(source); if (mClickedNode != null) { mClickedNode.recycle(); mClickedNode = null; } mClickedTime = -1; return ret; } int children = source.getChildCount(); for (int i = 0; i < children; i++) { AccessibilityNodeInfoCompat node = source.getChild(i); if (findClickedCheckableNode(node)) { if (!sCachedCheckableNode.equals(node)) { node.recycle(); } return true; } if (node != null) { node.recycle(); } } return false; } private boolean findCheckableNode(AccessibilityNodeInfoCompat source) { if (source.isCheckable()) { sCachedCheckableNode = source; return true; } int children = source.getChildCount(); for (int i = 0; i < children; i++) { AccessibilityNodeInfoCompat node = source.getChild(i); if (findCheckableNode(node)) { if (!sCachedCheckableNode.equals(node)) { node.recycle(); } return true; } if (node != null) { node.recycle(); } } return false; } @Override public boolean accept(AccessibilityEvent event, ScreenSpeakService context) { int type = event.getEventType(); if (type == AccessibilityEvent.TYPE_VIEW_CLICKED) { mClickedNode = null; mClickedTime = -1; if (event.isChecked()) { return true; } AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); AccessibilityNodeInfoCompat source = record.getSource(); if (source != null) { if (source.isCheckable()) { return true; } // it is bug in settings application that does not include clicked state on node // so we need to restore it later from TYPE_WINDOW_CONTENT_CHANGED event if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mClickedNode = source; mClickedTime = System.currentTimeMillis(); } } return false; } if (type == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return false; if (mClickedTime == -1 || mClickedNode == null) return false; long now = System.currentTimeMillis(); if ((mClickedTime + 1000) < now) { mClickedTime = -1; if (mClickedNode != null) { mClickedNode.recycle(); mClickedNode = null; } return false; } AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); AccessibilityNodeInfoCompat source = record.getSource(); return findClickedCheckableNode(source); } return false; } @Override public boolean format(AccessibilityEvent event, ScreenSpeakService context, Utterance utterance) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) { if (sCachedCheckableNode == null) return false; utterance.addAuditory(R.raw.tick); utterance.addHaptic(R.array.view_clicked_pattern); utterance.addSpoken(context.getString( sCachedCheckableNode.isChecked() ? R.string.value_checked : R.string.value_not_checked)); sCachedCheckableNode.recycle(); sCachedCheckableNode = null; return true; } AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); AccessibilityNodeInfoCompat source = record.getSource(); utterance.addAuditory(R.raw.tick); utterance.addHaptic(R.array.view_clicked_pattern); CharSequence eventText = AccessibilityEventUtils.getEventTextOrDescription(event); if (!TextUtils.isEmpty(eventText)) { utterance.addSpoken(eventText); } // Switch and ToggleButton state is sent along with the event, so only // append checked / not checked state for other types of controls. if (AccessibilityNodeInfoUtils.nodeMatchesClassByType(source, ToggleButton.class) || AccessibilityNodeInfoUtils.nodeMatchesClassByType(source, Switch.class)) { return true; } if (source == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { if (event.isChecked()) { utterance.addSpoken(context.getString(R.string.value_checked)); } else { utterance.addSpoken(context.getString(R.string.value_not_checked)); } return true; } if (source.isCheckable()) { if (source.isChecked()) { utterance.addSpoken(context.getString(R.string.value_checked)); } else { utterance.addSpoken(context.getString(R.string.value_not_checked)); } } return true; } }