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