Skip to content
Snippets Groups Projects
SaveFile.java 26.96 KiB
package com.notificationFramework.sedentary.frontEnd;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.util.Log;

import com.google.android.gms.awareness.Awareness;
import com.google.android.gms.awareness.AwarenessStatusCodes;
import com.google.android.gms.awareness.snapshot.HeadphoneStateResponse;
import com.google.android.gms.awareness.snapshot.LocationResponse;
import com.google.android.gms.awareness.snapshot.PlacesResponse;
import com.google.android.gms.awareness.snapshot.WeatherResponse;
import com.google.android.gms.awareness.state.Weather;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.PlaceLikelihood;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.RuntimeExecutionException;
import com.google.android.gms.tasks.Task;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Created by Peter De Jonckheere on 12/02/2018.
 * <p>
 * Contains the static methods which record various information about the notifications in the log
 * files stored in the external storage of the device.
 */

public class SaveFile {

    /**
     * Saves a general timestamp which logs that a notification was either sent, clicked or
     * acknowledged. The timestamp is saved in standard Android calendar format with 2 bits after.
     * "00" - acknowledged
     * "01" - clicked
     * "10" - sent
     * "11" - goal notification
     *
     * @param sent         1 if a notification has been sent, 1 if a goal notification has been sent,
     *                     0 otherwise
     * @param clicked      1 if a notification has been clicked on, 1 if a goal notification has been
     *                     sent, 0 otherwise
     * @param context      the application context which the method has been called from and which will
     *                     be used to access and write to the external storage of the device
     */
    private static void recordTimeStamp(int sent, int clicked, int movement, int acknowledged, Context context) {
        Calendar cal = Calendar.getInstance();
        ArrayList<String> contents = new ArrayList<String>();
        String line;
        String prevLine = null;
        String[] items;
        SharedPreferences shared = context.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = shared.edit();
        editor.putInt(context.getString(R.string.store_last_ack), acknowledged);
        editor.commit();
        //Checks for external storage
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            File file = new File(dir, "TIMESTAMPLOG.txt");
            //Creates a new file if one doesn't already exist
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    Log.e("FILE_ERROR", "COULDN'T CREATE NEW FILE");
                }
            }
            //Reads the current contents of the log file
            try {
                if (file.isFile() && file.canRead()) {
                    BufferedReader br = new BufferedReader(new FileReader(file));
                    while ((line = br.readLine()) != null) {
                        contents.add(line + '\n');
                        prevLine = line;
                    }
                    try {
                        items = prevLine.split(" ");
                        if (items[6].equals("10") && (movement == 1)){
                            if(DataAnalysis.analyse(prevLine, cal, context)){
                                recordNotification(0,0,0,1, context);
                            }
                        }
                    }catch(NullPointerException e){
                        Log.e("SPLIT", "NULL POINTER");
                    }
                }
                //Adds the new log to the file up to 1000 items. At 1000 items the oldest is removed
                if (file.isFile() & file.canWrite()) {
                    BufferedWriter bw = new BufferedWriter(new FileWriter(file));
                    if (contents.size() > 999) {
                        contents.remove(0);
                    }
                    for (String ts : contents) {
                        bw.write(ts);
                    }
                    bw.write(cal.getTime().toString() + " " + sent + "" + clicked + " " + acknowledged);
                    bw.close();
                }

            } catch (IOException e) {
                Log.e("FILEERROR", "COULD NOT WRITE TO FILE");
            }
        }
    }

    /**
     * Keeps track of the number of each type of record stored in a file. Three integers only are
     * written to the file used in this method. The first is the number of notifications sent, the
     * second is the number clicked on and the third is the number of movements registered.
     *
     * @param sent         1 if a notification has been sent, 1 if a goal notification has been
     *                     sent 0 otherwise
     * @param clicked      1 if a notification has been clicked on, 1 if a goal notification has
     *                     been sent, 0 otherwise
     * @param movement     1 if a movement has been registered, 0 otherwise
     * @param acknowledged 1 if a notification has been acknowledged by a movement, 0 otherwise
     * @param context      the application context which the method has been called from and which
     *                     will be used to access and write to the external storage of the device
     */
    public static void recordNotification(int sent, int clicked, int movement, int acknowledged, Context context){
        int ns = 0;
        int nc = 0;
        int nm = 0;
        int na = 0;
        //Checks if external storage is present
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            if (dir.isDirectory()) {
                File file = new File(dir, "NOTLOG.txt");
                //Creates a new file if one does not already exist
                try {
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    //Reads the current contents of the file
                    if (file.isFile() & file.canRead()) {
                        BufferedReader br = new BufferedReader(new FileReader(file));
                        try {
                            ns = Integer.parseInt(br.readLine());
                            nc = Integer.parseInt(br.readLine());
                            nm = Integer.parseInt(br.readLine());
                            na = Integer.parseInt(br.readLine());
                        } catch (NumberFormatException e) {
                            Log.e("FILEERROR", "COULD NOT READ FILE");
                        }
                        //Writes the modified values to the file based on the parameters
                        if (file.isFile() & file.canWrite()) {
                            BufferedWriter bw = new BufferedWriter(new FileWriter(file));
                            ns += sent;
                            nc += clicked;
                            nm += movement;
                            na += acknowledged;
                            bw.write(String.valueOf(ns));
                            bw.newLine();
                            bw.write(String.valueOf(nc));
                            bw.newLine();
                            bw.write(String.valueOf(nm));
                            bw.newLine();
                            bw.write(String.valueOf(na));
                            bw.close();
                        }
                    }
                } catch (IOException e) {
                    Log.e("FILEERROR", "WRITE FILE ERROR");
                }
            }
        }
        recordTimeStamp(sent, clicked, movement, acknowledged, context);
        getExtraData(context);
    }

    /**
     * Compresses the files present in the log directory into a single zipped folder.
     *
     * @param context the application context from which this method was called
     * @return the zip folder created
     */
    static File zipFiles(Context context) {
        File file = null;
        //Checks for presence of external storage
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            if (dir.isDirectory()) {
                file = new File(dir, "ZIPFILE.zip");
                //Deletes any old zip folder and creates a new one
                try {
                    if (file.exists()) {
                        file.delete();
                    }
                    if (file.createNewFile()) {
                        FileOutputStream fos = new FileOutputStream(file);
                        ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos));
                        File[] files = dir.listFiles();
                        try {
                            //For each file in the directory buffer the file the write into the zip
                            //folder by byte
                            for (int i = 0; i < files.length; i++) {
                                byte[] buffer = new byte[1024];
                                FileInputStream fis = new FileInputStream(files[i]);
                                zos.putNextEntry(new ZipEntry(files[i].getName()));
                                int length;
                                while ((length = fis.read(buffer)) > 0) {
                                    zos.write(buffer, 0, length);
                                }
                                zos.closeEntry();
                                fis.close();
                            }
                        } finally {
                            zos.close();
                        }
                    }
                } catch (IOException e) {
                    Log.e("ZIPERROR", "COULDN'T ZIP FOLDER");
                }
            }
        }
        return file;
    }

    /**
     * Records the type of preferences which were present when a notification was sent.
     *
     * @param context the application context which this method was called from
     */
    public static void recordNotificationType(Context context) {
        SharedPreferences shared =
                context.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE);
        int sound = 0;
        int led = 0;
        int vibration = 0;
        //Checks if external storage is available
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            if (dir.isDirectory()) {
                File file = new File(dir, "TYPELOG.txt");
                //If the file does not exist it is created
                try {
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    //Reads the current contents of the file
                    if (file.isFile() & file.canRead()) {
                        BufferedReader br = new BufferedReader(new FileReader(file));
                        try {
                            sound = Integer.parseInt(br.readLine());
                            led = Integer.parseInt(br.readLine());
                            vibration = Integer.parseInt(br.readLine());
                        } catch (NumberFormatException e) {
                            Log.e("FILEERROR", "CANNOT READ FILE");
                        }
                        //Writes new information to file based on parameters
                        if (file.isFile() & file.canWrite()) {
                            BufferedWriter bw = new BufferedWriter(new FileWriter(file));
                            if (shared.getBoolean(context.getString(R.string.audio), false)) {
                                sound++;
                            }
                            if (shared.getBoolean(context.getString(R.string.led), false)) {
                                led++;
                            }
                            if (shared.getBoolean(context.getString(R.string.vibration), false)) {
                                vibration++;
                            }
                            bw.write(String.valueOf(sound));
                            bw.newLine();
                            bw.write(String.valueOf(led));
                            bw.newLine();
                            bw.write(String.valueOf(vibration));
                            bw.close();
                        }
                    }
                } catch (IOException e) {
                    Log.e("FILEERROR", "COULD NOT WRITE TO FILE");
                }
            }
        }
    }


    public static void recordData(Context context) {
        SharedPreferences preferences = context.getSharedPreferences(context.getString(R.string.preference_file_key),
                Context.MODE_PRIVATE);
        double latitude = Double.longBitsToDouble(preferences.getLong(context.getString(R.string.store_lat), 0));
        double longitude = Double.longBitsToDouble(preferences.getLong(context.getString(R.string.store_long), 0));
        String places = "";
        for(int i  = 0 ; i < 3; i++) {
            try {
                String place = preferences.getString(context.getString(R.string.store_place) + String.valueOf(i), "");
                place = place.replace( " ", "");
                String placeType = String.valueOf(preferences.getInt(context.getString(R.string.store_place_type) + String.valueOf(i), 0));
                String placeLikelihood = String.valueOf(preferences.getFloat(context.getString(R.string.store_place_like) + String.valueOf(i), 0));
                places = places + place + " " + placeType + " " + placeLikelihood + " ";
            }catch(NullPointerException | IndexOutOfBoundsException e){

            }
            }
        float temperature = preferences.getFloat(context.getString(R.string.store_temp), 0);
        String line;
        ArrayList<String> contents = new ArrayList<String>();
        String writeLine = String.valueOf(latitude) + " " + String.valueOf(longitude) + " " + places
                + String.valueOf(temperature);
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            File file = new File(dir, "DATALOG.txt");
            //Creates a new file if one doesn't already exist
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    Log.e("FILE_ERROR", "COULDN'T CREATE NEW FILE");
                }
            }
            //Reads the current contents of the log file
            try {
                if (file.isFile() && file.canRead()) {
                    BufferedReader br = new BufferedReader(new FileReader(file));
                    while ((line = br.readLine()) != null) {
                        contents.add(line + '\n');
                    }
                }
                //Adds the new log to the file up to 1000 items. At 1000 items the oldest is removed
                if (file.isFile() & file.canWrite()) {
                    BufferedWriter bw = new BufferedWriter(new FileWriter(file));
                    if (contents.size() > 999) {
                        contents.remove(0);
                    }
                    for (String ts : contents) {
                        bw.write(ts);
                    }
                    bw.write(writeLine);
                    bw.close();
                }

            } catch (IOException e) {
                Log.e("FILEERROR", "COULD NOT WRITE TO FILE");
            }

        }
        DataAnalysis.placeAnalysis(context);
    }


    private static void getExtraData(final Context context){
        GoogleApiClient client;
        client = new GoogleApiClient.Builder(context)
                .addApi(Awareness.getSnapshotClient(context).getApi()).build();
        client.connect();
        try {
            Awareness.getSnapshotClient(context).getHeadphoneState().addOnCompleteListener(new OnCompleteListener<HeadphoneStateResponse>() {
                @Override
                public void onComplete(@NonNull Task<HeadphoneStateResponse> task) {
                    task.getResult().getHeadphoneState().getState();
                }
            });
            Awareness.getSnapshotClient(context).getLocation().addOnCompleteListener(new OnCompleteListener<LocationResponse>() {
                @Override
                public void onComplete(@NonNull Task<LocationResponse> task) {
                    SharedPreferences preferences = context.getSharedPreferences(context.getString(R.string.preference_file_key),
                            Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = preferences.edit();
                        editor.putLong(context.getString(R.string.store_lat), Double.doubleToLongBits(task.getResult().getLocation().getLatitude()));
                        editor.putLong(context.getString(R.string.store_long), Double.doubleToLongBits(task.getResult().getLocation().getLongitude()));
                    editor.commit();
                }

            });
            Awareness.getSnapshotClient(context).getPlaces().addOnCompleteListener(new OnCompleteListener<PlacesResponse>() {
                @Override
                public void onComplete(@NonNull Task<PlacesResponse> task) {
                    SharedPreferences preferences = context.getSharedPreferences(context.getString(R.string.preference_file_key),
                            Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = preferences.edit();
                    List<PlaceLikelihood> placeLikelihoods = task.getResult().getPlaceLikelihoods();
                    for(int i = 0; i < 3; i++) {
                        try {
                            PlaceLikelihood placeLikelihood = placeLikelihoods.get(i);
                            Place place = placeLikelihood.getPlace();
                            editor.putFloat(context.getString(R.string.store_place_like) +
                                    String.valueOf(i), placeLikelihood.getLikelihood());
                            editor.putString(context.getString(R.string.store_place)
                                    + String.valueOf(i), String.valueOf(place.getName()));
                            editor.putInt(context.getString(R.string.store_place_type)
                                    + String.valueOf(i), place.getPlaceTypes().get(i));
                        } catch (NullPointerException | IndexOutOfBoundsException e) {

                        }
                    }
                    editor.commit();
                }
            });
            Awareness.getSnapshotClient(context).getWeather().addOnCompleteListener(new OnCompleteListener<WeatherResponse>() {
                @Override
                public void onComplete(@NonNull Task<WeatherResponse> task) {
                    SharedPreferences preferences = context.getSharedPreferences(context.getString(R.string.preference_file_key),
                            Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putFloat(context.getString(R.string.store_temp),task.getResult().getWeather().getTemperature(Weather.CELSIUS));
                    editor.commit();
                }
            });
            recordData(context);
        }catch(SecurityException e){
            Intent i = new Intent(context, RequestPermission.class);
            context.startActivity(i);
        }
    }

    static List<com.notificationFramework.sedentary.frontEnd.Place> getPlaces(Context context){
        String line = "";
        String[] items;
        List<com.notificationFramework.sedentary.frontEnd.Place> places = new ArrayList<>();
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            File file = new File(dir, "DATALOG.txt");
            //Creates a new file if one doesn't already exist
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    Log.e("FILE_ERROR", "COULDN'T CREATE NEW FILE");
                }
            }
            //Reads the current contents of the log file
            try {
                if (file.isFile() && file.canRead()) {
                    BufferedReader br = new BufferedReader(new FileReader(file));
                    while ((line = br.readLine()) != null) {
                        items = line.split(" ");
                        for(int i = 0; i < 3; i++) {
                            try {
                                com.notificationFramework.sedentary.frontEnd.Place place = new com.notificationFramework.sedentary.frontEnd.Place(
                                        items[i + 2], Float.parseFloat(items[i + 4]), Integer.parseInt(items[i + 3]));
                                places.add(place);
                            }catch(NumberFormatException | IndexOutOfBoundsException e){

                            }
                        }
                    }
                }
            }catch(IOException e){

            }
        }
        return places;
    }

    static void writePopularType(Context context, int popularType, int acknowledged) {
        String line = "";
        ArrayList<String> contents = new ArrayList<String>();
        boolean matched = false;
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            File file = new File(dir, "POPLOG.txt");
            //Creates a new file if one doesn't already exist
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    Log.e("FILE_ERROR", "COULDN'T CREATE NEW FILE");
                }
            }
            //Reads the current contents of the log file
            try {
                if (file.isFile() && file.canRead()) {
                    BufferedReader br = new BufferedReader(new FileReader(file));
                    while ((line = br.readLine()) != null) {
                        contents.add(line + '\n');
                    }
                }
                if (file.isFile() & file.canWrite()) {
                    BufferedWriter bw = new BufferedWriter(new FileWriter(file));
                    for (String s : contents) {
                        String[] items = s.split(" ");
                        if (items[0].equals(String.valueOf(popularType))) {
                            int no = Integer.parseInt(items[1].trim());
                            no++;
                            int ack = Integer.parseInt(items[2].trim());
                            ack += acknowledged;
                            SharedPreferences shared = context.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE);
                            SharedPreferences.Editor editor = shared.edit();
                            editor.putInt(context.getString(R.string.place_ack_total), ack );
                            editor.apply();
                            bw.write(items[0] + " " + String.valueOf(no) + " " + ack);
                            matched = true;
                        }else{
                            bw.write(s);
                        }
                    }
                    if(!matched){
                        bw.write(String.valueOf(popularType) + " " + String.valueOf(1) + " " + acknowledged);
                    }
                    bw.close();
                }
            }catch(IOException e){
        }
    }
    }

    static int findMostPopularType(Context context) {
        String line = "";
        ArrayList<String> contents = new ArrayList<String>();
        if (isExternalStorageMounted()) {
            File dir = getDirectory(context);
            File file = new File(dir, "POPLOG.txt");
            //Creates a new file if one doesn't already exist
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    Log.e("FILE_ERROR", "COULDN'T CREATE NEW FILE");
                }
            }
            //Reads the current contents of the log file
            try {
                if (file.isFile() && file.canRead()) {
                    BufferedReader br = new BufferedReader(new FileReader(file));
                    while ((line = br.readLine()) != null) {
                        contents.add(line + '\n');
                    }
                    if(contents.size() >= 100) {
                        return mostPopular(contents);
                    }
                }
            }catch(IOException e){

            }

        }
        return -1;
    }

    private static int mostPopular(List<String> contents){
        int count = 1;
        int tempCount;
        String[] items = contents.get(0).split(" ");
        int popular = Integer.parseInt(items[0]);
        int temp;
        for (int i = 0; i < (contents.size() - 1); i++)
        {
            items = contents.get(i).split(" ");
            temp = Integer.parseInt(items[0]);
            tempCount = Integer.parseInt(items[1]);
            if (tempCount > count){
                popular = temp;
                count = tempCount;
            }
        }
        return popular;
    }

    /**
     * Checks if external storage is available on the device.
     *
     * @return true if external storage is available, false otherwise
     */
    private static boolean isExternalStorageMounted() {
        return (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()));

    }


    /**
     * Gets the directory for the application where data files should be stored. This ensures each
     * file uses the same directory and makes compresssion of data easier also.
     *
     * @param context the application context from which this method was called
     * @return the directory for files to be placed in
     */
    private static File getDirectory(Context context) {
        File file = new File(context.getExternalFilesDir(
                Environment.DIRECTORY_DOCUMENTS), "NOTLOGS");
        if (!file.exists()) {
            if (!file.mkdirs()) {
                Log.e("DIRERROR", "Directory not created");
            }
        }
        return file;
    }

}