fixed warning

This commit is contained in:
sonalyadav 2024-12-19 19:31:57 +05:30 committed by Sonal Yadav
parent 68c24c47e3
commit 6e929e3786

View file

@ -32,204 +32,205 @@ private const val RADIUS_STEP_SIZE_IN_METRES = 100
private const val MIN_NEARBY_RESULTS = 5 private const val MIN_NEARBY_RESULTS = 5
class FileProcessor class FileProcessor
@Inject @Inject
constructor( constructor(
private val context: Context, private val context: Context,
private val contentResolver: ContentResolver, private val contentResolver: ContentResolver,
private val gpsCategoryModel: GpsCategoryModel, private val gpsCategoryModel: GpsCategoryModel,
private val depictsModel: DepictModel, private val depictsModel: DepictModel,
@param:Named("default_preferences") private val defaultKvStore: JsonKvStore, @param:Named("default_preferences") private val defaultKvStore: JsonKvStore,
private val apiCall: CategoryApi, private val apiCall: CategoryApi,
private val okHttpJsonApiClient: OkHttpJsonApiClient, private val okHttpJsonApiClient: OkHttpJsonApiClient,
) { ) {
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
fun cleanup() { fun cleanup() {
compositeDisposable.clear() compositeDisposable.clear()
} }
/** /**
* Processes filePath coordinates, either from EXIF data or user location * Processes filePath coordinates, either from EXIF data or user location
*/ */
fun processFileCoordinates( fun processFileCoordinates(
similarImageInterface: SimilarImageInterface, similarImageInterface: SimilarImageInterface,
filePath: String?, filePath: String?,
inAppPictureLocation: LatLng?, inAppPictureLocation: LatLng?,
): ImageCoordinates { ): ImageCoordinates {
val exifInterface: ExifInterface? = val exifInterface: ExifInterface? =
try {
ExifInterface(filePath!!)
} catch (e: IOException) {
Timber.e(e)
null
}
// Redact EXIF data as indicated in preferences.
redactExifTags(exifInterface, getExifTagsToRedact())
Timber.d("Calling GPSExtractor")
val originalImageCoordinates = ImageCoordinates(exifInterface, inAppPictureLocation)
if (originalImageCoordinates.decimalCoords == null) {
// Find other photos taken around the same time which has gps coordinates
findOtherImages(
File(filePath),
similarImageInterface,
)
} else {
prePopulateCategoriesAndDepictionsBy(originalImageCoordinates)
}
return originalImageCoordinates
}
/**
* Gets EXIF Tags from preferences to be redacted.
*
* @return tags to be redacted
*/
fun getExifTagsToRedact(): Set<String> {
val prefManageEXIFTags =
defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet()
val redactTags: Set<String> =
context.resources.getStringArray(R.array.pref_exifTag_values).toSet()
return redactTags - prefManageEXIFTags
}
/**
* Redacts EXIF metadata as indicated in preferences.
*
* @param exifInterface ExifInterface object
* @param redactTags tags to be redacted
*/
fun redactExifTags(
exifInterface: ExifInterface?,
redactTags: Set<String>,
) {
compositeDisposable.add(
Observable
.fromIterable(redactTags)
.flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) }
.subscribe(
{ redactTag(exifInterface, it) },
{ Timber.d(it) },
{ save(exifInterface) },
),
)
}
private fun save(exifInterface: ExifInterface?) {
try { try {
exifInterface?.saveAttributes() ExifInterface(filePath!!)
} catch (e: IOException) {
Timber.w("EXIF redaction failed: %s", e.toString())
}
}
private fun redactTag(
exifInterface: ExifInterface?,
tag: String,
) {
Timber.d("Checking for tag: %s", tag)
exifInterface
?.getAttribute(tag)
?.takeIf { it.isNotEmpty() }
?.let { attributeName ->
exifInterface.setAttribute(tag, null).also {
Timber.d("Exif tag $tag with value $attributeName redacted.")
}
}
}
/**
* Find other images around the same location that were taken within the last 20 sec
*
* @param originalImageCoordinates
* @param fileBeingProcessed
* @param similarImageInterface
*/
private fun findOtherImages(
fileBeingProcessed: File,
similarImageInterface: SimilarImageInterface,
) {
val oneHundredAndTwentySeconds = 120 * 1000L
// Time when the original image was created
val timeOfCreation = fileBeingProcessed.lastModified()
LongRange
val timeOfCreationRange =
timeOfCreation - oneHundredAndTwentySeconds..timeOfCreation + oneHundredAndTwentySeconds
fileBeingProcessed.parentFile
.listFiles()
.asSequence()
.filter { it.lastModified() in timeOfCreationRange }
.map { Pair(it, readImageCoordinates(it)) }
.firstOrNull { it.second?.decimalCoords != null }
?.let { fileCoordinatesPair ->
similarImageInterface.showSimilarImageFragment(
fileBeingProcessed.path,
fileCoordinatesPair.first.absolutePath,
fileCoordinatesPair.second,
)
}
}
private fun readImageCoordinates(file: File) =
try {
/* Used null location as location for similar images captured before is not available
in case it is not present in the EXIF. */
ImageCoordinates(contentResolver.openInputStream(Uri.fromFile(file))!!, null)
} catch (e: IOException) { } catch (e: IOException) {
Timber.e(e) Timber.e(e)
try { null
ImageCoordinates(file.absolutePath, null)
} catch (ex: IOException) {
Timber.e(ex)
null
}
} }
// Redact EXIF data as indicated in preferences.
/** redactExifTags(exifInterface, getExifTagsToRedact())
* Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. Then Timber.d("Calling GPSExtractor")
* initiates the calls to MediaWiki API through an instance of CategoryApi. val originalImageCoordinates = ImageCoordinates(exifInterface, inAppPictureLocation)
* if (originalImageCoordinates.decimalCoords == null) {
* @param imageCoordinates // Find other photos taken around the same time which has gps coordinates
*/ findOtherImages(
fun prePopulateCategoriesAndDepictionsBy(imageCoordinates: ImageCoordinates) { File(filePath),
requireNotNull(imageCoordinates.decimalCoords) similarImageInterface,
compositeDisposable.add(
apiCall
.request(imageCoordinates.decimalCoords!!)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(
gpsCategoryModel::setCategoriesFromLocation,
{
Timber.e(it)
gpsCategoryModel.clear()
},
),
) )
} else {
prePopulateCategoriesAndDepictionsBy(originalImageCoordinates)
}
return originalImageCoordinates
}
compositeDisposable.add( /**
suggestNearbyDepictions(imageCoordinates), * Gets EXIF Tags from preferences to be redacted.
) *
* @return tags to be redacted
*/
fun getExifTagsToRedact(): Set<String> {
val prefManageEXIFTags =
defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet<String>()
val redactTags: Set<String> =
context.resources.getStringArray(R.array.pref_exifTag_values).toSet()
return redactTags - prefManageEXIFTags
}
/**
* Redacts EXIF metadata as indicated in preferences.
*
* @param exifInterface ExifInterface object
* @param redactTags tags to be redacted
*/
fun redactExifTags(
exifInterface: ExifInterface?,
redactTags: Set<String>,
) {
compositeDisposable.add(
Observable
.fromIterable(redactTags)
.flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) }
.subscribe(
{ redactTag(exifInterface, it) },
{ Timber.d(it) },
{ save(exifInterface) },
),
)
}
private fun save(exifInterface: ExifInterface?) {
try {
exifInterface?.saveAttributes()
} catch (e: IOException) {
Timber.w("EXIF redaction failed: %s", e.toString())
}
}
private fun redactTag(
exifInterface: ExifInterface?,
tag: String,
) {
Timber.d("Checking for tag: %s", tag)
exifInterface
?.getAttribute(tag)
?.takeIf { it.isNotEmpty() }
?.let { attributeName ->
exifInterface.setAttribute(tag, "").also {
Timber.d("EXIF tag $tag removed or set to empty.")
}
}
}
/**
* Find other images around the same location that were taken within the last 20 sec
*
* @param originalImageCoordinates
* @param fileBeingProcessed
* @param similarImageInterface
*/
private fun findOtherImages(
fileBeingProcessed: File,
similarImageInterface: SimilarImageInterface,
) {
val oneHundredAndTwentySeconds = 120 * 1000L
// Time when the original image was created
val timeOfCreation = fileBeingProcessed.lastModified()
LongRange
val timeOfCreationRange =
timeOfCreation - oneHundredAndTwentySeconds..timeOfCreation + oneHundredAndTwentySeconds
fileBeingProcessed.parentFile
.listFiles()
.asSequence()
.filter { it.lastModified() in timeOfCreationRange }
.map { Pair(it, readImageCoordinates(it)) }
.firstOrNull { it.second?.decimalCoords != null }
?.let { fileCoordinatesPair ->
similarImageInterface.showSimilarImageFragment(
fileBeingProcessed.path,
fileCoordinatesPair.first.absolutePath,
fileCoordinatesPair.second,
)
}
}
private fun readImageCoordinates(file: File) =
try {
/* Used null location as location for similar images captured before is not available
in case it is not present in the EXIF. */
ImageCoordinates(contentResolver.openInputStream(Uri.fromFile(file))!!, null)
} catch (e: IOException) {
Timber.e(e)
try {
ImageCoordinates(file.absolutePath, null)
} catch (ex: IOException) {
Timber.e(ex)
null
}
} }
private val radiiProgressionInMetres = /**
(DEFAULT_SUGGESTION_RADIUS_IN_METRES..MAX_SUGGESTION_RADIUS_IN_METRES step RADIUS_STEP_SIZE_IN_METRES) * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. Then
* initiates the calls to MediaWiki API through an instance of CategoryApi.
private fun suggestNearbyDepictions(imageCoordinates: ImageCoordinates): Disposable = *
Observable * @param imageCoordinates
.fromIterable(radiiProgressionInMetres.map { it / 1000.0 }) */
.concatMap { fun prePopulateCategoriesAndDepictionsBy(imageCoordinates: ImageCoordinates) {
Observable.fromCallable { requireNotNull(imageCoordinates.decimalCoords)
okHttpJsonApiClient.getNearbyPlaces( compositeDisposable.add(
imageCoordinates.latLng!!, apiCall
Locale.getDefault().language, .request(imageCoordinates.decimalCoords!!)
it, .subscribeOn(Schedulers.io())
) .observeOn(Schedulers.io())
}
}.subscribeOn(Schedulers.io())
.filter { it.size >= MIN_NEARBY_RESULTS }
.take(1)
.subscribe( .subscribe(
{ depictsModel.nearbyPlaces.offer(it) }, gpsCategoryModel::setCategoriesFromLocation,
{ Timber.e(it) }, {
) Timber.e(it)
gpsCategoryModel.clear()
},
),
)
compositeDisposable.add(
suggestNearbyDepictions(imageCoordinates),
)
} }
private val radiiProgressionInMetres =
(DEFAULT_SUGGESTION_RADIUS_IN_METRES..MAX_SUGGESTION_RADIUS_IN_METRES step RADIUS_STEP_SIZE_IN_METRES)
private fun suggestNearbyDepictions(imageCoordinates: ImageCoordinates): Disposable =
Observable
.fromIterable(radiiProgressionInMetres.map { it / 1000.0 })
.concatMap {
Observable.fromCallable {
okHttpJsonApiClient.getNearbyPlaces(
imageCoordinates.latLng!!,
Locale.getDefault().language,
it,
)
}
}.subscribeOn(Schedulers.io())
.filter { it.size >= MIN_NEARBY_RESULTS }
.take(1)
.subscribe(
{ depictsModel.nearbyPlaces.offer(it) },
{ Timber.e(it) },
)
}