Commit 9200ec89 authored by Ricki Hirner's avatar Ricki Hirner

Further improve notifications

parent b37a2ad0
......@@ -15,11 +15,8 @@ import android.graphics.drawable.BitmapDrawable
import android.os.Build
import android.support.v7.app.AppCompatDelegate
import at.bitfire.davdroid.log.Logger
<<<<<<< HEAD
=======
import at.bitfire.davdroid.ui.NotificationUtils
import kotlin.concurrent.thread
>>>>>>> 787260f1... Remove PermissionsActivity
class App: Application() {
......
......@@ -9,10 +9,6 @@ package at.bitfire.davdroid
object Constants {
// notification IDs
const val NOTIFICATION_EXTERNAL_FILE_LOGGING = 1
const val NOTIFICATION_REFRESH_COLLECTIONS = 2
const val DAVDROID_GREEN_RGBA = 0xFF8bc34a.toInt()
const val DEFAULT_SYNC_INTERVAL = 4 * 3600L // 4 hours
......
......@@ -11,11 +11,13 @@ package at.bitfire.davdroid
import android.accounts.Account
import android.app.PendingIntent
import android.app.Service
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Intent
import android.database.DatabaseUtils
import android.database.sqlite.SQLiteDatabase
import android.os.Binder
import android.os.Bundle
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import at.bitfire.dav4android.DavResource
......@@ -42,8 +44,14 @@ import kotlin.concurrent.thread
class DavService: Service() {
companion object {
@JvmField val ACTION_REFRESH_COLLECTIONS = "refreshCollections"
@JvmField val EXTRA_DAV_SERVICE_ID = "davServiceID"
const val ACTION_REFRESH_COLLECTIONS = "refreshCollections"
const val EXTRA_DAV_SERVICE_ID = "davServiceID"
/** Initialize a forced synchronization. Expects intent data
to be an URI of this format:
contents://<authority>/<account.type>/<account name>
**/
const val ACTION_FORCE_SYNC = "forceSync"
}
private val runningRefresh = HashSet<Long>()
......@@ -60,6 +68,15 @@ class DavService: Service() {
thread { refreshCollections(id) }
refreshingStatusListeners.forEach { it.get()?.onDavRefreshStatusChanged(id, true) }
}
ACTION_FORCE_SYNC -> {
val authority = intent.data.authority
val account = Account(
intent.data.pathSegments[1],
intent.data.pathSegments[0]
)
forceSync(authority, account)
}
}
}
......@@ -104,6 +121,14 @@ class DavService: Service() {
which actually do the work
*/
private fun forceSync(authority: String, account: Account) {
Logger.log.info("Forcing $authority synchronization of $account")
val extras = Bundle(2)
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true) // manual sync
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true) // run immediately (don't queue)
ContentResolver.requestSync(account, authority, extras)
}
private fun refreshCollections(service: Long) {
OpenHelper(this@DavService).use { dbHelper ->
val db = dbHelper.writableDatabase
......@@ -321,19 +346,20 @@ class DavService: Service() {
} catch(e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't refresh collection list", e)
val debugIntent = Intent(this@DavService, DebugInfoActivity::class.java)
val debugIntent = Intent(this, DebugInfoActivity::class.java)
debugIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e)
debugIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account)
val nm = NotificationManagerCompat.from(this)
val notify = NotificationCompat.Builder(this, NotificationUtils.CHANNEL_SYNC_ERRORS)
val notify = NotificationUtils.newBuilder(this)
.setSmallIcon(R.drawable.ic_sync_error_notification)
.setContentTitle(getString(R.string.dav_service_refresh_failed))
.setContentText(getString(R.string.dav_service_refresh_couldnt_refresh))
.setContentIntent(PendingIntent.getActivity(this, 0, debugIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setSubText(account.name)
.setCategory(NotificationCompat.CATEGORY_ERROR)
.build()
nm.notify(Constants.NOTIFICATION_REFRESH_COLLECTIONS, notify)
NotificationManagerCompat.from(this)
.notify(service.toString(), NotificationUtils.NOTIFY_REFRESH_COLLECTIONS, notify)
} finally {
runningRefresh.remove(service)
refreshingStatusListeners.forEach { it.get()?.onDavRefreshStatusChanged(service, false) }
......
......@@ -17,8 +17,6 @@ import android.preference.PreferenceManager
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import android.util.Log
import at.bitfire.davdroid.App
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.AppSettingsActivity
import at.bitfire.davdroid.ui.NotificationUtils
......@@ -30,13 +28,12 @@ import java.util.logging.Level
object Logger {
val LOG_TO_EXTERNAL_STORAGE = "log_to_external_storage"
const val LOG_TO_EXTERNAL_STORAGE = "log_to_external_storage"
@JvmField
val log = java.util.logging.Logger.getLogger("davdroid")!!
lateinit private var preferences: SharedPreferences
private lateinit var preferences: SharedPreferences
fun initialize(context: Context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context)
......@@ -66,9 +63,8 @@ object Logger {
val nm = NotificationManagerCompat.from(context)
// log to external file according to preferences
if (logToFile) {
val builder = NotificationCompat.Builder(context, NotificationUtils.CHANNEL_DEBUG)
val builder = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_DEBUG)
builder .setSmallIcon(R.drawable.ic_sd_storage_notification)
.setLargeIcon(App.getLauncherBitmap(context))
.setContentTitle(context.getString(R.string.logging_davdroid_file_logging))
.setLocalOnly(true)
......@@ -89,22 +85,22 @@ object Logger {
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(PendingIntent.getActivity(context, 0, prefIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.BigTextStyle()
.bigText(context.getString(R.string.logging_to_external_storage, dir.path)))
.setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(R.string.logging_to_external_storage, dir.path)))
.setOngoing(true)
} catch(e: IOException) {
log.log(Level.SEVERE, "Couldn't create external log file", e)
builder .setContentText(context.getString(R.string.logging_couldnt_create_file, e.localizedMessage))
val message = context.getString(R.string.logging_couldnt_create_file, e.localizedMessage)
builder .setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setCategory(NotificationCompat.CATEGORY_ERROR)
}
else
builder.setContentText(context.getString(R.string.logging_no_external_storage))
nm.notify(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING, builder.build())
nm.notify(NotificationUtils.NOTIFY_EXTERNAL_FILE_LOGGING, builder.build())
} else
nm.cancel(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING)
nm.cancel(NotificationUtils.NOTIFY_EXTERNAL_FILE_LOGGING)
}
}
\ No newline at end of file
......@@ -222,15 +222,15 @@ class ContactsSyncManager(
}
private fun notifyDiscardedChange() {
val notification = NotificationCompat.Builder(context, NotificationUtils.CHANNEL_SYNC_STATUS)
val notification = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_STATUS)
.setSmallIcon(R.drawable.ic_delete_notification)
.setContentTitle(context.getString(R.string.sync_contacts_read_only_address_book))
.setContentText(context.resources.getQuantityString(R.plurals.sync_contacts_local_contact_changes_discarded, numDiscarded, numDiscarded))
.setNumber(numDiscarded)
.setSubText(account.name)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setLocalOnly(true)
.setAutoCancel(true)
.build()
notificationManager.notify("discarded_${account.name}", 0, notification)
}
......
......@@ -28,6 +28,7 @@ import at.bitfire.dav4android.exception.ServiceUnavailableException
import at.bitfire.dav4android.exception.UnauthorizedException
import at.bitfire.davdroid.AccountSettings
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.DavService
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.SyncState
......@@ -329,13 +330,17 @@ abstract class SyncManager<out ResourceType: LocalResource, out CollectionType:
else
account
// TODO: "Retry" intent
/*val retryIntent = Intent(context, DavService::class.java)
retryIntent.data = notifyUri
val pendingRetryIntent = PendingIntent.getActivity(context, 1, retryIntent, PendingIntent.FLAG_UPDATE_CURRENT)*/
val channel: String
val priority: Int
if (e is IOError) {
channel = NotificationUtils.CHANNEL_SYNC_IO_ERRORS
priority = NotificationCompat.PRIORITY_LOW
} else {
channel = NotificationUtils.CHANNEL_SYNC_ERRORS
priority = NotificationCompat.PRIORITY_DEFAULT
}
val builder = NotificationUtils.newBuilder(context,
if (e is IOError) NotificationUtils.CHANNEL_SYNC_IO_ERRORS else NotificationUtils.CHANNEL_SYNC_ERRORS)
val builder = NotificationUtils.newBuilder(context, channel)
builder .setSmallIcon(R.drawable.ic_sync_error_notification)
.setContentTitle(localCollection.title)
.setContentText(message)
......@@ -343,8 +348,20 @@ abstract class SyncManager<out ResourceType: LocalResource, out CollectionType:
.setSubText(account.name)
.setOnlyAlertOnce(true)
.setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setPriority(priority)
.setCategory(NotificationCompat.CATEGORY_ERROR)
viewItemAction?.let { builder.addAction(it) }
val retryIntent = Intent(context, DavService::class.java)
retryIntent.action = DavService.ACTION_FORCE_SYNC
retryIntent.data = Uri.parse("contents://").buildUpon()
.authority(authority)
.appendPath(account.type)
.appendPath(account.name)
.build()
builder.addAction(android.R.drawable.ic_menu_rotate, context.getString(R.string.sync_error_retry),
PendingIntent.getService(context, 0, retryIntent, PendingIntent.FLAG_UPDATE_CURRENT))
notificationManager.notify(localCollection.uniqueId, NotificationUtils.NOTIFY_SYNC_ERROR, builder.build())
}
......@@ -363,7 +380,7 @@ abstract class SyncManager<out ResourceType: LocalResource, out CollectionType:
}
}
return if (intent != null && context.packageManager.resolveActivity(intent, 0) != null)
NotificationCompat.Action(android.R.drawable.ic_menu_view, "View item",
NotificationCompat.Action(android.R.drawable.ic_menu_view, context.getString(R.string.sync_error_view_item),
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
else
null
......
......@@ -8,7 +8,6 @@
package at.bitfire.davdroid.syncadapter
import android.accounts.Account
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.*
import android.content.pm.PackageManager
......@@ -17,6 +16,7 @@ import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.os.Bundle
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import at.bitfire.davdroid.AccountSettings
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
......@@ -64,11 +64,13 @@ class TasksSyncAdapterService: SyncAdapterService() {
}
}
} catch (e: TaskProvider.ProviderTooOldException) {
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notify = NotificationCompat.Builder(context, NotificationUtils.CHANNEL_SYNC_ERRORS)
val nm = NotificationManagerCompat.from(context)
val message = context.getString(R.string.sync_error_opentasks_required_version, e.provider.minVersionName, e.installedVersionName)
val notify = NotificationUtils.newBuilder(context)
.setSmallIcon(R.drawable.ic_sync_error_notification)
.setContentTitle(context.getString(R.string.sync_error_opentasks_too_old))
.setContentText(context.getString(R.string.sync_error_opentasks_required_version, e.provider.minVersionName, e.installedVersionName))
.setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setCategory(NotificationCompat.CATEGORY_ERROR)
try {
......
......@@ -164,8 +164,11 @@ class AppSettingsActivity: AppCompatActivity() {
// debugging settings
val prefLogToExternalStorage = findPreference(Logger.LOG_TO_EXTERNAL_STORAGE) as SwitchPreferenceCompat
prefLogToExternalStorage.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val context = activity!!
Logger.initialize(context)
// kill a potential :sync process, so that the new logger settings will be used
val am = activity!!.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
am.runningAppProcesses.forEach {
if (it.pid != Process.myPid()) {
Logger.log.info("Killing ${it.processName} process, pid = ${it.pid}")
......
......@@ -21,6 +21,8 @@ import at.bitfire.davdroid.R
object NotificationUtils {
// notification IDs
const val NOTIFY_EXTERNAL_FILE_LOGGING = 1
const val NOTIFY_REFRESH_COLLECTIONS = 2
const val NOTIFY_SYNC_ERROR = 10
const val NOTIFY_OPENTASKS = 20
const val NOTIFY_PERMISSIONS = 21
......@@ -52,7 +54,7 @@ object NotificationUtils {
}
nm.createNotificationChannels(listOf(
NotificationChannel(CHANNEL_DEBUG, context.getString(R.string.notification_channel_debugging), NotificationManager.IMPORTANCE_DEFAULT),
NotificationChannel(CHANNEL_DEBUG, context.getString(R.string.notification_channel_debugging), NotificationManager.IMPORTANCE_HIGH),
NotificationChannel(CHANNEL_GENERAL, context.getString(R.string.notification_channel_general), NotificationManager.IMPORTANCE_DEFAULT),
*syncChannels
))
......
......@@ -267,14 +267,16 @@
<item quantity="one">Local contact change discarded</item>
<item quantity="other">%d local contact changes discarded</item>
</plurals>
<string name="sync_error_authentication_failed">Authentication failed (check login credentials)</string>
<string name="sync_error_permissions">DAVdroid permissions</string>
<string name="sync_error_permissions_text">Additional permissions required</string>
<string name="sync_error_opentasks_too_old">OpenTasks too old</string>
<string name="sync_error_opentasks_required_version">Required version: %1$s (currently %2$s)</string>
<string name="sync_error_authentication_failed">Authentication failed (check login credentials)</string>
<string name="sync_error_io">Network or I/O error – %s</string>
<string name="sync_error_http_dav">HTTP server error – %s</string>
<string name="sync_error_local_storage">Local storage error – %s</string>
<string name="sync_error_retry">Retry</string>
<string name="sync_error_view_item">View item</string>
<!-- cert4android -->
<string name="certificate_notification_connection_security">DAVdroid: Connection security</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment