blob: d2bdf8051f74b0a16bb4b83f3785c948776443e3 [file] [log] [blame]
Rhed Jao0090ad72019-09-23 21:54:30 +08001/*
2 * Copyright (C) 2019 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 android.accessibilityservice;
18
Peter_Liang01a592b2020-02-24 22:33:04 +080019import static android.accessibilityservice.util.AccessibilityUtils.getFilteredHtmlText;
20import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage;
21
Rhed Jao0090ad72019-09-23 21:54:30 +080022import android.annotation.NonNull;
23import android.annotation.Nullable;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.pm.ActivityInfo;
28import android.content.pm.PackageManager;
29import android.content.res.Resources;
30import android.content.res.TypedArray;
31import android.content.res.XmlResourceParser;
Peter_Liang81332ef2020-02-19 12:42:23 +080032import android.graphics.drawable.Drawable;
Peter_Liang193da542020-02-18 13:20:05 +080033import android.text.TextUtils;
Rhed Jao0090ad72019-09-23 21:54:30 +080034import android.util.AttributeSet;
35import android.util.Xml;
36
37import org.xmlpull.v1.XmlPullParser;
38import org.xmlpull.v1.XmlPullParserException;
39
40import java.io.IOException;
41
42/**
43 * Activities of interest to users with accessibility needs may request to be targets of the
44 * accessibility shortcut. These activities must handle the
45 * {@link Intent#ACTION_MAIN} intent with category
46 * {@link Intent#CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET}, which will be dispatched by the system
47 * when the user activates the shortcut when it is configured to point at this target.
48 *
49 * @see Intent#CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET
50 *
51 * @hide
52 */
53public final class AccessibilityShortcutInfo {
54 private static final String TAG_ACCESSIBILITY_SHORTCUT = "accessibility-shortcut-target";
55
56 /**
57 * Name under which an activity component of the accessibility shortcut publishes information
58 * about itself. This meta-data must reference an XML resource containing an
59 * <code>&lt;accessibility-shortcut-target&gt;</code> tag.
60 */
61 public static final String META_DATA = "android.accessibilityshortcut.target";
62
63 /**
64 * The component name of the accessibility shortcut target.
65 */
66 private final ComponentName mComponentName;
67
68 /**
69 * The activity info of the accessibility shortcut target.
70 */
71 private final ActivityInfo mActivityInfo;
72
73 /**
74 * Resource id of the summary of the accessibility shortcut target.
75 */
76 private final int mSummaryResId;
77
78 /**
79 * Resource id of the description of the accessibility shortcut target.
80 */
81 private final int mDescriptionResId;
82
83 /**
Rhed Jao649b2fb2020-02-07 18:56:56 +080084 * Resource id of the animated image of the accessibility shortcut target.
85 */
86 private final int mAnimatedImageRes;
87
88 /**
89 * Resource id of the html description of the accessibility shortcut target.
90 */
91 private final int mHtmlDescriptionRes;
92
93 /**
Rhed Jao0090ad72019-09-23 21:54:30 +080094 * Creates a new instance.
95 *
96 * @param context Context for accessing resources.
97 * @param activityInfo The activity info.
98 * @throws XmlPullParserException If a XML parsing error occurs.
99 * @throws IOException If a XML parsing error occurs.
100 */
101 public AccessibilityShortcutInfo(@NonNull Context context, @NonNull ActivityInfo activityInfo)
102 throws XmlPullParserException, IOException {
103 final PackageManager packageManager = context.getPackageManager();
104 mComponentName = activityInfo.getComponentName();
105 mActivityInfo = activityInfo;
106
107 try (XmlResourceParser parser = mActivityInfo.loadXmlMetaData(
108 packageManager, META_DATA)) {
109 if (parser == null) {
110 throw new XmlPullParserException("Meta-data "
111 + TAG_ACCESSIBILITY_SHORTCUT + " does not exist");
112 }
113
114 int type = 0;
115 while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
116 type = parser.next();
117 }
118
119 final String nodeName = parser.getName();
120 if (!TAG_ACCESSIBILITY_SHORTCUT.equals(nodeName)) {
121 throw new XmlPullParserException("Meta-data does not start with"
122 + TAG_ACCESSIBILITY_SHORTCUT + " tag");
123 }
124
125 final AttributeSet allAttributes = Xml.asAttributeSet(parser);
126 final Resources resources = packageManager.getResourcesForApplication(
127 mActivityInfo.applicationInfo);
128 final TypedArray asAttributes = resources.obtainAttributes(allAttributes,
129 com.android.internal.R.styleable.AccessibilityShortcutTarget);
130
131 // Gets description
132 mDescriptionResId = asAttributes.getResourceId(
133 com.android.internal.R.styleable.AccessibilityShortcutTarget_description, 0);
134 // Gets summary
135 mSummaryResId = asAttributes.getResourceId(
136 com.android.internal.R.styleable.AccessibilityShortcutTarget_summary, 0);
Rhed Jao649b2fb2020-02-07 18:56:56 +0800137 // Gets animated image
138 mAnimatedImageRes = asAttributes.getResourceId(
139 com.android.internal.R.styleable
Peter_Liang193da542020-02-18 13:20:05 +0800140 .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0);
Rhed Jao649b2fb2020-02-07 18:56:56 +0800141 // Gets html description
142 mHtmlDescriptionRes = asAttributes.getResourceId(
143 com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
144 0);
Rhed Jao0090ad72019-09-23 21:54:30 +0800145 asAttributes.recycle();
146
147 if (mDescriptionResId == 0 || mSummaryResId == 0) {
148 throw new XmlPullParserException("No description or summary in meta-data");
149 }
150 } catch (PackageManager.NameNotFoundException e) {
151 throw new XmlPullParserException("Unable to create context for: "
152 + mActivityInfo.packageName);
153 }
154 }
155
156 /**
157 * The {@link ActivityInfo} of accessibility shortcut target.
158 *
159 * @return The activity info.
160 */
161 @NonNull
162 public ActivityInfo getActivityInfo() {
163 return mActivityInfo;
164 }
165
166 /**
Rhed Jao6dad25d2019-10-22 22:15:05 +0800167 * The {@link ComponentName} of the accessibility shortcut target.
168 *
169 * @return The component name
170 */
171 @NonNull
172 public ComponentName getComponentName() {
173 return mComponentName;
174 }
175
176 /**
Rhed Jao0090ad72019-09-23 21:54:30 +0800177 * The localized summary of the accessibility shortcut target.
178 *
179 * @return The localized summary if available, and {@code null} if a summary
180 * has not been provided.
181 */
182 @Nullable
183 public String loadSummary(@NonNull PackageManager packageManager) {
184 return loadResourceString(packageManager, mActivityInfo, mSummaryResId);
185 }
186
187 /**
188 * The localized description of the accessibility shortcut target.
189 *
190 * @return The localized description.
191 */
192 @Nullable
193 public String loadDescription(@NonNull PackageManager packageManager) {
194 return loadResourceString(packageManager, mActivityInfo, mDescriptionResId);
195 }
196
197 /**
Peter_Liang193da542020-02-18 13:20:05 +0800198 * Gets the animated image resource id.
Rhed Jao649b2fb2020-02-07 18:56:56 +0800199 *
200 * @return The animated image resource id.
Peter_Liang81332ef2020-02-19 12:42:23 +0800201 *
202 * @hide
Rhed Jao649b2fb2020-02-07 18:56:56 +0800203 */
204 public int getAnimatedImageRes() {
205 return mAnimatedImageRes;
206 }
207
208 /**
Peter_Liang81332ef2020-02-19 12:42:23 +0800209 * The animated image drawable of the accessibility shortcut target.
210 *
Peter_Liang193da542020-02-18 13:20:05 +0800211 * @return The animated image drawable, or null if the resource is invalid or the image
212 * exceed the screen size.
Peter_Liang01a592b2020-02-24 22:33:04 +0800213 *
214 * @hide
Peter_Liang81332ef2020-02-19 12:42:23 +0800215 */
216 @Nullable
Peter_Liangf532a232020-02-20 10:37:52 +0800217 public Drawable loadAnimatedImage(@NonNull Context context) {
Peter_Liang81332ef2020-02-19 12:42:23 +0800218 if (mAnimatedImageRes == /* invalid */ 0) {
219 return null;
220 }
221
Peter_Liang193da542020-02-18 13:20:05 +0800222 return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes);
Peter_Liang81332ef2020-02-19 12:42:23 +0800223 }
224
225 /**
Peter_Liang193da542020-02-18 13:20:05 +0800226 * The localized and restricted html description of the accessibility shortcut target.
227 * It filters the <img> tag which do not meet the custom specification and the <a> tag.
Rhed Jao649b2fb2020-02-07 18:56:56 +0800228 *
Peter_Liang193da542020-02-18 13:20:05 +0800229 * @return The localized and restricted html description.
Peter_Liang01a592b2020-02-24 22:33:04 +0800230 *
231 * @hide
Rhed Jao649b2fb2020-02-07 18:56:56 +0800232 */
233 @Nullable
234 public String loadHtmlDescription(@NonNull PackageManager packageManager) {
Peter_Liang193da542020-02-18 13:20:05 +0800235 final String htmlDescription = loadResourceString(packageManager, mActivityInfo,
236 mHtmlDescriptionRes);
237 return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription);
Rhed Jao649b2fb2020-02-07 18:56:56 +0800238 }
239
240 /**
Rhed Jao0090ad72019-09-23 21:54:30 +0800241 * Gets string resource by the given activity and resource id.
242 */
243 @Nullable
244 private String loadResourceString(@NonNull PackageManager packageManager,
245 @NonNull ActivityInfo activityInfo, int resId) {
246 if (resId == 0) {
247 return null;
248 }
249 final CharSequence text = packageManager.getText(activityInfo.packageName,
250 resId, activityInfo.applicationInfo);
251 if (text != null) {
252 return text.toString().trim();
253 }
254 return null;
255 }
256
257 /**
258 * {@inheritDoc}
259 */
260 @Override
261 public int hashCode() {
262 return 31 * 1 + ((mComponentName == null) ? 0 : mComponentName.hashCode());
263 }
264
265 /**
266 * {@inheritDoc}
267 */
268 @Override
269 public boolean equals(Object obj) {
270 if (this == obj) {
271 return true;
272 }
273 if (obj == null) {
274 return false;
275 }
276 if (getClass() != obj.getClass()) {
277 return false;
278 }
279 final AccessibilityShortcutInfo other = (AccessibilityShortcutInfo) obj;
280 if (mComponentName == null) {
281 if (other.mComponentName != null) {
282 return false;
283 }
284 } else if (!mComponentName.equals(other.mComponentName)) {
285 return false;
286 }
287 return true;
288 }
289
290 /**
291 * {@inheritDoc}
292 */
293 @Override
294 public String toString() {
295 StringBuilder stringBuilder = new StringBuilder();
296 stringBuilder.append("AccessibilityShortcutInfo[");
297 stringBuilder.append("activityInfo: ").append(mActivityInfo);
298 stringBuilder.append("]");
299 return stringBuilder.toString();
300 }
Rhed Jao0090ad72019-09-23 21:54:30 +0800301}