version upgrade, bug fix, moved notification dismiss to notification list fragment, added poll edit dialog
This commit is contained in:
parent
ad8815e792
commit
7c19241787
|
@ -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'
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -45,6 +45,8 @@ public interface OnHolderClickListener {
|
|||
|
||||
int POLL_VOTE = 18;
|
||||
|
||||
int NOTIFICATION_DISMISS = 19;
|
||||
|
||||
/**
|
||||
* called when an item was clicked
|
||||
*
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue