OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.content.browser.androidoverlay; |
| 6 |
| 7 import android.app.Dialog; |
| 8 import android.content.Context; |
| 9 import android.os.IBinder; |
| 10 import android.view.Surface; |
| 11 |
| 12 import org.chromium.base.ContextUtils; |
| 13 import org.chromium.gfx.mojom.Rect; |
| 14 import org.chromium.media.mojom.AndroidOverlay; |
| 15 import org.chromium.media.mojom.AndroidOverlayClient; |
| 16 import org.chromium.media.mojom.AndroidOverlayConfig; |
| 17 import org.chromium.mojo.system.MojoException; |
| 18 |
| 19 /** |
| 20 * Default AndroidOverlay impl. Uses a separate (shared) overlay-ui thread to o
wn a Dialog |
| 21 * instance, probably via a separate object that operates only on that thread.
We will post |
| 22 * messages to / from that thread from the main thread. |
| 23 */ |
| 24 public class DialogOverlayImpl implements AndroidOverlay, DialogOverlayCore.Host
, |
| 25 DialogOverlayOperations.WindowTokenLis
tener { |
| 26 private static final String TAG = "DialogOverlayImpl"; |
| 27 |
| 28 private AndroidOverlayClient mClient; |
| 29 private DialogOverlayOperations mOps; |
| 30 private DialogOverlayCore mDialogCore; |
| 31 private ThreadHoppingHost mHoppingHost; |
| 32 |
| 33 // If nonzero, then we have registered a surface with this ID. |
| 34 private int mSurfaceId; |
| 35 |
| 36 // Are we currently registered with |mOps| to receive token updates? |
| 37 private boolean mRegisteredForToken; |
| 38 |
| 39 /** |
| 40 * @param client Mojo client interface. |
| 41 * @param config initial overlay configuration. |
| 42 * @param handler handler that posts to the overlay-ui thread. This is the
android UI thread |
| 43 * that the dialog uses, not the browser UI thread. |
| 44 * @param provider the overlay provider that owns us. |
| 45 */ |
| 46 public DialogOverlayImpl(AndroidOverlayClient client, final AndroidOverlayCo
nfig config, |
| 47 DialogOverlayOperations ops) { |
| 48 mClient = client; |
| 49 mOps = ops; |
| 50 mDialogCore = new DialogOverlayCore(); |
| 51 mHoppingHost = new ThreadHoppingHost(this); |
| 52 |
| 53 // Post init to the proper (overlay-ui) thread. |
| 54 final DialogOverlayCore dialogCore = mDialogCore; |
| 55 final Context context = ContextUtils.getApplicationContext(); |
| 56 mOps.getOverlayUiHandler().post(new Runnable() { |
| 57 @Override |
| 58 public void run() { |
| 59 Dialog dialog = mOps.createDialog(context); |
| 60 dialogCore.initialize(dialog, config, mHoppingHost); |
| 61 } |
| 62 }); |
| 63 |
| 64 // Register to get token updates. |
| 65 mOps.registerWindowTokenListener(config.routingToken, this); |
| 66 mRegisteredForToken = true; |
| 67 } |
| 68 |
| 69 @Override |
| 70 public void onSurfaceReady(Surface surface) { |
| 71 if (mDialogCore == null || mClient == null) return; |
| 72 |
| 73 mSurfaceId = mOps.registerSurface(surface); |
| 74 mClient.onSurfaceReady(mSurfaceId); |
| 75 } |
| 76 |
| 77 @Override |
| 78 public void onOverlayDestroyed() { |
| 79 if (mDialogCore == null) return; |
| 80 |
| 81 // Notify the client that the overlay is gone. |
| 82 if (mClient != null) mClient.onDestroyed(); |
| 83 |
| 84 // Also clear out |mDialogCore| to prevent us from sending useless messa
ges to it. Note |
| 85 // that we might have already sent useless messages to it, and it should
be robust against |
| 86 // that sort of thing. |
| 87 cleanup(); |
| 88 |
| 89 // Note that we don't notify |mOps| yet, though we could. We wait for t
he client to close |
| 90 // their connection first. |
| 91 } |
| 92 |
| 93 // Due to threading issues, |mHoppingHost| doesn't forward this. |
| 94 @Override |
| 95 public void waitForCleanup() { |
| 96 assert false : "Not reached"; |
| 97 } |
| 98 |
| 99 // Client is done with this overlay. |
| 100 @Override |
| 101 public void close() { |
| 102 // TODO(liberato): verify that this actually works, else add an explicit
shutdown and hope |
| 103 // that the client calls it. |
| 104 |
| 105 // Allow surfaceDestroyed to proceed, if it's waiting. |
| 106 mHoppingHost.onCleanup(); |
| 107 |
| 108 // Notify |mDialogCore| that it has been released. This might not be ca
lled if it notifies |
| 109 // us that it's been destroyed. We still might send it in that case if
the client closes |
| 110 // the connection before we find out that it's been destroyed on the ove
rlay-ui thread. |
| 111 if (mDialogCore != null) { |
| 112 final DialogOverlayCore dialogCore = mDialogCore; |
| 113 mOps.getOverlayUiHandler().post(new Runnable() { |
| 114 @Override |
| 115 public void run() { |
| 116 dialogCore.release(); |
| 117 } |
| 118 }); |
| 119 |
| 120 // Note that we might get messagaes from |mDialogCore| after this, s
ince they might be |
| 121 // dispatched before |r| arrives. Clearing |mDialogCore| causes us
to ignore them. |
| 122 cleanup(); |
| 123 } |
| 124 |
| 125 // Notify the provider that we've been released by the client. Note tha
t the surface might |
| 126 // not have been destroyed yet, but that's okay. We could wait for a ca
llback from the |
| 127 // dialog core before proceeding, but this makes it easier for the clien
t to destroy and |
| 128 // re-create an overlay without worrying about an intermittent failure d
ue to having too |
| 129 // many overlays open at once. |
| 130 mOps.notifyReleased(); |
| 131 } |
| 132 |
| 133 private void sendWindowTokenToCore(final IBinder token) { |
| 134 final DialogOverlayCore dialogCore = mDialogCore; |
| 135 mOps.getOverlayUiHandler().post(new Runnable() { |
| 136 @Override |
| 137 public void run() { |
| 138 dialogCore.onWindowToken(token); |
| 139 } |
| 140 }); |
| 141 } |
| 142 |
| 143 @Override |
| 144 public void onWindowToken(final IBinder token) { |
| 145 if (mDialogCore == null) return; |
| 146 |
| 147 // Forward this change. |
| 148 // Note that if we don't have a window token, then we could wait until w
e do, simply by |
| 149 // skipping sending null if we haven't sent any non-null token yet. If
we're transitioning |
| 150 // between windows, that might make the client's job easier. It wouldn't
have to guess when |
| 151 // a new token is available. |
| 152 sendWindowTokenToCore(token); |
| 153 } |
| 154 |
| 155 @Override |
| 156 public void onDismissed() { |
| 157 // Notify the client that the overlay is going away. |
| 158 if (mClient != null) mClient.onDestroyed(); |
| 159 |
| 160 // Notify |mDialogCore| that it lost the token, if it had one. |
| 161 sendWindowTokenToCore(null); |
| 162 |
| 163 cleanup(); |
| 164 } |
| 165 |
| 166 @Override |
| 167 public void onConnectionError(MojoException e) { |
| 168 close(); |
| 169 } |
| 170 |
| 171 @Override |
| 172 public void scheduleLayout(final Rect rect) { |
| 173 if (mDialogCore == null) return; |
| 174 |
| 175 final DialogOverlayCore dialogCore = mDialogCore; |
| 176 mOps.getOverlayUiHandler().post(new Runnable() { |
| 177 @Override |
| 178 public void run() { |
| 179 dialogCore.layoutSurface(rect); |
| 180 } |
| 181 }); |
| 182 } |
| 183 |
| 184 /** |
| 185 * Unregister for callbacks, unregister any surface that we have, and forget
about |
| 186 * |mDialogCore|. Multiple calls are okay. |
| 187 */ |
| 188 private void cleanup() { |
| 189 if (mSurfaceId != 0) { |
| 190 mOps.unregisterSurface(mSurfaceId); |
| 191 mSurfaceId = 0; |
| 192 } |
| 193 |
| 194 if (mRegisteredForToken) { |
| 195 mOps.unregisterWindowTokenListener(); |
| 196 mRegisteredForToken = false; |
| 197 } |
| 198 |
| 199 // Also clear out |mDialogCore| to prevent us from sending useless messa
ges to it. Note |
| 200 // that we might have already sent useless messages to it, and it should
be robust against |
| 201 // that sort of thing. |
| 202 if (mDialogCore != null) mDialogCore = null; |
| 203 |
| 204 // If we wanted to send any message to |mClient|, we should have done so
already. |
| 205 mClient = null; |
| 206 } |
| 207 } |
OLD | NEW |