1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Handler;
22 import android.os.IBinder;
23 import android.os.Message;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.RemoteException;
27 
28 import java.util.HashMap;
29 
30 /**
31  * Safe identifier for a window.  This currently allows you to retrieve and observe
32  * the input focus state of the window.  Most applications will
33  * not use this, instead relying on the simpler (and more efficient) methods available
34  * on {@link View}.  This classes is useful when window input interactions need to be
35  * done across processes: the class itself is a Parcelable that can be passed to other
36  * processes for them to interact with your window, and it provides a limited safe API
37  * that doesn't allow the other process to negatively harm your window.
38  */
39 public class WindowId implements Parcelable {
40     @NonNull
41     private final IWindowId mToken;
42 
43     /**
44      * Subclass for observing changes to the focus state of an {@link WindowId}.
45      * You should use the same instance of this class for observing multiple
46      * {@link WindowId} objects, since this class is fairly heavy-weight -- the
47      * base class includes all of the mechanisms for connecting to and receiving updates
48      * from the window.
49      */
50     public static abstract class FocusObserver {
51         final IWindowFocusObserver.Stub mIObserver = new IWindowFocusObserver.Stub() {
52 
53             @Override
54             public void focusGained(IBinder inputToken) {
55                 WindowId token;
56                 synchronized (mRegistrations) {
57                     token = mRegistrations.get(inputToken);
58                 }
59                 if (mHandler != null) {
60                     mHandler.sendMessage(mHandler.obtainMessage(1, token));
61                 } else {
62                     onFocusGained(token);
63                 }
64             }
65 
66             @Override
67             public void focusLost(IBinder inputToken) {
68                 WindowId token;
69                 synchronized (mRegistrations) {
70                     token = mRegistrations.get(inputToken);
71                 }
72                 if (mHandler != null) {
73                     mHandler.sendMessage(mHandler.obtainMessage(2, token));
74                 } else {
75                     onFocusLost(token);
76                 }
77             }
78         };
79 
80         final HashMap<IBinder, WindowId> mRegistrations = new HashMap<>();
81 
82         class H extends Handler {
83             @Override
handleMessage(Message msg)84             public void handleMessage(Message msg) {
85                 switch (msg.what) {
86                     case 1:
87                         onFocusGained((WindowId)msg.obj);
88                         break;
89                     case 2:
90                         onFocusLost((WindowId)msg.obj);
91                         break;
92                     default:
93                         super.handleMessage(msg);
94                 }
95             }
96         }
97 
98         final Handler mHandler;
99 
100         /**
101          * Construct a new observer.  This observer will be configured so that all
102          * of its callbacks are dispatched on the current calling thread.
103          */
FocusObserver()104         public FocusObserver() {
105             mHandler = new H();
106         }
107 
108         /**
109          * Called when one of the monitored windows gains input focus.
110          */
onFocusGained(WindowId token)111         public abstract void onFocusGained(WindowId token);
112 
113         /**
114          * Called when one of the monitored windows loses input focus.
115          */
onFocusLost(WindowId token)116         public abstract void onFocusLost(WindowId token);
117     }
118 
119     /**
120      * Retrieve the current focus state of the associated window.
121      */
isFocused()122     public boolean isFocused() {
123         try {
124             return mToken.isFocused();
125         } catch (RemoteException e) {
126             return false;
127         }
128     }
129 
130     /**
131      * Start monitoring for changes in the focus state of the window.
132      */
registerFocusObserver(FocusObserver observer)133     public void registerFocusObserver(FocusObserver observer) {
134         synchronized (observer.mRegistrations) {
135             if (observer.mRegistrations.containsKey(mToken.asBinder())) {
136                 throw new IllegalStateException(
137                         "Focus observer already registered with input token");
138             }
139             observer.mRegistrations.put(mToken.asBinder(), this);
140             try {
141                 mToken.registerFocusObserver(observer.mIObserver);
142             } catch (RemoteException e) {
143             }
144         }
145     }
146 
147     /**
148      * Stop monitoring changes in the focus state of the window.
149      */
unregisterFocusObserver(FocusObserver observer)150     public void unregisterFocusObserver(FocusObserver observer) {
151         synchronized (observer.mRegistrations) {
152             if (observer.mRegistrations.remove(mToken.asBinder()) == null) {
153                 throw new IllegalStateException("Focus observer not registered with input token");
154             }
155             try {
156                 mToken.unregisterFocusObserver(observer.mIObserver);
157             } catch (RemoteException e) {
158             }
159         }
160     }
161 
162     /**
163      * Comparison operator on two IntentSender objects, such that true
164      * is returned then they both represent the same operation from the
165      * same package.
166      */
167     @Override
equals(@ullable Object otherObj)168     public boolean equals(@Nullable Object otherObj) {
169         if (otherObj instanceof WindowId) {
170             return mToken.asBinder().equals(((WindowId) otherObj).mToken.asBinder());
171         }
172         return false;
173     }
174 
175     @Override
hashCode()176     public int hashCode() {
177         return mToken.asBinder().hashCode();
178     }
179 
180     @Override
toString()181     public String toString() {
182         StringBuilder sb = new StringBuilder(128);
183         sb.append("IntentSender{");
184         sb.append(Integer.toHexString(System.identityHashCode(this)));
185         sb.append(": ");
186         sb.append(mToken.asBinder());
187         sb.append('}');
188         return sb.toString();
189     }
190 
describeContents()191     public int describeContents() {
192         return 0;
193     }
194 
writeToParcel(Parcel out, int flags)195     public void writeToParcel(Parcel out, int flags) {
196         out.writeStrongBinder(mToken.asBinder());
197     }
198 
199     public static final @android.annotation.NonNull Parcelable.Creator<WindowId> CREATOR = new Parcelable.Creator<WindowId>() {
200         @Override
201         public WindowId createFromParcel(Parcel in) {
202             IBinder target = in.readStrongBinder();
203             return target != null ? new WindowId(target) : null;
204         }
205 
206         @Override
207         public WindowId[] newArray(int size) {
208             return new WindowId[size];
209         }
210     };
211 
212     /** @hide */
213     @NonNull
getTarget()214     public IWindowId getTarget() {
215         return mToken;
216     }
217 
218     /** @hide */
WindowId(@onNull IWindowId target)219     public WindowId(@NonNull IWindowId target) {
220         mToken = target;
221     }
222 
223     /** @hide */
WindowId(@onNull IBinder target)224     public WindowId(@NonNull IBinder target) {
225         mToken = IWindowId.Stub.asInterface(target);
226     }
227 }
228