Home | History | Annotate | Download | only in app
      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 
     17 package android.app;
     18 
     19 import android.content.Context;
     20 import android.content.ContextWrapper;
     21 import android.content.IIntentSender;
     22 import android.content.Intent;
     23 import android.content.IntentSender;
     24 import android.graphics.SurfaceTexture;
     25 import android.os.IBinder;
     26 import android.os.RemoteException;
     27 import android.util.AttributeSet;
     28 import android.util.DisplayMetrics;
     29 import android.util.Log;
     30 import android.view.InputDevice;
     31 import android.view.InputEvent;
     32 import android.view.MotionEvent;
     33 import android.view.Surface;
     34 import android.view.TextureView;
     35 import android.view.TextureView.SurfaceTextureListener;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.view.WindowManager;
     39 import dalvik.system.CloseGuard;
     40 
     41 import java.lang.ref.WeakReference;
     42 
     43 /** @hide */
     44 public 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