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