Loading app/src/main/java/at/bitfire/davdroid/App.kt +3 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,9 @@ class App: Application(), Thread.UncaughtExceptionHandler { // check whether a tasks app is currently installed TasksWatcher.updateTaskSync(this) // watch storage because low storage means synchronization is stopped StorageLowReceiver.getInstance(this) // check/repair sync intervals AccountSettings.repairSyncIntervals(this) Loading app/src/main/java/at/bitfire/davdroid/StorageLowReceiver.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /*************************************************************************************************** * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. **************************************************************************************************/ package at.bitfire.davdroid import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.provider.Settings import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.MutableLiveData import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.NotificationUtils class StorageLowReceiver private constructor( val context: Context ): BroadcastReceiver(), AutoCloseable { companion object { fun getInstance(context: Context) = Singleton.getInstance(context) { StorageLowReceiver(context) } } val storageLow = MutableLiveData<Boolean>(false) init { Logger.log.fine("Listening for device storage low/OK broadcasts") val filter = IntentFilter().apply { addAction(Intent.ACTION_DEVICE_STORAGE_LOW) addAction(Intent.ACTION_DEVICE_STORAGE_OK) } context.registerReceiver(this, filter) } override fun close() { context.unregisterReceiver(this) } override fun onReceive(context: Context, intent: Intent) { when (intent.action) { Intent.ACTION_DEVICE_STORAGE_LOW -> onStorageLow() Intent.ACTION_DEVICE_STORAGE_OK -> onStorageOk() } } fun onStorageLow() { Logger.log.warning("Low storage, sync will not be started by Android!") storageLow.postValue(true) val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS) .setSmallIcon(R.drawable.ic_storage_notify) .setCategory(NotificationCompat.CATEGORY_ERROR) .setContentTitle(context.getString(R.string.storage_low_notify_title)) .setContentText(context.getString(R.string.storage_low_notify_text)) val settingsIntent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) if (settingsIntent.resolveActivity(context.packageManager) != null) notify.setContentIntent(PendingIntent.getActivity(context, 0, settingsIntent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE)) val nm = NotificationManagerCompat.from(context) nm.notify(NotificationUtils.NOTIFY_LOW_STORAGE, notify.build()) } fun onStorageOk() { Logger.log.info("Storage OK again") storageLow.postValue(false) val nm = NotificationManagerCompat.from(context) nm.cancel(NotificationUtils.NOTIFY_LOW_STORAGE) } } No newline at end of file app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt +18 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.Build import android.os.Bundle import android.provider.Settings import android.view.* import androidx.core.content.getSystemService import androidx.fragment.app.Fragment Loading @@ -29,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.DavUtils.SyncStatus import at.bitfire.davdroid.R import at.bitfire.davdroid.StorageLowReceiver import at.bitfire.davdroid.databinding.AccountListBinding import at.bitfire.davdroid.databinding.AccountListItemBinding import at.bitfire.davdroid.ui.account.AccountActivity Loading @@ -50,9 +52,23 @@ class AccountListFragment: Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) model.networkAvailable.observe(viewLifecycleOwner, { networkAvailable -> model.networkAvailable.observe(viewLifecycleOwner) { networkAvailable -> binding.noNetworkInfo.visibility = if (networkAvailable) View.GONE else View.VISIBLE }) } binding.manageConnections.setOnClickListener { val intent = Intent(Settings.ACTION_WIRELESS_SETTINGS) if (intent.resolveActivity(requireActivity().packageManager) != null) startActivity(intent) } StorageLowReceiver.getInstance(requireActivity()).storageLow.observe(viewLifecycleOwner) { storageLow -> binding.lowStorageInfo.visibility = if (storageLow) View.VISIBLE else View.GONE } binding.manageStorage.setOnClickListener { val intent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) if (intent.resolveActivity(requireActivity().packageManager) != null) startActivity(intent) } val accountAdapter = AccountAdapter(requireActivity()) binding.list.apply { Loading app/src/main/java/at/bitfire/davdroid/ui/NotificationUtils.kt +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ object NotificationUtils { const val NOTIFY_SYNC_ERROR = 10 const val NOTIFY_INVALID_RESOURCE = 11 const val NOTIFY_WEBDAV_ACCESS = 12 const val NOTIFY_LOW_STORAGE = 13 const val NOTIFY_OPENTASKS = 20 const val NOTIFY_PERMISSIONS = 21 Loading app/src/main/res/layout/account_list.xml +59 −10 Original line number Diff line number Diff line Loading @@ -5,20 +5,69 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatTextView <androidx.cardview.widget.CardView android:id="@+id/no_network_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="12dp" android:paddingTop="12dp" android:layout_margin="8dp" app:contentPadding="@dimen/card_padding"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:visibility="gone" app:drawableLeftCompat="@drawable/ic_signal_cellular_off" app:drawableTint="?android:attr/textColorPrimary" android:drawablePadding="8dp" style="@style/TextAppearance.MaterialComponents.Body1" android:text="@string/account_list_no_internet"/> <Button android:id="@+id/manage_connections" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Widget.MaterialComponents.Button.TextButton" android:text="Manage connections" /> </LinearLayout> </androidx.cardview.widget.CardView> <androidx.cardview.widget.CardView android:id="@+id/low_storage_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" app:contentPadding="@dimen/card_padding"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:visibility="visible" app:drawableLeftCompat="@drawable/ic_storage" app:drawableTint="?android:attr/textColorPrimary" android:drawablePadding="8dp" style="@style/TextAppearance.MaterialComponents.Body1" android:text="@string/account_list_low_storage"/> <Button android:id="@+id/manage_storage" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Widget.MaterialComponents.Button.TextButton" android:text="Manage storage" /> </LinearLayout> </androidx.cardview.widget.CardView> <androidx.recyclerview.widget.RecyclerView android:id="@android:id/list" android:layout_width="match_parent" Loading Loading
app/src/main/java/at/bitfire/davdroid/App.kt +3 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,9 @@ class App: Application(), Thread.UncaughtExceptionHandler { // check whether a tasks app is currently installed TasksWatcher.updateTaskSync(this) // watch storage because low storage means synchronization is stopped StorageLowReceiver.getInstance(this) // check/repair sync intervals AccountSettings.repairSyncIntervals(this) Loading
app/src/main/java/at/bitfire/davdroid/StorageLowReceiver.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /*************************************************************************************************** * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. **************************************************************************************************/ package at.bitfire.davdroid import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.provider.Settings import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.MutableLiveData import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.NotificationUtils class StorageLowReceiver private constructor( val context: Context ): BroadcastReceiver(), AutoCloseable { companion object { fun getInstance(context: Context) = Singleton.getInstance(context) { StorageLowReceiver(context) } } val storageLow = MutableLiveData<Boolean>(false) init { Logger.log.fine("Listening for device storage low/OK broadcasts") val filter = IntentFilter().apply { addAction(Intent.ACTION_DEVICE_STORAGE_LOW) addAction(Intent.ACTION_DEVICE_STORAGE_OK) } context.registerReceiver(this, filter) } override fun close() { context.unregisterReceiver(this) } override fun onReceive(context: Context, intent: Intent) { when (intent.action) { Intent.ACTION_DEVICE_STORAGE_LOW -> onStorageLow() Intent.ACTION_DEVICE_STORAGE_OK -> onStorageOk() } } fun onStorageLow() { Logger.log.warning("Low storage, sync will not be started by Android!") storageLow.postValue(true) val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS) .setSmallIcon(R.drawable.ic_storage_notify) .setCategory(NotificationCompat.CATEGORY_ERROR) .setContentTitle(context.getString(R.string.storage_low_notify_title)) .setContentText(context.getString(R.string.storage_low_notify_text)) val settingsIntent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) if (settingsIntent.resolveActivity(context.packageManager) != null) notify.setContentIntent(PendingIntent.getActivity(context, 0, settingsIntent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE)) val nm = NotificationManagerCompat.from(context) nm.notify(NotificationUtils.NOTIFY_LOW_STORAGE, notify.build()) } fun onStorageOk() { Logger.log.info("Storage OK again") storageLow.postValue(false) val nm = NotificationManagerCompat.from(context) nm.cancel(NotificationUtils.NOTIFY_LOW_STORAGE) } } No newline at end of file
app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt +18 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.Build import android.os.Bundle import android.provider.Settings import android.view.* import androidx.core.content.getSystemService import androidx.fragment.app.Fragment Loading @@ -29,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.DavUtils.SyncStatus import at.bitfire.davdroid.R import at.bitfire.davdroid.StorageLowReceiver import at.bitfire.davdroid.databinding.AccountListBinding import at.bitfire.davdroid.databinding.AccountListItemBinding import at.bitfire.davdroid.ui.account.AccountActivity Loading @@ -50,9 +52,23 @@ class AccountListFragment: Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) model.networkAvailable.observe(viewLifecycleOwner, { networkAvailable -> model.networkAvailable.observe(viewLifecycleOwner) { networkAvailable -> binding.noNetworkInfo.visibility = if (networkAvailable) View.GONE else View.VISIBLE }) } binding.manageConnections.setOnClickListener { val intent = Intent(Settings.ACTION_WIRELESS_SETTINGS) if (intent.resolveActivity(requireActivity().packageManager) != null) startActivity(intent) } StorageLowReceiver.getInstance(requireActivity()).storageLow.observe(viewLifecycleOwner) { storageLow -> binding.lowStorageInfo.visibility = if (storageLow) View.VISIBLE else View.GONE } binding.manageStorage.setOnClickListener { val intent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) if (intent.resolveActivity(requireActivity().packageManager) != null) startActivity(intent) } val accountAdapter = AccountAdapter(requireActivity()) binding.list.apply { Loading
app/src/main/java/at/bitfire/davdroid/ui/NotificationUtils.kt +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ object NotificationUtils { const val NOTIFY_SYNC_ERROR = 10 const val NOTIFY_INVALID_RESOURCE = 11 const val NOTIFY_WEBDAV_ACCESS = 12 const val NOTIFY_LOW_STORAGE = 13 const val NOTIFY_OPENTASKS = 20 const val NOTIFY_PERMISSIONS = 21 Loading
app/src/main/res/layout/account_list.xml +59 −10 Original line number Diff line number Diff line Loading @@ -5,20 +5,69 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatTextView <androidx.cardview.widget.CardView android:id="@+id/no_network_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="12dp" android:paddingTop="12dp" android:layout_margin="8dp" app:contentPadding="@dimen/card_padding"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:visibility="gone" app:drawableLeftCompat="@drawable/ic_signal_cellular_off" app:drawableTint="?android:attr/textColorPrimary" android:drawablePadding="8dp" style="@style/TextAppearance.MaterialComponents.Body1" android:text="@string/account_list_no_internet"/> <Button android:id="@+id/manage_connections" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Widget.MaterialComponents.Button.TextButton" android:text="Manage connections" /> </LinearLayout> </androidx.cardview.widget.CardView> <androidx.cardview.widget.CardView android:id="@+id/low_storage_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" app:contentPadding="@dimen/card_padding"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:visibility="visible" app:drawableLeftCompat="@drawable/ic_storage" app:drawableTint="?android:attr/textColorPrimary" android:drawablePadding="8dp" style="@style/TextAppearance.MaterialComponents.Body1" android:text="@string/account_list_low_storage"/> <Button android:id="@+id/manage_storage" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Widget.MaterialComponents.Button.TextButton" android:text="Manage storage" /> </LinearLayout> </androidx.cardview.widget.CardView> <androidx.recyclerview.widget.RecyclerView android:id="@android:id/list" android:layout_width="match_parent" Loading