version upgrade, bug fix, moved notification dismiss to notification list fragment, added poll edit dialog

This commit is contained in:
nuclearfog 2023-03-10 17:38:44 +01:00
parent ad8815e792
commit 7c19241787
No known key found for this signature in database
GPG Key ID: 03488A185C476379
23 changed files with 353 additions and 89 deletions

View File

@ -13,8 +13,8 @@ android {
applicationId 'org.nuclearfog.twidda'
minSdkVersion 21
targetSdkVersion 33
versionCode 75
versionName '3.0.7'
versionCode 76
versionName '3.0.8'
resConfigs 'en', 'de-rDE', 'zh-rCN'
}

View File

@ -10,9 +10,10 @@ import androidx.annotation.WorkerThread;
import java.lang.ref.WeakReference;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Executor implementation used to run tasks asynchronously
@ -26,10 +27,15 @@ public abstract class AsyncExecutor<Parameter, Result> {
*/
private static final int N_THREAD = 4;
/**
* timeout for queued processes
*/
private static final long P_TIMEOUT = 5000L;
/**
* thread pool executor
*/
private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(N_THREAD);
private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor(1, N_THREAD, P_TIMEOUT, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
/**
* handler used to send result back to activity/fragment

View File

@ -16,7 +16,7 @@ import org.nuclearfog.twidda.model.Notification;
*
* @author nuclearfog
*/
public class NotificationAction extends AsyncExecutor<NotificationAction.NotificationParam, NotificationAction.NotificationResult> {
public class NotificationAction extends AsyncExecutor<NotificationAction.NotificationActionParam, NotificationAction.NotificationActionResult> {
private Connection connection;
private AppDatabase db;
@ -32,39 +32,39 @@ public class NotificationAction extends AsyncExecutor<NotificationAction.Notific
@NonNull
@Override
protected NotificationResult doInBackground(@NonNull NotificationParam param) {
protected NotificationActionResult doInBackground(@NonNull NotificationActionParam param) {
try {
switch (param.mode) {
case NotificationParam.DATABASE:
case NotificationActionParam.DATABASE:
Notification result = db.getNotification(param.id);
if (result != null) {
return new NotificationResult(NotificationResult.DATABASE, result, null);
return new NotificationActionResult(NotificationActionResult.DATABASE, param.id, result, null);
}
case NotificationParam.ONLINE:
case NotificationActionParam.ONLINE:
result = connection.getNotification(param.id);
return new NotificationResult(NotificationResult.ONLINE, result, null);
return new NotificationActionResult(NotificationActionResult.ONLINE, param.id, result, null);
case NotificationParam.DISMISS:
case NotificationActionParam.DISMISS:
connection.dismissNotification(param.id);
db.removeNotification(param.id);
return new NotificationResult(NotificationResult.DISMISS, null, null);
return new NotificationActionResult(NotificationActionResult.DISMISS, param.id, null, null);
}
} catch (ConnectionException exception) {
if (exception.getErrorCode() == ConnectionException.RESOURCE_NOT_FOUND) {
db.removeNotification(param.id);
}
return new NotificationResult(NotificationResult.ERROR, null, exception);
return new NotificationActionResult(NotificationActionResult.ERROR, param.id, null, exception);
} catch (Exception exception) {
exception.printStackTrace();
}
return new NotificationResult(NotificationResult.ERROR, null, null);
return new NotificationActionResult(NotificationActionResult.ERROR, param.id, null, null);
}
/**
*
*/
public static class NotificationParam {
public static class NotificationActionParam {
public static final int DATABASE = 1;
public static final int ONLINE = 2;
@ -73,7 +73,7 @@ public class NotificationAction extends AsyncExecutor<NotificationAction.Notific
public final int mode;
public final long id;
public NotificationParam(int mode, long id) {
public NotificationActionParam(int mode, long id) {
this.mode = mode;
this.id = id;
}
@ -82,7 +82,7 @@ public class NotificationAction extends AsyncExecutor<NotificationAction.Notific
/**
*
*/
public static class NotificationResult {
public static class NotificationActionResult {
public static final int ERROR = -1;
public static final int DATABASE = 3;
@ -94,11 +94,13 @@ public class NotificationAction extends AsyncExecutor<NotificationAction.Notific
@Nullable
public final ConnectionException exception;
public final int mode;
public final long id;
public NotificationResult(int mode, @Nullable Notification notification, @Nullable ConnectionException exception) {
public NotificationActionResult(int mode, long id, @Nullable Notification notification, @Nullable ConnectionException exception) {
this.exception = exception;
this.notification = notification;
this.mode = mode;
this.id = id;
}
}
}

View File

@ -19,7 +19,7 @@ import java.util.List;
*
* @author nuclearfog
*/
public class NotificationLoader extends AsyncExecutor<NotificationLoader.NotificationParam, NotificationLoader.NotificationResult> {
public class NotificationLoader extends AsyncExecutor<NotificationLoader.NotificationLoaderParam, NotificationLoader.NotificationLoaderResult> {
private Connection connection;
private AppDatabase db;
@ -35,7 +35,7 @@ public class NotificationLoader extends AsyncExecutor<NotificationLoader.Notific
@NonNull
@Override
protected NotificationResult doInBackground(@NonNull NotificationParam params) {
protected NotificationLoaderResult doInBackground(@NonNull NotificationLoaderParam params) {
List<Notification> result = null;
try {
if (params.minId == 0L && params.maxId == 0L) {
@ -51,22 +51,22 @@ public class NotificationLoader extends AsyncExecutor<NotificationLoader.Notific
}
}
} catch (ConnectionException exception) {
return new NotificationResult(null, params.position, exception);
return new NotificationLoaderResult(null, params.position, exception);
} catch (Exception e) {
e.printStackTrace();
}
return new NotificationResult(result, params.position, null);
return new NotificationLoaderResult(result, params.position, null);
}
/**
*
*/
public static class NotificationParam {
public static class NotificationLoaderParam {
public final int position;
public final long minId, maxId;
public NotificationParam(int position, long minId, long maxId) {
public NotificationLoaderParam(int position, long minId, long maxId) {
this.position = position;
this.minId = minId;
this.maxId = maxId;
@ -76,7 +76,7 @@ public class NotificationLoader extends AsyncExecutor<NotificationLoader.Notific
/**
*
*/
public static class NotificationResult {
public static class NotificationLoaderResult {
public final int position;
@Nullable
@ -84,7 +84,7 @@ public class NotificationLoader extends AsyncExecutor<NotificationLoader.Notific
@Nullable
public final ConnectionException exception;
NotificationResult(@Nullable List<Notification> notifications, int position, @Nullable ConnectionException exception) {
NotificationLoaderResult(@Nullable List<Notification> notifications, int position, @Nullable ConnectionException exception) {
this.notifications = notifications;
this.exception = exception;
this.position = position;

View File

@ -202,7 +202,7 @@ public class StatusUpdate {
* get type of attachment
* currently there is only one type of media used at once
*
* @return media type {@link #EMPTY ,#MEDIA_VIDEO,#MEDIA_IMAGE,#MEDIA_GIF}
* @return media type {@link #EMPTY,#MEDIA_VIDEO,#MEDIA_IMAGE,#MEDIA_GIF}
*/
public int getAttachmentType() {
return attachment;

View File

@ -602,7 +602,7 @@ public class SettingsActivity extends AppCompatActivity implements OnClickListen
if (result.locations != null) {
locationAdapter.replaceItems(result.locations);
int position = locationAdapter.indexOf(settings.getTrendLocation());
if (position > 0)
if (position >= 0)
locationSpinner.setSelection(position, false);
locationSpinner.setOnItemSelectedListener(this);
} else {

View File

@ -50,8 +50,8 @@ import org.nuclearfog.textviewtool.LinkAndScrollMovement;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.async.NotificationAction;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationParam;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationResult;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionParam;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionResult;
import org.nuclearfog.twidda.backend.async.StatusAction;
import org.nuclearfog.twidda.backend.async.StatusAction.StatusParam;
import org.nuclearfog.twidda.backend.async.StatusAction.StatusResult;
@ -62,7 +62,6 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.config.Configuration;
import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.database.impl.DatabaseNotification;
import org.nuclearfog.twidda.database.impl.DatabaseStatus;
@ -325,7 +324,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
super.onStart();
if (notification != null) {
if (notification instanceof DatabaseNotification) {
NotificationParam param = new NotificationParam(NotificationParam.ONLINE, notification.getId());
NotificationActionParam param = new NotificationActionParam(NotificationActionParam.ONLINE, notification.getId());
notificationAsync.execute(param, this::onNotificationResult);
}
} else if (status != null) {
@ -343,7 +342,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
StatusParam param = new StatusParam(StatusParam.DATABASE, statusId);
statusAsync.execute(param, this::onStatusResult);
} else if (notificationId != 0L) {
NotificationParam param = new NotificationParam(NotificationParam.ONLINE, notificationId);
NotificationActionParam param = new NotificationActionParam(NotificationActionParam.ONLINE, notificationId);
notificationAsync.execute(param, this::onNotificationResult);
}
}
@ -393,15 +392,9 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
MenuItem optHide = m.findItem(R.id.menu_status_hide);
MenuItem optCopy = m.findItem(R.id.menu_status_copy);
MenuItem optMetrics = m.findItem(R.id.menu_status_metrics);
MenuItem optDismiss = m.findItem(R.id.menu_notification_dismiss);
MenuItem menuBookmark = m.findItem(R.id.menu_status_bookmark);
SubMenu copyMenu = optCopy.getSubMenu();
Configuration config = settings.getLogin().getConfiguration();
// set notification options
if (notification != null) {
optDismiss.setVisible(config.NotificationDismissEnabled());
}
// set status options
if (status != null) {
Status currentStatus = status;
@ -498,10 +491,6 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
metricsDialog.show(status.getMetrics());
}
}
// open notification dismiss dialog
else if (item.getItemId() == R.id.menu_notification_dismiss) {
confirmDialog.show(ConfirmDialog.NOTIFICATION_DISMISS);
}
// copy media links
else if (item.getGroupId() == MENU_GROUP_COPY) {
int index = item.getItemId();
@ -661,13 +650,6 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
}
}
break;
case ConfirmDialog.NOTIFICATION_DISMISS:
if (notification != null) {
NotificationParam param = new NotificationParam(NotificationParam.DISMISS, notification.getId());
notificationAsync.execute(param, this::onNotificationResult);
}
break;
}
}
@ -970,23 +952,23 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
*
* @param result notification containing status information
*/
public void onNotificationResult(NotificationResult result) {
public void onNotificationResult(NotificationActionResult result) {
switch (result.mode) {
case NotificationResult.DATABASE:
case NotificationActionResult.DATABASE:
if (result.notification != null) {
NotificationParam param = new NotificationParam(NotificationParam.ONLINE, result.notification.getId());
NotificationActionParam param = new NotificationActionParam(NotificationActionParam.ONLINE, result.notification.getId());
notificationAsync.execute(param, this::onNotificationResult);
}
// fall through
case NotificationResult.ONLINE:
case NotificationActionResult.ONLINE:
if (result.notification != null && result.notification.getStatus() != null) {
notification = result.notification;
setStatus(result.notification.getStatus());
}
break;
case NotificationResult.DISMISS:
case NotificationActionResult.DISMISS:
if (notification != null) {
Intent intent = new Intent();
intent.putExtra(INTENT_NOTIFICATION_REMOVED_ID, notification.getId());
@ -996,7 +978,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
finish();
break;
case NotificationResult.ERROR:
case NotificationActionResult.ERROR:
String message = ErrorHandler.getErrorMessage(this, result.exception);
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
if (notification == null) {

View File

@ -92,8 +92,9 @@ public class UserlistActivity extends AppCompatActivity implements ActivityResul
/**
* regex pattern to validate username
* e.g. username, @username or @username@instance.social
*/
private static final Pattern USERNAME_PATTERN = Pattern.compile("@?\\w{1,15}");
private static final Pattern USERNAME_PATTERN = Pattern.compile("@?[\\w\\d]{1,20}(@[\\w\\d.]{1,50})?");
private ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this);

View File

@ -0,0 +1,42 @@
package org.nuclearfog.twidda.ui.adapter;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.Adapter;
/**
*
*/
public class EditOptionsAdapter extends Adapter {
public EditOptionsAdapter(Context context) {}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
public interface OnOptionChangedListener {
}
}

View File

@ -145,9 +145,11 @@ public class NotificationAdapter extends Adapter<ViewHolder> implements OnHolder
long sinceId = 0;
long maxId = 0;
if (index == 0) {
Notification notification = items.get(index + 1);
if (notification != null) {
sinceId = notification.getId();
if (items.size() > 1) {
Notification notification = items.get(index + 1);
if (notification != null) {
sinceId = notification.getId();
}
}
} else if (index == items.size() - 1) {
Notification notification = items.get(index - 1);
@ -186,7 +188,13 @@ public class NotificationAdapter extends Adapter<ViewHolder> implements OnHolder
case OnHolderClickListener.STATUS_CLICK:
if (item != null) {
listener.onNotificationClick(item);
listener.onNotificationClick(item, OnNotificationClickListener.VIEW);
}
break;
case OnHolderClickListener.NOTIFICATION_DISMISS:
if (item != null) {
listener.onNotificationClick(item, OnNotificationClickListener.DISMISS);
}
break;
}
@ -269,12 +277,23 @@ public class NotificationAdapter extends Adapter<ViewHolder> implements OnHolder
*/
public interface OnNotificationClickListener {
/**
* show a notification
*/
int VIEW = 1;
/**
* dismiss a notification
*/
int DISMISS = 2;
/**
* called on notification click
*
* @param notification clicked notification
* @param action action {@link #VIEW,#DISMISS}
*/
void onNotificationClick(Notification notification);
void onNotificationClick(Notification notification, int action);
/**
* called on user item click

View File

@ -45,6 +45,8 @@ public interface OnHolderClickListener {
int POLL_VOTE = 18;
int NOTIFICATION_DISMISS = 19;
/**
* called when an item was clicked
*

View File

@ -44,6 +44,7 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
private ImageView profile, repostUserIcon, verifiedIcon, lockedIcon, repostIcon, favoriteIcon, replyStatus;
private TextView username, screenname, text, repost, favorite, reply, reposter, created, replyname, label;
private View dismissButton;
private RecyclerView iconList;
private GlobalSettings settings;
@ -61,6 +62,7 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
CardView cardLayout = (CardView) itemView;
ViewGroup container = itemView.findViewById(R.id.item_status_container);
label = itemView.findViewById(R.id.item_status_label);
dismissButton = itemView.findViewById(R.id.item_status_notification_dismiss);
profile = itemView.findViewById(R.id.item_status_profile_image);
verifiedIcon = itemView.findViewById(R.id.item_status_verified_icon);
lockedIcon = itemView.findViewById(R.id.item_status_locked_icon);
@ -93,6 +95,7 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
label.setOnClickListener(this);
itemView.setOnClickListener(this);
dismissButton.setOnClickListener(this);
}
@ -104,6 +107,8 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
listener.onItemClick(position, OnHolderClickListener.STATUS_CLICK);
} else if (v == label) {
listener.onItemClick(position, OnHolderClickListener.STATUS_LABEL);
} else if (v == dismissButton) {
listener.onItemClick(position, OnHolderClickListener.NOTIFICATION_DISMISS);
}
}
}
@ -234,6 +239,9 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
label.setVisibility(View.VISIBLE);
label.setText(text);
label.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0);
if (settings.getLogin().getConfiguration().NotificationDismissEnabled()) {
dismissButton.setVisibility(View.VISIBLE);
}
AppStyles.setDrawableColor(label, settings.getIconColor());
}
}

View File

@ -40,6 +40,7 @@ public class UserHolder extends ViewHolder implements OnClickListener {
private TextView username, screenname, followingCount, followerCount, label;
private ImageView profileImg, verifyIcon, lockedIcon;
private ImageButton delete;
private View notificationDismiss;
private GlobalSettings settings;
private Picasso picasso;
@ -56,6 +57,7 @@ public class UserHolder extends ViewHolder implements OnClickListener {
CardView background = (CardView) itemView;
ViewGroup container = itemView.findViewById(R.id.item_user_container);
label = itemView.findViewById(R.id.item_user_label);
notificationDismiss = itemView.findViewById(R.id.item_user_notification_dismiss);
username = itemView.findViewById(R.id.item_user_username);
screenname = itemView.findViewById(R.id.item_user_screenname);
followingCount = itemView.findViewById(R.id.item_user_following_count);
@ -74,6 +76,7 @@ public class UserHolder extends ViewHolder implements OnClickListener {
}
itemView.setOnClickListener(this);
notificationDismiss.setOnClickListener(this);
delete.setOnClickListener(this);
}
@ -86,6 +89,8 @@ public class UserHolder extends ViewHolder implements OnClickListener {
listener.onItemClick(position, OnHolderClickListener.USER_CLICK);
} else if (v == delete) {
listener.onItemClick(position, OnHolderClickListener.USER_REMOVE);
} else if (v == notificationDismiss) {
listener.onItemClick(position, OnHolderClickListener.NOTIFICATION_DISMISS);
}
}
}
@ -126,11 +131,11 @@ public class UserHolder extends ViewHolder implements OnClickListener {
int iconRes;
String text, name;
Resources resources = itemView.getResources();
if (notification.getUser() != null)
if (notification.getUser() != null) {
name = notification.getUser().getScreenname();
else
} else {
name = "";
}
switch (notification.getType()) {
default:
text = "";
@ -147,6 +152,9 @@ public class UserHolder extends ViewHolder implements OnClickListener {
iconRes = R.drawable.follower_request;
break;
}
if (settings.getLogin().getConfiguration().NotificationDismissEnabled()) {
notificationDismiss.setVisibility(VISIBLE);
}
label.setVisibility(VISIBLE);
label.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0);
label.setText(text);

View File

@ -55,7 +55,7 @@ public class ConnectionDialog extends Dialog implements OnCheckedChangeListener,
api1 = findViewById(R.id.dialog_connection_api1);
api2 = findViewById(R.id.dialog_connection_api2);
int width = (int) (context.getResources().getDisplayMetrics().widthPixels * 0.9);
int width = (int) (context.getResources().getDisplayMetrics().widthPixels * 0.9f);
getWindow().setLayout(width, WRAP_CONTENT);
AppStyles.setTheme(root);
enableApi.setOnCheckedChangeListener(this);

View File

@ -0,0 +1,29 @@
package org.nuclearfog.twidda.ui.dialogs;
import android.app.Dialog;
import android.content.Context;
import androidx.annotation.NonNull;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.helper.PollUpdate;
/**
* Dialog class used to show poll editor
*
* @author nuclearfog
*/
public class PollDialog extends Dialog {
public PollDialog(@NonNull Context context) {
super(context, R.style.PollDialog);
setContentView(R.layout.dialog_poll);
}
public void show(@NonNull PollUpdate poll) {
}
}

View File

@ -12,10 +12,14 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.NotificationAction;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionParam;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionResult;
import org.nuclearfog.twidda.backend.async.NotificationLoader;
import org.nuclearfog.twidda.backend.async.NotificationLoader.NotificationParam;
import org.nuclearfog.twidda.backend.async.NotificationLoader.NotificationResult;
import org.nuclearfog.twidda.backend.async.NotificationLoader.NotificationLoaderParam;
import org.nuclearfog.twidda.backend.async.NotificationLoader.NotificationLoaderResult;
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.model.Notification;
import org.nuclearfog.twidda.model.User;
@ -23,36 +27,47 @@ import org.nuclearfog.twidda.ui.activities.ProfileActivity;
import org.nuclearfog.twidda.ui.activities.StatusActivity;
import org.nuclearfog.twidda.ui.adapter.NotificationAdapter;
import org.nuclearfog.twidda.ui.adapter.NotificationAdapter.OnNotificationClickListener;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
/**
* fragment to show notifications
*
* @author nuclearfog
*/
public class NotificationFragment extends ListFragment implements OnNotificationClickListener, AsyncCallback<NotificationResult>, ActivityResultCallback<ActivityResult> {
public class NotificationFragment extends ListFragment implements OnNotificationClickListener, OnConfirmListener,
AsyncCallback<NotificationLoaderResult>, ActivityResultCallback<ActivityResult> {
private ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this);
private NotificationLoader notificationAsync;
private NotificationLoader notificationLoader;
private NotificationAction notificationAction;
private NotificationAdapter adapter;
private ConfirmDialog confirmDialog;
private Notification select;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
confirmDialog = new ConfirmDialog(requireContext());
notificationLoader = new NotificationLoader(requireContext());
notificationAction = new NotificationAction(requireContext());
adapter = new NotificationAdapter(requireContext(), this);
notificationAsync = new NotificationLoader(requireContext());
setAdapter(adapter);
confirmDialog.setConfirmListener(this);
load(0L, 0L, 0);
setRefresh(true);
}
@Override
public void onDestroyView() {
notificationAsync.cancel();
super.onDestroyView();
public void onDestroy() {
notificationLoader.cancel();
notificationAction.cancel();
super.onDestroy();
}
@ -75,11 +90,20 @@ public class NotificationFragment extends ListFragment implements OnNotification
@Override
public void onNotificationClick(Notification notification) {
public void onNotificationClick(Notification notification, int action) {
if (!isRefreshing()) {
Intent intent = new Intent(requireContext(), StatusActivity.class);
intent.putExtra(StatusActivity.KEY_NOTIFICATION_DATA, notification);
activityResultLauncher.launch(intent);
switch (action) {
case OnNotificationClickListener.VIEW:
Intent intent = new Intent(requireContext(), StatusActivity.class);
intent.putExtra(StatusActivity.KEY_NOTIFICATION_DATA, notification);
activityResultLauncher.launch(intent);
break;
case OnNotificationClickListener.DISMISS:
confirmDialog.show(ConfirmDialog.NOTIFICATION_DISMISS);
select = notification;
break;
}
}
}
@ -96,7 +120,7 @@ public class NotificationFragment extends ListFragment implements OnNotification
@Override
public boolean onPlaceholderClick(long sinceId, long maxId, int position) {
if (notificationAsync.isIdle()) {
if (notificationLoader.isIdle()) {
load(sinceId, maxId, position);
return true;
}
@ -127,7 +151,7 @@ public class NotificationFragment extends ListFragment implements OnNotification
@Override
public void onResult(NotificationResult result) {
public void onResult(NotificationLoaderResult result) {
if (result.notifications != null) {
adapter.addItems(result.notifications, result.position);
} else if (getContext() != null) {
@ -138,13 +162,39 @@ public class NotificationFragment extends ListFragment implements OnNotification
setRefresh(false);
}
@Override
public void onConfirm(int type, boolean rememberChoice) {
if (type == ConfirmDialog.NOTIFICATION_DISMISS) {
if (select != null) {
NotificationActionParam param = new NotificationActionParam(NotificationActionParam.DISMISS, select.getId());
notificationAction.execute(param, this::ondismiss);
}
}
}
/**
*
*/
public void ondismiss(NotificationActionResult result) {
if (result.mode == NotificationActionResult.DISMISS) {
adapter.removeItem(result.id);
} else if (result.mode == NotificationActionResult.ERROR) {
String message = ErrorHandler.getErrorMessage(getContext(), result.exception);
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
if (result.exception != null && result.exception.getErrorCode() == ConnectionException.RESOURCE_NOT_FOUND) {
adapter.removeItem(result.id);
}
}
}
/**
* @param minId lowest notification ID to load
* @param maxId highest notification Id to load
* @param pos index to insert the new items
*/
private void load(long minId, long maxId, int pos) {
NotificationParam param = new NotificationParam(pos, minId, maxId);
notificationAsync.execute(param, this);
NotificationLoaderParam param = new NotificationLoaderParam(pos, minId, maxId);
notificationLoader.execute(param, this);
}
}

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/dialog_poll_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dialog_poll_layout_margins"
android:text="@string/dialog_poll_title"
android:textSize="@dimen/dialog_poll_title_textsize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/dialog_poll_option_list"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/dialog_poll_option_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/dialog_poll_layout_margins"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/dialog_poll_title"
app:layout_constraintBottom_toTopOf="@id/dialog_poll_list_barrier"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/dialog_poll_list_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="dialog_poll_duration_input,dialog_poll_duration_label,dialog_poll_duration_timeunit"/>
<TextView
android:id="@+id/dialog_poll_duration_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_poll_duration_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/dialog_poll_option_list"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/dialog_poll_duration_input"/>
<EditText
android:id="@+id/dialog_poll_duration_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:inputType="numberDecimal"
android:maxLength="3"
android:autofillHints=""
android:hint="@string/dialog_poll_duration_hint"
android:layout_marginStart="@dimen/dialog_poll_layout_margins"
android:layout_marginEnd="@dimen/dialog_poll_layout_margins"
app:layout_constraintStart_toEndOf="@id/dialog_poll_duration_label"
app:layout_constraintTop_toBottomOf="@id/dialog_poll_option_list"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/dialog_poll_duration_timeunit" />
<Spinner
android:id="@+id/dialog_poll_duration_timeunit"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/dialog_poll_duration_input"
app:layout_constraintTop_toBottomOf="@id/dialog_poll_option_list"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -24,7 +24,23 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/item_status_label_barrier"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toStartOf="@id/item_status_notification_dismiss" />
<ImageButton
android:id="@+id/item_status_notification_dismiss"
android:layout_width="@dimen/item_status_notification_button_size"
android:layout_height="@dimen/item_status_notification_button_size"
android:padding="@dimen/item_status_notification_button_drawable_padding"
android:visibility="gone"
android:src="@drawable/cross"
android:scaleType="fitCenter"
android:contentDescription="@string/notification_dismiss"
app:layout_constraintDimensionRatio="1"
app:layout_constraintStart_toEndOf="@id/item_status_label"
app:layout_constraintTop_toTopOf="@id/item_status_label"
app:layout_constraintBottom_toBottomOf="@id/item_status_label"
app:layout_constraintEnd_toEndOf="parent"
style="@style/FeedbackButton"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/item_status_label_barrier"

View File

@ -24,7 +24,23 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/item_user_notification_barrier"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toStartOf="@id/item_user_notification_dismiss"/>
<ImageButton
android:id="@+id/item_user_notification_dismiss"
android:layout_width="@dimen/item_user_notification_button_size"
android:layout_height="@dimen/item_user_notification_button_size"
android:padding="@dimen/item_user_notification_button_drawable_padding"
android:visibility="gone"
android:src="@drawable/cross"
android:scaleType="fitCenter"
android:contentDescription="@string/notification_dismiss"
app:layout_constraintDimensionRatio="1"
app:layout_constraintStart_toEndOf="@id/item_user_label"
app:layout_constraintTop_toTopOf="@id/item_user_label"
app:layout_constraintBottom_toBottomOf="@id/item_user_label"
app:layout_constraintEnd_toEndOf="parent"
style="@style/FeedbackButton"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/item_user_notification_barrier"

View File

@ -34,11 +34,6 @@
android:title="@string/menu_status_hide"
android:visible="false" />
<item
android:id="@+id/menu_notification_dismiss"
android:title="@string/menu_notification_dismiss"
android:visible="false" />
<item
android:id="@+id/menu_status_delete"
android:title="@string/menu_status_delete"

View File

@ -77,6 +77,8 @@
<dimen name="item_status_text_margin">5dp</dimen>
<dimen name="item_status_label_margin">8dp</dimen>
<dimen name="item_status_drawable_margin">5dp</dimen>
<dimen name="item_status_notification_button_size">20sp</dimen>
<dimen name="item_status_notification_button_drawable_padding">1dp</dimen>
<dimen name="item_status_textsize_notification">12sp</dimen>
<dimen name="item_status_textsize_button">12sp</dimen>
<dimen name="item_status_textsize_date">12sp</dimen>
@ -98,6 +100,8 @@
<dimen name="item_user_textview_padding">5dp</dimen>
<dimen name="item_user_drawable_margin">5dp</dimen>
<dimen name="item_user_button_size">36dp</dimen>
<dimen name="item_user_notification_button_size">@dimen/item_status_notification_button_size</dimen>
<dimen name="item_user_notification_button_drawable_padding">@dimen/item_status_notification_button_drawable_padding</dimen>
<dimen name="item_user_button_padding">7dp</dimen>
<dimen name="item_user_textsize_small">12sp</dimen>
<dimen name="item_user_icon_size">14sp</dimen>
@ -236,6 +240,10 @@
<dimen name="dialog_connection_root_padding">8dp</dimen>
<dimen name="dialog_connection_textsizte_normal">13sp</dimen>
<!--dimens of dialog_poll.xml-->
<dimen name="dialog_poll_layout_margins">3dp</dimen>
<dimen name="dialog_poll_title_textsize">18sp</dimen>
<!--dimens of tabitem.xml-->
<dimen name="tabitem_icon_size">22sp</dimen>
<dimen name="tabitem_textsize">11sp</dimen>

View File

@ -213,6 +213,7 @@
<string name="confirm_cancel_status">discard changes?</string>
<string name="image_preview">Image preview</string>
<string name="status_add_image">add new image</string>
<string name="notification_dismiss">dismiss notification</string>
<string name="send_status">share status</string>
<string name="status_close">close editor</string>
<string name="profile_link">Link</string>
@ -220,6 +221,9 @@
<string name="profile_location">Location</string>
<string name="profile_bio">Bio</string>
<string name="connection_discard">discard</string>
<string name="dialog_poll_title">create poll</string>
<string name="dialog_poll_duration_label">Duration:</string>
<string name="dialog_poll_duration_hint">count</string>
<string name="confirm_discard">discard changes?</string>
<string name="user_data">User data</string>
<string name="follows_you">follows you</string>

View File

@ -49,6 +49,11 @@
<item name="android:windowMinWidthMinor">80%</item>
</style>
<style name="PollDialog" parent="Theme.AppCompat.Dialog">
<item name="android:windowMinWidthMinor">80%</item>
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
<!-- activity enter/exit animations -->
<style name="TransactionPending" parent="@android:style/Animation">
<item name="android:activityOpenEnterAnimation">@android:anim/fade_in</item>