Notifications about new streams
This commit is contained in:
parent
6a1d81fcf3
commit
da9bd1d420
40 changed files with 1090 additions and 27 deletions
|
|
@ -0,0 +1,112 @@
|
|||
package org.schabi.newpipe.settings
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import androidx.preference.Preference
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.subscription.NotificationMode
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
import org.schabi.newpipe.error.ErrorActivity
|
||||
import org.schabi.newpipe.error.ErrorInfo
|
||||
import org.schabi.newpipe.error.UserAction
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||
import org.schabi.newpipe.notifications.NotificationHelper
|
||||
import org.schabi.newpipe.notifications.NotificationWorker
|
||||
import org.schabi.newpipe.notifications.ScheduleOptions
|
||||
|
||||
class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferenceChangeListener {
|
||||
|
||||
private var notificationWarningSnackbar: Snackbar? = null
|
||||
private var loader: Disposable? = null
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.notifications_settings)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
defaultPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
defaultPreferences.unregisterOnSharedPreferenceChangeListener(this)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
val context = context ?: return
|
||||
if (key == getString(R.string.streams_notifications_interval_key) || key == getString(R.string.streams_notifications_network_key)) {
|
||||
NotificationWorker.schedule(context, ScheduleOptions.from(context), true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val enabled = NotificationHelper.isNotificationsEnabledNative(context)
|
||||
preferenceScreen.isEnabled = enabled
|
||||
if (!enabled) {
|
||||
if (notificationWarningSnackbar == null) {
|
||||
notificationWarningSnackbar = Snackbar.make(
|
||||
listView,
|
||||
R.string.notifications_disabled,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
).apply {
|
||||
setAction(R.string.settings) { v ->
|
||||
NotificationHelper.openNativeSettingsScreen(v.context)
|
||||
}
|
||||
setActionTextColor(Color.YELLOW)
|
||||
addCallback(object : Snackbar.Callback() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
notificationWarningSnackbar = null
|
||||
}
|
||||
})
|
||||
show()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notificationWarningSnackbar?.dismiss()
|
||||
notificationWarningSnackbar = null
|
||||
}
|
||||
loader?.dispose()
|
||||
loader = SubscriptionManager(requireContext())
|
||||
.subscriptions()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::updateSubscriptions, this::onError)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
loader?.dispose()
|
||||
loader = null
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun updateSubscriptions(subscriptions: List<SubscriptionEntity>) {
|
||||
var notified = 0
|
||||
for (subscription in subscriptions) {
|
||||
if (subscription.notificationMode != NotificationMode.DISABLED) {
|
||||
notified++
|
||||
}
|
||||
}
|
||||
val preference = findPreference<Preference>(getString(R.string.streams_notifications_channels_key))
|
||||
if (preference != null) {
|
||||
preference.summary = preference.context.getString(
|
||||
R.string.streams_notifications_channels_summary,
|
||||
notified,
|
||||
subscriptions.size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onError(e: Throwable) {
|
||||
ErrorActivity.reportErrorInSnackbar(
|
||||
this,
|
||||
ErrorInfo(e, UserAction.SUBSCRIPTION_GET, "Get subscriptions list")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package org.schabi.newpipe.settings.notifications;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.subscription.NotificationMode;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class NotificationsChannelsConfigFragment extends Fragment
|
||||
implements NotificationsConfigAdapter.ModeToggleListener {
|
||||
|
||||
private NotificationsConfigAdapter adapter;
|
||||
@Nullable
|
||||
private Disposable loader = null;
|
||||
private CompositeDisposable updaters;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
adapter = new NotificationsConfigAdapter(this);
|
||||
updaters = new CompositeDisposable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_channels_notifications, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
|
||||
recyclerView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (loader != null) {
|
||||
loader.dispose();
|
||||
}
|
||||
loader = new SubscriptionManager(requireContext())
|
||||
.subscriptions()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(adapter::update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (loader != null) {
|
||||
loader.dispose();
|
||||
}
|
||||
updaters.dispose();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModeToggle(final int position, @NotificationMode final int mode) {
|
||||
final NotificationsConfigAdapter.SubscriptionItem subscription = adapter.getItem(position);
|
||||
updaters.add(
|
||||
new SubscriptionManager(requireContext())
|
||||
.updateNotificationMode(subscription.getServiceId(),
|
||||
subscription.getUrl(), mode)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package org.schabi.newpipe.settings.notifications
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckedTextView
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.subscription.NotificationMode
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
import org.schabi.newpipe.settings.notifications.NotificationsConfigAdapter.SubscriptionHolder
|
||||
|
||||
class NotificationsConfigAdapter(
|
||||
private val listener: ModeToggleListener
|
||||
) : RecyclerView.Adapter<SubscriptionHolder>() {
|
||||
|
||||
private val differ = AsyncListDiffer(this, DiffCallback())
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): SubscriptionHolder {
|
||||
val view = LayoutInflater.from(viewGroup.context)
|
||||
.inflate(R.layout.item_notification_config, viewGroup, false)
|
||||
return SubscriptionHolder(view, listener)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(subscriptionHolder: SubscriptionHolder, i: Int) {
|
||||
subscriptionHolder.bind(differ.currentList[i])
|
||||
}
|
||||
|
||||
fun getItem(position: Int): SubscriptionItem = differ.currentList[position]
|
||||
|
||||
override fun getItemCount() = differ.currentList.size
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return differ.currentList[position].id
|
||||
}
|
||||
|
||||
fun update(newData: List<SubscriptionEntity>) {
|
||||
differ.submitList(
|
||||
newData.map {
|
||||
SubscriptionItem(
|
||||
id = it.uid,
|
||||
title = it.name,
|
||||
notificationMode = it.notificationMode,
|
||||
serviceId = it.serviceId,
|
||||
url = it.url
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
data class SubscriptionItem(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
@NotificationMode
|
||||
val notificationMode: Int,
|
||||
val serviceId: Int,
|
||||
val url: String
|
||||
)
|
||||
|
||||
class SubscriptionHolder(
|
||||
itemView: View,
|
||||
private val listener: ModeToggleListener
|
||||
) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
|
||||
|
||||
private val checkedTextView = itemView as CheckedTextView
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener(this)
|
||||
}
|
||||
|
||||
fun bind(data: SubscriptionItem) {
|
||||
checkedTextView.text = data.title
|
||||
checkedTextView.isChecked = data.notificationMode != NotificationMode.DISABLED
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val mode = if (checkedTextView.isChecked) {
|
||||
NotificationMode.DISABLED
|
||||
} else {
|
||||
NotificationMode.ENABLED_DEFAULT
|
||||
}
|
||||
listener.onModeToggle(adapterPosition, mode)
|
||||
}
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<SubscriptionItem>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: SubscriptionItem, newItem: SubscriptionItem): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: SubscriptionItem, newItem: SubscriptionItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItem: SubscriptionItem, newItem: SubscriptionItem): Any? {
|
||||
if (oldItem.notificationMode != newItem.notificationMode) {
|
||||
return newItem.notificationMode
|
||||
} else {
|
||||
return super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ModeToggleListener {
|
||||
fun onModeToggle(position: Int, @NotificationMode mode: Int)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue