Status Update
Comments
ra...@google.com <ra...@google.com>
ra...@google.com <ra...@google.com> #2
Can you please describe exactly what method call you're making on ExifInterface
? I would expect this to work for exifInterface.setAttribute(TAG_EXPOSURE_TIME, "1/1600")
but not exifInterface.setAttribute(TAG_EXPOSURE_TIME, "0.000625")
.
d....@infotech.team <d....@infotech.team> #3
```
val sourceExif = ExifInterface(sourceFilePath)
val destinationExif = ExifInterface(destinationFilePath)
exifTagsList
.associateWith { sourceExif.getAttribute(it) }
.forEach { (tag, value) ->
destinationExif.setAttribute(tag, value)
}
destinationExif.saveAttributes()
```
Where exifTagsList contains the tags I need, e.g. ExifInterface.TAG_EXPOSURE_TIME.
I'm not sure if it's okay to simply read all attributes as String and write them as String, but it worked fine for all tags, except this one.
The attribute value I get for `getAttribute(TAG_EXPOSURE_TIME)` is a String `6.25E-4`. This should be parsed correctly as double value of `0,000625`, which is equal to the actual photo exposure time of `1/1600`.
The problem here is IMO the lack of precision - for higher exposure times like 1/100, 1/50, etc. it works fine. It doesn't for smaller values like 1/1600 or 1/3200.
When you run this code, you will get 1/1666 instead of 1/1600, which I suppose is lack of precision, as 1/1666 = 0,0006:
`exifInterface.setAttribute(TAG_EXPOSURE_TIME, "6.25E-4")`
I tried `exifInterface.setAttribute(TAG_EXPOSURE_TIME, "1/1600")` but it didn't work for me. Probably the interface expects a value that it can parse as double. According to ExifInterface documentation:
> TAG_EXPOSURE_TIME
> Type is double.
su...@google.com <su...@google.com> #4
Thanks, I think part of the cause of the problem you're seeing is that exifInterface.getAttribute(TAG_EXPOSURE_TIME)
is returning "6.25E-4"
while setAttribute(TAG_EXPOSURE_TIME, value)
is ideally expecting "1/1600"
.
Turns out there is code to handle the case of passing a decimal string to setAttribute
, by converting it to a Rational
representation of { int numerator, int denominator }
, but it (arbitrarily)
Rational(double value) {
this(/* numerator= */ (long) (value * 10000), /* denominator */ 10000);
}
It looks like this code has been present for a long time, seems it was present in ExifInterface
added in 2016
So I think there's an obvious improvement we can make here:
-
Fix the
double
->Rational
conversion to support more precision. Ideally we would first try a denominator of1 / value
(since this would give an exact result in the case of this bug, and probably most other cases since shutter speeds that are less than 1 are usually1/something
), something like:Rational createRationalFromDouble(double value) { double inverse = 1 / value; if (isMathematicalInteger(value)) { return new Rational(1, (long) inverse); } // Not sure what we can do here }
For the
else
case, maybe we could do something clever with continued fractions:https://en.wikipedia.org/wiki/Continued_fraction
It feels like we should also align the format that getAttribute
returns with the format setAttribute
expects, in order to explicitly support the copying use-case you've described. That would mean changing getAttribute
to return "1/1600"
in this case. Unfortunately that is a breaking change for any existing code that is doing Double.parseDouble(exifInterface.getAttribute(TAG_EXPOSURE_TIME))
, so not feasible. If we were to introduce this it would need to be a separate method.
Description
Version used:1.0.0-alpha07
Devices/Android versions reproduced on:
java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
at android.os.Parcel.readException(Parcel.java:1692)
at android.os.Parcel.readException(Parcel.java:1637)
at android.app.job.IJobScheduler$Stub$Proxy.schedule(IJobScheduler.java:158)
at android.app.JobSchedulerImpl.schedule(JobSchedulerImpl.java:42)
at androidx.work.impl.background.systemjob.SystemJobScheduler.scheduleInternal(SystemJobScheduler.java:126)
at androidx.work.impl.background.systemjob.SystemJobScheduler.schedule(SystemJobScheduler.java:95)
at androidx.work.impl.Schedulers.schedule(Schedulers.java:99)
at androidx.work.impl.utils.EnqueueRunnable.scheduleWorkInBackground(EnqueueRunnable.java:114)
at androidx.work.impl.utils.EnqueueRunnable.run(EnqueueRunnable.java:86)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
I already reported this bug multiple times but it is still not fixed. I attached sample project from my prevoious report. In our production app there is only one schedule operation(it calulculates worker tree once and schedules them all on the same thread frame) that you see in the sample app, but it still crashes.
I attached a sample project.
Run the app and press the button multiple times in a quick succession.