1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-12-02 01:01:17 +00:00

Fix ANR while processing deleted files (fixes #199) (#204)

* Refactor file event finisher into EventProcessor#onItemFinished

EventProcessor: Add new unhandled event "FolderWatchStateChanged"

* Perform ContentResolver.delete operations asynchronously (fixes #199)
This commit is contained in:
Catfriend1 2019-01-06 03:46:04 +01:00 committed by GitHub
parent c96786bcb9
commit ae2e1874a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,6 +1,7 @@
package com.nutomic.syncthingandroid.service; package com.nutomic.syncthingandroid.service;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -10,6 +11,7 @@ import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
@ -125,25 +127,27 @@ public class EventProcessor implements Runnable, RestApi.OnReceiveEventListener
); );
break; break;
case "ItemFinished": case "ItemFinished":
String folder = (String) event.data.get("folder"); String action = (String) event.data.get("action");
String error = (String) event.data.get("error");
String folderId = (String) event.data.get("folder");
String relativeFilePath = (String) event.data.get("item");
// Lookup folder.path for the given folder.id if all fields were containted in the event.data.
String folderPath = null; String folderPath = null;
for (Folder f : mRestApi.getFolders()) { if (!TextUtils.isEmpty(action) &&
if (f.id.equals(folder)) { !TextUtils.isEmpty(folderId) &&
folderPath = f.path; !TextUtils.isEmpty(relativeFilePath)) {
for (Folder folder : mRestApi.getFolders()) {
if (folder.id.equals(folderId)) {
folderPath = folder.path;
break;
} }
} }
File updatedFile = new File(folderPath, (String) event.data.get("item")); }
if (!"delete".equals(event.data.get("action"))) { if (!TextUtils.isEmpty(folderPath)) {
Log.i(TAG, "Rescanned file via MediaScanner: " + updatedFile.toString()); onItemFinished(action, error, new File(folderPath, relativeFilePath));
MediaScannerConnection.scanFile(mContext, new String[]{updatedFile.getPath()},
null, null);
} else { } else {
// https://stackoverflow.com/a/29881556/1837158 Log.w(TAG, "ItemFinished: Failed to determine folder.path for folder.id=\"" + (TextUtils.isEmpty(folderId) ? "" : folderId) + "\"");
Log.i(TAG, "Deleted file from MediaStore: " + updatedFile.toString());
Uri contentUri = MediaStore.Files.getContentUri("external");
ContentResolver resolver = mContext.getContentResolver();
resolver.delete(contentUri, MediaStore.Images.ImageColumns.DATA + " LIKE ?",
new String[]{updatedFile.getPath()});
} }
break; break;
case "Ping": case "Ping":
@ -156,6 +160,7 @@ public class EventProcessor implements Runnable, RestApi.OnReceiveEventListener
case "FolderPaused": case "FolderPaused":
case "FolderScanProgress": case "FolderScanProgress":
case "FolderSummary": case "FolderSummary":
case "FolderWatchStateChanged":
case "ItemStarted": case "ItemStarted":
case "LocalIndexUpdated": case "LocalIndexUpdated":
case "LoginAttempt": case "LoginAttempt":
@ -285,4 +290,54 @@ public class EventProcessor implements Runnable, RestApi.OnReceiveEventListener
// Show notification. // Show notification.
mNotificationHandler.showConsentNotification(notificationId, title, piAccept, piIgnore); mNotificationHandler.showConsentNotification(notificationId, title, piAccept, piIgnore);
} }
/**
* Precondition: action != null
*/
private void onItemFinished(String action, String error, File updatedFile) {
String relativeFilePath = updatedFile.toString();
if (!TextUtils.isEmpty(error)) {
Log.e(TAG, "onItemFinished: Error \"" + error + "\" reported on file: " + relativeFilePath);
return;
}
switch (action) {
case "delete": // file deleted
Log.i(TAG, "Deleting file from MediaStore: " + relativeFilePath);
Uri contentUri = MediaStore.Files.getContentUri("external");
ContentResolver resolver = mContext.getContentResolver();
LoggingAsyncQueryHandler asyncQueryHandler = new LoggingAsyncQueryHandler(resolver);
asyncQueryHandler.startDelete(
0, // this will be passed to "onUpdatedComplete#token"
relativeFilePath, // this will be passed to "onUpdatedComplete#cookie"
contentUri,
MediaStore.Images.ImageColumns.DATA + " LIKE ?",
new String[]{updatedFile.getPath()}
);
break;
case "update": // file contents changed
case "metadata": // file metadata changed but not contents
Log.i(TAG, "Rescanning file via MediaScanner: " + relativeFilePath);
MediaScannerConnection.scanFile(mContext, new String[]{updatedFile.getPath()},
null, null);
break;
default:
Log.w(TAG, "onItemFinished: Unhandled action \"" + action + "\"");
}
}
private static class LoggingAsyncQueryHandler extends AsyncQueryHandler {
public LoggingAsyncQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
@Override
protected void onDeleteComplete(int token, Object cookie, int result) {
super.onUpdateComplete(token, cookie, result);
if (result == 1 && cookie != null) {
// ToDo Log.v(TAG, "onItemFinished: onDeleteComplete: [ok] file=" + cookie.toString() + ", token=" + Integer.toString(token));
}
}
}
} }