Home | History | Annotate | Download | only in view
      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
     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          */
    104         public FocusObserver() {
    105             mHandler = new H();
    106         }
    107 
    108         /**
    109          * Called when one of the monitored windows gains input focus.
    110          */
    111         public abstract void onFocusGained(WindowId token);
    112 
    113         /**
    114          * Called when one of the monitored windows loses input focus.
    115          */
    116         public abstract void onFocusLost(WindowId token);
    117     }
    118 
    119     /**
    120      * Retrieve the current focus state of the associated window.
    121      */
    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      */
    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      */
    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
    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
    176     public int hashCode() {
    177         return mToken.asBinder().hashCode();
    178     }
    179 
    180     @Override
    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 
    191     public int describeContents() {
    192         return 0;
    193     }
    194 
    195     public void writeToParcel(Parcel out, int flags) {
    196         out.writeStrongBinder(mToken.asBinder());
    197     }
    198 
    199     public static final 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
    214     public IWindowId getTarget() {
    215         return mToken;
    216     }
    217 
    218     /** @hide */
    219     public WindowId(@NonNull IWindowId target) {
    220         mToken = target;
    221     }
    222 
    223     /** @hide */
    224     public WindowId(@NonNull IBinder target) {
    225         mToken = IWindowId.Stub.asInterface(target);
    226     }
    227 }
    228