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

Implementing automatic "safe display mode" for e-ink devices. #17646

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Scapesfear
Copy link

@Scapesfear Scapesfear commented Dec 21, 2024

Purpose / Description

I am solving issue #17618 by implementing the initializeSafeDisplayMode() and hasEInkDisplay() method

Approach

The initializeSafeDisplayMode() method activates safe display mode if the device has an e-ink display and the user hasn't modified this setting. The hasEInkDisplay() method, which detects e-ink displays using a project-specific lookup knownEInkDevices set file of manufacturers and models.

How Has This Been Tested?

I added { "manufacturer": "Google", "models": ["sdk_gphone64_x86_64"] } ( for Pixel 5) and tested it , i have added video of it

Screen_recording_20241222_122258.mp4

Checklist

Please, go through these checks before submitting the PR.

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner

Copy link

welcome bot commented Dec 21, 2024

First PR! 🚀 We sincerely appreciate that you have taken the time to propose a change to AnkiDroid! Please have patience with us as we are all volunteers - we will get to this as soon as possible.

@Scapesfear
Copy link
Author

now in future commit i will implement hasEInkDisplay() method's complete logic

@david-allison
Copy link
Member

Please don't submit a PR until it's ready for review. I've marked this as a draft for now, please un-draft when completed

@david-allison david-allison marked this pull request as draft December 21, 2024 09:14
@david-allison david-allison added the Needs Author Reply Waiting for a reply from the original author label Dec 21, 2024
@Scapesfear
Copy link
Author

sorry for that, will keep this thing in mind for future.

@Scapesfear Scapesfear marked this pull request as ready for review December 21, 2024 17:34
@Scapesfear
Copy link
Author

I’ve added the hasEInkDisplay() method to detect E-Ink devices and integrated it with initializeSafeDisplayMode() to set preferences. Could you review the implementation and suggest improvements?

Also, since Android Studio lacks E-Ink emulation, how would you recommend testing this functionality?

