1

I'm trying to listen to any event in external storage directory, i.e. file added, removed or modified through Android FileObserver but its onEvent method never gets called.

I've tried all of the following solutions:

Android FileObserver [duplicate]

FileObserver instance is being garbage collected

How do you implement a FileObserver from an Android Service

And as for my code, its given below:

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.grapgame.fileobserverdemo">

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<application
    android:name=".AppClass"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".FileObserverService" />
</application>

</manifest>

Service

public class FileObserverService extends Service {

public static final String TAG = "FileObserverService";

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "onStart");

    final String pathToWatch = android.os.Environment.getExternalStorageDirectory().toString();
    Toast.makeText(this, "My Service Started and trying to watch " + pathToWatch, Toast.LENGTH_LONG).show();

    AppClass.fileObserver = new FileObserver(pathToWatch) { // set up a file observer to watch this directory on sd card
        @Override
        public void onEvent(int event, String file) {
            Log.d(TAG, "File created [" + pathToWatch + file + "]");
        }
    };
    AppClass.fileObserver.startWatching();
    return START_STICKY;
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

AppClass.java

public class AppClass extends Application {
    public static FileObserver fileObserver;

    @Override
    public void onCreate() {
        super.onCreate();
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, FileObserverService.class);
        startService(intent);
    }
}

I've also tried it without AppClass.java, I found this solution on Stackoverflow and tried. I also tried this GitHub code but nothing seems to work.

I know FileObserver is not recursive, for now I'm just trying to listen to one directory.

Any help is highly appreciated.

AwaisMajeed
  • 2,254
  • 2
  • 10
  • 25
  • From the the in-code comments, it seems you think that `Environment.getExternalStorageDirectory()` is SDCard directory, while in fact this directory is Internal Device storage. External is in relation to App, so "External to the Application". Also, FileObserver is not recursive, so you're monitoring only one directory. – Gotiasits Apr 17 '18 at 10:46
  • @Gotiasits thanks for your comment, I know external storage is not SD card, and I'm just trying to listen one directory for now. I've also updated my question to avoid any further misunderstanding. – AwaisMajeed Apr 17 '18 at 10:54

1 Answers1

2

Use class below RecursiveFileObserver

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import android.os.FileObserver;
import android.util.Log;

public class RecursiveFileObserver extends FileObserver {
    /**
     * Only modification events
     */
    public static int CHANGES_ONLY = CREATE | DELETE | CLOSE_WRITE | MOVE_SELF | MOVED_FROM | MOVED_TO;

    List<SingleFileObserver> mObservers;
    String mPath;
    int mMask;

    public RecursiveFileObserver(String path) {
        this(path, ALL_EVENTS);
    }

    public RecursiveFileObserver(String path, int mask) {
        super(path, mask);
        mPath = path;
        mMask = mask;
    }

    @Override
    public void startWatching() {
        if (mObservers != null) return;

        mObservers = new ArrayList<SingleFileObserver>();
        Stack<String> stack = new Stack<String>();
        stack.push(mPath);

        while (!stack.isEmpty()) {
            String parent = stack.pop();
            mObservers.add(new SingleFileObserver(parent, mMask));
            File path = new File(parent);
            File[] files = path.listFiles();
            if (null == files) continue;
            for (File f : files) {
                if (f.isDirectory() && !f.getName().equals(".") && !f.getName().equals("..")) {
                    stack.push(f.getPath());
                }
            }
        }

        for (SingleFileObserver sfo : mObservers) {
            sfo.startWatching();
        }
    }

    @Override
    public void stopWatching() {
        if (mObservers == null) return;

        for (SingleFileObserver sfo : mObservers) {
            sfo.stopWatching();
        }
        mObservers.clear();
        mObservers = null;
    }

    @Override
    public void onEvent(int event, String path) {
        switch (event) {
            case FileObserver.CREATE:
                Log.i("RecursiveFileObserver", "CREATE: " + path);

                break;
            case FileObserver.MODIFY:
                Log.i("RecursiveFileObserver", "MODIFY: " + path);
                break;
        }
    }

    /**
     * Monitor single directory and dispatch all events to its parent, with full path.
     *
     * @author uestc.Mobius <mobius@toraleap.com>
     * @version 2011.0121
     */
    class SingleFileObserver extends FileObserver {
        String mPath;

        public SingleFileObserver(String path) {
            this(path, ALL_EVENTS);
            mPath = path;
        }

        public SingleFileObserver(String path, int mask) {
            super(path, mask);
            mPath = path;
        }

        @Override
        public void onEvent(int event, String path) {
            String newPath = mPath + "/" + path;
            RecursiveFileObserver.this.onEvent(event, newPath);
        }
    }
}

Your Service will look like this

import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

import java.io.File;

public class FileObserverService extends Service {

    public static final String TAG = "FileObserverService";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: Service Started");
        File sdCard = Environment.getExternalStorageDirectory();
        AppClass.fileObserver = new RecursiveFileObserver(sdCard.getAbsolutePath());
        AppClass.fileObserver.startWatching();
        return START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: Service Destroyed");
    }
}

and AppClass like this

public class AppClass extends Application {
    public static RecursiveFileObserver fileObserver;

    @Override
    public void onCreate() {
        super.onCreate();
        Intent intent = new Intent(this, FileObserverService.class);
        startService(intent);
    }
}
rana_sadam
  • 1,216
  • 10
  • 18