FileObserverService.java
package com.example.statussaver.Utils;
import static android.os.FileObserver.ALL_EVENTS;
import static android.os.FileObserver.DELETE;
import static android.os.FileObserver.DELETE_SELF;
import static android.os.FileObserver.MOVED_FROM;
import static android.os.FileObserver.MOVED_TO;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.FileObserver;
import android.os.Handler;
import android.os.IBinder;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.example.statussaver.R;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class FileObserverService extends NotificationListenerService {
private RecursiveFileObserver fileObserver;
private String targetDirectory;
@Override
public void onCreate() {
super.onCreate();
setupFileObserver();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
String TAG = "de_ser";
private void setupFileObserver() {
targetDirectory = Consts.CPY_FILES_DIR;
fileObserver = new RecursiveFileObserver(Consts.WHATSAPP_DIRECTORY, new RecursiveFileObserver.EventListener() {
@Override
public void onEvent(int event, File file) {
if (file.exists()) {
Log.e("file_chk", "File: " + file);
} else {
Log.e("file_chk", "File not found");
}
switch (event) {
case DELETE: {
Log.d(TAG, "onEvent: DELETE " + file.exists());
handleFileDelete(file);
}
break;
default:
break;
}
}
});
fileObserver.startWatching();
}
private void handleFileDelete(File deletedFile) {
File fileDirectory = new File(Consts.CPY_FILES_DIR);
if (!fileDirectory.exists()) {
fileDirectory.mkdirs();
}
if (deletedFile.exists()) {
try {
String fileName = deletedFile.getName();
File destDirectory;
if (fileName.contains(".mp4")) {
destDirectory = new File(Consts.VID_FILE_DIR);
} else if (fileName.contains(".jpg") || fileName.contains(".jpeg") || fileName.contains(".png") || fileName.contains(".webp")) {
destDirectory = new File(Consts.IMG_FILE_DIR);
} else if (fileName.contains(".aac") || fileName.contains(".mp3")) {
destDirectory = new File(Consts.AUD_FILE_DIR);
} else if (fileName.contains(".opus") || fileName.contains(".ogg")) {
destDirectory = new File(Consts.VOICE_FILE_DIR);
} else {
destDirectory = new File(Consts.DOC_FILE_DIR);
}
if (!destDirectory.exists()) {
destDirectory.mkdirs();
}
File destFile = new File(destDirectory, fileName);
// Copy the file before deletion
copyFile(deletedFile, destFile);
// Now you can delete the file
boolean isDeleted = deletedFile.delete();
if (isDeleted) {
Log.e(TAG, "File deleted: " + deletedFile.getAbsolutePath());
} else {
Log.e(TAG, "Failed to delete file: " + deletedFile.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Error copying file: " + e.getMessage());
}
}else {
Log.e(TAG, "File does not exist anymore: " + deletedFile.getAbsolutePath());
}
}
private void copyFile(File sourceFile, File destFile) throws IOException {
try (FileInputStream fis = new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(destFile);
FileChannel source = fis.getChannel();
FileChannel destination = fos.getChannel()) {
destination.transferFrom(source, 0, source.size());
}
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(Uri.fromFile(destFile));
sendBroadcast(mediaScanIntent);
}
}
RecursiveFileObserver.java
package com.example.statussaver.Utils;
import android.os.FileObserver;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class RecursiveFileObserver extends FileObserver{
private final Map<String, FileObserver> mObservers = new HashMap<>();
private String mPath;
private int mMask;
private EventListener mListener;
public interface EventListener {
void onEvent(int event, File file);
}
public RecursiveFileObserver(String path, EventListener listener) {
this(path, ALL_EVENTS, listener);
}
public RecursiveFileObserver(String path, int mask, EventListener listener) {
super(path, mask);
mPath = path;
mMask = mask | FileObserver.CREATE | FileObserver.DELETE_SELF;
mListener = listener;
}
private void startWatching(String path) {
synchronized (mObservers) {
FileObserver observer = mObservers.remove(path);
if (observer != null) {
observer.stopWatching();
}
observer = new SingleFileObserver(path, mMask);
observer.startWatching();
mObservers.put(path, observer);
}
}
@Override
public void startWatching() {
Stack<String> stack = new Stack<>();
stack.push(mPath);
// Recursively watch all child directories
while (!stack.empty()) {
String parent = stack.pop();
startWatching(parent);
File path = new File(parent);
File[] files = path.listFiles();
if (files != null) {
for (File file : files) {
if (watch(file)) {
stack.push(file.getAbsolutePath());
}
}
}
}
}
private boolean watch(File file) {
return file.isDirectory() && !file.getName().equals(".") && !file.getName().equals("..");
}
private void stopWatching(String path) {
synchronized (mObservers) {
FileObserver observer = mObservers.remove(path);
if (observer != null) {
observer.stopWatching();
}
}
}
@Override
public void stopWatching() {
synchronized (mObservers) {
for (FileObserver observer : mObservers.values()) {
observer.stopWatching();
}
mObservers.clear();
}
}
@Override
public void onEvent(int event, String path) {
File file;
if (path == null) {
file = new File(mPath);
} else {
file = new File(mPath, path);
}
notify(event, file);
}
private void notify(int event, File file) {
if (mListener != null) {
mListener.onEvent(event & FileObserver.ALL_EVENTS, file);
}
}
private class SingleFileObserver extends FileObserver {
private String filePath;
public SingleFileObserver(String path, int mask) {
super(path, mask);
filePath = path;
}
@Override
public void onEvent(int event, String path) {
File file;
if (path == null) {
file = new File(filePath);
} else {
file = new File(filePath, path);
}
switch (event & FileObserver.ALL_EVENTS) {
case DELETE_SELF:
RecursiveFileObserver.this.stopWatching(filePath);
break;
case CREATE:
if (watch(file)) {
RecursiveFileObserver.this.startWatching(file.getAbsolutePath());
}
break;
}
RecursiveFileObserver.this.notify(event, file);
}
}
}
Consts.java
package com.example.statussaver.Utils;
import android.os.Environment;
import java.io.File;
public class Consts {
public static final File STATUS_DIRECTORY = new File(Environment.getExternalStorageDirectory()
+ File.separator + "WhatsApp/Media/.Statuses");
public static final String WHATSAPP_DIRECTORY = Environment.getExternalStorageDirectory()
+ File.separator + "WhatsApp/Media";
public static final String WHATSAPP_IMG_DIRECTORY = Environment.getExternalStorageDirectory()
+ File.separator + "Pictures";
public static final File STATUS_DIRECTORY_BUSINESS = new File(Environment.getExternalStorageDirectory()
+ File.separator + "WhatsApp Business/Media/.Statuses");
public static final String APP_DIR = Environment.getExternalStorageDirectory()
+ File.separator + "StatusSaverDir";
public static final String CPY_FILES_DIR = Environment.getExternalStorageDirectory()
+ File.separator + "WhatsWebCpyFiles";
public static final String VID_FILE_DIR = Environment.getExternalStorageDirectory()
+ File.separator + "Videos";
public static final String IMG_FILE_DIR = Environment.getExternalStorageDirectory()
+ File.separator + "Images";
public static final String AUD_FILE_DIR = Environment.getExternalStorageDirectory()
+ File.separator + "Audios";
public static final String DOC_FILE_DIR = Environment.getExternalStorageDirectory()
+ File.separator + "Documents";
public static final String VOICE_FILE_DIR = Environment.getExternalStorageDirectory()
+ File.separator + "Voices";
public static final String APP_DIR_BUSINESS = Environment.getExternalStorageDirectory()
+ File.separator + "BusinessStatusSaverDir";
public static final File APP_DIR_SAVED = new File(Environment.getExternalStorageDirectory()
+ File.separator + "StatusSaverDir");
public static final int THUMBSIZE = 128;
}
I am trying to copy file to a designated path when Delete event is triggered, but the file is being deleted before copy. I have tried to delay the delete event as well but it is not working. I want to copy the file that is being deleted to the designated first, and then execute the deletion process