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
17package android.view;
18
19import android.os.Handler;
20import android.os.IBinder;
21import android.os.Message;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.os.RemoteException;
25
26import 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 */
37public 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
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         */
102        public FocusObserver() {
103            mHandler = new H();
104        }
105
106        /**
107         * Called when one of the monitored windows gains input focus.
108         */
109        public abstract void onFocusGained(WindowId token);
110
111        /**
112         * Called when one of the monitored windows loses input focus.
113         */
114        public abstract void onFocusLost(WindowId token);
115    }
116
117    /**
118     * Retrieve the current focus state of the associated window.
119     */
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     */
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     */
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
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
175    public int hashCode() {
176        return mToken.asBinder().hashCode();
177    }
178
179    @Override
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
190    public int describeContents() {
191        return 0;
192    }
193
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 */
211    public IWindowId getTarget() {
212        return mToken;
213    }
214
215    /** @hide */
216    public WindowId(IWindowId target) {
217        mToken = target;
218    }
219
220    /** @hide */
221    public WindowId(IBinder target) {
222        mToken = IWindowId.Stub.asInterface(target);
223    }
224}
225