/TalkBack/src/com/google/android/marvin/talkback/Utils.java
Java | 290 lines | 178 code | 31 blank | 81 comment | 74 complexity | 77961e5a762938672fceb15cbb861664 MD5 | raw file
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.android.marvin.talkback; 18 19import android.content.Context; 20import android.content.pm.PackageInfo; 21import android.content.pm.PackageManager; 22import android.content.pm.PackageManager.NameNotFoundException; 23import android.content.res.Resources; 24import android.os.Bundle; 25import android.text.TextUtils; 26import android.util.Log; 27import android.view.accessibility.AccessibilityEvent; 28import android.view.accessibility.AccessibilityNodeInfo; 29 30import java.util.ArrayList; 31import java.util.HashMap; 32import java.util.List; 33import java.util.Map; 34 35/** 36 * This class contains utility methods. 37 * 38 * @author svetoslavganov@google.com (Svetoslav R. Ganov) 39 * @author alanv@google.com (Alan Viverette) 40 */ 41public class Utils { 42 private static final Context CONTEXT = TalkBackService.asContext(); 43 private static final CharSequence SEPARATOR = " "; 44 45 /** Invalid version code for a package. */ 46 public static final int INVALID_VERSION_CODE = -1; 47 48 /** 49 * @return The package version code or {@link #INVALID_VERSION_CODE} if the 50 * package does not exist. 51 */ 52 public static int getVersionCode(Context context, String packageName) { 53 final PackageManager packageManager = context.getPackageManager(); 54 55 try { 56 final PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); 57 return packageInfo.versionCode; 58 } catch (NameNotFoundException e) { 59 LogUtils.log(Utils.class, Log.ERROR, "Could not find package: %s\n%s", packageName, 60 e.toString()); 61 return INVALID_VERSION_CODE; 62 } 63 } 64 65 /** 66 * @return The package version name or <code>null</code> if the package does 67 * not exist. 68 */ 69 public static String getVersionName(Context context, String packageName) { 70 final PackageManager packageManager = context.getPackageManager(); 71 72 try { 73 final PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); 74 return packageInfo.versionName; 75 } catch (NameNotFoundException e) { 76 LogUtils.log(Utils.class, Log.ERROR, "Could not find package: %s\n%s", packageName, 77 e.toString()); 78 return null; 79 } 80 } 81 82 /** 83 /** 84 * Returns {@code true} if the specified node is a view group or has 85 * children. 86 * 87 * @param node The node to test. 88 * @return {@code true} if the specified node is a view group or has 89 * children. 90 */ 91 public static boolean isViewGroup(AccessibilityEvent event, AccessibilityNodeInfo node) { 92 if (event != null && Utils.eventMatchesClassByType(event, android.view.ViewGroup.class)) { 93 return true; 94 } 95 96 if (node != null 97 && ((node.getChildCount() > 0) || AccessibilityNodeInfoUtils 98 .nodeMatchesClassByType(node, android.view.ViewGroup.class))) { 99 return true; 100 } 101 102 return false; 103 } 104 105 /** 106 * Determines if the generating class of an {@link AccessibilityEvent} 107 * matches a given {@link Class} by type. 108 * 109 * @param event An {@link AccessibilityEvent} dispatched by the 110 * accessibility framework. 111 * @param clazz A {@link Class} to match by type or inherited type. 112 * @return {@code true} if the {@link AccessibilityNodeInfo} object matches 113 * the {@link Class} by type or inherited type, {@code false} 114 * otherwise. 115 */ 116 public static boolean eventMatchesClassByType(AccessibilityEvent event, Class<?> clazz) { 117 if (event == null || clazz == null) { 118 return false; 119 } 120 121 final ClassLoadingManager classLoader = ClassLoadingManager.getInstance(); 122 final CharSequence className = event.getClassName(); 123 final Class<?> nodeClass = classLoader.loadOrGetCachedClass(CONTEXT, className, null); 124 125 if (nodeClass == null) { 126 return false; 127 } 128 129 return clazz.isAssignableFrom(nodeClass); 130 } 131 132 /** 133 * Gets the text of an <code>event</code> by concatenating the text members 134 * (regardless of their priority) using space as a delimiter. 135 * 136 * @param context The context from which to load required resources. 137 * @param event The event. 138 * @return The event text. 139 */ 140 public static CharSequence getEventText(Context context, AccessibilityEvent event) { 141 final CharSequence contentDescription = event.getContentDescription(); 142 143 if (!TextUtils.isEmpty(contentDescription)) { 144 return new StringBuilder(contentDescription); 145 } else { 146 return getEventAggregateText(context, event); 147 } 148 } 149 150 /** 151 * Gets the text of an <code>event</code> by concatenating the text members 152 * (regardless of their priority) using space as a delimiter. 153 * 154 * @param context The context from which to load required resources. 155 * @param event The event. 156 * @return The event text. 157 */ 158 public static CharSequence getEventAggregateText(Context context, AccessibilityEvent event) { 159 final StringBuilder aggregator = new StringBuilder(); 160 final List<CharSequence> eventText = event.getText(); 161 162 for (CharSequence text : event.getText()) { 163 aggregator.append(text); 164 aggregator.append(SEPARATOR); 165 } 166 167 return aggregator; 168 } 169 170 /** 171 * Loads a map of key strings to value strings from array resources. 172 * 173 * @param context The parent context. 174 * @param keysResource A resource identifier for the array of key strings. 175 * @param valuesResource A resource identifier for the array of value 176 * strings. 177 * @return A map of keys to values. 178 */ 179 public static Map<String, String> loadMapFromStringArrays(Context context, int keysResource, 180 int valuesResource) { 181 final Resources res = context.getResources(); 182 final String[] keys = res.getStringArray(keysResource); 183 final String[] values = res.getStringArray(valuesResource); 184 185 if (keys.length != values.length) { 186 throw new IllegalArgumentException("Array size mismatch"); 187 } 188 189 final Map<String, String> map = new HashMap<String, String>(); 190 191 for (int i = 0; i < keys.length; i++) { 192 map.put(keys[i], values[i]); 193 } 194 195 return map; 196 } 197 198 /** 199 * @return If the <code>first</code> event is equal to the 200 * <code>second</code>. 201 */ 202 public static boolean accessibilityEventEquals(AccessibilityEvent first, 203 AccessibilityEvent second) { 204 if (first == null || second == null) { 205 return false; 206 } 207 if (first.getEventType() != second.getEventType()) { 208 return false; 209 } 210 if (first.getPackageName() == null) { 211 if (second.getPackageName() != null) { 212 return false; 213 } 214 } else if (!first.getPackageName().equals(second.getPackageName())) { 215 return false; 216 } 217 if (first.getClassName() == null) { 218 if (second.getClassName() != null) { 219 return false; 220 } 221 } else if (!first.getClassName().equals(second.getClassName())) { 222 return false; 223 } 224 if (!first.getText().equals(second.getText())) { // never null 225 return false; 226 } 227 if (first.getContentDescription() == null) { 228 if (second.getContentDescription() != null) { 229 return false; 230 } 231 } else if (!first.getContentDescription().equals(second.getContentDescription())) { 232 return false; 233 } 234 if (first.getBeforeText() == null) { 235 if (second.getBeforeText() != null) { 236 return false; 237 } 238 } else if (!first.getBeforeText().equals(second.getBeforeText())) { 239 return false; 240 } 241 if (first.getParcelableData() != null) { 242 // do not compare parcelable data it may not implement equals 243 // correctly 244 245 return false; 246 } 247 if (first.getAddedCount() != second.getAddedCount()) { 248 return false; 249 } 250 if (first.isChecked() != second.isChecked()) { 251 return false; 252 } 253 if (first.isEnabled() != second.isEnabled()) { 254 return false; 255 } 256 if (first.getFromIndex() != second.getFromIndex()) { 257 return false; 258 } 259 if (first.isFullScreen() != second.isFullScreen()) { 260 return false; 261 } 262 if (first.getCurrentItemIndex() != second.getCurrentItemIndex()) { 263 return false; 264 } 265 if (first.getItemCount() != second.getItemCount()) { 266 return false; 267 } 268 if (first.isPassword() != second.isPassword()) { 269 return false; 270 } 271 if (first.getRemovedCount() != second.getRemovedCount()) { 272 return false; 273 } 274 if (first.getEventTime() != second.getEventTime()) { 275 return false; 276 } 277 return true; 278 } 279 280 public static void addToListOrCreate(Bundle bundle, String listKey, int value) { 281 ArrayList<Integer> list = bundle.getIntegerArrayList(listKey); 282 283 if (list == null) { 284 list = new ArrayList<Integer>(); 285 bundle.putIntegerArrayList(listKey, list); 286 } 287 288 list.add(value); 289 } 290}