1/*
2 * Copyright (C) 2013 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.app;
18
19import static android.app.ActivityManager.START_CANCELED;
20
21import android.content.Context;
22import android.content.ContextWrapper;
23import android.content.IIntentSender;
24import android.content.Intent;
25import android.content.IntentSender;
26import android.graphics.SurfaceTexture;
27import android.os.Handler;
28import android.os.HandlerThread;
29import android.os.IBinder;
30import android.os.Message;
31import android.os.OperationCanceledException;
32import android.os.RemoteException;
33import android.util.AttributeSet;
34import android.util.DisplayMetrics;
35import android.util.Log;
36import android.view.InputDevice;
37import android.view.InputEvent;
38import android.view.MotionEvent;
39import android.view.Surface;
40import android.view.TextureView;
41import android.view.TextureView.SurfaceTextureListener;
42import android.view.View;
43import android.view.ViewGroup;
44import android.view.WindowManager;
45import dalvik.system.CloseGuard;
46
47import java.lang.ref.WeakReference;
48
49/** @hide */
50public class ActivityView extends ViewGroup {
51    private static final String TAG = "ActivityView";
52    private static final boolean DEBUG = false;
53
54    private static final int MSG_SET_SURFACE = 1;
55
56    DisplayMetrics mMetrics = new DisplayMetrics();
57    private final TextureView mTextureView;
58    private ActivityContainerWrapper mActivityContainer;
59    private Activity mActivity;
60    private int mWidth;
61    private int mHeight;
62    private Surface mSurface;
63    private int mLastVisibility;
64    private ActivityViewCallback mActivityViewCallback;
65
66    private HandlerThread mThread = new HandlerThread("ActivityViewThread");
67    private Handler mHandler;
68
69    public ActivityView(Context context) {
70        this(context, null);
71    }
72
73    public ActivityView(Context context, AttributeSet attrs) {
74        this(context, attrs, 0);
75    }
76
77    public ActivityView(Context context, AttributeSet attrs, int defStyle) {
78        super(context, attrs, defStyle);
79
80        while (context instanceof ContextWrapper) {
81            if (context instanceof Activity) {
82                mActivity = (Activity)context;
83                break;
84            }
85            context = ((ContextWrapper)context).getBaseContext();
86        }
87        if (mActivity == null) {
88            throw new IllegalStateException("The ActivityView's Context is not an Activity.");
89        }
90
91        try {
92            mActivityContainer = new ActivityContainerWrapper(
93                    ActivityManagerNative.getDefault().createVirtualActivityContainer(
94                            mActivity.getActivityToken(), new ActivityContainerCallback(this)));
95        } catch (RemoteException e) {
96            throw new RuntimeException("ActivityView: Unable to create ActivityContainer. "
97                    + e);
98        }
99
100        mThread.start();
101        mHandler = new Handler(mThread.getLooper()) {
102            @Override
103            public void handleMessage(Message msg) {
104                super.handleMessage(msg);
105                if (msg.what == MSG_SET_SURFACE) {
106                    try {
107                        mActivityContainer.setSurface((Surface) msg.obj, msg.arg1, msg.arg2,
108                                mMetrics.densityDpi);
109                    } catch (RemoteException e) {
110                        throw new RuntimeException(
111                                "ActivityView: Unable to set surface of ActivityContainer. " + e);
112                    }
113                }
114            }
115        };
116        mTextureView = new TextureView(context);
117        mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
118        addView(mTextureView);
119
120        WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
121        wm.getDefaultDisplay().getMetrics(mMetrics);
122
123        mLastVisibility = getVisibility();
124
125        if (DEBUG) Log.v(TAG, "ctor()");
126    }
127
128    @Override
129    protected void onLayout(boolean changed, int l, int t, int r, int b) {
130        mTextureView.layout(0, 0, r - l, b - t);
131    }
132
133    @Override
134    protected void onVisibilityChanged(View changedView, int visibility) {
135        super.onVisibilityChanged(changedView, visibility);
136
137        if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) {
138            Message msg = Message.obtain(mHandler, MSG_SET_SURFACE);
139            msg.obj = (visibility == View.GONE) ? null : mSurface;
140            msg.arg1 = mWidth;
141            msg.arg2 = mHeight;
142            mHandler.sendMessage(msg);
143        }
144        mLastVisibility = visibility;
145    }
146
147    private boolean injectInputEvent(InputEvent event) {
148        return mActivityContainer != null && mActivityContainer.injectEvent(event);
149    }
150
151    @Override
152    public boolean onTouchEvent(MotionEvent event) {
153        return injectInputEvent(event) || super.onTouchEvent(event);
154    }
155
156    @Override
157    public boolean onGenericMotionEvent(MotionEvent event) {
158        if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
159            if (injectInputEvent(event)) {
160                return true;
161            }
162        }
163        return super.onGenericMotionEvent(event);
164    }
165
166    @Override
167    public void onAttachedToWindow() {
168        if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer +
169                " mSurface=" + mSurface);
170    }
171
172    @Override
173    public void onDetachedFromWindow() {
174        if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer +
175                " mSurface=" + mSurface);
176    }
177
178    public boolean isAttachedToDisplay() {
179        return mSurface != null;
180    }
181
182    public void startActivity(Intent intent) {
183        if (mActivityContainer == null) {
184            throw new IllegalStateException("Attempt to call startActivity after release");
185        }
186        if (mSurface == null) {
187            throw new IllegalStateException("Surface not yet created.");
188        }
189        if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " +
190                (isAttachedToDisplay() ? "" : "not") + " attached");
191        if (mActivityContainer.startActivity(intent) == START_CANCELED) {
192            throw new OperationCanceledException();
193        }
194    }
195
196    public void startActivity(IntentSender intentSender) {
197        if (mActivityContainer == null) {
198            throw new IllegalStateException("Attempt to call startActivity after release");
199        }
200        if (mSurface == null) {
201            throw new IllegalStateException("Surface not yet created.");
202        }
203        if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " +
204                (isAttachedToDisplay() ? "" : "not") + " attached");
205        final IIntentSender iIntentSender = intentSender.getTarget();
206        if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) {
207            throw new OperationCanceledException();
208        }
209    }
210
211    public void startActivity(PendingIntent pendingIntent) {
212        if (mActivityContainer == null) {
213            throw new IllegalStateException("Attempt to call startActivity after release");
214        }
215        if (mSurface == null) {
216            throw new IllegalStateException("Surface not yet created.");
217        }
218        if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " "
219                + (isAttachedToDisplay() ? "" : "not") + " attached");
220        final IIntentSender iIntentSender = pendingIntent.getTarget();
221        if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) {
222            throw new OperationCanceledException();
223        }
224    }
225
226    public void release() {
227        if (DEBUG) Log.v(TAG, "release() mActivityContainer=" + mActivityContainer +
228                " mSurface=" + mSurface);
229        if (mActivityContainer == null) {
230            Log.e(TAG, "Duplicate call to release");
231            return;
232        }
233        mActivityContainer.release();
234        mActivityContainer = null;
235
236        if (mSurface != null) {
237            mSurface.release();
238            mSurface = null;
239        }
240
241        mTextureView.setSurfaceTextureListener(null);
242    }
243
244    private void attachToSurfaceWhenReady() {
245        final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
246        if (surfaceTexture == null || mSurface != null) {
247            // Either not ready to attach, or already attached.
248            return;
249        }
250
251        mSurface = new Surface(surfaceTexture);
252        try {
253            mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
254        } catch (RemoteException e) {
255            mSurface.release();
256            mSurface = null;
257            throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e);
258        }
259    }
260
261    /**
262     * Set the callback to use to report certain state changes.
263     *
264     * Note: If the surface has been created prior to this call being made, then
265     * ActivityViewCallback.onSurfaceAvailable will be called from within setCallback.
266     *
267     *  @param callback The callback to report events to.
268     *
269     * @see ActivityViewCallback
270     */
271    public void setCallback(ActivityViewCallback callback) {
272        mActivityViewCallback = callback;
273
274        if (mSurface != null) {
275            mActivityViewCallback.onSurfaceAvailable(this);
276        }
277    }
278
279    public static abstract class ActivityViewCallback {
280        /**
281         * Called when all activities in the ActivityView have completed and been removed. Register
282         * using {@link ActivityView#setCallback(ActivityViewCallback)}. Each ActivityView may
283         * have at most one callback registered.
284         */
285        public abstract void onAllActivitiesComplete(ActivityView view);
286        /**
287         * Called when the surface is ready to be drawn to. Calling startActivity prior to this
288         * callback will result in an IllegalStateException.
289         */
290        public abstract void onSurfaceAvailable(ActivityView view);
291        /**
292         * Called when the surface has been removed. Calling startActivity after this callback
293         * will result in an IllegalStateException.
294         */
295        public abstract void onSurfaceDestroyed(ActivityView view);
296    }
297
298    private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
299        @Override
300        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
301                int height) {
302            if (mActivityContainer == null) {
303                return;
304            }
305            if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height="
306                    + height);
307            mWidth = width;
308            mHeight = height;
309            attachToSurfaceWhenReady();
310            if (mActivityViewCallback != null) {
311                mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
312            }
313        }
314
315        @Override
316        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
317                int height) {
318            if (mActivityContainer == null) {
319                return;
320            }
321            if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height);
322        }
323
324        @Override
325        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
326            if (mActivityContainer == null) {
327                return true;
328            }
329            if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
330            mSurface.release();
331            mSurface = null;
332            try {
333                mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
334            } catch (RemoteException e) {
335                throw new RuntimeException(
336                        "ActivityView: Unable to set surface of ActivityContainer. " + e);
337            }
338            if (mActivityViewCallback != null) {
339                mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
340            }
341            return true;
342        }
343
344        @Override
345        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
346//            Log.d(TAG, "onSurfaceTextureUpdated");
347        }
348
349    }
350
351    private static class ActivityContainerCallback extends IActivityContainerCallback.Stub {
352        private final WeakReference<ActivityView> mActivityViewWeakReference;
353
354        ActivityContainerCallback(ActivityView activityView) {
355            mActivityViewWeakReference = new WeakReference<>(activityView);
356        }
357
358        @Override
359        public void setVisible(IBinder container, boolean visible) {
360            if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible +
361                    " ActivityView=" + mActivityViewWeakReference.get());
362        }
363
364        @Override
365        public void onAllActivitiesComplete(IBinder container) {
366            final ActivityView activityView = mActivityViewWeakReference.get();
367            if (activityView != null) {
368                final ActivityViewCallback callback = activityView.mActivityViewCallback;
369                if (callback != null) {
370                    final WeakReference<ActivityViewCallback> callbackRef =
371                            new WeakReference<>(callback);
372                    activityView.post(new Runnable() {
373                        @Override
374                        public void run() {
375                            ActivityViewCallback callback = callbackRef.get();
376                            if (callback != null) {
377                                callback.onAllActivitiesComplete(activityView);
378                            }
379                        }
380                    });
381                }
382            }
383        }
384    }
385
386    private static class ActivityContainerWrapper {
387        private final IActivityContainer mIActivityContainer;
388        private final CloseGuard mGuard = CloseGuard.get();
389        boolean mOpened; // Protected by mGuard.
390
391        ActivityContainerWrapper(IActivityContainer container) {
392            mIActivityContainer = container;
393            mOpened = true;
394            mGuard.open("release");
395        }
396
397        void attachToDisplay(int displayId) {
398            try {
399                mIActivityContainer.attachToDisplay(displayId);
400            } catch (RemoteException e) {
401            }
402        }
403
404        void setSurface(Surface surface, int width, int height, int density)
405                throws RemoteException {
406            mIActivityContainer.setSurface(surface, width, height, density);
407        }
408
409        int startActivity(Intent intent) {
410            try {
411                return mIActivityContainer.startActivity(intent);
412            } catch (RemoteException e) {
413                throw new RuntimeException("ActivityView: Unable to startActivity. " + e);
414            }
415        }
416
417        int startActivityIntentSender(IIntentSender intentSender) {
418            try {
419                return mIActivityContainer.startActivityIntentSender(intentSender);
420            } catch (RemoteException e) {
421                throw new RuntimeException(
422                        "ActivityView: Unable to startActivity from IntentSender. " + e);
423            }
424        }
425
426        int getDisplayId() {
427            try {
428                return mIActivityContainer.getDisplayId();
429            } catch (RemoteException e) {
430                return -1;
431            }
432        }
433
434        boolean injectEvent(InputEvent event) {
435            try {
436                return mIActivityContainer.injectEvent(event);
437            } catch (RemoteException e) {
438                return false;
439            }
440        }
441
442        void release() {
443            synchronized (mGuard) {
444                if (mOpened) {
445                    if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
446                    try {
447                        mIActivityContainer.release();
448                        mGuard.close();
449                    } catch (RemoteException e) {
450                    }
451                    mOpened = false;
452                }
453            }
454        }
455
456        @Override
457        protected void finalize() throws Throwable {
458            if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called");
459            try {
460                if (mGuard != null) {
461                    mGuard.warnIfOpen();
462                    release();
463                }
464            } finally {
465                super.finalize();
466            }
467        }
468
469    }
470}
471