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

Commit 336f9fdf authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Add sample WM Jetpack Extensions impl

Moving some of the common bits of Sidecar and Extensions
into helper classes and adding both to the reference repo.

Bug: 154172225
Test: m androidx.window.extensions
Test: Manual, by including extensions in the emulator image
Test: gradlew window:window:cAT
Change-Id: I44ff06b06b2202a5d687216e017528098dc14d1d
parent ea7f3662
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Sidecar
android_library_import {
    name: "window-sidecar",
    aars: ["window-sidecar-release.aar"],
@@ -20,7 +21,7 @@ android_library_import {

java_library {
    name: "androidx.window.sidecar",
    srcs: ["src/**/*.java"],
    srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
    static_libs: ["window-sidecar"],
    installable: true,
    sdk_version: "core_platform",
@@ -36,3 +37,31 @@ prebuilt_etc {
    src: "androidx.window.sidecar.xml",
    filename_from_src: true,
}

// Extensions
// NOTE: This module is still under active development and must not
// be used in production. Use 'androidx.window.sidecar' instead.
android_library_import {
    name: "window-extensions",
    aars: ["window-extensions-release.aar"],
    sdk_version: "current",
}

java_library {
    name: "androidx.window.extensions",
    srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.java"],
    static_libs: ["window-extensions"],
    installable: true,
    sdk_version: "core_platform",
    system_ext_specific: true,
    libs: ["framework", "androidx.annotation_annotation",],
    required: ["androidx.window.extensions.xml",],
}

prebuilt_etc {
    name: "androidx.window.extensions.xml",
    system_ext_specific: true,
    sub_dir: "permissions",
    src: "androidx.window.extensions.xml",
    filename_from_src: true,
}
+21 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2021 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.
  -->
<permissions>
    <library
        name="androidx.window.extensions"
        file="/system_ext/framework/androidx.window.extensions.jar"/>
</permissions>
+41 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 androidx.window.extensions;

import android.content.Context;

/**
 * Provider class that will instantiate the library implementation. It must be included in the
 * vendor library, and the vendor implementation must match the signature of this class.
 */
public class ExtensionProvider {
    /**
     * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
     * an OEM by overriding this method.
     */
    public static ExtensionInterface getExtensionImpl(Context context) {
        return new SampleExtensionImpl(context);
    }

    /**
     * The support library will use this method to check API version compatibility.
     * @return API version string in MAJOR.MINOR.PATCH-description format.
     */
    public static String getApiVersion() {
        return "1.0.0-settings_sample";
    }
}
+109 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 androidx.window.extensions;

import static android.view.Display.DEFAULT_DISPLAY;

import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;

import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.window.util.BaseDisplayFeature;
import androidx.window.util.SettingsConfigProvider;

import java.util.ArrayList;
import java.util.List;

/**
 * Reference implementation of androidx.window.extensions OEM interface for use with
 * WindowManager Jetpack.
 *
 * NOTE: This version is a work in progress and under active development. It MUST NOT be used in
 * production builds since the interface can still change before reaching stable version.
 * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
 */
class SampleExtensionImpl extends StubExtension implements
        SettingsConfigProvider.StateChangeCallback {
    private static final String TAG = "SampleExtension";

    private final SettingsConfigProvider mConfigProvider;

    SampleExtensionImpl(Context context) {
        mConfigProvider = new SettingsConfigProvider(context, this);
    }

    @Override
    public void onDevicePostureChanged() {
        updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
    }

    @Override
    public void onDisplayFeaturesChanged() {
        for (Activity activity : getActivitiesListeningForLayoutChanges()) {
            ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
            updateWindowLayout(activity, newLayout);
        }
    }

    @NonNull
    private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
        List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
        return new ExtensionWindowLayoutInfo(displayFeatures);
    }

    private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
        List<ExtensionDisplayFeature> features = new ArrayList<>();
        int displayId = activity.getDisplayId();
        if (displayId != DEFAULT_DISPLAY) {
            Log.w(TAG, "This sample doesn't support display features on secondary displays");
            return features;
        }

        if (activity.isInMultiWindowMode()) {
            // It is recommended not to report any display features in multi-window mode, since it
            // won't be possible to synchronize the display feature positions with window movement.
            return features;
        }

        List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
        for (BaseDisplayFeature baseFeature : storedFeatures) {
            Rect featureRect = baseFeature.getRect();
            rotateRectToDisplayRotation(displayId, featureRect);
            transformToWindowSpaceRect(activity, featureRect);
            features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
                    baseFeature.getState()));
        }
        return features;
    }

    @Override
    protected void onListenersChanged() {
        if (hasListeners()) {
            mConfigProvider.registerObserversIfNeeded();
        } else {
            mConfigProvider.unregisterObserversIfNeeded();
        }

        onDevicePostureChanged();
        onDisplayFeaturesChanged();
    }
}
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 androidx.window.extensions;

import android.app.Activity;

import androidx.annotation.NonNull;

import java.util.HashSet;
import java.util.Set;

/**
 * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
 * class for their implementation.
 */
abstract class StubExtension implements ExtensionInterface {

    private ExtensionCallback mExtensionCallback;
    private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
    private boolean mDeviceStateChangeListenerRegistered;

    StubExtension() {
    }

    @Override
    public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
        this.mExtensionCallback = extensionCallback;
    }

    @Override
    public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
        this.mWindowLayoutChangeListenerActivities.add(activity);
        this.onListenersChanged();
    }

    @Override
    public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
        this.mWindowLayoutChangeListenerActivities.remove(activity);
        this.onListenersChanged();
    }

    @Override
    public void onDeviceStateListenersChanged(boolean isEmpty) {
        this.mDeviceStateChangeListenerRegistered = !isEmpty;
        this.onListenersChanged();
    }

    void updateDeviceState(ExtensionDeviceState newState) {
        if (this.mExtensionCallback != null) {
            mExtensionCallback.onDeviceStateChanged(newState);
        }
    }

    void updateWindowLayout(@NonNull Activity activity,
            @NonNull ExtensionWindowLayoutInfo newLayout) {
        if (this.mExtensionCallback != null) {
            mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
        }
    }

    @NonNull
    Set<Activity> getActivitiesListeningForLayoutChanges() {
        return mWindowLayoutChangeListenerActivities;
    }

    protected boolean hasListeners() {
        return !mWindowLayoutChangeListenerActivities.isEmpty()
                || mDeviceStateChangeListenerRegistered;
    }

    protected abstract void onListenersChanged();
}
Loading