Arthelh/NCNF

View on GitHub
app/src/main/java/com/ncnf/storage/firebase/FirebaseCacheFileStore.java

Summary

Maintainability
A
0 mins
Test Coverage
A
90%
package com.ncnf.storage.firebase;

import android.content.Context;
import android.util.Log;

import com.google.firebase.storage.FirebaseStorage;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.concurrent.CompletableFuture;

import javax.inject.Inject;

/**
 * A file store that caches the file when they are downloaded
 */
public class FirebaseCacheFileStore extends FirebaseFileStore {
    private static final String LOG_TAG = "CACHING";

    private File file;
    private Context context;

    /**
     * Create a file storage with the given adapter
     * @param storage a Firebase Storage adapter
     */
    @Inject
    public FirebaseCacheFileStore(FirebaseStorage storage) {
        super(storage);
    }

    /**
     * Create a file storage with the default Firebase Storage adapter
     */
    public FirebaseCacheFileStore() {
        super();
    }

    /**
     * See parent.
     * Also create a pointer to a file in the cache
     */
    @Override
    public void setPath(String directory, String filename) {
        requiresContext();

        super.setPath(directory, filename);
        this.file = createFile(directory, filename);
    }

    /**
     * Set the context (required to access the filesystem)
     * @param context the activity context
     */
    public void setContext(Context context) {
        this.context = context;
    }

    /**
     * Try to find the file in the cache and download it if the file is not present.
     */
    @Override
    public CompletableFuture<byte[]> download() {
        super.requiresPath();

        if (file.exists()) {
            try {
                byte[] data = Files.readAllBytes(file.toPath());
                return CompletableFuture.completedFuture(data);
            } catch (IOException e) {
                // Will download the file
                Log.w(LOG_TAG, "Could not read the file: " + file.getPath());
            }
        }

        CompletableFuture<byte[]> future = super.download();
        future.thenApply(data -> {
            storeFile(data);
            return data;
        });
        return future;
    }

    /**
     * Invalidate the cached file if any and upload the new one
     * @param data an array of bytes
     * @return a future with whether the upload was successful
     */
    @Override
    public CompletableFuture<Boolean> upload(byte[] data) {
        // invalidate cache
        if (file.exists())
            file.delete();

        return super.upload(data);
    }

    // Write the given bytes to the file in the cache
    private void storeFile(byte[] data) {
        super.requiresPath();

        try {
            file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(data);
            fos.close();
        } catch (IOException e) {
            Log.w(LOG_TAG, "Could not store the file: " + file.getPath() + " because of: " + e);
        }
    }

    // Create a reference to a file in the cache
    private File createFile(String directory, String filename) {
        File root = context.getCacheDir();
        File dir = new File(root, directory);
        dir.mkdirs();
        return new File(dir, filename);
    }

    // raise an exception if the context is not set
    private void requiresContext() throws IllegalStateException {
        if (this.context == null)
            throw new IllegalStateException("Please set a context before setting the path.");
    }
}