@Fimagena's answer works perfectly for me. For those that have moved on to Kotlin and found the version generated by the Java->Kotlin code converter to be non-functional, here is a working Kotlin version:
package <your package>
import android.os.FileObserver
import java.io.File
var sObserverLists = mutableMapOf<File, MutableSet<FixedFileObserver>>()
abstract class FixedFileObserver(
path: String,
private val eventMask: Int = FileObserver.ALL_EVENTS
) {
private var fileObserver: FileObserver? = null
private val rootPath: File = File(path)
abstract fun onEvent(event: Int, relativePath: String?)
fun startWatching() {
synchronized(sObserverLists) {
if (!sObserverLists.containsKey(rootPath)) {
sObserverLists[rootPath] = mutableSetOf()
}
val fixedObservers = sObserverLists[rootPath]
fileObserver = if (fixedObservers!!.isNotEmpty()) {
fixedObservers.iterator().next().fileObserver
} else {
object : FileObserver(rootPath.path) {
override fun onEvent(event: Int, path: String?) {
for (fixedObserver in fixedObservers) {
if (event and fixedObserver.eventMask != 0) {
fixedObserver.onEvent(event, path)
}
}
}
}
}
fixedObservers.add(this)
fileObserver!!.startWatching()
}
}
fun stopWatching() {
synchronized(sObserverLists) {
val fixedObservers = sObserverLists[rootPath]
if (fixedObservers == null || fileObserver == null) {
return
}
fixedObservers.remove(this)
if (fixedObservers.isEmpty()) {
fileObserver!!.stopWatching()
}
fileObserver = null
}
}
}
And a bonus wrapper class for the rxJava/rxKotlin enthusiasts:
class RxFileObserver(
private val path: String, eventMask:
Int = FileObserver.ALL_EVENTS
) : FixedFileObserver(path, eventMask) {
private val subject = PublishSubject.create<String>().toSerialized()
val observable: Observable<String> =
subject.doOnSubscribe { startWatching() }
.doOnDispose { stopWatching() }
.share()
override fun onEvent(event: Int, relativePath: String?) {
subject.onNext(path)
}
}