finalized status schedule support, bug fix
This commit is contained in:
parent
782a7b3120
commit
ae5e367434
|
@ -492,7 +492,7 @@ public interface Connection {
|
|||
*
|
||||
* @param id scheduled status ID
|
||||
*/
|
||||
void canselScheduledStatus(long id) throws ConnectionException;
|
||||
void cancelScheduledStatus(long id) throws ConnectionException;
|
||||
|
||||
/**
|
||||
* return a list of domain names the current user has blocked
|
||||
|
|
|
@ -794,7 +794,7 @@ public class Mastodon implements Connection {
|
|||
|
||||
|
||||
@Override
|
||||
public void canselScheduledStatus(long id) throws ConnectionException {
|
||||
public void cancelScheduledStatus(long id) throws ConnectionException {
|
||||
try {
|
||||
Response response = delete(ENDPOINT_SCHEDULED_STATUS + "/" + id, new ArrayList<>());
|
||||
if (response.code() != 200) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.nuclearfog.twidda.backend.api.mastodon.impl;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -36,8 +38,8 @@ public class ScheduledMastodonStatus implements ScheduledStatus {
|
|||
JSONObject pollJson = params.optJSONObject("poll");
|
||||
JSONArray mediaArray = json.optJSONArray("media_attachments");
|
||||
String idStr = json.getString("id");
|
||||
String visibilityStr = json.getString("visibility");
|
||||
text = StringUtils.extractText(json.optString("text", ""));
|
||||
String visibilityStr = params.optString("visibility", "");
|
||||
text = StringUtils.extractText(params.optString("text", ""));
|
||||
time = StringUtils.getIsoTime(json.optString("scheduled_at", ""));
|
||||
sensitive = params.optBoolean("sensitive", false);
|
||||
spoiler = params.optBoolean("spoiler_text", false);
|
||||
|
@ -136,4 +138,12 @@ public class ScheduledMastodonStatus implements ScheduledStatus {
|
|||
public boolean isSpoiler() {
|
||||
return spoiler;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof ScheduledStatus))
|
||||
return false;
|
||||
return ((ScheduledStatus) obj).getId() == getId();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.nuclearfog.twidda.backend.async;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.nuclearfog.twidda.backend.api.Connection;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionException;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionManager;
|
||||
import org.nuclearfog.twidda.model.ScheduledStatus;
|
||||
|
||||
/**
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class ScheduleAction extends AsyncExecutor<ScheduleAction.Param, ScheduleAction.Result> {
|
||||
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public ScheduleAction(Context context) {
|
||||
connection = ConnectionManager.getDefaultConnection(context);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Result doInBackground(@NonNull Param param) {
|
||||
try {
|
||||
if (param.mode == Param.UPDATE) {
|
||||
ScheduledStatus status = connection.updateScheduledStatus(param.id, param.time);
|
||||
return new Result(Result.UPDATE, status.getId(), status, null);
|
||||
} else if (param.mode == Param.REMOVE) {
|
||||
connection.cancelScheduledStatus(param.id);
|
||||
return new Result(Result.REMOVE, param.id, null, null);
|
||||
}
|
||||
} catch (ConnectionException exception) {
|
||||
return new Result(Result.ERROR, 0L, null, exception);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class Param {
|
||||
|
||||
public static final int UPDATE = 1;
|
||||
public static final int REMOVE = 2;
|
||||
|
||||
final int mode;
|
||||
final long id, time;
|
||||
|
||||
public Param(int mode, long id, long time) {
|
||||
this.mode = mode;
|
||||
this.time = time;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class Result {
|
||||
|
||||
public static final int UPDATE = 10;
|
||||
public static final int REMOVE = 11;
|
||||
public static final int ERROR = -1;
|
||||
|
||||
public final int mode;
|
||||
public final long id;
|
||||
@Nullable
|
||||
public final ScheduledStatus status;
|
||||
@Nullable
|
||||
public final ConnectionException exception;
|
||||
|
||||
Result(int mode, long id, @Nullable ScheduledStatus status, @Nullable ConnectionException exception) {
|
||||
this.id = id;
|
||||
this.mode = mode;
|
||||
this.status = status;
|
||||
this.exception = exception;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package org.nuclearfog.twidda.backend.async;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.nuclearfog.twidda.backend.api.Connection;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionException;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionManager;
|
||||
import org.nuclearfog.twidda.model.lists.ScheduledStatuses;
|
||||
|
||||
/**
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class ScheduleLoader extends AsyncExecutor<ScheduleLoader.Param, ScheduleLoader.Result> {
|
||||
|
||||
private Connection connection;
|
||||
|
||||
public ScheduleLoader(Context context) {
|
||||
connection = ConnectionManager.getDefaultConnection(context);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Result doInBackground(@NonNull Param param) {
|
||||
try {
|
||||
ScheduledStatuses statuses = connection.getScheduledStatuses(param.minId, param.maxId);
|
||||
return new Result(statuses, param.index, null);
|
||||
} catch (ConnectionException exception) {
|
||||
return new Result(null, 0, exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class Param {
|
||||
final long minId, maxId;
|
||||
final int index;
|
||||
|
||||
public Param(long minId, long maxId, int index) {
|
||||
this.minId = minId;
|
||||
this.maxId = maxId;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class Result {
|
||||
public final int index;
|
||||
@Nullable
|
||||
public final ScheduledStatuses statuses;
|
||||
@Nullable
|
||||
public final ConnectionException exception;
|
||||
|
||||
Result(@Nullable ScheduledStatuses statuses, int index, @Nullable ConnectionException exception) {
|
||||
this.statuses = statuses;
|
||||
this.exception = exception;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,9 @@ public interface ScheduledStatus extends Serializable {
|
|||
*/
|
||||
long getId();
|
||||
|
||||
/**
|
||||
* @return time to publish status
|
||||
*/
|
||||
long getPublishTime();
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.nuclearfog.twidda.model.lists;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.nuclearfog.twidda.model.ScheduledStatus;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
@ -8,6 +10,22 @@ import java.util.LinkedList;
|
|||
* @author nuclearfog
|
||||
*/
|
||||
public class ScheduledStatuses extends LinkedList<ScheduledStatus> {
|
||||
|
||||
private static final long serialVersionUID = 9015646013535818699L;
|
||||
|
||||
|
||||
public ScheduledStatuses() {
|
||||
}
|
||||
|
||||
|
||||
public ScheduledStatuses(ScheduledStatuses scheduledStatuses) {
|
||||
addAll(scheduledStatuses);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ScheduledStatus get(int index) {
|
||||
return super.get(index);
|
||||
}
|
||||
}
|
|
@ -401,6 +401,13 @@ public class MainActivity extends AppCompatActivity implements ActivityResultCal
|
|||
drawerLayout.close();
|
||||
return true;
|
||||
}
|
||||
// open status schedule viewer
|
||||
else if (item.getItemId() == R.id.menu_navigator_schedule) {
|
||||
Intent intent = new Intent(this, ScheduleActivity.class);
|
||||
startActivity(intent);
|
||||
drawerLayout.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.nuclearfog.twidda.ui.activities;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
@ -9,6 +10,7 @@ import androidx.appcompat.widget.Toolbar;
|
|||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.ui.adapter.viewpager.ScheduleAdapter;
|
||||
|
||||
/**
|
||||
|
@ -22,6 +24,7 @@ public class ScheduleActivity extends AppCompatActivity {
|
|||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.page_tab_view);
|
||||
|
||||
ViewGroup root = findViewById(R.id.page_tab_view_root);
|
||||
Toolbar toolbar = findViewById(R.id.page_tab_view_toolbar);
|
||||
ViewPager2 viewPager = findViewById(R.id.page_tab_view_pager);
|
||||
View tabSelector = findViewById(R.id.page_tab_view_tabs);
|
||||
|
@ -30,7 +33,8 @@ public class ScheduleActivity extends AppCompatActivity {
|
|||
viewPager.setAdapter(adapter);
|
||||
|
||||
tabSelector.setVisibility(View.GONE);
|
||||
toolbar.setTitle("");
|
||||
toolbar.setTitle(R.string.toolbar_schedule_title);
|
||||
setSupportActionBar(toolbar);
|
||||
AppStyles.setTheme(root);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package org.nuclearfog.twidda.ui.adapter.recyclerview;
|
|||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
|
||||
|
@ -18,6 +17,10 @@ import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.ScheduleHolder;
|
|||
*/
|
||||
public class ScheduleAdapter extends Adapter<ViewHolder> implements OnHolderClickListener {
|
||||
|
||||
private static final int NO_LOADING = -1;
|
||||
|
||||
private static final int MIN_COUNT = 2;
|
||||
|
||||
private static final int ITEM_GAP = 1;
|
||||
private static final int ITEM_SCHEDULE = 2;
|
||||
|
||||
|
@ -25,9 +28,11 @@ public class ScheduleAdapter extends Adapter<ViewHolder> implements OnHolderClic
|
|||
private OnScheduleClickListener listener;
|
||||
|
||||
private ScheduledStatuses items = new ScheduledStatuses();
|
||||
private int loadingIndex = -1;
|
||||
|
||||
private int loadingIndex = NO_LOADING;
|
||||
|
||||
/**
|
||||
* @param listener item click listener
|
||||
*/
|
||||
public ScheduleAdapter(OnScheduleClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
@ -55,7 +60,10 @@ public class ScheduleAdapter extends Adapter<ViewHolder> implements OnHolderClic
|
|||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
if (holder instanceof ScheduleHolder) {
|
||||
|
||||
ScheduledStatus item = items.get(position);
|
||||
if (item != null) {
|
||||
((ScheduleHolder) holder).setContent(item);
|
||||
}
|
||||
} else if (holder instanceof PlaceHolder) {
|
||||
PlaceHolder placeHolder = (PlaceHolder) holder;
|
||||
placeHolder.setLoading(loadingIndex == position);
|
||||
|
@ -71,21 +79,164 @@ public class ScheduleAdapter extends Adapter<ViewHolder> implements OnHolderClic
|
|||
|
||||
@Override
|
||||
public void onItemClick(int position, int type, int... extras) {
|
||||
|
||||
if (type == SCHEDULE_CLICK) {
|
||||
ScheduledStatus item = items.get(position);
|
||||
if (item != null) {
|
||||
listener.onScheduleClick(item, OnScheduleClickListener.SELECT);
|
||||
}
|
||||
} else if (type == SCHEDULE_REMOVE) {
|
||||
listener.onScheduleClick(items.get(position), OnScheduleClickListener.REMOVE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onPlaceholderClick(int index) {
|
||||
long sinceId = 0;
|
||||
long maxId = 0;
|
||||
if (index == 0) {
|
||||
if (items.size() > 1) {
|
||||
ScheduledStatus item = items.get(index + 1);
|
||||
if (item != null) {
|
||||
sinceId = item.getId();
|
||||
}
|
||||
}
|
||||
} else if (index == items.size() - 1) {
|
||||
ScheduledStatus item = items.get(index - 1);
|
||||
if (item != null) {
|
||||
maxId = item.getId() - 1;
|
||||
}
|
||||
} else {
|
||||
ScheduledStatus item = items.get(index + 1);
|
||||
if (item != null) {
|
||||
sinceId = item.getId();
|
||||
}
|
||||
item = items.get(index - 1);
|
||||
if (item != null) {
|
||||
maxId = item.getId() - 1;
|
||||
}
|
||||
}
|
||||
boolean success = listener.onPlaceholderClick(sinceId, maxId, index);
|
||||
if (success) {
|
||||
loadingIndex = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void setItems(ScheduledStatuses newItems) {
|
||||
items.clear();
|
||||
items.addAll(newItems);
|
||||
if (newItems.size() > MIN_COUNT) {
|
||||
items.add(null);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void addItems(ScheduledStatuses newItems, int index) {
|
||||
disableLoading();
|
||||
if (newItems.size() > MIN_COUNT) {
|
||||
if (items.isEmpty() || items.get(index) != null) {
|
||||
// Add placeholder
|
||||
items.add(index, null);
|
||||
notifyItemInserted(index);
|
||||
}
|
||||
} else if (!items.isEmpty() && items.get(index) == null) {
|
||||
// remove placeholder
|
||||
items.remove(index);
|
||||
notifyItemRemoved(index);
|
||||
}
|
||||
if (!newItems.isEmpty()) {
|
||||
items.addAll(index, newItems);
|
||||
notifyItemRangeInserted(index, newItems.size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void removeItem(long id) {
|
||||
int pos = -1;
|
||||
for (int i = items.size() - 1 ; i >= 0 ; i--) {
|
||||
ScheduledStatus item = items.get(i);
|
||||
if (item != null && item.getId() == id) {
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pos >= 0) {
|
||||
items.remove(pos);
|
||||
notifyItemRemoved(pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void updateItem(@NonNull ScheduledStatus item) {
|
||||
for (int pos = items.size() - 1 ; pos >= 0 ; pos--) {
|
||||
ScheduledStatus current = items.get(pos);
|
||||
if (current != null && current.getId() == item.getId()) {
|
||||
items.set(pos, item);
|
||||
notifyItemChanged(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void clear() {
|
||||
items.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public ScheduledStatuses getItems() {
|
||||
return new ScheduledStatuses(items);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public long getTopItemId() {
|
||||
ScheduledStatus item = items.peekFirst();
|
||||
if (item != null)
|
||||
return item.getId();
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* disable placeholder load animation
|
||||
*/
|
||||
public void disableLoading() {
|
||||
if (loadingIndex != NO_LOADING) {
|
||||
int oldIndex = loadingIndex;
|
||||
loadingIndex = NO_LOADING;
|
||||
notifyItemChanged(oldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface OnScheduleClickListener {
|
||||
|
||||
int SELECT = 1;
|
||||
int REMOVE = 2;
|
||||
|
||||
void onScheduleClick(ScheduledStatus status, int type);
|
||||
|
||||
boolean onPlaceholderClick(long min_id, long max_id, int position);
|
||||
}
|
||||
}
|
|
@ -49,6 +49,10 @@ public interface OnHolderClickListener {
|
|||
|
||||
int HASHTAG_REMOVE = 25;
|
||||
|
||||
int SCHEDULE_CLICK = 26;
|
||||
|
||||
int SCHEDULE_REMOVE = 27;
|
||||
|
||||
/**
|
||||
* called when an item was clicked
|
||||
*
|
||||
|
|
|
@ -7,17 +7,17 @@ import android.view.View.OnClickListener;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.ScheduledStatus;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* @author nuclearfog
|
||||
*/
|
||||
|
@ -27,33 +27,45 @@ public class ScheduleHolder extends ViewHolder implements OnClickListener {
|
|||
|
||||
private OnHolderClickListener listener;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public ScheduleHolder(ViewGroup parent, OnHolderClickListener listener) {
|
||||
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_schedule, parent, false));
|
||||
this.listener = listener;
|
||||
GlobalSettings settings = GlobalSettings.get(parent.getContext());
|
||||
CardView cardLayout = (CardView) itemView;
|
||||
ViewGroup container = itemView.findViewById(R.id.item_schedule_container);
|
||||
View removeButton = itemView.findViewById(R.id.item_schedule_delete_button);
|
||||
time = itemView.findViewById(R.id.item_schedule_time);
|
||||
text = itemView.findViewById(R.id.item_schedule_text);
|
||||
|
||||
time.setCompoundDrawablesWithIntrinsicBounds(R.drawable.schedule, 0, 0, 0);
|
||||
AppStyles.setTheme(container, Color.TRANSPARENT);
|
||||
cardLayout.setCardBackgroundColor(settings.getCardColor());
|
||||
|
||||
|
||||
container.setOnClickListener(this);
|
||||
removeButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
int position = getLayoutPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
if (v.getId() == R.id.item_schedule_container) {
|
||||
listener.onItemClick(position, OnHolderClickListener.SCHEDULE_CLICK);
|
||||
} else if (v.getId() == R.id.item_schedule_delete_button) {
|
||||
listener.onItemClick(position, OnHolderClickListener.SCHEDULE_REMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void setContent(ScheduledStatus status) {
|
||||
time.setText(StringUtils.formatExpirationTime(time.getResources(), status.getPublishTime()));
|
||||
time.setText(SimpleDateFormat.getDateTimeInstance().format(status.getPublishTime()));
|
||||
text.setText(status.getText());
|
||||
}
|
||||
}
|
|
@ -144,6 +144,8 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
|
|||
*/
|
||||
public static final int UNFEATURE_HASHTAG = 629;
|
||||
|
||||
public static final int SCHEDULE_REMOVE = 630;
|
||||
|
||||
|
||||
private TextView title, message, remember_label;
|
||||
private Button confirm, cancel;
|
||||
|
@ -293,6 +295,10 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
|
|||
case UNFEATURE_HASHTAG:
|
||||
messageRes = R.string.confirm_hashtag_unfeature;
|
||||
break;
|
||||
|
||||
case SCHEDULE_REMOVE:
|
||||
messageRes = R.string.confirm_schedule_remove;
|
||||
break;
|
||||
}
|
||||
// setup title
|
||||
title.setVisibility(titleVis);
|
||||
|
|
|
@ -2,30 +2,173 @@ package org.nuclearfog.twidda.ui.fragments;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
|
||||
import org.nuclearfog.twidda.backend.async.ScheduleAction;
|
||||
import org.nuclearfog.twidda.backend.async.ScheduleLoader;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||
import org.nuclearfog.twidda.model.ScheduledStatus;
|
||||
import org.nuclearfog.twidda.model.lists.ScheduledStatuses;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.ScheduleAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.ScheduleAdapter.OnScheduleClickListener;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||
import org.nuclearfog.twidda.ui.dialogs.TimePickerDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.TimePickerDialog.TimeSelectedCallback;
|
||||
|
||||
/**
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class ScheduleFragment extends ListFragment {
|
||||
public class ScheduleFragment extends ListFragment implements OnScheduleClickListener, OnConfirmListener, TimeSelectedCallback {
|
||||
|
||||
private static final String KEY_SAVE = "schedule_status_save";
|
||||
|
||||
private static final int CLEAR_LIST = -1;
|
||||
|
||||
private ScheduleAdapter adapter;
|
||||
private ScheduleLoader scheduleLoader;
|
||||
private ScheduleAction scheduleAction;
|
||||
private ConfirmDialog confirm;
|
||||
private TimePickerDialog timepicker;
|
||||
|
||||
@Nullable
|
||||
private ScheduledStatus selection;
|
||||
|
||||
private AsyncCallback<ScheduleLoader.Result> loaderCallback = this::onLoaderResult;
|
||||
private AsyncCallback<ScheduleAction.Result> actionCallback = this::onActionResult;
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
scheduleLoader = new ScheduleLoader(requireContext());
|
||||
scheduleAction = new ScheduleAction(requireContext());
|
||||
adapter = new ScheduleAdapter(this);
|
||||
confirm = new ConfirmDialog(requireActivity(), this);
|
||||
timepicker = new TimePickerDialog(requireActivity(), this);
|
||||
setAdapter(adapter);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
Object data = savedInstanceState.getSerializable(KEY_SAVE);
|
||||
if (data instanceof ScheduledStatuses) {
|
||||
adapter.setItems((ScheduledStatuses) data);
|
||||
}
|
||||
}
|
||||
load(0L, 0L, CLEAR_LIST);
|
||||
setRefresh(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putSerializable(KEY_SAVE, adapter.getItems());
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onReload() {
|
||||
|
||||
load(adapter.getTopItemId(), 0L, CLEAR_LIST);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
adapter.clear();
|
||||
load(0L, 0L, CLEAR_LIST);
|
||||
setRefresh(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onScheduleClick(ScheduledStatus status, int type) {
|
||||
if (type == OnScheduleClickListener.SELECT) {
|
||||
if (!timepicker.isShowing() && scheduleAction.isIdle()) {
|
||||
selection = status;
|
||||
timepicker.show(status.getPublishTime());
|
||||
}
|
||||
} else if (type == OnScheduleClickListener.REMOVE) {
|
||||
if (!confirm.isShowing() && scheduleAction.isIdle()) {
|
||||
selection = status;
|
||||
confirm.show(ConfirmDialog.SCHEDULE_REMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onPlaceholderClick(long min_id, long max_id, int position) {
|
||||
if (scheduleLoader.isIdle()) {
|
||||
load(min_id, max_id, position);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onConfirm(int type, boolean remember) {
|
||||
if (type == ConfirmDialog.SCHEDULE_REMOVE) {
|
||||
if (selection != null) {
|
||||
ScheduleAction.Param param = new ScheduleAction.Param(ScheduleAction.Param.REMOVE, selection.getId(), 0L);
|
||||
scheduleAction.execute(param, actionCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTimeSelected(long time) {
|
||||
if (selection != null && time != 0L) {
|
||||
ScheduleAction.Param param = new ScheduleAction.Param(ScheduleAction.Param.UPDATE, selection.getId(), time);
|
||||
scheduleAction.execute(param, actionCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void onLoaderResult(ScheduleLoader.Result result) {
|
||||
if (result.statuses != null) {
|
||||
if (result.index == CLEAR_LIST) {
|
||||
adapter.setItems(result.statuses);
|
||||
} else {
|
||||
adapter.addItems(result.statuses, result.index);
|
||||
}
|
||||
} else {
|
||||
ErrorUtils.showErrorMessage(requireContext(), result.exception);
|
||||
}
|
||||
setRefresh(false);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void onActionResult(ScheduleAction.Result result) {
|
||||
if (result.mode == ScheduleAction.Result.REMOVE) {
|
||||
adapter.removeItem(result.id);
|
||||
Toast.makeText(requireContext(), R.string.info_schedule_removed, Toast.LENGTH_SHORT).show();
|
||||
} else if (result.mode == ScheduleAction.Result.UPDATE) {
|
||||
if (result.status != null) {
|
||||
adapter.updateItem(result.status);
|
||||
Toast.makeText(requireContext(), R.string.info_schedule_updated, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else if (result.mode == ScheduleAction.Result.ERROR) {
|
||||
ErrorUtils.showErrorMessage(requireContext(), result.exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void load(long min_id, long max_id, int position) {
|
||||
ScheduleLoader.Param param = new ScheduleLoader.Param(min_id, max_id, position);
|
||||
scheduleLoader.execute(param, loaderCallback);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:width="16sp"
|
||||
android:height="16sp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
|
|
|
@ -7,19 +7,43 @@
|
|||
<LinearLayout
|
||||
android:id="@+id/item_schedule_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/item_schedule_layout_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_schedule_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_schedule_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/item_schedule_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/item_schedule_layout_margins"
|
||||
android:lines="1"
|
||||
android:drawablePadding="@dimen/item_schedule_drawable_padding" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_schedule_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/item_schedule_layout_margins"
|
||||
android:maxLines="@integer/item_schedule_text_max_lines" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/item_schedule_delete_button"
|
||||
android:layout_width="@dimen/item_schedule_button_size"
|
||||
android:layout_height="@dimen/item_schedule_button_size"
|
||||
android:padding="@dimen/item_schedule_button_padding"
|
||||
android:layout_margin="@dimen/item_schedule_layout_margins"
|
||||
android:contentDescription="@string/descr_remove_schedule"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/cross"
|
||||
style="@style/RoundButton" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
android:id="@+id/menu_navigator_filter"
|
||||
android:title="@string/menu_open_filter" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_navigator_schedule"
|
||||
android:title="@string/menu_schedule" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_navigator_status"
|
||||
android:title="@string/menu_status" />
|
||||
|
|
|
@ -142,7 +142,12 @@
|
|||
<dimen name="dropdown_drawable_padding">5dp</dimen>
|
||||
|
||||
<!--dimens of item_schedule-xml-->
|
||||
<dimen name="item_schedule_layout_margins">3dp</dimen>
|
||||
<dimen name="item_schedule_layout_padding">5dp</dimen>
|
||||
<dimen name="item_schedule_button_padding">1dp</dimen>
|
||||
<dimen name="item_schedule_button_size">20sp</dimen>
|
||||
<dimen name="item_schedule_drawable_padding">5dp</dimen>
|
||||
<integer name="item_schedule_text_max_lines">12</integer>
|
||||
|
||||
<!--dimens of page_login.xml-->
|
||||
<dimen name="loginpage_toolbar_height">@dimen/toolbar_height</dimen>
|
||||
|
|
|
@ -80,6 +80,8 @@
|
|||
<string name="info_hashtag_unfollowed">Hashtag unfollowed</string>
|
||||
<string name="info_hashtag_unfeatured">Hashtag unfeatured</string>
|
||||
<string name="info_status_reported">Status reported</string>
|
||||
<string name="info_schedule_updated">scheduled post updated</string>
|
||||
<string name="info_schedule_removed">scheduled post removed</string>
|
||||
<string name="info_error">Error</string>
|
||||
|
||||
<!-- toast messages for error information -->
|
||||
|
@ -130,6 +132,7 @@
|
|||
|
||||
<!-- menu icon strings -->
|
||||
<string name="menu_status">write status</string>
|
||||
<string name="menu_schedule">scheduled status</string>
|
||||
<string name="menu_hashtags">Hashtags</string>
|
||||
<string name="menu_hashtag_follow">follow hashtag</string>
|
||||
<string name="menu_hashtag_unfollow">unfollow hashtag</string>
|
||||
|
@ -291,6 +294,7 @@
|
|||
<string name="confirm_remove_user_from_list">remove user from list?</string>
|
||||
<string name="descr_remove_user">remove user from list</string>
|
||||
<string name="descr_remove_hashtag">remove hashtag from list</string>
|
||||
<string name="descr_remove_schedule">remove scheduled status from list</string>
|
||||
<string name="descr_remove_domain">remove domain from list</string>
|
||||
<string name="confirm_unknown_error">unknown error!</string>
|
||||
<string name="list_following_indicator">following list</string>
|
||||
|
@ -311,11 +315,13 @@
|
|||
<string name="confirm_proxy_bypass">Opening an external link would bypass proxy connection. Proceed?</string>
|
||||
<string name="confirm_hashtag_unfollow">unfollow hashtag?</string>
|
||||
<string name="confirm_hashtag_unfeature">unfeature hashtag?</string>
|
||||
<string name="confirm_schedule_remove">cancel scheduled post?</string>
|
||||
<string name="confirm_remove_account">remove account from list?</string>
|
||||
<string name="confirm_remove_filter">delete filter?</string>
|
||||
<string name="account_user_unnamed">\'unnamed\'</string>
|
||||
<string name="toolbar_status_favoriter">User favoriting this status</string>
|
||||
<string name="toolbar_status_liker">User liking this status</string>
|
||||
<string name="toolbar_schedule_title">scheduled Posts</string>
|
||||
<string name="time_now">now</string>
|
||||
<string name="status_replyname_empty">Reply</string>
|
||||
<string name="status_media_preview">Media preview</string>
|
||||
|
|
Loading…
Reference in New Issue