Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b50bc1f6 authored by Adam He's avatar Adam He Committed by Android (Google) Code Review
Browse files

Merge "API for autofill integration with keyboard."

parents 5dfe7b9e bc67f2e1
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -22653,6 +22653,7 @@ package android.inputmethodservice {
    method public void onConfigureWindow(android.view.Window, boolean, boolean);
    method public android.view.View onCreateCandidatesView();
    method public android.view.View onCreateExtractTextView();
    method @Nullable public android.view.inputmethod.InlineSuggestionsRequest onCreateInlineSuggestionsRequest();
    method public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
    method public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
    method public android.view.View onCreateInputView();
@@ -22669,6 +22670,7 @@ package android.inputmethodservice {
    method public void onFinishInput();
    method public void onFinishInputView(boolean);
    method public void onInitializeInterface();
    method public boolean onInlineSuggestionsResponse(@NonNull android.view.inputmethod.InlineSuggestionsResponse);
    method public boolean onKeyDown(int, android.view.KeyEvent);
    method public boolean onKeyLongPress(int, android.view.KeyEvent);
    method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -41574,7 +41576,8 @@ package android.service.autofill {
    method @NonNull public java.util.List<android.service.autofill.FillContext> getFillContexts();
    method public int getFlags();
    method public int getId();
    method public void writeToParcel(android.os.Parcel, int);
    method @Nullable public android.view.inputmethod.InlineSuggestionsRequest getInlineSuggestionsRequest();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
    field public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 2; // 0x2
    field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
@@ -41591,6 +41594,7 @@ package android.service.autofill {
  public static final class FillResponse.Builder {
    ctor public FillResponse.Builder();
    method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
    method @NonNull public android.service.autofill.FillResponse.Builder addInlineSuggestionSlice(@NonNull android.app.slice.Slice);
    method @NonNull public android.service.autofill.FillResponse build();
    method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
+18 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.inputmethodservice;
import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -28,6 +29,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Log;
import android.view.InputChannel;
import android.view.autofill.AutofillId;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
@@ -39,6 +41,7 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodSession;
@@ -72,6 +75,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
    private static final int DO_SHOW_SOFT_INPUT = 60;
    private static final int DO_HIDE_SOFT_INPUT = 70;
    private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
    private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;

    final WeakReference<AbstractInputMethodService> mTarget;
    final Context mContext;
@@ -225,6 +229,11 @@ class IInputMethodWrapper extends IInputMethod.Stub
            case DO_CHANGE_INPUTMETHOD_SUBTYPE:
                inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
                return;
            case DO_CREATE_INLINE_SUGGESTIONS_REQUEST:
                SomeArgs args = (SomeArgs) msg.obj;
                inputMethod.onCreateInlineSuggestionsRequest((ComponentName) args.arg1,
                        (AutofillId) args.arg2, (IInlineSuggestionsRequestCallback) args.arg3);
                return;
        }
        Log.w(TAG, "Unhandled message code: " + msg.what);
    }
@@ -265,6 +274,15 @@ class IInputMethodWrapper extends IInputMethod.Stub
                mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps));
    }

    @BinderThread
    @Override
    public void onCreateInlineSuggestionsRequest(ComponentName componentName, AutofillId autofillId,
            IInlineSuggestionsRequestCallback cb) {
        mCaller.executeOrSendMessage(
                mCaller.obtainMessageOOO(DO_CREATE_INLINE_SUGGESTIONS_REQUEST, componentName,
                        autofillId, cb));
    }

    @BinderThread
    @Override
    public void bindInput(InputBinding binding) {
+185 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import static java.lang.annotation.RetentionPolicy.SOURCE;

import android.annotation.AnyThread;
@@ -35,6 +37,7 @@ import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -47,6 +50,8 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.provider.Settings;
@@ -70,11 +75,14 @@ import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillId;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
@@ -91,11 +99,14 @@ import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Collections;

/**
@@ -436,6 +447,14 @@ public class InputMethodService extends AbstractInputMethodService {
    final Insets mTmpInsets = new Insets();
    final int[] mTmpLocation = new int[2];

    @Nullable
    private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null;

    @Nullable
    private InlineSuggestionsResponseCallbackImpl mInlineSuggestionsResponseCallback = null;

    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);

    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
        onComputeInsets(mTmpInsets);
        if (isExtractViewShown()) {
@@ -493,6 +512,18 @@ public class InputMethodService extends AbstractInputMethodService {
            attachToken(token);
        }

        /**
         * {@inheritDoc}
         * @hide
         */
        @MainThread
        @Override
        public void onCreateInlineSuggestionsRequest(ComponentName componentName,
                AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
            Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
            handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
        }

        /**
         * {@inheritDoc}
         */
@@ -670,6 +701,103 @@ public class InputMethodService extends AbstractInputMethodService {
        }
    }

    // TODO(b/137800469): Add detailed docs explaining the inline suggestions process.
    /**
     * Returns an {@link InlineSuggestionsRequest} to be sent to Autofill.
     *
     * <p>Should be implemented by subclasses.</p>
     */
    public @Nullable InlineSuggestionsRequest onCreateInlineSuggestionsRequest() {
        return null;
    }

    /**
     * Called when Autofill responds back with {@link InlineSuggestionsResponse} containing
     * inline suggestions.
     *
     * <p>Should be implemented by subclasses.</p>
     *
     * @param response {@link InlineSuggestionsResponse} passed back by Autofill.
     * @return Whether the IME will use and render  the inline suggestions.
     */
    public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
        return false;
    }

    /**
     * Returns whether inline suggestions are enabled on this service.
     *
     * TODO(b/137800469): check XML for value.
     */
    private boolean isInlineSuggestionsEnabled() {
        return true;
    }

    /**
     * Sends an {@link InlineSuggestionsRequest} obtained from
     * {@link #onCreateInlineSuggestionsRequest()} to the current Autofill Session through
     * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
     */
    private void makeInlineSuggestionsRequest() {
        if (mInlineSuggestionsRequestInfo == null) {
            Log.w(TAG, "makeInlineSuggestionsRequest() called with null requestInfo cache");
            return;
        }

        final IInlineSuggestionsRequestCallback requestCallback =
                mInlineSuggestionsRequestInfo.mCallback;
        try {
            final InlineSuggestionsRequest request = onCreateInlineSuggestionsRequest();
            if (request == null) {
                Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
                requestCallback.onInlineSuggestionsUnsupported();
            } else {
                if (mInlineSuggestionsResponseCallback == null) {
                    mInlineSuggestionsResponseCallback =
                            new InlineSuggestionsResponseCallbackImpl(this,
                                    mInlineSuggestionsRequestInfo.mComponentName,
                                    mInlineSuggestionsRequestInfo.mFocusedId);
                }
                requestCallback.onInlineSuggestionsRequest(request,
                        mInlineSuggestionsResponseCallback);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
        }
    }

    private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
            @NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
        mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
                callback);

        if (!isInlineSuggestionsEnabled()) {
            try {
                callback.onInlineSuggestionsUnsupported();
            } catch (RemoteException e) {
                Log.w(TAG, "handleMakeInlineSuggestionsRequest() RemoteException:" + e);
            }
            return;
        }

        if (!mInputStarted) {
            Log.w(TAG, "onStartInput() not called yet");
            return;
        }

        makeInlineSuggestionsRequest();
    }

    private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
            @NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
        if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
            Log.d(TAG, "Response component=" + componentName + " differs from request component="
                    + mInlineSuggestionsRequestInfo.mComponentName + ", ignoring response");
            return;
        }
        onInlineSuggestionsResponse(response);
    }

    private void notifyImeHidden() {
        setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
        onPreRenderedWindowVisibilityChanged(false /* setVisible */);
@@ -687,6 +815,63 @@ public class InputMethodService extends AbstractInputMethodService {
        inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r));
    }

    /**
     * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
     */
    private static final class InlineSuggestionsResponseCallbackImpl
            extends IInlineSuggestionsResponseCallback.Stub {
        private final WeakReference<InputMethodService> mInputMethodService;

        private final ComponentName mRequestComponentName;
        private final AutofillId mRequestAutofillId;

        private InlineSuggestionsResponseCallbackImpl(InputMethodService inputMethodService,
                ComponentName componentName, AutofillId autofillId) {
            mInputMethodService = new WeakReference<>(inputMethodService);
            mRequestComponentName = componentName;
            mRequestAutofillId = autofillId;
        }

        @Override
        public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
                throws RemoteException {
            final InputMethodService service = mInputMethodService.get();
            if (service != null) {
                service.mHandler.sendMessage(obtainMessage(
                        InputMethodService::handleOnInlineSuggestionsResponse, service,
                        mRequestComponentName, mRequestAutofillId, response));
            }
        }
    }

    /**
     * Information about incoming requests from Autofill Frameworks for inline suggestions.
     */
    private static final class InlineSuggestionsRequestInfo {
        final ComponentName mComponentName;
        final AutofillId mFocusedId;
        final IInlineSuggestionsRequestCallback mCallback;

        InlineSuggestionsRequestInfo(ComponentName componentName, AutofillId focusedId,
                IInlineSuggestionsRequestCallback callback) {
            this.mComponentName = componentName;
            this.mFocusedId = focusedId;
            this.mCallback = callback;
        }

        /**
         * Returns whether the cached {@link ComponentName} matches the passed in activity.
         */
        public boolean validate(ComponentName componentName) {
            final boolean result = componentName.equals(mComponentName);
            if (!result) {
                Log.d(TAG, "Cached request info ComponentName=" + mComponentName
                        + " differs from received ComponentName=" + componentName);
            }
            return result;
        }
    }

    /**
     * Concrete implementation of
     * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
+83 −24
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
import android.view.inputmethod.InlineSuggestionsRequest;

import com.android.internal.util.DataClass;
import com.android.internal.util.Preconditions;
@@ -116,20 +117,34 @@ public final class FillRequest implements Parcelable {
     */
    private final @RequestFlags int mFlags;

    /**
     * Gets the {@link android.view.inputmethod.InlineSuggestionsRequest} associated
     * with this request.
     *
     * TODO(b/137800469): Add more doc describing how to handle the inline suggestions request.
     *
     * @return the suggestionspec
     */
    private final @Nullable InlineSuggestionsRequest mInlineSuggestionsRequest;

    private void onConstructed() {
        Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts");
    }



    // Code below generated by codegen v1.0.0.
    // Code below generated by codegen v1.0.14.
    //
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    //
    // To regenerate run:
    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/FillRequest.java
    //
    // CHECKSTYLE:OFF Generated code
    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
    //   Settings > Editor > Code Style > Formatter Control
    //@formatter:off


    /** @hide */
    @IntDef(flag = true, prefix = "FLAG_", value = {
@@ -184,6 +199,11 @@ public final class FillRequest implements Parcelable {
     *
     *   @return any combination of {@link #FLAG_MANUAL_REQUEST} and
     *           {@link #FLAG_COMPATIBILITY_MODE_REQUEST}.
     * @param inlineSuggestionsRequest
     *   Gets the {@link android.view.inputmethod.InlineSuggestionsRequest} associated
     *   with this request.
     *
     *   TODO(b/137800469): Add more doc describing how to handle the inline suggestions request.
     * @hide
     */
    @DataClass.Generated.Member
@@ -191,7 +211,8 @@ public final class FillRequest implements Parcelable {
            int id,
            @NonNull List<FillContext> fillContexts,
            @Nullable Bundle clientState,
            @RequestFlags int flags) {
            @RequestFlags int flags,
            @Nullable InlineSuggestionsRequest inlineSuggestionsRequest) {
        this.mId = id;
        this.mFillContexts = fillContexts;
        com.android.internal.util.AnnotationValidations.validate(
@@ -203,6 +224,7 @@ public final class FillRequest implements Parcelable {
                mFlags,
                FLAG_MANUAL_REQUEST
                        | FLAG_COMPATIBILITY_MODE_REQUEST);
        this.mInlineSuggestionsRequest = inlineSuggestionsRequest;

        onConstructed();
    }
@@ -256,6 +278,19 @@ public final class FillRequest implements Parcelable {
        return mFlags;
    }

    /**
     * Gets the {@link android.view.inputmethod.InlineSuggestionsRequest} associated
     * with this request.
     *
     * TODO(b/137800469): Add more doc describing how to handle the inline suggestions request.
     *
     * @return the suggestionspec
     */
    @DataClass.Generated.Member
    public @Nullable InlineSuggestionsRequest getInlineSuggestionsRequest() {
        return mInlineSuggestionsRequest;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
@@ -266,40 +301,36 @@ public final class FillRequest implements Parcelable {
                "id = " + mId + ", " +
                "fillContexts = " + mFillContexts + ", " +
                "clientState = " + mClientState + ", " +
                "flags = " + requestFlagsToString(mFlags) +
                "flags = " + requestFlagsToString(mFlags) + ", " +
                "inlineSuggestionsRequest = " + mInlineSuggestionsRequest +
        " }";
    }

    @Override
    @DataClass.Generated.Member
    public void writeToParcel(Parcel dest, int flags) {
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        // You can override field parcelling by defining methods like:
        // void parcelFieldName(Parcel dest, int flags) { ... }

        byte flg = 0;
        if (mClientState != null) flg |= 0x4;
        if (mInlineSuggestionsRequest != null) flg |= 0x10;
        dest.writeByte(flg);
        dest.writeInt(mId);
        dest.writeParcelableList(mFillContexts, flags);
        if (mClientState != null) dest.writeBundle(mClientState);
        dest.writeInt(mFlags);
        if (mInlineSuggestionsRequest != null) dest.writeTypedObject(mInlineSuggestionsRequest, flags);
    }

    @Override
    @DataClass.Generated.Member
    public int describeContents() { return 0; }

    @DataClass.Generated.Member
    public static final @NonNull Parcelable.Creator<FillRequest> CREATOR
            = new Parcelable.Creator<FillRequest>() {
        @Override
        public FillRequest[] newArray(int size) {
            return new FillRequest[size];
        }

        @Override
    /** @hide */
    @SuppressWarnings({"unchecked", "RedundantCast"})
        public FillRequest createFromParcel(Parcel in) {
    @DataClass.Generated.Member
    /* package-private */ FillRequest(@NonNull Parcel in) {
        // You can override field unparcelling by defining methods like:
        // static FieldType unparcelFieldName(Parcel in) { ... }

@@ -309,20 +340,48 @@ public final class FillRequest implements Parcelable {
        in.readParcelableList(fillContexts, FillContext.class.getClassLoader());
        Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle();
        int flags = in.readInt();
            return new FillRequest(
                    id,
                    fillContexts,
                    clientState,
                    flags);
        InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR);

        this.mId = id;
        this.mFillContexts = fillContexts;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mFillContexts);
        this.mClientState = clientState;
        this.mFlags = flags;

        Preconditions.checkFlagsArgument(
                mFlags,
                FLAG_MANUAL_REQUEST
                        | FLAG_COMPATIBILITY_MODE_REQUEST);
        this.mInlineSuggestionsRequest = inlineSuggestionsRequest;

        onConstructed();
    }

    @DataClass.Generated.Member
    public static final @NonNull Parcelable.Creator<FillRequest> CREATOR
            = new Parcelable.Creator<FillRequest>() {
        @Override
        public FillRequest[] newArray(int size) {
            return new FillRequest[size];
        }

        @Override
        public FillRequest createFromParcel(@NonNull Parcel in) {
            return new FillRequest(in);
        }
    };

    @DataClass.Generated(
            time = 1565152134349L,
            codegenVersion = "1.0.0",
            time = 1575928271155L,
            codegenVersion = "1.0.14",
            sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
    @Deprecated
    private void __metadata() {}


    //@formatter:on
    // End of generated code

}
+38 −2

File changed.

Preview size limit exceeded, changes collapsed.

Loading