package com.marverenic.music.data.store; import android.content.Context; import android.support.v4.util.LongSparseArray; import com.marverenic.music.model.Song; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.Properties; import rx.Observable; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; import timber.log.Timber; public class LocalPlayCountStore implements PlayCountStore { private static final String PLAY_COUNT_FILENAME = ".playcount"; private static final String PLAY_COUNT_HEADER = "This file contains play count information for " + "Jockey and should not be edited"; private Context mContext; private final LongSparseArray<Count> mCounts; public LocalPlayCountStore(Context context) { mContext = context; mCounts = new LongSparseArray<>(); } @Override public Observable<Void> refresh() { return Observable.fromCallable( () -> { mCounts.clear(); Properties playCountMap = getPlayCounts(); Enumeration iterator = playCountMap.propertyNames(); while (iterator.hasMoreElements()) { String key = (String) iterator.nextElement(); String value = playCountMap.getProperty(key); Count count = new Count(value); mCounts.put(Long.parseLong(key), count); } return (Void) null; }) .observeOn(Schedulers.io()) .subscribeOn(AndroidSchedulers.mainThread()); } private File getPlayCountFile() { return new File(mContext.getExternalFilesDir(null), PLAY_COUNT_FILENAME); } private Properties getPlayCounts() throws IOException { File file = getPlayCountFile(); Properties playCounts = new Properties(); if (file.exists()) { InputStream inputStream = null; try { inputStream = new FileInputStream(file); playCounts.load(inputStream); } finally { if (inputStream != null) { inputStream.close(); } } } return playCounts; } @Override public void save() { Properties properties = new Properties(); for (int i = 0; i < mCounts.size(); i++) { String key = Long.toString(mCounts.keyAt(i)); String value = mCounts.valueAt(i).getCommaSeparatedValues(); properties.put(key, value); } try { writePlayCounts(properties); } catch (IOException ioException) { Timber.e(ioException, "save: Failed to write play counts to disk"); } } private void writePlayCounts(Properties playCounts) throws IOException { FileWriter fileWriter = null; try { fileWriter = new FileWriter(getPlayCountFile()); playCounts.store(fileWriter, PLAY_COUNT_HEADER); } finally { if (fileWriter != null) { fileWriter.close(); } } } @Override public int getPlayCount(Song song) { Count count = mCounts.get(song.getSongId()); if (count == null) { return 0; } else { return count.mPlays; } } @Override public int getSkipCount(Song song) { Count count = mCounts.get(song.getSongId()); if (count == null) { return 0; } else { return count.mSkips; } } @Override public long getPlayDate(Song song) { Count count = mCounts.get(song.getSongId()); if (count == null) { return 0; } else { return count.mDate; } } @Override public void incrementPlayCount(Song song) { setPlayCount(song, getPlayCount(song) + 1); } @Override public void incrementSkipCount(Song song) { setSkipCount(song, getSkipCount(song) + 1); } @Override public void setPlayDateToNow(Song song) { setPlayDate(song, System.currentTimeMillis() / 1000); } private Count getOrInitializeCount(Song song) { Count count = mCounts.get(song.getSongId()); if (count == null) { count = new Count(); mCounts.put(song.getSongId(), count); } return count; } @Override public void setPlayCount(Song song, int count) { getOrInitializeCount(song).mPlays = count; } @Override public void setSkipCount(Song song, int count) { getOrInitializeCount(song).mSkips = count; } @Override public void setPlayDate(Song song, long timeInUnixSeconds) { getOrInitializeCount(song).mDate = timeInUnixSeconds; } private static class Count { int mPlays; int mSkips; long mDate; Count() { } Count(String commaSeparatedValues) { String[] originalValues = commaSeparatedValues.split(","); int playCount = Integer.parseInt(originalValues[0]); int skipCount = Integer.parseInt(originalValues[1]); long playDate = 0; if (originalValues.length > 2) { playDate = Long.parseLong(originalValues[2]); } mPlays = playCount; mSkips = skipCount; mDate = playDate; } public String getCommaSeparatedValues() { return mPlays + "," + mSkips + "," + mDate; } } }