1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 06:37:30 +00:00

Handle invalid custom date time format. Fixes austinhuang0131/barinsta#1499

This commit is contained in:
Ammar Githam 2021-07-06 07:40:25 +09:00
parent 82d7555eee
commit 383485abec
7 changed files with 102 additions and 72 deletions

View File

@ -16,18 +16,20 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import java.time.format.DateTimeFormatter;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import awais.instagrabber.R;
import awais.instagrabber.databinding.DialogTimeSettingsBinding; import awais.instagrabber.databinding.DialogTimeSettingsBinding;
import awais.instagrabber.utils.DateUtils;
import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
public final class TimeSettingsDialog extends DialogFragment implements AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener, public final class TimeSettingsDialog extends DialogFragment implements AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener,
View.OnClickListener, TextWatcher { View.OnClickListener, TextWatcher {
private DialogTimeSettingsBinding timeSettingsBinding; private DialogTimeSettingsBinding binding;
private final LocalDateTime magicDate; private final LocalDateTime magicDate;
private DateTimeFormatter currentFormat; private DateTimeFormatter currentFormat;
private String selectedFormat; private String selectedFormat;
@ -55,57 +57,67 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
timeSettingsBinding = DialogTimeSettingsBinding.inflate(inflater, container, false); binding = DialogTimeSettingsBinding.inflate(inflater, container, false);
timeSettingsBinding.cbCustomFormat.setOnCheckedChangeListener(this); binding.cbCustomFormat.setOnCheckedChangeListener(this);
timeSettingsBinding.cbCustomFormat.setChecked(customDateTimeFormatEnabled); binding.cbCustomFormat.setChecked(customDateTimeFormatEnabled);
timeSettingsBinding.cbSwapTimeDate.setChecked(swapDateTimeEnabled); binding.cbSwapTimeDate.setChecked(swapDateTimeEnabled);
timeSettingsBinding.etCustomFormat.setText(customDateTimeFormat); binding.customFormatEditText.setText(customDateTimeFormat);
final String[] dateTimeFormat = dateTimeSelection.split(";"); // output = time;separator;date final String[] dateTimeFormat = dateTimeSelection.split(";"); // output = time;separator;date
timeSettingsBinding.spTimeFormat.setSelection(Integer.parseInt(dateTimeFormat[0])); binding.spTimeFormat.setSelection(Integer.parseInt(dateTimeFormat[0]));
timeSettingsBinding.spSeparator.setSelection(Integer.parseInt(dateTimeFormat[1])); binding.spSeparator.setSelection(Integer.parseInt(dateTimeFormat[1]));
timeSettingsBinding.spDateFormat.setSelection(Integer.parseInt(dateTimeFormat[2])); binding.spDateFormat.setSelection(Integer.parseInt(dateTimeFormat[2]));
timeSettingsBinding.cbSwapTimeDate.setOnCheckedChangeListener(this); binding.cbSwapTimeDate.setOnCheckedChangeListener(this);
refreshTimeFormat(); refreshTimeFormat();
timeSettingsBinding.spTimeFormat.setOnItemSelectedListener(this); binding.spTimeFormat.setOnItemSelectedListener(this);
timeSettingsBinding.spDateFormat.setOnItemSelectedListener(this); binding.spDateFormat.setOnItemSelectedListener(this);
timeSettingsBinding.spSeparator.setOnItemSelectedListener(this); binding.spSeparator.setOnItemSelectedListener(this);
timeSettingsBinding.etCustomFormat.addTextChangedListener(this); binding.customFormatEditText.addTextChangedListener(this);
timeSettingsBinding.btnConfirm.setOnClickListener(this); binding.btnConfirm.setOnClickListener(this);
timeSettingsBinding.btnInfo.setOnClickListener(this); binding.customFormatField.setEndIconOnClickListener(this);
return timeSettingsBinding.getRoot(); return binding.getRoot();
} }
private void refreshTimeFormat() { private void refreshTimeFormat() {
if (timeSettingsBinding.cbCustomFormat.isChecked()) final boolean isCustom = binding.cbCustomFormat.isChecked();
selectedFormat = timeSettingsBinding.etCustomFormat.getText().toString(); if (isCustom) {
else { final Editable text = binding.customFormatEditText.getText();
final String sepStr = String.valueOf(timeSettingsBinding.spSeparator.getSelectedItem()); if (text != null) {
final String timeStr = String.valueOf(timeSettingsBinding.spTimeFormat.getSelectedItem()); selectedFormat = text.toString();
final String dateStr = String.valueOf(timeSettingsBinding.spDateFormat.getSelectedItem()); }
} else {
final String sepStr = String.valueOf(binding.spSeparator.getSelectedItem());
final String timeStr = String.valueOf(binding.spTimeFormat.getSelectedItem());
final String dateStr = String.valueOf(binding.spDateFormat.getSelectedItem());
final boolean isSwapTime = timeSettingsBinding.cbSwapTimeDate.isChecked(); final boolean isSwapTime = binding.cbSwapTimeDate.isChecked();
final boolean isBlankSeparator = timeSettingsBinding.spSeparator.getSelectedItemPosition() <= 0; final boolean isBlankSeparator = binding.spSeparator.getSelectedItemPosition() <= 0;
selectedFormat = (isSwapTime ? dateStr : timeStr) selectedFormat = (isSwapTime ? dateStr : timeStr)
+ (isBlankSeparator ? " " : " '" + sepStr + "' ") + (isBlankSeparator ? " " : " '" + sepStr + "' ")
+ (isSwapTime ? timeStr : dateStr); + (isSwapTime ? timeStr : dateStr);
} }
timeSettingsBinding.btnConfirm.setEnabled(true); binding.btnConfirm.setEnabled(true);
try { try {
currentFormat = DateTimeFormatter.ofPattern(selectedFormat, LocaleUtils.getCurrentLocale()); currentFormat = DateTimeFormatter.ofPattern(selectedFormat, LocaleUtils.getCurrentLocale());
timeSettingsBinding.timePreview.setText(magicDate.format(currentFormat)); if (isCustom) {
final boolean valid = !TextUtils.isEmpty(selectedFormat) && DateUtils.checkFormatterValid(currentFormat);
binding.customFormatField.setError(valid ? null :getString(R.string.invalid_format));
if (!valid) {
binding.btnConfirm.setEnabled(false);
} }
catch (Exception e) { }
timeSettingsBinding.btnConfirm.setEnabled(false); binding.timePreview.setText(magicDate.format(currentFormat));
timeSettingsBinding.timePreview.setText(null); } catch (Exception e) {
binding.btnConfirm.setEnabled(false);
binding.timePreview.setText(null);
} }
} }
@ -116,16 +128,14 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override @Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
if (buttonView == timeSettingsBinding.cbCustomFormat) { if (buttonView == binding.cbCustomFormat) {
final View parent = (View) timeSettingsBinding.etCustomFormat.getParent(); binding.customFormatField.setVisibility(isChecked ? View.VISIBLE : View.GONE);
parent.setVisibility(isChecked ? View.VISIBLE : View.GONE); binding.customFormatField.setEnabled(isChecked);
timeSettingsBinding.etCustomFormat.setEnabled(isChecked);
timeSettingsBinding.btnInfo.setEnabled(isChecked);
timeSettingsBinding.spTimeFormat.setEnabled(!isChecked); binding.spTimeFormat.setEnabled(!isChecked);
timeSettingsBinding.spDateFormat.setEnabled(!isChecked); binding.spDateFormat.setEnabled(!isChecked);
timeSettingsBinding.spSeparator.setEnabled(!isChecked); binding.spSeparator.setEnabled(!isChecked);
timeSettingsBinding.cbSwapTimeDate.setEnabled(!isChecked); binding.cbSwapTimeDate.setEnabled(!isChecked);
} }
refreshTimeFormat(); refreshTimeFormat();
} }
@ -137,20 +147,21 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
if (v == timeSettingsBinding.btnConfirm) { if (v == binding.btnConfirm) {
if (onConfirmListener != null) { if (onConfirmListener != null) {
onConfirmListener.onConfirm( onConfirmListener.onConfirm(
timeSettingsBinding.cbCustomFormat.isChecked(), binding.cbCustomFormat.isChecked(),
timeSettingsBinding.spTimeFormat.getSelectedItemPosition(), binding.spTimeFormat.getSelectedItemPosition(),
timeSettingsBinding.spSeparator.getSelectedItemPosition(), binding.spSeparator.getSelectedItemPosition(),
timeSettingsBinding.spDateFormat.getSelectedItemPosition(), binding.spDateFormat.getSelectedItemPosition(),
selectedFormat, selectedFormat,
timeSettingsBinding.cbSwapTimeDate.isChecked()); binding.cbSwapTimeDate.isChecked());
} }
dismiss(); dismiss();
} else if (v == timeSettingsBinding.btnInfo) { } else if (v == binding.customFormatField.findViewById(R.id.text_input_end_icon)) {
timeSettingsBinding.customPanel.setVisibility(timeSettingsBinding.customPanel binding.customPanel.setVisibility(
.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); binding.customPanel.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE
);
} }
} }

