package yuku.alkitab.base.ac; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.ShareCompat; import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.NestedScrollView; import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.text.format.DateFormat; import android.text.method.LinkMovementMethod; import android.util.Log; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import com.afollestad.materialdialogs.MaterialDialog; import com.google.android.gms.analytics.HitBuilders; import yuku.afw.V; import yuku.afw.storage.Preferences; import yuku.alkitab.base.App; import yuku.alkitab.base.S; import yuku.alkitab.base.U; import yuku.alkitab.base.ac.base.BaseLeftDrawerActivity; import yuku.alkitab.base.devotion.ArticleMeidA; import yuku.alkitab.base.devotion.ArticleMorningEveningEnglish; import yuku.alkitab.base.devotion.ArticleRenunganHarian; import yuku.alkitab.base.devotion.ArticleRoc; import yuku.alkitab.base.devotion.ArticleSantapanHarian; import yuku.alkitab.base.devotion.DevotionArticle; import yuku.alkitab.base.devotion.DevotionDownloader; import yuku.alkitab.base.storage.Prefkey; import yuku.alkitab.base.util.Background; import yuku.alkitab.base.util.Jumper; import yuku.alkitab.base.widget.CallbackSpan; import yuku.alkitab.base.widget.LeftDrawer; import yuku.alkitab.base.widget.TwofingerLinearLayout; import yuku.alkitab.debug.BuildConfig; import yuku.alkitab.debug.R; import yuku.alkitab.reminder.ac.DevotionReminderActivity; import yuku.alkitab.util.Ari; import yuku.alkitabintegration.display.Launcher; import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class DevotionActivity extends BaseLeftDrawerActivity implements LeftDrawer.Devotion.Listener { public static final String TAG = DevotionActivity.class.getSimpleName(); private static final int REQCODE_share = 1; public static final DevotionDownloader devotionDownloader = new DevotionDownloader(); static final ThreadLocal<SimpleDateFormat> yyyymmdd = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd", Locale.US); } }; TwofingerLinearLayout.Listener root_listener = new TwofingerLinearLayout.OnefingerListener() { @Override public void onOnefingerLeft() { bNext_click(); } @Override public void onOnefingerRight() { bPrev_click(); } }; public static Intent createIntent() { return new Intent(App.context, DevotionActivity.class); } @Override public void bPrev_click() { currentDate.setTime(currentDate.getTime() - 3600 * 24 * 1000); display(); } @Override public void bNext_click() { currentDate.setTime(currentDate.getTime() + 3600 * 24 * 1000); display(); } @Override public void bReload_click() { willNeed(this.currentKind, yyyymmdd.get().format(currentDate), true); } @Override public void cbKind_itemSelected(final DevotionKind kind) { currentKind = kind; Preferences.setString(Prefkey.devotion_last_kind_name, currentKind.name); Background.run(() -> prefetch(currentKind)); display(); } @Override protected LeftDrawer getLeftDrawer() { return leftDrawer; } public enum DevotionKind { SH("sh", "Santapan Harian", "Persekutuan Pembaca Alkitab") { @Override public DevotionArticle getArticle(final String date) { return new ArticleSantapanHarian(date); } @Override public String getShareUrl(final SimpleDateFormat format, final Date date) { return "http://www.sabda.org/publikasi/e-sh/print/?edisi=" + yyyymmdd.get().format(date); } }, MEID_A("meid-a", "Renungan Pagi", "Charles H. Spurgeon") { @Override public DevotionArticle getArticle(final String date) { return new ArticleMeidA(date); } @Override public String getShareUrl(final SimpleDateFormat format, final Date date) { return "http://www.bibleforandroid.com/renunganpagi/" + yyyymmdd.get().format(date).substring(4); } }, ROC("roc", "My Utmost (B. Indonesia)", "Oswald Chambers") { @Override public DevotionArticle getArticle(final String date) { return new ArticleRoc(date); } @Override public String getShareUrl(final SimpleDateFormat format, final Date date) { return null; } }, RH("rh", "Renungan Harian", "Yayasan Gloria") { @Override public DevotionArticle getArticle(final String date) { return new ArticleRenunganHarian(date); } @Override public String getShareUrl(final SimpleDateFormat format, final Date date) { return "http://www.sabda.org/publikasi/e-rh/print/?edisi=" + yyyymmdd.get().format(date); } @Override public int getPrefetchDays() { return 3; } }, ME_EN("me-en", "Morning & Evening", "Charles H. Spurgeon") { @Override public DevotionArticle getArticle(final String date) { return new ArticleMorningEveningEnglish(date); } @Override public String getShareUrl(final SimpleDateFormat format, final Date date) { return "http://www.ccel.org/ccel/spurgeon/morneve.d" + yyyymmdd.get().format(date) + "am.html"; } },; public final String name; public final String title; public final String subtitle; DevotionKind(final String name, final String title, final String subtitle) { this.name = name; this.title = title; this.subtitle = subtitle; } public static DevotionKind getByName(String name) { if (name == null) return null; for (final DevotionKind kind : values()) { if (name.equals(kind.name)) { return kind; } } return null; } public abstract DevotionArticle getArticle(final String date); @Nullable public abstract String getShareUrl(SimpleDateFormat format, Date date); public int getPrefetchDays() { return 31; } } public static final DevotionKind DEFAULT_DEVOTION_KIND = DevotionKind.SH; DrawerLayout drawerLayout; LeftDrawer.Devotion leftDrawer; TwofingerLinearLayout root; TextView lContent; NestedScrollView scrollContent; boolean renderSucceeded = false; // currently shown DevotionKind currentKind; Date currentDate; static class LongReadChecker extends Handler { DevotionKind startKind; String startDate; final WeakReference<DevotionActivity> ac; public LongReadChecker(DevotionActivity activity) { ac = new WeakReference<>(activity); } /** * This will be called 30 seconds after startKind and startDate are set. */ @Override public void handleMessage(final Message msg) { final DevotionActivity ac = this.ac.get(); if (ac == null) return; if (ac.isFinishing()) { Log.d(TAG, "Activity is already closed"); return; } final String currentDate = yyyymmdd.get().format(ac.currentDate); if (U.equals(startKind, ac.currentKind) && U.equals(startDate, currentDate)) { Log.d(TAG, "Long read detected: now=[" + ac.currentKind + " " + currentDate + "]"); App.getTracker().send(new HitBuilders.EventBuilder("devotion-longread", startKind.name).setLabel(startDate).setValue(30L).build()); } else { Log.d(TAG, "Not long enough for long read: previous=[" + startKind + " " + startDate + "] now=[" + ac.currentKind + " " + currentDate + "]"); } } public void start() { final DevotionActivity ac = this.ac.get(); if (ac == null) return; startKind = ac.currentKind; startDate = yyyymmdd.get().format(ac.currentDate); removeMessages(1); sendEmptyMessageDelayed(1, BuildConfig.DEBUG ? 10000 : 30000); } } final LongReadChecker longReadChecker = new LongReadChecker(this); final BroadcastReceiver br = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); if (DevotionDownloader.ACTION_DOWNLOADED.equals(action)) { // is it for us? final String name = intent.getStringExtra("name"); final String date = intent.getStringExtra("date"); if (yyyymmdd.get().format(currentDate).equals(date) && currentKind.name.equals(name)) { display(); } } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_devotion); drawerLayout = V.get(this, R.id.drawerLayout); leftDrawer = V.get(this, R.id.left_drawer); leftDrawer.configure(this, drawerLayout); final Toolbar toolbar = V.get(this, R.id.toolbar); setSupportActionBar(toolbar); final ActionBar actionBar = getSupportActionBar(); assert actionBar != null; actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp); root = V.get(this, R.id.root); lContent = V.get(this, R.id.lContent); scrollContent = V.get(this, R.id.scrollContent); root.setTwofingerEnabled(false); root.setListener(root_listener); final DevotionKind storedKind = DevotionKind.getByName(Preferences.getString(Prefkey.devotion_last_kind_name, DEFAULT_DEVOTION_KIND.name)); currentKind = storedKind == null ? DEFAULT_DEVOTION_KIND : storedKind; currentDate = new Date(); Background.run(() -> prefetch(currentKind)); display(); } @Override protected void onStart() { super.onStart(); { // apply background color, and clear window background to prevent overdraw getWindow().setBackgroundDrawableResource(android.R.color.transparent); scrollContent.setBackgroundColor(S.applied.backgroundColor); } // text formats lContent.setTextColor(S.applied.fontColor); lContent.setTypeface(S.applied.fontFace, S.applied.fontBold); lContent.setTextSize(TypedValue.COMPLEX_UNIT_DIP, S.applied.fontSize2dp); lContent.setLineSpacing(0, S.applied.lineSpacingMult); SettingsActivity.setPaddingBasedOnPreferences(lContent); getWindow().getDecorView().setKeepScreenOn(Preferences.getBoolean(getString(R.string.pref_keepScreenOn_key), getResources().getBoolean(R.bool.pref_keepScreenOn_default))); App.getLbm().registerReceiver(br, new IntentFilter(DevotionDownloader.ACTION_DOWNLOADED)); } @Override protected void onStop() { super.onStop(); App.getLbm().unregisterReceiver(br); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_devotion, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { final int itemId = item.getItemId(); if (itemId == android.R.id.home) { leftDrawer.toggleDrawer(); return true; } else if (itemId == R.id.menuCopy) { U.copyToClipboard(currentKind.title + "\n" + lContent.getText()); Snackbar.make(root, R.string.renungan_sudah_disalin, Snackbar.LENGTH_SHORT).show(); return true; } else if (itemId == R.id.menuShare) { final String shareUrl = currentKind.getShareUrl(yyyymmdd.get(), currentDate); final Intent intent = ShareCompat.IntentBuilder.from(DevotionActivity.this) .setType("text/plain") .setSubject(currentKind.title) .setText(currentKind.title + '\n' + getCurrentDateDisplay() + (shareUrl == null ? "" : ('\n' + shareUrl)) + "\n\n" + lContent.getText()) .getIntent(); startActivityForResult(ShareActivity.createIntent(intent, getString(R.string.bagikan_renungan)), REQCODE_share); return true; } else if (itemId == R.id.menuReminder) { startActivity(DevotionReminderActivity.createIntent()); return true; } return super.onOptionsItemSelected(item); } void display() { final String date = yyyymmdd.get().format(currentDate); final DevotionArticle article = S.getDb().tryGetDevotion(currentKind.name, date); if (article == null || !article.getReadyToUse()) { willNeed(currentKind, date, true); } if (article == null) { Log.d(TAG, "rendering null article"); } else { Log.d(TAG, "rendering article name=" + article.getKind().name + " date=" + article.getDate() + " readyToUse=" + article.getReadyToUse()); } if (article != null && article.getReadyToUse()) { renderSucceeded = true; lContent.setText(article.getContent(verseClickListener), TextView.BufferType.SPANNABLE); lContent.setLinksClickable(true); lContent.setMovementMethod(LinkMovementMethod.getInstance()); } else { renderSucceeded = false; if (article == null) { lContent.setText(R.string.belum_tersedia_menunggu_pengambilan_data_lewat_internet_pastikan_ada); } else { // berarti belum siap pakai lContent.setText(R.string.belum_tersedia_mungkin_tanggal_yang_diminta_belum_disiapkan); } } { // widget texts final String dateDisplay = getCurrentDateDisplay(); // action bar final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setTitle(currentKind.title); actionBar.setSubtitle(dateDisplay); } // drawer texts final LeftDrawer.Devotion.Handle handle = leftDrawer.getHandle(); handle.setDevotionKind(currentKind); handle.setDevotionDate(dateDisplay); } if (renderSucceeded) { App.getTracker().send(new HitBuilders.EventBuilder("devotion-render", currentKind.name).setLabel(yyyymmdd.get().format(currentDate)).setValue(0L).build()); longReadChecker.start(); } } private String getCurrentDateDisplay() { return dayOfWeekName(currentDate) + ", " + DateFormat.getDateFormat(this).format(currentDate); } static class PatchTextExtraInfoJson { String type; String kind; String date; } final CallbackSpan.OnClickListener<String> verseClickListener = (widget, reference) -> { Log.d(TAG, "Clicked verse reference inside devotion: " + reference); if (reference.startsWith("patchtext:")) { final Uri uri = Uri.parse(reference); final String referenceUrl = uri.getQueryParameter("referenceUrl"); final PatchTextExtraInfoJson extraInfo = new PatchTextExtraInfoJson(); extraInfo.type = "devotion"; extraInfo.kind = currentKind.name; extraInfo.date = yyyymmdd.get().format(currentDate); startActivity(PatchTextActivity.createIntent(lContent.getText(), App.getDefaultGson().toJson(extraInfo), referenceUrl)); } else { int ari; if (reference.startsWith("ari:")) { ari = Integer.parseInt(reference.substring(4)); startActivity(Launcher.openAppAtBibleLocationWithVerseSelected(ari)); } else { // we need to parse it manually by text final Jumper jumper = new Jumper(reference); if (!jumper.getParseSucceeded()) { new MaterialDialog.Builder(DevotionActivity.this) .content(getString(R.string.alamat_tidak_sah_alamat, reference)) .positiveText(R.string.ok) .show(); return; } // Make sure references are parsed using Indonesian book names. String[] bookNames = getResources().getStringArray(R.array.standard_book_names_in); int[] bookIds = new int[bookNames.length]; for (int i = 0, len = bookNames.length; i < len; i++) { bookIds[i] = i; } final int bookId = jumper.getBookId(bookNames, bookIds); final int chapter_1 = jumper.getChapter(); final int verse_1 = jumper.getVerse(); ari = Ari.encode(bookId, chapter_1, verse_1); final boolean hasRange = jumper.getHasRange(); if (hasRange || verse_1 == 0) { startActivity(Launcher.openAppAtBibleLocation(ari)); } else { startActivity(Launcher.openAppAtBibleLocationWithVerseSelected(ari)); } } } }; private static final int[] WEEKDAY_NAMES_RESIDS = {R.string.hari_minggu, R.string.hari_senin, R.string.hari_selasa, R.string.hari_rabu, R.string.hari_kamis, R.string.hari_jumat, R.string.hari_sabtu}; private String dayOfWeekName(Date date) { @SuppressWarnings("deprecation") int day = date.getDay(); return getString(WEEKDAY_NAMES_RESIDS[day]); } synchronized void willNeed(final DevotionKind kind, final String date, final boolean prioritize) { final DevotionArticle article = kind.getArticle(date); devotionDownloader.add(article, prioritize); } public void prefetch(DevotionKind kind) { final Date today = new Date(); // delete those older than 180 days! final int deleted = S.getDb().deleteDevotionsWithTouchTimeBefore(new Date(today.getTime() - 180 * 86400_000L)); if (deleted > 0) { Log.d(TAG, "old devotions deleted: " + deleted); } for (int i = 0; i < kind.getPrefetchDays(); i++) { final String date = yyyymmdd.get().format(today); if (S.getDb().tryGetDevotion(kind.name, date) == null) { Log.d(TAG, "Prefetcher need to get " + kind + " " + date); willNeed(kind, date, false); } // go to the next day today.setTime(today.getTime() + 86400_000L); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQCODE_share && resultCode == RESULT_OK) { final ShareActivity.Result result = ShareActivity.obtainResult(data); if (result != null && result.chosenIntent != null) { final Intent chosenIntent = result.chosenIntent; if (U.equals(chosenIntent.getComponent().getPackageName(), "com.facebook.katana")) { final String shareUrl = currentKind.getShareUrl(yyyymmdd.get(), currentDate); if (shareUrl != null) { chosenIntent.putExtra(Intent.EXTRA_TEXT, shareUrl); startActivity(chosenIntent); } else { new MaterialDialog.Builder(this) .content(R.string.no_url_for_facebook) .positiveText(R.string.ok) .show(); } } else { startActivity(chosenIntent); } } } } }