package yuku.alkitab.base.util; import android.support.annotation.Nullable; import android.util.Log; import yuku.afw.App; import yuku.afw.storage.Preferences; import yuku.alkitab.base.S; import yuku.alkitab.base.br.DailyVerseAppWidgetReceiver; import yuku.alkitab.base.model.MVersionDb; import yuku.alkitab.base.model.MVersionInternal; import yuku.alkitab.base.model.VersionImpl; import yuku.alkitab.debug.BuildConfig; import yuku.alkitab.debug.R; import yuku.alkitab.model.Version; import yuku.alkitab.util.IntArrayList; import yuku.bintex.BintexReader; import java.io.IOException; import java.io.InputStream; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Random; /** * Handles list of predefined daily verse aris. * Also handles saved user settings about widgets. */ public abstract class DailyVerseData { static final String TAG = DailyVerseData.class.getSimpleName(); private static IntArrayList dailyVerses; public static class SavedState { public String versionId; public boolean darkText; public float textSize; /** Legacy */ public boolean transparentBackground; public int backgroundAlpha; // 0 to 255 public int click; } /** * Get list of ari for the specified parameter. * @param savedState WILL be changed AND SAVED in case of unavailable verses. * @param version to be checked to know whether we have the requested aris * @param direction try increasing {@link yuku.alkitab.base.util.DailyVerseData.SavedState#click} or decrease, according to this direction * @return list of ari */ @Nullable public static int[] getAris(int appWidgetId, final SavedState savedState, final Version version, final int direction) { final IntArrayList allDailyVerses = listAllDailyVerses(); final int size = allDailyVerses.size(); boolean savedStateChanged = false; int[] aris = null; final int maxTries = 20; for (int trial = 0; trial < maxTries; trial++) { final int index = getIndexFromSeed(appWidgetId, size, savedState.click); final int encoded = allDailyVerses.get(index); final int verseCount = encoded & 0xff; aris = new int[verseCount]; aris[0] = encoded >>> 8; for (int i = 1; i < verseCount; i++) { aris[i] = aris[i - 1] + 1; } // all verses must be available on the specified version boolean allAvailable = true; for (final int ari : aris) { final String verseText = version.loadVerseText(ari); if (verseText == null) { allAvailable = false; break; } } if (!allAvailable) { Log.d(TAG, "ari 0x" + Integer.toHexString(aris[0]) + " verseCount=" + verseCount + " click=" + savedState.click + " are not available in version " + savedState.versionId); if (trial != maxTries - 1) { savedState.click += direction; savedStateChanged = true; } aris = null; // remove unsuccessful attempt } else { break; } } if (savedStateChanged) { saveSavedState(appWidgetId, savedState); } return aris; } /** * Get index of the element of the array of all predefined daily verses. * @param appWidgetId seed * @param size seed * @param click seed */ private static int getIndexFromSeed(final int appWidgetId, final int size, final int click) { final Calendar calendar = GregorianCalendar.getInstance(); final long year = calendar.get(Calendar.YEAR); final long day = calendar.get(Calendar.DAY_OF_YEAR); final long fifteensecs = BuildConfig.DEBUG ? (calendar.get(Calendar.HOUR_OF_DAY) * 240 + calendar.get(Calendar.MINUTE) * 4 + calendar.get(Calendar.SECOND) / 15) : 0; final long randomDay = (((year - 1900) << 9) | day) + fifteensecs; final long seed = (appWidgetId << 20) | (randomDay + click); final Random r = new Random(seed); return r.nextInt(size); } private static IntArrayList listAllDailyVerses() { if (dailyVerses == null) { dailyVerses = new IntArrayList(); try { InputStream is = App.context.getResources().openRawResource(R.raw.daily_verses_bt); BintexReader br = new BintexReader(is); while (true) { int ari = br.readInt(); if (ari == -1) { break; } final int verseCount = br.readUint8(); dailyVerses.add(ari << 8 | verseCount); } br.close(); } catch (IOException e) { throw new RuntimeException("Error reading daily verses", e); } } return dailyVerses; } public static Version getVersion(String versionId) { if (versionId == null) { return VersionImpl.getInternalVersion(); } if (MVersionInternal.getVersionInternalId().equals(versionId)) { return VersionImpl.getInternalVersion(); } // try database versions for (final MVersionDb mvDb : S.getDb().listAllVersions()) { if (mvDb.getVersionId().equals(versionId)) { if (mvDb.hasDataFile()) { return mvDb.getVersion(); } else { break; } } } Log.w(DailyVerseAppWidgetReceiver.TAG, "Version selected for app widget: " + versionId + " is no longer available. Reverting to internal version."); return VersionImpl.getInternalVersion(); } public static SavedState loadSavedState(final int appWidgetId) { final SavedState res = new SavedState(); res.darkText = Preferences.getBoolean("app_widget_" + appWidgetId + "_option_dark_text", false); res.textSize = Preferences.getFloat("app_widget_" + appWidgetId + "_option_text_size", 14.f); res.transparentBackground = Preferences.getBoolean("app_widget_" + appWidgetId + "_option_transparent_background", false); res.versionId = Preferences.getString("app_widget_" + appWidgetId + "_version"); res.click = Preferences.getInt("app_widget_" + appWidgetId + "_click", 0); res.backgroundAlpha = Preferences.getInt("app_widget_" + appWidgetId + "_option_backgroundAlpha", 255); return res; } /** * @param savedState null to delete saved state for the specified appWidgetId. */ public static void saveSavedState(final int appWidgetId, final SavedState savedState) { Preferences.hold(); try { if (savedState == null) { Preferences.remove("app_widget_" + appWidgetId + "_option_dark_text"); Preferences.remove("app_widget_" + appWidgetId + "_option_text_size"); Preferences.remove("app_widget_" + appWidgetId + "_option_transparent_background"); Preferences.remove("app_widget_" + appWidgetId + "_version"); Preferences.remove("app_widget_" + appWidgetId + "_click"); Preferences.remove("app_widget_" + appWidgetId + "_option_backgroundAlpha"); } else { Preferences.setBoolean("app_widget_" + appWidgetId + "_option_dark_text", savedState.darkText); Preferences.setFloat("app_widget_" + appWidgetId + "_option_text_size", savedState.textSize); Preferences.setBoolean("app_widget_" + appWidgetId + "_option_transparent_background", savedState.transparentBackground); Preferences.setString("app_widget_" + appWidgetId + "_version", savedState.versionId); Preferences.setInt("app_widget_" + appWidgetId + "_click", savedState.click); Preferences.setInt("app_widget_" + appWidgetId + "_option_backgroundAlpha", savedState.backgroundAlpha); } } finally { Preferences.unhold(); } } }