package yuku.alkitab.base.ac; import android.app.DatePickerDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.ColorStateList; import android.net.Uri; import android.os.Bundle; import android.support.v4.content.res.ResourcesCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import com.afollestad.materialdialogs.MaterialDialog; import yuku.afw.V; import yuku.afw.storage.Preferences; import yuku.afw.widget.EasyAdapter; import yuku.alkitab.base.App; import yuku.alkitab.base.S; import yuku.alkitab.base.ac.base.BaseLeftDrawerActivity; import yuku.alkitab.base.model.ReadingPlan; import yuku.alkitab.base.storage.Prefkey; import yuku.alkitab.base.util.Background; import yuku.alkitab.base.util.CurrentReading; import yuku.alkitab.base.util.Foreground; import yuku.alkitab.base.util.ReadingPlanManager; import yuku.alkitab.base.util.Sqlitil; import yuku.alkitab.base.widget.LeftDrawer; import yuku.alkitab.debug.BuildConfig; import yuku.alkitab.debug.R; import yuku.alkitab.util.IntArrayList; import yuku.alkitabintegration.display.Launcher; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; public class ReadingPlanActivity extends BaseLeftDrawerActivity implements LeftDrawer.ReadingPlan.Listener { public static final String TAG = ReadingPlanActivity.class.getSimpleName(); public static final String ACTION_READING_PLAN_PROGRESS_CHANGED = ReadingPlanActivity.class.getName() + ".action.READING_PLAN_PROGRESS_CHANGED"; private static final int REQCODE_openList = 1; DrawerLayout drawerLayout; LeftDrawer.ReadingPlan leftDrawer; private ReadingPlan readingPlan; private List<ReadingPlan.ReadingPlanInfo> downloadedReadingPlanInfos; private int todayNumber; private int dayNumber; /** * List of reading codes that is read for the current reading plan. * A reading code is a combination of day (left-bit-shifted by 8) and the reading sequence for that day starting from 0. */ private IntArrayList readReadingCodes; private boolean newDropDownItems; private ImageButton bLeft; private ImageButton bRight; private TextView bToday; private ListView lsReadingPlan; private ReadingPlanAdapter readingPlanAdapter; private ActionBar actionBar; private LinearLayout llNavigations; private FrameLayout flNoData; private Button bDownload; private boolean showDetails; float density; public static Intent createIntent() { return new Intent(App.context, ReadingPlanActivity.class); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reading_plan); density = getResources().getDisplayMetrics().density; 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); actionBar = getSupportActionBar(); assert actionBar != null; actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp); llNavigations = V.get(this, R.id.llNavigations); flNoData = V.get(this, R.id.flNoDataContainer); lsReadingPlan = V.get(this, R.id.lsTodayReadings); lsReadingPlan.setAdapter(readingPlanAdapter = new ReadingPlanAdapter()); bToday = V.get(this, R.id.bToday); bToday.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { final PopupMenu popupMenu = new PopupMenu(ReadingPlanActivity.this, v); final Menu menu = popupMenu.getMenu(); menu.add(0, 1, 0, getString(R.string.rp_showCalendar)); menu.add(0, 3, 0, getString(R.string.rp_gotoToday)); menu.add(0, 2, 0, getString(R.string.rp_gotoFirstUnread)); menu.add(0, 5, 0, getString(R.string.rp_menuCatchMeUp)); menu.add(0, 4, 0, getString(R.string.rp_setStartDate)); popupMenu.setOnMenuItemClickListener(menuItem -> { popupMenu.dismiss(); int itemId = menuItem.getItemId(); switch (itemId) { case 1: showCalendar(); break; case 2: gotoFirstUnread(); break; case 3: gotoToday(); break; case 4: showSetStartDateDialog(); break; case 5: catchMeUp(); break; } return true; }); popupMenu.show(); } private void gotoToday() { loadDayNumber(); changeDay(0); } private void gotoFirstUnread() { dayNumber = findFirstUnreadDay(); changeDay(0); } private void showCalendar() { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTimeInMillis(readingPlan.info.startTime); calendar.add(Calendar.DATE, dayNumber); DatePickerDialog.OnDateSetListener dateSetListener = (view, year, monthOfYear, dayOfMonth) -> { Calendar newCalendar = new GregorianCalendar(year, monthOfYear, dayOfMonth); Calendar startCalendar = GregorianCalendar.getInstance(); startCalendar.setTimeInMillis(readingPlan.info.startTime); int newDay = calculateDaysDiff(startCalendar, newCalendar); if (newDay < 0) { newDay = 0; } else if (newDay >= readingPlan.info.duration) { newDay = readingPlan.info.duration - 1; } dayNumber = newDay; changeDay(0); }; DatePickerDialog datePickerDialog = new DatePickerDialog(ReadingPlanActivity.this, dateSetListener, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)); datePickerDialog.show(); } private void showSetStartDateDialog() { final Calendar today = GregorianCalendar.getInstance(); today.setTimeInMillis(readingPlan.info.startTime); final DatePickerDialog.OnDateSetListener dateSetListener = (view, year, monthOfYear, dayOfMonth) -> { final Calendar newDate = new GregorianCalendar(year, monthOfYear, dayOfMonth, 2, 0, 0); // plus 2 hours to prevent DST-related problems if (readingPlan == null) { return; } final long startTime = newDate.getTimeInMillis(); readingPlan.info.startTime = startTime; S.getDb().updateReadingPlanStartDate(readingPlan.info.id, startTime); changeDay(0); loadDayNumber(); updateButtonStatus(); }; new DatePickerDialog(ReadingPlanActivity.this, dateSetListener, today.get(Calendar.YEAR), today.get(Calendar.MONTH), today.get(Calendar.DAY_OF_MONTH)).show(); } private void catchMeUp() { new MaterialDialog.Builder(ReadingPlanActivity.this) .content(R.string.rp_reset) .positiveText(R.string.ok) .onPositive((dialog, which) -> { int firstUnreadDay = findFirstUnreadDay(); Calendar calendar = GregorianCalendar.getInstance(); calendar.add(Calendar.DATE, -firstUnreadDay); S.getDb().updateReadingPlanStartDate(readingPlan.info.id, calendar.getTime().getTime()); loadReadingPlan(readingPlan.info.id); loadDayNumber(); readingPlanAdapter.load(); updateButtonStatus(); }) .negativeText(R.string.cancel) .show(); } }); bLeft = V.get(this, R.id.bLeft); bLeft.setOnClickListener(v -> changeDay(-1)); bRight = V.get(this, R.id.bRight); bRight.setOnClickListener(v -> changeDay(+1)); bDownload = V.get(this, R.id.bDownload); final long id = Preferences.getLong(Prefkey.active_reading_plan_id, 0); loadReadingPlan(id); prepareDropDownNavigation(); loadDayNumber(); App.getLbm().registerReceiver(reload, new IntentFilter(ACTION_READING_PLAN_PROGRESS_CHANGED)); } @Override protected void onDestroy() { super.onDestroy(); App.getLbm().unregisterReceiver(reload); } final BroadcastReceiver reload = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { reload(); } }; @Override protected void onStart() { super.onStart(); reload(); } void reload() { if (readingPlan != null) { loadReadingPlan(readingPlan.info.id); // so startTime can change } loadReadingPlanProgress(); prepareDisplay(); } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.activity_reading_plan, menu); return true; } @Override public boolean onPrepareOptionsMenu(final Menu menu) { final boolean anyReadingPlan = downloadedReadingPlanInfos.size() != 0; menu.findItem(R.id.menuDelete).setVisible(anyReadingPlan); if (!anyReadingPlan) { leftDrawer.getHandle().setDescription(null); } return true; } @Override public boolean onOptionsItemSelected(final MenuItem item) { final int itemId = item.getItemId(); if (itemId == android.R.id.home) { leftDrawer.toggleDrawer(); return true; } else if (itemId == R.id.menuDownload) { downloadReadingPlanList(); return true; } else if (itemId == R.id.menuDelete) { deleteReadingPlan(); return true; } return super.onOptionsItemSelected(item); } private void loadReadingPlan(long id) { downloadedReadingPlanInfos = S.getDb().listAllReadingPlanInfo(); if (downloadedReadingPlanInfos.size() == 0) { return; } Pair<String, byte[]> nameAndData = S.getDb().getReadingPlanNameAndData(id); long startTime = 0; if (id == 0 || nameAndData == null) { id = downloadedReadingPlanInfos.get(0).id; startTime = downloadedReadingPlanInfos.get(0).startTime; nameAndData = S.getDb().getReadingPlanNameAndData(id); } else { for (ReadingPlan.ReadingPlanInfo info : downloadedReadingPlanInfos) { if (id == info.id) { startTime = info.startTime; } } } final InputStream inputStream = new ByteArrayInputStream(nameAndData.second); final ReadingPlan res = ReadingPlanManager.readVersion1(inputStream, nameAndData.first); res.info.id = id; res.info.startTime = startTime; readingPlan = res; Preferences.setLong(Prefkey.active_reading_plan_id, id); leftDrawer.getHandle().setDescription( TextUtils.expandTemplate( getText(R.string.rp_description_with_id), readingPlan.info.title, String.valueOf(readingPlan.info.duration), readingPlan.info.name, readingPlan.info.description ) ); } private void loadReadingPlanProgress() { if (readingPlan == null) { return; } readReadingCodes = S.getDb().getAllReadingCodesByReadingPlanProgressGid(ReadingPlan.gidFromName(readingPlan.info.name)); } public void goToIsiActivity(final int dayNumber, final int sequence) { final int[] selectedVerses = readingPlan.dailyVerses[dayNumber]; final int ari_start = selectedVerses[sequence * 2]; final int ari_end = selectedVerses[sequence * 2 + 1]; CurrentReading.set(ari_start, ari_end); startActivity(Launcher.openAppAtBibleLocation(ari_start)); } private void loadDayNumber() { if (readingPlan == null) { return; } Calendar startCalendar = GregorianCalendar.getInstance(); startCalendar.setTimeInMillis(readingPlan.info.startTime); int tn = calculateDaysDiff(startCalendar, GregorianCalendar.getInstance()); if (tn >= readingPlan.info.duration) { tn = readingPlan.info.duration - 1; } else if (tn < 0) { tn = 0; } dayNumber = todayNumber = tn; } private int calculateDaysDiff(Calendar startCalendar, Calendar endCalendar) { startCalendar.set(Calendar.HOUR_OF_DAY, 0); startCalendar.set(Calendar.MINUTE, 0); startCalendar.set(Calendar.SECOND, 0); startCalendar.set(Calendar.MILLISECOND, 0); endCalendar.set(Calendar.HOUR_OF_DAY, 0); endCalendar.set(Calendar.MINUTE, 0); endCalendar.set(Calendar.SECOND, 0); endCalendar.set(Calendar.MILLISECOND, 0); // add 2 hours to prevent DST-related problems return (int) ((2 * 3600 * 1000 + endCalendar.getTime().getTime() - startCalendar.getTime().getTime()) / (1000 * 60 * 60 * 24)); } public boolean prepareDropDownNavigation() { if (downloadedReadingPlanInfos.size() == 0) { actionBar.setDisplayShowTitleEnabled(true); actionBar.setTitle(R.string.rp_activity_title); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); return true; } actionBar.setDisplayShowTitleEnabled(false); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); long id = Preferences.getLong(Prefkey.active_reading_plan_id, 0); int itemNumber = 0; //Drop-down navigation List<String> titles = new ArrayList<>(); for (int i = 0; i < downloadedReadingPlanInfos.size(); i++) { ReadingPlan.ReadingPlanInfo info = downloadedReadingPlanInfos.get(i); titles.add(info.title); if (info.id == id) { itemNumber = i; } } final ArrayAdapter<String> navigationAdapter = new ArrayAdapter<>(actionBar.getThemedContext(), android.R.layout.simple_spinner_dropdown_item, titles); newDropDownItems = false; actionBar.setListNavigationCallbacks(navigationAdapter, (i, l) -> { if (newDropDownItems) { loadReadingPlan(downloadedReadingPlanInfos.get(i).id); loadDayNumber(); reload(); } newDropDownItems = true; return true; }); actionBar.setSelectedNavigationItem(itemNumber); return false; } public void prepareDisplay() { if (readingPlan == null) { llNavigations.setVisibility(View.GONE); lsReadingPlan.setVisibility(View.GONE); flNoData.setVisibility(View.VISIBLE); bDownload.setOnClickListener(v -> downloadReadingPlanList()); } else { llNavigations.setVisibility(View.VISIBLE); lsReadingPlan.setVisibility(View.VISIBLE); flNoData.setVisibility(View.GONE); } readingPlanAdapter.load(); updateButtonStatus(); } private int findFirstUnreadDay() { for (int i = 0; i < readingPlan.info.duration - 1; i++) { boolean[] readMarks = new boolean[readingPlan.dailyVerses[i].length / 2]; ReadingPlanManager.writeReadMarksByDay(readReadingCodes, readMarks, i); for (boolean readMark : readMarks) { if (!readMark) { return i; } } } return readingPlan.info.duration - 1; } private void deleteReadingPlan() { new MaterialDialog.Builder(this) .content(getString(R.string.rp_deletePlan, readingPlan.info.title)) .positiveText(R.string.delete) .onPositive((dialog, which) -> { S.getDb().deleteReadingPlanById(readingPlan.info.id); readingPlan = null; Preferences.remove(Prefkey.active_reading_plan_id); loadReadingPlan(0); loadReadingPlanProgress(); loadDayNumber(); prepareDropDownNavigation(); prepareDisplay(); supportInvalidateOptionsMenu(); }) .negativeText(R.string.cancel) .show(); } private void changeDay(int day) { int newDay = dayNumber + day; if (newDay < 0 || newDay >= readingPlan.info.duration) { return; } dayNumber = newDay; readingPlanAdapter.load(); updateButtonStatus(); } private void updateButtonStatus() { if (readingPlan == null) { return; } bLeft.setEnabled(dayNumber != 0); bRight.setEnabled(dayNumber != readingPlan.info.duration - 1); bToday.setText(getReadingDateHeader(dayNumber)); } @Override public void bRestart_click() { new MaterialDialog.Builder(this) .content(R.string.rp_restart_desc) .positiveText(R.string.ok) .onPositive((dialog, which) -> { S.getDb().deleteAllReadingPlanProgressForGid(ReadingPlan.gidFromName(readingPlan.info.name)); S.getDb().updateReadingPlanStartDate(readingPlan.info.id, System.currentTimeMillis()); loadReadingPlan(readingPlan.info.id); loadDayNumber(); readingPlanAdapter.load(); reload(); updateButtonStatus(); }) .negativeText(R.string.cancel) .show(); leftDrawer.closeDrawer(); } @Override protected LeftDrawer getLeftDrawer() { return leftDrawer; } private void downloadReadingPlanList() { startActivityForResult(HelpActivity.createIntent(BuildConfig.SERVER_HOST + "rp/downloads?app_versionCode=" + App.getVersionCode() + "&app_versionName=" + Uri.encode(App.getVersionName()), getString(R.string.rp_menuDownload)), REQCODE_openList); } @Override protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { if (requestCode == REQCODE_openList && resultCode == RESULT_OK) { final Uri uri = data.getData(); if (uri != null) { downloadByAlkitabUri(uri); } return; } super.onActivityResult(requestCode, resultCode, data); } private void downloadByAlkitabUri(final Uri uri) { if (!"alkitab".equals(uri.getScheme()) || !"/addon/download".equals(uri.getPath()) || !"readingplan".equals(uri.getQueryParameter("kind")) || !"rpb".equals(uri.getQueryParameter("type")) || uri.getQueryParameter("name") == null) { new MaterialDialog.Builder(this) .content("Invalid uri:\n\n" + uri) .positiveText(R.string.ok) .show(); return; } final String name = uri.getQueryParameter("name"); downloadReadingPlanFromServer(name); } void downloadReadingPlanFromServer(final String name) { if (S.getDb().listReadingPlanNames().contains(name)) { new MaterialDialog.Builder(this) .content(R.string.rp_download_already_have) .positiveText(R.string.ok) .show(); return; } final AtomicBoolean cancelled = new AtomicBoolean(false); final MaterialDialog pd = new MaterialDialog.Builder(this) .content(R.string.rp_download_reading_plan_progress) .progress(true, 0) .dismissListener(dialog -> cancelled.set(true)) .show(); Background.run(() -> { try { final byte[] data = App.downloadBytes(BuildConfig.SERVER_HOST + "rp/get_rp?name=" + name); if (cancelled.get()) return; Foreground.run(() -> { final long id = ReadingPlanManager.insertReadingPlanToDb(data, name); if (id == 0) { new MaterialDialog.Builder(ReadingPlanActivity.this) .content(getString(R.string.rp_download_reading_plan_data_corrupted)) .positiveText(R.string.ok) .show(); return; } Preferences.setLong(Prefkey.active_reading_plan_id, id); loadReadingPlan(id); loadReadingPlanProgress(); loadDayNumber(); prepareDropDownNavigation(); prepareDisplay(); supportInvalidateOptionsMenu(); }); } catch (Exception e) { if (!cancelled.get()) { Log.e(TAG, "downloading reading plan data", e); Foreground.run(() -> new MaterialDialog.Builder(ReadingPlanActivity.this) .content(getString(R.string.rp_download_reading_plan_failed)) .positiveText(R.string.ok) .show() ); } } finally { pd.dismiss(); } }); } private float getActualPercentage() { return 100.f * readReadingCodes.size() / countAllReadings(); } private float getTargetPercentage() { return 100.f * countTarget() / countAllReadings(); } private int countTarget() { int doubledCount = 0; for (int i = 0; i <= todayNumber; i++) { doubledCount += readingPlan.dailyVerses[i].length; } return doubledCount / 2; } private int countAllReadings() { int doubledCount = 0; for (int i = 0; i < readingPlan.info.duration; i++) { doubledCount += readingPlan.dailyVerses[i].length; } return doubledCount / 2; } public String getReadingDateHeader(final int dayNumber) { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTimeInMillis(readingPlan.info.startTime); calendar.add(Calendar.DATE, dayNumber); return getString(R.string.rp_dayHeader, (dayNumber + 1), Sqlitil.toLocaleDateMedium(calendar.getTime())); } void one_reading_longClick(final int day, final int sequence) { new MaterialDialog.Builder(this) .content(R.string.rp_mark_as_read_up_to) .positiveText(R.string.ok) .negativeText(R.string.cancel) .onPositive((dialog, which) -> { ReadingPlanManager.markAsReadUpTo(readingPlan.info.name, readingPlan.dailyVerses, day, sequence); reload(); }) .show(); } class ReadingPlanAdapter extends EasyAdapter { private int[] todayReadings; ColorStateList originalCommentTextColor = null; public void load() { if (readingPlan == null) { todayReadings = null; } else { todayReadings = readingPlan.dailyVerses[dayNumber]; } notifyDataSetChanged(); } @Override public int getCount() { if (todayReadings == null) return 0; if (showDetails) { return (todayReadings.length / 2) + readingPlan.info.duration + 1; } else { return (todayReadings.length / 2) + 1; } } @Override public View newView(final int position, final ViewGroup parent) { final int itemViewType = getItemViewType(position); if (itemViewType == 0) { return getLayoutInflater().inflate(R.layout.item_reading_plan_one_reading, parent, false); } else if (itemViewType == 1) { return getLayoutInflater().inflate(R.layout.item_reading_plan_summary, parent, false); } else if (itemViewType == 2) { return getLayoutInflater().inflate(R.layout.item_reading_plan_one_day, parent, false); } return null; } @Override public void bindView(final View res, final int position, final ViewGroup parent) { final int itemViewType = getItemViewType(position); if (itemViewType == 0) { final Button bReference = V.get(res, R.id.bReference); final CheckBox checkbox = V.get(res, R.id.checkbox); final boolean[] readMarks = new boolean[todayReadings.length / 2]; ReadingPlanManager.writeReadMarksByDay(readReadingCodes, readMarks, dayNumber); bReference.setText(S.activeVersion.referenceRange(todayReadings[position * 2], todayReadings[position * 2 + 1])); bReference.setOnClickListener(v -> { final int todayReadingsSize = readingPlan.dailyVerses[dayNumber].length / 2; if (position < todayReadingsSize) { goToIsiActivity(dayNumber, position); } else if (position > todayReadingsSize) { goToIsiActivity(position - todayReadingsSize - 1, 0); } }); checkbox.setOnCheckedChangeListener(null); checkbox.setChecked(readMarks[position]); checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> { ReadingPlanManager.updateReadingPlanProgress(readingPlan.info.name, dayNumber, position, isChecked); loadReadingPlanProgress(); load(); }); } else if (itemViewType == 1) { final ProgressBar pbReadingProgress = V.get(res, R.id.pbReadingProgress); final TextView tActual = V.get(res, R.id.tActual); final TextView tTarget = V.get(res, R.id.tTarget); final TextView tComment = V.get(res, R.id.tComment); final TextView tDetail = V.get(res, R.id.tDetail); float actualPercentage = getActualPercentage(); float targetPercentage = getTargetPercentage(); pbReadingProgress.setMax(10000); pbReadingProgress.setProgress((int) (actualPercentage * 100)); pbReadingProgress.setSecondaryProgress((int) (targetPercentage * 100)); tActual.setText(getString(R.string.rp_commentActual, String.format(Locale.US, "%.2f", actualPercentage))); tTarget.setText(getString(R.string.rp_commentTarget, String.format(Locale.US, "%.2f", targetPercentage))); if (originalCommentTextColor == null) { originalCommentTextColor = tComment.getTextColors(); } if (actualPercentage == targetPercentage) { tComment.setText(R.string.rp_commentOnSchedule); tComment.setTextColor(ResourcesCompat.getColor(getResources(), R.color.escape, getTheme())); } else if (actualPercentage < targetPercentage) { tComment.setText(getString(R.string.rp_commentBehindSchedule, String.format(Locale.US, "%.2f", targetPercentage - actualPercentage))); tComment.setTextColor(originalCommentTextColor); } else { tComment.setText(getString(R.string.rp_commentAheadSchedule, String.format(Locale.US, "%.2f", actualPercentage - targetPercentage))); tComment.setTextColor(ResourcesCompat.getColor(getResources(), R.color.escape, getTheme())); } tDetail.setOnClickListener(v -> { showDetails = !showDetails; if (showDetails) { tDetail.setText(R.string.rp_hideDetails); } else { tDetail.setText(R.string.rp_showDetails); } notifyDataSetChanged(); }); } else if (itemViewType == 2) { final LinearLayout layout = V.get(res, R.id.llOneDayReadingPlan); final int day = position - todayReadings.length / 2 - 1; //Text title TextView tTitle = V.get(res, android.R.id.text1); tTitle.setText(getReadingDateHeader(day)); //Text reading int[] ariRanges = readingPlan.dailyVerses[day]; final int checkbox_count = ariRanges.length / 2; { // remove extra checkboxes for (int i = layout.getChildCount() - 1; i >= 0; i--) { final View view = layout.getChildAt(i); if (view instanceof CheckBox && view.getTag() != null) { Integer tag = (Integer) view.getTag(); if (tag >= checkbox_count) layout.removeViewAt(i); } } } final boolean[] readMarks = new boolean[checkbox_count]; ReadingPlanManager.writeReadMarksByDay(readReadingCodes, readMarks, day); for (int i = 0; i < checkbox_count; i++) { final int sequence = i; CheckBox checkBox = (CheckBox) layout.findViewWithTag(i); if (checkBox == null) { checkBox = (CheckBox) getLayoutInflater().inflate(R.layout.item_reading_plan_one_reading_checkbox, layout, false); checkBox.setTag(i); layout.addView(checkBox); } checkBox.setOnCheckedChangeListener(null); checkBox.setChecked(readMarks[sequence]); checkBox.setText(S.activeVersion.referenceRange(ariRanges[sequence * 2], ariRanges[sequence * 2 + 1])); checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> { ReadingPlanManager.updateReadingPlanProgress(readingPlan.info.name, day, sequence, isChecked); loadReadingPlanProgress(); load(); }); checkBox.setOnLongClickListener(v -> { one_reading_longClick(day, sequence); return true; }); } } } @Override public int getViewTypeCount() { return 3; } @Override public int getItemViewType(final int position) { if (position < todayReadings.length / 2) { return 0; } else if (position == todayReadings.length / 2) { return 1; } else { return 2; } } @Override public boolean isEnabled(final int position) { return false; } } }