[go: nahoru, domu]

blob: cf4e99251d7fac915cdc31f318fef60c5fa95035 [file] [log] [blame]
/*
* Copyright 2020 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.ui.benchmark.android
import android.R
import android.annotation.TargetApi
import android.app.Activity
import android.graphics.Canvas
import android.graphics.Picture
import android.graphics.RenderNode
import android.os.Build
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
class AndroidTestCaseRunner<T : AndroidTestCase>(
private val testCaseFactory: () -> T,
private val activity: Activity
) {
val measuredWidth: Int
get() = view!!.measuredWidth
val measuredHeight: Int
get() = view!!.measuredHeight
private var view: ViewGroup? = null
private val supportsRenderNode = Build.VERSION.SDK_INT >= 29
private val screenWithSpec: Int
private val screenHeightSpec: Int
private val capture = if (supportsRenderNode) RenderNodeCapture() else PictureCapture()
private var canvas: Canvas? = null
private var testCase: T? = null
init {
val displayMetrics = DisplayMetrics()
@Suppress("DEPRECATION") /* defaultDisplay + getMetrics() */
activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
val height = displayMetrics.heightPixels
val width = displayMetrics.widthPixels
screenWithSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST)
screenHeightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
}
fun setupContent() {
require(view == null) { "Content was already set!" }
view = setupContentInternal(activity)
}
private fun setupContentInternal(activity: Activity): ViewGroup {
testCase = testCaseFactory()
return testCase!!.getContent(activity).also { activity.setContentView(it) }
}
fun measure() {
getView().measure(screenWithSpec, screenHeightSpec)
}
fun measureWithSpec(widthSpec: Int, heightSpec: Int) {
getView().measure(widthSpec, heightSpec)
}
fun drawPrepare() {
canvas = capture.beginRecording(getView().width, getView().height)
}
fun draw() {
getView().draw(canvas)
}
fun drawFinish() {
capture.endRecording()
}
fun requestLayout() {
getView().requestLayout()
}
fun layout() {
val view = getView()
view.layout(view.left, view.top, view.right, view.bottom)
}
fun doFrame() {
if (view == null) {
setupContent()
}
measure()
layout()
drawPrepare()
draw()
drawFinish()
}
fun invalidateViews() {
invalidateViews(getView())
}
fun disposeContent() {
if (view == null) {
// Already disposed or never created any content
return
}
// Clear the view
val rootView = activity.findViewById(R.id.content) as ViewGroup
rootView.removeAllViews()
// Important so we can set the content again.
view = null
testCase = null
}
fun getTestCase(): T {
return testCase!!
}
private fun getView(): ViewGroup {
require(view != null) { "View was not set! Call setupContent first!" }
return view!!
}
}
private fun invalidateViews(view: View) {
view.invalidate()
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
val child = view.getChildAt(i)
invalidateViews(child)
}
}
}
// We must separate the use of RenderNode so that it isn't referenced in any
// way on platforms that don't have it. This extracts RenderNode use to a
// potentially unloaded class, RenderNodeCapture.
private interface DrawCapture {
fun beginRecording(width: Int, height: Int): Canvas
fun endRecording()
}
@TargetApi(Build.VERSION_CODES.Q)
private class RenderNodeCapture : DrawCapture {
private val renderNode = RenderNode("Test")
override fun beginRecording(width: Int, height: Int): Canvas {
renderNode.setPosition(0, 0, width, height)
return renderNode.beginRecording()
}
override fun endRecording() {
renderNode.endRecording()
}
}
private class PictureCapture : DrawCapture {
private val picture = Picture()
override fun beginRecording(width: Int, height: Int): Canvas {
return picture.beginRecording(width, height)
}
override fun endRecording() {
picture.endRecording()
}
}