Accessibility feature - framework changes (replacing 698, 699, 700, 701 and merging with the latest Donut)
This commit is contained in:
@@ -64,6 +64,8 @@ endif
|
||||
##
|
||||
## READ ME: ########################################################
|
||||
LOCAL_SRC_FILES += \
|
||||
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
|
||||
core/java/android/accessibilityservice/IEventListener.aidl \
|
||||
core/java/android/accounts/IAccountsService.aidl \
|
||||
core/java/android/app/IActivityPendingResult.aidl \
|
||||
core/java/android/app/IActivityWatcher.aidl \
|
||||
@@ -106,6 +108,8 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/os/IPermissionController.aidl \
|
||||
core/java/android/os/IPowerManager.aidl \
|
||||
core/java/android/text/IClipboard.aidl \
|
||||
core/java/android/view/accessibility/IAccessibilityManager.aidl \
|
||||
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
|
||||
core/java/android/view/IApplicationToken.aidl \
|
||||
core/java/android/view/IOnKeyguardExitResult.aidl \
|
||||
core/java/android/view/IRotationWatcher.aidl \
|
||||
|
||||
1143
api/current.xml
1143
api/current.xml
File diff suppressed because it is too large
Load Diff
225
core/java/android/accessibilityservice/AccessibilityService.java
Normal file
225
core/java/android/accessibilityservice/AccessibilityService.java
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.accessibilityservice;
|
||||
|
||||
import com.android.internal.os.HandlerCaller;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* An accessibility service runs in the background and receives callbacks by the system
|
||||
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
|
||||
* in the user interface, for example, the focus has changed, a button has been clicked,
|
||||
* etc.
|
||||
* <p>
|
||||
* An accessibility service extends this class and implements its abstract methods. Such
|
||||
* a service is declared as any other service in an AndroidManifest.xml but it must also
|
||||
* specify that it handles the "android.accessibilityservice.AccessibilityService"
|
||||
* {@link android.content.Intent}. Following is an example of such a declaration:
|
||||
* <p>
|
||||
* <code>
|
||||
* <service android:name=".MyAccessibilityService"><br>
|
||||
* <intent-filter><br>
|
||||
* <action android:name="android.accessibilityservice.AccessibilityService" /><br>
|
||||
* </intent-filter><br>
|
||||
* </service><br>
|
||||
* </code>
|
||||
* <p>
|
||||
* The lifecycle of an accessibility service is managed exclusively by the system. Starting
|
||||
* or stopping an accessibility service is triggered by an explicit user action through
|
||||
* enabling or disabling it in the device settings. After the system binds to a service it
|
||||
* calls {@link AccessibilityService#onServiceConnected()}. This method can be
|
||||
* overriden by clients that want to perform post binding setup. An accessibility service
|
||||
* is configured though setting an {@link AccessibilityServiceInfo} by calling
|
||||
* {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. You can call this
|
||||
* method any time to change the service configuration but it is good practice to do that
|
||||
* in the overriden {@link AccessibilityService#onServiceConnected()}.
|
||||
* <p>
|
||||
* An accessibility service can be registered for events in specific packages to provide a
|
||||
* specific type of feedback and is notified with a certain timeout after the last event
|
||||
* of interest has been fired.
|
||||
* <p>
|
||||
* <b>Notification strategy</b>
|
||||
* <p>
|
||||
* For each feedback type only one accessibility service is notified. Services are notified
|
||||
* in the order of registration. Hence, if two services are registered for the same
|
||||
* feedback type in the same package the first one wins. It is possible however, to
|
||||
* register a service as the default one for a given feedback type. In such a case this
|
||||
* service is invoked if no other service was interested in the event. In other words, default
|
||||
* services do not compete with other services and are notified last regardless of the
|
||||
* registration order. This enables "generic" accessibility services that work reasonably
|
||||
* well with most applications to coexist with "polished" ones that are targeted for
|
||||
* specific applications.
|
||||
* <p>
|
||||
* <b>Event types</b>
|
||||
* <p>
|
||||
* {@link AccessibilityEvent#TYPE_VIEW_CLICKED}
|
||||
* {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
|
||||
* {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
|
||||
* {@link AccessibilityEvent#TYPE_VIEW_SELECTED}
|
||||
* {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
|
||||
* {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
|
||||
* {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
|
||||
* <p>
|
||||
* <b>Feedback types</b>
|
||||
* <p>
|
||||
* {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
|
||||
* {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
|
||||
* {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
|
||||
* {@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
|
||||
* {@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
|
||||
*
|
||||
* @see AccessibilityEvent
|
||||
* @see AccessibilityServiceInfo
|
||||
* @see android.view.accessibility.AccessibilityManager
|
||||
*
|
||||
* Note: The event notification timeout is useful to avoid propagating events to the client
|
||||
* too frequently since this is accomplished via an expensive interprocess call.
|
||||
* One can think of the timeout as a criteria to determine when event generation has
|
||||
* settled down.
|
||||
*/
|
||||
public abstract class AccessibilityService extends Service {
|
||||
/**
|
||||
* The {@link Intent} that must be declared as handled by the service.
|
||||
*/
|
||||
public static final String SERVICE_INTERFACE =
|
||||
"android.accessibilityservice.AccessibilityService";
|
||||
|
||||
private static final String LOG_TAG = "AccessibilityService";
|
||||
|
||||
private AccessibilityServiceInfo mInfo;
|
||||
|
||||
IAccessibilityServiceConnection mConnection;
|
||||
|
||||
/**
|
||||
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
|
||||
*
|
||||
* @param event An event.
|
||||
*/
|
||||
public abstract void onAccessibilityEvent(AccessibilityEvent event);
|
||||
|
||||
/**
|
||||
* Callback for interrupting the accessibility feedback.
|
||||
*/
|
||||
public abstract void onInterrupt();
|
||||
|
||||
/**
|
||||
* This method is a part of the {@link AccessibilityService} lifecycle and is
|
||||
* called after the system has successfully bound to the service. If is
|
||||
* convenient to use this method for setting the {@link AccessibilityServiceInfo}.
|
||||
*
|
||||
* @see AccessibilityServiceInfo
|
||||
* @see #setServiceInfo(AccessibilityServiceInfo)
|
||||
*/
|
||||
protected void onServiceConnected() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AccessibilityServiceInfo} that describes this service.
|
||||
* <p>
|
||||
* Note: You can call this method any time but the info will be picked up after
|
||||
* the system has bound to this service and when this method is called thereafter.
|
||||
*
|
||||
* @param info The info.
|
||||
*/
|
||||
public final void setServiceInfo(AccessibilityServiceInfo info) {
|
||||
mInfo = info;
|
||||
sendServiceInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AccessibilityServiceInfo} for this service if the latter is
|
||||
* properly set and there is an {@link IAccessibilityServiceConnection} to the
|
||||
* AccessibilityManagerService.
|
||||
*/
|
||||
private void sendServiceInfo() {
|
||||
if (mInfo != null && mConnection != null) {
|
||||
try {
|
||||
mConnection.setServiceInfo(mInfo);
|
||||
} catch (RemoteException re) {
|
||||
Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IBinder onBind(Intent intent) {
|
||||
return new IEventListenerWrapper(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the internal {@link IEventListener} interface to convert
|
||||
* incoming calls to it back to calls on an {@link AccessibilityService}.
|
||||
*/
|
||||
class IEventListenerWrapper extends IEventListener.Stub
|
||||
implements HandlerCaller.Callback {
|
||||
|
||||
private static final int DO_SET_SET_CONNECTION = 10;
|
||||
private static final int DO_ON_INTERRUPT = 20;
|
||||
private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
|
||||
|
||||
private final HandlerCaller mCaller;
|
||||
|
||||
private AccessibilityService mTarget;
|
||||
|
||||
public IEventListenerWrapper(AccessibilityService context) {
|
||||
mTarget = context;
|
||||
mCaller = new HandlerCaller(context, this);
|
||||
}
|
||||
|
||||
public void setConnection(IAccessibilityServiceConnection connection) {
|
||||
Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection);
|
||||
mCaller.sendMessage(message);
|
||||
}
|
||||
|
||||
public void onInterrupt() {
|
||||
Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
|
||||
mCaller.sendMessage(message);
|
||||
}
|
||||
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
|
||||
mCaller.sendMessage(message);
|
||||
}
|
||||
|
||||
public void executeMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case DO_ON_ACCESSIBILITY_EVENT :
|
||||
AccessibilityEvent event = (AccessibilityEvent) message.obj;
|
||||
mTarget.onAccessibilityEvent(event);
|
||||
event.recycle();
|
||||
return;
|
||||
case DO_ON_INTERRUPT :
|
||||
mTarget.onInterrupt();
|
||||
return;
|
||||
case DO_SET_SET_CONNECTION :
|
||||
mConnection = ((IAccessibilityServiceConnection) message.obj);
|
||||
mTarget.onServiceConnected();
|
||||
return;
|
||||
default :
|
||||
Log.w(LOG_TAG, "Unknown message type " + message.what);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.accessibilityservice;
|
||||
|
||||
parcelable AccessibilityServiceInfo;
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.accessibilityservice;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* This class describes an {@link AccessibilityService}. The system
|
||||
* notifies an {@link AccessibilityService} for
|
||||
* {@link android.view.accessibility.AccessibilityEvent}s
|
||||
* according to the information encapsulated in this class.
|
||||
*
|
||||
* @see AccessibilityService
|
||||
* @see android.view.accessibility.AccessibilityEvent
|
||||
*/
|
||||
public class AccessibilityServiceInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* Denotes spoken feedback.
|
||||
*/
|
||||
public static final int FEEDBACK_SPOKEN = 0x0000001;
|
||||
|
||||
/**
|
||||
* Denotes haptic feedback.
|
||||
*/
|
||||
public static final int FEEDBACK_HAPTIC = 0x0000002;
|
||||
|
||||
/**
|
||||
* Denotes audible (not spoken) feedback.
|
||||
*/
|
||||
public static final int FEEDBACK_AUDIBLE = 0x0000004;
|
||||
|
||||
/**
|
||||
* Denotes visual feedback.
|
||||
*/
|
||||
public static final int FEEDBACK_VISUAL = 0x0000008;
|
||||
|
||||
/**
|
||||
* Denotes generic feedback.
|
||||
*/
|
||||
public static final int FEEDBACK_GENERIC = 0x0000010;
|
||||
|
||||
/**
|
||||
* If an {@link AccessibilityService} is the default for a given type.
|
||||
* Default service is invoked only if no package specific one exists. In case of
|
||||
* more than one package specific service only the earlier registered is notified.
|
||||
*/
|
||||
public static final int DEFAULT = 0x0000001;
|
||||
|
||||
/**
|
||||
* The event types an {@link AccessibilityService} is interested in.
|
||||
*
|
||||
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
|
||||
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
|
||||
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
|
||||
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
|
||||
* @see android.view.accessibility.AccessibilityEvent#TYPE_ACTIVITY_STARTED
|
||||
* @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
|
||||
* @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
|
||||
*/
|
||||
public int eventTypes;
|
||||
|
||||
/**
|
||||
* The package names an {@link AccessibilityService} is interested in. Setting
|
||||
* to null is equivalent to all packages.
|
||||
*/
|
||||
public String[] packageNames;
|
||||
|
||||
/**
|
||||
* The feedback type an {@link AccessibilityService} provides.
|
||||
*
|
||||
* @see #FEEDBACK_AUDIBLE
|
||||
* @see #FEEDBACK_GENERIC
|
||||
* @see #FEEDBACK_HAPTIC
|
||||
* @see #FEEDBACK_SPOKEN
|
||||
* @see #FEEDBACK_VISUAL
|
||||
*/
|
||||
public int feedbackType;
|
||||
|
||||
/**
|
||||
* The timeout after the most recent event of a given type before an
|
||||
* {@link AccessibilityService} is notified.
|
||||
* <p>
|
||||
* Note: The event notification timeout is useful to avoid propagating events to the client
|
||||
* too frequently since this is accomplished via an expensive interprocess call.
|
||||
* One can think of the timeout as a criteria to determine when event generation has
|
||||
* settled down
|
||||
*/
|
||||
public long notificationTimeout;
|
||||
|
||||
/**
|
||||
* This field represents a set of flags used for configuring an
|
||||
* {@link AccessibilityService}.
|
||||
*
|
||||
* @see #DEFAULT
|
||||
*/
|
||||
public int flags;
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeInt(eventTypes);
|
||||
parcel.writeStringArray(packageNames);
|
||||
parcel.writeInt(feedbackType);
|
||||
parcel.writeLong(notificationTimeout);
|
||||
parcel.writeInt(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Parcelable.Creator
|
||||
*/
|
||||
public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
|
||||
new Parcelable.Creator<AccessibilityServiceInfo>() {
|
||||
public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
|
||||
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
||||
info.eventTypes = parcel.readInt();
|
||||
info.packageNames = parcel.readStringArray();
|
||||
info.feedbackType = parcel.readInt();
|
||||
info.notificationTimeout = parcel.readLong();
|
||||
info.flags = parcel.readInt();
|
||||
return info;
|
||||
}
|
||||
|
||||
public AccessibilityServiceInfo[] newArray(int size) {
|
||||
return new AccessibilityServiceInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.accessibilityservice;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
|
||||
/**
|
||||
* Interface AccessibilityManagerService#Service implements, and passes to an
|
||||
* AccessibilityService so it can dynamically configure how the system handles it.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IAccessibilityServiceConnection {
|
||||
|
||||
void setServiceInfo(in AccessibilityServiceInfo info);
|
||||
}
|
||||
34
core/java/android/accessibilityservice/IEventListener.aidl
Normal file
34
core/java/android/accessibilityservice/IEventListener.aidl
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
** Copyright 2009, 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 android.accessibilityservice;
|
||||
|
||||
import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* Top-level interface to accessibility service component (implemented in Service).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IEventListener {
|
||||
|
||||
void setConnection(in IAccessibilityServiceConnection connection);
|
||||
|
||||
void onAccessibilityEvent(in AccessibilityEvent event);
|
||||
|
||||
void onInterrupt();
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import com.android.internal.policy.PolicyManager;
|
||||
|
||||
import android.content.ComponentCallbacks;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
@@ -32,11 +34,12 @@ import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.TextKeyListener;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Config;
|
||||
@@ -58,10 +61,10 @@ import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.android.internal.policy.PolicyManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -2013,7 +2016,24 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
return onTrackballEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
event.setClassName(getClass().getName());
|
||||
event.setPackageName(getPackageName());
|
||||
|
||||
LayoutParams params = getWindow().getAttributes();
|
||||
boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) &&
|
||||
(params.height == LayoutParams.FILL_PARENT);
|
||||
event.setFullScreen(isFullScreen);
|
||||
|
||||
CharSequence title = getTitle();
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
event.getText().add(title);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of
|
||||
* {@link android.view.Window.Callback#onCreatePanelView}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import com.google.android.collect.Maps;
|
||||
import com.android.internal.policy.PolicyManager;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.IBluetoothDevice;
|
||||
@@ -37,9 +40,9 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.IPackageDataObserver;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
import android.content.pm.IPackageStatsObserver;
|
||||
import android.content.pm.IPackageInstallObserver;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.IPackageStatsObserver;
|
||||
import android.content.pm.InstrumentationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -68,15 +71,15 @@ import android.net.wifi.IWifiManager;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.FileUtils;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.Vibrator;
|
||||
import android.os.FileUtils.FileStatus;
|
||||
@@ -87,10 +90,9 @@ import android.util.Log;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.WindowManagerImpl;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.android.internal.policy.PolicyManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -100,16 +102,14 @@ import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
class ReceiverRestrictedContext extends ContextWrapper {
|
||||
ReceiverRestrictedContext(Context base) {
|
||||
super(base);
|
||||
@@ -172,6 +172,7 @@ class ApplicationContext extends Context {
|
||||
private Resources.Theme mTheme = null;
|
||||
private PackageManager mPackageManager;
|
||||
private NotificationManager mNotificationManager = null;
|
||||
private AccessibilityManager mAccessibilityManager = null;
|
||||
private ActivityManager mActivityManager = null;
|
||||
private Context mReceiverRestrictedContext = null;
|
||||
private SearchManager mSearchManager = null;
|
||||
@@ -904,6 +905,8 @@ class ApplicationContext extends Context {
|
||||
return getNotificationManager();
|
||||
} else if (KEYGUARD_SERVICE.equals(name)) {
|
||||
return new KeyguardManager();
|
||||
} else if (ACCESSIBILITY_SERVICE.equals(name)) {
|
||||
return AccessibilityManager.getInstance(this);
|
||||
} else if (LOCATION_SERVICE.equals(name)) {
|
||||
return getLocationManager();
|
||||
} else if (SEARCH_SERVICE.equals(name)) {
|
||||
|
||||
@@ -16,32 +16,34 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import com.android.internal.policy.PolicyManager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Bundle;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
|
||||
import com.android.internal.policy.PolicyManager;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@@ -81,6 +83,7 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
* {@hide}
|
||||
*/
|
||||
protected boolean mCancelable = true;
|
||||
|
||||
private Message mCancelMessage;
|
||||
private Message mDismissMessage;
|
||||
|
||||
@@ -209,7 +212,9 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
if (mShowing) {
|
||||
if (Config.LOGV) Log.v(LOG_TAG,
|
||||
"[Dialog] start: already showing, ignore");
|
||||
if (mDecor != null) mDecor.setVisibility(View.VISIBLE);
|
||||
if (mDecor != null) {
|
||||
mDecor.setVisibility(View.VISIBLE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -236,7 +241,9 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
* Hide the dialog, but do not dismiss it.
|
||||
*/
|
||||
public void hide() {
|
||||
if (mDecor != null) mDecor.setVisibility(View.GONE);
|
||||
if (mDecor != null) {
|
||||
mDecor.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,6 +273,7 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
}
|
||||
|
||||
mWindowManager.removeView(mDecor);
|
||||
|
||||
mDecor = null;
|
||||
mWindow.closeAllPanels();
|
||||
onStop();
|
||||
@@ -280,7 +288,7 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
Message.obtain(mDismissMessage).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// internal method to make sure mcreated is set properly without requiring
|
||||
// users to call through to super in onCreate
|
||||
void dispatchOnCreate(Bundle savedInstanceState) {
|
||||
@@ -608,6 +616,18 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
return onTrackballEvent(ev);
|
||||
}
|
||||
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
event.setClassName(getClass().getName());
|
||||
event.setPackageName(mContext.getPackageName());
|
||||
|
||||
LayoutParams params = getWindow().getAttributes();
|
||||
boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) &&
|
||||
(params.height == LayoutParams.FILL_PARENT);
|
||||
event.setFullScreen(isFullScreen);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Activity#onCreatePanelView(int)
|
||||
*/
|
||||
|
||||
@@ -1133,6 +1133,15 @@ public abstract class Context {
|
||||
* @see android.app.NotificationManager
|
||||
*/
|
||||
public static final String NOTIFICATION_SERVICE = "notification";
|
||||
/**
|
||||
* Use with {@link #getSystemService} to retrieve a
|
||||
* {@link android.view.accessibility.AccessibilityManager} for giving the user
|
||||
* feedback for UI events through the registered event listeners.
|
||||
*
|
||||
* @see #getSystemService
|
||||
* @see android.view.accessibility.AccessibilityManager
|
||||
*/
|
||||
public static final String ACCESSIBILITY_SERVICE = "accessibility";
|
||||
/**
|
||||
* Use with {@link #getSystemService} to retrieve a
|
||||
* {@link android.app.NotificationManager} for controlling keyguard.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.preference;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -23,6 +24,8 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -42,6 +45,9 @@ public class CheckBoxPreference extends Preference {
|
||||
private CharSequence mSummaryOff;
|
||||
|
||||
private boolean mChecked;
|
||||
private boolean mSendAccessibilityEventViewClickedType;
|
||||
|
||||
private AccessibilityManager mAccessibilityManager;
|
||||
|
||||
private boolean mDisableDependentsState;
|
||||
|
||||
@@ -55,6 +61,9 @@ public class CheckBoxPreference extends Preference {
|
||||
mDisableDependentsState = a.getBoolean(
|
||||
com.android.internal.R.styleable.CheckBoxPreference_disableDependentsState, false);
|
||||
a.recycle();
|
||||
|
||||
mAccessibilityManager =
|
||||
(AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
|
||||
}
|
||||
|
||||
public CheckBoxPreference(Context context, AttributeSet attrs) {
|
||||
@@ -64,14 +73,26 @@ public class CheckBoxPreference extends Preference {
|
||||
public CheckBoxPreference(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
|
||||
|
||||
View checkboxView = view.findViewById(com.android.internal.R.id.checkbox);
|
||||
if (checkboxView != null && checkboxView instanceof Checkable) {
|
||||
((Checkable) checkboxView).setChecked(mChecked);
|
||||
|
||||
// send an event to announce the value change of the CheckBox and is done here
|
||||
// because clicking a preference does not immediately change the checked state
|
||||
// for example when enabling the WiFi
|
||||
if (mSendAccessibilityEventViewClickedType &&
|
||||
mAccessibilityManager.isEnabled() &&
|
||||
checkboxView.isEnabled()) {
|
||||
mSendAccessibilityEventViewClickedType = false;
|
||||
|
||||
int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED;
|
||||
checkboxView.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
|
||||
}
|
||||
}
|
||||
|
||||
// Sync the summary view
|
||||
@@ -85,7 +106,7 @@ public class CheckBoxPreference extends Preference {
|
||||
summaryView.setText(mSummaryOff);
|
||||
useDefaultSummary = false;
|
||||
}
|
||||
|
||||
|
||||
if (useDefaultSummary) {
|
||||
final CharSequence summary = getSummary();
|
||||
if (summary != null) {
|
||||
@@ -111,6 +132,10 @@ public class CheckBoxPreference extends Preference {
|
||||
|
||||
boolean newValue = !isChecked();
|
||||
|
||||
// in onBindView() an AccessibilityEventViewClickedType is sent to announce the change
|
||||
// not sending
|
||||
mSendAccessibilityEventViewClickedType = true;
|
||||
|
||||
if (!callChangeListener(newValue)) {
|
||||
return;
|
||||
}
|
||||
@@ -124,10 +149,11 @@ public class CheckBoxPreference extends Preference {
|
||||
* @param checked The checked state.
|
||||
*/
|
||||
public void setChecked(boolean checked) {
|
||||
|
||||
mChecked = checked;
|
||||
|
||||
persistBoolean(checked);
|
||||
|
||||
|
||||
notifyDependencyChange(shouldDisableDependents());
|
||||
|
||||
notifyChanged();
|
||||
|
||||
@@ -1904,6 +1904,17 @@ public final class Settings {
|
||||
*/
|
||||
public static final String USE_GOOGLE_MAIL = "use_google_mail";
|
||||
|
||||
/**
|
||||
* If accessibility is enabled.
|
||||
*/
|
||||
public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
|
||||
|
||||
/**
|
||||
* List of the enabled accessibility providers.
|
||||
*/
|
||||
public static final String ENABLED_ACCESSIBILITY_SERVICES =
|
||||
"enabled_accessibility_services";
|
||||
|
||||
/**
|
||||
* Whether to notify the user of open networks.
|
||||
* <p>
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.view.menu.MenuBuilder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -25,12 +28,12 @@ import android.graphics.LinearGradient;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
@@ -42,30 +45,30 @@ import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Config;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Poolable;
|
||||
import android.util.Pool;
|
||||
import android.util.Pools;
|
||||
import android.util.Poolable;
|
||||
import android.util.PoolableManager;
|
||||
import android.util.Config;
|
||||
import android.util.Pools;
|
||||
import android.util.SparseArray;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityEventSource;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.ScrollBarDrawable;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.view.menu.MenuBuilder;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.WeakHashMap;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -553,7 +556,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
*
|
||||
* @see android.view.ViewGroup
|
||||
*/
|
||||
public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
|
||||
private static final boolean DBG = false;
|
||||
|
||||
/**
|
||||
@@ -850,6 +853,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
*/
|
||||
public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000;
|
||||
|
||||
/**
|
||||
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
|
||||
* should add all focusable Views regardless if they are focusable in touch mode.
|
||||
*/
|
||||
public static final int FOCUSABLES_ALL = 0x00000000;
|
||||
|
||||
/**
|
||||
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
|
||||
* should add only Views focusable in touch mode.
|
||||
*/
|
||||
public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
|
||||
|
||||
/**
|
||||
* Use with {@link #focusSearch}. Move focus to the previous selectable
|
||||
* item.
|
||||
@@ -1550,6 +1565,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
@ViewDebug.ExportedProperty
|
||||
protected int mPaddingBottom;
|
||||
|
||||
/**
|
||||
* Briefly describes the view and is primarily used for accessibility support.
|
||||
*/
|
||||
private CharSequence mContentDescription;
|
||||
|
||||
/**
|
||||
* Cache the paddingRight set by the user to append to the scrollbar's size.
|
||||
*/
|
||||
@@ -1858,6 +1878,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK;
|
||||
}
|
||||
break;
|
||||
case com.android.internal.R.styleable.View_contentDescription:
|
||||
mContentDescription = a.getString(attr);
|
||||
break;
|
||||
case com.android.internal.R.styleable.View_soundEffectsEnabled:
|
||||
if (!a.getBoolean(attr, true)) {
|
||||
viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
|
||||
@@ -2255,6 +2278,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
* otherwise is returned.
|
||||
*/
|
||||
public boolean performClick() {
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
||||
|
||||
if (mOnClickListener != null) {
|
||||
playSoundEffect(SoundEffectConstants.CLICK);
|
||||
mOnClickListener.onClick(this);
|
||||
@@ -2272,6 +2297,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
* otherwise is returned.
|
||||
*/
|
||||
public boolean performLongClick() {
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
|
||||
|
||||
boolean handled = false;
|
||||
if (mOnLongClickListener != null) {
|
||||
handled = mOnLongClickListener.onLongClick(View.this);
|
||||
@@ -2492,6 +2519,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
* from (in addition to direction). Will be <code>null</code> otherwise.
|
||||
*/
|
||||
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
|
||||
if (gainFocus) {
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
|
||||
}
|
||||
|
||||
InputMethodManager imm = InputMethodManager.peekInstance();
|
||||
if (!gainFocus) {
|
||||
if (isPressed()) {
|
||||
@@ -2513,6 +2544,79 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void sendAccessibilityEvent(int eventType) {
|
||||
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
|
||||
sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
|
||||
event.setClassName(getClass().getName());
|
||||
event.setPackageName(getContext().getPackageName());
|
||||
event.setEnabled(isEnabled());
|
||||
event.setContentDescription(mContentDescription);
|
||||
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) {
|
||||
ArrayList<View> focusablesTempList = mAttachInfo.mFocusablesTempList;
|
||||
getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL);
|
||||
event.setItemCount(focusablesTempList.size());
|
||||
event.setCurrentItemIndex(focusablesTempList.indexOf(this));
|
||||
focusablesTempList.clear();
|
||||
}
|
||||
|
||||
dispatchPopulateAccessibilityEvent(event);
|
||||
|
||||
AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an {@link AccessibilityEvent} to the {@link View} children
|
||||
* to be populated.
|
||||
*
|
||||
* @param event The event.
|
||||
*
|
||||
* @return True if the event population was completed.
|
||||
*/
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link View} description. It briefly describes the view and is
|
||||
* primarily used for accessibility support. Set this property to enable
|
||||
* better accessibility support for your application. This is especially
|
||||
* true for views that do not have textual representation (For example,
|
||||
* ImageButton).
|
||||
*
|
||||
* @return The content descriptiopn.
|
||||
*
|
||||
* @attr ref android.R.styleable#View_contentDescription
|
||||
*/
|
||||
public CharSequence getContentDescription() {
|
||||
return mContentDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link View} description. It briefly describes the view and is
|
||||
* primarily used for accessibility support. Set this property to enable
|
||||
* better accessibility support for your application. This is especially
|
||||
* true for views that do not have textual representation (For example,
|
||||
* ImageButton).
|
||||
*
|
||||
* @param contentDescription The content description.
|
||||
*
|
||||
* @attr ref android.R.styleable#View_contentDescription
|
||||
*/
|
||||
public void setContentDescription(CharSequence contentDescription) {
|
||||
mContentDescription = contentDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked whenever this view loses focus, either by losing window focus or by losing
|
||||
* focus within its window. This method can be used to clear any state tied to the
|
||||
@@ -3222,11 +3326,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
* @param direction The direction of the focus
|
||||
*/
|
||||
public void addFocusables(ArrayList<View> views, int direction) {
|
||||
if (!isFocusable()) return;
|
||||
addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
|
||||
}
|
||||
|
||||
if (isInTouchMode() && !isFocusableInTouchMode()) return;
|
||||
/**
|
||||
* Adds any focusable views that are descendants of this view (possibly
|
||||
* including this view if it is focusable itself) to views. This method
|
||||
* adds all focusable views regardless if we are in touch mode or
|
||||
* only views focusable in touch mode if we are in touch mode depending on
|
||||
* the focusable mode paramater.
|
||||
*
|
||||
* @param views Focusable views found so far or null if all we are interested is
|
||||
* the number of focusables.
|
||||
* @param direction The direction of the focus.
|
||||
* @param focusableMode The type of focusables to be added.
|
||||
*
|
||||
* @see #FOCUSABLES_ALL
|
||||
* @see #FOCUSABLES_TOUCH_MODE
|
||||
*/
|
||||
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
|
||||
if (!isFocusable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
views.add(this);
|
||||
if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
|
||||
isInTouchMode() && !isFocusableInTouchMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (views != null) {
|
||||
views.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8378,7 +8508,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
|
||||
* calling up the hierarchy.
|
||||
*/
|
||||
final Rect mTmpInvalRect = new Rect();
|
||||
|
||||
|
||||
/**
|
||||
* Temporary list for use in collecting focusable descendents of a view.
|
||||
*/
|
||||
final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
|
||||
|
||||
/**
|
||||
* Creates a new set of attachment information with the specified
|
||||
* events handler and thread.
|
||||
|
||||
@@ -24,15 +24,16 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Config;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Config;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.LayoutAnimationController;
|
||||
@@ -601,6 +602,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
*/
|
||||
@Override
|
||||
public void addFocusables(ArrayList<View> views, int direction) {
|
||||
addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
|
||||
final int focusableCount = views.size();
|
||||
|
||||
final int descendantFocusability = getDescendantFocusability();
|
||||
@@ -612,7 +621,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View child = children[i];
|
||||
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
|
||||
child.addFocusables(views, direction);
|
||||
child.addFocusables(views, direction, focusableMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -625,7 +634,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
|
||||
// No focusable descendants
|
||||
(focusableCount == views.size())) {
|
||||
super.addFocusables(views, direction);
|
||||
super.addFocusables(views, direction, focusableMode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1020,6 +1029,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
boolean populated = false;
|
||||
for (int i = 0, count = getChildCount(); i < count; i++) {
|
||||
populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
return populated;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,8 @@ import android.util.Log;
|
||||
import android.util.EventLog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Scroller;
|
||||
@@ -640,6 +642,7 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
host.dispatchAttachedToWindow(attachInfo, 0);
|
||||
getRunQueue().executeActions(attachInfo.mHandler);
|
||||
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
|
||||
|
||||
} else {
|
||||
desiredWindowWidth = mWinFrame.width();
|
||||
desiredWindowHeight = mWinFrame.height();
|
||||
@@ -1723,7 +1726,7 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
}
|
||||
mView.dispatchWindowFocusChanged(hasWindowFocus);
|
||||
}
|
||||
|
||||
|
||||
// Note: must be done after the focus change callbacks,
|
||||
// so all of the view state is set up correctly.
|
||||
if (hasWindowFocus) {
|
||||
@@ -1741,6 +1744,10 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
|
||||
mHasHadWindowFocus = true;
|
||||
}
|
||||
|
||||
if (hasWindowFocus && mView != null) {
|
||||
sendAccessibilityEvents();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DIE:
|
||||
@@ -2526,6 +2533,21 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* The window is getting focus so if there is anything focused/selected
|
||||
* send an {@link AccessibilityEvent} to announce that.
|
||||
*/
|
||||
private void sendAccessibilityEvents() {
|
||||
if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) {
|
||||
return;
|
||||
}
|
||||
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
||||
View focusedView = mView.findFocus();
|
||||
if (focusedView != null && focusedView != mView) {
|
||||
focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean showContextMenuForChild(View originalView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* Abstract base class for a top-level window look and behavior policy. An
|
||||
@@ -153,7 +153,16 @@ public abstract class Window {
|
||||
* @return boolean Return true if this event was consumed.
|
||||
*/
|
||||
public boolean dispatchTrackballEvent(MotionEvent event);
|
||||
|
||||
|
||||
/**
|
||||
* Called to process population of {@link AccessibilityEvent}s.
|
||||
*
|
||||
* @param event The event.
|
||||
*
|
||||
* @return boolean Return true if event population was completed.
|
||||
*/
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
|
||||
|
||||
/**
|
||||
* Instantiate the view to display in the panel for 'featureId'.
|
||||
* You can return null, in which case the default content (typically
|
||||
|
||||
@@ -173,7 +173,6 @@ public class WindowManagerImpl implements WindowManager {
|
||||
mRoots[index] = root;
|
||||
mParams[index] = wparams;
|
||||
}
|
||||
|
||||
// do this last because it fires off messages to start doing things
|
||||
root.setView(view, wparams, panelParentView);
|
||||
}
|
||||
|
||||
19
core/java/android/view/accessibility/AccessibilityEvent.aidl
Normal file
19
core/java/android/view/accessibility/AccessibilityEvent.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2009, 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 android.view.accessibility;
|
||||
|
||||
parcelable AccessibilityEvent;
|
||||
734
core/java/android/view/accessibility/AccessibilityEvent.java
Normal file
734
core/java/android/view/accessibility/AccessibilityEvent.java
Normal file
@@ -0,0 +1,734 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.view.accessibility;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class represents accessibility events that are sent by the system when
|
||||
* something notable happens in the user interface. For example, when a
|
||||
* {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc.
|
||||
* <p>
|
||||
* This class represents various semantically different accessibility event
|
||||
* types. Each event type has associated a set of related properties. In other
|
||||
* words, each event type is characterized via a subset of the properties exposed
|
||||
* by this class. For each event type there is a corresponding constant defined
|
||||
* in this class. Since some event types are semantically close there are mask
|
||||
* constants that group them together. Follows a specification of the event
|
||||
* types and their associated properties:
|
||||
* <p>
|
||||
* <b>VIEW TYPES</b> <br>
|
||||
* <p>
|
||||
* <b>View clicked</b> - represents the event of clicking on a {@link android.view.View}
|
||||
* like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
|
||||
* Type:{@link #TYPE_VIEW_CLICKED} <br>
|
||||
* Properties:
|
||||
* {@link #getClassName()},
|
||||
* {@link #getPackageName()},
|
||||
* {@link #getEventTime()},
|
||||
* {@link #getText()},
|
||||
* {@link #isChecked()},
|
||||
* {@link #isEnabled()},
|
||||
* {@link #isPassword()},
|
||||
* {@link #getItemCount()},
|
||||
* {@link #getCurrentItemIndex()}
|
||||
* <p>
|
||||
* <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View}
|
||||
* like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
|
||||
* Type:{@link #TYPE_VIEW_LONG_CLICKED} <br>
|
||||
* Properties:
|
||||
* {@link #getClassName()},
|
||||
* {@link #getPackageName()},
|
||||
* {@link #getEventTime()},
|
||||
* {@link #getText()},
|
||||
* {@link #isChecked()},
|
||||
* {@link #isEnabled()},
|
||||
* {@link #isPassword()},
|
||||
* {@link #getItemCount()},
|
||||
* {@link #getCurrentItemIndex()}
|
||||
* <p>
|
||||
* <b>View selected</b> - represents the event of selecting an item usually in
|
||||
* the context of an {@link android.widget.AdapterView}. <br>
|
||||
* Type: {@link #TYPE_VIEW_SELECTED} <br>
|
||||
* Properties:
|
||||
* {@link #getClassName()},
|
||||
* {@link #getPackageName()},
|
||||
* {@link #getEventTime()},
|
||||
* {@link #getText()},
|
||||
* {@link #isChecked()},
|
||||
* {@link #isEnabled()},
|
||||
* {@link #isPassword()},
|
||||
* {@link #getItemCount()},
|
||||
* {@link #getCurrentItemIndex()}
|
||||
* <p>
|
||||
* <b>View focused</b> - represents the event of focusing a
|
||||
* {@link android.view.View}. <br>
|
||||
* Type: {@link #TYPE_VIEW_FOCUSED} <br>
|
||||
* Properties:
|
||||
* {@link #getClassName()},
|
||||
* {@link #getPackageName()},
|
||||
* {@link #getEventTime()},
|
||||
* {@link #getText()},
|
||||
* {@link #isChecked()},
|
||||
* {@link #isEnabled()},
|
||||
* {@link #isPassword()},
|
||||
* {@link #getItemCount()},
|
||||
* {@link #getCurrentItemIndex()}
|
||||
* <p>
|
||||
* <b>View text changed</b> - represents the event of changing the text of an
|
||||
* {@link android.widget.EditText}. <br>
|
||||
* Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br>
|
||||
* Properties:
|
||||
* {@link #getClassName()},
|
||||
* {@link #getPackageName()},
|
||||
* {@link #getEventTime()},
|
||||
* {@link #getText()},
|
||||
* {@link #isChecked()},
|
||||
* {@link #isEnabled()},
|
||||
* {@link #isPassword()},
|
||||
* {@link #getItemCount()},
|
||||
* {@link #getCurrentItemIndex()},
|
||||
* {@link #getFromIndex()},
|
||||
* {@link #getAddedCount()},
|
||||
* {@link #getRemovedCount()},
|
||||
* {@link #getBeforeText()}
|
||||
* <p>
|
||||
* <b>TRANSITION TYPES</b> <br>
|
||||
* <p>
|
||||
* <b>Window state changed</b> - represents the event of opening/closing a
|
||||
* {@link android.widget.PopupWindow}, {@link android.view.Menu},
|
||||
* {@link android.app.Dialog}, etc. <br>
|
||||
* Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br>
|
||||
* Properties:
|
||||
* {@link #getClassName()},
|
||||
* {@link #getPackageName()},
|
||||
* {@link #getEventTime()},
|
||||
* {@link #getText()}
|
||||
* <p>
|
||||
* <b>NOTIFICATION TYPES</b> <br>
|
||||
* <p>
|
||||
* <b>Notification state changed</b> - represents the event showing/hiding
|
||||
* {@link android.app.Notification}.
|
||||
* Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br>
|
||||
* Properties:
|
||||
* {@link #getClassName()},
|
||||
* {@link #getPackageName()},
|
||||
* {@link #getEventTime()},
|
||||
* {@link #getText()}
|
||||
* {@link #getParcelableData()}
|
||||
* <p>
|
||||
* <b>Security note</b>
|
||||
* <p>
|
||||
* Since an event contains the text of its source privacy can be compromised by leaking of
|
||||
* sensitive information such as passwords. To address this issue any event fired in response
|
||||
* to manipulation of a PASSWORD field does NOT CONTAIN the text of the password.
|
||||
*
|
||||
* @see android.view.accessibility.AccessibilityManager
|
||||
* @see android.accessibilityservice.AccessibilityService
|
||||
*/
|
||||
public final class AccessibilityEvent implements Parcelable {
|
||||
|
||||
/**
|
||||
* Invalid selection/focus position.
|
||||
*
|
||||
* @see #getCurrentItemIndex()
|
||||
*/
|
||||
public static final int INVALID_POSITION = -1;
|
||||
|
||||
/**
|
||||
* Maximum length of the text fields.
|
||||
*
|
||||
* @see #getBeforeText()
|
||||
* @see #getText()
|
||||
*/
|
||||
public static final int MAX_TEXT_LENGTH = 500;
|
||||
|
||||
/**
|
||||
* Represents the event of clicking on a {@link android.view.View} like
|
||||
* {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
|
||||
*/
|
||||
public static final int TYPE_VIEW_CLICKED = 0x00000001;
|
||||
|
||||
/**
|
||||
* Represents the event of long clicking on a {@link android.view.View} like
|
||||
* {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
|
||||
*/
|
||||
public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;
|
||||
|
||||
/**
|
||||
* Represents the event of selecting an item usually in the context of an
|
||||
* {@link android.widget.AdapterView}.
|
||||
*/
|
||||
public static final int TYPE_VIEW_SELECTED = 0x00000004;
|
||||
|
||||
/**
|
||||
* Represents the event of focusing a {@link android.view.View}.
|
||||
*/
|
||||
public static final int TYPE_VIEW_FOCUSED = 0x00000008;
|
||||
|
||||
/**
|
||||
* Represents the event of changing the text of an {@link android.widget.EditText}.
|
||||
*/
|
||||
public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
|
||||
|
||||
/**
|
||||
* Represents the event of opening/closing a {@link android.widget.PopupWindow},
|
||||
* {@link android.view.Menu}, {@link android.app.Dialog}, etc.
|
||||
*/
|
||||
public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
|
||||
|
||||
/**
|
||||
* Represents the event showing/hiding a {@link android.app.Notification}.
|
||||
*/
|
||||
public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
|
||||
|
||||
/**
|
||||
* Mask for {@link AccessibilityEvent} all types.
|
||||
*
|
||||
* @see #TYPE_VIEW_CLICKED
|
||||
* @see #TYPE_VIEW_LONG_CLICKED
|
||||
* @see #TYPE_VIEW_SELECTED
|
||||
* @see #TYPE_VIEW_FOCUSED
|
||||
* @see #TYPE_VIEW_TEXT_CHANGED
|
||||
* @see #TYPE_WINDOW_STATE_CHANGED
|
||||
* @see #TYPE_NOTIFICATION_STATE_CHANGED
|
||||
*/
|
||||
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
|
||||
|
||||
private static final int MAX_POOL_SIZE = 2;
|
||||
private static final Object mPoolLock = new Object();
|
||||
private static AccessibilityEvent sPool;
|
||||
private static int sPoolSize;
|
||||
|
||||
private static final int CHECKED = 0x00000001;
|
||||
private static final int ENABLED = 0x00000002;
|
||||
private static final int PASSWORD = 0x00000004;
|
||||
private static final int FULL_SCREEN = 0x00000080;
|
||||
|
||||
private AccessibilityEvent mNext;
|
||||
|
||||
private int mEventType;
|
||||
private int mBooleanProperties;
|
||||
private int mCurrentItemIndex;
|
||||
private int mItemCount;
|
||||
private int mFromIndex;
|
||||
private int mAddedCount;
|
||||
private int mRemovedCount;
|
||||
|
||||
private long mEventTime;
|
||||
|
||||
private CharSequence mClassName;
|
||||
private CharSequence mPackageName;
|
||||
private CharSequence mContentDescription;
|
||||
private CharSequence mBeforeText;
|
||||
|
||||
private Parcelable mParcelableData;
|
||||
|
||||
private final List<CharSequence> mText = new ArrayList<CharSequence>();
|
||||
|
||||
private boolean mIsInPool;
|
||||
|
||||
/*
|
||||
* Hide constructor from clients.
|
||||
*/
|
||||
private AccessibilityEvent() {
|
||||
mCurrentItemIndex = INVALID_POSITION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the source is checked.
|
||||
*
|
||||
* @return True if the view is checked, false otherwise.
|
||||
*/
|
||||
public boolean isChecked() {
|
||||
return getBooleanProperty(CHECKED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the source is checked.
|
||||
*
|
||||
* @param isChecked True if the view is checked, false otherwise.
|
||||
*/
|
||||
public void setChecked(boolean isChecked) {
|
||||
setBooleanProperty(CHECKED, isChecked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the source is enabled.
|
||||
*
|
||||
* @return True if the view is enabled, false otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return getBooleanProperty(ENABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the source is enabled.
|
||||
*
|
||||
* @param isEnabled True if the view is enabled, false otherwise.
|
||||
*/
|
||||
public void setEnabled(boolean isEnabled) {
|
||||
setBooleanProperty(ENABLED, isEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the source is a password field.
|
||||
*
|
||||
* @return True if the view is a password field, false otherwise.
|
||||
*/
|
||||
public boolean isPassword() {
|
||||
return getBooleanProperty(PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the source is a password field.
|
||||
*
|
||||
* @param isPassword True if the view is a password field, false otherwise.
|
||||
*/
|
||||
public void setPassword(boolean isPassword) {
|
||||
setBooleanProperty(PASSWORD, isPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the source is taking the entire screen.
|
||||
*
|
||||
* @param isFullScreen True if the source is full screen, false otherwise.
|
||||
*/
|
||||
public void setFullScreen(boolean isFullScreen) {
|
||||
setBooleanProperty(FULL_SCREEN, isFullScreen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the source is taking the entire screen.
|
||||
*
|
||||
* @return True if the source is full screen, false otherwise.
|
||||
*/
|
||||
public boolean isFullScreen() {
|
||||
return getBooleanProperty(FULL_SCREEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event type.
|
||||
*
|
||||
* @return The event type.
|
||||
*/
|
||||
public int getEventType() {
|
||||
return mEventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event type.
|
||||
*
|
||||
* @param eventType The event type.
|
||||
*/
|
||||
public void setEventType(int eventType) {
|
||||
mEventType = eventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of items that can be visited.
|
||||
*
|
||||
* @return The number of items.
|
||||
*/
|
||||
public int getItemCount() {
|
||||
return mItemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of items that can be visited.
|
||||
*
|
||||
* @param itemCount The number of items.
|
||||
*/
|
||||
public void setItemCount(int itemCount) {
|
||||
mItemCount = itemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the source in the list of items the can be visited.
|
||||
*
|
||||
* @return The current item index.
|
||||
*/
|
||||
public int getCurrentItemIndex() {
|
||||
return mCurrentItemIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the source in the list of items that can be visited.
|
||||
*
|
||||
* @param currentItemIndex The current item index.
|
||||
*/
|
||||
public void setCurrentItemIndex(int currentItemIndex) {
|
||||
mCurrentItemIndex = currentItemIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the first character of the changed sequence.
|
||||
*
|
||||
* @return The index of the first character.
|
||||
*/
|
||||
public int getFromIndex() {
|
||||
return mFromIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the first character of the changed sequence.
|
||||
*
|
||||
* @param fromIndex The index of the first character.
|
||||
*/
|
||||
public void setFromIndex(int fromIndex) {
|
||||
mFromIndex = fromIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of added characters.
|
||||
*
|
||||
* @return The number of added characters.
|
||||
*/
|
||||
public int getAddedCount() {
|
||||
return mAddedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of added characters.
|
||||
*
|
||||
* @param addedCount The number of added characters.
|
||||
*/
|
||||
public void setAddedCount(int addedCount) {
|
||||
mAddedCount = addedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of removed characters.
|
||||
*
|
||||
* @return The number of removed characters.
|
||||
*/
|
||||
public int getRemovedCount() {
|
||||
return mRemovedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of removed characters.
|
||||
*
|
||||
* @param removedCount The number of removed characters.
|
||||
*/
|
||||
public void setRemovedCount(int removedCount) {
|
||||
mRemovedCount = removedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time in which this event was sent.
|
||||
*
|
||||
* @return The event time.
|
||||
*/
|
||||
public long getEventTime() {
|
||||
return mEventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time in which this event was sent.
|
||||
*
|
||||
* @param eventTime The event time.
|
||||
*/
|
||||
public void setEventTime(long eventTime) {
|
||||
mEventTime = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class name of the source.
|
||||
*
|
||||
* @return The class name.
|
||||
*/
|
||||
public CharSequence getClassName() {
|
||||
return mClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class name of the source.
|
||||
*
|
||||
* @param className The lass name.
|
||||
*/
|
||||
public void setClassName(CharSequence className) {
|
||||
mClassName = className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the package name of the source.
|
||||
*
|
||||
* @return The package name.
|
||||
*/
|
||||
public CharSequence getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the package name of the source.
|
||||
*
|
||||
* @param packageName The package name.
|
||||
*/
|
||||
public void setPackageName(CharSequence packageName) {
|
||||
mPackageName = packageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text of the event. The index in the list represents the priority
|
||||
* of the text. Specifically, the lower the index the higher the priority.
|
||||
*
|
||||
* @return The text.
|
||||
*/
|
||||
public List<CharSequence> getText() {
|
||||
return mText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text before a change.
|
||||
*
|
||||
* @return The text before the change.
|
||||
*/
|
||||
public CharSequence getBeforeText() {
|
||||
return mBeforeText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text before a change.
|
||||
*
|
||||
* @param beforeText The text before the change.
|
||||
*/
|
||||
public void setBeforeText(CharSequence beforeText) {
|
||||
mBeforeText = beforeText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description of the source.
|
||||
*
|
||||
* @return The description.
|
||||
*/
|
||||
public CharSequence getContentDescription() {
|
||||
return mContentDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description of the source.
|
||||
*
|
||||
* @param contentDescription The description.
|
||||
*/
|
||||
public void setContentDescription(CharSequence contentDescription) {
|
||||
mContentDescription = contentDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Parcelable} data.
|
||||
*
|
||||
* @return The parcelable data.
|
||||
*/
|
||||
public Parcelable getParcelableData() {
|
||||
return mParcelableData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Parcelable} data of the event.
|
||||
*
|
||||
* @param parcelableData The parcelable data.
|
||||
*/
|
||||
public void setParcelableData(Parcelable parcelableData) {
|
||||
mParcelableData = parcelableData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cached instance if such is available or a new one is
|
||||
* instantiated with type property set.
|
||||
*
|
||||
* @param eventType The event type.
|
||||
* @return An instance.
|
||||
*/
|
||||
public static AccessibilityEvent obtain(int eventType) {
|
||||
AccessibilityEvent event = AccessibilityEvent.obtain();
|
||||
event.setEventType(eventType);
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cached instance if such is available or a new one is
|
||||
* instantiated.
|
||||
*
|
||||
* @return An instance.
|
||||
*/
|
||||
public static AccessibilityEvent obtain() {
|
||||
synchronized (mPoolLock) {
|
||||
if (sPool != null) {
|
||||
AccessibilityEvent event = sPool;
|
||||
sPool = sPool.mNext;
|
||||
sPoolSize--;
|
||||
event.mNext = null;
|
||||
event.mIsInPool = false;
|
||||
return event;
|
||||
}
|
||||
return new AccessibilityEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance back to be reused.
|
||||
* <p>
|
||||
* <b>Note: You must not touch the object after calling this function.</b>
|
||||
*/
|
||||
public void recycle() {
|
||||
if (mIsInPool) {
|
||||
return;
|
||||
}
|
||||
|
||||
clear();
|
||||
synchronized (mPoolLock) {
|
||||
if (sPoolSize <= MAX_POOL_SIZE) {
|
||||
mNext = sPool;
|
||||
sPool = this;
|
||||
mIsInPool = true;
|
||||
sPoolSize++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the state of this instance.
|
||||
*/
|
||||
private void clear() {
|
||||
mEventType = 0;
|
||||
mBooleanProperties = 0;
|
||||
mCurrentItemIndex = INVALID_POSITION;
|
||||
mItemCount = 0;
|
||||
mFromIndex = 0;
|
||||
mAddedCount = 0;
|
||||
mRemovedCount = 0;
|
||||
mEventTime = 0;
|
||||
mClassName = null;
|
||||
mPackageName = null;
|
||||
mContentDescription = null;
|
||||
mBeforeText = null;
|
||||
mText.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a boolean property.
|
||||
*
|
||||
* @param property The property.
|
||||
* @return The value.
|
||||
*/
|
||||
private boolean getBooleanProperty(int property) {
|
||||
return (mBooleanProperties & property) == property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean property.
|
||||
*
|
||||
* @param property The property.
|
||||
* @param value The value.
|
||||
*/
|
||||
private void setBooleanProperty(int property, boolean value) {
|
||||
if (value) {
|
||||
mBooleanProperties |= property;
|
||||
} else {
|
||||
mBooleanProperties &= ~property;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from a {@link Parcel}.
|
||||
*
|
||||
* @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
|
||||
*/
|
||||
public void initFromParcel(Parcel parcel) {
|
||||
mEventType = parcel.readInt();
|
||||
mBooleanProperties = parcel.readInt();
|
||||
mCurrentItemIndex = parcel.readInt();
|
||||
mItemCount = parcel.readInt();
|
||||
mFromIndex = parcel.readInt();
|
||||
mAddedCount = parcel.readInt();
|
||||
mRemovedCount = parcel.readInt();
|
||||
mEventTime = parcel.readLong();
|
||||
mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
|
||||
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
|
||||
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
|
||||
mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
|
||||
mParcelableData = parcel.readParcelable(null);
|
||||
parcel.readList(mText, null);
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeInt(mEventType);
|
||||
parcel.writeInt(mBooleanProperties);
|
||||
parcel.writeInt(mCurrentItemIndex);
|
||||
parcel.writeInt(mItemCount);
|
||||
parcel.writeInt(mFromIndex);
|
||||
parcel.writeInt(mAddedCount);
|
||||
parcel.writeInt(mRemovedCount);
|
||||
parcel.writeLong(mEventTime);
|
||||
TextUtils.writeToParcel(mClassName, parcel, 0);
|
||||
TextUtils.writeToParcel(mPackageName, parcel, 0);
|
||||
TextUtils.writeToParcel(mContentDescription, parcel, 0);
|
||||
TextUtils.writeToParcel(mBeforeText, parcel, 0);
|
||||
parcel.writeParcelable(mParcelableData, flags);
|
||||
parcel.writeList(mText);
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(super.toString());
|
||||
builder.append("; EventType: " + mEventType);
|
||||
builder.append("; EventTime: " + mEventTime);
|
||||
builder.append("; ClassName: " + mClassName);
|
||||
builder.append("; PackageName: " + mPackageName);
|
||||
builder.append("; Text: " + mText);
|
||||
builder.append("; ContentDescription: " + mContentDescription);
|
||||
builder.append("; ItemCount: " + mItemCount);
|
||||
builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
|
||||
builder.append("; IsEnabled: " + isEnabled());
|
||||
builder.append("; IsPassword: " + isPassword());
|
||||
builder.append("; IsChecked: " + isChecked());
|
||||
builder.append("; IsFullScreen: " + isFullScreen());
|
||||
builder.append("; BeforeText: " + mBeforeText);
|
||||
builder.append("; FromIndex: " + mFromIndex);
|
||||
builder.append("; AddedCount: " + mAddedCount);
|
||||
builder.append("; RemovedCount: " + mRemovedCount);
|
||||
builder.append("; ParcelableData: " + mParcelableData);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Parcelable.Creator
|
||||
*/
|
||||
public static final Parcelable.Creator<AccessibilityEvent> CREATOR =
|
||||
new Parcelable.Creator<AccessibilityEvent>() {
|
||||
public AccessibilityEvent createFromParcel(Parcel parcel) {
|
||||
AccessibilityEvent event = AccessibilityEvent.obtain();
|
||||
event.initFromParcel(parcel);
|
||||
return event;
|
||||
}
|
||||
|
||||
public AccessibilityEvent[] newArray(int size) {
|
||||
return new AccessibilityEvent[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.view.accessibility;
|
||||
|
||||
/**
|
||||
* This interface is implemented by classes source of {@link AccessibilityEvent}s.
|
||||
*/
|
||||
public interface AccessibilityEventSource {
|
||||
|
||||
/**
|
||||
* Handles the request for sending an {@link AccessibilityEvent} given
|
||||
* the event type. The method must first check if accessibility is on
|
||||
* via calling {@link AccessibilityManager#isEnabled()}, obtain
|
||||
* an {@link AccessibilityEvent} from the event pool through calling
|
||||
* {@link AccessibilityEvent#obtain(int)}, populate the event, and
|
||||
* send it for dispatch via calling
|
||||
* {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)}.
|
||||
*
|
||||
* @see AccessibilityEvent
|
||||
* @see AccessibilityManager
|
||||
*
|
||||
* @param eventType The event type.
|
||||
*/
|
||||
public void sendAccessibilityEvent(int eventType);
|
||||
|
||||
/**
|
||||
* Handles the request for sending an {@link AccessibilityEvent}. The
|
||||
* method does not guarantee to check if accessibility is on before
|
||||
* sending the event for dispatch. It is responsibility of the caller
|
||||
* to do the check via calling {@link AccessibilityManager#isEnabled()}.
|
||||
*
|
||||
* @see AccessibilityEvent
|
||||
* @see AccessibilityManager
|
||||
*
|
||||
* @param event The event.
|
||||
*/
|
||||
public void sendAccessibilityEventUnchecked(AccessibilityEvent event);
|
||||
}
|
||||
198
core/java/android/view/accessibility/AccessibilityManager.java
Normal file
198
core/java/android/view/accessibility/AccessibilityManager.java
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.view.accessibility;
|
||||
|
||||
import static android.util.Config.LOGV;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
|
||||
* Such events are generated when something notable happens in the user interface,
|
||||
* for example an {@link android.app.Activity} starts, the focus or selection of a
|
||||
* {@link android.view.View} changes etc. Parties interested in handling accessibility
|
||||
* events implement and register an accessibility service which extends
|
||||
* {@link android.accessibilityservice.AccessibilityService}.
|
||||
*
|
||||
* @see AccessibilityEvent
|
||||
* @see android.accessibilityservice.AccessibilityService
|
||||
* @see android.content.Context#getSystemService
|
||||
*/
|
||||
public final class AccessibilityManager {
|
||||
private static final String LOG_TAG = "AccessibilityManager";
|
||||
|
||||
static final Object sInstanceSync = new Object();
|
||||
|
||||
private static AccessibilityManager sInstance;
|
||||
|
||||
private static final int DO_SET_ENABLED = 10;
|
||||
|
||||
final IAccessibilityManager mService;
|
||||
|
||||
final Handler mHandler;
|
||||
|
||||
boolean mIsEnabled;
|
||||
|
||||
final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
|
||||
public void setEnabled(boolean enabled) {
|
||||
mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget();
|
||||
}
|
||||
};
|
||||
|
||||
class MyHandler extends Handler {
|
||||
|
||||
MyHandler(Looper mainLooper) {
|
||||
super(mainLooper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case DO_SET_ENABLED :
|
||||
synchronized (mHandler) {
|
||||
mIsEnabled = (message.arg1 == 1);
|
||||
}
|
||||
return;
|
||||
default :
|
||||
Log.w(LOG_TAG, "Unknown message type: " + message.what);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an AccessibilityManager instance (create one if necessary).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static AccessibilityManager getInstance(Context context) {
|
||||
synchronized (sInstanceSync) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new AccessibilityManager(context);
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
*/
|
||||
private AccessibilityManager(Context context) {
|
||||
mHandler = new MyHandler(context.getMainLooper());
|
||||
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
|
||||
mService = IAccessibilityManager.Stub.asInterface(iBinder);
|
||||
try {
|
||||
mService.addClient(mClient);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the {@link AccessibilityManager} is enabled.
|
||||
*
|
||||
* @return True if this {@link AccessibilityManager} is enabled, false otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
synchronized (mHandler) {
|
||||
return mIsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not
|
||||
* enabled the call is a NOOP.
|
||||
*
|
||||
* @param event The {@link AccessibilityEvent}.
|
||||
*
|
||||
* @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent}
|
||||
* while accessibility is not enabled.
|
||||
*/
|
||||
public void sendAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (!mIsEnabled) {
|
||||
throw new IllegalStateException("Accessibility off. Did you forget to check that?");
|
||||
}
|
||||
boolean doRecycle = false;
|
||||
try {
|
||||
event.setEventTime(SystemClock.uptimeMillis());
|
||||
// it is possible that this manager is in the same process as the service but
|
||||
// client using it is called through Binder from another process. Example: MMS
|
||||
// app adds a SMS notification and the NotificationManagerService calls this method
|
||||
long identityToken = Binder.clearCallingIdentity();
|
||||
doRecycle = mService.sendAccessibilityEvent(event);
|
||||
Binder.restoreCallingIdentity(identityToken);
|
||||
if (LOGV) {
|
||||
Log.i(LOG_TAG, event + " sent");
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error during sending " + event + " ", re);
|
||||
} finally {
|
||||
if (doRecycle) {
|
||||
event.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests interruption of the accessibility feedback from all accessibility services.
|
||||
*/
|
||||
public void interrupt() {
|
||||
if (!mIsEnabled) {
|
||||
throw new IllegalStateException("Accessibility off. Did you forget to check that?");
|
||||
}
|
||||
try {
|
||||
mService.interrupt();
|
||||
if (LOGV) {
|
||||
Log.i(LOG_TAG, "Requested interrupt from all services");
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ServiceInfo}s of the installed accessibility services.
|
||||
*
|
||||
* @return An unmodifiable list with {@link ServiceInfo}s.
|
||||
*/
|
||||
public List<ServiceInfo> getAccessibilityServiceList() {
|
||||
List<ServiceInfo> services = null;
|
||||
try {
|
||||
services = mService.getAccessibilityServiceList();
|
||||
if (LOGV) {
|
||||
Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
|
||||
}
|
||||
return Collections.unmodifiableList(services);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* //device/java/android/android/app/INotificationManager.aidl
|
||||
**
|
||||
** Copyright 2009, 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 android.view.accessibility;
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.IAccessibilityManagerClient;
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
||||
/**
|
||||
* Interface implemented by the AccessibilityManagerService called by
|
||||
* the AccessibilityMasngers.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface IAccessibilityManager {
|
||||
|
||||
void addClient(IAccessibilityManagerClient client);
|
||||
|
||||
boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent);
|
||||
|
||||
List<ServiceInfo> getAccessibilityServiceList();
|
||||
|
||||
void interrupt();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 android.view.accessibility;
|
||||
|
||||
/**
|
||||
* Interface a client of the IAccessibilityManager implements to
|
||||
* receive information about changes in the manager state.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IAccessibilityManagerClient {
|
||||
|
||||
void setEnabled(boolean enabled);
|
||||
|
||||
}
|
||||
@@ -24,11 +24,12 @@ import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.SoundEffectConstants;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
|
||||
/**
|
||||
@@ -618,7 +619,9 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently selected item
|
||||
* Sets the currently selected item. To support accessibility subclasses that
|
||||
* override this method must invoke the overriden super method first.
|
||||
*
|
||||
* @param position Index (starting at 0) of the data item to be selected.
|
||||
*/
|
||||
public abstract void setSelection(int position);
|
||||
@@ -844,6 +847,11 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
|
||||
fireOnSelected();
|
||||
}
|
||||
}
|
||||
|
||||
// we fire selection events here not in View
|
||||
if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) {
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnSelected() {
|
||||
@@ -860,6 +868,35 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
boolean populated = false;
|
||||
// This is an exceptional case which occurs when a window gets the
|
||||
// focus and sends a focus event via its focused child to announce
|
||||
// current focus/selection. AdapterView fires selection but not focus
|
||||
// events so we change the event type here.
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
|
||||
event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED);
|
||||
}
|
||||
|
||||
// we send selection events only from AdapterView to avoid
|
||||
// generation of such event for each child
|
||||
View selectedView = getSelectedView();
|
||||
if (selectedView != null) {
|
||||
populated = selectedView.dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
if (!populated) {
|
||||
if (selectedView != null) {
|
||||
event.setEnabled(selectedView.isEnabled());
|
||||
}
|
||||
event.setItemCount(getCount());
|
||||
event.setCurrentItemIndex(getSelectedItemPosition());
|
||||
}
|
||||
|
||||
return populated;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canAnimate() {
|
||||
return super.canAnimate() && mItemCount > 0;
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
|
||||
import com.android.internal.R;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
|
||||
/**
|
||||
@@ -194,5 +195,13 @@ public class CheckedTextView extends TextView implements Checkable {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
boolean populated = super.dispatchPopulateAccessibilityEvent(event);
|
||||
if (!populated) {
|
||||
event.setChecked(mChecked);
|
||||
}
|
||||
return populated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -124,6 +124,7 @@ public abstract class CompoundButton extends Button implements Checkable {
|
||||
if (mOnCheckedChangeWidgetListener != null) {
|
||||
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
|
||||
}
|
||||
|
||||
mBroadcasting = false;
|
||||
}
|
||||
}
|
||||
@@ -204,6 +205,25 @@ public abstract class CompoundButton extends Button implements Checkable {
|
||||
refreshDrawableState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
boolean populated = super.dispatchPopulateAccessibilityEvent(event);
|
||||
|
||||
if (!populated) {
|
||||
int resourceId = 0;
|
||||
if (mChecked) {
|
||||
resourceId = R.string.accessibility_compound_button_selected;
|
||||
} else {
|
||||
resourceId = R.string.accessibility_compound_button_unselected;
|
||||
}
|
||||
String state = getResources().getString(resourceId);
|
||||
event.getText().add(state);
|
||||
event.setChecked(mChecked);
|
||||
}
|
||||
|
||||
return populated;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
@@ -32,6 +32,8 @@ import android.net.Uri;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.RemoteViews.RemoteView;
|
||||
|
||||
|
||||
@@ -848,7 +850,7 @@ public class ImageView extends View {
|
||||
public int getBaseline() {
|
||||
return mBaselineAligned ? getMeasuredHeight() : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a tinting option for the image.
|
||||
*
|
||||
@@ -878,7 +880,7 @@ public class ImageView extends View {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setAlpha(int alpha) {
|
||||
alpha &= 0xFF; // keep it legal
|
||||
if (mAlpha != alpha) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.SoundEffectConstants;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
import com.android.internal.R;
|
||||
@@ -1845,6 +1846,32 @@ public class ListView extends AbsListView {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
boolean populated = super.dispatchPopulateAccessibilityEvent(event);
|
||||
|
||||
if (!populated) {
|
||||
int itemCount = 0;
|
||||
int currentItemIndex = getSelectedItemPosition();
|
||||
|
||||
ListAdapter adapter = getAdapter();
|
||||
if (adapter != null) {
|
||||
for (int i = 0, count = adapter.getCount(); i < count; i++) {
|
||||
if (adapter.isEnabled(i)) {
|
||||
itemCount++;
|
||||
} else if (i <= currentItemIndex) {
|
||||
currentItemIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event.setItemCount(itemCount);
|
||||
event.setCurrentItemIndex(currentItemIndex);
|
||||
}
|
||||
|
||||
return populated;
|
||||
}
|
||||
|
||||
/**
|
||||
* setSelectionAfterHeaderView set the selection to be the first list item
|
||||
* after the header views.
|
||||
|
||||
@@ -18,6 +18,8 @@ package android.widget;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@@ -33,8 +35,6 @@ import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.os.IBinder;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -1017,6 +1017,7 @@ public class PopupWindow {
|
||||
unregisterForScrollChanged();
|
||||
|
||||
mWindowManager.removeView(mPopupView);
|
||||
|
||||
if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
|
||||
((ViewGroup) mPopupView).removeView(mContentView);
|
||||
}
|
||||
@@ -1316,7 +1317,16 @@ public class PopupWindow {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendAccessibilityEvent(int eventType) {
|
||||
// clinets are interested in the content not the container, make it event source
|
||||
if (mContentView != null) {
|
||||
mContentView.sendAccessibilityEvent(eventType);
|
||||
} else {
|
||||
super.sendAccessibilityEvent(eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,17 +16,22 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewDebug;
|
||||
import android.widget.RemoteViews.RemoteView;
|
||||
import android.graphics.Rect;
|
||||
import com.android.internal.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.RemoteViews.RemoteView;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* A Layout where the positions of the children can be described in relation to each other or to the
|
||||
@@ -137,6 +142,8 @@ public class RelativeLayout extends ViewGroup {
|
||||
private final Rect mSelfBounds = new Rect();
|
||||
private int mIgnoreGravity;
|
||||
|
||||
private static SortedSet<View> mTopToBottomLeftToRightSet = null;
|
||||
|
||||
public RelativeLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -782,6 +789,57 @@ public class RelativeLayout extends ViewGroup {
|
||||
return new LayoutParams(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (mTopToBottomLeftToRightSet == null) {
|
||||
mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
|
||||
}
|
||||
|
||||
// sort children top-to-bottom and left-to-right
|
||||
for (int i = 0, count = getChildCount(); i < count; i++) {
|
||||
mTopToBottomLeftToRightSet.add(getChildAt(i));
|
||||
}
|
||||
|
||||
for (View view : mTopToBottomLeftToRightSet) {
|
||||
if (view.dispatchPopulateAccessibilityEvent(event)) {
|
||||
mTopToBottomLeftToRightSet.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mTopToBottomLeftToRightSet.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two views in left-to-right and top-to-bottom fashion.
|
||||
*/
|
||||
private class TopToBottomLeftToRightComparator implements Comparator<View> {
|
||||
public int compare(View first, View second) {
|
||||
// top - bottom
|
||||
int topDifference = first.getTop() - second.getTop();
|
||||
if (topDifference != 0) {
|
||||
return topDifference;
|
||||
}
|
||||
// left - right
|
||||
int leftDifference = first.getLeft() - second.getLeft();
|
||||
if (leftDifference != 0) {
|
||||
return leftDifference;
|
||||
}
|
||||
// break tie by height
|
||||
int heightDiference = first.getHeight() - second.getHeight();
|
||||
if (heightDiference != 0) {
|
||||
return heightDiference;
|
||||
}
|
||||
// break tie by width
|
||||
int widthDiference = first.getWidth() - second.getWidth();
|
||||
if (widthDiference != 0) {
|
||||
return widthDiference;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-child layout information associated with RelativeLayout.
|
||||
*
|
||||
|
||||
@@ -16,21 +16,22 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
import android.view.View;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.SoundEffectConstants;
|
||||
import android.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.R;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SoundEffectConstants;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* SlidingDrawer hides content out of the screen and allows the user to drag a handle
|
||||
@@ -746,6 +747,8 @@ public class SlidingDrawer extends ViewGroup {
|
||||
openDrawer();
|
||||
invalidate();
|
||||
requestLayout();
|
||||
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -777,6 +780,7 @@ public class SlidingDrawer extends ViewGroup {
|
||||
scrollListener.onScrollStarted();
|
||||
}
|
||||
animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft());
|
||||
|
||||
if (scrollListener != null) {
|
||||
scrollListener.onScrollEnded();
|
||||
}
|
||||
@@ -798,6 +802,9 @@ public class SlidingDrawer extends ViewGroup {
|
||||
scrollListener.onScrollStarted();
|
||||
}
|
||||
animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft());
|
||||
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
||||
|
||||
if (scrollListener != null) {
|
||||
scrollListener.onScrollEnded();
|
||||
}
|
||||
@@ -827,6 +834,7 @@ public class SlidingDrawer extends ViewGroup {
|
||||
}
|
||||
|
||||
mExpanded = true;
|
||||
|
||||
if (mOnDrawerOpenListener != null) {
|
||||
mOnDrawerOpenListener.onDrawerOpened();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import com.android.internal.util.FastMath;
|
||||
import com.android.internal.widget.EditableInputConnection;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
@@ -31,17 +36,17 @@ import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Message;
|
||||
import android.text.BoringLayout;
|
||||
import android.text.ClipboardManager;
|
||||
import android.text.DynamicLayout;
|
||||
import android.text.Editable;
|
||||
import android.text.GetChars;
|
||||
import android.text.GraphicsOperations;
|
||||
import android.text.ClipboardManager;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.text.Layout;
|
||||
@@ -49,9 +54,9 @@ import android.text.ParcelableSpan;
|
||||
import android.text.Selection;
|
||||
import android.text.SpanWatcher;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.SpannedString;
|
||||
import android.text.SpannableString;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
@@ -64,19 +69,18 @@ import android.text.method.KeyListener;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.MetaKeyKeyListener;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.text.method.TimeKeyListener;
|
||||
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.text.method.SingleLineTransformationMethod;
|
||||
import android.text.method.TextKeyListener;
|
||||
import android.text.method.TimeKeyListener;
|
||||
import android.text.method.TransformationMethod;
|
||||
import android.text.style.ParagraphStyle;
|
||||
import android.text.style.URLSpan;
|
||||
import android.text.style.UpdateAppearance;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Gravity;
|
||||
@@ -89,25 +93,22 @@ import android.view.ViewDebug;
|
||||
import android.view.ViewRoot;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.RemoteViews.RemoteView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.android.internal.util.FastMath;
|
||||
import com.android.internal.widget.EditableInputConnection;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
/**
|
||||
* Displays text to the user and optionally allows them to edit it. A TextView
|
||||
* is a complete text editor, however the basic class is configured to not
|
||||
@@ -6129,10 +6130,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
|
||||
private class ChangeWatcher
|
||||
implements TextWatcher, SpanWatcher {
|
||||
|
||||
private CharSequence mBeforeText;
|
||||
|
||||
public void beforeTextChanged(CharSequence buffer, int start,
|
||||
int before, int after) {
|
||||
if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start
|
||||
+ " before=" + before + " after=" + after + ": " + buffer);
|
||||
|
||||
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
|
||||
mBeforeText = buffer.toString();
|
||||
}
|
||||
|
||||
TextView.this.sendBeforeTextChanged(buffer, start, before, after);
|
||||
}
|
||||
|
||||
@@ -6141,6 +6150,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start
|
||||
+ " before=" + before + " after=" + after + ": " + buffer);
|
||||
TextView.this.handleTextChanged(buffer, start, before, after);
|
||||
|
||||
if (AccessibilityManager.getInstance(mContext).isEnabled() &&
|
||||
(isFocused() || isSelected() &&
|
||||
isShown())) {
|
||||
sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
|
||||
mBeforeText = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void afterTextChanged(Editable buffer) {
|
||||
@@ -6775,6 +6791,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return TextUtils.substring(mTransformed, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
boolean isPassword =
|
||||
(mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) ==
|
||||
(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
|
||||
if (!isPassword) {
|
||||
CharSequence text = getText();
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
text = getHint();
|
||||
}
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
if (text.length() > AccessibilityEvent.MAX_TEXT_LENGTH) {
|
||||
text = text.subSequence(0, AccessibilityEvent.MAX_TEXT_LENGTH + 1);
|
||||
}
|
||||
event.getText().add(text);
|
||||
}
|
||||
} else {
|
||||
event.setPassword(isPassword);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
|
||||
int fromIndex, int removedCount, int addedCount) {
|
||||
AccessibilityEvent event =
|
||||
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
|
||||
event.setFromIndex(fromIndex);
|
||||
event.setRemovedCount(removedCount);
|
||||
event.setAddedCount(addedCount);
|
||||
event.setBeforeText(beforeText);
|
||||
sendAccessibilityEventUnchecked(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateContextMenu(ContextMenu menu) {
|
||||
super.onCreateContextMenu(menu);
|
||||
|
||||
@@ -21,8 +21,8 @@ import android.app.ITransientNotification;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
@@ -278,7 +278,7 @@ public class Toast {
|
||||
}
|
||||
tv.setText(s);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================================================
|
||||
// All the gunk below is the interaction with the Notification Service, which handles
|
||||
// the proper ordering of these system-wide.
|
||||
@@ -373,6 +373,7 @@ public class Toast {
|
||||
TAG, "REMOVE! " + mView + " in " + this);
|
||||
mWM.removeView(mView);
|
||||
}
|
||||
|
||||
mView = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1165,6 +1165,11 @@
|
||||
enabled for events such as long presses. -->
|
||||
<attr name="hapticFeedbackEnabled" format="boolean" />
|
||||
|
||||
<!-- Defines text that briefly describes content of the view. This property is used
|
||||
primarily for accessibility. Since some views do not have textual
|
||||
representation this attribute can be used for providing such. -->
|
||||
<attr name="contentDescription" format="string" localization="suggested" />
|
||||
|
||||
<!-- Name of the method in this View's context to invoke when the view is
|
||||
clicked. This name must correspond to a public method that takes
|
||||
exactly one parameter of type View. For instance, if you specify
|
||||
|
||||
@@ -1100,6 +1100,7 @@
|
||||
<public type="attr" name="targetSdkVersion" id="0x01010270" />
|
||||
<public type="attr" name="maxSdkVersion" id="0x01010271" />
|
||||
<public type="attr" name="testOnly" id="0x01010272" />
|
||||
<public type="attr" name="contentDescription" id="0x01010273" />
|
||||
|
||||
<public type="anim" name="anticipate_interpolator" id="0x010a0007" />
|
||||
<public type="anim" name="overshoot_interpolator" id="0x010a0008" />
|
||||
|
||||
@@ -887,7 +887,6 @@
|
||||
properties uploaded by the checkin service. Not for use by normal
|
||||
applications.</string>
|
||||
|
||||
|
||||
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permlab_bindGadget">choose widgets</string>
|
||||
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
@@ -2366,10 +2365,12 @@
|
||||
|
||||
<!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale pairs. This is used at startup to set a default locale by checking the system property ro.carrier for the carrier-id and searching through this array -->
|
||||
<string-array translatable="false" name="carrier_locales">
|
||||
</string-array>
|
||||
</string-array>
|
||||
|
||||
<!-- Title for the selected state of a CompoundButton. -->
|
||||
<string name="accessibility_compound_button_selected">checked</string>
|
||||
|
||||
<!-- Title for the unselected state of a CompoundButton. -->
|
||||
<string name="accessibility_compound_button_unselected">not checked</string>
|
||||
|
||||
</resources>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,668 @@
|
||||
/*
|
||||
** Copyright 2009, 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.server;
|
||||
|
||||
import static android.util.Config.LOGV;
|
||||
|
||||
import com.android.internal.os.HandlerCaller;
|
||||
import com.android.internal.os.HandlerCaller.SomeArgs;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||
import android.accessibilityservice.IEventListener;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextUtils.SimpleStringSplitter;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.IAccessibilityManager;
|
||||
import android.view.accessibility.IAccessibilityManagerClient;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is instantiated by the system as a system level service and can be
|
||||
* accessed only by the system. The task of this service is to be a centralized
|
||||
* event dispatch for {@link AccessibilityEvent}s generated across all processes
|
||||
* on the device. Events are dispatched to {@link AccessibilityService}s.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
implements HandlerCaller.Callback {
|
||||
|
||||
private static final String LOG_TAG = "AccessibilityManagerService";
|
||||
|
||||
private static int sIdCounter = 0;
|
||||
|
||||
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
|
||||
|
||||
private static final int DO_SET_SERVICE_INFO = 10;
|
||||
|
||||
final HandlerCaller mCaller;
|
||||
|
||||
final Context mContext;
|
||||
|
||||
final Object mLock = new Object();
|
||||
|
||||
final List<Service> mServices = new ArrayList<Service>();
|
||||
|
||||
final List<IAccessibilityManagerClient> mClients =
|
||||
new ArrayList<IAccessibilityManagerClient>();
|
||||
|
||||
final Map<ComponentName, Service> mComponentNameToServiceMap =
|
||||
new HashMap<ComponentName, Service>();
|
||||
|
||||
private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
|
||||
|
||||
private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
|
||||
|
||||
private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
|
||||
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private int mHandledFeedbackTypes = 0;
|
||||
|
||||
private boolean mIsEnabled;
|
||||
|
||||
/**
|
||||
* Handler for delayed event dispatch.
|
||||
*/
|
||||
private Handler mHandler = new Handler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
Service service = (Service) message.obj;
|
||||
int eventType = message.arg1;
|
||||
|
||||
synchronized (mLock) {
|
||||
notifyEventListenerLocked(service, eventType);
|
||||
AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
|
||||
service.mPendingEvents.remove(eventType);
|
||||
tryRecycleLocked(oldEvent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param context A {@link Context} instance.
|
||||
*/
|
||||
AccessibilityManagerService(Context context) {
|
||||
mContext = context;
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
mCaller = new HandlerCaller(context, this);
|
||||
|
||||
registerPackageChangeAndBootCompletedBroadcastReceiver();
|
||||
registerSettingsContentObservers();
|
||||
|
||||
synchronized (mLock) {
|
||||
populateAccessibilityServiceListLocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link BroadcastReceiver} for the events of
|
||||
* adding/changing/removing/restarting a package and boot completion.
|
||||
*/
|
||||
private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
|
||||
Context context = mContext;
|
||||
|
||||
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
synchronized (mLock) {
|
||||
populateAccessibilityServiceListLocked();
|
||||
manageServicesLocked();
|
||||
|
||||
if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
|
||||
mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
|
||||
updateClientsLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// package changes
|
||||
IntentFilter packageFilter = new IntentFilter();
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
|
||||
packageFilter.addDataScheme("package");
|
||||
context.registerReceiver(broadcastReceiver, packageFilter);
|
||||
|
||||
// boot completed
|
||||
IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
|
||||
mContext.registerReceiver(broadcastReceiver, bootFiler);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
|
||||
* and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
|
||||
*/
|
||||
private void registerSettingsContentObservers() {
|
||||
ContentResolver contentResolver = mContext.getContentResolver();
|
||||
|
||||
Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED);
|
||||
contentResolver.registerContentObserver(enabledUri, false,
|
||||
new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
super.onChange(selfChange);
|
||||
|
||||
mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mIsEnabled) {
|
||||
manageServicesLocked();
|
||||
} else {
|
||||
unbindAllServicesLocked();
|
||||
}
|
||||
updateClientsLocked();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Uri providersUri =
|
||||
Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
|
||||
contentResolver.registerContentObserver(providersUri, false,
|
||||
new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
super.onChange(selfChange);
|
||||
|
||||
synchronized (mLock) {
|
||||
manageServicesLocked();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addClient(IAccessibilityManagerClient client) {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
client.setEnabled(mIsEnabled);
|
||||
mClients.add(client);
|
||||
} catch (RemoteException re) {
|
||||
Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sendAccessibilityEvent(AccessibilityEvent event) {
|
||||
synchronized (mLock) {
|
||||
notifyAccessibilityServicesDelayedLocked(event, false);
|
||||
notifyAccessibilityServicesDelayedLocked(event, true);
|
||||
}
|
||||
// event not scheduled for dispatch => recycle
|
||||
if (mHandledFeedbackTypes == 0) {
|
||||
event.recycle();
|
||||
} else {
|
||||
mHandledFeedbackTypes = 0;
|
||||
}
|
||||
|
||||
return (OWN_PROCESS_ID != Binder.getCallingPid());
|
||||
}
|
||||
|
||||
public List<ServiceInfo> getAccessibilityServiceList() {
|
||||
synchronized (mLock) {
|
||||
return mInstalledServices;
|
||||
}
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
synchronized (mLock) {
|
||||
for (int i = 0, count = mServices.size(); i < count; i++) {
|
||||
Service service = mServices.get(i);
|
||||
try {
|
||||
service.mServiceInterface.onInterrupt();
|
||||
} catch (RemoteException re) {
|
||||
if (re instanceof DeadObjectException) {
|
||||
Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
|
||||
if (removeDeadServiceLocked(service)) {
|
||||
count--;
|
||||
i--;
|
||||
}
|
||||
} else {
|
||||
Log.e(LOG_TAG, "Error during sending interrupt request to "
|
||||
+ service.mService, re);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void executeMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case DO_SET_SERVICE_INFO:
|
||||
SomeArgs arguments = ((SomeArgs) message.obj);
|
||||
|
||||
AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
|
||||
Service service = (Service) arguments.arg2;
|
||||
|
||||
synchronized (mLock) {
|
||||
service.mEventTypes = info.eventTypes;
|
||||
service.mFeedbackType = info.feedbackType;
|
||||
String[] packageNames = info.packageNames;
|
||||
if (packageNames != null) {
|
||||
service.mPackageNames.addAll(Arrays.asList(packageNames));
|
||||
}
|
||||
service.mNotificationTimeout = info.notificationTimeout;
|
||||
service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
Log.w(LOG_TAG, "Unknown message type: " + message.what);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the cached list of installed {@link AccessibilityService}s.
|
||||
*/
|
||||
private void populateAccessibilityServiceListLocked() {
|
||||
mInstalledServices.clear();
|
||||
|
||||
List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
|
||||
new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
|
||||
|
||||
for (int i = 0, count = installedServices.size(); i < count; i++) {
|
||||
mInstalledServices.add(installedServices.get(i).serviceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@link AccessibilityService}s delayed notification. The delay is configurable
|
||||
* and denotes the period after the last event before notifying the service.
|
||||
*
|
||||
* @param event The event.
|
||||
* @param isDefault True to notify default listeners, not default services.
|
||||
*/
|
||||
private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
|
||||
boolean isDefault) {
|
||||
for (int i = 0, count = mServices.size(); i < count; i++) {
|
||||
Service service = mServices.get(i);
|
||||
|
||||
if (service.mIsDefault == isDefault) {
|
||||
if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
|
||||
mHandledFeedbackTypes |= service.mFeedbackType;
|
||||
notifyAccessibilityServiceDelayedLocked(service, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an {@link AccessibilityService} delayed notification. The delay is configurable
|
||||
* and denotes the period after the last event before notifying the service.
|
||||
*
|
||||
* @param service The service.
|
||||
* @param event The event.
|
||||
*/
|
||||
private void notifyAccessibilityServiceDelayedLocked(Service service,
|
||||
AccessibilityEvent event) {
|
||||
synchronized (mLock) {
|
||||
int eventType = event.getEventType();
|
||||
AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
|
||||
service.mPendingEvents.put(eventType, event);
|
||||
|
||||
int what = eventType | (service.mId << 16);
|
||||
if (oldEvent != null) {
|
||||
mHandler.removeMessages(what);
|
||||
tryRecycleLocked(oldEvent);
|
||||
}
|
||||
|
||||
Message message = mHandler.obtainMessage(what, service);
|
||||
message.arg1 = event.getEventType();
|
||||
mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recycles an event if it can be safely recycled. The condition is that no
|
||||
* not notified service is interested in the event.
|
||||
*
|
||||
* @param event The event.
|
||||
*/
|
||||
private void tryRecycleLocked(AccessibilityEvent event) {
|
||||
int eventType = event.getEventType();
|
||||
List<Service> services = mServices;
|
||||
|
||||
// linear in the number of service which is not large
|
||||
for (int i = 0, count = services.size(); i < count; i++) {
|
||||
Service service = services.get(i);
|
||||
if (service.mPendingEvents.get(eventType) == event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies a service for a scheduled event given the event type.
|
||||
*
|
||||
* @param service The service.
|
||||
* @param eventType The type of the event to dispatch.
|
||||
*/
|
||||
private void notifyEventListenerLocked(Service service, int eventType) {
|
||||
IEventListener listener = service.mServiceInterface;
|
||||
AccessibilityEvent event = service.mPendingEvents.get(eventType);
|
||||
|
||||
try {
|
||||
listener.onAccessibilityEvent(event);
|
||||
if (LOGV) {
|
||||
Log.i(LOG_TAG, "Event " + event + " sent to " + listener);
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
if (re instanceof DeadObjectException) {
|
||||
Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
|
||||
synchronized (mLock) {
|
||||
removeDeadServiceLocked(service);
|
||||
}
|
||||
} else {
|
||||
Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a dead service.
|
||||
*
|
||||
* @param service The service.
|
||||
* @return True if the service was removed, false otherwise.
|
||||
*/
|
||||
private boolean removeDeadServiceLocked(Service service) {
|
||||
mServices.remove(service);
|
||||
mHandler.removeMessages(service.mId);
|
||||
|
||||
if (LOGV) {
|
||||
Log.i(LOG_TAG, "Dead service " + service.mService + " removed");
|
||||
}
|
||||
|
||||
if (mServices.isEmpty()) {
|
||||
mIsEnabled = false;
|
||||
updateClientsLocked();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if given event can be dispatched to a service based on the package of the
|
||||
* event source and already notified services for that event type. Specifically, a
|
||||
* service is notified if it is interested in events from the package and no other service
|
||||
* providing the same feedback type has been notified. Exception are services the
|
||||
* provide generic feedback (feedback type left as a safety net for unforeseen feedback
|
||||
* types) which are always notified.
|
||||
*
|
||||
* @param service The potential receiver.
|
||||
* @param event The event.
|
||||
* @param handledFeedbackTypes The feedback types for which services have been notified.
|
||||
* @return True if the listener should be notified, false otherwise.
|
||||
*/
|
||||
private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
|
||||
int handledFeedbackTypes) {
|
||||
|
||||
if (!service.isConfigured()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!service.mService.isBinderAlive()) {
|
||||
removeDeadServiceLocked(service);
|
||||
return false;
|
||||
}
|
||||
|
||||
int eventType = event.getEventType();
|
||||
if ((service.mEventTypes & eventType) != eventType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<String> packageNames = service.mPackageNames;
|
||||
CharSequence packageName = event.getPackageName();
|
||||
|
||||
if (packageNames.isEmpty() || packageNames.contains(packageName)) {
|
||||
int feedbackType = service.mFeedbackType;
|
||||
if ((handledFeedbackTypes & feedbackType) != feedbackType
|
||||
|| feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages services by starting enabled ones and stopping disabled ones.
|
||||
*/
|
||||
private void manageServicesLocked() {
|
||||
populateEnabledServicesLocked(mEnabledServices);
|
||||
updateServicesStateLocked(mInstalledServices, mEnabledServices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds all bound services.
|
||||
*/
|
||||
private void unbindAllServicesLocked() {
|
||||
List<Service> services = mServices;
|
||||
|
||||
for (int i = 0, count = services.size(); i < count; i++) {
|
||||
Service service = services.get(i);
|
||||
|
||||
service.unbind();
|
||||
mComponentNameToServiceMap.remove(service.mComponentName);
|
||||
}
|
||||
services.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates a list with the {@link ComponentName}s of all enabled
|
||||
* {@link AccessibilityService}s.
|
||||
*
|
||||
* @param enabledServices The list.
|
||||
*/
|
||||
private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
|
||||
enabledServices.clear();
|
||||
|
||||
String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
|
||||
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
|
||||
|
||||
if (servicesValue != null) {
|
||||
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
|
||||
splitter.setString(servicesValue);
|
||||
while (splitter.hasNext()) {
|
||||
ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
|
||||
enabledServices.add(enabledService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state of each service by starting (or keeping running) enabled ones and
|
||||
* stopping the rest.
|
||||
*
|
||||
* @param installedServices All installed {@link AccessibilityService}s.
|
||||
* @param enabledServices The {@link ComponentName}s of the enabled services.
|
||||
*/
|
||||
private void updateServicesStateLocked(List<ServiceInfo> installedServices,
|
||||
Set<ComponentName> enabledServices) {
|
||||
|
||||
Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
|
||||
List<Service> services = mServices;
|
||||
|
||||
for (int i = 0, count = installedServices.size(); i < count; i++) {
|
||||
ServiceInfo intalledService = installedServices.get(i);
|
||||
ComponentName componentName = new ComponentName(intalledService.packageName,
|
||||
intalledService.name);
|
||||
Service service = componentNameToServiceMap.get(componentName);
|
||||
|
||||
if (enabledServices.contains(componentName)) {
|
||||
if (service == null) {
|
||||
new Service(componentName).bind();
|
||||
}
|
||||
} else {
|
||||
if (service != null) {
|
||||
service.unbind();
|
||||
componentNameToServiceMap.remove(componentName);
|
||||
services.remove(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
|
||||
*/
|
||||
private void updateClientsLocked() {
|
||||
for (int i = 0, count = mClients.size(); i < count; i++) {
|
||||
try {
|
||||
mClients.get(i).setEnabled(mIsEnabled);
|
||||
} catch (RemoteException re) {
|
||||
mClients.remove(i);
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents an accessibility service. It stores all per service
|
||||
* data required for the service management, provides API for starting/stopping the
|
||||
* service and is responsible for adding/removing the service in the data structures
|
||||
* for service management. The class also exposes configuration interface that is
|
||||
* passed to the service it represents as soon it is bound. It also serves as the
|
||||
* connection for the service.
|
||||
*/
|
||||
class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
|
||||
int mId = 0;
|
||||
|
||||
IBinder mService;
|
||||
|
||||
IEventListener mServiceInterface;
|
||||
|
||||
int mEventTypes;
|
||||
|
||||
int mFeedbackType;
|
||||
|
||||
Set<String> mPackageNames = new HashSet<String>();
|
||||
|
||||
boolean mIsDefault;
|
||||
|
||||
long mNotificationTimeout;
|
||||
|
||||
boolean mIsActive;
|
||||
|
||||
ComponentName mComponentName;
|
||||
|
||||
Intent mIntent;
|
||||
|
||||
// the events pending events to be dispatched to this service
|
||||
final SparseArray<AccessibilityEvent> mPendingEvents =
|
||||
new SparseArray<AccessibilityEvent>();
|
||||
|
||||
Service(ComponentName componentName) {
|
||||
mId = sIdCounter++;
|
||||
mComponentName = componentName;
|
||||
mIntent = new Intent().setComponent(mComponentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds to the accessibility service.
|
||||
*/
|
||||
public void bind() {
|
||||
if (mService == null) {
|
||||
mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds form the accessibility service and removes it from the data
|
||||
* structures for service management.
|
||||
*/
|
||||
public void unbind() {
|
||||
if (mService != null) {
|
||||
mContext.unbindService(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the service is configured i.e. at least event types of interest
|
||||
* and feedback type must be set.
|
||||
*
|
||||
* @return True if the service is configured, false otherwise.
|
||||
*/
|
||||
public boolean isConfigured() {
|
||||
return (mEventTypes != 0 && mFeedbackType != 0);
|
||||
}
|
||||
|
||||
public void setServiceInfo(AccessibilityServiceInfo info) {
|
||||
mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
|
||||
}
|
||||
|
||||
public void onServiceConnected(ComponentName componentName, IBinder service) {
|
||||
mService = service;
|
||||
mServiceInterface = IEventListener.Stub.asInterface(service);
|
||||
|
||||
try {
|
||||
mServiceInterface.setConnection(this);
|
||||
synchronized (mLock) {
|
||||
if (!mServices.contains(this)) {
|
||||
mServices.add(this);
|
||||
mComponentNameToServiceMap.put(componentName, this);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
|
||||
}
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
synchronized (mLock) {
|
||||
Service service = mComponentNameToServiceMap.remove(componentName);
|
||||
mServices.remove(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import com.android.server.status.IconData;
|
||||
import com.android.server.status.NotificationData;
|
||||
import com.android.server.status.StatusBarService;
|
||||
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.INotificationManager;
|
||||
@@ -30,33 +34,29 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AsyncPlayer;
|
||||
import android.media.RingtoneManager;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Binder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Power;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.util.Config;
|
||||
import android.text.TextUtils;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.server.status.IconData;
|
||||
import com.android.server.status.NotificationData;
|
||||
import com.android.server.status.StatusBarService;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.io.IOException;
|
||||
|
||||
class NotificationManagerService extends INotificationManager.Stub
|
||||
{
|
||||
@@ -98,7 +98,7 @@ class NotificationManagerService extends INotificationManager.Stub
|
||||
private boolean mBatteryLow;
|
||||
private boolean mBatteryFull;
|
||||
private NotificationRecord mLedNotification;
|
||||
|
||||
|
||||
private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
|
||||
private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
|
||||
private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
|
||||
@@ -594,6 +594,9 @@ class NotificationManagerService extends INotificationManager.Stub
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
sendAccessibilityEventTypeNotificationChangedDoCheck(notification, pkg);
|
||||
|
||||
} else {
|
||||
if (old != null && old.statusBarKey != null) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
@@ -676,6 +679,26 @@ class NotificationManagerService extends INotificationManager.Stub
|
||||
idOut[0] = id;
|
||||
}
|
||||
|
||||
private void sendAccessibilityEventTypeNotificationChangedDoCheck(Notification notification,
|
||||
CharSequence packageName) {
|
||||
AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
|
||||
if (!manager.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccessibilityEvent event =
|
||||
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
|
||||
event.setPackageName(packageName);
|
||||
event.setClassName(Notification.class.getName());
|
||||
event.setParcelableData(notification);
|
||||
CharSequence tickerText = notification.tickerText;
|
||||
if (!TextUtils.isEmpty(tickerText)) {
|
||||
event.getText().add(tickerText);
|
||||
}
|
||||
|
||||
manager.sendAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
private void cancelNotificationLocked(NotificationRecord r) {
|
||||
// status bar
|
||||
if (r.notification.icon != 0) {
|
||||
|
||||
@@ -227,6 +227,14 @@ class ServerThread extends Thread {
|
||||
Log.e(TAG, "Failure starting Connectivity Service", e);
|
||||
}
|
||||
|
||||
try {
|
||||
Log.i(TAG, "Starting Accessibility Manager.");
|
||||
ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
|
||||
new AccessibilityManagerService(context));
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "Failure starting Accessibility Manager", e);
|
||||
}
|
||||
|
||||
try {
|
||||
Log.i(TAG, "Starting Notification Manager.");
|
||||
ServiceManager.addService(Context.NOTIFICATION_SERVICE,
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.HARDWARE_TEST" />
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_VIEW_TYPES" />
|
||||
<uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" />
|
||||
<uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" />
|
||||
|
||||
<application android:theme="@style/Theme">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
@@ -939,7 +942,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="android.widget.AutoCompleteTextViewSimple"
|
||||
<activity android:name="android.widget.AutoCompleteTextViewSimple"
|
||||
android:label="AutoCompleteTextViewSimple">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -947,6 +950,12 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name=".accessibility.AccessibilityTestService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accessibilityservice.AccessibilityService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.frameworktest.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Notification;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* This class text the accessibility framework end to end.
|
||||
* <p>
|
||||
* Note: Since accessibility is provided by {@link AccessibilityService}s we create one,
|
||||
* and it generates an event and an interruption dispatching them through the
|
||||
* {@link AccessibilityManager}. We verify the received result. To trigger the test
|
||||
* go to Settings->Accessibility and select the enable accessibility check and then
|
||||
* select the check for this service (same name as the class).
|
||||
*/
|
||||
public class AccessibilityTestService extends AccessibilityService {
|
||||
|
||||
private static final String LOG_TAG = "AccessibilityTestService";
|
||||
|
||||
private static final String CLASS_NAME = "foo.bar.baz.Test";
|
||||
private static final String PACKAGE_NAME = "foo.bar.baz";
|
||||
private static final String TEXT = "Some stuff";
|
||||
private static final String BEFORE_TEXT = "Some other stuff";
|
||||
|
||||
private static final String CONTENT_DESCRIPTION = "Content description";
|
||||
|
||||
private static final int ITEM_COUNT = 10;
|
||||
private static final int CURRENT_ITEM_INDEX = 1;
|
||||
private static final int INTERRUPT_INVOCATION_TYPE = 0x00000200;
|
||||
|
||||
private static final int FROM_INDEX = 1;
|
||||
private static final int ADDED_COUNT = 2;
|
||||
private static final int REMOVED_COUNT = 1;
|
||||
|
||||
private static final int NOTIFICATION_TIMEOUT_MILLIS = 80;
|
||||
|
||||
private int mReceivedResult;
|
||||
|
||||
private Timer mTimer = new Timer();
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
||||
info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
|
||||
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
|
||||
info.notificationTimeout = NOTIFICATION_TIMEOUT_MILLIS;
|
||||
info.flags &= AccessibilityServiceInfo.DEFAULT;
|
||||
setServiceInfo(info);
|
||||
|
||||
// we need to wait until the system picks our configuration
|
||||
// otherwise it will not notify us
|
||||
mTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
testAccessibilityEventDispatching();
|
||||
testInterrupt();
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error in testing Accessibility feature", e);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check here if the event we received is actually the one we sent.
|
||||
*/
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
assert(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED == event.getEventType());
|
||||
assert(event != null);
|
||||
assert(event.getEventTime() > 0);
|
||||
assert(CLASS_NAME.equals(event.getClassName()));
|
||||
assert(PACKAGE_NAME.equals(event.getPackageName()));
|
||||
assert(1 == event.getText().size());
|
||||
assert(TEXT.equals(event.getText().get(0)));
|
||||
assert(BEFORE_TEXT.equals(event.getBeforeText()));
|
||||
assert(event.isChecked());
|
||||
assert(CONTENT_DESCRIPTION.equals(event.getContentDescription()));
|
||||
assert(ITEM_COUNT == event.getItemCount());
|
||||
assert(CURRENT_ITEM_INDEX == event.getCurrentItemIndex());
|
||||
assert(event.isEnabled());
|
||||
assert(event.isPassword());
|
||||
assert(FROM_INDEX == event.getFromIndex());
|
||||
assert(ADDED_COUNT == event.getAddedCount());
|
||||
assert(REMOVED_COUNT == event.getRemovedCount());
|
||||
assert(event.getParcelableData() != null);
|
||||
assert(1 == ((Notification) event.getParcelableData()).icon);
|
||||
|
||||
// set the type of the receved request
|
||||
mReceivedResult = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flag that we received the interrupt request.
|
||||
*/
|
||||
@Override
|
||||
public void onInterrupt() {
|
||||
|
||||
// set the type of the receved request
|
||||
mReceivedResult = INTERRUPT_INVOCATION_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If an {@link AccessibilityEvent} is sent and received correctly.
|
||||
*/
|
||||
public void testAccessibilityEventDispatching() throws Exception {
|
||||
AccessibilityEvent event =
|
||||
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
|
||||
|
||||
assert(event != null);
|
||||
event.setClassName(CLASS_NAME);
|
||||
event.setPackageName(PACKAGE_NAME);
|
||||
event.getText().add(TEXT);
|
||||
event.setBeforeText(BEFORE_TEXT);
|
||||
event.setChecked(true);
|
||||
event.setContentDescription(CONTENT_DESCRIPTION);
|
||||
event.setItemCount(ITEM_COUNT);
|
||||
event.setCurrentItemIndex(CURRENT_ITEM_INDEX);
|
||||
event.setEnabled(true);
|
||||
event.setPassword(true);
|
||||
event.setFromIndex(FROM_INDEX);
|
||||
event.setAddedCount(ADDED_COUNT);
|
||||
event.setRemovedCount(REMOVED_COUNT);
|
||||
event.setParcelableData(new Notification(1, "Foo", 1234));
|
||||
|
||||
AccessibilityManager.getInstance(this).sendAccessibilityEvent(event);
|
||||
|
||||
assert(mReceivedResult == event.getEventType());
|
||||
|
||||
Log.i(LOG_TAG, "AccessibilityTestService#testAccessibilityEventDispatching: Success");
|
||||
}
|
||||
|
||||
/**
|
||||
* If accessibility feedback interruption is triggered and received correctly.
|
||||
*/
|
||||
public void testInterrupt() throws Exception {
|
||||
AccessibilityManager.getInstance(this).interrupt();
|
||||
|
||||
assert(INTERRUPT_INVOCATION_TYPE == mReceivedResult);
|
||||
|
||||
Log.i(LOG_TAG, "AccessibilityTestService#testInterrupt: Success");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (C) 2009 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.frameworktest.accessibility;
|
||||
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* This class exercises the caching and recycling of {@link AccessibilityEvent}s.
|
||||
*/
|
||||
public class RecycleAccessibilityEventTest extends TestCase {
|
||||
|
||||
private static final String CLASS_NAME = "foo.bar.baz.Test";
|
||||
private static final String PACKAGE_NAME = "foo.bar.baz";
|
||||
private static final String TEXT = "Some stuff";
|
||||
|
||||
private static final String CONTENT_DESCRIPTION = "Content description";
|
||||
private static final int ITEM_COUNT = 10;
|
||||
private static final int CURRENT_ITEM_INDEX = 1;
|
||||
|
||||
private static final int FROM_INDEX = 1;
|
||||
private static final int ADDED_COUNT = 2;
|
||||
private static final int REMOVED_COUNT = 1;
|
||||
|
||||
/**
|
||||
* If an {@link AccessibilityEvent} is marshaled/unmarshaled correctly
|
||||
*/
|
||||
@MediumTest
|
||||
public void testAccessibilityEventViewTextChangedType() {
|
||||
AccessibilityEvent first =
|
||||
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
|
||||
assertNotNull(first);
|
||||
|
||||
first.setClassName(CLASS_NAME);
|
||||
first.setPackageName(PACKAGE_NAME);
|
||||
first.getText().add(TEXT);
|
||||
first.setFromIndex(FROM_INDEX);
|
||||
first.setAddedCount(ADDED_COUNT);
|
||||
first.setRemovedCount(REMOVED_COUNT);
|
||||
first.setChecked(true);
|
||||
first.setContentDescription(CONTENT_DESCRIPTION);
|
||||
first.setItemCount(ITEM_COUNT);
|
||||
first.setCurrentItemIndex(CURRENT_ITEM_INDEX);
|
||||
first.setEnabled(true);
|
||||
first.setPassword(true);
|
||||
|
||||
first.recycle();
|
||||
|
||||
assertNotNull(first);
|
||||
assertNull(first.getClassName());
|
||||
assertNull(first.getPackageName());
|
||||
assertEquals(0, first.getText().size());
|
||||
assertFalse(first.isChecked());
|
||||
assertNull(first.getContentDescription());
|
||||
assertEquals(0, first.getItemCount());
|
||||
assertEquals(AccessibilityEvent.INVALID_POSITION, first.getCurrentItemIndex());
|
||||
assertFalse(first.isEnabled());
|
||||
assertFalse(first.isPassword());
|
||||
assertEquals(0, first.getFromIndex());
|
||||
assertEquals(0, first.getAddedCount());
|
||||
assertEquals(0, first.getRemovedCount());
|
||||
|
||||
// get another event from the pool (this must be the recycled first)
|
||||
AccessibilityEvent second = AccessibilityEvent.obtain();
|
||||
assertEquals(first, second);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user