Loading app/build.gradle +3 −3 Original line number Diff line number Diff line Loading @@ -4,13 +4,13 @@ apply plugin: 'kotlin-kapt' apply plugin: 'com.mikepenz.aboutlibraries.plugin' android { compileSdkVersion 31 compileSdkVersion 32 buildToolsVersion '32.0.0' defaultConfig { applicationId "at.bitfire.icsdroid" minSdkVersion 21 targetSdkVersion 31 targetSdkVersion 32 versionCode 60 versionName "2.1-alpha.1" Loading Loading @@ -65,7 +65,7 @@ android { } dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation project(':cert4android') Loading app/src/main/java/at/bitfire/icsdroid/MyApp.kt +4 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ package at.bitfire.icsdroid import android.app.Application import androidx.appcompat.app.AppCompatDelegate import at.bitfire.icsdroid.ui.NotificationUtils import at.bitfire.icsdroid.ui.Settings class MyApp: Application() { Loading @@ -13,6 +14,9 @@ class MyApp: Application() { override fun onCreate() { super.onCreate() // create notification channels NotificationUtils.createChannels(this) // dark mode is not persisted over app restarts if (Settings(this).forceDarkMode()) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) Loading app/src/main/java/at/bitfire/icsdroid/SyncAdapter.kt +4 −2 Original line number Diff line number Diff line Loading @@ -9,15 +9,17 @@ import android.app.PendingIntent import android.content.* import android.os.Bundle import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.work.WorkManager import at.bitfire.icsdroid.ui.CalendarListActivity import at.bitfire.icsdroid.ui.NotificationUtils import kotlinx.coroutines.runBlocking class SyncAdapter( context: Context ): AbstractThreadedSyncAdapter(context, false) { override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult): Unit = runBlocking { val manual = extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) SyncWorker.run(context, manual) } Loading @@ -29,7 +31,7 @@ class SyncAdapter( } override fun onSecurityException(account: Account?, extras: Bundle?, authority: String?, syncResult: SyncResult?) { val nm = NotificationUtils.createChannels(context) val nm = NotificationManagerCompat.from(context) val notification = NotificationCompat.Builder(context, NotificationUtils.CHANNEL_SYNC) .setSmallIcon(R.drawable.ic_sync_problem_white) .setContentTitle(context.getString(R.string.sync_permission_required)) Loading app/src/main/java/at/bitfire/icsdroid/SyncWorker.kt +30 −13 Original line number Diff line number Diff line Loading @@ -10,10 +10,15 @@ import android.content.ContentProviderClient import android.content.Context import android.provider.CalendarContract import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.* import at.bitfire.ical4android.CalendarStorageException import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat import at.bitfire.icsdroid.db.LocalCalendar import at.bitfire.icsdroid.ui.NotificationUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.withContext import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit Loading @@ -22,7 +27,7 @@ import kotlin.math.min class SyncWorker( context: Context, workerParams: WorkerParameters ): Worker(context, workerParams) { ): CoroutineWorker(context, workerParams) { companion object { Loading @@ -30,21 +35,25 @@ class SyncWorker( private val nrSyncThreads = min(Runtime.getRuntime().availableProcessors(), 4) /** * Enqueues a sync job for immediate execution. If the sync is forced, * the "requires network connection" constraint won't be set. * * @param context required for managing work * @param force *true* enqueues the sync regardless of the network state; *false* adds a [NetworkType.CONNECTED] constraint * * @return WorkManager operation */ fun run(context: Context, force: Boolean = false) { fun run(context: Context, force: Boolean = false): Operation { val request = OneTimeWorkRequestBuilder<SyncWorker>() val policy: ExistingWorkPolicy if (force) { Log.i(Constants.TAG, "Manual sync, ignoring network condition") // work is user-initiated request.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // overwrite existing syncs (which may have unwanted constraints) policy = ExistingWorkPolicy.REPLACE Loading @@ -58,7 +67,7 @@ class SyncWorker( policy = ExistingWorkPolicy.KEEP } WorkManager.getInstance(context) return WorkManager.getInstance(context) .beginUniqueWork(NAME, policy, request.build()) .enqueue() } Loading @@ -70,10 +79,10 @@ class SyncWorker( private val syncQueue = LinkedBlockingQueue<Runnable>() private val syncExecutor = ThreadPoolExecutor(nrSyncThreads, nrSyncThreads, 5, TimeUnit.SECONDS, syncQueue) private val syncExecutor = ThreadPoolExecutor(nrSyncThreads, nrSyncThreads, 5, TimeUnit.SECONDS, syncQueue).asCoroutineDispatcher() @SuppressLint("Recycle") override fun doWork(): Result { override suspend fun doWork(): Result = withContext(Dispatchers.IO) { applicationContext.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)?.let { providerClient -> try { return performSync(AppAccount.get(applicationContext), providerClient) Loading @@ -84,6 +93,19 @@ class SyncWorker( return Result.failure() } override suspend fun getForegroundInfo(): ForegroundInfo { val notify = NotificationCompat.Builder(applicationContext, NotificationUtils.CHANNEL_SYNC) .setContentTitle("Synchronizing") .setProgress(1, 0, true) return ForegroundInfo(NotificationUtils.NOTIFY_SYNC, notify.build()) } /*override fun onStopped() { super.onStopped() syncExecutor.shutdownNow() }*/ private fun performSync(account: Account, provider: ContentProviderClient): Result { Log.i(Constants.TAG, "Synchronizing ${account.name}") try { Loading @@ -104,9 +126,4 @@ class SyncWorker( return Result.success() } override fun onStopped() { syncExecutor.shutdownNow() } } app/src/main/java/at/bitfire/icsdroid/ui/NotificationUtils.kt +4 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ object NotificationUtils { const val CHANNEL_SYNC = "sync" const val NOTIFY_PERMISSION = 0 const val NOTIFY_SYNC = 1 val flagImmutableCompat: Int = Loading @@ -25,15 +26,12 @@ object NotificationUtils { 0 fun createChannels(context: Context): NotificationManager { val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager fun createChannels(context: Context) { if (Build.VERSION.SDK_INT >= 26) { val nm = context.getSystemService(NotificationManager::class.java) nm.createNotificationChannel(NotificationChannel(CHANNEL_SYNC, context.getString(R.string.notification_channel_sync_problem), NotificationManager.IMPORTANCE_LOW)) context.getString(R.string.notification_channel_sync), NotificationManager.IMPORTANCE_LOW)) } return nm } } No newline at end of file Loading
app/build.gradle +3 −3 Original line number Diff line number Diff line Loading @@ -4,13 +4,13 @@ apply plugin: 'kotlin-kapt' apply plugin: 'com.mikepenz.aboutlibraries.plugin' android { compileSdkVersion 31 compileSdkVersion 32 buildToolsVersion '32.0.0' defaultConfig { applicationId "at.bitfire.icsdroid" minSdkVersion 21 targetSdkVersion 31 targetSdkVersion 32 versionCode 60 versionName "2.1-alpha.1" Loading Loading @@ -65,7 +65,7 @@ android { } dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation project(':cert4android') Loading
app/src/main/java/at/bitfire/icsdroid/MyApp.kt +4 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ package at.bitfire.icsdroid import android.app.Application import androidx.appcompat.app.AppCompatDelegate import at.bitfire.icsdroid.ui.NotificationUtils import at.bitfire.icsdroid.ui.Settings class MyApp: Application() { Loading @@ -13,6 +14,9 @@ class MyApp: Application() { override fun onCreate() { super.onCreate() // create notification channels NotificationUtils.createChannels(this) // dark mode is not persisted over app restarts if (Settings(this).forceDarkMode()) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) Loading
app/src/main/java/at/bitfire/icsdroid/SyncAdapter.kt +4 −2 Original line number Diff line number Diff line Loading @@ -9,15 +9,17 @@ import android.app.PendingIntent import android.content.* import android.os.Bundle import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.work.WorkManager import at.bitfire.icsdroid.ui.CalendarListActivity import at.bitfire.icsdroid.ui.NotificationUtils import kotlinx.coroutines.runBlocking class SyncAdapter( context: Context ): AbstractThreadedSyncAdapter(context, false) { override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult): Unit = runBlocking { val manual = extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) SyncWorker.run(context, manual) } Loading @@ -29,7 +31,7 @@ class SyncAdapter( } override fun onSecurityException(account: Account?, extras: Bundle?, authority: String?, syncResult: SyncResult?) { val nm = NotificationUtils.createChannels(context) val nm = NotificationManagerCompat.from(context) val notification = NotificationCompat.Builder(context, NotificationUtils.CHANNEL_SYNC) .setSmallIcon(R.drawable.ic_sync_problem_white) .setContentTitle(context.getString(R.string.sync_permission_required)) Loading
app/src/main/java/at/bitfire/icsdroid/SyncWorker.kt +30 −13 Original line number Diff line number Diff line Loading @@ -10,10 +10,15 @@ import android.content.ContentProviderClient import android.content.Context import android.provider.CalendarContract import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.* import at.bitfire.ical4android.CalendarStorageException import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat import at.bitfire.icsdroid.db.LocalCalendar import at.bitfire.icsdroid.ui.NotificationUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.withContext import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit Loading @@ -22,7 +27,7 @@ import kotlin.math.min class SyncWorker( context: Context, workerParams: WorkerParameters ): Worker(context, workerParams) { ): CoroutineWorker(context, workerParams) { companion object { Loading @@ -30,21 +35,25 @@ class SyncWorker( private val nrSyncThreads = min(Runtime.getRuntime().availableProcessors(), 4) /** * Enqueues a sync job for immediate execution. If the sync is forced, * the "requires network connection" constraint won't be set. * * @param context required for managing work * @param force *true* enqueues the sync regardless of the network state; *false* adds a [NetworkType.CONNECTED] constraint * * @return WorkManager operation */ fun run(context: Context, force: Boolean = false) { fun run(context: Context, force: Boolean = false): Operation { val request = OneTimeWorkRequestBuilder<SyncWorker>() val policy: ExistingWorkPolicy if (force) { Log.i(Constants.TAG, "Manual sync, ignoring network condition") // work is user-initiated request.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // overwrite existing syncs (which may have unwanted constraints) policy = ExistingWorkPolicy.REPLACE Loading @@ -58,7 +67,7 @@ class SyncWorker( policy = ExistingWorkPolicy.KEEP } WorkManager.getInstance(context) return WorkManager.getInstance(context) .beginUniqueWork(NAME, policy, request.build()) .enqueue() } Loading @@ -70,10 +79,10 @@ class SyncWorker( private val syncQueue = LinkedBlockingQueue<Runnable>() private val syncExecutor = ThreadPoolExecutor(nrSyncThreads, nrSyncThreads, 5, TimeUnit.SECONDS, syncQueue) private val syncExecutor = ThreadPoolExecutor(nrSyncThreads, nrSyncThreads, 5, TimeUnit.SECONDS, syncQueue).asCoroutineDispatcher() @SuppressLint("Recycle") override fun doWork(): Result { override suspend fun doWork(): Result = withContext(Dispatchers.IO) { applicationContext.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)?.let { providerClient -> try { return performSync(AppAccount.get(applicationContext), providerClient) Loading @@ -84,6 +93,19 @@ class SyncWorker( return Result.failure() } override suspend fun getForegroundInfo(): ForegroundInfo { val notify = NotificationCompat.Builder(applicationContext, NotificationUtils.CHANNEL_SYNC) .setContentTitle("Synchronizing") .setProgress(1, 0, true) return ForegroundInfo(NotificationUtils.NOTIFY_SYNC, notify.build()) } /*override fun onStopped() { super.onStopped() syncExecutor.shutdownNow() }*/ private fun performSync(account: Account, provider: ContentProviderClient): Result { Log.i(Constants.TAG, "Synchronizing ${account.name}") try { Loading @@ -104,9 +126,4 @@ class SyncWorker( return Result.success() } override fun onStopped() { syncExecutor.shutdownNow() } }
app/src/main/java/at/bitfire/icsdroid/ui/NotificationUtils.kt +4 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ object NotificationUtils { const val CHANNEL_SYNC = "sync" const val NOTIFY_PERMISSION = 0 const val NOTIFY_SYNC = 1 val flagImmutableCompat: Int = Loading @@ -25,15 +26,12 @@ object NotificationUtils { 0 fun createChannels(context: Context): NotificationManager { val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager fun createChannels(context: Context) { if (Build.VERSION.SDK_INT >= 26) { val nm = context.getSystemService(NotificationManager::class.java) nm.createNotificationChannel(NotificationChannel(CHANNEL_SYNC, context.getString(R.string.notification_channel_sync_problem), NotificationManager.IMPORTANCE_LOW)) context.getString(R.string.notification_channel_sync), NotificationManager.IMPORTANCE_LOW)) } return nm } } No newline at end of file