summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/RemoteAccessibilityController.java168
-rw-r--r--core/java/android/view/SurfaceView.java347
2 files changed, 298 insertions, 217 deletions
diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java
new file mode 100644
index 000000000000..bc0fab1bcf8d
--- /dev/null
+++ b/core/java/android/view/RemoteAccessibilityController.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Matrix;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
+
+class RemoteAccessibilityController {
+ private static final String TAG = "RemoteAccessibilityController";
+ private int mHostId;
+ private RemoteAccessibilityEmbeddedConnection mConnectionWrapper;
+ private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+ private final float[] mMatrixValues = new float[9];
+ private View mHostView;
+
+ RemoteAccessibilityController(View v) {
+ mHostView = v;
+ }
+
+ private void runOnUiThread(Runnable runnable) {
+ final Handler h = mHostView.getHandler();
+ if (h != null && h.getLooper() != Looper.myLooper()) {
+ h.post(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ void assosciateHierarchy(IAccessibilityEmbeddedConnection connection,
+ IBinder leashToken, int hostId) {
+ mHostId = hostId;
+
+ try {
+ leashToken = connection.associateEmbeddedHierarchy(
+ leashToken, mHostId);
+ setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error in associateEmbeddedHierarchy " + e);
+ }
+ }
+
+ void disassosciateHierarchy() {
+ setRemoteAccessibilityEmbeddedConnection(null, null);
+ }
+
+ boolean alreadyAssociated(IAccessibilityEmbeddedConnection connection) {
+ if (mConnectionWrapper == null) {
+ return false;
+ }
+ return mConnectionWrapper.mConnection.equals(connection);
+ }
+
+ boolean connected() {
+ return mConnectionWrapper != null;
+ }
+
+ IBinder getLeashToken() {
+ return mConnectionWrapper.getLeashToken();
+ }
+
+ /**
+ * Wrapper of accessibility embedded connection for embedded view hierarchy.
+ */
+ private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+ private final IAccessibilityEmbeddedConnection mConnection;
+ private final IBinder mLeashToken;
+
+ RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+ IBinder leashToken) {
+ mConnection = connection;
+ mLeashToken = leashToken;
+ }
+
+ IAccessibilityEmbeddedConnection getConnection() {
+ return mConnection;
+ }
+
+ IBinder getLeashToken() {
+ return mLeashToken;
+ }
+
+ void linkToDeath() throws RemoteException {
+ mConnection.asBinder().linkToDeath(this, 0);
+ }
+
+ void unlinkToDeath() {
+ mConnection.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ unlinkToDeath();
+ runOnUiThread(() -> {
+ if (mConnectionWrapper == this) {
+ mConnectionWrapper = null;
+ }
+ });
+ }
+ }
+
+ private void setRemoteAccessibilityEmbeddedConnection(
+ IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
+ try {
+ if (mConnectionWrapper != null) {
+ mConnectionWrapper.getConnection()
+ .disassociateEmbeddedHierarchy();
+ mConnectionWrapper.unlinkToDeath();
+ mConnectionWrapper = null;
+ }
+ if (connection != null && leashToken != null) {
+ mConnectionWrapper =
+ new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ mConnectionWrapper.linkToDeath();
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
+ }
+ }
+
+ private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
+ return mConnectionWrapper;
+ }
+
+ void setScreenMatrix(Matrix m) {
+ // If the screen matrix is identity or doesn't change, do nothing.
+ if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) {
+ return;
+ }
+
+ try {
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+ if (wrapper == null) {
+ return;
+ }
+ m.getValues(mMatrixValues);
+ wrapper.getConnection().setScreenMatrix(mMatrixValues);
+ mScreenMatrixForEmbeddedHierarchy.set(m);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setScreenMatrix " + e);
+ }
+ }
+
+
+
+
+
+
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 33b6b0aab6bf..e3067a664f2e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -227,9 +227,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
private int mParentSurfaceGenerationId;
- private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
+ private RemoteAccessibilityController mRemoteAccessibilityController =
+ new RemoteAccessibilityController(this);
- private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
private final Matrix mTmpMatrix = new Matrix();
private final float[] mMatrixValues = new float[9];
@@ -927,6 +927,104 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
+ private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
+ boolean creating, boolean sizeChanged, boolean needBLASTSync) {
+ boolean realSizeChanged = false;
+
+ mSurfaceLock.lock();
+ try {
+ mDrawingStopped = !mVisible;
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "Cur surface: " + mSurface);
+
+ // If we are creating the surface control or the parent surface has not
+ // changed, then set relative z. Otherwise allow the parent
+ // SurfaceChangedCallback to update the relative z. This is needed so that
+ // we do not change the relative z before the server is ready to swap the
+ // parent surface.
+ if (creating || (mParentSurfaceGenerationId
+ == viewRoot.mSurface.getGenerationId())) {
+ updateRelativeZ(mTmpTransaction);
+ }
+ mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
+
+ if (mViewVisibility) {
+ mTmpTransaction.show(mSurfaceControl);
+ } else {
+ mTmpTransaction.hide(mSurfaceControl);
+ }
+
+ if (mSurfacePackage != null) {
+ reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
+ }
+
+ updateBackgroundVisibility(mTmpTransaction);
+ updateBackgroundColor(mTmpTransaction);
+ if (mUseAlpha) {
+ float alpha = getFixedAlpha();
+ mTmpTransaction.setAlpha(mSurfaceControl, alpha);
+ mSurfaceAlpha = alpha;
+ }
+
+ // While creating the surface, we will set it's initial
+ // geometry. Outside of that though, we should generally
+ // leave it to the RenderThread.
+ //
+ // There is one more case when the buffer size changes we aren't yet
+ // prepared to sync (as even following the transaction applying
+ // we still need to latch a buffer).
+ // b/28866173
+ if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+ onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
+ mScreenRect.left, /*positionLeft*/
+ mScreenRect.top /*positionTop*/ ,
+ mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
+ mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
+
+ // Set a window crop when creating the surface or changing its size to
+ // crop the buffer to the surface size since the buffer producer may
+ // use SCALING_MODE_SCALE and submit a larger size than the surface
+ // size.
+ if (mClipSurfaceToBounds && mClipBounds != null) {
+ mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+ } else {
+ mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ }
+ } else if (needBLASTSync) {
+ viewRoot.setUseBLASTSyncTransaction();
+ }
+ mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+ if (sizeChanged && !creating) {
+ setBufferSize(mTmpTransaction);
+ }
+
+ mTmpTransaction.apply();
+ updateEmbeddedAccessibilityMatrix();
+
+ mSurfaceFrame.left = 0;
+ mSurfaceFrame.top = 0;
+ if (translator == null) {
+ mSurfaceFrame.right = mSurfaceWidth;
+ mSurfaceFrame.bottom = mSurfaceHeight;
+ } else {
+ float appInvertedScale = translator.applicationInvertedScale;
+ mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
+ mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+ }
+ final int surfaceWidth = mSurfaceFrame.right;
+ final int surfaceHeight = mSurfaceFrame.bottom;
+ realSizeChanged = mLastSurfaceWidth != surfaceWidth
+ || mLastSurfaceHeight != surfaceHeight;
+ mLastSurfaceWidth = surfaceWidth;
+ mLastSurfaceHeight = surfaceHeight;
+ } finally {
+ mSurfaceLock.unlock();
+ }
+ return realSizeChanged;
+ }
+
/** @hide */
protected void updateSurface() {
if (!mHaveFrame) {
@@ -965,7 +1063,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
&& mRequestedVisible;
final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
- boolean redrawNeeded = false;
getLocationInSurface(mLocation);
final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
|| mWindowSpaceTop != mLocation[1];
@@ -988,7 +1085,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
+ " top=" + (mWindowSpaceTop != mLocation[1]));
try {
- final boolean visible = mVisible = mRequestedVisible;
+ mVisible = mRequestedVisible;
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
mSurfaceWidth = myWidth;
@@ -1014,119 +1111,26 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return;
}
- boolean realSizeChanged = false;
+ final boolean needBLASTSync =
+ (layoutSizeChanged || positionChanged || visibleChanged) &&
+ viewRoot.useBLAST();
+ final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
+ translator, creating, sizeChanged, needBLASTSync);
+ final boolean redrawNeeded = sizeChanged || creating ||
+ (mVisible && !mDrawFinished);
- mSurfaceLock.lock();
try {
- mDrawingStopped = !visible;
-
- if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
- + "Cur surface: " + mSurface);
-
- // If we are creating the surface control or the parent surface has not
- // changed, then set relative z. Otherwise allow the parent
- // SurfaceChangedCallback to update the relative z. This is needed so that
- // we do not change the relative z before the server is ready to swap the
- // parent surface.
- if (creating || (mParentSurfaceGenerationId
- == viewRoot.mSurface.getGenerationId())) {
- updateRelativeZ(mTmpTransaction);
- }
- mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
-
- if (mViewVisibility) {
- mTmpTransaction.show(mSurfaceControl);
- } else {
- mTmpTransaction.hide(mSurfaceControl);
- }
-
- if (mSurfacePackage != null) {
- reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
- }
-
- updateBackgroundVisibility(mTmpTransaction);
- updateBackgroundColor(mTmpTransaction);
- if (mUseAlpha) {
- mTmpTransaction.setAlpha(mSurfaceControl, alpha);
- mSurfaceAlpha = alpha;
- }
-
- // While creating the surface, we will set it's initial
- // geometry. Outside of that though, we should generally
- // leave it to the RenderThread.
- //
- // There is one more case when the buffer size changes we aren't yet
- // prepared to sync (as even following the transaction applying
- // we still need to latch a buffer).
- // b/28866173
- if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
- onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
- mScreenRect.left, /*positionLeft*/
- mScreenRect.top /*positionTop*/ ,
- mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
- mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
-
- // Set a window crop when creating the surface or changing its size to
- // crop the buffer to the surface size since the buffer producer may
- // use SCALING_MODE_SCALE and submit a larger size than the surface
- // size.
- if (mClipSurfaceToBounds && mClipBounds != null) {
- mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
- } else {
- mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
- mSurfaceHeight);
- }
- } else if ((layoutSizeChanged || positionChanged || visibleChanged) &&
- viewRoot.useBLAST()) {
- viewRoot.setUseBLASTSyncTransaction();
- }
- mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
- if (sizeChanged && !creating) {
- setBufferSize(mTmpTransaction);
- }
-
- mTmpTransaction.apply();
- updateScreenMatrixForEmbeddedHierarchy();
-
- if (sizeChanged || creating) {
- redrawNeeded = true;
- }
-
- mSurfaceFrame.left = 0;
- mSurfaceFrame.top = 0;
- if (translator == null) {
- mSurfaceFrame.right = mSurfaceWidth;
- mSurfaceFrame.bottom = mSurfaceHeight;
- } else {
- float appInvertedScale = translator.applicationInvertedScale;
- mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
- mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
- }
-
- final int surfaceWidth = mSurfaceFrame.right;
- final int surfaceHeight = mSurfaceFrame.bottom;
- realSizeChanged = mLastSurfaceWidth != surfaceWidth
- || mLastSurfaceHeight != surfaceHeight;
- mLastSurfaceWidth = surfaceWidth;
- mLastSurfaceHeight = surfaceHeight;
- } finally {
- mSurfaceLock.unlock();
- }
-
- try {
- redrawNeeded |= visible && !mDrawFinished;
-
SurfaceHolder.Callback[] callbacks = null;
final boolean surfaceChanged = creating;
- if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
+ if (mSurfaceCreated && (surfaceChanged || (!mVisible && visibleChanged))) {
mSurfaceCreated = false;
notifySurfaceDestroyed();
}
copySurface(creating /* surfaceControlCreated */, sizeChanged);
- if (visible && mSurface.isValid()) {
+ if (mVisible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
@@ -1754,7 +1758,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@Override
public void surfaceDestroyed() {
setWindowStopped(true);
- setRemoteAccessibilityEmbeddedConnection(null, null);
+ mRemoteAccessibilityController.disassosciateHierarchy();
}
/**
@@ -1834,14 +1838,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
- final RemoteAccessibilityEmbeddedConnection wrapper =
- getRemoteAccessibilityEmbeddedConnection();
- if (wrapper == null) {
+ if (!mRemoteAccessibilityController.connected()) {
return;
}
// Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
// leashed child would return the root node in the embedded hierarchy
- info.addChild(wrapper.getLeashToken());
+ info.addChild(mRemoteAccessibilityController.getLeashToken());
}
@Override
@@ -1850,7 +1852,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// If developers explicitly set the important mode for it, don't change the mode.
// Only change the mode to important when this SurfaceView isn't explicitly set and has
// an embedded hierarchy.
- if (mRemoteAccessibilityEmbeddedConnection == null
+ if (!mRemoteAccessibilityController.connected()
|| mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
return mode;
}
@@ -1859,74 +1861,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
- final RemoteAccessibilityEmbeddedConnection wrapper =
- getRemoteAccessibilityEmbeddedConnection();
-
- // Do nothing if package is embedding the same view hierarchy.
- if (wrapper != null && wrapper.getConnection().equals(connection)) {
- return;
- }
-
- // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
- setRemoteAccessibilityEmbeddedConnection(null, null);
-
- try {
- final IBinder leashToken = connection.associateEmbeddedHierarchy(
- getViewRootImpl().mLeashToken, getAccessibilityViewId());
- setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
- } catch (RemoteException e) {
- Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
- }
- updateScreenMatrixForEmbeddedHierarchy();
- }
-
- private void setRemoteAccessibilityEmbeddedConnection(
- IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
- try {
- if (mRemoteAccessibilityEmbeddedConnection != null) {
- mRemoteAccessibilityEmbeddedConnection.getConnection()
- .disassociateEmbeddedHierarchy();
- mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
- mRemoteAccessibilityEmbeddedConnection = null;
- }
- if (connection != null && leashToken != null) {
- mRemoteAccessibilityEmbeddedConnection =
- new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
- mRemoteAccessibilityEmbeddedConnection.linkToDeath();
- }
- } catch (RemoteException e) {
- Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
- }
- }
-
- private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
- return mRemoteAccessibilityEmbeddedConnection;
- }
-
- private void updateScreenMatrixForEmbeddedHierarchy() {
- getBoundsOnScreen(mTmpRect);
- mTmpMatrix.reset();
- mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
- mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
- mScreenRect.height() / (float) mSurfaceHeight);
-
- // If the screen matrix is identity or doesn't change, do nothing.
- if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
+ if (mRemoteAccessibilityController.alreadyAssociated(connection)) {
return;
}
+ mRemoteAccessibilityController.assosciateHierarchy(connection,
+ getViewRootImpl().mLeashToken, getAccessibilityViewId());
- try {
- final RemoteAccessibilityEmbeddedConnection wrapper =
- getRemoteAccessibilityEmbeddedConnection();
- if (wrapper == null) {
- return;
- }
- mTmpMatrix.getValues(mMatrixValues);
- wrapper.getConnection().setScreenMatrix(mMatrixValues);
- mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
- } catch (RemoteException e) {
- Log.d(TAG, "Error while setScreenMatrix " + e);
- }
+ updateEmbeddedAccessibilityMatrix();
}
private void notifySurfaceDestroyed() {
@@ -1954,6 +1895,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
+ void updateEmbeddedAccessibilityMatrix() {
+ if (!mRemoteAccessibilityController.connected()) {
+ return;
+ }
+ getBoundsOnScreen(mTmpRect);
+ mTmpMatrix.reset();
+ mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
+ mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
+ mScreenRect.height() / (float) mSurfaceHeight);
+ mRemoteAccessibilityController.setScreenMatrix(mTmpMatrix);
+ }
+
@Override
protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
@Nullable Rect previouslyFocusedRect) {
@@ -1970,44 +1923,4 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
+ "Exception requesting focus on embedded window", e);
}
}
-
- /**
- * Wrapper of accessibility embedded connection for embedded view hierarchy.
- */
- private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
- private final IAccessibilityEmbeddedConnection mConnection;
- private final IBinder mLeashToken;
-
- RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
- IBinder leashToken) {
- mConnection = connection;
- mLeashToken = leashToken;
- }
-
- IAccessibilityEmbeddedConnection getConnection() {
- return mConnection;
- }
-
- IBinder getLeashToken() {
- return mLeashToken;
- }
-
- void linkToDeath() throws RemoteException {
- mConnection.asBinder().linkToDeath(this, 0);
- }
-
- void unlinkToDeath() {
- mConnection.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- unlinkToDeath();
- runOnUiThread(() -> {
- if (mRemoteAccessibilityEmbeddedConnection == this) {
- mRemoteAccessibilityEmbeddedConnection = null;
- }
- });
- }
- }
}