1 /* <lambda>null2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.window.layout.adapter.extensions 18 19 import android.app.Activity 20 import android.content.Context 21 import android.os.Build 22 import androidx.annotation.UiContext 23 import androidx.window.core.Bounds 24 import androidx.window.extensions.layout.DisplayFoldFeature 25 import androidx.window.extensions.layout.FoldingFeature as OEMFoldingFeature 26 import androidx.window.extensions.layout.SupportedWindowFeatures 27 import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo 28 import androidx.window.layout.FoldingFeature 29 import androidx.window.layout.FoldingFeature.State.Companion.FLAT 30 import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED 31 import androidx.window.layout.HardwareFoldingFeature 32 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD 33 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE 34 import androidx.window.layout.SupportedPosture 35 import androidx.window.layout.WindowLayoutInfo 36 import androidx.window.layout.WindowMetrics 37 import androidx.window.layout.WindowMetricsCalculatorCompat 38 39 internal object ExtensionsWindowLayoutInfoAdapter { 40 41 internal fun translate( 42 windowMetrics: WindowMetrics, 43 oemFeature: OEMFoldingFeature, 44 ): FoldingFeature? { 45 val type = 46 when (oemFeature.type) { 47 OEMFoldingFeature.TYPE_FOLD -> FOLD 48 OEMFoldingFeature.TYPE_HINGE -> HINGE 49 else -> return null 50 } 51 val state = 52 when (oemFeature.state) { 53 OEMFoldingFeature.STATE_FLAT -> FLAT 54 OEMFoldingFeature.STATE_HALF_OPENED -> HALF_OPENED 55 else -> return null 56 } 57 val bounds = Bounds(oemFeature.bounds) 58 return if (validBounds(windowMetrics, bounds)) { 59 HardwareFoldingFeature(Bounds(oemFeature.bounds), type, state) 60 } else { 61 null 62 } 63 } 64 65 internal fun translate( 66 @UiContext context: Context, 67 info: OEMWindowLayoutInfo, 68 ): WindowLayoutInfo { 69 val calculator = WindowMetricsCalculatorCompat() 70 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 71 translate(calculator.computeCurrentWindowMetrics(context), info) 72 } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && (context is Activity)) { 73 translate(calculator.computeCurrentWindowMetrics(context), info) 74 } else { 75 throw UnsupportedOperationException( 76 "Display Features are only supported after Q. Display features for non-Activity " + 77 "contexts are not expected to be reported on devices running Q." 78 ) 79 } 80 } 81 82 internal fun translate( 83 windowMetrics: WindowMetrics, 84 info: OEMWindowLayoutInfo 85 ): WindowLayoutInfo { 86 val features = 87 info.displayFeatures.mapNotNull { feature -> 88 when (feature) { 89 is OEMFoldingFeature -> translate(windowMetrics, feature) 90 else -> null 91 } 92 } 93 return WindowLayoutInfo(features) 94 } 95 96 internal fun translate(features: SupportedWindowFeatures): List<SupportedPosture> { 97 val isTableTopSupported = 98 features.displayFoldFeatures.any { feature -> 99 feature.hasProperties(DisplayFoldFeature.FOLD_PROPERTY_SUPPORTS_HALF_OPENED) 100 } 101 return if (isTableTopSupported) { 102 listOf(SupportedPosture.TABLETOP) 103 } else { 104 emptyList() 105 } 106 } 107 108 /** 109 * Checks the bounds for a [FoldingFeature] within a given [WindowMetrics]. Validates the 110 * following: 111 * - [Bounds] are not `0` 112 * - [Bounds] are either full width or full height 113 * - [Bounds] do not take up the entire [windowMetrics] 114 * 115 * @param windowMetrics Extracted from a [UiContext] housing the [FoldingFeature]. 116 * @param bounds the bounds of a [FoldingFeature] 117 * @return true if the bounds are valid for the [WindowMetrics], false otherwise. 118 */ 119 private fun validBounds(windowMetrics: WindowMetrics, bounds: Bounds): Boolean { 120 val windowBounds = windowMetrics.bounds 121 if (bounds.isZero) { 122 return false 123 } 124 if (bounds.width != windowBounds.width() && bounds.height != windowBounds.height()) { 125 return false 126 } 127 if (bounds.width < windowBounds.width() && bounds.height < windowBounds.height()) { 128 return false 129 } 130 if (bounds.width == windowBounds.width() && bounds.height == windowBounds.height()) { 131 return false 132 } 133 134 return true 135 } 136 } 137