DisplayFeatureTesting.kt

/*
 * Copyright 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.
 */
@file:JvmName("DisplayFeatureTesting")

package androidx.window.testing.layout

import android.app.Activity
import android.graphics.Rect
import androidx.window.layout.FoldingFeature
import androidx.window.layout.FoldingFeature.OcclusionType.Companion.FULL
import androidx.window.layout.FoldingFeature.OcclusionType.Companion.NONE
import androidx.window.layout.FoldingFeature.Orientation
import androidx.window.layout.FoldingFeature.Orientation.Companion.HORIZONTAL
import androidx.window.layout.FoldingFeature.Orientation.Companion.VERTICAL
import androidx.window.layout.FoldingFeature.State
import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
import androidx.window.layout.WindowMetricsCalculator

/**
 * A convenience method to get a test fold with default values provided. With the default values
 * it returns a [FoldingFeature.State.HALF_OPENED] feature that splits the screen along the
 * [FoldingFeature.Orientation.HORIZONTAL] axis.
 *
 * The bounds of the feature are calculated based on [orientation] and [size]. If the
 * feature is [VERTICAL] then the feature is centered horizontally. The top-left x-coordinate is
 * center - ([size] / 2) and the top-right x-coordinate is center + ([size] / 2). If the feature is
 * [HORIZONTAL] then the feature is centered vertically. The top-left y-coordinate is center -
 * ([size] / 2) and the bottom-left y-coordinate is center - ([size] / 2). The folding features
 * always cover the window in one dimension and that determines the other coordinates.
 *
 * @param activity that will house the [FoldingFeature].
 * @param size the smaller dimension  of the fold. The larger dimension  always covers the entire
 * window.
 * @param state [State] of the fold. The default value is [HALF_OPENED]
 * @param orientation [Orientation] of the fold. The default value is [HORIZONTAL]
 * @return [FoldingFeature] that is splitting if the width is not 0 and runs parallel to the
 * [Orientation] axis.
 */
@Suppress("FunctionName")
@JvmOverloads
@JvmName("createFoldingFeature")
fun FoldingFeature(
    activity: Activity,
    center: Int = -1,
    size: Int = 0,
    state: State = HALF_OPENED,
    orientation: Orientation = HORIZONTAL
): FoldingFeature {
    val shouldTreatAsHinge = size != 0
    val isSeparating = shouldTreatAsHinge || state == HALF_OPENED
    val offset = size / 2
    val metricsCalculator = WindowMetricsCalculator.getOrCreate()
    val windowBounds = metricsCalculator.computeCurrentWindowMetrics(activity).bounds
    val actualCenter = if (center < 0) {
        when (orientation) {
            HORIZONTAL -> windowBounds.centerY()
            VERTICAL -> windowBounds.centerX()
            else -> windowBounds.centerX()
        }
    } else {
        center
    }
    val start = actualCenter - offset
    val end = actualCenter + offset
    val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)
    val bounds = if (orientation == VERTICAL) {
        val windowHeight = metrics.bounds.height()
        Rect(start, 0, end, windowHeight)
    } else {
        val windowWidth = metrics.bounds.width()
        Rect(0, start, windowWidth, end)
    }
    val occlusionType = if (shouldTreatAsHinge) {
        FULL
    } else {
        NONE
    }
    return FakeFoldingFeature2(
        bounds = bounds,
        isSeparating = isSeparating,
        occlusionType = occlusionType,
        orientation = orientation,
        state = state
    )
}

private class FakeFoldingFeature2(
    override val bounds: Rect,
    override val isSeparating: Boolean,
    override val occlusionType: FoldingFeature.OcclusionType,
    override val orientation: Orientation,
    override val state: State
) : FoldingFeature {
    init {
        require(!(bounds.width() == 0 && bounds.height() == 0)) { "Bounds must be non zero" }
        require(!(bounds.left != 0 && bounds.top != 0)) {
            "Bounding rectangle must start at the top or left window edge for folding features"
        }
    }
}