diff --git a/libpretixui-android/build.gradle b/libpretixui-android/build.gradle index 9a62403..bc63b05 100644 --- a/libpretixui-android/build.gradle +++ b/libpretixui-android/build.gradle @@ -33,6 +33,7 @@ android { } } buildFeatures { + buildConfig = true dataBinding = true viewBinding = true } @@ -72,6 +73,8 @@ dependencies { implementation(project(':libpretixsync')) { transitive = false } + implementation 'io.sentry:sentry-android:8.18.0' + implementation 'com.github.bumptech.glide:glide:4.12.0' kapt 'com.github.bumptech.glide:compiler:4.12.0' } diff --git a/libpretixui-android/src/main/java/eu/pretix/libpretixui/android/utils/Anko.kt b/libpretixui-android/src/main/java/eu/pretix/libpretixui/android/utils/Anko.kt new file mode 100644 index 0000000..29756ba --- /dev/null +++ b/libpretixui-android/src/main/java/eu/pretix/libpretixui/android/utils/Anko.kt @@ -0,0 +1,55 @@ +package eu.pretix.libpretixui.android.utils + +import android.app.Activity +import android.content.Context +import android.os.Handler +import android.os.Looper +import androidx.fragment.app.Fragment +import java.lang.ref.WeakReference + +// contains excerpts from https://github.com/Kotlin/anko + +/* + * Copyright 2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +private object ContextHelper { + val handler = Handler(Looper.getMainLooper()) +} + +/** + * Execute [f] on the application UI thread. + */ +fun Context.runOnUiThread(f: Context.() -> Unit) { + if (Looper.getMainLooper() === Looper.myLooper()) f() else ContextHelper.handler.post { f() } +} + +inline fun Fragment.runOnUiThread(crossinline f: () -> Unit) { + requireActivity().runOnUiThread { f() } +} + +class AnkoAsyncContext(val weakRef: WeakReference) + +/** + * Execute [f] on the application UI thread if the underlying [Activity] still exists and is not finished. + * The receiver [Activity] will be passed to [f]. + * If it is not exist anymore or if it was finished, [f] will not be called. + */ +fun AnkoAsyncContext.activityUiThread(f: (T) -> Unit): Boolean { + val activity = weakRef.get() ?: return false + if (activity.isFinishing) return false + activity.runOnUiThread { f(activity) } + return true +} diff --git a/libpretixui-android/src/main/java/eu/pretix/libpretixui/android/utils/Async.kt b/libpretixui-android/src/main/java/eu/pretix/libpretixui/android/utils/Async.kt new file mode 100644 index 0000000..fd5fb17 --- /dev/null +++ b/libpretixui-android/src/main/java/eu/pretix/libpretixui/android/utils/Async.kt @@ -0,0 +1,60 @@ +package eu.pretix.libpretixui.android.utils + +import android.app.Dialog +import eu.pretix.libpretixui.android.BuildConfig +import io.sentry.Sentry +import java.lang.ref.WeakReference +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.Future +import kotlin.system.exitProcess + +val crashLogger: (Throwable) -> Unit = { throwable: Throwable -> + throwable.printStackTrace() + if (BuildConfig.DEBUG) { + exitProcess(1) + } else { + Sentry.captureException(throwable) + } +} + +fun T.doAsyncSentry( + exceptionHandler: ((Throwable) -> Unit)? = crashLogger, + task: AnkoAsyncContext.() -> Unit +): Future { + val context = AnkoAsyncContext(WeakReference(this)) + return BackgroundExecutor.submit { + return@submit try { + context.task() + } catch (thr: Throwable) { + val result = exceptionHandler?.invoke(thr) + if (result != null) { + result + } else { + Unit + } + } + } +} +internal object BackgroundExecutor { + var executor: ExecutorService = + Executors.newScheduledThreadPool(2 * Runtime.getRuntime().availableProcessors()) + + fun submit(task: () -> T): Future = executor.submit(task) +} + +fun Dialog.safeDismiss() { + try { + dismiss() + } catch (e: IllegalArgumentException) { + e.printStackTrace() + } +} + +fun Dialog.safeCancel() { + try { + cancel() + } catch (e: IllegalArgumentException) { + e.printStackTrace() + } +}