package yuku.alkitab.base.util; import android.support.annotation.NonNull; import android.util.Log; import yuku.alkitab.base.S; import yuku.alkitab.base.model.ReadingPlan; import yuku.alkitab.util.IntArrayList; import yuku.bintex.BintexReader; import yuku.bintex.ValueMap; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Date; public class ReadingPlanManager { public static final String TAG = ReadingPlanManager.class.getSimpleName(); private static final byte[] RPB_HEADER = {0x52, (byte) 0x8a, 0x61, 0x34, 0x00, (byte) 0xe0, (byte) 0xea}; public static long insertReadingPlanToDb(final byte[] data, final String name) { final ReadingPlan.ReadingPlanInfo info = new ReadingPlan.ReadingPlanInfo(); try { // check the file has correct header and infos final BintexReader reader = new BintexReader(new ByteArrayInputStream(data)); final boolean ok = readInfo(info, name, reader); reader.close(); if (!ok) { Log.e(TAG, "Error parsing reading plan data"); return 0; } info.startTime = new Date().getTime(); return S.getDb().insertReadingPlan(info, data); } catch (IOException e) { Log.e(TAG, "Error reading reading plan, should not happen", e); return 0; } } public static void updateReadingPlanProgress(final String readingPlanName, final int dayNumber, final int readingSequence, final boolean checked) { final int readingCode = dayNumber << 8 | readingSequence; final String gid = ReadingPlan.gidFromName(readingPlanName); if (checked) { S.getDb().insertOrUpdateReadingPlanProgress(gid, readingCode, System.currentTimeMillis()); } else { S.getDb().deleteReadingPlanProgress(gid, readingCode); } } public static void markAsReadUpTo(final String readingPlanName, final int[][] dailyVerses, final int upToDayNumber, final int upToReadingSequence) { final String gid = ReadingPlan.gidFromName(readingPlanName); final IntArrayList readingCodes = new IntArrayList(); for (int day = 0; day < dailyVerses.length; day++) { if (day > upToDayNumber) break; for (int sequence = 0, readingsADay = dailyVerses[day].length / 2; sequence < readingsADay; sequence++) { if (day == upToDayNumber && sequence > upToReadingSequence) break; readingCodes.add(day << 8 | sequence); } } S.getDb().insertOrUpdateMultipleReadingPlanProgresses(gid, readingCodes, System.currentTimeMillis()); } @NonNull public static ReadingPlan readVersion1(InputStream inputStream, String name) { try { ReadingPlan readingPlan = new ReadingPlan(); final BintexReader reader = new BintexReader(inputStream); try { if (!readInfo(readingPlan.info, name, reader)) { throw new RuntimeException("Cannot read info"); } if (readingPlan.info.version != 1) { throw new RuntimeException("Reading plan version is not 1"); } int[][] dailyVerses = new int[readingPlan.info.duration][]; int counter = 0; while (counter < readingPlan.info.duration) { int count = reader.readUint8(); if (count == -1) { throw new RuntimeException("Error reading."); } int[] aris = new int[count]; for (int j = 0; j < count; j++) { aris[j] = reader.readInt(); } dailyVerses[counter] = aris; counter++; } readingPlan.dailyVerses = dailyVerses; } finally { reader.close(); } if (reader.readUint8() != 0) { throw new RuntimeException("No footer."); } return readingPlan; } catch (IOException e) { throw new RuntimeException(e); } } /** * @return false if reading plan data is not in a valid format. */ public static boolean readInfo(final ReadingPlan.ReadingPlanInfo info, final String name, final BintexReader reader) throws IOException { byte[] headers = new byte[8]; reader.readRaw(headers); for (int i = 0; i < 7; i++) { if (RPB_HEADER[i] != headers[i]) { return false; } } info.version = headers[7]; info.name = name; final ValueMap map = reader.readValueSimpleMap(); info.title = map.getString("title"); info.description = map.getString("description"); info.duration = map.getInt("duration"); return true; } public static IntArrayList filterReadingCodesByDayStartEnd(IntArrayList readingCodes, int dayStart, int dayEnd) { IntArrayList res = new IntArrayList(); int start = dayStart << 8; int end = (dayEnd + 1) << 8; for (int i = 0; i < readingCodes.size(); i++) { final int readingCode = readingCodes.get(i); if (readingCode >= start && readingCode < end) { res.add(readingCode); } } return res; } public static void writeReadMarksByDay(IntArrayList readingCodes, boolean[] readMarks, int dayNumber) { readingCodes = filterReadingCodesByDayStartEnd(readingCodes, dayNumber, dayNumber); for (int i = 0; i < readingCodes.size(); i++) { final int readingCode = readingCodes.get(i); final int sequence = (readingCode & 0x000000ff); readMarks[sequence] = true; } } }