rkuenzi-epfl/Wnder

View on GitHub
app/src/main/java/com/github/wnder/picture/LocalPictureDatabase.java

Summary

Maintainability
A
35 mins
Test Coverage
C
78%
package com.github.wnder.picture;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Location;

import com.github.wnder.guessLocation.MapBoxHelper;
import com.google.gson.Gson;
import com.mapbox.mapboxsdk.geometry.LatLng;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * Class serving the interaction with the local db
 */
public class LocalPictureDatabase {
    private final String BITMAP_DIR_NAME = "images";
    private final String MAP_DIR_NAME = "maps";
    private final String METADATA_DIR_NAME = "metadata";
    private File bitmapDirectory;
    private File mapSnapshotDirectory;
    private File metadataDirectory;
    private final String SCOREBOARD = "scoreboard";


    /**
     * Constructor
     * @param context app context
     */
    public LocalPictureDatabase(Context context) {
        // Will be used many times in this class, so init now
        bitmapDirectory = context.getDir(BITMAP_DIR_NAME, Context.MODE_PRIVATE);
        metadataDirectory = context.getDir(METADATA_DIR_NAME, Context.MODE_PRIVATE);
        mapSnapshotDirectory = context.getDir(MAP_DIR_NAME, Context.MODE_PRIVATE);
    }


