1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-11-06 18:23:01 +00:00

Add notification costumization settings menu

This commit is contained in:
Stypox
2020-09-08 19:02:05 +02:00
parent 408e819d32
commit 71b32fe641
25 changed files with 774 additions and 290 deletions

View File

@@ -258,7 +258,7 @@ public class RouterActivity extends AppCompatActivity {
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(
R.layout.preferred_player_dialog_view, null, false);
R.layout.single_choice_dialog_view, null, false);
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {

View File

@@ -77,11 +77,11 @@ public final class MainPlayer extends Service {
static final String ACTION_FAST_FORWARD
= "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD";
static final String ACTION_BUFFERING
= "org.schabi.newpipe.player.BackgroundPlayer.ACTION_BUFFERING";
= "org.schabi.newpipe.player.MainPlayer.ACTION_BUFFERING";
static final String ACTION_SHUFFLE
= "org.schabi.newpipe.player.BackgroundPlayer.ACTION_SHUFFLE";
static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource";
= "org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE";
public static final String ACTION_RECREATE_NOTIFICATION
= "org.schabi.newpipe.player.MainPlayer.ACTION_RECREATE_NOTIFICATION";
/*//////////////////////////////////////////////////////////////////////////
// Service's LifeCycle

View File

@@ -0,0 +1,141 @@
package org.schabi.newpipe.player;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import org.schabi.newpipe.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
public final class NotificationConstants {
private NotificationConstants() { }
public static final int NOTHING = 0;
public static final int PREVIOUS = 1;
public static final int NEXT = 2;
public static final int REWIND = 3;
public static final int FORWARD = 4;
public static final int SMART_REWIND_PREVIOUS = 5;
public static final int SMART_FORWARD_NEXT = 6;
public static final int PLAY_PAUSE = 7;
public static final int PLAY_PAUSE_BUFFERING = 8;
public static final int REPEAT = 9;
public static final int SHUFFLE = 10;
public static final int CLOSE = 11;
@Retention(RetentionPolicy.SOURCE)
@IntDef({NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT,
PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, SHUFFLE, CLOSE})
public @interface Action { }
@StringRes
public static final int[] ACTION_SUMMARIES = {
R.string.notification_action_nothing,
R.string.notification_action_previous,
R.string.notification_action_next,
R.string.notification_action_rewind,
R.string.notification_action_forward,
R.string.notification_action_smart_rewind_previous,
R.string.notification_action_smart_forward_next,
R.string.notification_action_play_pause,
R.string.notification_action_play_pause_buffering,
R.string.notification_action_repeat,
R.string.notification_action_shuffle,
R.string.close,
};
@DrawableRes
public static final int[] ACTION_ICONS = {
0,
R.drawable.exo_icon_previous,
R.drawable.exo_icon_next,
R.drawable.exo_icon_rewind,
R.drawable.exo_icon_fastforward,
R.drawable.exo_icon_previous,
R.drawable.exo_icon_next,
R.drawable.ic_pause_white_24dp,
R.drawable.ic_hourglass_top_white_24dp,
R.drawable.exo_icon_repeat_all,
R.drawable.exo_icon_shuffle_on,
R.drawable.ic_close_white_24dp,
};
@Action
public static final int[] SLOT_DEFAULTS = {
SMART_REWIND_PREVIOUS,
PLAY_PAUSE_BUFFERING,
SMART_FORWARD_NEXT,
REPEAT,
CLOSE,
};
@Action
public static final int[][] SLOT_ALLOWED_ACTIONS = {
new int[] {PREVIOUS, REWIND, SMART_REWIND_PREVIOUS},
new int[] {REWIND, PLAY_PAUSE, PLAY_PAUSE_BUFFERING},
new int[] {NEXT, FORWARD, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING},
new int[] {NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS,
SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE},
new int[] {NOTHING, NEXT, FORWARD, SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE},
};
public static final int[] SLOT_PREF_KEYS = {
R.string.notification_slot_0_key,
R.string.notification_slot_1_key,
R.string.notification_slot_2_key,
R.string.notification_slot_3_key,
R.string.notification_slot_4_key,
};
public static final Integer[] SLOT_COMPACT_DEFAULTS = {0, 1, 2};
public static final int[] SLOT_COMPACT_PREF_KEYS = {
R.string.notification_slot_compact_0_key,
R.string.notification_slot_compact_1_key,
R.string.notification_slot_compact_2_key,
};
/**
* @param context the context to use
* @param sharedPreferences the shared preferences to query values from
* @param slotCount remove indices >= than this value (set to {@code 5} to do nothing, or make
* it lower if there are slots with empty actions)
* @return a sorted list of the indices of the slots to use as compact slots
*/
public static List<Integer> getCompactSlotsFromPreferences(
@NonNull final Context context,
final SharedPreferences sharedPreferences,
final int slotCount) {
final SortedSet<Integer> compactSlots = new TreeSet<>();
for (int i = 0; i < 3; i++) {
final int compactSlot = sharedPreferences.getInt(
context.getString(SLOT_COMPACT_PREF_KEYS[i]), Integer.MAX_VALUE);
if (compactSlot == Integer.MAX_VALUE) {
// settings not yet populated, return default values
return new ArrayList<>(Arrays.asList(SLOT_COMPACT_DEFAULTS));
}
// a negative value (-1) is set when the user does not want a particular compact slot
if (compactSlot >= 0 && compactSlot < slotCount) {
compactSlots.add(compactSlot);
}
}
return new ArrayList<>(compactSlots);
}
}

View File

@@ -20,6 +20,8 @@ import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.NavigationHelper;
import java.util.List;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.content.Context.NOTIFICATION_SERVICE;
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL;
@@ -46,11 +48,8 @@ public final class NotificationUtil {
@Nullable private static NotificationUtil instance = null;
private String notificationSlot0 = "smart_rewind_prev";
private String notificationSlot1 = "play_pause_buffering";
private String notificationSlot2 = "smart_forward_next";
private String notificationSlot3 = "repeat";
private String notificationSlot4 = "close";
@NotificationConstants.Action
private int[] notificationSlots = NotificationConstants.SLOT_DEFAULTS.clone();
private NotificationManager notificationManager;
private NotificationCompat.Builder notificationBuilder;
@@ -91,35 +90,28 @@ public final class NotificationUtil {
final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context,
player.context.getString(R.string.notification_channel_id));
final String compactView = player.sharedPreferences.getString(player.context.getString(
R.string.settings_notifications_compact_view_key), "0,1,2");
int compactSlot0 = 0;
int compactSlot1 = 1;
int compactSlot2 = 2;
try {
if (compactView != null) {
final String[] parts = compactView.split(",");
compactSlot0 = Integer.parseInt(parts[0]);
compactSlot1 = Integer.parseInt(parts[1]);
compactSlot2 = Integer.parseInt(parts[2]);
if (compactSlot0 > 4) {
compactSlot0 = 0;
}
if (compactSlot1 > 4) {
compactSlot1 = 1;
}
if (compactSlot2 > 4) {
compactSlot2 = 2;
}
}
} catch (final Exception e) {
e.printStackTrace();
initializeNotificationSlots(player);
// count the number of real slots, to make sure compact slots indices are not out of bound
int nonNothingSlotCount = 5;
if (notificationSlots[3] == NotificationConstants.NOTHING) {
--nonNothingSlotCount;
}
if (notificationSlots[4] == NotificationConstants.NOTHING) {
--nonNothingSlotCount;
}
// build the compact slot indices array (need code to convert from Integer... because Java)
final List<Integer> compactSlotList = NotificationConstants.getCompactSlotsFromPreferences(
player.context, player.sharedPreferences, nonNothingSlotCount);
final int[] compactSlots = new int[compactSlotList.size()];
for (int i = 0; i < compactSlotList.size(); i++) {
compactSlots[i] = compactSlotList.get(i);
}
builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(player.mediaSessionManager.getSessionToken())
.setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2))
.setShowActionsInCompactView(compactSlots))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
@@ -131,7 +123,6 @@ public final class NotificationUtil {
.setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID,
new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT));
initializeNotificationSlots(player);
updateActions(builder, player);
setLargeIcon(builder, player);
@@ -171,22 +162,20 @@ public final class NotificationUtil {
boolean hasSlotWithBuffering() {
return notificationSlot0.equals("play_pause_buffering")
|| notificationSlot1.equals("play_pause_buffering")
|| notificationSlot2.equals("play_pause_buffering")
|| notificationSlot3.equals("play_pause_buffering")
|| notificationSlot4.equals("play_pause_buffering");
return notificationSlots[1] == NotificationConstants.PLAY_PAUSE_BUFFERING
|| notificationSlots[2] == NotificationConstants.PLAY_PAUSE_BUFFERING;
}
public void cancelNotification() {
void cancelNotification() {
try {
if (notificationManager != null) {
notificationManager.cancel(NOTIFICATION_ID);
notificationManager = null;
}
} catch (final Exception e) {
Log.e(TAG, "Could not cancel notification", e);
}
notificationManager = null;
notificationBuilder = null;
}
@@ -195,32 +184,25 @@ public final class NotificationUtil {
/////////////////////////////////////////////////////
private void initializeNotificationSlots(final VideoPlayerImpl player) {
notificationSlot0 = player.sharedPreferences.getString(
player.context.getString(R.string.notification_action_0_key), notificationSlot0);
notificationSlot1 = player.sharedPreferences.getString(
player.context.getString(R.string.notification_action_1_key), notificationSlot1);
notificationSlot2 = player.sharedPreferences.getString(
player.context.getString(R.string.notification_action_2_key), notificationSlot2);
notificationSlot3 = player.sharedPreferences.getString(
player.context.getString(R.string.notification_action_3_key), notificationSlot3);
notificationSlot4 = player.sharedPreferences.getString(
player.context.getString(R.string.notification_action_4_key), notificationSlot4);
for (int i = 0; i < 5; ++i) {
notificationSlots[i] = player.sharedPreferences.getInt(
player.context.getString(NotificationConstants.SLOT_PREF_KEYS[i]),
NotificationConstants.SLOT_DEFAULTS[i]);
}
}
@SuppressLint("RestrictedApi")
private void updateActions(final NotificationCompat.Builder builder,
final VideoPlayerImpl player) {
builder.mActions.clear();
addAction(builder, player, notificationSlot0);
addAction(builder, player, notificationSlot1);
addAction(builder, player, notificationSlot2);
addAction(builder, player, notificationSlot3);
addAction(builder, player, notificationSlot4);
for (int i = 0; i < 5; ++i) {
addAction(builder, player, notificationSlots[i]);
}
}
private void addAction(final NotificationCompat.Builder builder,
final VideoPlayerImpl player,
final String slot) {
@NotificationConstants.Action final int slot) {
final NotificationCompat.Action action = getAction(player, slot);
if (action != null) {
builder.addAction(action);
@@ -228,23 +210,42 @@ public final class NotificationUtil {
}
@Nullable
private NotificationCompat.Action getAction(final VideoPlayerImpl player,
final String slot) {
switch (slot) {
case "play_pause_buffering":
if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT
|| player.getCurrentState() == BasePlayer.STATE_BLOCKED
|| player.getCurrentState() == BasePlayer.STATE_BUFFERING) {
return getAction(player, R.drawable.ic_hourglass_top_white_24dp,
"Buffering", ACTION_BUFFERING);
private NotificationCompat.Action getAction(
final VideoPlayerImpl player,
@NotificationConstants.Action final int selectedAction) {
final int baseActionIcon = NotificationConstants.ACTION_ICONS[selectedAction];
switch (selectedAction) {
case NotificationConstants.PREVIOUS:
return getAction(player, baseActionIcon, "Previous", ACTION_PLAY_PREVIOUS);
case NotificationConstants.NEXT:
return getAction(player, baseActionIcon, "Next", ACTION_PLAY_NEXT);
case NotificationConstants.REWIND:
return getAction(player, baseActionIcon, "Rewind", ACTION_FAST_REWIND);
case NotificationConstants.FORWARD:
return getAction(player, baseActionIcon, "Forward", ACTION_FAST_FORWARD);
case NotificationConstants.SMART_REWIND_PREVIOUS:
if (player.playQueue != null && player.playQueue.size() > 1) {
return getAction(player, R.drawable.exo_notification_previous,
"Previous", ACTION_PLAY_PREVIOUS);
} else {
return getAction(player,
player.isPlaying() ? R.drawable.exo_notification_pause
: R.drawable.exo_notification_play,
player.isPlaying() ? "Pause" : "Play",
ACTION_PLAY_PAUSE);
return getAction(player, R.drawable.exo_controls_rewind,
"Rewind", ACTION_FAST_REWIND);
}
case "play_pause":
case NotificationConstants.SMART_FORWARD_NEXT:
if (player.playQueue != null && player.playQueue.size() > 1) {
return getAction(player, R.drawable.exo_notification_next,
"Next", ACTION_PLAY_NEXT);
} else {
return getAction(player, R.drawable.exo_controls_fastforward,
"Forward", ACTION_FAST_FORWARD);
}
case NotificationConstants.PLAY_PAUSE:
final boolean pauseOrPlay = player.isPlaying()
|| player.getCurrentState() == BasePlayer.STATE_PREFLIGHT
|| player.getCurrentState() == BasePlayer.STATE_BLOCKED
@@ -254,48 +255,38 @@ public final class NotificationUtil {
: R.drawable.exo_notification_play,
pauseOrPlay ? "Pause" : "Play",
ACTION_PLAY_PAUSE);
case "rewind":
return getAction(player, R.drawable.exo_controls_rewind,
"Rewind", ACTION_FAST_REWIND);
case "smart_rewind_prev":
if (player.playQueue != null && player.playQueue.size() > 1) {
return getAction(player, R.drawable.exo_notification_previous,
"Prev", ACTION_PLAY_PREVIOUS);
case NotificationConstants.PLAY_PAUSE_BUFFERING:
if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT
|| player.getCurrentState() == BasePlayer.STATE_BLOCKED
|| player.getCurrentState() == BasePlayer.STATE_BUFFERING) {
return getAction(player, R.drawable.ic_hourglass_top_white_24dp_png,
"Buffering", ACTION_BUFFERING);
} else {
return getAction(player, R.drawable.exo_controls_rewind,
"Rewind", ACTION_FAST_REWIND);
return getAction(player,
player.isPlaying() ? R.drawable.exo_notification_pause
: R.drawable.exo_notification_play,
player.isPlaying() ? "Pause" : "Play",
ACTION_PLAY_PAUSE);
}
case "forward":
return getAction(player, R.drawable.exo_controls_fastforward,
"Forward", ACTION_FAST_FORWARD);
case "smart_forward_next":
if (player.playQueue != null && player.playQueue.size() > 1) {
return getAction(player, R.drawable.exo_notification_next,
"Next", ACTION_PLAY_NEXT);
} else {
return getAction(player, R.drawable.exo_controls_fastforward,
"Forward", ACTION_FAST_FORWARD);
}
case "next":
return getAction(player, R.drawable.exo_notification_next,
"Next", ACTION_PLAY_NEXT);
case "prev":
return getAction(player, R.drawable.exo_notification_previous,
"Prev", ACTION_PLAY_PREVIOUS);
case "repeat":
case NotificationConstants.REPEAT:
return getAction(player, getRepeatModeDrawable(player.getRepeatMode()),
getRepeatModeTitle(player.getRepeatMode()), ACTION_REPEAT);
case "shuffle":
case NotificationConstants.SHUFFLE:
final boolean shuffled = player.playQueue != null && player.playQueue.isShuffled();
return getAction(player,
shuffled ? R.drawable.exo_controls_shuffle_on
: R.drawable.exo_controls_shuffle_off,
shuffled ? "ShuffleOn" : "ShuffleOff",
ACTION_SHUFFLE);
case "close":
return getAction(player, R.drawable.ic_close_white_24dp,
case NotificationConstants.CLOSE:
return getAction(player, R.drawable.ic_close_white_24dp_png,
"Close", ACTION_CLOSE);
case "n/a":
case NotificationConstants.NOTHING:
default:
// do nothing
return null;

View File

@@ -112,6 +112,7 @@ import static org.schabi.newpipe.player.MainPlayer.ACTION_OPEN_CONTROLS;
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT;
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PAUSE;
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS;
import static org.schabi.newpipe.player.MainPlayer.ACTION_RECREATE_NOTIFICATION;
import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
@@ -1179,6 +1180,7 @@ public class VideoPlayerImpl extends VideoPlayer
intentFilter.addAction(ACTION_FAST_FORWARD);
intentFilter.addAction(ACTION_BUFFERING);
intentFilter.addAction(ACTION_SHUFFLE);
intentFilter.addAction(ACTION_RECREATE_NOTIFICATION);
intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED);
intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED);
@@ -1236,6 +1238,9 @@ public class VideoPlayerImpl extends VideoPlayer
case ACTION_SHUFFLE:
onShuffleClicked();
break;
case ACTION_RECREATE_NOTIFICATION:
resetNotification(true);
break;
case Intent.ACTION_HEADSET_PLUG: //FIXME
/*notificationManager.cancel(NOTIFICATION_ID);
mediaSessionManager.dispose();

View File

@@ -0,0 +1,271 @@
package org.schabi.newpipe.settings;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.fragment.app.Fragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.player.MainPlayer;
import org.schabi.newpipe.player.NotificationConstants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;
import java.util.List;
public class NotificationSettingsFragment extends Fragment {
private Switch scaleSwitch;
private NotificationSlot[] notificationSlots;
private SharedPreferences pref;
private List<Integer> compactSlots;
private String scaleKey;
////////////////////////////////////////////////////////////////////////////
// Lifecycle
////////////////////////////////////////////////////////////////////////////
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pref = PreferenceManager.getDefaultSharedPreferences(requireContext());
scaleKey = getString(R.string.scale_to_square_image_in_notifications_key);
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
return inflater.inflate(R.layout.settings_notification, container, false);
}
@Override
public void onViewCreated(@NonNull final View rootView,
@Nullable final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
setupScaleSwitch(rootView);
setupActions(rootView);
}
@Override
public void onResume() {
super.onResume();
ThemeHelper.setTitleToAppCompatActivity(getActivity(),
getString(R.string.settings_category_notification_title));
}
@Override
public void onPause() {
super.onPause();
saveChanges();
requireContext().sendBroadcast(new Intent(MainPlayer.ACTION_RECREATE_NOTIFICATION));
}
////////////////////////////////////////////////////////////////////////////
// Setup
////////////////////////////////////////////////////////////////////////////
private void setupScaleSwitch(@NonNull final View view) {
scaleSwitch = view.findViewById(R.id.notificationScaleSwitch);
scaleSwitch.setChecked(pref.getBoolean(scaleKey, false));
view.findViewById(R.id.notificationScaleSwitchClickableArea)
.setOnClickListener(v -> scaleSwitch.toggle());
}
private void setupActions(@NonNull final View view) {
compactSlots =
NotificationConstants.getCompactSlotsFromPreferences(requireContext(), pref, 5);
notificationSlots = new NotificationSlot[5];
for (int i = 0; i < 5; i++) {
notificationSlots[i] = new NotificationSlot(i, view);
}
}
////////////////////////////////////////////////////////////////////////////
// Saving
////////////////////////////////////////////////////////////////////////////
private void saveChanges() {
final SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(scaleKey, scaleSwitch.isChecked());
for (int i = 0; i < 3; i++) {
editor.putInt(getString(NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]),
(i < compactSlots.size() ? compactSlots.get(i) : -1));
}
for (int i = 0; i < 5; i++) {
editor.putInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]),
notificationSlots[i].selectedAction);
}
editor.apply();
}
////////////////////////////////////////////////////////////////////////////
// Notification action
////////////////////////////////////////////////////////////////////////////
private static final int[] SLOT_ITEMS = {
R.id.notificationAction0,
R.id.notificationAction1,
R.id.notificationAction2,
R.id.notificationAction3,
R.id.notificationAction4,
};
private static final int[] SLOT_TITLES = {
R.string.notification_action_0_title,
R.string.notification_action_1_title,
R.string.notification_action_2_title,
R.string.notification_action_3_title,
R.string.notification_action_4_title,
};
private class NotificationSlot {
final int i;
@NotificationConstants.Action int selectedAction;
ImageView icon;
TextView summary;
NotificationSlot(final int actionIndex, final View parentView) {
this.i = actionIndex;
final View view = parentView.findViewById(SLOT_ITEMS[i]);
setupSelectedAction(view);
setupTitle(view);
setupCheckbox(view);
}
void setupTitle(final View view) {
((TextView) view.findViewById(R.id.notificationActionTitle))
.setText(SLOT_TITLES[i]);
view.findViewById(R.id.notificationActionClickableArea).setOnClickListener(
v -> openActionChooserDialog());
}
void setupCheckbox(final View view) {
final CheckBox compactSlotCheckBox = view.findViewById(R.id.notificationActionCheckBox);
compactSlotCheckBox.setChecked(compactSlots.contains(i));
view.findViewById(R.id.notificationActionCheckBoxClickableArea).setOnClickListener(
v -> {
if (compactSlotCheckBox.isChecked()) {
compactSlots.remove((Integer) i);
} else if (compactSlots.size() < 3) {
compactSlots.add(i);
} else {
Toast.makeText(requireContext(),
R.string.notification_actions_at_most_three,
Toast.LENGTH_SHORT).show();
return;
}
compactSlotCheckBox.toggle();
});
}
void setupSelectedAction(final View view) {
icon = view.findViewById(R.id.notificationActionIcon);
summary = view.findViewById(R.id.notificationActionSummary);
selectedAction = pref.getInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]),
NotificationConstants.SLOT_DEFAULTS[i]);
updateInfo();
}
void updateInfo() {
if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) {
icon.setImageDrawable(null);
} else {
icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(),
NotificationConstants.ACTION_ICONS[selectedAction]));
}
summary.setText(NotificationConstants.ACTION_SUMMARIES[selectedAction]);
}
void openActionChooserDialog() {
final LayoutInflater inflater = LayoutInflater.from(requireContext());
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(
R.layout.single_choice_dialog_view, null, false);
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
final AlertDialog alertDialog = new AlertDialog.Builder(requireContext())
.setTitle(SLOT_TITLES[i])
.setView(radioGroup)
.setCancelable(true)
.create();
final View.OnClickListener radioButtonsClickListener = v -> {
final int id = ((RadioButton) v).getId();
selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][id];
updateInfo();
alertDialog.dismiss();
};
for (int id = 0; id < NotificationConstants.SLOT_ALLOWED_ACTIONS[i].length; ++id) {
final int action = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][id];
final RadioButton radioButton
= (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
// if present set action icon with correct color
if (NotificationConstants.ACTION_ICONS[action] != 0) {
final Drawable drawable = AppCompatResources.getDrawable(requireContext(),
NotificationConstants.ACTION_ICONS[action]);
if (drawable != null) {
final int color = ThemeHelper.resolveColorFromAttr(requireContext(),
android.R.attr.textColorPrimary);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
drawable.setTint(color);
} else {
drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
radioButton.setCompoundDrawablesWithIntrinsicBounds(
null, null, drawable, null);
}
}
radioButton.setText(NotificationConstants.ACTION_SUMMARIES[action]);
radioButton.setChecked(action == selectedAction);
radioButton.setId(id);
radioButton.setLayoutParams(new RadioGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
radioButton.setOnClickListener(radioButtonsClickListener);
radioGroup.addView(radioButton);
}
alertDialog.show();
if (DeviceUtils.isTv(requireContext())) {
FocusOverlayView.setupFocusObserver(alertDialog);
}
}
}
}