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

Commit 7d7325f8 authored by Yunfan Chen's avatar Yunfan Chen Committed by Android (Google) Code Review
Browse files

Merge "Introduce Multiwindow Fullscreen API"

parents ee4078d6 55981b3c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -4292,6 +4292,7 @@ package android.app {
    method @Deprecated public final void removeDialog(int);
    method public void reportFullyDrawn();
    method public android.view.DragAndDropPermissions requestDragAndDropPermissions(android.view.DragEvent);
    method public void requestFullscreenMode(@NonNull int, @Nullable android.os.OutcomeReceiver<java.lang.Void,java.lang.Throwable>);
    method public final void requestPermissions(@NonNull String[], int);
    method public final void requestShowKeyboardShortcuts();
    method @Deprecated public boolean requestVisibleBehind(boolean);
@@ -4378,6 +4379,8 @@ package android.app {
    field public static final int DEFAULT_KEYS_SEARCH_LOCAL = 3; // 0x3
    field public static final int DEFAULT_KEYS_SHORTCUT = 2; // 0x2
    field protected static final int[] FOCUSED_STATE_SET;
    field public static final int FULLSCREEN_MODE_REQUEST_ENTER = 1; // 0x1
    field public static final int FULLSCREEN_MODE_REQUEST_EXIT = 0; // 0x0
    field public static final int RESULT_CANCELED = 0; // 0x0
    field public static final int RESULT_FIRST_USER = 1; // 0x1
    field public static final int RESULT_OK = -1; // 0xffffffff
+42 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ import android.os.GraphicsEnvironment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -987,6 +988,17 @@ public class Activity extends ContextThemeWrapper
    /** @hide */
    boolean mIsInPictureInPictureMode;

    /** @hide */
    @IntDef(prefix = { "FULLSCREEN_REQUEST_" }, value = {
            FULLSCREEN_MODE_REQUEST_EXIT,
            FULLSCREEN_MODE_REQUEST_ENTER
    })
    public @interface FullscreenModeRequest {}

    public static final int FULLSCREEN_MODE_REQUEST_EXIT = 0;

    public static final int FULLSCREEN_MODE_REQUEST_ENTER = 1;

    private boolean mShouldDockBigOverlays;

    private UiTranslationController mUiTranslationController;
@@ -3000,6 +3012,36 @@ public class Activity extends ContextThemeWrapper
        return false;
    }

    /**
     * Request to put the a freeform activity into fullscreen. This will only be allowed if the
     * activity is on a freeform display, such as a desktop device. The requester has to be the
     * top-most activity and the request should be a response to a user input. When getting
     * fullscreen and receiving corresponding {@link #onConfigurationChanged(Configuration)} and
     * {@link #onMultiWindowModeChanged(boolean, Configuration)}, the activity should relayout
     * itself and the system bars' visibilities can be controlled as usual fullscreen apps.
     *
     * Calling it again with the exit request can restore the activity to the previous status.
     * This will only happen when it got into fullscreen through this API.
     *
     * If an app wants to be in fullscreen always, it should claim as not being resizable
     * by setting
     * <a href="https://developer.android.com/guide/topics/large-screens/multi-window-support#resizeableActivity">
     * {@code android:resizableActivity="false"}</a> instead of calling this API.
     *
     * @param request Can be {@link #FULLSCREEN_MODE_REQUEST_ENTER} or
     *                {@link #FULLSCREEN_MODE_REQUEST_EXIT} to indicate this request is to get
     *                fullscreen or get restored.
     * @param approvalCallback Optional callback, use {@code null} when not necessary. When the
     *                         request is approved or rejected, the callback will be triggered. This
     *                         will happen before any configuration change. The callback will be
     *                         dispatched on the main thread.
     */
    public void requestFullscreenMode(@NonNull @FullscreenModeRequest int request,
            @Nullable OutcomeReceiver<Void, Throwable> approvalCallback) {
        FullscreenRequestHandler.requestFullscreenMode(
                request, approvalCallback, mCurrentConfig, getActivityToken());
    }

    /**
     * Specifies a preference to dock big overlays like the expanded picture-in-picture on TV
     * (see {@link PictureInPictureParams.Builder#setExpandedAspectRatio}). Docking puts the
+9 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.Singleton;
@@ -372,6 +373,14 @@ public class ActivityClient {
        }
    }

    void requestMultiwindowFullscreen(IBinder token, int request, IRemoteCallback callback) {
        try {
            getActivityClientController().requestMultiwindowFullscreen(token, request, callback);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

    void startLockTaskModeByToken(IBinder token) {
        try {
            getActivityClientController().startLockTaskModeByToken(token);
+6 −0
Original line number Diff line number Diff line
@@ -61,6 +61,12 @@ public class ActivityTaskManager {
     */
    public static final int INVALID_TASK_ID = -1;

    /**
     * Invalid windowing mode.
     * @hide
     */
    public static final int INVALID_WINDOWING_MODE = -1;

    /**
     * Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
     * that the resize doesn't need to preserve the window, and can be skipped if bounds
+123 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.app;

import static android.app.Activity.FULLSCREEN_MODE_REQUEST_ENTER;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.OutcomeReceiver;

/**
 * @hide
 */
public class FullscreenRequestHandler {
    @IntDef(prefix = { "RESULT_" }, value = {
            RESULT_APPROVED,
            RESULT_FAILED_NOT_IN_FREEFORM,
            RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY,
            RESULT_FAILED_NOT_DEFAULT_FREEFORM,
            RESULT_FAILED_NOT_TOP_FOCUSED
    })
    public @interface RequestResult {}

    public static final int RESULT_APPROVED = 0;
    public static final int RESULT_FAILED_NOT_IN_FREEFORM = 1;
    public static final int RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY = 2;
    public static final int RESULT_FAILED_NOT_DEFAULT_FREEFORM = 3;
    public static final int RESULT_FAILED_NOT_TOP_FOCUSED = 4;

    public static final String REMOTE_CALLBACK_RESULT_KEY = "result";

    static void requestFullscreenMode(@NonNull @Activity.FullscreenModeRequest int request,
            @Nullable OutcomeReceiver<Void, Throwable> approvalCallback, Configuration config,
            IBinder token) {
        int earlyCheck = earlyCheckRequestMatchesWindowingMode(
                request, config.windowConfiguration.getWindowingMode());
        if (earlyCheck != RESULT_APPROVED) {
            if (approvalCallback != null) {
                notifyFullscreenRequestResult(approvalCallback, earlyCheck);
            }
            return;
        }
        try {
            if (approvalCallback != null) {
                ActivityClient.getInstance().requestMultiwindowFullscreen(token, request,
                        new IRemoteCallback.Stub() {
                            @Override
                            public void sendResult(Bundle res) {
                                notifyFullscreenRequestResult(
                                        approvalCallback, res.getInt(REMOTE_CALLBACK_RESULT_KEY));
                            }
                        });
            } else {
                ActivityClient.getInstance().requestMultiwindowFullscreen(token, request, null);
            }
        } catch (Throwable e) {
            if (approvalCallback != null) {
                approvalCallback.onError(e);
            }
        }
    }

    private static void notifyFullscreenRequestResult(
            OutcomeReceiver<Void, Throwable> callback, int result) {
        Throwable e = null;
        switch (result) {
            case RESULT_FAILED_NOT_IN_FREEFORM:
                e = new IllegalStateException("The window is not a freeform window, the request "
                        + "to get into fullscreen cannot be approved.");
                break;
            case RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY:
                e = new IllegalStateException("The window is not in fullscreen by calling the "
                        + "requestFullscreenMode API before, such that cannot be restored.");
                break;
            case RESULT_FAILED_NOT_DEFAULT_FREEFORM:
                e = new IllegalStateException("The window is not launched in freeform by default.");
                break;
            case RESULT_FAILED_NOT_TOP_FOCUSED:
                e = new IllegalStateException("The window is not the top focused window.");
                break;
            default:
                callback.onResult(null);
                break;
        }
        if (e != null) {
            callback.onError(e);
        }
    }

    private static int earlyCheckRequestMatchesWindowingMode(int request, int windowingMode) {
        if (request == FULLSCREEN_MODE_REQUEST_ENTER) {
            if (windowingMode != WINDOWING_MODE_FREEFORM) {
                return RESULT_FAILED_NOT_IN_FREEFORM;
            }
        } else {
            if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
                return RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY;
            }
        }
        return RESULT_APPROVED;
    }
}
Loading