Direct play from the the lists
Pause/play buttons work; position in media item not always updated yet
This commit is contained in:
parent
af7b947368
commit
71db0368c4
@ -27,7 +27,7 @@ public class ActionButtonUtils {
|
|||||||
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
drawables = context.obtainStyledAttributes(new int[]{
|
drawables = context.obtainStyledAttributes(new int[]{
|
||||||
R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download, R.attr.borderless_button, R.attr.navigation_accept});
|
R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download, R.attr.av_pause, R.attr.navigation_accept});
|
||||||
labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label, R.string.mark_read_label};
|
labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label, R.string.mark_read_label};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ public class ActionButtonUtils {
|
|||||||
} else {
|
} else {
|
||||||
// item is not being downloaded
|
// item is not being downloaded
|
||||||
butSecondary.setVisibility(View.VISIBLE);
|
butSecondary.setVisibility(View.VISIBLE);
|
||||||
if (media.isPlaying()) {
|
if (media.isCurrentlyPlaying()) {
|
||||||
butSecondary.setImageDrawable(drawables.getDrawable(3));
|
butSecondary.setImageDrawable(drawables.getDrawable(3));
|
||||||
} else {
|
} else {
|
||||||
butSecondary
|
butSecondary
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package de.danoeh.antennapod.adapter;
|
package de.danoeh.antennapod.adapter;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
@ -9,6 +10,7 @@ import de.danoeh.antennapod.R;
|
|||||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||||
@ -46,7 +48,13 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
|
|||||||
DownloadRequester.getInstance().cancelDownload(context, media);
|
DownloadRequester.getInstance().cancelDownload(context, media);
|
||||||
Toast.makeText(context, R.string.download_cancelled_msg, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.download_cancelled_msg, Toast.LENGTH_SHORT).show();
|
||||||
} else { // media is downloaded
|
} else { // media is downloaded
|
||||||
if (item.getState() != FeedItem.State.PLAYING) {
|
if (item.hasMedia() && item.getMedia().isCurrentlyPlaying()) {
|
||||||
|
context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
|
||||||
|
}
|
||||||
|
else if (item.hasMedia() && item.getMedia().isPlaying()) {
|
||||||
|
context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE));
|
||||||
|
}
|
||||||
|
else {
|
||||||
DBTasks.playMedia(context, media, false, true, false);
|
DBTasks.playMedia(context, media, false, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,8 +188,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
FeedItem item = (FeedItem) v.getTag();
|
FeedItem item = (FeedItem) v.getTag();
|
||||||
callback.onActionButtonPressed(item);
|
callback.onActionButtonPressed(item);
|
||||||
EventDistributor.getInstance().sendPlaybackHistoryUpdateBroadcast();
|
|
||||||
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,7 +148,6 @@ public class NewEpisodesListAdapter extends BaseAdapter {
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
FeedItem item = (FeedItem) v.getTag();
|
FeedItem item = (FeedItem) v.getTag();
|
||||||
actionButtonCallback.onActionButtonPressed(item);
|
actionButtonCallback.onActionButtonPressed(item);
|
||||||
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,7 +138,6 @@ public class QueueListAdapter extends BaseAdapter {
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
FeedItem item = (FeedItem) v.getTag();
|
FeedItem item = (FeedItem) v.getTag();
|
||||||
actionButtonCallback.onActionButtonPressed(item);
|
actionButtonCallback.onActionButtonPressed(item);
|
||||||
EventDistributor.getInstance().sendQueueUpdateBroadcast();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -296,6 +296,9 @@ public class ItemlistFragment extends ListFragment {
|
|||||||
updateProgressBarVisibility();
|
updateProgressBarVisibility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((arg & EventDistributor.PLAYER_STATUS_UPDATE) != 0) {
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -340,6 +340,9 @@ public class NewEpisodesFragment extends Fragment {
|
|||||||
getActivity().supportInvalidateOptionsMenu();
|
getActivity().supportInvalidateOptionsMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((arg & EventDistributor.PLAYER_STATUS_UPDATE) != 0) {
|
||||||
|
listAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,6 +171,9 @@ public class PlaybackHistoryFragment extends ListFragment {
|
|||||||
startItemLoader();
|
startItemLoader();
|
||||||
getActivity().supportInvalidateOptionsMenu();
|
getActivity().supportInvalidateOptionsMenu();
|
||||||
}
|
}
|
||||||
|
if ((arg & EventDistributor.PLAYER_STATUS_UPDATE) != 0) {
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -477,6 +477,9 @@ public class QueueFragment extends Fragment {
|
|||||||
getActivity().supportInvalidateOptionsMenu();
|
getActivity().supportInvalidateOptionsMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((arg & EventDistributor.PLAYER_STATUS_UPDATE) != 0) {
|
||||||
|
listAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ public class EventDistributor extends Observable {
|
|||||||
public static final int PLAYBACK_HISTORY_UPDATE = 16;
|
public static final int PLAYBACK_HISTORY_UPDATE = 16;
|
||||||
public static final int DOWNLOAD_QUEUED = 32;
|
public static final int DOWNLOAD_QUEUED = 32;
|
||||||
public static final int DOWNLOAD_HANDLED = 64;
|
public static final int DOWNLOAD_HANDLED = 64;
|
||||||
|
public static final int PLAYER_STATUS_UPDATE = 128;
|
||||||
|
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
private AbstractQueue<Integer> events;
|
private AbstractQueue<Integer> events;
|
||||||
@ -124,6 +125,10 @@ public class EventDistributor extends Observable {
|
|||||||
addEvent(DOWNLOAD_HANDLED);
|
addEvent(DOWNLOAD_HANDLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendPlayerStatusUpdateBroadcast() {
|
||||||
|
addEvent(PLAYER_STATUS_UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
public static abstract class EventListener implements Observer {
|
public static abstract class EventListener implements Observer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,6 +127,15 @@ public class FeedMedia extends FeedFile implements Playable {
|
|||||||
&& PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == id;
|
&& PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads playback preferences to determine whether this FeedMedia object is
|
||||||
|
* currently being played and the player status is playing.
|
||||||
|
*/
|
||||||
|
public boolean isCurrentlyPlaying() {
|
||||||
|
return isPlaying() && PlaybackPreferences.getPlayerStatusIsPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTypeAsInt() {
|
public int getTypeAsInt() {
|
||||||
return FEEDFILETYPE_FEEDMEDIA;
|
return FEEDFILETYPE_FEEDMEDIA;
|
||||||
|
@ -8,6 +8,7 @@ import android.util.Log;
|
|||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.BuildConfig;
|
import de.danoeh.antennapod.core.BuildConfig;
|
||||||
|
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to preferences set by the playback service. A private
|
* Provides access to preferences set by the playback service. A private
|
||||||
@ -43,6 +44,9 @@ public class PlaybackPreferences implements
|
|||||||
/** True if last played media was a video. */
|
/** True if last played media was a video. */
|
||||||
public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
|
public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
|
||||||
|
|
||||||
|
/** True if player status is playing. */
|
||||||
|
public static final String PREF_PLAYER_STATUS_IS_PLAYING = "de.danoeh.antennapod.preferences.playerStatusIsPlaying";
|
||||||
|
|
||||||
/** Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. */
|
/** Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. */
|
||||||
public static final long NO_MEDIA_PLAYING = -1;
|
public static final long NO_MEDIA_PLAYING = -1;
|
||||||
|
|
||||||
@ -51,6 +55,7 @@ public class PlaybackPreferences implements
|
|||||||
private long currentlyPlayingMedia;
|
private long currentlyPlayingMedia;
|
||||||
private boolean currentEpisodeIsStream;
|
private boolean currentEpisodeIsStream;
|
||||||
private boolean currentEpisodeIsVideo;
|
private boolean currentEpisodeIsVideo;
|
||||||
|
private boolean playerStatusIsPlaying;
|
||||||
|
|
||||||
private static PlaybackPreferences instance;
|
private static PlaybackPreferences instance;
|
||||||
private Context context;
|
private Context context;
|
||||||
@ -87,6 +92,7 @@ public class PlaybackPreferences implements
|
|||||||
NO_MEDIA_PLAYING);
|
NO_MEDIA_PLAYING);
|
||||||
currentEpisodeIsStream = sp.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true);
|
currentEpisodeIsStream = sp.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true);
|
||||||
currentEpisodeIsVideo = sp.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false);
|
currentEpisodeIsVideo = sp.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false);
|
||||||
|
playerStatusIsPlaying = sp.getBoolean(PREF_PLAYER_STATUS_IS_PLAYING, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -109,6 +115,11 @@ public class PlaybackPreferences implements
|
|||||||
currentlyPlayingFeedMediaId = sp.getLong(
|
currentlyPlayingFeedMediaId = sp.getLong(
|
||||||
PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING);
|
PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING);
|
||||||
}
|
}
|
||||||
|
else if (key.equals(PREF_PLAYER_STATUS_IS_PLAYING)) {
|
||||||
|
playerStatusIsPlaying = sp.getBoolean(
|
||||||
|
PREF_PLAYER_STATUS_IS_PLAYING, false);
|
||||||
|
EventDistributor.getInstance().sendPlayerStatusUpdateBroadcast();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void instanceAvailable() {
|
private static void instanceAvailable() {
|
||||||
@ -143,4 +154,10 @@ public class PlaybackPreferences implements
|
|||||||
return instance.currentEpisodeIsVideo;
|
return instance.currentEpisodeIsVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean getPlayerStatusIsPlaying() {
|
||||||
|
instanceAvailable();
|
||||||
|
return instance.playerStatusIsPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,18 @@ public class PlaybackService extends Service {
|
|||||||
*/
|
*/
|
||||||
public static final String ACTION_SKIP_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.skipCurrentEpisode";
|
public static final String ACTION_SKIP_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.skipCurrentEpisode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the PlaybackService receives this action, it will pause playback.
|
||||||
|
*/
|
||||||
|
public static final String ACTION_PAUSE_PLAY_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.pausePlayCurrentEpisode";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the PlaybackService receives this action, it will resume playback.
|
||||||
|
*/
|
||||||
|
public static final String ACTION_RESUME_PLAY_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.resumePlayCurrentEpisode";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in NOTIFICATION_TYPE_RELOAD.
|
* Used in NOTIFICATION_TYPE_RELOAD.
|
||||||
*/
|
*/
|
||||||
@ -216,6 +228,10 @@ public class PlaybackService extends Service {
|
|||||||
AudioManager.ACTION_AUDIO_BECOMING_NOISY));
|
AudioManager.ACTION_AUDIO_BECOMING_NOISY));
|
||||||
registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(
|
registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(
|
||||||
ACTION_SKIP_CURRENT_EPISODE));
|
ACTION_SKIP_CURRENT_EPISODE));
|
||||||
|
registerReceiver(pausePlayCurrentEpisodeReceiver, new IntentFilter(
|
||||||
|
ACTION_PAUSE_PLAY_CURRENT_EPISODE));
|
||||||
|
registerReceiver(pauseResumeCurrentEpisodeReceiver, new IntentFilter(
|
||||||
|
ACTION_RESUME_PLAY_CURRENT_EPISODE));
|
||||||
remoteControlClient = setupRemoteControlClient();
|
remoteControlClient = setupRemoteControlClient();
|
||||||
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
|
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
|
||||||
mediaPlayer = new PlaybackServiceMediaPlayer(this, mediaPlayerCallback);
|
mediaPlayer = new PlaybackServiceMediaPlayer(this, mediaPlayerCallback);
|
||||||
@ -427,6 +443,7 @@ public class PlaybackService extends Service {
|
|||||||
// remove notifcation on pause
|
// remove notifcation on pause
|
||||||
stopForeground(true);
|
stopForeground(true);
|
||||||
}
|
}
|
||||||
|
writePlayerStatusPlaybackPreferences();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -443,9 +460,11 @@ public class PlaybackService extends Service {
|
|||||||
|
|
||||||
taskManager.startPositionSaver();
|
taskManager.startPositionSaver();
|
||||||
taskManager.startWidgetUpdater();
|
taskManager.startWidgetUpdater();
|
||||||
|
writePlayerStatusPlaybackPreferences();
|
||||||
setupNotification(newInfo);
|
setupNotification(newInfo);
|
||||||
started = true;
|
started = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ERROR:
|
case ERROR:
|
||||||
writePlaybackPreferencesNoMediaPlaying();
|
writePlaybackPreferencesNoMediaPlaying();
|
||||||
break;
|
break;
|
||||||
@ -634,6 +653,8 @@ public class PlaybackService extends Service {
|
|||||||
editor.putLong(
|
editor.putLong(
|
||||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
||||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
|
editor.putBoolean(
|
||||||
|
PlaybackPreferences.PREF_PLAYER_STATUS_IS_PLAYING, false);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,6 +668,7 @@ public class PlaybackService extends Service {
|
|||||||
PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
|
PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
|
||||||
MediaType mediaType = mediaPlayer.getCurrentMediaType();
|
MediaType mediaType = mediaPlayer.getCurrentMediaType();
|
||||||
boolean stream = mediaPlayer.isStreaming();
|
boolean stream = mediaPlayer.isStreaming();
|
||||||
|
boolean isPlaying = (info.playerStatus == PlayerStatus.PLAYING);
|
||||||
|
|
||||||
if (info.playable != null) {
|
if (info.playable != null) {
|
||||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
||||||
@ -683,6 +705,23 @@ public class PlaybackService extends Service {
|
|||||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
||||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
}
|
}
|
||||||
|
editor.putBoolean(
|
||||||
|
PlaybackPreferences.PREF_PLAYER_STATUS_IS_PLAYING, isPlaying);
|
||||||
|
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writePlayerStatusPlaybackPreferences() {
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.d(TAG, "Writing player status playback preferences");
|
||||||
|
|
||||||
|
SharedPreferences.Editor editor = PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||||
|
PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
|
||||||
|
boolean isPlaying = (info.playerStatus == PlayerStatus.PLAYING);
|
||||||
|
|
||||||
|
editor.putBoolean(
|
||||||
|
PlaybackPreferences.PREF_PLAYER_STATUS_IS_PLAYING, isPlaying);
|
||||||
|
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
@ -1101,6 +1140,28 @@ public class PlaybackService extends Service {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private BroadcastReceiver pauseResumeCurrentEpisodeReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (StringUtils.equals(intent.getAction(), ACTION_RESUME_PLAY_CURRENT_EPISODE)) {
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.d(TAG, "Received RESUME_PLAY_CURRENT_EPISODE intent");
|
||||||
|
mediaPlayer.resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private BroadcastReceiver pausePlayCurrentEpisodeReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (StringUtils.equals(intent.getAction(), ACTION_PAUSE_PLAY_CURRENT_EPISODE)) {
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.d(TAG, "Received PAUSE_PLAY_CURRENT_EPISODE intent");
|
||||||
|
mediaPlayer.pause(false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static MediaType getCurrentMediaType() {
|
public static MediaType getCurrentMediaType() {
|
||||||
return currentMediaType;
|
return currentMediaType;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user