View File

@ -90,4 +90,5 @@ object Constants {
const val DM_THREAD_ACTION_EXTRA_THREAD_TITLE = "thread_title" const val DM_THREAD_ACTION_EXTRA_THREAD_TITLE = "thread_title"
const val X_IG_APP_ID = "936619743392459" const val X_IG_APP_ID = "936619743392459"
const val EXTRA_INITIAL_URI = "initial_uri" const val EXTRA_INITIAL_URI = "initial_uri"
const val defaultDateTimeFormat = "hh:mm:ss a 'on' dd-MM-yyyy"
} }

View File

@ -1,6 +1,9 @@
package awais.instagrabber.utils package awais.instagrabber.utils
import android.util.Log
import awais.instagrabber.utils.extensions.TAG
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
object DateUtils { object DateUtils {
@ -14,4 +17,13 @@ object DateUtils {
fun isBeforeOrEqual(localDateTime: LocalDateTime, comparedTo: LocalDateTime): Boolean { fun isBeforeOrEqual(localDateTime: LocalDateTime, comparedTo: LocalDateTime): Boolean {
return localDateTime.isBefore(comparedTo) || localDateTime.isEqual(comparedTo) return localDateTime.isBefore(comparedTo) || localDateTime.isEqual(comparedTo)
} }
@JvmStatic
fun checkFormatterValid(datetimeParser: DateTimeFormatter): Boolean = try {
LocalDateTime.now().format(datetimeParser)
true
} catch (e: Exception) {
Log.e(TAG, "checkFormatterValid: ", e)
false
}
} }

View File

@ -37,7 +37,9 @@ class SettingsHelper(context: Context) {
} }
private fun getStringDefault(@StringSettings key: String): String { private fun getStringDefault(@StringSettings key: String): String {
if (PreferenceKeys.DATE_TIME_FORMAT == key) return "hh:mm:ss a 'on' dd-MM-yyyy" if (PreferenceKeys.DATE_TIME_FORMAT == key) {
return Constants.defaultDateTimeFormat
}
return if (PreferenceKeys.DATE_TIME_SELECTION == key) "0;3;0" else "" return if (PreferenceKeys.DATE_TIME_SELECTION == key) "0;3;0" else ""
} }

