diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1cef4a6..7f32fce 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,10 +1,10 @@ import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties -import org.gradle.api.tasks.testing.logging.TestLogEvent -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.Date import java.util.TimeZone +import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("com.android.application") @@ -42,7 +42,7 @@ android { buildConfigField("String", "DATABASE_VERSION", "\"3.0\"") // Please disable ACRA or use your own instance in forked versions of the project - buildConfigField("String", "ACRA_URI", "\"https://acra.rumblur.space/report\"") + buildConfigField("String", "ACRA_URI", "\"https://acra.kotatsu.app/report\"") buildConfigField("String", "ACRA_AUTH_LOGIN", acraAuthLogin) buildConfigField("String", "ACRA_AUTH_PASSWORD", acraAuthPassword) @@ -108,22 +108,24 @@ android { dimension = "default" } create("dev") { - resConfigs("xxhdpi") + resourceConfigurations += setOf("xxhdpi") dimension = "default" } } packagingOptions { - resources.excludes.addAll(listOf( - "META-INF/DEPENDENCIES", - "LICENSE.txt", - "META-INF/LICENSE", - "META-INF/LICENSE.txt", - "META-INF/README.md", - "META-INF/NOTICE", - "META-INF/*.kotlin_module", - "META-INF/*.version", - )) + resources.excludes.addAll( + listOf( + "META-INF/DEPENDENCIES", + "LICENSE.txt", + "META-INF/LICENSE", + "META-INF/LICENSE.txt", + "META-INF/README.md", + "META-INF/NOTICE", + "META-INF/*.kotlin_module", + "META-INF/*.version" + ) + ) } dependenciesInfo { @@ -166,7 +168,7 @@ dependencies { // AndroidX libraries implementation("androidx.annotation:annotation:1.4.0-alpha02") implementation("androidx.appcompat:appcompat:1.6.0") - implementation("androidx.browser:browser:1.4.0") + implementation("androidx.browser:browser:1.5.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0") implementation("androidx.core:core-ktx:1.9.0") @@ -182,12 +184,16 @@ dependencies { // Data serialization (JSON, protobuf) val kotlinSerializationVersion = "1.3.3" implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion") + implementation( + "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion" + ) // Job scheduling implementation("androidx.work:work-runtime-ktx:2.7.1") implementation("com.google.guava:guava:31.1-android") - implementation("com.github.Koitharu.pausing-coroutine-dispatcher:pausing-coroutine-dispatcher:5213d53420") + implementation( + "com.github.Koitharu.pausing-coroutine-dispatcher:pausing-coroutine-dispatcher:5213d53420" + ) // Network implementation("com.squareup.retrofit2:retrofit:2.9.0") @@ -218,12 +224,16 @@ dependencies { val flowbindingVersion = "1.2.0" implementation("io.github.reactivecircus.flowbinding:flowbinding-android:$flowbindingVersion") implementation("io.github.reactivecircus.flowbinding:flowbinding-appcompat:$flowbindingVersion") - implementation("io.github.reactivecircus.flowbinding:flowbinding-recyclerview:$flowbindingVersion") - implementation("io.github.reactivecircus.flowbinding:flowbinding-swiperefreshlayout:$flowbindingVersion") + implementation( + "io.github.reactivecircus.flowbinding:flowbinding-recyclerview:$flowbindingVersion" + ) + implementation( + "io.github.reactivecircus.flowbinding:flowbinding-swiperefreshlayout:$flowbindingVersion" + ) implementation("io.github.reactivecircus.flowbinding:flowbinding-viewpager:$flowbindingVersion") // UI libraries - implementation("com.google.android.material:material:1.7.0-alpha01") + implementation("com.google.android.material:material:1.8.0") implementation("com.github.arkon.FlexibleAdapter:flexible-adapter:c8013533") implementation("com.github.arkon.FlexibleAdapter:flexible-adapter-ui:c8013533") implementation("dev.chrisbanes.insetter:insetter:0.6.1") @@ -252,7 +262,7 @@ dependencies { implementation("com.github.tachiyomiorg:conductor-support-preference:$conductorVersion") // Crash reports/analytics - implementation("ch.acra:acra-http:5.9.6") + implementation("ch.acra:acra-http:5.9.7") implementation("org.jsoup:jsoup:1.15.3") @@ -271,15 +281,15 @@ tasks { // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) withType { kotlinOptions.freeCompilerArgs += listOf( - "-opt-in=kotlin.Experimental", - "-opt-in=kotlin.RequiresOptIn", - "-opt-in=kotlin.ExperimentalStdlibApi", - "-opt-in=kotlinx.coroutines.FlowPreview", - "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", - "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", - "-opt-in=coil.annotation.ExperimentalCoilApi", - "-Xjvm-default=enable", + "-opt-in=kotlin.Experimental", + "-opt-in=kotlin.RequiresOptIn", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-opt-in=kotlinx.coroutines.FlowPreview", + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", + "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", + "-opt-in=coil.annotation.ExperimentalCoilApi", + "-Xjvm-default=enable" ) } @@ -322,4 +332,4 @@ fun runCommand(command: String): String { standardOutput = byteOut } return String(byteOut.toByteArray()).trim() -} \ No newline at end of file +} diff --git a/app/src/main/java/org/xtimms/ridebus/App.kt b/app/src/main/java/org/xtimms/ridebus/App.kt index 940ed98..d673aa3 100644 --- a/app/src/main/java/org/xtimms/ridebus/App.kt +++ b/app/src/main/java/org/xtimms/ridebus/App.kt @@ -37,16 +37,15 @@ open class App : Application(), DefaultLifecycleObserver { MapKitFactory.setApiKey("397822a9-4d94-49cb-b402-d419ed3d5355") - preferences.themeMode() - .asImmediateFlow { - AppCompatDelegate.setDefaultNightMode( - when (it) { - PreferenceValues.ThemeMode.light -> AppCompatDelegate.MODE_NIGHT_NO - PreferenceValues.ThemeMode.dark -> AppCompatDelegate.MODE_NIGHT_YES - PreferenceValues.ThemeMode.system -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - } - ) - }.launchIn(ProcessLifecycleOwner.get().lifecycleScope) + preferences.themeMode().asImmediateFlow { + AppCompatDelegate.setDefaultNightMode( + when (it) { + PreferenceValues.ThemeMode.light -> AppCompatDelegate.MODE_NIGHT_NO + PreferenceValues.ThemeMode.dark -> AppCompatDelegate.MODE_NIGHT_YES + PreferenceValues.ThemeMode.system -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + } + ) + }.launchIn(ProcessLifecycleOwner.get().lifecycleScope) if (!LogcatLogger.isInstalled && preferences.verboseLogging()) { LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE)) diff --git a/app/src/main/java/org/xtimms/ridebus/AppModule.kt b/app/src/main/java/org/xtimms/ridebus/AppModule.kt index 1babfa0..52ca18b 100644 --- a/app/src/main/java/org/xtimms/ridebus/AppModule.kt +++ b/app/src/main/java/org/xtimms/ridebus/AppModule.kt @@ -6,7 +6,11 @@ import kotlinx.serialization.json.Json import org.xtimms.ridebus.data.database.RideBusDatabase import org.xtimms.ridebus.data.preference.PreferencesHelper import org.xtimms.ridebus.network.NetworkHelper -import uy.kohesive.injekt.api.* +import uy.kohesive.injekt.api.InjektModule +import uy.kohesive.injekt.api.InjektRegistrar +import uy.kohesive.injekt.api.addSingleton +import uy.kohesive.injekt.api.addSingletonFactory +import uy.kohesive.injekt.api.get class AppModule(val app: Application) : InjektModule { diff --git a/app/src/main/java/org/xtimms/ridebus/data/preference/PreferenceKeys.kt b/app/src/main/java/org/xtimms/ridebus/data/preference/PreferenceKeys.kt index c5ffdda..2289f00 100644 --- a/app/src/main/java/org/xtimms/ridebus/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/org/xtimms/ridebus/data/preference/PreferenceKeys.kt @@ -11,4 +11,6 @@ object PreferenceKeys { const val reducedMotion = "reduced_motion" const val verboseLogging = "verbose_logging" + + const val city = "pref_city_key" } diff --git a/app/src/main/java/org/xtimms/ridebus/data/preference/PreferencesHelper.kt b/app/src/main/java/org/xtimms/ridebus/data/preference/PreferencesHelper.kt index 0c37697..1f0695e 100644 --- a/app/src/main/java/org/xtimms/ridebus/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/org/xtimms/ridebus/data/preference/PreferencesHelper.kt @@ -58,7 +58,7 @@ class PreferencesHelper(val context: Context) { if (context.applicationContext.isTablet()) ALWAYS else NEVER ) - fun city() = flowPrefs.getString("pref_city_key", "-1") + fun city() = flowPrefs.getString(Keys.city, "0") fun themeDarkAmoled() = flowPrefs.getBoolean("pref_theme_dark_amoled_key", false) diff --git a/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateChecker.kt b/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateChecker.kt index 87f1f3a..7f23658 100644 --- a/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateChecker.kt +++ b/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateChecker.kt @@ -17,8 +17,13 @@ class DatabaseUpdateChecker { private val networkService: NetworkHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy() - suspend fun checkForUpdate(context: Context, isUserPrompt: Boolean = false): DatabaseUpdateResult { - if (isUserPrompt.not() && Date().time < preferences.lastDatabaseCheck().get() + TimeUnit.DAYS.toMillis(1)) { + suspend fun checkForUpdate( + context: Context, + isUserPrompt: Boolean = false + ): DatabaseUpdateResult { + if (isUserPrompt.not() && Date().time < preferences.lastDatabaseCheck() + .get() + TimeUnit.DAYS.toMillis(1) + ) { return DatabaseUpdateResult.NoNewUpdate } @@ -29,7 +34,11 @@ class DatabaseUpdateChecker { .parseAs() .let { preferences.lastDatabaseCheck().set(Date().time) - if (SemanticVersioning.isNewVersion(it.version, preferences.databaseVersion().get())) { + if (SemanticVersioning.isNewVersion( + it.version, + preferences.databaseVersion().get() + ) + ) { DatabaseUpdateResult.NewUpdate(it) } else { DatabaseUpdateResult.NoNewUpdate @@ -37,7 +46,9 @@ class DatabaseUpdateChecker { } when (result) { - is DatabaseUpdateResult.NewUpdate -> DatabaseUpdateNotifier(context).promptUpdate(result.update) + is DatabaseUpdateResult.NewUpdate -> DatabaseUpdateNotifier(context).promptUpdate( + result.update + ) else -> {} } result diff --git a/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateService.kt b/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateService.kt index c15c60f..0f982e1 100644 --- a/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateService.kt +++ b/app/src/main/java/org/xtimms/ridebus/data/updater/database/DatabaseUpdateService.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.os.IBinder import android.os.PowerManager import androidx.core.content.ContextCompat +import java.io.File import logcat.LogPriority import org.xtimms.ridebus.BuildConfig import org.xtimms.ridebus.R @@ -25,7 +26,6 @@ import org.xtimms.ridebus.util.system.acquireWakeLock import org.xtimms.ridebus.util.system.isServiceRunning import org.xtimms.ridebus.util.system.logcat import uy.kohesive.injekt.injectLazy -import java.io.File class DatabaseUpdateService : Service() { @@ -104,7 +104,7 @@ class DatabaseUpdateService : Service() { try { val databasePath = database.openHelper.writableDatabase.path.replace("ridebus.db", "") val oldDatabaseFile = withIOContext { File(databasePath, "ridebus.db") } - val databaseFile = withIOContext { File(databasePath, "ridebus_new.db") } + val databaseFile = withIOContext { File(databasePath, "update.db") } if (response.isSuccessful) { if (database.isOpen) { @@ -132,9 +132,12 @@ class DatabaseUpdateService : Service() { companion object { - internal const val EXTRA_DOWNLOAD_URL = "${BuildConfig.APPLICATION_ID}.DatabaseUpdaterService.DOWNLOAD_URL" - internal const val EXTRA_DOWNLOAD_TITLE = "${BuildConfig.APPLICATION_ID}.DatabaseUpdaterService.DOWNLOAD_TITLE" - internal const val EXTRA_DOWNLOAD_VERSION = "${BuildConfig.APPLICATION_ID}.DatabaseUpdaterService.DOWNLOAD_VERSION" + internal const val EXTRA_DOWNLOAD_URL = + "${BuildConfig.APPLICATION_ID}.DatabaseUpdaterService.DOWNLOAD_URL" + internal const val EXTRA_DOWNLOAD_TITLE = + "${BuildConfig.APPLICATION_ID}.DatabaseUpdaterService.DOWNLOAD_TITLE" + internal const val EXTRA_DOWNLOAD_VERSION = + "${BuildConfig.APPLICATION_ID}.DatabaseUpdaterService.DOWNLOAD_VERSION" private fun isRunning(context: Context): Boolean = context.isServiceRunning(DatabaseUpdateService::class.java) @@ -155,12 +158,21 @@ class DatabaseUpdateService : Service() { } } - internal fun downloadDatabasePendingService(context: Context, url: String, version: String): PendingIntent { + internal fun downloadDatabasePendingService( + context: Context, + url: String, + version: String + ): PendingIntent { val intent = Intent(context, DatabaseUpdateService::class.java).apply { putExtra(EXTRA_DOWNLOAD_URL, url) putExtra(EXTRA_DOWNLOAD_VERSION, version) } - return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + return PendingIntent.getService( + context, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) } } } diff --git a/app/src/main/java/org/xtimms/ridebus/ui/main/MainActivity.kt b/app/src/main/java/org/xtimms/ridebus/ui/main/MainActivity.kt index f93f495..ec39406 100644 --- a/app/src/main/java/org/xtimms/ridebus/ui/main/MainActivity.kt +++ b/app/src/main/java/org/xtimms/ridebus/ui/main/MainActivity.kt @@ -30,6 +30,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.Router import com.google.android.material.navigation.NavigationBarView import dev.chrisbanes.insetter.applyInsetter +import kotlin.collections.set import kotlinx.coroutines.delay import kotlinx.coroutines.flow.launchIn import logcat.LogPriority @@ -46,6 +47,7 @@ import org.xtimms.ridebus.ui.base.activity.BaseActivity import org.xtimms.ridebus.ui.base.controller.* import org.xtimms.ridebus.ui.favourite.FavouritesController import org.xtimms.ridebus.ui.main.welcome.WelcomeDialogController +import org.xtimms.ridebus.ui.more.CriticalDatabaseUpdateDialogController import org.xtimms.ridebus.ui.more.MoreController import org.xtimms.ridebus.ui.more.NewScheduleDialogController import org.xtimms.ridebus.ui.more.NewUpdateDialogController @@ -62,7 +64,6 @@ import org.xtimms.ridebus.util.system.isTablet import org.xtimms.ridebus.util.system.logcat import org.xtimms.ridebus.util.system.toast import org.xtimms.ridebus.util.view.setNavigationBarTransparentCompat -import kotlin.collections.set class MainActivity : BaseActivity() { @@ -330,7 +331,11 @@ class MainActivity : BaseActivity() { private fun handleIntentAction(intent: Intent): Boolean { val notificationId = intent.getIntExtra("notificationId", -1) if (notificationId > -1) { - NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0)) + NotificationReceiver.dismissNotification( + applicationContext, + notificationId, + intent.getIntExtra("groupId", 0) + ) } isHandlingShortcut = true @@ -495,7 +500,11 @@ class MainActivity : BaseActivity() { Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED ) { - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1) + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.POST_NOTIFICATIONS), + 1 + ) } } diff --git a/app/src/main/java/org/xtimms/ridebus/ui/more/UpdateDialogControllers.kt b/app/src/main/java/org/xtimms/ridebus/ui/more/UpdateDialogControllers.kt index 70753be..3a0814c 100644 --- a/app/src/main/java/org/xtimms/ridebus/ui/more/UpdateDialogControllers.kt +++ b/app/src/main/java/org/xtimms/ridebus/ui/more/UpdateDialogControllers.kt @@ -34,7 +34,10 @@ class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundl .replace("""---(\R|.)*Checksums(\R|.)*""".toRegex(), "") val info = Markwon.create(activity!!).toMarkdown(releaseBody) - return MaterialAlertDialogBuilder(activity!!, materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered) + return MaterialAlertDialogBuilder( + activity!!, + materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered + ) .setTitle(R.string.update_check_notification_update_available) .setMessage(info) .setIcon(R.drawable.ic_update) @@ -88,7 +91,10 @@ class NewScheduleDialogController(bundle: Bundle? = null) : DialogController(bun append(infoBody) } - return MaterialAlertDialogBuilder(activity!!, materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered) + return MaterialAlertDialogBuilder( + activity!!, + materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered + ) .setTitle(R.string.update_check_notification_database_update_available) .setMessage(message) .setIcon(R.drawable.ic_database_update) @@ -103,7 +109,10 @@ class NewScheduleDialogController(bundle: Bundle? = null) : DialogController(bun .setNeutralButton(R.string.action_postpone) { _, _ -> dialog?.dismiss() applicationContext?.let { context -> - NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_DATABASE_UPDATER) + NotificationReceiver.dismissNotificationPendingBroadcast( + context, + Notifications.ID_DATABASE_UPDATER + ) } } .create() diff --git a/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsController.kt b/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsController.kt index 5b649c2..b272277 100644 --- a/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsController.kt +++ b/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsController.kt @@ -42,6 +42,7 @@ import org.xtimms.ridebus.util.preference.minusAssign import org.xtimms.ridebus.util.preference.plusAssign import org.xtimms.ridebus.util.system.logcat import org.xtimms.ridebus.util.system.toast +import org.xtimms.ridebus.util.view.snack import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy @@ -261,6 +262,10 @@ class RouteDetailsController : adapter.updateDataSet(stops) } + fun onError() { + binding.root.snack("No stops") + } + override fun onStopClick(position: Int) { val adapter = stopsAdapter val route = route?.routeId ?: return diff --git a/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsPresenter.kt b/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsPresenter.kt index c38e4a9..bbdbd7f 100644 --- a/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsPresenter.kt +++ b/app/src/main/java/org/xtimms/ridebus/ui/routes/details/RouteDetailsPresenter.kt @@ -1,12 +1,14 @@ package org.xtimms.ridebus.ui.routes.details import android.os.Bundle +import logcat.LogPriority import org.xtimms.ridebus.data.database.RideBusDatabase import org.xtimms.ridebus.data.database.entity.Route import org.xtimms.ridebus.data.database.entity.Stop import org.xtimms.ridebus.ui.base.presenter.BasePresenter import org.xtimms.ridebus.ui.routes.details.stop.StopOnRouteItem import org.xtimms.ridebus.util.Times +import org.xtimms.ridebus.util.system.logcat import rx.Observable import rx.Single import rx.Subscription @@ -14,7 +16,6 @@ import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.* class RouteDetailsPresenter( val route: Route, @@ -49,7 +50,15 @@ class RouteDetailsPresenter( ).map { times -> StopOnRouteItem(stop, times, index) } }.toSortedList() .observeOn(AndroidSchedulers.mainThread()) - .subscribeLatestCache(RouteDetailsController::onNextStops) + .subscribeLatestCache( + { view, it -> + view.onNextStops(it) + }, + { _, error -> + logcat(LogPriority.ERROR, error) + view?.onError() + } + ) } private fun getTimes(typeDay: Int?, stopId: Int): Single { diff --git a/app/src/main/java/org/xtimms/ridebus/ui/stops/details/RoutesOnStopController.kt b/app/src/main/java/org/xtimms/ridebus/ui/stops/details/RoutesOnStopController.kt index 3200f1e..ae86570 100644 --- a/app/src/main/java/org/xtimms/ridebus/ui/stops/details/RoutesOnStopController.kt +++ b/app/src/main/java/org/xtimms/ridebus/ui/stops/details/RoutesOnStopController.kt @@ -1,5 +1,9 @@ package org.xtimms.ridebus.ui.stops.details +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -7,6 +11,8 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import dev.chrisbanes.insetter.applyInsetter import eu.davidea.flexibleadapter.FlexibleAdapter +import java.text.SimpleDateFormat +import java.util.* import logcat.LogPriority import org.xtimms.ridebus.R import org.xtimms.ridebus.data.database.RideBusDatabase @@ -19,8 +25,6 @@ import org.xtimms.ridebus.ui.schedule.ScheduleTabbedController import org.xtimms.ridebus.util.system.logcat import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.text.SimpleDateFormat -import java.util.* class RoutesOnStopController : NucleusController, @@ -36,7 +40,9 @@ class RoutesOnStopController : this.stop = stop } - constructor(stopId: Int) : this(Injekt.get().stopDao().getStop(stopId).firstOrNull()) + constructor(stopId: Int) : this( + Injekt.get().stopDao().getStop(stopId).firstOrNull() + ) @Suppress("unused") constructor(bundle: Bundle) : this(bundle.getInt(STOP_EXTRA)) @@ -49,6 +55,8 @@ class RoutesOnStopController : */ private var adapter: RoutesOnStopAdapter? = null + private val timeReceiver = TimeReceiver() + override fun getTitle(): String { return "${stop?.name}" } @@ -57,7 +65,9 @@ class RoutesOnStopController : return "${stop?.direction}" } - override fun createBinding(inflater: LayoutInflater) = StopsRouteControllerBinding.inflate(inflater) + override fun createBinding(inflater: LayoutInflater) = StopsRouteControllerBinding.inflate( + inflater + ) override fun createPresenter(): RouteOnStopPresenter { return RouteOnStopPresenter(stop!!) @@ -93,7 +103,7 @@ class RoutesOnStopController : fun onNextRoute(routesOnStop: List) { val adapter = adapter ?: return hideProgressBar() - adapter.updateDataSet(routesOnStop) + adapter.updateDataSet(routesOnStop, false) } fun onNextRouteError(error: Throwable) { @@ -126,6 +136,24 @@ class RoutesOnStopController : binding.progress.isVisible = false } + override fun onAttach(view: View) { + super.onAttach(view) + view.context.registerReceiver(timeReceiver, IntentFilter(Intent.ACTION_TIME_TICK)) + adapter?.notifyDataSetChanged() + } + + override fun onDetach(view: View) { + view.context.registerReceiver(timeReceiver, IntentFilter(Intent.ACTION_TIME_TICK)) + super.onDetach(view) + } + + private inner class TimeReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context?, intent: Intent?) { + adapter?.notifyDataSetChanged() + } + } + companion object { const val STOP_EXTRA = "stop" } diff --git a/app/src/main/java/org/xtimms/ridebus/util/system/ContextExt.kt b/app/src/main/java/org/xtimms/ridebus/util/system/ContextExt.kt index 1bd2fe6..c6ad986 100644 --- a/app/src/main/java/org/xtimms/ridebus/util/system/ContextExt.kt +++ b/app/src/main/java/org/xtimms/ridebus/util/system/ContextExt.kt @@ -51,7 +51,11 @@ private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720 * @param resource the text resource. * @param duration the duration of the toast. Defaults to short. */ -fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast { +fun Context.toast( + @StringRes resource: Int, + duration: Int = Toast.LENGTH_SHORT, + block: (Toast) -> Unit = {} +): Toast { return toast(getString(resource), duration, block) } @@ -61,7 +65,12 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, * @param text the text to display. * @param duration the duration of the toast. Defaults to short. */ -fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast { +fun Context.toast( + text: String?, + duration: Int = Toast.LENGTH_SHORT, + block: (Toast) -> Unit = { + } +): Toast { return Toast.makeText(this, text.orEmpty(), duration).also { block(it) it.show() @@ -94,7 +103,9 @@ fun Context.openInBrowser(uri: Uri, @ColorInt toolbarColor: Int? = null) { val intent = CustomTabsIntent.Builder() .setDefaultColorSchemeParams( CustomTabColorSchemeParams.Builder() - .setToolbarColor(toolbarColor ?: getResourceColor(androidx.appcompat.R.attr.colorPrimary)) + .setToolbarColor( + toolbarColor ?: getResourceColor(androidx.appcompat.R.attr.colorPrimary) + ) .build() ) .build() @@ -104,9 +115,11 @@ fun Context.openInBrowser(uri: Uri, @ColorInt toolbarColor: Int? = null) { } } -fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder { +fun Context.notificationBuilder( + channelId: String, + block: (NotificationCompat.Builder.() -> Unit)? = null +): NotificationCompat.Builder { val builder = NotificationCompat.Builder(this, channelId) - .setColor(ContextCompat.getColor(this, R.color.accent_blue)) if (block != null) { builder.block() } @@ -204,7 +217,11 @@ fun Context.prepareTabletUiContext(): Context { * @see Settings.Global.ANIMATOR_DURATION_SCALE */ val Context.animatorDurationScale: Float - get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) + get() = Settings.Global.getFloat( + this.contentResolver, + Settings.Global.ANIMATOR_DURATION_SCALE, + 1f + ) /** * Convenience method to acquire a partial wake lock. diff --git a/app/src/main/java/org/xtimms/ridebus/widget/ArrivalTimeView.kt b/app/src/main/java/org/xtimms/ridebus/widget/ArrivalTimeView.kt index 521e7ef..54c48c3 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/ArrivalTimeView.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/ArrivalTimeView.kt @@ -47,7 +47,9 @@ class ArrivalTimeView @JvmOverloads constructor( } else { val relative = time - now binding.remainingTime.text = when { - relative.hours == 0 && relative.minutes == 0 -> context.getString(R.string.token_time_now) + relative.hours == 0 && relative.minutes == 0 -> context.getString( + R.string.token_time_now + ) relative.hours == 0 -> context.getString( R.string.reltime_minutes, relative.minutes diff --git a/app/src/main/java/org/xtimms/ridebus/widget/EmptyView.kt b/app/src/main/java/org/xtimms/ridebus/widget/EmptyView.kt index 2c2b5e3..bd1a543 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/EmptyView.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/EmptyView.kt @@ -43,7 +43,9 @@ class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? binding.actionsContainer.removeAllViews() val buttonContext = ContextThemeWrapper(context, R.style.Widget_RideBus_Button_ActionButton) - val buttonColor = ColorStateList.valueOf(context.getThemeColor(com.google.android.material.R.attr.colorOnBackground)) + val buttonColor = ColorStateList.valueOf( + context.getThemeColor(com.google.android.material.R.attr.colorOnBackground) + ) actions?.forEach { val button = MaterialButton( buttonContext, diff --git a/app/src/main/java/org/xtimms/ridebus/widget/ExtendedNavigationView.kt b/app/src/main/java/org/xtimms/ridebus/widget/ExtendedNavigationView.kt index 1ac5eef..9eccc45 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/ExtendedNavigationView.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/ExtendedNavigationView.kt @@ -41,25 +41,44 @@ open class ExtendedNavigationView @JvmOverloads constructor( /** * A checkbox. */ - open class Checkbox(val resTitle: Int, var checked: Boolean = false, var enabled: Boolean = true) : Item() + open class Checkbox( + val resTitle: Int, + var checked: Boolean = false, + var enabled: Boolean = true + ) : Item() /** * A checkbox belonging to a group. The group must handle selections and restrictions. */ - class CheckboxGroup(resTitle: Int, override val group: Group, checked: Boolean = false, enabled: Boolean = true) : + class CheckboxGroup( + resTitle: Int, + override val group: Group, + checked: Boolean = false, + enabled: Boolean = true + ) : Checkbox(resTitle, checked, enabled), GroupedItem /** * A radio belonging to a group (a sole radio makes no sense). The group must handle * selections and restrictions. */ - class Radio(val resTitle: Int, override val group: Group, var checked: Boolean = false, var enabled: Boolean = true) : + class Radio( + val resTitle: Int, + override val group: Group, + var checked: Boolean = false, + var enabled: Boolean = true + ) : Item(), GroupedItem /** * An item with which needs more than two states (selected/deselected). */ - abstract class MultiState(val resTitle: Int, var state: Int = 0, var enabled: Boolean = true, var isVisible: Boolean = true) : Item() { + abstract class MultiState( + val resTitle: Int, + var state: Int = 0, + var enabled: Boolean = true, + var isVisible: Boolean = true + ) : Item() { /** * Returns the drawable associated to every possible each state. @@ -72,9 +91,17 @@ open class ExtendedNavigationView @JvmOverloads constructor( * @param context any context. * @param resId the vector resource to load and tint */ - fun tintVector(context: Context, resId: Int, @AttrRes colorAttrRes: Int = com.google.android.material.R.attr.colorPrimary): Drawable { + fun tintVector( + context: Context, + resId: Int, + @AttrRes colorAttrRes: Int = com.google.android.material.R.attr.colorPrimary + ): Drawable { return AppCompatResources.getDrawable(context, resId)!!.apply { - setTint(context.getResourceColor(if (enabled) colorAttrRes else com.google.android.material.R.attr.colorControlNormal)) + setTint( + context.getResourceColor( + if (enabled) colorAttrRes else com.google.android.material.R.attr.colorControlNormal + ) + ) } } } @@ -83,7 +110,12 @@ open class ExtendedNavigationView @JvmOverloads constructor( * An item with which needs more than two states (selected/deselected) belonging to a group. * The group must handle selections and restrictions. */ - abstract class MultiStateGroup(resTitle: Int, override val group: Group, state: Int = 0, enabled: Boolean = true) : + abstract class MultiStateGroup( + resTitle: Int, + override val group: Group, + state: Int = 0, + enabled: Boolean = true + ) : MultiState(resTitle, state, enabled), GroupedItem /** @@ -101,7 +133,10 @@ open class ExtendedNavigationView @JvmOverloads constructor( return when (state) { SORT_ASC -> tintVector(context, R.drawable.ic_arrow_up) SORT_DESC -> tintVector(context, R.drawable.ic_arrow_down) - SORT_NONE -> AppCompatResources.getDrawable(context, R.drawable.empty_drawable_32dp) + SORT_NONE -> AppCompatResources.getDrawable( + context, + R.drawable.empty_drawable_32dp + ) else -> null } } @@ -120,7 +155,11 @@ open class ExtendedNavigationView @JvmOverloads constructor( override fun getStateDrawable(context: Context): Drawable? { return when (state) { - State.IGNORE.value -> tintVector(context, R.drawable.ic_check_box_outline_blank, com.google.android.material.R.attr.colorControlNormal) + State.IGNORE.value -> tintVector( + context, + R.drawable.ic_check_box_outline_blank, + com.google.android.material.R.attr.colorControlNormal + ) State.INCLUDE.value -> tintVector(context, R.drawable.ic_check_box) State.EXCLUDE.value -> tintVector(context, R.drawable.ic_check_box_x) else -> throw Exception("Unknown state") diff --git a/app/src/main/java/org/xtimms/ridebus/widget/HideBottomNavigationOnScrollBehavior.kt b/app/src/main/java/org/xtimms/ridebus/widget/HideBottomNavigationOnScrollBehavior.kt index 0860e9a..980b8fa 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/HideBottomNavigationOnScrollBehavior.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/HideBottomNavigationOnScrollBehavior.kt @@ -27,7 +27,11 @@ class HideBottomNavigationOnScrollBehavior @JvmOverloads constructor( private var dyRatio = 1F - override fun layoutDependsOn(parent: CoordinatorLayout, child: BottomNavigationView, dependency: View): Boolean { + override fun layoutDependsOn( + parent: CoordinatorLayout, + child: BottomNavigationView, + dependency: View + ): Boolean { return dependency is AppBarLayout } @@ -71,7 +75,10 @@ class HideBottomNavigationOnScrollBehavior @JvmOverloads constructor( type: Int ) { super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type) - child.translationY = (child.translationY + (dy * dyRatio)).coerceIn(0F, child.height.toFloat()) + child.translationY = (child.translationY + (dy * dyRatio)).coerceIn( + 0F, + child.height.toFloat() + ) } override fun onStopNestedScroll( diff --git a/app/src/main/java/org/xtimms/ridebus/widget/preference/ActivityListPreference.kt b/app/src/main/java/org/xtimms/ridebus/widget/preference/ActivityListPreference.kt index a0a3c5e..7cbae94 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/preference/ActivityListPreference.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/preference/ActivityListPreference.kt @@ -17,7 +17,11 @@ class ActivityListPreference : ListPreference { defStyleRes: Int ) : super(context, attrs, defStyleAttr, defStyleRes) - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context) : super(context) diff --git a/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreference.kt b/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreference.kt index 2ac80ec..4469de0 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreference.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreference.kt @@ -36,7 +36,11 @@ class ThemesPreference @JvmOverloads constructor(context: Context, attrs: Attrib super.onBindViewHolder(holder) recycler = holder.findViewById(R.id.themes_list) as RecyclerView - recycler?.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + recycler?.layoutManager = LinearLayoutManager( + context, + LinearLayoutManager.HORIZONTAL, + false + ) recycler?.adapter = adapter // Retain scroll position on activity recreate after changing theme diff --git a/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreferenceAdapter.kt b/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreferenceAdapter.kt index 1a26e07..af37125 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreferenceAdapter.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/preference/ThemesPreferenceAdapter.kt @@ -5,7 +5,6 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.view.ContextThemeWrapper import androidx.recyclerview.widget.RecyclerView -import org.xtimms.ridebus.R import org.xtimms.ridebus.data.preference.PreferenceValues import org.xtimms.ridebus.data.preference.PreferencesHelper import org.xtimms.ridebus.databinding.PrefThemeItemBinding @@ -29,7 +28,10 @@ class ThemesPreferenceAdapter(private val clickListener: OnItemClickListener) : parent: ViewGroup, viewType: Int ): ThemeViewHolder { - val themeResIds = ThemingDelegate.getThemeResIds(themes[viewType], preferences.themeDarkAmoled().get()) + val themeResIds = ThemingDelegate.getThemeResIds( + themes[viewType], + preferences.themeDarkAmoled().get() + ) val themedContext = themeResIds.fold(parent.context) { context, themeResId -> ContextThemeWrapper(context, themeResId) @@ -54,7 +56,9 @@ class ThemesPreferenceAdapter(private val clickListener: OnItemClickListener) : inner class ThemeViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { - private val selectedColor = view.context.getResourceColor(androidx.appcompat.R.attr.colorAccent) + private val selectedColor = view.context.getResourceColor( + androidx.appcompat.R.attr.colorAccent + ) private val unselectedColor = view.context.getResourceColor(android.R.attr.textColorHint) fun bind(appTheme: PreferenceValues.AppTheme) { diff --git a/app/src/main/java/org/xtimms/ridebus/widget/sheet/TabbedBottomSheetDialog.kt b/app/src/main/java/org/xtimms/ridebus/widget/sheet/TabbedBottomSheetDialog.kt index 7d81241..442561f 100644 --- a/app/src/main/java/org/xtimms/ridebus/widget/sheet/TabbedBottomSheetDialog.kt +++ b/app/src/main/java/org/xtimms/ridebus/widget/sheet/TabbedBottomSheetDialog.kt @@ -7,7 +7,8 @@ import android.view.ViewGroup import org.xtimms.ridebus.databinding.CommonTabbedSheetBinding import org.xtimms.ridebus.widget.ViewPagerAdapter -abstract class TabbedBottomSheetDialog(private val activity: Activity) : BaseBottomSheetDialog(activity) { +abstract class TabbedBottomSheetDialog(private val activity: Activity) : + BaseBottomSheetDialog(activity) { lateinit var binding: CommonTabbedSheetBinding diff --git a/app/src/main/res/layout/routes_detail_controller.xml b/app/src/main/res/layout/routes_detail_controller.xml index faa4ea8..975cd72 100644 --- a/app/src/main/res/layout/routes_detail_controller.xml +++ b/app/src/main/res/layout/routes_detail_controller.xml @@ -11,7 +11,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" - tools:listitem="@layout/stops_item" /> + tools:listitem="@layout/route_detail_stop_item" /> Расписание Карта Резервное копирование и восстановление - Автоматическое обновление расписания - Позволяет получать новое расписание, когда оно доступно + Оповещать о наличии новой версии расписания + Вам придёт уведомление, если будет доступна новая версия базы данных расписания Остановки рядом Помощь Полоцк @@ -226,4 +226,5 @@ По воскресеньям По пятницам С понедельника по четверг + Условия использования Яндекс.Карт \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 83402e5..d53dcf2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,8 +57,8 @@ Schedule Map Backup and restore - Automatic schedule updates - Allows to get the new schedule when it is available + Notify about the availability of a new version of the schedule + You will receive a notification if a new version of the schedule database is available Stops nearby Help Polotsk @@ -240,6 +240,9 @@ Remote server error Network error Invalid API key + Yandex.Maps Terms of Use + The database structure has changed in this version + To continue working, you need to download a compatible database from the server. This action cannot be skipped. Done in %1$s with %2$s error Done in %1$s with %2$s errors diff --git a/app/standard/release/output-metadata.json b/app/standard/release/output-metadata.json index 284ed32..76b6bec 100644 --- a/app/standard/release/output-metadata.json +++ b/app/standard/release/output-metadata.json @@ -20,52 +20,52 @@ "filters": [ { "filterType": "ABI", - "value": "x86" + "value": "armeabi-v7a" } ], "attributes": [], "versionCode": 3, "versionName": "0.3", - "outputFile": "app-standard-x86-release.apk" + "outputFile": "app-standard-armeabi-v7a-release.apk" }, { "type": "ONE_OF_MANY", "filters": [ { "filterType": "ABI", - "value": "armeabi-v7a" + "value": "arm64-v8a" } ], "attributes": [], "versionCode": 3, "versionName": "0.3", - "outputFile": "app-standard-armeabi-v7a-release.apk" + "outputFile": "app-standard-arm64-v8a-release.apk" }, { "type": "ONE_OF_MANY", "filters": [ { "filterType": "ABI", - "value": "x86_64" + "value": "x86" } ], "attributes": [], "versionCode": 3, "versionName": "0.3", - "outputFile": "app-standard-x86_64-release.apk" + "outputFile": "app-standard-x86-release.apk" }, { "type": "ONE_OF_MANY", "filters": [ { "filterType": "ABI", - "value": "arm64-v8a" + "value": "x86_64" } ], "attributes": [], "versionCode": 3, "versionName": "0.3", - "outputFile": "app-standard-arm64-v8a-release.apk" + "outputFile": "app-standard-x86_64-release.apk" } ], "elementType": "File" diff --git a/build.gradle.kts b/build.gradle.kts index a871f91..1630a17 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,4 +34,4 @@ buildscript { tasks.register("clean", Delete::class) { delete(rootProject.buildDir) -} \ No newline at end of file +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 88cd14f..aebe261 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -3,4 +3,4 @@ plugins { } repositories { mavenCentral() -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index 083d411..0cade14 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/settings.gradle.kts b/settings.gradle.kts index 539fd82..cd2761c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,4 +17,4 @@ pluginManagement { } rootProject.name = "RideBus Rewrite" -include(":app") \ No newline at end of file +include(":app")