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