Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minicap issue# 54 android15 support #60

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions experimental/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '1.8'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,26 @@ import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import android.util.Size
import io.devicefarmer.minicap.output.ScreenshotOutput
import io.devicefarmer.minicap.utils.DisplayInfo
import io.devicefarmer.minicap.utils.DisplayManagerGlobal
import io.devicefarmer.minicap.utils.SurfaceControl
import java.io.PrintStream
import kotlin.system.exitProcess

import android.view.Surface
import android.hardware.display.VirtualDisplay
import io.devicefarmer.minicap.utils.DisplayManager
/**
* Provides screen images using [SurfaceControl]. This is pretty similar to the native version
* of minicap but here it is done at a higher level making things a bit easier.
*/
class SurfaceProvider(displayId: Int, targetSize: Size, orientation: Int) : BaseProvider(displayId, targetSize, orientation) {
constructor(display: Int) : this(display, currentScreenSize(), currentRotation())

private var virtualDisplay: VirtualDisplay? = null
private var displayManager: DisplayManager? = null
private var m_displayId : Int = 0;
companion object {
private fun currentScreenSize(): Size {
return currentDisplayInfo().run {
Expand Down Expand Up @@ -82,22 +87,31 @@ class SurfaceProvider(displayId: Int, targetSize: Size, orientation: Int) : Base
private fun initSurface(l: ImageReader.OnImageAvailableListener) {
//must be done on the main thread
// Support Android 12 (preview),and resolve black screen problem
val secure =
Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Build.VERSION.SDK_INT == Build.VERSION_CODES.R && "S" != Build.VERSION.CODENAME
display = SurfaceControl.createDisplay("minicap", secure)
//initialise the surface to get the display in the ImageReader
SurfaceControl.openTransaction()
try {
val secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Build.VERSION.SDK_INT == Build.VERSION_CODES.R && "S" != Build.VERSION.CODENAME
display = SurfaceControl.createDisplay("minicap", secure)
//initialise the surface to get the display in the ImageReader
SurfaceControl.openTransaction()
SurfaceControl.setDisplaySurface(display, getImageReader().surface)
SurfaceControl.setDisplayProjection(
display,
0,
Rect(0, 0, getScreenSize().width, getScreenSize().height),
Rect(0, 0, getTargetSize().width, getTargetSize().height)
)
SurfaceControl.setDisplayProjection(display, 0, Rect(0, 0, getScreenSize().width, getScreenSize().height), Rect(0, 0, getTargetSize().width, getTargetSize().height) )
SurfaceControl.setDisplayLayerStack(display, displayInfo.layerStack)
} finally {
SurfaceControl.closeTransaction()
} catch (e: NoSuchMethodException) {
System.err.println("Handle the exception gracefully")
System.err.println("NoSuchMethodException Method not found: ${e.message}")
System.err.println("Try Display: using DisplayManager API")
try {
//Varun Kumar:- Android 15 Support
if (displayManager == null) {
displayManager = DisplayManager.create();
}
virtualDisplay = displayManager!!.createVirtualDisplay("minicap", getScreenSize().width, getScreenSize().height, m_displayId, getImageReader().surface);
virtualDisplay!!.surface =getImageReader().surface;
} catch (displayManagerException : Exception) {
System.err.println("$displayManagerException Could not create display using DisplayManager")
}
}
finally {
SurfaceControl.closeTransaction()
}
getImageReader().setOnImageAvailableListener(l, handler)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.devicefarmer.minicap.utils

import android.annotation.SuppressLint
import android.hardware.display.VirtualDisplay
import android.view.Display
import android.view.Surface
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.util.regex.Pattern

@SuppressLint("PrivateApi", "DiscouragedPrivateApi")
class DisplayManager private constructor(private val manager: Any) {

private var createVirtualDisplayMethod: Method? = null

companion object {
fun create(): DisplayManager {
try {
val clazz = Class.forName("android.hardware.display.DisplayManagerGlobal")
val getInstanceMethod = clazz.getDeclaredMethod("getInstance")
val dmg = getInstanceMethod.invoke(null)
return DisplayManager(dmg)
} catch (e: ReflectiveOperationException) {
throw AssertionError(e)
}
}
}

private fun parseDisplayFlags(text: String?): Int {
val regex = Pattern.compile("FLAG_[A-Z_]+")
var flags = 0
if (text == null) return flags
val m = regex.matcher(text)
while (m.find()) {
val flagString = m.group()
try {
val filed: Field = Display::class.java.getDeclaredField(flagString)
flags = flags or filed.getInt(null)
} catch (e: ReflectiveOperationException) {
// Silently ignore, some flags reported by "dumpsys display" are @TestApi
}
}
return flags
}

fun getDisplayIds(): IntArray {
return try {
manager.javaClass.getMethod("getDisplayIds").invoke(manager) as IntArray
} catch (e: ReflectiveOperationException) {
throw AssertionError(e)
}
}

@Throws(NoSuchMethodException::class)
private fun getCreateVirtualDisplayMethod(): Method {
if (createVirtualDisplayMethod == null) {
createVirtualDisplayMethod = android.hardware.display.DisplayManager::class.java
.getMethod("createVirtualDisplay", String::class.java, Int::class.java, Int::class.java, Int::class.java, Surface::class.java)
}
return createVirtualDisplayMethod!!
}

@Throws(Exception::class)
fun createVirtualDisplay(name: String, width: Int, height: Int, displayIdToMirror: Int, surface: Surface): VirtualDisplay {
val method: Method = getCreateVirtualDisplayMethod()
return method.invoke(null, name, width, height, displayIdToMirror, surface) as VirtualDisplay
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,6 @@ object SurfaceControl {
}

fun createDisplay(name: String?, secure: Boolean): IBinder {
try {
return clazz!!.getMethod(
"createDisplay",
String::class.java,
Boolean::class.javaPrimitiveType
).invoke(null, name, secure) as IBinder
} catch (e: Exception) {
log.error("SurfaceControl error", e)
throw Error(e)
}
return clazz!!.getMethod("createDisplay", String::class.java, Boolean::class.javaPrimitiveType ).invoke(null, name, secure) as IBinder
}
}
4 changes: 2 additions & 2 deletions experimental/build.gradle
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.4.32"
ext.kotlin_version = "1.6.21"
repositories {
google()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.3"
classpath "com.android.tools.build:gradle:7.4.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.4.30"
// NOTE: Do not place your application dependencies here; they belong
Expand Down
7 changes: 6 additions & 1 deletion experimental/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED \
--add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
--add-opens=java.base/java.io=ALL-UNNAMED \
--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
Expand Down
Binary file modified experimental/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion experimental/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip

Loading