finalized status schedule support, bug fix

This commit is contained in:
nuclearfog 2023-09-30 20:52:57 +02:00
parent 782a7b3120
commit ae5e367434
No known key found for this signature in database
GPG Key ID: 03488A185C476379
19 changed files with 577 additions and 30 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -14,6 +14,9 @@ public interface ScheduledStatus extends Serializable {
*/
long getId();
/**
* @return time to publish status
*/
long getPublishTime();
/**

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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
*

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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>