/* * Copyright (C) 2013 Google Inc. * * 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.talkback; import android.os.Bundle; import android.text.TextUtils; import com.android.talkback.SpeechController.SpeechParam; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Represents a fragment of feedback included within a {@link FeedbackItem}. It * must contain speech with optional earcons and haptic feedback. */ class FeedbackFragment { /** Text to be spoken when processing this fragment */ private CharSequence mText; /** * Set of resource IDs indicating the auditory icons to be played when this * fragment is processed */ private Set<Integer> mEarcons; /** * Set of resource IDs indicating the haptic patterns to be generated when * this fragment is processed */ private Set<Integer> mHaptics; /** * {@link SpeechController.SpeechParam} fields used for altering various * properties on the speech feedback for this fragment. * * @see SpeechParam#PITCH * @see SpeechParam#RATE * @see SpeechParam#VOLUME */ private Bundle mSpeechParams; /** * {@link Utterance} metadata parameters used for altering various * properties on the non-speech feedback for this fragment. * * @see Utterance#KEY_METADATA_EARCON_RATE * @see Utterance#KEY_METADATA_EARCON_VOLUME */ private Bundle mNonSpeechParams; public FeedbackFragment(CharSequence text, Bundle speechParams) { this(text, null, null, speechParams, null); } public FeedbackFragment(CharSequence text, Set<Integer> earcons, Set<Integer> haptics, Bundle speechParams, Bundle nonSpeechParams) { mText = text; mEarcons = new HashSet<>(); if (earcons != null) { mEarcons.addAll(earcons); } mHaptics = new HashSet<>(); if (haptics != null) { mHaptics.addAll(haptics); } mSpeechParams = new Bundle(Bundle.EMPTY); if (speechParams != null) { mSpeechParams.putAll(speechParams); } mNonSpeechParams = new Bundle(Bundle.EMPTY); if (nonSpeechParams != null) { mNonSpeechParams.putAll(nonSpeechParams); } } /** * @return The text of this fragment */ public CharSequence getText() { return mText; } /** * @param text The text to set for this fragment */ public void setText(CharSequence text) { mText = text; } /** * @return An unmodifiable set of IDs of the earcons to play along with this * fragment */ public Set<Integer> getEarcons() { return Collections.unmodifiableSet(mEarcons); } /** * @param earconId The ID of the earcon to add to the set of earcons to play * when this fragment is processed */ public void addEarcon(int earconId) { mEarcons.add(earconId); } /** * Clears all earcons associated with this fragment */ public void clearAllEarcons() { mEarcons.clear(); } /** * @return an unmodifiable set of IDs of the haptic patterns to produce * along with this fragment */ public Set<Integer> getHaptics() { return Collections.unmodifiableSet(mHaptics); } /** * @param hapticId The ID of the haptic pattern to add to the set of haptic * patterns to play when this fragment is processed */ public void addHaptic(int hapticId) { mHaptics.add(hapticId); } /** * Clears all haptic patterns associated with this fragment. */ public void clearAllHaptics() { mHaptics.clear(); } /** * @return the {@link SpeechParam} parameters to use when processing this * fragment */ public Bundle getSpeechParams() { return mSpeechParams; } /** * @param speechParams the {@link SpeechParam} parameters to use when * processing this fragment */ public void setSpeechParams(Bundle speechParams) { mSpeechParams = speechParams; } /** * @return the {@link Utterance} non-speech parameters to use when * processing this fragment */ public Bundle getNonSpeechParams() { return mNonSpeechParams; } /** * @param nonSpeechParams the {@link SpeechParam} parameters to use when * processing this fragment */ public void setNonSpeechParams(Bundle nonSpeechParams) { mNonSpeechParams = nonSpeechParams; } @Override public String toString() { return "{text:" + mText + ", earcons:" + mEarcons + ", haptics:" + mHaptics + ", speechParams:" + mSpeechParams + "nonSpeechParams:" + mNonSpeechParams + "}"; } @Override public int hashCode() { int hashCode = 17; hashCode = 31 * hashCode + (mText == null ? 0 : mText.hashCode()); hashCode = 31 * hashCode + (mEarcons == null ? 0 : mEarcons.hashCode()); hashCode = 31 * hashCode + (mHaptics == null ? 0 : mHaptics.hashCode()); hashCode = 31 * hashCode + getBundleHashCode(mSpeechParams); hashCode = 31 * hashCode + getBundleHashCode(mNonSpeechParams); return hashCode; } private int getBundleHashCode(Bundle bundle) { if (bundle == null) { return 0; } int hashCode = 0; for (String key : bundle.keySet()) { Object value = bundle.get(key); hashCode += value == null ? 0 : value.hashCode(); } return hashCode; } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof FeedbackFragment)) { return false; } FeedbackFragment fragment = (FeedbackFragment) obj; if (!TextUtils.equals(mText, fragment.mText)) { return false; } //noinspection SimplifiableIfStatement if (objectsNotEqual(mEarcons, fragment.mEarcons) || objectsNotEqual(mHaptics, fragment.mHaptics)) { return false; } return !(bundleNotEqual(mSpeechParams, fragment.mSpeechParams) || bundleNotEqual(mNonSpeechParams, fragment.mNonSpeechParams)); } private boolean objectsNotEqual(Object obj1, Object obj2) { return (obj1 != null || obj2 != null) && (obj1 == null || obj2 == null || !obj1.equals(obj2)); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean bundleNotEqual(Bundle bundle1, Bundle bundle2) { if (bundle1 == null && bundle2 == null) { return false; } if (bundle1 != null && bundle2 != null) { int size = bundle1.size(); if (bundle2.size() != size) { return true; } for(String key : bundle1.keySet()) { if(objectsNotEqual(bundle1.get(key), bundle2.get(key))) { return true; } } return false; } return true; } }