private fun hasEInkDisplay(): Boolean {
return try {
// Load JSON from assets
val jsonFileName = "eink_devices.json"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a real benefit to having this in a JSON file and loading it vs just having a map of manufacturers with an array of their modules in the code? I'm not sure I see a benefit

Copy link
Author

@Scapesfear Scapesfear Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Externalizing data improves code readability, maintainability, and future-proofing by separating data from logic. This modular approach simplifies updates and supports dynamic configurations(that can be introduced in future ), but a hardcoded map can be implemented if preferred.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as you can see my current json file which contains almost 90-95% e-ink devices is huge , add these many code lines in the code itself just for populating the map seems inefficient , but i can implement map if you want . please review

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for a file. Hardcode it so we're doing less I/O on startup

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file isn't that big

"locality" for a feature is a value of it's own in my opinion - by which I mean "everything related to a feature in one spot"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay i will get it done , i will make an map of eink_devices, but i want to confirm should i make the map in the hasEInkDisplay method itself or another method wich return the map to be used , please clarify

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate class, it doesn't belong in AnkiDroidApp

Copy link
Author

@Scapesfear Scapesfear Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that could be overkill , to make a seperate DeviceInfo class , making private map of knowin eink devices of key - manufacturer string and value as list of string of models , is it fine ?

* This method checks if the `safe_display` key is already set in the shared preferences.
* If the key is not set and the device has an e-ink display, it sets the `safe_display` key to `true`.
*/
@OptIn(UnstableApi::class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious, what are the unstable APIs used here and below and why are they necessary vs a stable API ?

Copy link
Author

@Scapesfear Scapesfear Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially used OptIn(UnstableApi::class) due to reliance on experimental APIs like android.system.Os and android.media3.common.util.Log for advanced system operations and logging. However, I've since replaced these unstable APIs with Timber, a stable and widely-used logging tool, ensuring better stability and compatibility while maintaining the required functionality.

Copy link
Author

@Scapesfear Scapesfear Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have finally fixed all the timber issues that were present in my code , please review, the issues were , i was not able to see my timber logs with "SafeDisplayMode" tag in the log cat , and when i checked with some Log.d statemnts i came to know my Timber tree was not getting intialized , thus i made a intializeTimberTree method

@xenonnn4w
Copy link
Contributor

xenonnn4w commented Dec 22, 2024

since Android Studio lacks E-Ink emulation, how would you recommend testing this functionality?

You may try adding something like this
{ "manufacturer": "Google", "models": ["sdk_gphone64_x86_64"] }
to your json file or the array. Also in models change the attribute value to $deviceModel, here it was for Pixel 5 .

@Scapesfear Scapesfear marked this pull request as draft December 22, 2024 07:11
@Scapesfear Scapesfear marked this pull request as ready for review December 22, 2024 09:08
@@ -1,22 +1,142 @@
[
{
"manufacturer": "Onyx",
"models": ["Boox Note Air", "Boox Note 5", "Boox Nova Air", "Boox Nova 3", "Boox Poke 3", "Boox Tab Ultra"]
"manufacturer": "Onyx Boox",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you source this data from?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that this is what the devices are reporting as manufacturer/model?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry i can't confirm 100% about that because nowhere it is directly written what does Build.MODEL print for the devices , although there were some issue in my data i am fixing that .

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any guidance on how to collect the accurate data will be extremely helpful

Copy link
Author

@Scapesfear Scapesfear Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi , i was asking about keeping a naming convention for models in the map can i keep names such that the names only contains numbers and alphabets that too lowercase , no symbols like "-" or spaces , and then after getting the Build.Manufacturer and Build.Model , i can process them to remove any symbols and sapcing for better mathcing , please suggest ?

Copy link
Member

@david-allison david-allison Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, find a list which you know is accurate, even if it's incomplete, rather than AI-generated slop. If you're not certain, it's best not to include it (and therefore, I believe we're starting from scratch).

My first example found from a quick search in the repo doesn't match. You can bounce from this to a decent source.

Manufacturer = ONYX
Model = NovaAir

You'll want to use an authoritative source. None of those blogs are usable

* Returns true if a match is found, false otherwise.
*/
fun hasEInkDisplay(): Boolean {
initializeTimber("SafeDisplayMode")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh? Why not initialize this after logging is setup?

It doesn't need to be done so early

private fun hasEInkDisplay(): Boolean {
return try {
// Load JSON from assets
val jsonFileName = "eink_devices.json"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for a file. Hardcode it so we're doing less I/O on startup

val deviceManufacturer = Build.MANUFACTURER.lowercase().trim()
val deviceModel = Build.MODEL.lowercase().trim()

Timber.tag("SafeDisplayMode").d("Checking device: manufacturer=$deviceManufacturer, model=$deviceModel")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a non-idiomatic use of Timber, please fix to match our code style.

Comment on lines 338 to 452
for (i in 0 until jsonArray.length()) {
val deviceInfo = jsonArray.getJSONObject(i)
val manufacturer = deviceInfo.getString("manufacturer").lowercase().trim()
val models = deviceInfo.getJSONArray("models")

// If manufacturer matches, check models
if (deviceManufacturer.contains(manufacturer) || manufacturer.contains(deviceManufacturer)) {
for (j in 0 until models.length()) {
val model = models.getString(j).lowercase().trim()
if (deviceModel.contains(model) || model.contains(deviceModel)) {
Timber.tag("SafeDisplayMode").d("E-Ink display detected: Manufacturer=$manufacturer, Model=$model")
return true
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method feels like it should be ~1 line of code:

return knownEInkDevices.contains(DeviceInfo(Build.MANUFACTURER, Build.MODEL))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay , i can get i done , but this requires creating an inner class DeviceInfo and an companion object ( knownEInkDevices map ) is it allowed ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pseudocode, but it feels like a reasonable solution, is there a reason you're asking in partiuclar?

@Scapesfear Scapesfear marked this pull request as draft December 23, 2024 06:04
@Scapesfear Scapesfear marked this pull request as ready for review December 24, 2024 04:34
@Scapesfear Scapesfear marked this pull request as draft December 24, 2024 06:06
@Scapesfear Scapesfear marked this pull request as ready for review December 24, 2024 06:39
`hasEInkDisplay` method which uses JSON file.
…nfirmed devices,modified `hasEInkDisplay`

method.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Author Reply Waiting for a reply from the original author New contributor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants