package com.github.libretube import android.app.Application import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.os.Build import android.os.StrictMode import android.os.StrictMode.VmPolicy import androidx.preference.PreferenceManager import androidx.work.ExistingPeriodicWorkPolicy import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import com.github.libretube.api.RetrofitInstance import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.obj.WatchHistoryItem import com.github.libretube.db.obj.WatchPosition import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceKeys import com.github.libretube.util.ExceptionHandler import com.github.libretube.util.NotificationHelper import java.lang.Exception class MyApp : Application() { override fun onCreate() { super.onCreate() /** * Initialize the needed [NotificationChannel]s for DownloadService and BackgroundMode */ initializeNotificationChannels() /** * Set the applicationContext as context for the [PreferenceHelper] */ PreferenceHelper.setContext(applicationContext) /** * Initialize the [DatabaseHolder] */ DatabaseHolder.initializeDatabase(this) /** * bypassing fileUriExposedException, see https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed */ val builder = VmPolicy.Builder() StrictMode.setVmPolicy(builder.build()) /** * Set the api and the auth api url */ setRetrofitApiUrls() /** * Initialize the notification listener in the background */ NotificationHelper.enqueueWork(this, ExistingPeriodicWorkPolicy.KEEP) /** * Handler for uncaught exceptions */ val defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler() val exceptionHandler = ExceptionHandler(defaultExceptionHandler) Thread.setDefaultUncaughtExceptionHandler(exceptionHandler) /** * Legacy preference file migration */ prefFileMigration() /** * Database Migration */ databaseMigration() } /** * Set the api urls needed for the [RetrofitInstance] */ private fun setRetrofitApiUrls() { RetrofitInstance.url = PreferenceHelper.getString(PreferenceKeys.FETCH_INSTANCE, PIPED_API_URL) // set auth instance RetrofitInstance.authUrl = if (PreferenceHelper.getBoolean(PreferenceKeys.AUTH_INSTANCE_TOGGLE, false)) { PreferenceHelper.getString( PreferenceKeys.AUTH_INSTANCE, PIPED_API_URL ) } else { RetrofitInstance.url } } /** * Initializes the required [NotificationChannel]s for the app. */ private fun initializeNotificationChannels() { createNotificationChannel( DOWNLOAD_CHANNEL_ID, "Download Service", "Shows a notification when downloading media.", NotificationManager.IMPORTANCE_NONE ) createNotificationChannel( BACKGROUND_CHANNEL_ID, "Background Mode", "Shows a notification with buttons to control the audio player", NotificationManager.IMPORTANCE_LOW ) createNotificationChannel( PUSH_CHANNEL_ID, "Notification Worker", "Shows a notification when new streams are available.", NotificationManager.IMPORTANCE_DEFAULT ) } /** * Creates a [NotificationChannel] */ private fun createNotificationChannel( id: String, name: String, descriptionText: String, importance: Int ) { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(id, name, importance) channel.description = descriptionText // Register the channel in the system val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } /** * Migration from old preference files to new one */ private fun prefFileMigration() { val legacyUserPrefs = getSharedPreferences("username", Context.MODE_PRIVATE) val username = legacyUserPrefs.getString("username", "")!! if (username != "") { PreferenceHelper.setUsername(username) legacyUserPrefs.edit().putString("username", "") } val legacyTokenPrefs = getSharedPreferences("token", Context.MODE_PRIVATE) val token = legacyUserPrefs.getString("token", "")!! if (token != "") { PreferenceHelper.setToken(token) legacyTokenPrefs.edit().putString("token", "") } } /** * Migration from the preferences to the database */ private fun databaseMigration() { val prefs = PreferenceManager.getDefaultSharedPreferences(this) val mapper = ObjectMapper() Thread { val legacyWatchHistory = prefs.getString("watch_history", "") if (legacyWatchHistory != "") { try { val type = object : TypeReference>() {} val watchHistoryItems = mapper.readValue(legacyWatchHistory, type) DatabaseHolder.db.watchHistoryDao().insertAll( *watchHistoryItems.toTypedArray() ) } catch (e: Exception) {} prefs.edit().putString("watch_history", "").commit() } val legacyWatchPositions = prefs.getString("watch_positions", "") if (legacyWatchPositions != "") { try { val type = object : TypeReference>() {} val watchPositions = mapper.readValue(legacyWatchPositions, type) DatabaseHolder.db.watchPositionDao().insertAll( *watchPositions.toTypedArray() ) } catch (e: Exception) {} prefs.edit().remove("watch_positions").commit() } prefs.edit() .remove("custom_instances") .remove("local_subscriptions") .commit() }.start() } }