Warn when scheduling a post within 5 minutes (#1698)

* Warn when scheduling a post within 5 minutes

* Fix NPE when scheduled post time isn't set

* Use AlertDialog with option to cancel instead of Toast when a post isn't scheduled far enough in advance

* Move schedule validation warning to scheduling bottom sheet

* Fix scheduling error display when sending after an initially-valid scheduling time has become invalid
This commit is contained in:
Levi Bard 2020-02-25 18:33:25 +01:00 committed by GitHub
parent aba36ca6f8
commit 80e0c55b67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 20 deletions

View file

@ -65,6 +65,7 @@ import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog
import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog
import com.keylesspalace.tusky.components.compose.view.ComposeOptionsListener
import com.keylesspalace.tusky.components.compose.view.ComposeScheduleView
import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
@ -695,9 +696,16 @@ class ComposeActivity : BaseActivity(),
updateVisibleCharactersLeft()
}
private fun verifyScheduledTime(): Boolean {
return composeScheduleView.verifyScheduledTime(composeScheduleView.getDateTime(viewModel.scheduledAt.value))
}
private fun onSendClicked() {
enableButtons(false)
sendStatus()
if (verifyScheduledTime()) {
sendStatus()
} else {
showScheduleView()
}
}
/** This is for the fancy keyboards which can insert images and stuff. */
@ -723,6 +731,7 @@ class ComposeActivity : BaseActivity(),
}
private fun sendStatus() {
enableButtons(false)
val contentText = composeEditField.text.toString()
var spoilerText = ""
if (viewModel.showContentWarning.value!!) {
@ -974,7 +983,11 @@ class ComposeActivity : BaseActivity(),
override fun onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int) {
composeScheduleView.onTimeSet(hourOfDay, minute)
viewModel.updateScheduledAt(composeScheduleView.time)
scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN
if (verifyScheduledTime()) {
scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN
} else {
showScheduleView()
}
}
private fun resetSchedule() {

View file

@ -22,6 +22,8 @@ import android.util.AttributeSet;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
@ -48,8 +50,10 @@ public class ComposeScheduleView extends ConstraintLayout {
private Button resetScheduleButton;
private TextView scheduledDateTimeView;
private TextView invalidScheduleWarningView;
private Calendar scheduleDateTime;
public static int MINIMUM_SCHEDULED_SECONDS = 330; // Minimum is 5 minutes, pad 30 seconds for posting
public ComposeScheduleView(Context context) {
super(context);
@ -76,8 +80,10 @@ public class ComposeScheduleView extends ConstraintLayout {
resetScheduleButton = findViewById(R.id.resetScheduleButton);
scheduledDateTimeView = findViewById(R.id.scheduledDateTime);
invalidScheduleWarningView = findViewById(R.id.invalidScheduleWarning);
scheduledDateTimeView.setOnClickListener(v -> openPickDateDialog());
invalidScheduleWarningView.setText(R.string.warning_scheduling_interval);
scheduleDateTime = null;
@ -89,10 +95,13 @@ public class ComposeScheduleView extends ConstraintLayout {
private void setScheduledDateTime() {
if (scheduleDateTime == null) {
scheduledDateTimeView.setText("");
invalidScheduleWarningView.setVisibility(GONE);
} else {
Date scheduled = scheduleDateTime.getTime();
scheduledDateTimeView.setText(String.format("%s %s",
dateFormat.format(scheduleDateTime.getTime()),
timeFormat.format(scheduleDateTime.getTime())));
dateFormat.format(scheduled),
timeFormat.format(scheduled)));
verifyScheduledTime(scheduled);
}
}
@ -124,9 +133,7 @@ public class ComposeScheduleView extends ConstraintLayout {
.setValidator(
DateValidatorPointForward.from(yesterday))
.build();
if (scheduleDateTime == null) {
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
}
initializeSuggestedTime();
MaterialDatePicker<Long> picker = MaterialDatePicker.Builder
.datePicker()
.setSelection(scheduleDateTime.getTimeInMillis())
@ -147,6 +154,16 @@ public class ComposeScheduleView extends ConstraintLayout {
picker.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "time_picker");
}
public Date getDateTime(String scheduledAt) {
if (scheduledAt != null) {
try {
return iso8601.parse(scheduledAt);
} catch (ParseException e) {
}
}
return null;
}
public void setDateTime(String scheduledAt) {
Date date;
try {
@ -154,27 +171,34 @@ public class ComposeScheduleView extends ConstraintLayout {
} catch (ParseException e) {
return;
}
if (scheduleDateTime == null) {
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
}
initializeSuggestedTime();
scheduleDateTime.setTime(date);
setScheduledDateTime();
}
private void onDateSet(long selection) {
if (scheduleDateTime == null) {
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
public boolean verifyScheduledTime(@Nullable Date scheduledTime) {
boolean valid;
if (scheduledTime != null) {
Calendar minimumScheduledTime = getCalendar();
minimumScheduledTime.add(Calendar.SECOND, MINIMUM_SCHEDULED_SECONDS);
valid = scheduledTime.after(minimumScheduledTime.getTime());
} else {
valid = true;
}
Calendar newDate = Calendar.getInstance(TimeZone.getDefault());
invalidScheduleWarningView.setVisibility(valid ? GONE : VISIBLE);
return valid;
}
private void onDateSet(long selection) {
initializeSuggestedTime();
Calendar newDate = getCalendar();
newDate.setTimeInMillis(selection);
scheduleDateTime.set(newDate.get(Calendar.YEAR), newDate.get(Calendar.MONTH), newDate.get(Calendar.DATE));
openPickTimeDialog();
}
public void onTimeSet(int hourOfDay, int minute) {
if (scheduleDateTime == null) {
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
}
initializeSuggestedTime();
scheduleDateTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
scheduleDateTime.set(Calendar.MINUTE, minute);
setScheduledDateTime();
@ -186,4 +210,16 @@ public class ComposeScheduleView extends ConstraintLayout {
}
return iso8601.format(scheduleDateTime.getTime());
}
@NonNull
public static Calendar getCalendar() {
return Calendar.getInstance(TimeZone.getDefault());
}
private void initializeSuggestedTime() {
if (scheduleDateTime == null) {
scheduleDateTime = getCalendar();
scheduleDateTime.add(Calendar.MINUTE, 15);
}
}
}

View file

@ -10,7 +10,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/action_reset_schedule"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@id/invalidScheduleWarning"
app:layout_constraintStart_toStartOf="parent" />
<TextView
@ -23,10 +23,27 @@
android:paddingBottom="16dp"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@id/invalidScheduleWarning"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toEndOf="@id/resetScheduleButton"
tools:text="2020/01/01 00:00:00" />
<TextView
android:id="@+id/invalidScheduleWarning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:paddingStart="4dp"
android:paddingTop="4dp"
android:paddingBottom="16dp"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
tools:text="@string/warning_scheduling_interval"
android:visibility="gone" />
</merge>

View file

@ -547,5 +547,6 @@
<string name="no_saved_status">You don\'t have any drafts.</string>
<string name="no_scheduled_status">You don\'t have any scheduled statuses.</string>
<string name="warning_scheduling_interval">Mastodon has a minimum scheduling interval of 5 minutes.</string>
</resources>