View File

@ -11,11 +11,11 @@ import java.util.*
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
object TextUtils { object TextUtils {
lateinit var datetimeParser: DateTimeFormatter var dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern(Constants.defaultDateTimeFormat)
@JvmStatic @JvmStatic
fun isEmpty(charSequence: CharSequence?): Boolean { fun isEmpty(charSequence: CharSequence?): Boolean {
if (charSequence == null || charSequence.length < 1) return true if (charSequence.isNullOrBlank()) return true
if (charSequence is String) { if (charSequence is String) {
var str = charSequence var str = charSequence
if ("" == str || "null" == str || str.isEmpty()) return true if ("" == str || "null" == str || str.isEmpty()) return true
@ -78,7 +78,8 @@ object TextUtils {
@JvmStatic @JvmStatic
fun setFormatter(datetimeParser: DateTimeFormatter) { fun setFormatter(datetimeParser: DateTimeFormatter) {
this.datetimeParser = datetimeParser if (!DateUtils.checkFormatterValid(datetimeParser)) return
this.dateTimeFormatter = datetimeParser
} }
@JvmStatic @JvmStatic
@ -86,11 +87,11 @@ object TextUtils {
return LocalDateTime.ofInstant( return LocalDateTime.ofInstant(
Instant.ofEpochSecond(epochSecond), Instant.ofEpochSecond(epochSecond),
ZoneId.systemDefault() ZoneId.systemDefault()
).format(datetimeParser) ).format(dateTimeFormatter)
} }
@JvmStatic @JvmStatic
fun nowToString(): String { fun nowToString(): String {
return LocalDateTime.now().format(datetimeParser) return LocalDateTime.now().format(dateTimeFormatter)
} }
} }

View File

@ -33,27 +33,29 @@
android:gravity="center" /> android:gravity="center" />
</LinearLayout> </LinearLayout>
<LinearLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/custom_format_field"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:visibility="gone"
android:visibility="gone"> app:counterEnabled="false"
app:counterMaxLength="50"
app:endIconDrawable="@drawable/ic_outline_info_24"
app:endIconMode="custom"
app:hintEnabled="false"
tools:visibility="visible">
<androidx.appcompat.widget.AppCompatEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/etCustomFormat" android:id="@+id/custom_format_edit_text"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:autofillHints="no"
android:enabled="false" /> android:inputType="text"
android:maxLength="50"
android:padding="16dp"
tools:text="test" />
<androidx.appcompat.widget.AppCompatImageButton </com.google.android.material.textfield.TextInputLayout>
android:id="@+id/btnInfo"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
app:srcCompat="@drawable/ic_outline_info_24" />
</LinearLayout>
<FrameLayout <FrameLayout
android:id="@+id/customPanel" android:id="@+id/customPanel"

View File

@ -516,4 +516,5 @@
<string name="share_link">Share link…</string> <string name="share_link">Share link…</string>
<string name="slide_to_cancel">Slide to Cancel</string> <string name="slide_to_cancel">Slide to Cancel</string>
<string name="disable_screen_transitions">Disable screen transitions</string> <string name="disable_screen_transitions">Disable screen transitions</string>
<string name="invalid_format">Invalid format</string>
</resources> </resources>