Notifications about new streams

This commit is contained in:
Vasiliy 2019-05-08 20:17:54 +03:00 committed by Koitharu
parent 6a1d81fcf3
commit da9bd1d420
No known key found for this signature in database
GPG key ID: 8E861F8CE6E7CE27
40 changed files with 1090 additions and 27 deletions

View file

@ -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")
)
}
}

View file

@ -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()
);
}
}

View file

@ -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)
}
}