Status Update
Comments
il...@google.com <il...@google.com>
ap...@google.com <ap...@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")
.
cl...@google.com <cl...@google.com> #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.
na...@google.com <na...@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
Component used: Navigation Version used: navigation-compose:2.6.0-alpha08 Devices/Android versions reproduced on: Pixel 7 / Android 13
Since upgrading to compose navigation alpha07 (and 8) we are seeing a crash when using a bottom menu.
Attached is a sample project, but the core to recreate this issue is: