blob: 26d340542e7adfc577ceed887bb3d5c86221e735 [file] [log] [blame]
/*
* Copyright (C) 2006 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import java.util.HashMap;
/**
* Safe identifier for a window. This currently allows you to retrieve and observe
* the input focus state of the window. Most applications will
* not use this, instead relying on the simpler (and more efficient) methods available
* on {@link View}. This classes is useful when window input interactions need to be
* done across processes: the class itself is a Parcelable that can be passed to other
* processes for them to interact with your window, and it provides a limited safe API
* that doesn't allow the other process to negatively harm your window.
*/
public class WindowId implements Parcelable {
@NonNull
private final IWindowId mToken;
/**
* Subclass for observing changes to the focus state of an {@link WindowId}.
* You should use the same instance of this class for observing multiple
* {@link WindowId} objects, since this class is fairly heavy-weight -- the
* base class includes all of the mechanisms for connecting to and receiving updates
* from the window.
*/
public static abstract class FocusObserver {
final IWindowFocusObserver.Stub mIObserver = new IWindowFocusObserver.Stub() {
@Override
public void focusGained(IBinder inputToken) {
WindowId token;
synchronized (mRegistrations) {
token = mRegistrations.get(inputToken);
}
if (mHandler != null) {
mHandler.sendMessage(mHandler.obtainMessage(1, token));
} else {
onFocusGained(token);
}
}
@Override
public void focusLost(IBinder inputToken) {
WindowId token;
synchronized (mRegistrations) {
token = mRegistrations.get(inputToken);
}
if (mHandler != null) {
mHandler.sendMessage(mHandler.obtainMessage(2, token));
} else {
onFocusLost(token);
}
}
};
final HashMap<IBinder, WindowId> mRegistrations = new HashMap<>();
class H extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
onFocusGained((WindowId)msg.obj);
break;
case 2:
onFocusLost((WindowId)msg.obj);
break;
default:
super.handleMessage(msg);
}
}
}
final Handler mHandler;
/**
* Construct a new observer. This observer will be configured so that all
* of its callbacks are dispatched on the current calling thread.
*/
public FocusObserver() {
mHandler = new H();
}
/**
* Called when one of the monitored windows gains input focus.
*/
public abstract void onFocusGained(WindowId token);
/**
* Called when one of the monitored windows loses input focus.
*/
public abstract void onFocusLost(WindowId token);
}
/**
* Retrieve the current focus state of the associated window.
*/
public boolean isFocused() {
try {
return mToken.isFocused();
} catch (RemoteException e) {
return false;
}
}
/**
* Start monitoring for changes in the focus state of the window.
*/
public void registerFocusObserver(FocusObserver observer) {
synchronized (observer.mRegistrations) {
if (observer.mRegistrations.containsKey(mToken.asBinder())) {
throw new IllegalStateException(
"Focus observer already registered with input token");
}
observer.mRegistrations.put(mToken.asBinder(), this);
try {
mToken.registerFocusObserver(observer.mIObserver);
} catch (RemoteException e) {
}
}
}
/**
* Stop monitoring changes in the focus state of the window.
*/
public void unregisterFocusObserver(FocusObserver observer) {
synchronized (observer.mRegistrations) {
if (observer.mRegistrations.remove(mToken.asBinder()) == null) {
throw new IllegalStateException("Focus observer not registered with input token");
}
try {
mToken.unregisterFocusObserver(observer.mIObserver);
} catch (RemoteException e) {
}
}
}
/**
* Comparison operator on two IntentSender objects, such that true
* is returned then they both represent the same operation from the
* same package.
*/
@Override
public boolean equals(@Nullable Object otherObj) {
if (otherObj instanceof WindowId) {
return mToken.asBinder().equals(((WindowId) otherObj).mToken.asBinder());
}
return false;
}
@Override
public int hashCode() {
return mToken.asBinder().hashCode();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("IntentSender{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(": ");
sb.append(mToken.asBinder());
sb.append('}');
return sb.toString();
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeStrongBinder(mToken.asBinder());
}
public static final @android.annotation.NonNull Parcelable.Creator<WindowId> CREATOR = new Parcelable.Creator<WindowId>() {
@Override
public WindowId createFromParcel(Parcel in) {
IBinder target = in.readStrongBinder();
return target != null ? new WindowId(target) : null;
}
@Override
public WindowId[] newArray(int size) {
return new WindowId[size];
}
};
/** @hide */
@NonNull
public IWindowId getTarget() {
return mToken;
}
/** @hide */
public WindowId(@NonNull IWindowId target) {
mToken = target;
}
/** @hide */
public WindowId(@NonNull IBinder target) {
mToken = IWindowId.Stub.asInterface(target);
}
}