Extract NotificationSlot from NotificationActionsPreference
This commit is contained in:
parent
30f0db1d28
commit
aab6580195
|
@ -5,38 +5,22 @@ import static org.schabi.newpipe.player.notification.NotificationConstants.ACTIO
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.ColorStateList;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.RadioButton;
|
|
||||||
import android.widget.RadioGroup;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
|
||||||
import androidx.core.widget.TextViewCompat;
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceViewHolder;
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
import org.schabi.newpipe.App;
|
import org.schabi.newpipe.App;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
|
||||||
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
|
||||||
import org.schabi.newpipe.player.notification.NotificationConstants;
|
import org.schabi.newpipe.player.notification.NotificationConstants;
|
||||||
import org.schabi.newpipe.util.DeviceUtils;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
|
||||||
import org.schabi.newpipe.views.FocusOverlayView;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
public class NotificationActionsPreference extends Preference {
|
public class NotificationActionsPreference extends Preference {
|
||||||
|
@ -47,8 +31,9 @@ public class NotificationActionsPreference extends Preference {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Nullable private NotificationSlot[] notificationSlots = null;
|
private NotificationSlot[] notificationSlots;
|
||||||
@Nullable private List<Integer> compactSlots = null;
|
private List<Integer> compactSlots;
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -85,10 +70,26 @@ public class NotificationActionsPreference extends Preference {
|
||||||
compactSlots = NotificationConstants.getCompactSlotsFromPreferences(getContext(),
|
compactSlots = NotificationConstants.getCompactSlotsFromPreferences(getContext(),
|
||||||
getSharedPreferences(), 5);
|
getSharedPreferences(), 5);
|
||||||
notificationSlots = IntStream.range(0, 5)
|
notificationSlots = IntStream.range(0, 5)
|
||||||
.mapToObj(i -> new NotificationSlot(i, view))
|
.mapToObj(i -> new NotificationSlot(getContext(), getSharedPreferences(), i, view,
|
||||||
|
compactSlots.contains(i), this::onToggleCompactSlot))
|
||||||
.toArray(NotificationSlot[]::new);
|
.toArray(NotificationSlot[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onToggleCompactSlot(final int i, final CheckBox checkBox) {
|
||||||
|
if (checkBox.isChecked()) {
|
||||||
|
compactSlots.remove((Integer) i);
|
||||||
|
} else if (compactSlots.size() < 3) {
|
||||||
|
compactSlots.add(i);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
R.string.notification_actions_at_most_three,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkBox.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Saving
|
// Saving
|
||||||
|
@ -106,156 +107,10 @@ public class NotificationActionsPreference extends Preference {
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
editor.putInt(getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]),
|
editor.putInt(getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]),
|
||||||
notificationSlots[i].selectedAction);
|
notificationSlots[i].getSelectedAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.apply();
|
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;
|
|
||||||
selectedAction = Objects.requireNonNull(getSharedPreferences()).getInt(
|
|
||||||
getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]),
|
|
||||||
NotificationConstants.SLOT_DEFAULTS[i]);
|
|
||||||
final View view = parentView.findViewById(SLOT_ITEMS[i]);
|
|
||||||
|
|
||||||
// only show the last two notification slots on Android 13+
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || i >= 3) {
|
|
||||||
setupSelectedAction(view);
|
|
||||||
setupTitle(view);
|
|
||||||
setupCheckbox(view);
|
|
||||||
} else {
|
|
||||||
view.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
// there are no compact slots to customize on Android 33+
|
|
||||||
compactSlotCheckBox.setVisibility(View.GONE);
|
|
||||||
view.findViewById(R.id.notificationActionCheckBoxClickableArea)
|
|
||||||
.setVisibility(View.GONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(getContext(),
|
|
||||||
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);
|
|
||||||
updateInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateInfo() {
|
|
||||||
if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) {
|
|
||||||
icon.setImageDrawable(null);
|
|
||||||
} else {
|
|
||||||
icon.setImageDrawable(AppCompatResources.getDrawable(getContext(),
|
|
||||||
NotificationConstants.ACTION_ICONS[selectedAction]));
|
|
||||||
}
|
|
||||||
|
|
||||||
summary.setText(NotificationConstants.getActionName(getContext(), selectedAction));
|
|
||||||
}
|
|
||||||
|
|
||||||
void openActionChooserDialog() {
|
|
||||||
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
|
||||||
final SingleChoiceDialogViewBinding binding =
|
|
||||||
SingleChoiceDialogViewBinding.inflate(inflater);
|
|
||||||
|
|
||||||
final AlertDialog alertDialog = new AlertDialog.Builder(getContext())
|
|
||||||
.setTitle(SLOT_TITLES[i])
|
|
||||||
.setView(binding.getRoot())
|
|
||||||
.setCancelable(true)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
final View.OnClickListener radioButtonsClickListener = v -> {
|
|
||||||
selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][v.getId()];
|
|
||||||
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 = ListRadioIconItemBinding.inflate(inflater)
|
|
||||||
.getRoot();
|
|
||||||
|
|
||||||
// if present set action icon with correct color
|
|
||||||
final int iconId = NotificationConstants.ACTION_ICONS[action];
|
|
||||||
if (iconId != 0) {
|
|
||||||
radioButton.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconId, 0);
|
|
||||||
|
|
||||||
final var color = ColorStateList.valueOf(ThemeHelper
|
|
||||||
.resolveColorFromAttr(getContext(), android.R.attr.textColorPrimary));
|
|
||||||
TextViewCompat.setCompoundDrawableTintList(radioButton, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
radioButton.setText(NotificationConstants.getActionName(getContext(), action));
|
|
||||||
radioButton.setChecked(action == selectedAction);
|
|
||||||
radioButton.setId(id);
|
|
||||||
radioButton.setLayoutParams(new RadioGroup.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
|
||||||
radioButton.setOnClickListener(radioButtonsClickListener);
|
|
||||||
binding.list.addView(radioButton);
|
|
||||||
}
|
|
||||||
alertDialog.show();
|
|
||||||
|
|
||||||
if (DeviceUtils.isTv(getContext())) {
|
|
||||||
FocusOverlayView.setupFocusObserver(alertDialog);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
package org.schabi.newpipe.settings.custom;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
|
import androidx.core.widget.TextViewCompat;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
||||||
|
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
||||||
|
import org.schabi.newpipe.player.notification.NotificationConstants;
|
||||||
|
import org.schabi.newpipe.util.DeviceUtils;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
import org.schabi.newpipe.views.FocusOverlayView;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
class NotificationSlot {
|
||||||
|
|
||||||
|
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 final int i;
|
||||||
|
private @NotificationConstants.Action int selectedAction;
|
||||||
|
private final Context context;
|
||||||
|
private final BiConsumer<Integer, CheckBox> onToggleCompactSlot;
|
||||||
|
|
||||||
|
private ImageView icon;
|
||||||
|
private TextView summary;
|
||||||
|
|
||||||
|
NotificationSlot(final Context context,
|
||||||
|
final SharedPreferences prefs,
|
||||||
|
final int actionIndex,
|
||||||
|
final View parentView,
|
||||||
|
final boolean isCompactSlotChecked,
|
||||||
|
final BiConsumer<Integer, CheckBox> onToggleCompactSlot) {
|
||||||
|
this.context = context;
|
||||||
|
this.i = actionIndex;
|
||||||
|
this.onToggleCompactSlot = onToggleCompactSlot;
|
||||||
|
|
||||||
|
selectedAction = Objects.requireNonNull(prefs).getInt(
|
||||||
|
context.getString(NotificationConstants.SLOT_PREF_KEYS[i]),
|
||||||
|
NotificationConstants.SLOT_DEFAULTS[i]);
|
||||||
|
final View view = parentView.findViewById(SLOT_ITEMS[i]);
|
||||||
|
|
||||||
|
// only show the last two notification slots on Android 13+
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || i >= 3) {
|
||||||
|
setupSelectedAction(view);
|
||||||
|
setupTitle(view);
|
||||||
|
setupCheckbox(view, isCompactSlotChecked);
|
||||||
|
} else {
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 boolean isCompactSlotChecked) {
|
||||||
|
final CheckBox compactSlotCheckBox = view.findViewById(R.id.notificationActionCheckBox);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
// there are no compact slots to customize on Android 33+
|
||||||
|
compactSlotCheckBox.setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.notificationActionCheckBoxClickableArea)
|
||||||
|
.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
compactSlotCheckBox.setChecked(isCompactSlotChecked);
|
||||||
|
view.findViewById(R.id.notificationActionCheckBoxClickableArea).setOnClickListener(
|
||||||
|
v -> onToggleCompactSlot.accept(i, compactSlotCheckBox));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupSelectedAction(final View view) {
|
||||||
|
icon = view.findViewById(R.id.notificationActionIcon);
|
||||||
|
summary = view.findViewById(R.id.notificationActionSummary);
|
||||||
|
updateInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateInfo() {
|
||||||
|
if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) {
|
||||||
|
icon.setImageDrawable(null);
|
||||||
|
} else {
|
||||||
|
icon.setImageDrawable(AppCompatResources.getDrawable(context,
|
||||||
|
NotificationConstants.ACTION_ICONS[selectedAction]));
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.setText(NotificationConstants.getActionName(context, selectedAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
void openActionChooserDialog() {
|
||||||
|
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
|
final SingleChoiceDialogViewBinding binding =
|
||||||
|
SingleChoiceDialogViewBinding.inflate(inflater);
|
||||||
|
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||||
|
.setTitle(SLOT_TITLES[i])
|
||||||
|
.setView(binding.getRoot())
|
||||||
|
.setCancelable(true)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
final View.OnClickListener radioButtonsClickListener = v -> {
|
||||||
|
selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][v.getId()];
|
||||||
|
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 = ListRadioIconItemBinding.inflate(inflater)
|
||||||
|
.getRoot();
|
||||||
|
|
||||||
|
// if present set action icon with correct color
|
||||||
|
final int iconId = NotificationConstants.ACTION_ICONS[action];
|
||||||
|
if (iconId != 0) {
|
||||||
|
radioButton.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconId, 0);
|
||||||
|
|
||||||
|
final var color = ColorStateList.valueOf(ThemeHelper
|
||||||
|
.resolveColorFromAttr(context, android.R.attr.textColorPrimary));
|
||||||
|
TextViewCompat.setCompoundDrawableTintList(radioButton, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
radioButton.setText(NotificationConstants.getActionName(context, action));
|
||||||
|
radioButton.setChecked(action == selectedAction);
|
||||||
|
radioButton.setId(id);
|
||||||
|
radioButton.setLayoutParams(new RadioGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
radioButton.setOnClickListener(radioButtonsClickListener);
|
||||||
|
binding.list.addView(radioButton);
|
||||||
|
}
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
|
if (DeviceUtils.isTv(context)) {
|
||||||
|
FocusOverlayView.setupFocusObserver(alertDialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotificationConstants.Action
|
||||||
|
public int getSelectedAction() {
|
||||||
|
return selectedAction;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue