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