koreader / android-luajit-launcher

Android NativeActivity based launcher for LuaJIT, implementing the main loop within Lua land via FFI
MIT License
130 stars 83 forks source link

Added onyx note air3c #471

Open Gonlo2 opened 6 months ago

Gonlo2 commented 6 months ago

Changes to support the onyx air book3c device. I have not been able to check that the code works after compiling it since I don't have the android development kit installed although I have checked that the paths to get/modify the white/warm light work correctly with the adb shell.

The android version is 12, the device version is 2023-12-29_16-55_3.5_f54c71ad2 and the device info is

Manufacturer: qualcomm
Brand: onyx
Model: noteair3c
Device: noteair3c
Product: noteair3c
Hardware: qcom
Platform: bengal

The log obtained after the execution of the compability test could be found in test.log.zip


This change is Reviewable

pazos commented 6 months ago

Thanks, looks good!

I have not been able to check that the code works after compiling it since I don't have the android development kit installed although I have checked that the paths to get/modify the white/warm light work correctly with the adb shell.

I'm afraid we cannot merge it without tests. We cannot trust adb

(whoops, button!)

pazos commented 6 months ago

Building KO is easy, just a few steps: https://github.com/koreader/koreader/blob/master/doc/Building.md

If you don't want to bother with the task, let us know, so someone can create an APK for you to try :)

Gonlo2 commented 6 months ago

I have compiled the application in debug mode but it seems that it gives permissions problems when trying to modify the brightness value preventing the app to start, then when I have time I will investigate to see if I can modify the brightness using the sdk with reflection.

Gonlo2 commented 5 months ago

After investigating the SDK a little bit it seems that the light management works with this, during the next few days I will be using koreader on the device in case I see something weird, but it looks good. Wait until I used it a little before integrating it.

hugleo commented 5 months ago

Hi,

Can you test your device with this driver? https://github.com/koreader/android-luajit-launcher/blob/master/app/src/main/java/org/koreader/launcher/device/lights/OnyxAdbLightsController.kt

The code looks essentially the same.

How to enable the permission: https://github.com/koreader/koreader/wiki/Android-tips-and-tricks#adb-stuff

Reflection alone or double reflection (Android 10) will not work for these methods anymore. It works on the Onyx SDK because it utilizes the external library 'lsposed' to bypass the hidden API restrictions in Android >=11 ;-) https://github.com/onyx-intl/OnyxAndroidDemo/commit/377c24306bc78571521c77741ae8e80e9a9c2dbb

hugleo commented 5 months ago

I'm attempting to make OnyxSdkLightsController.kt generic. I assume that setLightValue and getLightValue will work properly for any Onyx device, which makes the driver more compact. Of course, we can still use the old methods setWarmLightDeviceValue, setColdLightDeviceValue, and getBrightnessConfig with slight adaptations ;-)

package org.koreader.launcher.device.lights

import android.app.Activity
import android.util.Log
import org.koreader.launcher.device.LightsInterface
import android.content.Context
import java.lang.Class.forName
import java.lang.reflect.Method

class OnyxAdbLightsController : LightsInterface {
    companion object {
        private const val TAG = "Lights"
        private const val MIN = 0
    }

    override fun getPlatform(): String {
        return "onyx-adb-lights"
    }

    override fun hasFallback(): Boolean {
        return false
    }

    override fun hasWarmth(): Boolean {
        return true
    }

    override fun needsPermission(): Boolean {
        return false
    }

    override fun getBrightness(activity: Activity): Int {
        return FrontLightAdb.getBrightness(activity)
    }

    override fun getWarmth(activity: Activity): Int {
        return FrontLightAdb.getWarmth(activity)
    }

    override fun setBrightness(activity: Activity, brightness: Int) {
        if (brightness < MIN || brightness > getMaxBrightness()) {
            Log.w(TAG, "brightness value of of range: $brightness")
            return
        }
        Log.v(TAG, "Setting brightness to $brightness")
        FrontLightAdb.setBrightness(brightness, activity)
    }

    override fun setWarmth(activity: Activity, warmth: Int) {
        if (warmth < MIN || warmth > getMaxWarmth()) {
            Log.w(TAG, "warmth value of of range: $warmth")
            return
        }
        Log.v(TAG, "Setting warmth to $warmth")
        FrontLightAdb.setWarmth(warmth, activity)
    }

    override fun getMinWarmth(): Int {
        return MIN
    }

    override fun getMaxWarmth(): Int {
        return FrontLightAdb.getMaxWarmth()
    }

    override fun getMinBrightness(): Int {
        return MIN
    }

    override fun getMaxBrightness(): Int {
        return FrontLightAdb.getMaxBrightness()
    }

    override fun enableFrontlightSwitch(activity: Activity): Int {
        return 1
    }

    override fun hasStandaloneWarmth(): Boolean {
        return false
    }
}

object FrontLightAdb {
    private const val TAG = "Lights"

    private val flController: Class<*>? = try {
        forName("android.onyx.hardware.DeviceController")
    } catch (e: Exception) {
        Log.w(TAG, "$e")
        null
    }

    private fun getMethod(name: String, vararg parameterTypes: Class<*>): Method? {
        return try {
            flController?.getMethod(name, *parameterTypes)
        } catch (e: Exception) {
            Log.w(TAG, "$e")
            null
        }
    }

    private val setLightValueMethod: Method? = getMethod("setLightValue", Integer.TYPE, Integer.TYPE)
    private val getLightValueMethod: Method? = getMethod("getLightValue", Integer.TYPE)
    private val getMaxLightValueMethod: Method? = getMethod("getMaxLightValue", Integer.TYPE)
    private val checkCTMMethod: Method? = getMethod("checkCTM")

    private fun getMaxLightValue(lightType: Int): Int {
        return try {
            getMaxLightValueMethod?.invoke(flController, lightType) as? Int ?: 0
        } catch (e: Exception) {
            Log.e(TAG, "Error getting the max light", e)
            0
        }
    }

    fun checkType(): Int {
        if (checkCTMMethod?.invoke(flController) as? Boolean == true) {
            return 1
        } else {
            return 0
        }
    }

    private val type = checkType()

    fun getBrightnessType(): Int {
        if (type == 1) {
            return 7
        } else {
            return 3
        }
    }

    fun getWarmthType(): Int {
        if (type == 1) {
            return 6
        } else {
            return 2
        }
    }

    private var brightnessType = getBrightnessType()
    private var warmthType = getWarmthType()
    private val brightnessMax = getMaxLightValue(brightnessType)
    private val warmthMax = getMaxLightValue(warmthType)

    fun getMaxBrightness(): Int {
        return brightnessMax
    }

    fun getMaxWarmth(): Int {
        return warmthMax
    }

    private fun getValue(method: Method?, lightType: Int): Int {
        return try {
            method?.invoke(flController, lightType) as? Int ?: 0
        } catch (e: Exception) {
            Log.e(TAG, "Error getting light value", e)
            0
        }
    }

    private fun setValue(method: Method?, lightType: Int, value: Int) {
        try {
            method?.invoke(flController, lightType, value)
        } catch (e: Exception) {
            Log.e(TAG, "Error setting light value", e)
        }
    }

    fun getWarmth(context: Context?): Int {
        return getValue(getLightValueMethod, warmthType)
    }

    fun getBrightness(context: Context?): Int {
        return getValue(getLightValueMethod, brightnessType)
    }

    fun setWarmth(value: Int, context: Context?) {
        setValue(setLightValueMethod, warmthType, value)
    }

    fun setBrightness(value: Int, context: Context?) {
        setValue(setLightValueMethod, brightnessType, value)
    }
}