Add new ContentCapture APIs to let apps change the ContentCaptureContext.
Test: atest CtsContentCaptureServiceTestCases:android.contentcaptureservice.cts.LoginActivityTest#testSimpleLifecycle_changeContextOnCreate \ CtsContentCaptureServiceTestCases:android.contentcaptureservice.cts.LoginActivityTest#testSimpleLifecycle_changeContextAfterCreate Test: atest FrameworksCoreTests:android.view.contentcapture.ContentCaptureEventTest Bug: 124266664 Change-Id: I0348e81e1b2bac01363cf615d2ab32e5bab8aee1
This commit is contained in:
@@ -53462,6 +53462,7 @@ package android.view.contentcapture {
|
||||
method public void close();
|
||||
method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext);
|
||||
method public final void destroy();
|
||||
method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
|
||||
method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
|
||||
method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
|
||||
method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
|
||||
@@ -53469,6 +53470,7 @@ package android.view.contentcapture {
|
||||
method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
|
||||
method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
|
||||
method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
|
||||
method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext);
|
||||
}
|
||||
|
||||
public final class ContentCaptureSessionId implements android.os.Parcelable {
|
||||
|
||||
@@ -9317,6 +9317,7 @@ package android.view.contentcapture {
|
||||
|
||||
public final class ContentCaptureEvent implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
|
||||
method public long getEventTime();
|
||||
method @Nullable public android.view.autofill.AutofillId getId();
|
||||
method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
|
||||
@@ -9325,6 +9326,7 @@ package android.view.contentcapture {
|
||||
method @Nullable public android.view.contentcapture.ViewNode getViewNode();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
|
||||
field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
|
||||
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
|
||||
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
|
||||
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
|
||||
|
||||
@@ -2725,6 +2725,7 @@ package android.view.contentcapture {
|
||||
|
||||
public final class ContentCaptureEvent implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
|
||||
method public long getEventTime();
|
||||
method @Nullable public android.view.autofill.AutofillId getId();
|
||||
method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
|
||||
@@ -2733,6 +2734,7 @@ package android.view.contentcapture {
|
||||
method @Nullable public android.view.contentcapture.ViewNode getViewNode();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
|
||||
field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
|
||||
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
|
||||
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
|
||||
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
|
||||
|
||||
@@ -339,7 +339,7 @@ public abstract class ContentCaptureService extends Service {
|
||||
}
|
||||
switch (event.getType()) {
|
||||
case ContentCaptureEvent.TYPE_SESSION_STARTED:
|
||||
final ContentCaptureContext clientContext = event.getClientContext();
|
||||
final ContentCaptureContext clientContext = event.getContentCaptureContext();
|
||||
clientContext.setParentSessionId(event.getParentSessionId());
|
||||
mSessionUids.put(sessionIdString, uid);
|
||||
onCreateContentCaptureSession(clientContext, sessionId);
|
||||
@@ -383,8 +383,8 @@ public abstract class ContentCaptureService extends Service {
|
||||
}
|
||||
final Integer rightUid = mSessionUids.get(sessionId);
|
||||
if (rightUid == null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
|
||||
+ ": " + mSessionUids);
|
||||
}
|
||||
// Just ignore, as the session could have been finished already
|
||||
|
||||
@@ -9366,7 +9366,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
* Gets the session used to notify Content Capture events.
|
||||
*
|
||||
* @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)},
|
||||
* inherited by ancestore, default session or {@code null} if content capture is disabled for
|
||||
* inherited by ancestors, default session or {@code null} if content capture is disabled for
|
||||
* this view.
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -20,10 +20,6 @@ import android.annotation.Nullable;
|
||||
import android.view.autofill.AutofillId;
|
||||
import android.view.contentcapture.ViewNode.ViewStructureImpl;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A session that is explicitly created by the app (and hence is a descendant of
|
||||
* {@link MainContentCaptureSession}).
|
||||
@@ -35,21 +31,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
|
||||
@NonNull
|
||||
private final ContentCaptureSession mParent;
|
||||
|
||||
/**
|
||||
* {@link ContentCaptureContext} set by client, or {@code null} when it's the
|
||||
* {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
|
||||
* context.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
private final ContentCaptureContext mClientContext;
|
||||
|
||||
/** @hide */
|
||||
protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent,
|
||||
@NonNull ContentCaptureContext clientContext) {
|
||||
super(clientContext);
|
||||
mParent = parent;
|
||||
mClientContext = Preconditions.checkNotNull(clientContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,6 +58,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
|
||||
mParent.flush(reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
|
||||
getMainCaptureSession().notifyContextUpdated(mId, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onDestroy() {
|
||||
getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
|
||||
@@ -101,13 +92,4 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
|
||||
boolean isContentCaptureEnabled() {
|
||||
return getMainCaptureSession().isContentCaptureEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
void dump(String prefix, PrintWriter pw) {
|
||||
if (mClientContext != null) {
|
||||
// NOTE: we don't dump clientContent because it could have PII
|
||||
pw.print(prefix); pw.println("hasClientContext");
|
||||
}
|
||||
super.dump(prefix, pw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,13 +91,22 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
*/
|
||||
public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
|
||||
|
||||
/**
|
||||
* Called after a call to
|
||||
* {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
|
||||
*
|
||||
* <p>The passed context is available through {@link #getContentCaptureContext()}.
|
||||
*/
|
||||
public static final int TYPE_CONTEXT_UPDATED = 6;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "TYPE_" }, value = {
|
||||
TYPE_VIEW_APPEARED,
|
||||
TYPE_VIEW_DISAPPEARED,
|
||||
TYPE_VIEW_TEXT_CHANGED,
|
||||
TYPE_INITIAL_VIEW_TREE_APPEARING,
|
||||
TYPE_INITIAL_VIEW_TREE_APPEARED
|
||||
TYPE_INITIAL_VIEW_TREE_APPEARED,
|
||||
TYPE_CONTEXT_UPDATED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EventType{}
|
||||
@@ -193,12 +202,13 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link #TYPE_SESSION_STARTED}.
|
||||
* Gets the {@link ContentCaptureContext} set calls to
|
||||
* {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
|
||||
*
|
||||
* @hide
|
||||
* <p>Only set on {@link #TYPE_CONTEXT_UPDATED} events.
|
||||
*/
|
||||
@Nullable
|
||||
public ContentCaptureContext getClientContext() {
|
||||
public ContentCaptureContext getContentCaptureContext() {
|
||||
return mClientContext;
|
||||
}
|
||||
|
||||
@@ -220,8 +230,8 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
* Gets the type of the event.
|
||||
*
|
||||
* @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
|
||||
* {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or
|
||||
* {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}.
|
||||
* {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING},
|
||||
* {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}.
|
||||
*/
|
||||
public @EventType int getType() {
|
||||
return mType;
|
||||
@@ -299,6 +309,10 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
if (mText != null) {
|
||||
pw.print(", text="); pw.println(getSanitizedString(mText));
|
||||
}
|
||||
if (mClientContext != null) {
|
||||
pw.print(", context="); mClientContext.dump(pw); pw.println();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -325,6 +339,9 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
if (mText != null) {
|
||||
string.append(", text=").append(getSanitizedString(mText));
|
||||
}
|
||||
if (mClientContext != null) {
|
||||
string.append(", context=").append(mClientContext);
|
||||
}
|
||||
return string.append(']').toString();
|
||||
}
|
||||
|
||||
@@ -345,7 +362,7 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) {
|
||||
parcel.writeString(mParentSessionId);
|
||||
}
|
||||
if (mType == TYPE_SESSION_STARTED) {
|
||||
if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) {
|
||||
parcel.writeParcelable(mClientContext, flags);
|
||||
}
|
||||
}
|
||||
@@ -375,7 +392,7 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
|
||||
event.setParentSessionId(parcel.readString());
|
||||
}
|
||||
if (type == TYPE_SESSION_STARTED) {
|
||||
if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) {
|
||||
event.setClientContext(parcel.readParcelable(null));
|
||||
}
|
||||
return event;
|
||||
@@ -404,6 +421,8 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
return "INITIAL_VIEW_HIERARCHY_STARTED";
|
||||
case TYPE_INITIAL_VIEW_TREE_APPEARED:
|
||||
return "INITIAL_VIEW_HIERARCHY_FINISHED";
|
||||
case TYPE_CONTEXT_UPDATED:
|
||||
return "CONTEXT_UPDATED";
|
||||
default:
|
||||
return "UKNOWN_TYPE: " + type;
|
||||
}
|
||||
|
||||
@@ -165,6 +165,14 @@ public abstract class ContentCaptureSession implements AutoCloseable {
|
||||
// Lazily created on demand.
|
||||
private ContentCaptureSessionId mContentCaptureSessionId;
|
||||
|
||||
/**
|
||||
* {@link ContentCaptureContext} set by client, or {@code null} when it's the
|
||||
* {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
|
||||
* context.
|
||||
*/
|
||||
@Nullable
|
||||
private ContentCaptureContext mClientContext;
|
||||
|
||||
/**
|
||||
* List of children session.
|
||||
*/
|
||||
@@ -183,6 +191,12 @@ public abstract class ContentCaptureSession implements AutoCloseable {
|
||||
mId = Preconditions.checkNotNull(id);
|
||||
}
|
||||
|
||||
// Used by ChildCOntentCaptureSession
|
||||
ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
|
||||
this();
|
||||
mClientContext = Preconditions.checkNotNull(initialContext);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
abstract MainContentCaptureSession getMainCaptureSession();
|
||||
@@ -239,6 +253,30 @@ public abstract class ContentCaptureSession implements AutoCloseable {
|
||||
*/
|
||||
abstract void flush(@FlushReason int reason);
|
||||
|
||||
/**
|
||||
* Sets the {@link ContentCaptureContext} associated with the session.
|
||||
*
|
||||
* <p>Typically used to change the context associated with the default session from an activity.
|
||||
*/
|
||||
public final void setContentCaptureContext(@Nullable ContentCaptureContext context) {
|
||||
mClientContext = context;
|
||||
updateContentCaptureContext(context);
|
||||
}
|
||||
|
||||
abstract void updateContentCaptureContext(@Nullable ContentCaptureContext context);
|
||||
|
||||
/**
|
||||
* Gets the {@link ContentCaptureContext} associated with the session.
|
||||
*
|
||||
* @return context set on constructor or by
|
||||
* {@link #setContentCaptureContext(ContentCaptureContext)}, or {@code null} if never
|
||||
* explicitly set.
|
||||
*/
|
||||
@Nullable
|
||||
public final ContentCaptureContext getContentCaptureContext() {
|
||||
return mClientContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys this session, flushing out all pending notifications to the service.
|
||||
*
|
||||
@@ -424,6 +462,9 @@ public abstract class ContentCaptureSession implements AutoCloseable {
|
||||
@CallSuper
|
||||
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
|
||||
pw.print(prefix); pw.print("id: "); pw.println(mId);
|
||||
if (mClientContext != null) {
|
||||
pw.print(prefix); mClientContext.dump(pw); pw.println();
|
||||
}
|
||||
synchronized (mLock) {
|
||||
pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
|
||||
if (mChildren != null && !mChildren.isEmpty()) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package android.view.contentcapture;
|
||||
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
|
||||
@@ -269,11 +270,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
|
||||
final int eventType = event.getType();
|
||||
if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
|
||||
if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
|
||||
if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
|
||||
&& eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
|
||||
// TODO(b/120494182): comment when this could happen (dialogs?)
|
||||
Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
|
||||
+ ContentCaptureEvent.getTypeAsString(eventType)
|
||||
+ "): session not started yet");
|
||||
+ "): dropping because session not started yet");
|
||||
return;
|
||||
}
|
||||
if (mDisabled.get()) {
|
||||
@@ -476,6 +478,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
|
||||
notifyContextUpdated(mId, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the buffer and return a {@link ParceledListSlice} with the previous events.
|
||||
*/
|
||||
@@ -613,6 +620,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
}
|
||||
}
|
||||
|
||||
void notifyContextUpdated(@NonNull String sessionId,
|
||||
@Nullable ContentCaptureContext context) {
|
||||
sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
|
||||
.setClientContext(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
|
||||
pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package android.view.contentcapture;
|
||||
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
|
||||
@@ -174,7 +175,8 @@ public class ContentCaptureEventTest {
|
||||
assertThat(event.getIds()).isNull();
|
||||
assertThat(event.getText()).isNull();
|
||||
assertThat(event.getViewNode()).isNull();
|
||||
final ContentCaptureContext clientContext = event.getClientContext();
|
||||
final ContentCaptureContext clientContext = event.getContentCaptureContext();
|
||||
assertThat(clientContext).isNotNull();
|
||||
assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
|
||||
}
|
||||
|
||||
@@ -205,9 +207,44 @@ public class ContentCaptureEventTest {
|
||||
assertThat(event.getIds()).isNull();
|
||||
assertThat(event.getText()).isNull();
|
||||
assertThat(event.getViewNode()).isNull();
|
||||
assertThat(event.getClientContext()).isNull();
|
||||
assertThat(event.getContentCaptureContext()).isNull();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testContextUpdated_directly() {
|
||||
final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
|
||||
.setClientContext(mClientContext);
|
||||
assertThat(event).isNotNull();
|
||||
assertContextUpdatedEvent(event);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContextUpdated_throughParcel() {
|
||||
final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
|
||||
.setClientContext(mClientContext);
|
||||
assertThat(event).isNotNull();
|
||||
final ContentCaptureEvent clone = cloneThroughParcel(event);
|
||||
assertContextUpdatedEvent(clone);
|
||||
}
|
||||
|
||||
private void assertContextUpdatedEvent(ContentCaptureEvent event) {
|
||||
assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_UPDATED);
|
||||
assertThat(event.getEventTime()).isAtLeast(MY_EPOCH);
|
||||
assertThat(event.getSessionId()).isEqualTo("42");
|
||||
assertThat(event.getParentSessionId()).isNull();
|
||||
assertThat(event.getId()).isNull();
|
||||
assertThat(event.getIds()).isNull();
|
||||
assertThat(event.getText()).isNull();
|
||||
assertThat(event.getViewNode()).isNull();
|
||||
final ContentCaptureContext clientContext = event.getContentCaptureContext();
|
||||
assertThat(clientContext).isNotNull();
|
||||
assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
|
||||
}
|
||||
|
||||
// TODO(b/123036895): add test for all events type (right now we're just testing the 3 types
|
||||
// that use logic to write to parcel
|
||||
|
||||
private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
|
||||
|
||||
@@ -160,5 +160,10 @@ public class ContentCaptureSessionTest {
|
||||
public void internalNotifyViewHierarchyEvent(boolean started) {
|
||||
throw new UnsupportedOperationException("should not have been called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContentCaptureContext(ContentCaptureContext context) {
|
||||
throw new UnsupportedOperationException("should not have been called");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ final class ContentCaptureServerSession {
|
||||
final IBinder mActivityToken;
|
||||
private final ContentCapturePerUserService mService;
|
||||
private final RemoteContentCaptureService mRemoteService;
|
||||
|
||||
// NOTE: this is the "internal" context (like package and taskId), not the explicit content
|
||||
// set by apps - those are only send to the ContentCaptureService.
|
||||
private final ContentCaptureContext mContentCaptureContext;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user