    /**
     * Store a bitmap to a local file
     * @param bmp bitmap to store
     * @param directory directory in which the bitmap will be stored
     * @param uniqueId uniqueId of the file
     */
    private void storeBitmapFile(Bitmap bmp, File directory, String uniqueId) {
        File myPath = new File(directory, uniqueId);

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(myPath);
            bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Store the metadata file
     * @param data the data to write
     * @param uniqueId unique id of picture
     */
    private void storeMetadataFile(String data, String uniqueId) {
        File file = new File(metadataDirectory, uniqueId);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            fos.write(data.getBytes());
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Store the picture in the internal storage
     * @param picture LocalPicture to store
     */
    public void storePicture(LocalPicture picture) {
        storeBitmapFile(picture.getBitmap(), bitmapDirectory, picture.getUniqueId());

        storeBitmapFile(picture.getMapSnapshot(), mapSnapshotDirectory, picture.getUniqueId());

        String serializedPicture = LocalPictureSerializer.serializePicture(picture.getPicLocation(), picture.getGuessLocation(), picture.getScoreboard());
        storeMetadataFile(serializedPicture, picture.getUniqueId());
    }


    /**
     * Delete a file
     * @param directory directory in which to delete the file
     * @param uniqueId uniqueId of picture
     */
    private void deleteFile(File directory, String uniqueId) {
        new File(directory, uniqueId).delete();
    }

    /**
     * Deletes picture file AND metadata file
     * @param uniqueId uniqueId of picture
     */
    public void deletePicture(String uniqueId) {
        deleteFile(bitmapDirectory, uniqueId);
        deleteFile(mapSnapshotDirectory, uniqueId);
        deleteFile(metadataDirectory, uniqueId);
    }


    /**
     * Load a bitmap from a local file
     * @param directory directory from which the bitmap will be loaded
     * @param uniqueId the id of the picture
     * @return a bitmap of the picture
     */
    private Bitmap loadBitmapFile(File directory, String uniqueId) throws IOException {
        File file = new File(directory, uniqueId);
        FileInputStream fis = new FileInputStream(file);
        byte[] bytes = new byte[(int) file.length()];

        fis.read(bytes, 0, bytes.length);
        fis.close();

        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    }

    /**
     * Load the metadata file
     * @param uniqueId uniqueId of picture
     * @return content of file, empty if there is a problem
     */
    private String loadMetadataFile(String uniqueId){
        String toReturn = "";

        //setup file input stream
        File file = new File(metadataDirectory, uniqueId);
        FileInputStream fis;
        try {
            fis = new FileInputStream(file);

            //setup input stream reader and string builder
            InputStreamReader inputStreamReader =
                    new InputStreamReader(fis, StandardCharsets.UTF_8);
            StringBuilder stringBuilder = new StringBuilder();

            //use buffer: for as long as there is data, read it.
            try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
                String line = reader.readLine();
                while (line != null) {
                    stringBuilder.append(line);
                    line = reader.readLine();
                }

                //close stream
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //cast string builder
                toReturn = stringBuilder.toString();
            }
        }
        catch (FileNotFoundException e){
            e.printStackTrace();
        }

        return toReturn;
    }


    /**
     * Get the bitmap of the image from the local database
     * @param uniqueId id of the image
     * @return the bitmap of the image
     */
    public CompletableFuture<Bitmap> getBitmap(String uniqueId) {
        CompletableFuture<Bitmap> cf = new CompletableFuture<>();
        try {
            Bitmap bitmap = loadBitmapFile(bitmapDirectory, uniqueId);
            cf.complete(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return cf;
    }

    /**
     * Get the map of the image from the local database
     * @param uniqueId id of the image
     * @return the map of the image
     */
    public CompletableFuture<Bitmap> getMapSnapshot(Context context, Location guessLocation, Location pictureLocation, String uniqueId) {
        CompletableFuture<Bitmap> cf = new CompletableFuture<>();
        try {
            Bitmap mapSnapshot = loadBitmapFile(mapSnapshotDirectory, uniqueId);
            cf.complete(mapSnapshot);
        } catch (IOException e) {
            LatLng guessLatLng = new LatLng(guessLocation.getLatitude(), guessLocation.getLongitude());
            LatLng pictureLatLng = new LatLng(pictureLocation.getLatitude(), pictureLocation.getLongitude());
            MapBoxHelper.onMapSnapshotAvailable(context, guessLatLng, pictureLatLng, (mapSnapshot) -> {
                cf.complete(mapSnapshot);
            });
        }
        return cf;
    }

    /**
     * Tool method used to get real or guessed location using serialization
     * @param uniqueId uniqueId of picture
     * @param type real or guess
     * @return real or guessed location, depending on realOrGuess
     */
    private Location getRealOrGuessed(String uniqueId, LocationType type){
        String serializedData = loadMetadataFile(uniqueId);
        if (serializedData == null) {
            return null;
        }
        JSONObject json = LocalPictureSerializer.deserializePicture(serializedData);
        if (json == null) {
            return null;
        }

        if (type == LocationType.REAL) {
            return LocalPictureSerializer.getRealLocation(json);
        } else {
            return LocalPictureSerializer.getGuessLocation(json);
        }
    }

    /**
     * Get the location of the image from the local database
     * @param uniqueId id of the image
     * @return the actual location of the image
     */
    public Location getLocation(String uniqueId) {
        return getRealOrGuessed(uniqueId, LocationType.REAL);
    }

    /**
     * Get the location of the image from the local database
     * @param uniqueId id of the image
     * @return the actual location of the image
     */
    public Location getGuessedLocation(String uniqueId) {
        return getRealOrGuessed(uniqueId, LocationType.GUESSED);
    }

    /**
     * Get the scoreboard of the image from the local database
     * @param uniqueId id of the image
     * @return a map of the scoreboard
     */
    public Map<String, Double> getScoreboard(String uniqueId) {
        String serializedData = loadMetadataFile(uniqueId);
        try {
            //deserialize and go back to hashmap
            JSONObject json = LocalPictureSerializer.deserializePicture(serializedData);
            String scoreboard = json.getString(SCOREBOARD);
            return new Gson().fromJson(scoreboard, Map.class);
        } catch (JSONException e){
            e.printStackTrace();
            return null;
        }
    }


    /**
     * Update the scoreboard of the picture in the internal storage
     * @param scoreboard updated scoreboard
     */
    public void updateScoreboard(String uniqueId, Map<String, Double> scoreboard) {
        try {
            //Get and remove old scoreboard
            String oldMetadata = loadMetadataFile(uniqueId);
            JSONObject json = LocalPictureSerializer.deserializePicture(oldMetadata);
            json.remove(SCOREBOARD);

            //put in new scoreboard
            json.put(SCOREBOARD, scoreboard);
            storeMetadataFile(json.toString(), uniqueId);
        } catch (JSONException e){
            e.printStackTrace();
        }
    }
}