diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7687510..d4c5c90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,45 +14,31 @@ jobs: shell: bash steps: - - uses: actions/checkout@v2 - - name: set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: '11' - distribution: 'adopt' - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Get Extra files - uses: prewk/s3-cp-action@v1 - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_REGION: 'eu-west-1' - SOURCE: 's3://splitbus/gmaps.zip' - DEST: './app/src/main/java/com/am/stbus/presentation/screens' - - - name: Unzip extra files - run: | - echo "Poceo unzip" - cd app/src/main/java/com/am/stbus/presentation/screens - ls - unzip gmaps.zip - echo "Gotov unzip" - - - name: Add Google services JSON - run: | - cd app - ls - echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > google-services.json - echo "Gotov Google services JSON" - - - name: Update gradleProperties - id: createServiceAccount - run: | - echo "first message" - echo '${{ secrets.GRADLE_PROPERTIES }}' > gradle.properties - - - name: Build with Gradle - run: ./gradlew buildRelease + - uses: actions/checkout@v3 + - name: Setup JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Set up Android SDK + uses: android-actions/setup-android@v2 + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Add Google services JSON + run: | + cd app + ls + echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > google-services.json + echo "Gotov Google services JSON" + + - name: Update secrets + id: updateSecrets + run: | + echo "Updating Secrets.kt" + echo '${{ secrets.SECRETS }}' > app/src/main/java/com/am/stbus/common/Secrets.kt + + - name: Build with Gradle + run: ./gradlew buildRelease diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml index 41e5727..13294a8 100644 --- a/.github/workflows/upload.yml +++ b/.github/workflows/upload.yml @@ -14,70 +14,56 @@ jobs: shell: bash steps: - - uses: actions/checkout@v2 - - name: set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: '11' - distribution: 'adopt' + - uses: actions/checkout@v3 + - name: Setup JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - name: Set up Android SDK + uses: android-actions/setup-android@v2 - - name: Get Extra files - uses: prewk/s3-cp-action@v1 - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_REGION: 'eu-west-1' - SOURCE: 's3://splitbus/gmaps.zip' - DEST: './app/src/main/java/com/am/stbus/presentation/screens' + - name: Grant execute permission for gradlew + run: chmod +x gradlew - - name: Unzip extra files - run: | - echo "Poceo unzip" - cd app/src/main/java/com/am/stbus/presentation/screens - ls - unzip gmaps.zip - echo "Gotov unzip" + - name: Add Google services JSON + run: | + cd app + ls + echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > google-services.json + echo "Gotov Google services JSON" - - name: Add Google services JSON - run: | - cd app - ls - echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > google-services.json - echo "Gotov Google services JSON" + - name: Update secrets + id: updateSecrets + run: | + echo "Updating Secrets.kt" + echo '${{ secrets.SECRETS }}' > app/src/main/java/com/am/stbus/common/Secrets.kt - - name: Update gradleProperties - id: gradleProperties - run: | - echo "first message" - echo '${{ secrets.GRADLE_PROPERTIES }}' > gradle.properties + - name: Build Release AAB + id: buildRelease + run: ./gradlew bundleRelease - - name: Build Release AAB - id: buildRelease - run: ./gradlew bundleRelease + - name: Sign AAB + id: sign + uses: r0adkll/sign-android-release@v1 + with: + releaseDirectory: app/build/outputs/bundle/release + signingKeyBase64: ${{ secrets.KEY_SIGNING }} + alias: ${{ secrets.KEY_ALIAS }} + keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} + keyPassword: ${{ secrets.KEY_PASSWORD }} - - name: Sign AAB - id: sign - uses: r0adkll/sign-android-release@v1 - with: - releaseDirectory: app/build/outputs/bundle/release - signingKeyBase64: ${{ secrets.KEY_SIGNING }} - alias: ${{ secrets.KEY_ALIAS }} - keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} - keyPassword: ${{ secrets.KEY_PASSWORD }} + - name: Create service_account.json + id: createServiceAccount + run: echo '${{ secrets.SERVICE_ACCOUNT_JSON }}' > service_account.json - - name: Create service_account.json - id: createServiceAccount - run: echo '${{ secrets.SERVICE_ACCOUNT_JSON }}' > service_account.json - - - name: Deploy to Play Store (BETA) - id: deploy - uses: r0adkll/upload-google-play@v1 - with: - serviceAccountJson: service_account.json - packageName: com.am.stbus - releaseFiles: app/build/outputs/bundle/release/app-release.aab - track: internal - status: completed \ No newline at end of file + - name: Deploy to Play Store (BETA) + id: deploy + uses: r0adkll/upload-google-play@v1 + with: + serviceAccountJson: service_account.json + packageName: com.am.stbus + releaseFiles: app/build/outputs/bundle/release/app-release.aab + track: internal + status: completed \ No newline at end of file diff --git a/.gitignore b/.gitignore index fe433ff..b3ab340 100644 --- a/.gitignore +++ b/.gitignore @@ -1,82 +1,54 @@ -# Built application files -*.apk -*.ap_ - -# Files for the ART/Dalvik VM -*.dex - -# Java class files -*.class - -# Generated files -bin/ -gen/ -out/ - # Gradle files .gradle/ build/ +**/build/ # Local configuration file (sdk path, etc) local.properties +Secrets.kt -# Proguard folder generated by Eclipse -proguard/ - -# Log Files +# Log files *.log -# Android Studio Navigation editor temp files -.navigation/ - -# Android Studio captures folder -captures/ - -# Intellij +# Android Studio +.idea/ *.iml -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/dictionaries -.idea/libraries -# Android Studio 3 in .gitignore file. -.idea/caches -.idea/modules.xml -# Comment next line if keeping position of elements in Navigation Editor is relevant for you -.idea/navEditor.xml - -# Keystore files -*.jks - -# External native build folder generated in Android Studio 2.2 and later -.externalNativeBuild +*.ipr +*.iws +out/ -# Google Services (e.g. APIs or Firebase) -google-services.json +# Kotlin and Java compiled files +*.class +*.jar +*.war +*.ear +*.dex -# Freeline -freeline.py -freeline/ -freeline_project_description.json +# Generated files +gen/ +bin/ +captures/ +output.json -# Fastlane -fastlane/ -Gemfile -Gemfile.lock +# IntelliJ +*.hprof -# Android -release/ -release/res/values/ +# Gradle caches and wrapper +!gradle/wrapper/gradle-wrapper.jar -# Podatci za Google Maps -gmaps/ +# Signing keys (don’t commit these!) +*.keystore +*.jks -# Gmaps config -gradle.properties +# NDK / CMake / Native build +.externalNativeBuild/ +.cxx/ -# Changelog generator -changelog.sh +# OS-specific +.DS_Store +Thumbs.db -/.idea/deploymentTargetDropDown.xml -/.idea/kotlinc.xml +# Others +*.apk +*.ap_ +*.aab diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 88ea3aa..3b25318 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,5 +1,39 @@ + + + diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b589d56..b86273d 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index b946f4b..bd6abc9 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -7,7 +7,7 @@ - + diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 2d27d91..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,215 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'kotlin-kapt' -apply plugin: 'androidx.navigation.safeargs.kotlin' -apply plugin: 'com.google.gms.google-services' -apply plugin: 'com.google.firebase.crashlytics' -apply plugin: 'com.google.android.gms.oss-licenses-plugin' - -android { - compileSdkVersion 33 - - defaultConfig { - applicationId "com.am.stbus" - minSdkVersion 19 - targetSdkVersion 33 - versionCode 80 - versionName "3.2.3" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - android.defaultConfig.vectorDrawables.useSupportLibrary = true - multiDexEnabled true - } - - buildTypes { - debug { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - buildConfigField "java.util.Date", "BUILD_TIME", "new java.util.Date(" + System.currentTimeMillis() + "L)" - resValue 'string', "api_key", gmaps_debug_key - applicationIdSuffix ".dev" - versionNameSuffix "." + "-dev" - } - - release { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - buildConfigField "java.util.Date", "BUILD_TIME", "new java.util.Date(" + System.currentTimeMillis() + "L)" - resValue 'string', "api_key", gmaps_release_key - } - - } - - - compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - } - - kotlinOptions { - jvmTarget = "1.8" - } - lint { - abortOnError false - checkReleaseBuilds false - } - - //namespace 'com.am.stbus' - -} - -ext { - // AndroidX - appcompat_version = "1.4.1" - core_ktx_version = "1.7.0" - recyclerview_version = "1.2.1" - browser_version = "1.4.0" - swipe_refresh_version = "1.1.0" - constraint_layout_version = "2.1.4" - vector_drawable_version = "1.0.0" - preference_ktx_version = "1.2.0" - - // Navigation - nav_version_ktx_version = "2.4.2" - - // ViewModel and LiveData - lifecycle_version = "2.2.0" - - // Room - room_version = "2.4.2" - - // Gmaps - play_services_maps_version = "18.0.2" - play_services_location_version = "19.0.1" - - // Firebase Analytics - firebase_analytics_version = "21.0.0" - firebase_crashlytics_version = "18.2.11" - - // Material - material_components_version = "1.7.0-alpha02" - - // Koin - koin_version = "3.2.0" - - // RxF - rx_android_version = "2.1.1" - rx_kotlin_version = "2.3.0" - - // Jsoup - jsoup_version = "1.15.1" - - // ThreeTen - three_ten_abp_version = "1.4.0" - - // Timber - timber_version = "5.0.1" - - // Glide - glide_version = "4.13.0" - - // Photo view - photoview_version = "2.3.0" - - // licenses - licenses_version = "17.0.0" -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21" - // implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - - // Testing - implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.5.0-alpha04' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha07' - - // AndroidX - implementation "androidx.appcompat:appcompat:$appcompat_version" - implementation "androidx.core:core-ktx:$core_ktx_version" - implementation "androidx.recyclerview:recyclerview:$recyclerview_version" - implementation "androidx.browser:browser:$browser_version" - implementation "androidx.swiperefreshlayout:swiperefreshlayout:$swipe_refresh_version" - implementation "androidx.constraintlayout:constraintlayout:$constraint_layout_version" - implementation "androidx.vectordrawable:vectordrawable:$vector_drawable_version" - implementation "androidx.preference:preference-ktx:$preference_ktx_version" - - // Navigation - implementation "androidx.navigation:navigation-fragment-ktx:$nav_version_ktx_version" - implementation "androidx.navigation:navigation-ui-ktx:$nav_version_ktx_version" - - // ViewModel and LiveData - implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" - kapt "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" - - // Room - implementation "androidx.room:room-runtime:$room_version" - kapt "androidx.room:room-compiler:$room_version" - implementation "androidx.room:room-ktx:$room_version" - implementation "androidx.room:room-rxjava2:$room_version" - - // Gmaps - implementation "com.google.android.gms:play-services-maps:$play_services_maps_version" - implementation "com.google.android.gms:play-services-location:$play_services_location_version" - - // Firebase Analytics - implementation "com.google.firebase:firebase-analytics:$firebase_analytics_version" - implementation "com.google.firebase:firebase-crashlytics:$firebase_crashlytics_version" - - // Material Components - implementation "com.google.android.material:material:$material_components_version" - - // Koin - implementation "io.insert-koin:koin-core:$koin_version" - implementation "io.insert-koin:koin-android:$koin_version" - implementation "io.insert-koin:koin-androidx-navigation:$koin_version" - - // Rx - implementation "io.reactivex.rxjava2:rxandroid:$rx_android_version" - implementation "io.reactivex.rxjava2:rxkotlin:$rx_kotlin_version" - - // Jsoup - implementation "org.jsoup:jsoup:$jsoup_version" - - // ThreeTen - implementation "com.jakewharton.threetenabp:threetenabp:$three_ten_abp_version" - - // Timber for Logging - implementation "com.jakewharton.timber:timber:$timber_version" - - // Glide - implementation "com.github.bumptech.glide:glide:$glide_version" - kapt "com.github.bumptech.glide:compiler:$glide_version" - - // PhotoView - implementation "com.github.chrisbanes:PhotoView:$photoview_version" - - // Libraries - implementation "com.google.android.gms:play-services-oss-licenses:$licenses_version" -} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..a3ab5ca --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,107 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) + alias(libs.plugins.jetbrains.kotlin.serialization) + alias(libs.plugins.ksp) +} + +android { + namespace = "com.am.stbus" + compileSdk = 36 + + defaultConfig { + applicationId = "com.am.stbus" + minSdk = 23 + targetSdk = 36 + versionCode = 81 + versionName = "4.0.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + debug { + isMinifyEnabled = false + applicationIdSuffix = ".dev" + versionNameSuffix = "." + "-dev" + } + release { + isMinifyEnabled = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + + //implementation(libs.androidx.navigation3) + implementation(libs.androidx.lifecycle.viewmodel.navigation3) + implementation(libs.kotlinx.serialization.core) + + implementation(libs.kotlinx.coroutines.android) + + implementation(libs.room.runtime) + ksp(libs.room.compiler) + implementation(libs.room.ktx) + + implementation(platform(libs.koin.bom)) + implementation(libs.koin.android) + implementation(libs.koin.compose) + implementation(libs.koin.core.coroutines) + implementation(libs.koin.androidx.workmanager) + + implementation(libs.jakewharton.timber) + implementation(libs.jakewharton.threetenabp) + implementation(libs.squareup.retrofit) + implementation(libs.squareup.retrofit.serialization) + + + /* + implementation(libs.androidx.material3.windowsizeclass) + implementation(libs.androidx.adaptive.layout) + implementation(libs.androidx.material3.navigation3) + */ + + implementation(libs.kotlinx.serialization.core) + implementation(libs.kotlinx.serialization.json) + implementation(libs.androidx.navigation3.runtime) + implementation(libs.androidx.navigation3.ui) + implementation(libs.androidx.lifecycle.viewmodel.navigation3) + implementation(libs.androidx.material.icons.extended) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.jsoup) + implementation(libs.androidx.navigation3.runtime) + + testImplementation(libs.junit) + testImplementation(libs.kotlinx.coroutines.test) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) + testImplementation(kotlin("test")) +} \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..abf62a2 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,91 @@ +{ + "project_info": { + "project_number": "123", + "firebase_url": "https://android-app.firebaseio.com", + "project_id": "android-app", + "storage_bucket": "test.com," + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "123", + "android_client_info": { + "package_name": "com.am.stbus" + } + }, + "oauth_client": [ + { + "client_id": "123", + "client_type": 1, + "android_info": { + "package_name": "com.am.stbus", + "certificate_hash": "456" + } + }, + { + "client_id": "123.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "456" + }, + { + "current_key": "789" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "123.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "123", + "android_client_info": { + "package_name": "com.am.stbus.dev" + } + }, + "oauth_client": [ + { + "client_id": "123.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.am.stbus.dev", + "certificate_hash": "345" + } + }, + { + "client_id": "123.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "456" + }, + { + "current_key": "789" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "12345.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3108668..3ecf819 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,8 +25,7 @@ --> + xmlns:tools="http://schemas.android.com/tools"> @@ -38,7 +37,6 @@ - + + android:value="@integer/google_play_services_version" /> <!– Fixes crash for Android 9+ users –> + --> diff --git a/app/src/main/java/com/am/stbus/SplitBusApplication.kt b/app/src/main/java/com/am/stbus/SplitBusApplication.kt index e8e1b8a..ac5d1a3 100644 --- a/app/src/main/java/com/am/stbus/SplitBusApplication.kt +++ b/app/src/main/java/com/am/stbus/SplitBusApplication.kt @@ -25,7 +25,11 @@ package com.am.stbus import android.app.Application -import com.am.stbus.common.di.* +import com.am.stbus.common.di.appModule +import com.am.stbus.common.di.networkModule +import com.am.stbus.common.di.repositoryModule +import com.am.stbus.common.di.useCaseModule +import com.am.stbus.common.di.viewModelModule import com.jakewharton.threetenabp.AndroidThreeTen import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -33,13 +37,21 @@ import timber.log.Timber class SplitBusApplication : Application() { +/* private var listOfModules = listOf( + applicationModule, + localModules, + networkModules, + repositoriesModule, + useCaseModule, + viewModelModule + )*/ + private var listOfModules = listOf( - applicationModule, - localModules, - networkModules, - repositoriesModule, - useCaseModule, - viewModelModule + appModule, + networkModule, + repositoryModule, + useCaseModule, + viewModelModule ) override fun onCreate() { @@ -57,9 +69,11 @@ class SplitBusApplication : Application() { } private fun setupTimber() { - if (BuildConfig.DEBUG) { - Timber.plant(Timber.DebugTree()) - } + Timber.plant(Timber.DebugTree()) + + /* if (BuildConfig.DEBUG) { + Timber.plant(Timber.DebugTree()) + }*/ } private fun setupThreeTen() { diff --git a/app/src/main/java/com/am/stbus/common/Constants.kt b/app/src/main/java/com/am/stbus/common/Constants.kt index 53d2adc..48a35d3 100644 --- a/app/src/main/java/com/am/stbus/common/Constants.kt +++ b/app/src/main/java/com/am/stbus/common/Constants.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,10 +26,14 @@ package com.am.stbus.common object Constants { - const val DB_VERSION = 5 + const val DB_VERSION = 1 const val PROMET_URL = "http://www.promet-split.hr" const val PROMET_NOVOSTI_URL = "http://www.promet-split.hr/obavijesti" + const val PROMET_API_URL = "https://api.promet-split.hr/Fleet/api/v1/" + const val PROMET_ALL_LINES_URL = "$PROMET_URL/vozni-red/sve-linije/" + const val PROMET_ALL_LINE_ID_URL = "$PROMET_URL/vozni-red/sve-linije/linijaid/" + // Timetable Base URLs const val AREA_CITY_URL = "$PROMET_URL/vozni-red/split/" @@ -78,17 +82,20 @@ object InformationConstants { const val ID_PROMET_WEB = 12 // Informacije - const val KARTA_GRAD_URL = "https://splitbuspublic.s3.eu-central-1.amazonaws.com/public/grad.jpg" - const val KARTA_PRIGRAD_URL = "https://splitbuspublic.s3.eu-central-1.amazonaws.com/public/prigrad.jpg" - const val TARIFNE_URL = "https://splitbuspublic.s3.eu-central-1.amazonaws.com/public/tarifne.jpg" + const val KARTA_GRAD_URL = + "https://splitbuspublic.s3.eu-central-1.amazonaws.com/public/grad.jpg" + const val KARTA_PRIGRAD_URL = + "https://splitbuspublic.s3.eu-central-1.amazonaws.com/public/prigrad.jpg" + const val TARIFNE_URL = + "https://splitbuspublic.s3.eu-central-1.amazonaws.com/public/tarifne.jpg" const val NEXT_BIKE_URL = "https://www.nextbike.hr/hr/split/lokacije/" const val PARKING_URL = "http://www.splitparking.hr/parkiralista" const val GARAZE_URL = "http://www.splitparking.hr/garaze" - const val NEXT_BIKE_IFRAME = "" - -} + const val NEXT_BIKE_IFRAME = + "" +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/models/GmapsBusStop.kt b/app/src/main/java/com/am/stbus/common/Secrets.kt similarity index 87% rename from app/src/main/java/com/am/stbus/domain/models/GmapsBusStop.kt rename to app/src/main/java/com/am/stbus/common/Secrets.kt index f1aad42..ac02ad4 100644 --- a/app/src/main/java/com/am/stbus/domain/models/GmapsBusStop.kt +++ b/app/src/main/java/com/am/stbus/common/Secrets.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,8 @@ * SOFTWARE. */ -package com.am.stbus.domain.models +package com.am.stbus.common -data class GmapsBusStop(var title: String, var busLinesArray: ArrayList) \ No newline at end of file +val PROMET_KEY = "placeholder" +val GMAPS_DEBUG_KEY = "test" +val GMAPS_RELEASE_KEY = "test2" \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/TimetablesData.kt b/app/src/main/java/com/am/stbus/common/TimetablesData.kt deleted file mode 100644 index e83e11c..0000000 --- a/app/src/main/java/com/am/stbus/common/TimetablesData.kt +++ /dev/null @@ -1,441 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.common - -import com.am.stbus.R -import com.am.stbus.domain.models.Timetable - -class TimetablesData { - - fun getTimetableTitle(lineId: Int): Int = - when (lineId) { - // Gradski - 1 -> R.string.bus32 - 2 -> R.string.bus31 - 3 -> R.string.bus6 - 4 -> R.string.bus71 - 5 -> R.string.bus72 - 6 -> R.string.bus81 - 7 -> R.string.bus82 - 8 -> R.string.bus9 - 9 -> R.string.bus111 - 10 -> R.string.bus112 - 11 -> R.string.bus121 - 12 -> R.string.bus122 - 13 -> R.string.bus141 - 14 -> R.string.bus142 - 15 -> R.string.bus15 - 16 -> R.string.bus171 - 17 -> R.string.bus172 - 18 -> R.string.bus18 - 19 -> R.string.bus20 - 20 -> R.string.bus21 - 21 -> R.string.bus241 - 22 -> R.string.bus242 - 23 -> R.string.bus251 - 24 -> R.string.bus252 - 25 -> R.string.bus261 - 26 -> R.string.bus262 - 27 -> R.string.bus271 - 28 -> R.string.bus272 - 29 -> R.string.bus39 - 30 -> R.string.bus40 - - - // Urbano - 101 -> R.string.bus1 - 102 -> R.string.bus021 - 103 -> R.string.bus022 - 104 -> R.string.bus023 - 105 -> R.string.bus5 - 106 -> R.string.bus51 - 107 -> R.string.bus10 - 108 -> R.string.bus16 - 109 -> R.string.bus22 - 110 -> R.string.bus23 - 111 -> R.string.bus281 - 112 -> R.string.bus282 - 113 -> R.string.bus291 - 114 -> R.string.bus292 - 115 -> R.string.bus301 - 116 -> R.string.bus302 - 117 -> R.string.bus311 - 118 -> R.string.bus312 - 119 -> R.string.bus321 - 120 -> R.string.bus322 - 121 -> R.string.bus331 - 122 -> R.string.bus332 - 123 -> R.string.bus341 - 124 -> R.string.bus342 - 125 -> R.string.bus351 - 126 -> R.string.bus352 - 127 -> R.string.bus361 - 128 -> R.string.bus362 - 129 -> R.string.bus371 - 130 -> R.string.bus372 - 131 -> R.string.bus381 - 132 -> R.string.bus382 - 133 -> R.string.bus601 - 134 -> R.string.bus602 - 135 -> R.string.zeljkstari - 136 -> R.string.kstarizelj - 137 -> R.string.trogir_kstari - 138 -> R.string.kstari_trogir - 139 -> R.string.trostdirekt2 - - // Prigradsko - 201 -> R.string.bus671 - 202 -> R.string.bus672 - 203 -> R.string.bus681 - 204 -> R.string.bus682 - 205 -> R.string.bus691 - 206 -> R.string.bus692 - 207 -> R.string.bus711 - 208 -> R.string.bus712 - 209 -> R.string.bus731 - 210 -> R.string.bus732 - 211 -> R.string.bus761 - 212 -> R.string.bus762 - 213 -> R.string.bus771 - 214 -> R.string.bus772 - 215 -> R.string.bus801 - 216 -> R.string.bus802 - 217 -> R.string.bus811 - 218 -> R.string.bus812 - 219 -> R.string.bus861 - 220 -> R.string.bus862 - // 84 - // 85 - // 86 - 221 -> R.string.bus900 - 222 -> R.string.bus911 - 223 -> R.string.bus912 - 224 -> R.string.bus931 - 225 -> R.string.bus932 - 226 -> R.string.kstarirudine - 227 -> R.string.rudinekstari - - - // Trogir i Solta - 301 -> R.string.bus411 - 302 -> R.string.bus412 - 303 -> R.string.bus421 - 304 -> R.string.bus422 - 305 -> R.string.bus441 - 306 -> R.string.bus442 - 307 -> R.string.bus451 - 308 -> R.string.bus452 - 309 -> R.string.bus481 - 310 -> R.string.bus482 - 311 -> R.string.bus491 - 312 -> R.string.bus492 - 313 -> R.string.bus501 - 314 -> R.string.bus502 - 315 -> R.string.bus511 - 316 -> R.string.bus512 - 317 -> R.string.bus521 - 318 -> R.string.bus522 - - 401 -> R.string.buss1 - 402 -> R.string.buss2 - 403 -> R.string.buss3 - 404 -> R.string.buss4 - else -> throw IllegalArgumentException("Illegal lineId $lineId") - } - - fun getTimetableTitleAsOnPrometWebsite(lineId: Int): String = - when (lineId) { - // Gradski - 1 -> "3 BRNIK" - 2 -> "3A BRNIK" - 3 -> "6 KILA" - 4 -> "7 ŽNJAN" - 5 -> "7 ZAPADNA" - 6 -> "8 ŽNJAN" - 7 -> "8 ZVONČAC" - 8 -> "9 RAVNE NJIVE" - 9 -> "11 SPINUT" - 10 -> "11 RAVNE" - 11 -> "12 SV. FRANE" - 12 -> "12 BENE" - 13 -> "14 BRDA" - 14 -> "14 DUILOVO" - 15 -> "15 DUILOVO" - 16 -> "17 SPINUT" - 17 -> "17 TRSTENIK" - 18 -> "18 SIROBUJA" - 19 -> "20 BRDA" - 20 -> "21 SV.FRANE" - 21 -> "24 SPLIT" - 22 -> "24 TTTS" - 23 -> "25 SPLIT" - 24 -> "25 STOBREČ" - 25 -> "26 SPLIT" - 26 -> "26 KAMEN" - 27 -> "27 SPLIT" - 28 -> "27 ŽRNOVNICA" - 29 -> "39 LORA" - 30 -> "40 TRAJ" - - // Urbano - 101 -> "1 BUNJE" - 102 -> "2 ZRAČN" - 103 -> "2 SPLIT" - 104 -> "2A" - 105 -> "5 DRAČEVAC" - 106 -> "5A DRAČEVAC" - 107 -> "10 JAPIRKO" - 108 -> "16 NINČEVIĆI" - 109 -> "22 KLIS" - 110 -> "23 HNK" - 111 -> "28 SPLIT" - 112 -> "28 DUBRAVA" - 113 -> "29 SPLIT" - 114 -> "29 NAKLICE" - 115 -> "30 SPLIT" - 116 -> "30 PODSTRANA" - 117 -> "31 SPLIT" - 118 -> "31 VRANJIC" - 119 -> "32 SPLIT" - 120 -> "32 KUČINE" - 121 -> "33 SPLIT" - 122 -> "33 K" - 123 -> "34 SPLIT" - 124 -> "34 KLIS" - 125 -> "35 SPLIT" - 126 -> "35 DU" - 127 -> "36 SPLIT" - 128 -> "36 KOPRIVNO" - 129 -> "37 SP" - 130 -> "37 TR" - 131 -> "38 SPLIT" - 132 -> "38 ZRAČNA" - 133 -> "60 SPLIT" - 134 -> "60 RAVNIČKI" - 135 -> "ŽELJEZNIČKA STANICA - KAŠTEL STARI" - 136 -> "KAŠTEL STARI - ŽELJEZNIČKA STANICA" - 137 -> "TROGIR KOLODVOR - ŽELJEZNIČKA STANICA" - 138 -> "ŽELJEZNIČKA STANICA (K. STARI) - TROGIR KOLODVOR INTEGRIRANA " - 139 -> "TROGIR - SPLIT" - - - // Prigradsko - 201 -> "67 SPLIT" - 202 -> "67 DOLAC" - 203 -> "68 SPLIT" - 204 -> "68 ŠEST" - 205 -> "69 SPLIT" - 206 -> "69 ŠEST" - 207 -> "71 SPLIT" - 208 -> "71 SUTINA" - 209 -> "73 SPLIT" - 210 -> "73 OGORJE" - 211 -> "76 SPLIT" - 212 -> "76 KLJACI" - 213 -> "77 SPLIT" - 214 -> "77 CRIVAC" - 215 -> "80 SPLIT" - 216 -> "80 DRNIŠ" - 217 -> "81 SPLIT" - 218 -> "81 NISKO" - 219 -> "86 SPLIT" - 220 -> "86 KLADNJICE" - // 84 - // 85 - // 86 - 221 -> "90 SITNO" - 222 -> "91 K" - 223 -> "91 D" - 224 -> "93 K" - 225 -> "93 ŠE" - 226 -> "KAŠTEL STARI - RUDINE" - 227 -> "RUDINE - KAŠTEL STARI" - - // Trogir i Solta - 301 -> "41 TROGIR" - 302 -> "41 PLANO" - 303 -> "42 TROGIR" - 304 -> "42 SLATINE" - 305 -> "44 TROGIR" - 306 -> "44 OKRUG DONJI" - 307 -> "45 TROGIR" - 308 -> "45 SEGET" - 309 -> "48 TROGIR" - 310 -> "48 MARINA" - 311 -> "49 TROGIR" - 312 -> "49 VINIŠĆE" - 313 -> "50 TROGIR" - 314 -> "50 SEVID" - 315 -> "51 TROGIR" - 316 -> "51 LJUBITOVICA" - 317 -> "52 TROGIR" - 318 -> "52 VINOVAC" - - 401 -> "MASLINICA" - 402 -> "ROGAČ - GROHOTE - SREDNJE SELO" - 403 -> "STOMORSKA" - 404 -> "ROGAČ - GROHOTE - NEČUJAM" - else -> throw IllegalArgumentException("Illegal lineId $lineId") - } - - companion object { - - const val AREA_CITY = 0 - const val AREA_URBAN = 1 - const val AREA_SUBURBAN = 2 - const val AREA_TROGIR = 4 - const val AREA_SOLTA = 5 - - val list: List = listOf( - // Gradski - Timetable(1, "3", 31, AREA_CITY, 0, "", ""), - Timetable(2, "3A", 32, AREA_CITY, 0, "", ""), - Timetable(3, "6", 60, AREA_CITY, 0, "", ""), - Timetable(4, "7", 71, AREA_CITY, 0, "", ""), - Timetable(5, "7", 72, AREA_CITY, 0, "", ""), - Timetable(6, "8", 81, AREA_CITY, 0, "", ""), - Timetable(7, "8", 82, AREA_CITY, 0, "", ""), - Timetable(8, "9", 90, AREA_CITY, 0, "", ""), - Timetable(9, "11", 111, AREA_CITY, 0, "", ""), - Timetable(10, "11", 112, AREA_CITY, 0, "", ""), - Timetable(11, "12", 121, AREA_CITY, 0, "", ""), - Timetable(12, "12", 122, AREA_CITY, 0, "", ""), - Timetable(13, "14", 141, AREA_CITY, 0, "", ""), - Timetable(14, "14", 142, AREA_CITY, 0, "", ""), - Timetable(15, "15", 150, AREA_CITY, 0, "", ""), - Timetable(16, "17", 171, AREA_CITY, 0, "", ""), - Timetable(17, "17", 172, AREA_CITY, 0, "", ""), - Timetable(18, "18", 180, AREA_CITY, 0, "", ""), - Timetable(19, "20", 200, AREA_CITY, 0, "", ""), - Timetable(20, "21", 210, AREA_CITY, 0, "", ""), - Timetable(21, "24", 241, AREA_CITY, 0, "", ""), - Timetable(22, "24", 242, AREA_CITY, 0, "", ""), - Timetable(23, "25", 251, AREA_CITY, 0, "", ""), - Timetable(24, "25", 252, AREA_CITY, 0, "", ""), - Timetable(25, "26", 261, AREA_CITY, 0, "", ""), - Timetable(26, "26", 262, AREA_CITY, 0, "", ""), - Timetable(27, "27", 271, AREA_CITY, 0, "", ""), - Timetable(28, "27", 272, AREA_CITY, 0, "", ""), - Timetable(29, "39", 390, AREA_CITY, 0, "", ""), - Timetable(30, "40", 400, AREA_CITY, 0, "", ""), - - // Urbano - Timetable(101, "1", 10, AREA_URBAN, 0, "", ""), - Timetable(102, "2", 21, AREA_URBAN, 0, "", ""), - Timetable(103, "2", 22, AREA_URBAN, 0, "", ""), - Timetable(104, "2A", 23, AREA_URBAN, 0, "", ""), - Timetable(105, "5", 50, AREA_URBAN, 0, "", ""), - Timetable(106, "5A", 51, AREA_URBAN, 0, "", ""), - Timetable(107, "10", 100, AREA_URBAN, 0, "", ""), - Timetable(108, "16", 160, AREA_URBAN, 0, "", ""), - Timetable(109, "22", 220, AREA_URBAN, 0, "", ""), - Timetable(110, "23", 230, AREA_URBAN, 0, "", ""), - Timetable(111, "28", 281, AREA_URBAN, 0, "", ""), - Timetable(112, "28", 282, AREA_URBAN, 0, "", ""), - Timetable(113, "29", 291, AREA_URBAN, 0, "", ""), - Timetable(114, "29", 292, AREA_URBAN, 0, "", ""), - Timetable(115, "30", 301, AREA_URBAN, 0, "", ""), - Timetable(116, "30", 302, AREA_URBAN, 0, "", ""), - Timetable(117, "31", 311, AREA_URBAN, 0, "", ""), - Timetable(118, "31", 312, AREA_URBAN, 0, "", ""), - Timetable(119, "32", 321, AREA_URBAN, 0, "", ""), - Timetable(120, "32", 322, AREA_URBAN, 0, "", ""), - Timetable(121, "33", 331, AREA_URBAN, 0, "", ""), - Timetable(122, "33", 332, AREA_URBAN, 0, "", ""), - Timetable(123, "34", 341, AREA_URBAN, 0, "", ""), - Timetable(124, "34", 342, AREA_URBAN, 0, "", ""), - Timetable(125, "35", 351, AREA_URBAN, 0, "", ""), - Timetable(126, "35", 352, AREA_URBAN, 0, "", ""), - Timetable(127, "36", 361, AREA_URBAN, 0, "", ""), - Timetable(128, "36", 362, AREA_URBAN, 0, "", ""), - Timetable(129, "37", 371, AREA_URBAN, 0, "", ""), - Timetable(130, "37", 372, AREA_URBAN, 0, "", ""), - Timetable(131, "38", 381, AREA_URBAN, 0, "", ""), - Timetable(132, "38", 382, AREA_URBAN, 0, "", ""), - Timetable(133, "60", 601, AREA_URBAN, 0, "", ""), - Timetable(134, "60", 602, AREA_URBAN, 0, "", ""), - Timetable(135, "", 1000, AREA_URBAN, 0, "", ""), - Timetable(136, "", 1001, AREA_URBAN, 0, "", ""), - Timetable(137, "", 1002, AREA_URBAN, 0, "", ""), - Timetable(138, "", 1003, AREA_URBAN, 0, "", ""), - Timetable(139, "", 1004, AREA_URBAN, 0, "", ""), - - // Prigradsko - Timetable(201, "67", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(202, "67", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(203, "68", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(204, "68", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(205, "69", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(206, "69", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(207, "71", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(208, "71", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(209, "73", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(210, "73", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(211, "76", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(212, "76", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(213, "77", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(214, "77", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(215, "80", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(216, "80", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(217, "81", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(218, "81", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(219, "86", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(220, "86", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(221, "90", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(222, "91", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(223, "91", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(224, "93", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(225, "93", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(226, "", 9999, AREA_SUBURBAN, 0, "", ""), - Timetable(227, "", 9999, AREA_SUBURBAN, 0, "", ""), - - // Trogir i Solta - Timetable(301, "41", 9999, AREA_TROGIR, 0, "", ""), - Timetable(302, "41", 9999, AREA_TROGIR, 0, "", ""), - Timetable(303, "42", 9999, AREA_TROGIR, 0, "", ""), - Timetable(304, "42", 9999, AREA_TROGIR, 0, "", ""), - Timetable(305, "44", 9999, AREA_TROGIR, 0, "", ""), - Timetable(306, "44", 9999, AREA_TROGIR, 0, "", ""), - Timetable(307, "45", 9999, AREA_TROGIR, 0, "", ""), - Timetable(308, "45", 9999, AREA_TROGIR, 0, "", ""), - Timetable(309, "48", 9999, AREA_TROGIR, 0, "", ""), - Timetable(310, "48", 9999, AREA_TROGIR, 0, "", ""), - Timetable(311, "49", 9999, AREA_TROGIR, 0, "", ""), - Timetable(312, "49", 9999, AREA_TROGIR, 0, "", ""), - Timetable(313, "50", 9999, AREA_TROGIR, 0, "", ""), - Timetable(314, "50", 9999, AREA_TROGIR, 0, "", ""), - Timetable(315, "51", 9999, AREA_TROGIR, 0, "", ""), - Timetable(316, "51", 9999, AREA_TROGIR, 0, "", ""), - Timetable(317, "52", 9999, AREA_TROGIR, 0, "", ""), - Timetable(318, "52", 9999, AREA_TROGIR, 0, "", ""), - - Timetable(401, "", 9999, AREA_SOLTA, 0, "", ""), - Timetable(402, "", 9999, AREA_SOLTA, 0, "", ""), - Timetable(403, "", 9999, AREA_SOLTA, 0, "", ""), - Timetable(404, "", 9999, AREA_SOLTA, 0, "", "") - ) - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/di/LocalModules.kt b/app/src/main/java/com/am/stbus/common/di/AppModule.kt similarity index 69% rename from app/src/main/java/com/am/stbus/common/di/LocalModules.kt rename to app/src/main/java/com/am/stbus/common/di/AppModule.kt index 7ed0caf..f0f4e33 100644 --- a/app/src/main/java/com/am/stbus/common/di/LocalModules.kt +++ b/app/src/main/java/com/am/stbus/common/di/AppModule.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,28 +25,29 @@ package com.am.stbus.common.di import androidx.room.Room -import com.am.stbus.data.local.AppDatabase -import org.koin.android.ext.koin.androidApplication +import com.am.stbus.common.room.AppDatabase +import com.am.stbus.data.ApiService +import com.am.stbus.data.room.FavouriteItemDao +import org.koin.android.ext.koin.androidContext import org.koin.dsl.module +import retrofit2.Retrofit -val localModules = module { +val appModule = module { - single { - Room.databaseBuilder( - androidApplication(), - AppDatabase::class.java, - "split-bus-db" - ).fallbackToDestructiveMigration().build() + single { + get().create(ApiService::class.java) } - single { - get().newsDao() + single { + Room.databaseBuilder( + androidContext(), + AppDatabase::class.java, "split-bus-db" + ).build() } - single { - get().timetableDao() + single { + get().favouriteItemDao() } -} - +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/di/NetworkModule.kt b/app/src/main/java/com/am/stbus/common/di/NetworkModule.kt new file mode 100644 index 0000000..34ee6d6 --- /dev/null +++ b/app/src/main/java/com/am/stbus/common/di/NetworkModule.kt @@ -0,0 +1,56 @@ +package com.am.stbus.common.di + +import com.am.stbus.common.Constants.PROMET_API_URL +import com.am.stbus.common.PROMET_KEY +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.kotlinx.serialization.asConverterFactory +import java.util.concurrent.TimeUnit + +val networkModule = module { + + single { + Json { + ignoreUnknownKeys = true + isLenient = true + } + } + + // Provide OkHttpClient + single { + OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + .addInterceptor { chain -> + //return response + chain.proceed( + //create request + chain.request() + .newBuilder() + .also { + it.addHeader("accept", "application/json, text/plain, */*") + it.addHeader("appuid", "PrometSplit.Mobile") + it.addHeader("x-auth-key", PROMET_KEY) + it.addHeader("x-tenant", "KingICT.PS.Public") + }.build() + ) + }.build() + } + + single { + Retrofit.Builder() + .baseUrl(PROMET_API_URL) + .client(get()) + .addConverterFactory( + Json.asConverterFactory( + "application/json; charset=UTF8".toMediaType() + ) + ) + .build() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/di/NetworkModules.kt b/app/src/main/java/com/am/stbus/common/di/RepositoryModule.kt similarity index 80% rename from app/src/main/java/com/am/stbus/common/di/NetworkModules.kt rename to app/src/main/java/com/am/stbus/common/di/RepositoryModule.kt index e696ced..c6ff46a 100644 --- a/app/src/main/java/com/am/stbus/common/di/NetworkModules.kt +++ b/app/src/main/java/com/am/stbus/common/di/RepositoryModule.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,18 +24,20 @@ package com.am.stbus.common.di -import com.am.stbus.data.network.RemoteNewsDataSource -import com.am.stbus.data.network.RemoteTimetableDataSource +import com.am.stbus.domain.repositories.PrometApiRepository +import com.am.stbus.domain.repositories.TimetablesRepository import org.koin.dsl.module -val networkModules = module { +val repositoryModule = module { single { - RemoteNewsDataSource() + PrometApiRepository( + apiService = get() + ) } single { - RemoteTimetableDataSource() + TimetablesRepository() } } \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/di/UseCaseModule.kt b/app/src/main/java/com/am/stbus/common/di/UseCaseModule.kt index 04f6dcf..e74b72a 100644 --- a/app/src/main/java/com/am/stbus/common/di/UseCaseModule.kt +++ b/app/src/main/java/com/am/stbus/common/di/UseCaseModule.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,35 +24,28 @@ package com.am.stbus.common.di -import com.am.stbus.domain.usecases.news.NewsDetailUseCase -import com.am.stbus.domain.usecases.news.NewsListUseCase -import com.am.stbus.domain.usecases.timetables.TimetableDetailUseCase -import com.am.stbus.domain.usecases.timetables.TimetableListUseCase +import com.am.stbus.domain.usecases.FavouritesRoomDbUseCase +import com.am.stbus.domain.usecases.GetBusStopArrivalsUseCase +import com.am.stbus.domain.usecases.GetTimetableDetailDataUseCase import org.koin.dsl.module val useCaseModule = module { - factory { - NewsListUseCase( - newsRepository = get() + single { + GetBusStopArrivalsUseCase( + prometApiRepository = get() ) } - factory { - NewsDetailUseCase( - newsRepository = get() + single { + GetTimetableDetailDataUseCase( + timetablesRepository = get() ) } - factory { - TimetableListUseCase( - timetableRepository = get() - ) - } - - factory { - TimetableDetailUseCase( - timetableRepository = get() + single { + FavouritesRoomDbUseCase( + favouriteItemDao = get() ) } diff --git a/app/src/main/java/com/am/stbus/common/di/ViewModelModule.kt b/app/src/main/java/com/am/stbus/common/di/ViewModelModule.kt index d1ffc8d..40e07c2 100644 --- a/app/src/main/java/com/am/stbus/common/di/ViewModelModule.kt +++ b/app/src/main/java/com/am/stbus/common/di/ViewModelModule.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,66 +24,29 @@ package com.am.stbus.common.di -import com.am.stbus.presentation.screens.favourites.FavouritesViewModel -import com.am.stbus.presentation.screens.information.InformationListViewModel -import com.am.stbus.presentation.screens.information.informationNewsListFragment.InformationNewsListViewModel -import com.am.stbus.presentation.screens.information.informationNewsListFragment.informationNewsDetailFragment.InformationNewsDetailViewModel -import com.am.stbus.presentation.screens.timetables.TimetablesSharedViewModel -import com.am.stbus.presentation.screens.timetables.TimetablesViewModel -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListViewModel -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment.TimetableDetailFragmentArgs -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment.TimetableDetailViewModel -import org.koin.androidx.viewmodel.dsl.viewModel +import com.am.stbus.presentation.MainViewModel +import com.am.stbus.presentation.screens.stops.detail.BusStopArrivalsDetailViewModel +import com.am.stbus.presentation.screens.timetables.detail.TimetablesDetailViewModel +import org.koin.core.module.dsl.viewModel import org.koin.dsl.module -val viewModelModule = module{ +val viewModelModule = module { viewModel { - InformationListViewModel( + MainViewModel( + favouritesRoomDbUseCase = get() ) } viewModel { - FavouritesViewModel( - timetableListUseCase = get() + BusStopArrivalsDetailViewModel( + getDeparturesUseCase = get() ) } viewModel { - TimetablesViewModel( - timetableListUseCase = get() + TimetablesDetailViewModel( + getTimetableDetailDataUseCase = get() ) } - - viewModel { - TimetablesListViewModel( - timetableListUseCase = get() - ) - } - - viewModel {(args: TimetableDetailFragmentArgs) -> - TimetableDetailViewModel( - args = args, - timetableListUseCase = get(), - timetableDetailUseCase = get() - ) - } - - viewModel { - InformationNewsListViewModel( - getNewsListUseCase = get() - ) - } - - viewModel {(url: String) -> - InformationNewsDetailViewModel( - url = url, - getNewsDetailUseCase = get() - ) - } - - viewModel { - TimetablesSharedViewModel() - } - } \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/extensions/CommonExtensions.kt b/app/src/main/java/com/am/stbus/common/extensions/CommonExtensions.kt deleted file mode 100644 index f751eb3..0000000 --- a/app/src/main/java/com/am/stbus/common/extensions/CommonExtensions.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.common.extensions - -import android.content.Context -import android.content.Intent -import android.content.res.Resources -import android.net.Uri -import android.os.Build -import com.am.stbus.BuildConfig -import com.am.stbus.common.Constants.PROMET_URL - -fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).toInt() -fun Int.toDp(): Int = (this / Resources.getSystem().displayMetrics.density).toInt() - -fun Context.loadUrl(url: String) = startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) - -fun Context.loadPrometUrl() = this.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(PROMET_URL))) -fun Context.loadEmailReport(linija: String, error: String) = this.startActivity( - Intent.createChooser( - Intent( - Intent.ACTION_SENDTO, - Uri.fromParts("mailto", "antoniomarinnn@gmail.com", null) - ).apply { - putExtra(Intent.EXTRA_SUBJECT, "Split Bus") - putExtra( - Intent.EXTRA_TEXT, - "Molimo opisite problem!(Please describe your issue!)" + - "\n\n\n\nInformacije o uredaju \n --------------------------------" + - "\n Linija: $linija \n Split Bus verzija: ${BuildConfig.VERSION_NAME}" + - "\n Greska: $error" + - "\n Android: ${Build.VERSION.RELEASE} (SDK: ${Build.VERSION.SDK_INT})" + - "\n Telefon: ${Build.MODEL} (Uredaj: ${Build.DEVICE})" - ) - }, "Email" - ) -) \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/extensions/ViewExtensions.kt b/app/src/main/java/com/am/stbus/common/extensions/ViewExtensions.kt deleted file mode 100644 index 471b410..0000000 --- a/app/src/main/java/com/am/stbus/common/extensions/ViewExtensions.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.common.extensions - -import android.app.Activity -import android.os.Build -import android.text.Spanned -import android.view.View -import androidx.core.content.ContextCompat -import androidx.core.text.HtmlCompat -import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY - -fun View.systemUiVisibilityFullScreen() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE) - } -} - -fun Activity.changeStatusBarColor(colorId: Int) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - this.window.statusBarColor = ContextCompat.getColor(this, colorId) - } -} - -fun String.toSpanned(): Spanned { - return HtmlCompat.fromHtml(this, FROM_HTML_MODE_LEGACY) -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/local/AppDatabase.kt b/app/src/main/java/com/am/stbus/common/room/AppDatabase.kt similarity index 75% rename from app/src/main/java/com/am/stbus/data/local/AppDatabase.kt rename to app/src/main/java/com/am/stbus/common/room/AppDatabase.kt index 13234da..fa6b585 100644 --- a/app/src/main/java/com/am/stbus/data/local/AppDatabase.kt +++ b/app/src/main/java/com/am/stbus/common/room/AppDatabase.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,16 +22,15 @@ * SOFTWARE. */ -package com.am.stbus.data.local +package com.am.stbus.common.room import androidx.room.Database import androidx.room.RoomDatabase -import com.am.stbus.common.Constants -import com.am.stbus.domain.models.NewsListItem -import com.am.stbus.domain.models.Timetable +import com.am.stbus.common.Constants.DB_VERSION +import com.am.stbus.data.models.FavouriteItem +import com.am.stbus.data.room.FavouriteItemDao -@Database(entities = [NewsListItem::class, Timetable::class], version = Constants.DB_VERSION, exportSchema = false) +@Database(entities = [FavouriteItem::class], version = DB_VERSION) abstract class AppDatabase : RoomDatabase() { - abstract fun newsDao(): NewsDao - abstract fun timetableDao(): TimetableDao -} + abstract fun favouriteItemDao(): FavouriteItemDao +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/ApiService.kt b/app/src/main/java/com/am/stbus/data/ApiService.kt new file mode 100644 index 0000000..eeeb678 --- /dev/null +++ b/app/src/main/java/com/am/stbus/data/ApiService.kt @@ -0,0 +1,16 @@ +package com.am.stbus.data + +import com.am.stbus.data.models.BusStopArrival +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Path + +interface ApiService { + + @GET("stop/{busStopId}/arrival-times") + suspend fun getBusStopArrivals( + @Path(value = "busStopId") busStopId: Int + ): Response> + +} + diff --git a/app/src/main/java/com/am/stbus/data/Model.kt b/app/src/main/java/com/am/stbus/data/Model.kt new file mode 100644 index 0000000..35045ab --- /dev/null +++ b/app/src/main/java/com/am/stbus/data/Model.kt @@ -0,0 +1,25 @@ +package com.am.stbus.data + +/* @SerialName("pathwayId") + val pathwayId: Int?, + @SerialName("transportServiceRouteId") + val transportServiceRouteId: Int?, + @SerialName("transportServiceRouteTripId") + val transportServiceRouteTripId: Int?, + @SerialName("publicName") + val publicName: String?, + @SerialName("name") + val name: String?, + @SerialName("serviceCalendarId") + val serviceCalendarId: Int?, + @SerialName("departureTime") + val departureTime: String?, + @SerialName("isActive") + val isActive: Boolean? +) + +"departureTime": "23:54:00", +"transportServiceRouteTripId": 21933498, +"transportServiceRouteId": 83874, +"transportServiceRouteCode": "60" +*/ \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/local/NewsDao.kt b/app/src/main/java/com/am/stbus/data/local/NewsDao.kt deleted file mode 100644 index 3a2c6b4..0000000 --- a/app/src/main/java/com/am/stbus/data/local/NewsDao.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.data.local - -import androidx.room.* -import com.am.stbus.domain.models.NewsListItem - -@Dao -interface NewsDao { - @Query("SELECT * FROM newsListItem") - suspend fun getAll(): List - - @Query("SELECT * FROM newsListItem WHERE newsId IN (:newsIds)") - suspend fun loadAllByIds(newsIds: IntArray): List - - @Query("SELECT * FROM newsListItem WHERE url LIKE :url LIMIT 1") - suspend fun findByUrl(url: String): NewsListItem - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAll(news: List) - - @Delete - suspend fun delete(news: NewsListItem) - - @Query("DELETE FROM newsListItem") - suspend fun nukeTable() -} diff --git a/app/src/main/java/com/am/stbus/data/local/TimetableDao.kt b/app/src/main/java/com/am/stbus/data/local/TimetableDao.kt deleted file mode 100644 index d1da574..0000000 --- a/app/src/main/java/com/am/stbus/data/local/TimetableDao.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.data.local - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import com.am.stbus.domain.models.Timetable -import io.reactivex.Completable -import io.reactivex.Single - -@Dao -interface TimetableDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertAll(news: List): Completable - - @Query("SELECT * FROM timetable") - fun getAll(): Single> - - @Query("SELECT * FROM timetable WHERE lineId LIKE :lineId LIMIT 1") - fun loadItemByLineId(lineId: Int): List - - @Query("SELECT * FROM timetable WHERE area_id LIKE :areaId LIMIT 1") - fun loadItemsByAreaId(areaId: Int): List - - @Query("SELECT * FROM timetable WHERE favourite IS 1") - fun loadFavourites(): List - - @Query("UPDATE timetable SET favourite = :favourite WHERE lineId LIKE :lineId ") - fun setFavouriteToLineId(lineId: Int, favourite: Int): Completable - - @Query("UPDATE timetable SET content = :content, content_date = :contentDate WHERE lineId LIKE :lineId ") - fun setTimetableContentToLineId(lineId: Int, content: String, contentDate: String): Completable - -} diff --git a/app/src/main/java/com/am/stbus/domain/models/Notification.kt b/app/src/main/java/com/am/stbus/data/models/BusLine.kt similarity index 81% rename from app/src/main/java/com/am/stbus/domain/models/Notification.kt rename to app/src/main/java/com/am/stbus/data/models/BusLine.kt index a6faa5a..4d00a4a 100644 --- a/app/src/main/java/com/am/stbus/domain/models/Notification.kt +++ b/app/src/main/java/com/am/stbus/data/models/BusLine.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,16 @@ * SOFTWARE. */ -package com.am.stbus.domain.models +package com.am.stbus.data.models + +import kotlinx.serialization.Serializable + +@Serializable +data class BusLine( + val id: Int, + val title: Int, + val number: String, + val websiteTitle: String +) + -data class Notification(val id: Int, val notificationTitle: String, val notificationMessage: String) \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/models/BusLineArea.kt b/app/src/main/java/com/am/stbus/data/models/BusLineArea.kt new file mode 100644 index 0000000..238b801 --- /dev/null +++ b/app/src/main/java/com/am/stbus/data/models/BusLineArea.kt @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.data.models + +import com.am.stbus.R +import com.am.stbus.common.Constants.AREA_CITY_URL +import com.am.stbus.common.Constants.AREA_SOLTA_URL +import com.am.stbus.common.Constants.AREA_SUBURBAN_URL +import com.am.stbus.common.Constants.AREA_TROGIR_URL +import com.am.stbus.common.Constants.AREA_URBAN_URL +import kotlinx.serialization.Serializable + +@Serializable +sealed class BusLineArea { + @Serializable + object City : BusLineArea() + + @Serializable + object Urban : BusLineArea() + + @Serializable + object Suburban : BusLineArea() + + @Serializable + object Trogir : BusLineArea() + + @Serializable + object Solta : BusLineArea() + + companion object { + fun BusLineArea.getPagerTitle(): Int { + return when (this) { + City -> R.string.timetables_area_city + Urban -> R.string.timetables_area_urban + Suburban -> R.string.timetables_area_suburban + Trogir -> R.string.timetables_area_trogir + Solta -> R.string.timetables_area_solta + } + } + + fun BusLineArea.getBaseUrl(): String { + return when (this) { + City -> AREA_CITY_URL + Urban -> AREA_URBAN_URL + Suburban -> AREA_SUBURBAN_URL + Trogir -> AREA_TROGIR_URL + Solta -> AREA_SOLTA_URL + } + } + } +} + + + diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListViewModel.kt b/app/src/main/java/com/am/stbus/data/models/BusStop.kt similarity index 85% rename from app/src/main/java/com/am/stbus/presentation/screens/information/InformationListViewModel.kt rename to app/src/main/java/com/am/stbus/data/models/BusStop.kt index 77149b9..b92739c 100644 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListViewModel.kt +++ b/app/src/main/java/com/am/stbus/data/models/BusStop.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,10 +22,14 @@ * SOFTWARE. */ -package com.am.stbus.presentation.screens.information +package com.am.stbus.data.models -import androidx.lifecycle.ViewModel +import kotlinx.serialization.Serializable + +@Serializable +data class BusStop( + val id: Int, + val title: Int, +) -class InformationListViewModel() : ViewModel() { -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/models/NewsItem.kt b/app/src/main/java/com/am/stbus/data/models/BusStopArrival.kt similarity index 75% rename from app/src/main/java/com/am/stbus/domain/models/NewsItem.kt rename to app/src/main/java/com/am/stbus/data/models/BusStopArrival.kt index 8569eda..6e2a22f 100644 --- a/app/src/main/java/com/am/stbus/domain/models/NewsItem.kt +++ b/app/src/main/java/com/am/stbus/data/models/BusStopArrival.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,9 +22,17 @@ * SOFTWARE. */ -package com.am.stbus.domain.models +package com.am.stbus.data.models -data class NewsItem ( - val newsItemContent: String, - val newsItemImageUrl: String +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BusStopArrival( + @SerialName("lineNumber") + val lineNumber: String?, + @SerialName("title") + val title: String?, + @SerialName("time") + val time: String?, ) \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/models/NewsListItem.kt b/app/src/main/java/com/am/stbus/data/models/FavouriteItem.kt similarity index 77% rename from app/src/main/java/com/am/stbus/domain/models/NewsListItem.kt rename to app/src/main/java/com/am/stbus/data/models/FavouriteItem.kt index 41622c1..cae70ab 100644 --- a/app/src/main/java/com/am/stbus/domain/models/NewsListItem.kt +++ b/app/src/main/java/com/am/stbus/data/models/FavouriteItem.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,16 +22,15 @@ * SOFTWARE. */ -package com.am.stbus.domain.models +package com.am.stbus.data.models import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey @Entity -data class NewsListItem( - @PrimaryKey val newsId: Int, - @ColumnInfo(name = "title") val title: String, - @ColumnInfo(name = "desc") val desc: String, - @ColumnInfo(name = "date") val date: String, - @ColumnInfo(name = "url") val url: String) \ No newline at end of file +data class FavouriteItem( + @PrimaryKey(autoGenerate = true) val uid: Long = 0, + @ColumnInfo(name = "id") val id: Int, + @ColumnInfo(name = "type") val type: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/network/RemoteNewsDataSource.kt b/app/src/main/java/com/am/stbus/data/network/RemoteNewsDataSource.kt deleted file mode 100644 index fd4fc18..0000000 --- a/app/src/main/java/com/am/stbus/data/network/RemoteNewsDataSource.kt +++ /dev/null @@ -1,107 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.data.network - -import com.am.stbus.common.Constants -import com.am.stbus.domain.models.NewsItem -import com.am.stbus.domain.models.NewsListItem -import io.reactivex.Single -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.withContext -import org.jsoup.Jsoup -import timber.log.Timber - -class RemoteNewsDataSource { - - suspend fun getNewsList(dispatcher: CoroutineDispatcher): List { - return withContext(dispatcher) { - try { - val doc = Jsoup.connect(Constants.PROMET_NOVOSTI_URL).timeout(Constants.NETWORK_REQUEST_TIMEOUT).get() - val newsList = mutableListOf() - var count = 0 - val elements = doc.select("h3.c-article-card__title > a") - - for (e in elements) { - - val title = e.text() - val url = e.attr("href") - - var summary = "" - var datum: String - - doc.apply { - datum = select("div.c-article-card__date")[count].text() - val summaryElement = select("div.c-article-card__content")[count] - val summaryElementSize = summaryElement.select("div.c-article-card__summary").size - if (summaryElementSize > 0) - summary = summaryElement.child(2).text() - - } - - newsList.add(NewsListItem(count, title, summary, datum, url)) - count += 1 - } - - newsList - } catch (e: Exception) { - Timber.e("Error fetching content") - emptyList() - } - } - } - - fun getNewsDetail(url: String): Single { - return Single.fromCallable { - - val doc = Jsoup.connect(url).timeout(Constants.NETWORK_REQUEST_TIMEOUT).get() - var newsImgUrl: String - val newsContentWithAttachments: String - - doc.apply { - val newsContent = if (select("[class=c-article-detail__body c-text-body]").size > 0) { - select("[class=c-article-detail__body c-text-body]").html() - } else { - select("[class=EDN_article_content]").html() - } - - val ul = doc.select("[class=o-list-bare c-article-document-list]").select("li") - val attachments = ul.map { - val attachmentUrl = it.select("[class=c-article-document o-media]").first()?.attr("href") - val attachmentTitle = it.select("[class=c-article-document__title c-text-lead]").text() - val attachmentProperties = it.select("[class=c-article-document__meta c-text-smallprint]").text() - return@map "$attachmentTitle $attachmentProperties" - }.joinToString(separator = "

") - - newsContentWithAttachments = newsContent + attachments - - newsImgUrl = "http://www.promet-split.hr${select("[class=c-gallery-fotorama__item c-gallery-fotorama__item--image js-gallery-unwrap]") - .attr("data-img")}" - } - - return@fromCallable NewsItem(newsContentWithAttachments, newsImgUrl) - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/network/RemoteTimetableDataSource.kt b/app/src/main/java/com/am/stbus/data/network/RemoteTimetableDataSource.kt deleted file mode 100644 index b0d5f2b..0000000 --- a/app/src/main/java/com/am/stbus/data/network/RemoteTimetableDataSource.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.data.network - -import com.am.stbus.common.Constants -import com.am.stbus.common.TimetablesData -import io.reactivex.Single -import org.jsoup.Jsoup -import java.util.* -import kotlin.collections.ArrayList - -class RemoteTimetableDataSource { - - fun getTimetableDetail(lineId: Int, url: String): Single { - return Single.fromCallable { - - val timetableId = Jsoup.connect(url).timeout(Constants.NETWORK_REQUEST_TIMEOUT).get() - .select(".c-vozni-red__search-select option:contains(" - + TimetablesData().getTimetableTitleAsOnPrometWebsite(lineId).toUpperCase(Locale.ROOT) - + ")") - .attr("value") - - - val doc = Jsoup.connect(Constants.PROMET_URL + timetableId).timeout(Constants.NETWORK_REQUEST_TIMEOUT).get() - val workDayList = ArrayList() - val saturdayList = ArrayList() - val sundayList = ArrayList() - - doc.apply { - select("table.c-vozni-red__table td:eq(0)").let { - for (e in it) { - workDayList.add("\n ${e.text()}") - } - // dodgy fix za zadnji red :) - workDayList.add("") - } - - select("table.c-vozni-red__table td:eq(1)").let { - for (e in it) { - saturdayList.add("\n ${e.text()}") - } - saturdayList.add("") - } - - select("table.c-vozni-red__table td:eq(2)").let { - for (e in it) { - sundayList.add("\n ${e.text()}") - } - sundayList.add("") - } - } - - - val timetableItems = listOf( - doc.select("h3.c-vozni-red__line").text(), - doc.select("p.c-vozni-red__valid").text(), - workDayList, - saturdayList, - sundayList, - doc.select("div.c-vozni-red-note__items").text() - ) - - if (doc.select("p.c-vozni-red__valid").text().isNullOrBlank() && workDayList.isEmpty() && saturdayList.isEmpty() && sundayList.isEmpty()) { - // Null response - return@fromCallable "" - } else { - // Valid response - return@fromCallable timetableItems.joinToString(Constants.EMA_DELIMITER) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/repositoriesImpl/NewsRepositoryImpl.kt b/app/src/main/java/com/am/stbus/data/repositoriesImpl/NewsRepositoryImpl.kt deleted file mode 100644 index c8403eb..0000000 --- a/app/src/main/java/com/am/stbus/data/repositoriesImpl/NewsRepositoryImpl.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.data.repositoriesImpl - -import com.am.stbus.data.local.NewsDao -import com.am.stbus.data.network.RemoteNewsDataSource -import com.am.stbus.domain.models.NewsItem -import com.am.stbus.domain.models.NewsListItem -import com.am.stbus.domain.repositories.NewsRepository -import io.reactivex.Single -import kotlinx.coroutines.CoroutineDispatcher - -class NewsRepositoryImpl( - private val remoteNewsDataSource: RemoteNewsDataSource, - private val localNewsDataSource: NewsDao -): NewsRepository { - override suspend fun getNewsList(remote: Boolean, dispatcher: CoroutineDispatcher): List { - return when(remote) { - true -> remoteNewsDataSource.getNewsList(dispatcher) - false -> localNewsDataSource.getAll() - } - } - - override suspend fun saveNewsList(list: List) { - localNewsDataSource.insertAll(list) - } - - override suspend fun deleteNewsList() { - localNewsDataSource.nukeTable() - } - - override fun getNewsDetail(remote: Boolean, url: String): Single { - return when (remote) { - true -> remoteNewsDataSource.getNewsDetail(url) - false -> throw IllegalArgumentException("Shouldn't be here!") - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/repositoriesImpl/TimetableRepositoryImpl.kt b/app/src/main/java/com/am/stbus/data/repositoriesImpl/TimetableRepositoryImpl.kt deleted file mode 100644 index 00d2a62..0000000 --- a/app/src/main/java/com/am/stbus/data/repositoriesImpl/TimetableRepositoryImpl.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.data.repositoriesImpl - -import com.am.stbus.data.local.TimetableDao -import com.am.stbus.data.network.RemoteTimetableDataSource -import com.am.stbus.domain.models.Timetable -import com.am.stbus.domain.repositories.TimetableRepository -import io.reactivex.Completable -import io.reactivex.Single - -class TimetableRepositoryImpl( - private val remoteTimetableDataSource: RemoteTimetableDataSource, - private val localTimetableDataSource: TimetableDao -): TimetableRepository { - - override fun saveTimetables(list: List): Completable { - return localTimetableDataSource.insertAll(list) - } - - override fun getTimetables(): Single> { - return localTimetableDataSource.getAll() - } - - override fun updateFavourites(lineId: Int, favourite: Int): Completable { - return localTimetableDataSource.setFavouriteToLineId(lineId, favourite) - } - - override fun getTimetableDetail(lineId: Int, url: String): Single{ - return remoteTimetableDataSource.getTimetableDetail(lineId, url) - } - - override fun saveTimetableDetail(lineId: Int, content: String, contentDate: String): Completable { - return localTimetableDataSource.setTimetableContentToLineId(lineId, content, contentDate) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/di/ApplicationModule.kt b/app/src/main/java/com/am/stbus/data/room/FavouriteItemDao.kt similarity index 68% rename from app/src/main/java/com/am/stbus/common/di/ApplicationModule.kt rename to app/src/main/java/com/am/stbus/data/room/FavouriteItemDao.kt index 1cc9d0f..c3a98b5 100644 --- a/app/src/main/java/com/am/stbus/common/di/ApplicationModule.kt +++ b/app/src/main/java/com/am/stbus/data/room/FavouriteItemDao.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,15 +22,22 @@ * SOFTWARE. */ -package com.am.stbus.common.di +package com.am.stbus.data.room -import androidx.preference.PreferenceManager -import org.koin.android.ext.koin.androidApplication -import org.koin.dsl.module +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import com.am.stbus.data.models.FavouriteItem -val applicationModule = module { +@Dao +interface FavouriteItemDao { + @Query("SELECT * FROM favouriteitem") + suspend fun getAll(): List + + @Insert + suspend fun insert(favouriteItem: FavouriteItem) + + @Query("DELETE FROM FavouriteItem WHERE id = :id AND type = :type") + suspend fun delete(id: Int, type: Int) - single { - PreferenceManager.getDefaultSharedPreferences(androidApplication()) - } } \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/data/static/BusLinesData.kt b/app/src/main/java/com/am/stbus/data/static/BusLinesData.kt new file mode 100644 index 0000000..0f5f8da --- /dev/null +++ b/app/src/main/java/com/am/stbus/data/static/BusLinesData.kt @@ -0,0 +1,143 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.data.static + +import com.am.stbus.R +import com.am.stbus.data.models.BusLine + +val CITY_BUS_LINES = listOf( + BusLine(id = 1, title = R.string.bus3, number = "3", websiteTitle = "3 BRNIK"), + BusLine(id = 2, title = R.string.bus3A, number = "3A", websiteTitle = "3A BRNIK"), + BusLine(id = 3, title = R.string.bus6, number = "6", websiteTitle = "6 KILA"), + BusLine(id = 4, title = R.string.bus71, number = "7", websiteTitle = "7 ŽNJAN"), + BusLine(id = 5, title = R.string.bus72, number = "7", websiteTitle = "7 ZAPADNA"), + BusLine(id = 6, title = R.string.bus81, number = "8", websiteTitle = "8 ŽNJAN"), + BusLine(id = 7, title = R.string.bus82, number = "8", websiteTitle = "8 ZVONČAC"), + BusLine(id = 8, title = R.string.bus9, number = "9", websiteTitle = "9 RAVNE"), + BusLine(id = 9, title = R.string.bus111, number = "11", websiteTitle = "11 SPINUT"), + BusLine(id = 10, title = R.string.bus112, number = "11", websiteTitle = "11 RAVNE"), + BusLine(id = 11, title = R.string.bus121, number = "12", websiteTitle = "12 SV. FRANE"), + BusLine(id = 12, title = R.string.bus122, number = "12", websiteTitle = "12 BENE"), + BusLine(id = 13, title = R.string.bus131, number = "13", websiteTitle = "13 SPINUT"), + BusLine(id = 14, title = R.string.bus132, number = "13", websiteTitle = "13 DUILOVO"), + BusLine(id = 15, title = R.string.bus141, number = "14", websiteTitle = "14 BRDA"), + BusLine(id = 16, title = R.string.bus142, number = "14", websiteTitle = "14 ŽNJAN"), + BusLine(id = 17, title = R.string.bus15, number = "15", websiteTitle = "15 DUILOVO"), + BusLine(id = 18, title = R.string.bus171, number = "17", websiteTitle = "17 SPINUT"), + BusLine(id = 19, title = R.string.bus172, number = "17", websiteTitle = "17 TRSTENIK"), + BusLine(id = 20, title = R.string.bus18, number = "18", websiteTitle = "18 SIROBUJA"), + BusLine(id = 21, title = R.string.bus20, number = "20", websiteTitle = "20 BRDA"), + BusLine(id = 22, title = R.string.bus21, number = "21", websiteTitle = "21 SV.FRANE"), + BusLine(id = 23, title = R.string.bus241, number = "24", websiteTitle = "24 SPLIT"), + BusLine(id = 24, title = R.string.bus242, number = "24", websiteTitle = "24 TTTS"), + BusLine(id = 25, title = R.string.bus251, number = "25", websiteTitle = "25 SPLIT"), + BusLine(id = 26, title = R.string.bus252, number = "25", websiteTitle = "25 STOBREČ"), + BusLine(id = 27, title = R.string.bus261, number = "26", websiteTitle = "26 SPLIT"), + BusLine(id = 28, title = R.string.bus262, number = "26", websiteTitle = "26 KAMEN"), + BusLine(id = 29, title = R.string.bus271, number = "27", websiteTitle = "27 SPLIT"), + BusLine(id = 30, title = R.string.bus272, number = "27", websiteTitle = "27 ŽRNOVNICA") +) + +val URBAN_AREA_BUS_LINES = listOf( + BusLine(id = 100, title = R.string.bus1, number = "1", websiteTitle = "1 BUNJE"), + BusLine(id = 101, title = R.string.bus021, number = "2", websiteTitle = "2 SPLIT"), + BusLine(id = 102, title = R.string.bus022, number = "2", websiteTitle = "2 ZRAČNA"), + BusLine(id = 103, title = R.string.bus2A, number = "2A", websiteTitle = "2A K.SUĆURAC"), + BusLine(id = 104, title = R.string.bus5, number = "5", websiteTitle = "5 DRAČEVAC"), + BusLine(id = 105, title = R.string.bus5A, number = "5A", websiteTitle = "5A DRAČEVAC"), + BusLine(id = 106, title = R.string.bus10, number = "10", websiteTitle = "10 JAPIRKO"), + BusLine(id = 107, title = R.string.bus16, number = "16", websiteTitle = "16 NINČEVIĆI"), + BusLine(id = 108, title = R.string.bus221, number = "22", websiteTitle = "22 SPLIT"), + BusLine(id = 109, title = R.string.bus222, number = "22", websiteTitle = "22 KLIS"), + BusLine(id = 110, title = R.string.bus281, number = "28", websiteTitle = "28 SPLIT"), + BusLine(id = 111, title = R.string.bus282, number = "28", websiteTitle = "28 DUBRAVA"), + BusLine(id = 112, title = R.string.bus291, number = "29", websiteTitle = "29 SPLIT"), + BusLine(id = 113, title = R.string.bus292, number = "29", websiteTitle = "29 NAKLICE"), + BusLine(id = 114, title = R.string.bus301, number = "30", websiteTitle = "30 SPLIT"), + BusLine(id = 115, title = R.string.bus302, number = "30", websiteTitle = "30 PODSTRANA"), + BusLine(id = 116, title = R.string.bus311, number = "31", websiteTitle = "31 SPLIT"), + BusLine(id = 117, title = R.string.bus312, number = "31", websiteTitle = "31 VRANJIC"), + BusLine(id = 118, title = R.string.bus321, number = "32", websiteTitle = "32 SPLIT"), + BusLine(id = 119, title = R.string.bus322, number = "32", websiteTitle = "32 KUČINE"), + BusLine(id = 120, title = R.string.bus331, number = "33", websiteTitle = "33 SPLIT"), + BusLine(id = 121, title = R.string.bus332, number = "33", websiteTitle = "33 KLIS"), + BusLine(id = 122, title = R.string.bus351, number = "35", websiteTitle = "35 SPLIT"), + BusLine(id = 123, title = R.string.bus352, number = "35", websiteTitle = "35 DUGOPOLJE"), + BusLine(id = 124, title = R.string.bus361, number = "36", websiteTitle = "36 SPLIT"), + BusLine(id = 125, title = R.string.bus362, number = "36", websiteTitle = "36 KOPRIVNO"), + BusLine(id = 126, title = R.string.bus371, number = "37", websiteTitle = "37 SPLIT"), + BusLine(id = 127, title = R.string.bus372, number = "37", websiteTitle = "37 TROGIR"), + BusLine(id = 128, title = R.string.bus381, number = "38", websiteTitle = "38 SPLIT"), + BusLine(id = 129, title = R.string.bus382, number = "38", websiteTitle = "38 ZRAČNA"), + BusLine(id = 130, title = R.string.bus601, number = "60", websiteTitle = "60 SPLIT"), + BusLine(id = 131, title = R.string.bus602, number = "60", websiteTitle = "60 RAVNIČKI"), + BusLine(id = 132, title = R.string.bus391, number = "39", websiteTitle = "39 KAŠTEL"), + BusLine(id = 133, title = R.string.bus392, number = "39", websiteTitle = "39 RUDINE"), + BusLine(id = 134, title = R.string.bus39A1, number = "39A", websiteTitle = "39A KAŠTEL"), + BusLine(id = 135, title = R.string.bus39A2, number = "39A", websiteTitle = "39A ŽELJEZNIČKA"), + BusLine(id = 136, title = R.string.trostdirekt, number = "", websiteTitle = "TROGIR") +) + +val SUBURBAN_AREA_BUS_LINES = listOf( + BusLine(id = 500, title = R.string.bus671, number = "67", websiteTitle = "67 SPLIT"), + BusLine(id = 501, title = R.string.bus671, number = "67", websiteTitle = "67 DOLAC"), + BusLine(id = 502, title = R.string.bus681, number = "68", websiteTitle = "68 SPLIT"), + BusLine(id = 503, title = R.string.bus682, number = "68", websiteTitle = "68 ŠESTANOVAC"), + BusLine(id = 504, title = R.string.bus691, number = "69", websiteTitle = "69 SPLIT"), + BusLine(id = 505, title = R.string.bus692, number = "69", websiteTitle = "69 ŠESTANOVAC"), + BusLine(id = 506, title = R.string.bus701, number = "70", websiteTitle = "70 GRAB"), + BusLine(id = 507, title = R.string.bus702, number = "70", websiteTitle = "70 BISKO"), + BusLine(id = 508, title = R.string.bus711, number = "71", websiteTitle = "71 SPLIT"), + BusLine(id = 509, title = R.string.bus712, number = "71", websiteTitle = "71 SUTINA"), + BusLine(id = 510, title = R.string.bus731, number = "73", websiteTitle = "73 SPLIT"), + BusLine(id = 511, title = R.string.bus732, number = "73", websiteTitle = "73 OGORJE"), + BusLine(id = 512, title = R.string.bus761, number = "76", websiteTitle = "76 SPLIT"), + BusLine(id = 513, title = R.string.bus762, number = "76", websiteTitle = "76 KLJACI"), + BusLine(id = 514, title = R.string.bus771, number = "77", websiteTitle = "77 SPLIT"), + BusLine(id = 515, title = R.string.bus772, number = "77", websiteTitle = "77 CRIVAC"), + BusLine(id = 516, title = R.string.bus801, number = "80", websiteTitle = "80 SPLIT"), + BusLine(id = 517, title = R.string.bus802, number = "80", websiteTitle = "80 DRNIŠ"), + BusLine(id = 518, title = R.string.bus811, number = "81", websiteTitle = "81 SPLIT"), + BusLine(id = 519, title = R.string.bus812, number = "81", websiteTitle = "81 NISKO"), + BusLine(id = 520, title = R.string.bus861, number = "86", websiteTitle = "86 SPLIT"), + BusLine(id = 521, title = R.string.bus862, number = "86", websiteTitle = "86 KLADNJICE"), + BusLine(id = 522, title = R.string.bus900, number = "90", websiteTitle = "90 SITNO"), + BusLine(id = 523, title = R.string.bus911, number = "91", websiteTitle = "91 K.STARI"), + BusLine(id = 524, title = R.string.bus912, number = "91", websiteTitle = "91 DIVOJEVIĆI"), + BusLine(id = 525, title = R.string.bus931, number = "93", websiteTitle = "93 K.STARI"), + BusLine(id = 526, title = R.string.bus932, number = "93", websiteTitle = "93 ŠERIĆI") +) + + +val TROGIR_BUS_LINES = emptyList() + +val SOLTA_BUS_LINES = emptyList() + +val COMBINED_BUS_LINES = + CITY_BUS_LINES + URBAN_AREA_BUS_LINES + SUBURBAN_AREA_BUS_LINES + TROGIR_BUS_LINES + SOLTA_BUS_LINES + +fun findBusLinePerId(id: Int): BusLine? { + return COMBINED_BUS_LINES.firstOrNull { it.id == id } +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/di/RepositoriesModule.kt b/app/src/main/java/com/am/stbus/data/static/BusStopsData.kt similarity index 55% rename from app/src/main/java/com/am/stbus/common/di/RepositoriesModule.kt rename to app/src/main/java/com/am/stbus/data/static/BusStopsData.kt index 8d9ad59..8088f4b 100644 --- a/app/src/main/java/com/am/stbus/common/di/RepositoriesModule.kt +++ b/app/src/main/java/com/am/stbus/data/static/BusStopsData.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,28 +22,42 @@ * SOFTWARE. */ -package com.am.stbus.common.di +package com.am.stbus.data.static -import com.am.stbus.data.repositoriesImpl.NewsRepositoryImpl -import com.am.stbus.data.repositoriesImpl.TimetableRepositoryImpl -import com.am.stbus.domain.repositories.NewsRepository -import com.am.stbus.domain.repositories.TimetableRepository -import org.koin.dsl.module +import com.am.stbus.R +import com.am.stbus.data.models.BusStop -val repositoriesModule = module { - - single { - NewsRepositoryImpl( - remoteNewsDataSource = get(), - localNewsDataSource = get() - ) - } - - single { - TimetableRepositoryImpl( - remoteTimetableDataSource = get(), - localTimetableDataSource = get() - ) - } +val BUS_ARRIVALS_STOPS = listOf( + BusStop( + id = 675258, + title = R.string.bus_stop_hnk + ), + BusStop( + id = 676742, + title = R.string.bus_stop_hnk_izlaz + ), + BusStop( + id = 675289, + title = R.string.bus_stop_pazar_poljicka + ), + BusStop( + id = 675290, + title = R.string.bus_stop_pazar_opcina + ), + BusStop( + id = 675287, + title = R.string.bus_stop_opcina_poljicka + ), + BusStop( + id = 675286, + title = R.string.bus_stop_opcina_dom_rata + ), + BusStop( + id = 676393, + title = R.string.bus_stop_sukoišan + ) +) +fun findBusStopArrivalPerId(id: Int): BusStop? { + return BUS_ARRIVALS_STOPS.firstOrNull { it.id == id } } \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/models/Information.kt b/app/src/main/java/com/am/stbus/domain/models/Information.kt deleted file mode 100644 index ddebe6e..0000000 --- a/app/src/main/java/com/am/stbus/domain/models/Information.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.domain.models - -data class Information(val informationId: Int, val viewType: Int, val informationTitle: String, val informationDesc: String) \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/models/Timetable.kt b/app/src/main/java/com/am/stbus/domain/models/Timetable.kt deleted file mode 100644 index 3857495..0000000 --- a/app/src/main/java/com/am/stbus/domain/models/Timetable.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.domain.models - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity -data class Timetable( - @PrimaryKey val lineId: Int, - @ColumnInfo(name = "line_number") val lineNumber: String, - @ColumnInfo(name = "gmaps_id") val gmapsId: Int, - @ColumnInfo(name = "area_id") val areaId: Int, - @ColumnInfo(name = "favourite") var favourite: Int, - @ColumnInfo(name = "content") var content: String, - @ColumnInfo(name = "content_date") var contentDate: String) \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/usecases/news/NewsDetailUseCase.kt b/app/src/main/java/com/am/stbus/domain/repositories/PrometApiRepository.kt similarity index 72% rename from app/src/main/java/com/am/stbus/domain/usecases/news/NewsDetailUseCase.kt rename to app/src/main/java/com/am/stbus/domain/repositories/PrometApiRepository.kt index 5f9b7de..e1e3712 100644 --- a/app/src/main/java/com/am/stbus/domain/usecases/news/NewsDetailUseCase.kt +++ b/app/src/main/java/com/am/stbus/domain/repositories/PrometApiRepository.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,16 +22,17 @@ * SOFTWARE. */ -package com.am.stbus.domain.usecases.news +package com.am.stbus.domain.repositories -import com.am.stbus.domain.models.NewsItem -import com.am.stbus.domain.repositories.NewsRepository -import io.reactivex.Single +import com.am.stbus.data.ApiService +import com.am.stbus.data.models.BusStopArrival +import retrofit2.Response -class NewsDetailUseCase(private val newsRepository: NewsRepository) { - - fun get(remote: Boolean, url: String): Single { - return newsRepository.getNewsDetail(remote, url) +class PrometApiRepository( + private val apiService: ApiService +) { + suspend fun getBusStopArrivals(busStopId: Int): Response> { + return apiService.getBusStopArrivals(busStopId = busStopId) } } \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/repositories/TimetableRepository.kt b/app/src/main/java/com/am/stbus/domain/repositories/TimetableRepository.kt deleted file mode 100644 index 8847fd5..0000000 --- a/app/src/main/java/com/am/stbus/domain/repositories/TimetableRepository.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.domain.repositories - -import com.am.stbus.domain.models.Timetable -import io.reactivex.Completable -import io.reactivex.Single - -interface TimetableRepository { - fun saveTimetables(list: List): Completable - fun getTimetables(): Single> - fun updateFavourites(lineId: Int, favourite: Int): Completable - fun getTimetableDetail(lineId: Int, url: String): Single - fun saveTimetableDetail(lineId: Int, content: String, contentDate: String): Completable -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/repositories/NewsRepository.kt b/app/src/main/java/com/am/stbus/domain/repositories/TimetablesRepository.kt similarity index 54% rename from app/src/main/java/com/am/stbus/domain/repositories/NewsRepository.kt rename to app/src/main/java/com/am/stbus/domain/repositories/TimetablesRepository.kt index 540524b..caeb703 100644 --- a/app/src/main/java/com/am/stbus/domain/repositories/NewsRepository.kt +++ b/app/src/main/java/com/am/stbus/domain/repositories/TimetablesRepository.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,14 +24,34 @@ package com.am.stbus.domain.repositories -import com.am.stbus.domain.models.NewsItem -import com.am.stbus.domain.models.NewsListItem -import io.reactivex.Single -import kotlinx.coroutines.CoroutineDispatcher +import com.am.stbus.common.Constants.NETWORK_REQUEST_TIMEOUT +import com.am.stbus.common.Constants.PROMET_ALL_LINES_URL +import com.am.stbus.common.Constants.PROMET_ALL_LINE_ID_URL +import org.jsoup.Jsoup +import org.jsoup.nodes.Document + +class TimetablesRepository { + + fun getTimetableId(websiteTitle: String): Int? { + + val timetablePath = + Jsoup.connect(PROMET_ALL_LINES_URL).timeout(NETWORK_REQUEST_TIMEOUT).get() + .select( + ".c-vozni-red__search-select option:contains(" + + websiteTitle + + ")" + ) + .attr("value") + + val timetableId = timetablePath.split("/").lastOrNull()?.toIntOrNull() + + return timetableId + } + + fun getTimetableForId(timetableId: Int): Document { + return Jsoup.connect(PROMET_ALL_LINE_ID_URL + timetableId) + .timeout(NETWORK_REQUEST_TIMEOUT).get() + } + -interface NewsRepository { - suspend fun getNewsList(remote: Boolean, dispatcher: CoroutineDispatcher): List - suspend fun saveNewsList(list: List) - suspend fun deleteNewsList() - fun getNewsDetail(remote: Boolean, url: String): Single } \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/usecases/timetables/TimetableDetailUseCase.kt b/app/src/main/java/com/am/stbus/domain/usecases/FavouritesRoomDbUseCase.kt similarity index 63% rename from app/src/main/java/com/am/stbus/domain/usecases/timetables/TimetableDetailUseCase.kt rename to app/src/main/java/com/am/stbus/domain/usecases/FavouritesRoomDbUseCase.kt index 976c765..e8c0219 100644 --- a/app/src/main/java/com/am/stbus/domain/usecases/timetables/TimetableDetailUseCase.kt +++ b/app/src/main/java/com/am/stbus/domain/usecases/FavouritesRoomDbUseCase.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,19 +22,28 @@ * SOFTWARE. */ -package com.am.stbus.domain.usecases.timetables +package com.am.stbus.domain.usecases -import com.am.stbus.domain.repositories.TimetableRepository -import io.reactivex.Completable -import io.reactivex.Single +import com.am.stbus.data.models.FavouriteItem +import com.am.stbus.data.room.FavouriteItemDao -class TimetableDetailUseCase(private val timetableRepository: TimetableRepository) { +class FavouritesRoomDbUseCase( + private val favouriteItemDao: FavouriteItemDao +) { + suspend fun getAllTimetable(): List { + return favouriteItemDao.getAll() + } - fun getTimetableDetail(lineId: Int, url: String): Single { - return timetableRepository.getTimetableDetail(lineId, url) + suspend fun add(id: Int, type: Int) { + favouriteItemDao.insert( + FavouriteItem( + id = id, + type = type + ) + ) } - fun saveTimetableDetail(lineId: Int, content: String, contentDate: String): Completable { - return timetableRepository.saveTimetableDetail(lineId, content, contentDate) + suspend fun remove(id: Int, type: Int) { + favouriteItemDao.delete(id, type) } } \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/common/helpers/Event.kt b/app/src/main/java/com/am/stbus/domain/usecases/GetBusStopArrivalsUseCase.kt similarity index 58% rename from app/src/main/java/com/am/stbus/common/helpers/Event.kt rename to app/src/main/java/com/am/stbus/domain/usecases/GetBusStopArrivalsUseCase.kt index a8d9e70..c7785f2 100644 --- a/app/src/main/java/com/am/stbus/common/helpers/Event.kt +++ b/app/src/main/java/com/am/stbus/domain/usecases/GetBusStopArrivalsUseCase.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2013 - 2021 Antonio Marin + * Copyright (c) 2013 - 2025 Antonio Marin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,31 +22,29 @@ * SOFTWARE. */ -package com.am.stbus.common.helpers +package com.am.stbus.domain.usecases -/** - * Used as a wrapper for data that is exposed via a LiveData that represents an event. - */ -open class Event(private val content: T) { - - var hasBeenHandled = false - private set // Allow external read but not write +import com.am.stbus.data.models.BusStopArrival +import com.am.stbus.domain.repositories.PrometApiRepository +import timber.log.Timber - /** - * Returns the content and prevents its use again. - */ - fun getContentIfNotHandled(action: (T) -> Unit) { - if (!hasBeenHandled) { - action(content) - hasBeenHandled = true - } - } +class GetBusStopArrivalsUseCase( + private val prometApiRepository: PrometApiRepository +) { + suspend fun run(busStopId: Int): Result> { + return try { + val response = prometApiRepository.getBusStopArrivals(busStopId) - /** - * Returns the content, even if it's already been handled. - */ - fun peekContent(): T = content -} + Timber.d("Debugging - response $response") -fun T.inEvent(): Event = Event(this) + if (response.isSuccessful) { + Result.success(response.body() ?: emptyList()) + } else { + Result.failure(Exception("error")) + } + } catch (exp: Exception) { + Result.failure(Exception(exp)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/usecases/GetTimetableDetailDataUseCase.kt b/app/src/main/java/com/am/stbus/domain/usecases/GetTimetableDetailDataUseCase.kt new file mode 100644 index 0000000..c5e7ee9 --- /dev/null +++ b/app/src/main/java/com/am/stbus/domain/usecases/GetTimetableDetailDataUseCase.kt @@ -0,0 +1,98 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.domain.usecases + +import com.am.stbus.domain.repositories.TimetablesRepository + +class GetTimetableDetailDataUseCase( + private val timetablesRepository: TimetablesRepository +) { + fun run(websiteTitle: String): Result { + return try { + + val timetableId = timetablesRepository.getTimetableId(websiteTitle) + + if (timetableId == null) { + return Result.failure(Exception("Missing timetableId")) + } + + val doc = timetablesRepository.getTimetableForId(timetableId) + + val workDayList = mutableListOf>() + val saturdayList = mutableListOf>() + val sundayList = mutableListOf>() + + val table = doc.select("table.c-vozni-red__table") + + table.select("tbody tr").forEach { row -> + val cells = row.select("td") + + // Column 0: Weekday + workDayList.add( + cells.getOrNull(0) + ?.select("span.c-vozni-red__box--new")?.map { + it.text() + } ?: emptyList() + ) + + // Column 1: Saturday + saturdayList.add( + cells.getOrNull(1) + ?.select("span.c-vozni-red__box--new")?.map { + it.text() + } ?: emptyList() + ) + + // Column 2: Sunday/Holiday + sundayList.add( + cells.getOrNull(2) + ?.select("span.c-vozni-red__box--new")?.map { + it.text() + } ?: emptyList() + ) + } + + Result.success( + TimetableDetailData( + validFrom = doc.select("p.c-vozni-red__valid").text(), + workdayItems = workDayList.toList(), + saturdayItems = saturdayList.toList(), + sundayList = sundayList.toList(), + notes = doc.select("div.c-vozni-red-note__items").text() + ) + ) + } catch (exp: Exception) { + Result.failure(exp) + } + } + + data class TimetableDetailData( + val validFrom: String, + val workdayItems: List>, + val saturdayItems: List>, + val sundayList: List>, + val notes: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/usecases/news/NewsListUseCase.kt b/app/src/main/java/com/am/stbus/domain/usecases/news/NewsListUseCase.kt deleted file mode 100644 index dd71346..0000000 --- a/app/src/main/java/com/am/stbus/domain/usecases/news/NewsListUseCase.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.domain.usecases.news - -import com.am.stbus.domain.models.NewsListItem -import com.am.stbus.domain.repositories.NewsRepository -import kotlinx.coroutines.CoroutineDispatcher - -class NewsListUseCase(private val newsRepository: NewsRepository) { - - suspend fun getLocalNewsList( - dispatcher: CoroutineDispatcher - ): List { - return newsRepository.getNewsList(false, dispatcher) - } - - suspend fun getRemoteNewsList( - dispatcher: CoroutineDispatcher - ): List { - newsRepository.deleteNewsList() - val list = newsRepository.getNewsList(true, dispatcher) - newsRepository.saveNewsList(list) - return list - } - - suspend fun deleteNewsList() { - newsRepository.deleteNewsList() - } - - suspend fun save(list: List) { - newsRepository.saveNewsList(list) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/domain/usecases/timetables/TimetableListUseCase.kt b/app/src/main/java/com/am/stbus/domain/usecases/timetables/TimetableListUseCase.kt deleted file mode 100644 index 19ca68e..0000000 --- a/app/src/main/java/com/am/stbus/domain/usecases/timetables/TimetableListUseCase.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.domain.usecases.timetables - -import com.am.stbus.domain.models.Timetable -import com.am.stbus.domain.repositories.TimetableRepository -import io.reactivex.Completable -import io.reactivex.Single - -class TimetableListUseCase(private val timetableRepository: TimetableRepository) { - - fun saveTimetables(list: List): Completable { - return timetableRepository.saveTimetables(list) - } - - fun getTimetables(): Single> { - return timetableRepository.getTimetables() - } - - fun updateFavourites(lineId: Int, favourite: Int): Completable { - return timetableRepository.updateFavourites(lineId, favourite) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/MainActivity.kt b/app/src/main/java/com/am/stbus/presentation/MainActivity.kt new file mode 100644 index 0000000..ae3b2af --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/MainActivity.kt @@ -0,0 +1,197 @@ +package com.am.stbus.presentation + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DirectionsBus +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.rounded.DepartureBoard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator +import androidx.navigation3.runtime.NavKey +import androidx.navigation3.runtime.entryProvider +import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator +import androidx.navigation3.ui.NavDisplay +import com.am.stbus.data.models.BusLine +import com.am.stbus.data.models.BusStop +import com.am.stbus.presentation.screens.home.HomeScreen +import com.am.stbus.presentation.screens.stops.detail.BusStopArrivalsDetailScreen +import com.am.stbus.presentation.screens.stops.list.BusStopArrivalsListScreen +import com.am.stbus.presentation.screens.timetables.detail.TimetablesDetailScreen +import com.am.stbus.presentation.screens.timetables.list.TimetablesScreen +import com.am.stbus.presentation.theme.SplitBusTheme +import kotlinx.serialization.Serializable +import org.koin.androidx.viewmodel.ext.android.viewModel + +class MainActivity : ComponentActivity() { + + private val viewModel: MainViewModel by viewModel() + + @OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + + SplitBusTheme { + + val backStack = remember { mutableStateListOf(HomeScreenKey) } + + val barsVisible = TOP_LEVEL_ROUTES.map { it.navKey }.contains(backStack.last()) + + val busStopFavourites = viewModel.busStopFavourites + + val busLinesFavourites = viewModel.busLinesFavourites + + Scaffold( + bottomBar = { + if (barsVisible) { + NavigationBar { + TOP_LEVEL_ROUTES.forEach { topLevelRoute -> + + val isSelected = topLevelRoute.navKey == backStack.last() + + NavigationBarItem( + selected = isSelected, + onClick = { + backStack.add(topLevelRoute.navKey) + if (backStack.size > 2) { + backStack.removeAt(1) + } + }, + icon = { + Icon( + imageVector = topLevelRoute.icon, + contentDescription = null + ) + } + ) + } + } + } + } + ) { innerPadding -> + NavDisplay( + modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), + entryDecorators = listOf( + rememberSaveableStateHolderNavEntryDecorator(), + rememberViewModelStoreNavEntryDecorator() + ), + backStack = backStack, + onBack = { backStack.removeLastOrNull() }, + entryProvider = entryProvider { + entry { + HomeScreen( + busStopFavourites = busStopFavourites, + busLinesFavourites = busLinesFavourites, + onBusStopClicked = { busStop -> + backStack.add(BusStopArrivalsDetailScreenKey(busStop)) + }, + onBusLineClicked = { busLine -> + backStack.add(TimetableDetailScreenKey(busLine)) + } + ) + } + entry { + BusStopArrivalsListScreen { + backStack.add(BusStopArrivalsDetailScreenKey(it)) + } + } + entry { + val favourite = busStopFavourites.contains(it.busStop) + BusStopArrivalsDetailScreen( + busStop = it.busStop, + favourite = favourite, + onFavouriteClicked = { busStop -> + if (favourite) { + viewModel.removeBusStopFromFavourites(busStop) + } else { + viewModel.addBusStopToFavourites(busStop) + } + }, + onBackClicked = { + backStack.removeAt(backStack.lastIndex) + } + ) + } + entry { + TimetablesScreen { + backStack.add(TimetableDetailScreenKey(it)) + } + } + entry { + val favourite = busLinesFavourites.contains(it.busLine) + TimetablesDetailScreen( + busLine = it.busLine, + favourite = favourite, + onFavouriteClicked = { busLine -> + if (favourite) { + viewModel.removeBusLineFromFavourites(busLine) + } else { + viewModel.addBusLineToFavourites(busLine) + } + }, + onBackClicked = { + backStack.removeAt(backStack.lastIndex) + } + ) + } + } + ) + } + } + } + } +} + +// Bottom Bar +private sealed interface BottomBarRoutes { + val icon: ImageVector + val navKey: NavKey +} + +private data object Home : BottomBarRoutes { + override val icon = Icons.Default.Home + override val navKey: NavKey = HomeScreenKey +} + +private data object BusStops : BottomBarRoutes { + override val icon = Icons.Rounded.DepartureBoard + override val navKey: NavKey = BusStopsScreenKey +} + +private data object TimetablesList : BottomBarRoutes { + override val icon = Icons.Default.DirectionsBus + override val navKey: NavKey = TimetablesListScreenKey +} + +private val TOP_LEVEL_ROUTES: List = + listOf(Home, BusStops, TimetablesList) + + +@Serializable +data object HomeScreenKey : NavKey + +@Serializable +data object BusStopsScreenKey : NavKey + +@Serializable +data object TimetablesListScreenKey : NavKey + +@Serializable +data class TimetableDetailScreenKey(val busLine: BusLine) : NavKey + +@Serializable +data class BusStopArrivalsDetailScreenKey(val busStop: BusStop) : NavKey diff --git a/app/src/main/java/com/am/stbus/presentation/MainViewModel.kt b/app/src/main/java/com/am/stbus/presentation/MainViewModel.kt new file mode 100644 index 0000000..0951bf1 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/MainViewModel.kt @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation + +import androidx.compose.runtime.mutableStateListOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.am.stbus.data.models.BusLine +import com.am.stbus.data.models.BusStop +import com.am.stbus.data.static.findBusLinePerId +import com.am.stbus.data.static.findBusStopArrivalPerId +import com.am.stbus.domain.usecases.FavouritesRoomDbUseCase +import kotlinx.coroutines.launch + +class MainViewModel( + private val favouritesRoomDbUseCase: FavouritesRoomDbUseCase +) : ViewModel() { + + val busStopFavourites = + mutableStateListOf() + + val busLinesFavourites = + mutableStateListOf() + + init { + getAllFavourites() + } + + fun getAllFavourites() { + viewModelScope.launch { + val favourites = favouritesRoomDbUseCase.getAllTimetable() + + val busStops = + favourites.filter { it.type == 0 }.mapNotNull { findBusStopArrivalPerId(it.id) } + val busLines = favourites.filter { it.type == 1 }.mapNotNull { findBusLinePerId(it.id) } + + busStopFavourites.addAll(busStops) + busLinesFavourites.addAll(busLines) + } + } + + fun addBusStopToFavourites(busStop: BusStop) { + viewModelScope.launch { + favouritesRoomDbUseCase.add(busStop.id, 0) + } + + busStopFavourites.add(busStop) + } + + fun removeBusStopFromFavourites(busStop: BusStop) { + viewModelScope.launch { + favouritesRoomDbUseCase.remove(busStop.id, 0) + } + + busStopFavourites.remove(busStop) + } + + fun addBusLineToFavourites(busLine: BusLine) { + viewModelScope.launch { + favouritesRoomDbUseCase.add(busLine.id, 1) + } + + busLinesFavourites.add(busLine) + } + + fun removeBusLineFromFavourites(busLine: BusLine) { + viewModelScope.launch { + favouritesRoomDbUseCase.remove(busLine.id, 1) + } + + busLinesFavourites.remove(busLine) + } + +} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/MainActivity.kt b/app/src/main/java/com/am/stbus/presentation/screens/MainActivity.kt deleted file mode 100644 index d50d7ab..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/MainActivity.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens - -import android.os.Build -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.app.AppCompatDelegate -import androidx.core.content.ContextCompat -import androidx.core.view.isVisible -import androidx.navigation.NavController -import androidx.navigation.fragment.NavHostFragment -import androidx.navigation.ui.NavigationUI -import androidx.preference.PreferenceManager -import com.am.stbus.R -import com.am.stbus.common.extensions.changeStatusBarColor -import kotlinx.android.synthetic.main.activity_main.* - - -class MainActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - setupAppTheme() - - //container.systemUiVisibilityFullScreen() - - val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment - val navigationController = navHostFragment.navController - - // Za skrivanje bottomNavBara - navigationController.addOnDestinationChangedListener(onDestinationChangedListener) - - // This is extremely cool, link bottomNavigationBar with Navigation Controller - // Because we link them there's no need for BottomNavigationView.onNavigationItemSelectedListener - // NOTE: id's must same inside nav_graph.xml and bottom_nav_menu.xml - NavigationUI.setupWithNavController(nav_view, navigationController) - } - - override fun onSupportNavigateUp(): Boolean { - onBackPressed() - return true - } - - private val onDestinationChangedListener = NavController.OnDestinationChangedListener { controller, destination, arguments -> - if (destination.id in ROOT_FRAGMENTS) { - nav_view.visibility = View.VISIBLE - supportActionBar?.setDisplayHomeAsUpEnabled(false) - supportActionBar?.setDisplayShowHomeEnabled(false) - setupWhiteNavigationNavigationBarColor(true) - } else { - nav_view.visibility = View.GONE - supportActionBar?.setDisplayHomeAsUpEnabled(true) - supportActionBar?.setDisplayShowHomeEnabled(true) - setupWhiteNavigationNavigationBarColor(false) - } - - if (destination.id == R.id.informationGmapsFragment) { - container.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE) - this.changeStatusBarColor(R.color.poluprozirniStatusBar) - } else { - container.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE - this.changeStatusBarColor(R.color.colorPrimaryDark) - } - - app_bar.isVisible = destination.id == R.id.settingsFragment - } - - private fun setupWhiteNavigationNavigationBarColor(white: Boolean) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) { - window.navigationBarColor = ContextCompat.getColor(this, R.color.crna) - } - else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { - if (white) { - window.navigationBarColor = ContextCompat.getColor(this, R.color.colorSystemNavigationBackground) - } else { - window.navigationBarColor = ContextCompat.getColor(this, R.color.prozirnaAndroid) - } - } - - } - - fun setupAppTheme() { - val preference = PreferenceManager.getDefaultSharedPreferences(this) - when (preference.getString("darkMode", "1")) { - "1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) - "2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - "3" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - } - } - - companion object { - - val ROOT_FRAGMENTS = intArrayOf( - R.id.favouriteFragment, - R.id.timetablesFragment, - R.id.informationListFragment, - R.id.settingsFragment - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/common/AppBarScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/common/AppBarScreen.kt new file mode 100644 index 0000000..90346ef --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/common/AppBarScreen.kt @@ -0,0 +1,87 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.common + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AppBarScreen( + modifier: Modifier = Modifier, + title: String, + titleColour: Color, + showBackButton: Boolean = false, + onBackClicked: () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + content: @Composable ColumnScope.() -> Unit = {} +) { + Column( + modifier = modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + TopAppBar( + title = { + Text( + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontWeight = FontWeight.Bold, + color = titleColour, + text = title + ) + }, + actions = actions, + navigationIcon = { + if (showBackButton) { + IconButton(onClick = { onBackClicked() }) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = titleColour + ) + } + } + } + ) + content() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/common/ErrorScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/common/ErrorScreen.kt new file mode 100644 index 0000000..390e618 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/common/ErrorScreen.kt @@ -0,0 +1,123 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.common + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.core.net.toUri +import com.am.stbus.R +import com.am.stbus.common.Constants.PROMET_URL +import com.am.stbus.presentation.theme.SplitBusTheme + +@Composable +fun ErrorScreen( + linija: String +) { + + val context = LocalContext.current + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.Center + ) { + Text( + modifier = Modifier.fillMaxWidth(), + style = MaterialTheme.typography.headlineMedium, + text = stringResource(R.string.snippet_error_title), + textAlign = TextAlign.Center + ) + Spacer(modifier = Modifier.height(12.dp)) + Text( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.snippet_error_desc), + textAlign = TextAlign.Center + ) + Spacer(modifier = Modifier.height(12.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + OutlinedButton( + onClick = { + context.startActivity( + Intent.createChooser( + Intent( + Intent.ACTION_SENDTO, + Uri.fromParts("mailto", "antoniomarinnn@gmail.com", null) + ).apply { + putExtra(Intent.EXTRA_SUBJECT, "Split Bus") + putExtra( + Intent.EXTRA_TEXT, + "Molimo opisite problem!(Please describe your issue!)" + + "\n Linija: $linija \n\n" + ) + }, "Email" + ) + ) + } + ) { + Text(stringResource(R.string.snippet_error_report)) + } + Spacer(modifier = Modifier.width(12.dp)) + OutlinedButton( + onClick = { + context.startActivity(Intent(Intent.ACTION_VIEW, PROMET_URL.toUri())) + } + ) { + Text(stringResource(R.string.snippet_error_promet)) + } + } + + } +} + +@Preview +@Composable +fun ErrorScreenPreview() { + SplitBusTheme { + ErrorScreen("test") + } +} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/common/LoadingScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/common/LoadingScreen.kt new file mode 100644 index 0000000..89725b6 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/common/LoadingScreen.kt @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.common + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.CircularWavyProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.LoadingIndicator +import androidx.compose.material3.LoadingIndicatorDefaults +import androidx.compose.material3.MaterialShapes +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.am.stbus.presentation.theme.SplitBusTheme + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun LoadingScreen() { + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + LoadingIndicator( + modifier = Modifier + .scale(4f) + .padding(top = 24.dp) + .align(Alignment.Center), + color = MaterialTheme.colorScheme.secondary, + polygons = LoadingIndicatorDefaults.IndeterminateIndicatorPolygons + ) + + CircularWavyProgressIndicator( + modifier = Modifier + .scale(7f) + .padding(top = 64.dp, start = 8.dp, end = 0.dp) + .align(Alignment.CenterEnd), + color = MaterialTheme.colorScheme.primary, + stroke = Stroke(width = 0.3f), + amplitude = 1f + ) + + LoadingIndicator( + modifier = Modifier + .scale(2.5f) + .padding(end = 64.dp, top = 148.dp) + .align(Alignment.Center), + color = MaterialTheme.colorScheme.tertiary, + polygons = listOf( + MaterialShapes.SoftBoom, + MaterialShapes.Sunny, + MaterialShapes.Gem, + MaterialShapes.Pill, + MaterialShapes.Clover4Leaf + ) + ) + } +} + +@Preview +@Composable +fun LoadingScreenPreview() { + SplitBusTheme { + LoadingScreen() + } +} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesAdapter.kt b/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesAdapter.kt deleted file mode 100644 index 00fe7cc..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesAdapter.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.favourites - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.PopupMenu -import androidx.appcompat.view.ContextThemeWrapper -import androidx.recyclerview.widget.RecyclerView -import com.am.stbus.R -import com.am.stbus.common.TimetablesData -import com.am.stbus.domain.models.Timetable -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment.Companion.FAVOURITE_ADDED -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment.Companion.FAVOURITE_REMOVED -import kotlinx.android.synthetic.main.item_row_timetable.view.* - -class FavouritesAdapter(val context: Context?, - var onClickListener: (Timetable) -> Unit, - var onClickFavourites: (Int, Timetable) -> Unit, - var onClickMenuGmaps: (Timetable) -> Unit -) : RecyclerView.Adapter() { - - private var items = mutableListOf() - - fun addEntireData(timetables: List) { - items.addAll(timetables) - //notifyItemRangeInserted(0, timetables.size) - // Dodati remove funkcije... - notifyDataSetChanged() - } - - fun removeFavourite(position: Int) { - items.removeAt(position) - notifyItemRemoved(position) - notifyItemRangeChanged(position, items.size) - } - - fun addItem(news: Timetable) { - items.add(news) - } - - fun clear() { - items.clear() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationsViewHolder { - val itemView = LayoutInflater.from(parent.context) - .inflate(R.layout.item_row_timetable, parent, false) - return NotificationsViewHolder(itemView) - } - - override fun getItemCount(): Int { - return items.size - } - - override fun onBindViewHolder(holder: NotificationsViewHolder, position: Int) { - holder.bind(position) - } - - inner class NotificationsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - - fun bind(position: Int) { - - val item = items[position] - - itemView.apply { - tv_line_id.text = item.lineNumber - tv_line_name.text = context.getString(TimetablesData().getTimetableTitle(item.lineId)) - setOnClickListener { - onClickListener(item) - } - - iv_menu.setOnClickListener { - PopupMenu(ContextThemeWrapper(it.context, R.style.ListPopup), it).apply { - inflate(R.menu.menu_timetable_list) - - // Favourites labels - menu.findItem(R.id.action_favourites).let { menuItem -> - when (item.favourite) { - FAVOURITE_REMOVED -> menuItem.setTitle(R.string.timetables_menu_add_to_favourites) - FAVOURITE_ADDED -> menuItem.setTitle(R.string.timetables_menu_remove_from_favourites) - else -> throw IllegalArgumentException("Illegal favourite status ${item.favourite}") - } - } - - // onClickListeners - setOnMenuItemClickListener { menuItem -> - when (menuItem.itemId) { - R.id.action_favourites -> onClickFavourites(position, item) - R.id.action_gmaps -> onClickMenuGmaps(item) - } - true - } - }.show() - } - } - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesFragment.kt deleted file mode 100644 index 67d8336..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesFragment.kt +++ /dev/null @@ -1,177 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.favourites - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.navigation.findNavController -import androidx.recyclerview.widget.LinearLayoutManager -import com.am.stbus.BuildConfig -import com.am.stbus.R -import com.am.stbus.domain.models.Timetable -import com.am.stbus.presentation.screens.settings.ContentFragment.Companion.FIRST_RUN_CONTENT -import com.am.stbus.presentation.screens.settings.ContentFragment.Companion.UPDATE_APP_CONTENT -import com.am.stbus.presentation.screens.timetables.TimetablesSharedViewModel -import kotlinx.android.synthetic.main.fragment_favourites.* -import org.koin.androidx.navigation.koinNavGraphViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel -import timber.log.Timber - -class FavouritesFragment : Fragment() { - - private val timetablesSharedViewModel: TimetablesSharedViewModel by koinNavGraphViewModel(R.id.nav_graph) - - private val viewModel: FavouritesViewModel by viewModel() - - private val favouriteAdapter by lazy { - FavouritesAdapter( - requireContext(), - { onTimetableClicked(it) }, - { position, timetable -> onTimetableFavouritesClicked(position, timetable) }, - { onTimetableGmapsClicked(it) } - ) - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_favourites, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.title = getString(R.string.nav_favourites) - - rv_timetables.apply { - layoutManager = LinearLayoutManager(requireContext()) - setHasFixedSize(false) - adapter = favouriteAdapter - } - - timetablesSharedViewModel.timetables.observe(viewLifecycleOwner) { timetables -> - val favouriteTimetables = timetables?.filter { it.favourite == 1 } ?: emptyList() - populateFavouriteTimetables(favouriteTimetables) - } - - viewModel.timetableList.observe(viewLifecycleOwner) { timetablesEvent -> - timetablesEvent.getContentIfNotHandled { - populateFavouriteTimetables(it) - } - } - - viewModel.removedFavourite.observe(viewLifecycleOwner) { - timetablesSharedViewModel.updateFavourite(it.lineId, it.favourite) - } - - checkShouldWelcomeBeShown() - } - - private fun populateFavouriteTimetables(timetables: List) { - - favouriteAdapter.clear() - favouriteAdapter.addEntireData(timetables) - - rv_timetables.isVisible = timetables.isNotEmpty() - tv_empty_title.isVisible = timetables.isEmpty() - tv_empty_message.isVisible = timetables.isEmpty() - - - } - - private fun checkShouldWelcomeBeShown() { - val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return - - when (sharedPref.getInt( - SHARED_PREFS_BUILD_VERSION_KEY, - SHARED_PREFS_BUILD_VERSION_DEFAULT_VALUE - )) { - BuildConfig.VERSION_CODE -> Timber.i("All good! :)") - SHARED_PREFS_BUILD_VERSION_DEFAULT_VALUE -> showWelcomeFragment(FIRST_RUN_CONTENT) - else -> showWelcomeFragment(UPDATE_APP_CONTENT) - } - } - - private fun showWelcomeFragment(updateAppContent: Int) { - - // Pokreni Welcome fragment - requireView().findNavController().navigate( - FavouritesFragmentDirections.actionFavouriteFragmentToContentFragment( - when (updateAppContent) { - FIRST_RUN_CONTENT -> FIRST_RUN_CONTENT - UPDATE_APP_CONTENT -> UPDATE_APP_CONTENT - else -> throw IllegalArgumentException("Wrong first run argument") - } - ) - ) - - // Update value in shared prefs - val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return - with(sharedPref.edit()) { - putInt(SHARED_PREFS_BUILD_VERSION_KEY, BuildConfig.VERSION_CODE) - commit() - } - - } - - private fun onTimetableClicked(timetable: Timetable) { - requireView().findNavController().navigate( - FavouritesFragmentDirections - .actionFavouriteFragmentToTimetableDetailFragment( - timetable.lineId, - timetable.lineNumber, - timetable.gmapsId, - timetable.areaId, - timetable.favourite, - timetable.content, - timetable.contentDate - ) - ) - } - - private fun onTimetableFavouritesClicked(position: Int, timetable: Timetable) { - viewModel.removeFavouritesStatus(position, timetable) - } - - private fun onTimetableGmapsClicked(timetable: Timetable) { - requireView().findNavController().navigate( - FavouritesFragmentDirections - .actionFavouriteFragmentToInformationGmapsFragment(timetable.gmapsId) - ) - } - - companion object { - const val SHARED_PREFS_SPLIT_BUS = "SB_PREFERENCES" - const val SHARED_PREFS_BUILD_VERSION_KEY = "BUILD_VERSION" - const val SHARED_PREFS_BUILD_VERSION_DEFAULT_VALUE = 0 - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesViewModel.kt deleted file mode 100644 index b7ffede..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/favourites/FavouritesViewModel.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.favourites - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.am.stbus.common.TimetablesData -import com.am.stbus.common.helpers.Event -import com.am.stbus.common.helpers.inEvent -import com.am.stbus.domain.models.Timetable -import com.am.stbus.domain.usecases.timetables.TimetableListUseCase -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment.Companion.FAVOURITE_REMOVED -import io.reactivex.CompletableObserver -import io.reactivex.SingleObserver -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers - -class FavouritesViewModel(private val timetableListUseCase: TimetableListUseCase) : ViewModel() { - - private val schedulers = Schedulers.io() - private val thread = AndroidSchedulers.mainThread() - - private val _timetableList = MutableLiveData>>() - val timetableList: LiveData>> - get() = _timetableList - - private val _removedFavourite = MutableLiveData() - val removedFavourite: LiveData - get() = _removedFavourite - - init { - getTimetables() - } - - private fun getTimetables() { - timetableListUseCase.getTimetables() - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object : SingleObserver> { - override fun onSuccess(timetables: List) { - if (timetables.isEmpty()) { - saveTimetables() - } else { - val favouriteTimetables = timetables.filter { it.favourite == 1 } - _timetableList.postValue(favouriteTimetables.inEvent()) - } - } - - override fun onSubscribe(d: Disposable) { - // TODO - } - - override fun onError(e: Throwable) { - // TODO - } - - }) - } - - private fun saveTimetables() { - timetableListUseCase.saveTimetables(TimetablesData.list) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object : CompletableObserver { - override fun onComplete() { - _timetableList.postValue(emptyList().inEvent()) - } - - override fun onSubscribe(d: Disposable) { - } - - override fun onError(e: Throwable) { - } - }) - } - - fun removeFavouritesStatus(position: Int, timetable: Timetable) { - timetableListUseCase.updateFavourites(timetable.lineId, FAVOURITE_REMOVED) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object : CompletableObserver { - override fun onComplete() { - _removedFavourite.postValue( - TimetablesListFragment.UpdatedFavourite( - position, - timetable.lineId, - FAVOURITE_REMOVED - ) - ) - } - - override fun onSubscribe(d: Disposable) { - } - - override fun onError(e: Throwable) { - } - }) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/home/HomeScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/home/HomeScreen.kt new file mode 100644 index 0000000..5c82db1 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/home/HomeScreen.kt @@ -0,0 +1,129 @@ +package com.am.stbus.presentation.screens.home + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.BusAlert +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.am.stbus.R +import com.am.stbus.data.models.BusLine +import com.am.stbus.data.models.BusStop +import com.am.stbus.presentation.screens.common.AppBarScreen +import com.am.stbus.presentation.screens.stops.list.BusStopItemView +import com.am.stbus.presentation.screens.timetables.list.BusLineItemView +import com.am.stbus.presentation.theme.SplitBusTheme + +@Composable +fun HomeScreen( + busStopFavourites: List, + busLinesFavourites: List, + onBusStopClicked: (BusStop) -> Unit, + onBusLineClicked: (BusLine) -> Unit +) { + + val emptyScreen = busStopFavourites.isEmpty() && busLinesFavourites.isEmpty() + + AppBarScreen( + title = stringResource(R.string.nav_favourites), + titleColour = MaterialTheme.colorScheme.primary + ) { + if (emptyScreen) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + modifier = Modifier.size(52.dp), + imageVector = Icons.Rounded.BusAlert, + contentDescription = "Back", + tint = MaterialTheme.colorScheme.primary, + ) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp, start = 16.dp, end = 16.dp), + style = MaterialTheme.typography.headlineSmall, + textAlign = TextAlign.Center, + text = stringResource(id = R.string.favourite_empty_title) + ) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp, start = 16.dp, end = 16.dp), + textAlign = TextAlign.Center, + text = stringResource(id = R.string.favourite_empty_message) + ) + } + } else { + if (busStopFavourites.isNotEmpty()) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + text = stringResource(R.string.nav_bus_stops), + fontSize = 18.sp, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.tertiary + ) + busStopFavourites.forEach { busStop -> + BusStopItemView( + busStop = busStop, + onBusStopClicked = onBusStopClicked + ) + } + } + if (busLinesFavourites.isNotEmpty()) { + Spacer(modifier = Modifier.height(12.dp)) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + text = stringResource(R.string.nav_timetables), + fontSize = 18.sp, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.secondary + ) + busLinesFavourites.forEach { + BusLineItemView( + busLine = it, + onBusLineClicked = onBusLineClicked + ) + } + } + } + } + +} + +@Preview +@Composable +fun HomeScreenPreview() { + SplitBusTheme { + Row( + modifier = Modifier + .fillMaxSize() + ) { + HomeScreen(emptyList(), emptyList(), {}) { } + //omeScreen() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListAdapter.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListAdapter.kt deleted file mode 100644 index 6ed44cd..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListAdapter.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.recyclerview.widget.RecyclerView -import com.am.stbus.R -import com.am.stbus.common.InformationConstants.TYPE_HEADER -import com.am.stbus.common.InformationConstants.TYPE_ITEM -import com.am.stbus.domain.models.Information -import kotlinx.android.synthetic.main.item_row_information_header.view.* -import kotlinx.android.synthetic.main.item_row_information_item.view.* - - -class InformationListAdapter(val context: Context?, - var onClickListener: (Information) -> Unit -) : RecyclerView.Adapter() { - - private var informationList = mutableListOf() - private val positionsThatShouldHideDivider: List = listOf(1, 6, 11) - - fun addEntireData(notificationsData: List) { - informationList.addAll(notificationsData) - notifyDataSetChanged() - } - - fun addItem(information: Information) { - informationList.add(information) - } - - fun clear() { - informationList.clear() - } - - fun isEmpty(): Boolean { - return informationList.isEmpty() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return when(viewType) { - TYPE_HEADER -> HeaderViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_row_information_header, parent, false)) - TYPE_ITEM -> ItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_row_information_item, parent, false)) - else -> throw IllegalArgumentException("Wrong type") - } - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (holder) { - is ItemViewHolder -> { - holder.bind(position) - } - is HeaderViewHolder -> { - holder.bind(position) - } - } - - } - - override fun getItemViewType(position: Int): Int { - return informationList[position].viewType - } - - override fun getItemCount(): Int { - return informationList.size - } - - - inner class HeaderViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { - - fun bind(position: Int) { - - val information = informationList[position] - - itemView.apply { - tv_category.text = information.informationTitle - } - } - } - - inner class ItemViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { - - fun bind(position: Int) { - - val information = informationList[position] - - itemView.apply { - tv_title.text = information.informationTitle - tv_desc.text = information.informationDesc - - view.isVisible = !positionsThatShouldHideDivider.contains(position) - - setOnClickListener { - onClickListener(information) - } - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListFragment.kt deleted file mode 100644 index 408e3d4..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/InformationListFragment.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information - -import android.content.Intent -import android.content.SharedPreferences -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.browser.customtabs.CustomTabsIntent -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment -import androidx.navigation.Navigation -import androidx.recyclerview.widget.LinearLayoutManager -import com.am.stbus.R -import com.am.stbus.common.Constants.PROMET_URL -import com.am.stbus.common.InformationConstants.GARAZE_URL -import com.am.stbus.common.InformationConstants.ID_CYCLES -import com.am.stbus.common.InformationConstants.ID_GARAGES -import com.am.stbus.common.InformationConstants.ID_GENERAL_CATEGORY -import com.am.stbus.common.InformationConstants.ID_GMAPS -import com.am.stbus.common.InformationConstants.ID_LATEST_NEWS -import com.am.stbus.common.InformationConstants.ID_MAPS_CATEGORY -import com.am.stbus.common.InformationConstants.ID_PARKING -import com.am.stbus.common.InformationConstants.ID_PROMET_WEB -import com.am.stbus.common.InformationConstants.ID_SUBURBAN_MAP -import com.am.stbus.common.InformationConstants.ID_TARIFF_ZONES_MAP -import com.am.stbus.common.InformationConstants.ID_TOURIST_INFO -import com.am.stbus.common.InformationConstants.ID_URBAN_MAP -import com.am.stbus.common.InformationConstants.ID_WEBSITES_CATEGORY -import com.am.stbus.common.InformationConstants.KARTA_GRAD_URL -import com.am.stbus.common.InformationConstants.KARTA_PRIGRAD_URL -import com.am.stbus.common.InformationConstants.NEXT_BIKE_URL -import com.am.stbus.common.InformationConstants.PARKING_URL -import com.am.stbus.common.InformationConstants.TARIFNE_URL -import com.am.stbus.common.InformationConstants.TYPE_HEADER -import com.am.stbus.common.InformationConstants.TYPE_ITEM -import com.am.stbus.domain.models.Information -import kotlinx.android.synthetic.main.fragment_information_list.* -import org.koin.android.ext.android.inject - -class InformationListFragment : Fragment() { - - private val preferencesManager: SharedPreferences by inject() - - private val informationListAdapter by lazy { - InformationListAdapter(requireContext()) { onInformationClicked(it) } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_information_list, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.title = getString(R.string.nav_information) - - recyclerView.apply { - layoutManager = LinearLayoutManager(requireContext()) - setHasFixedSize(true) - adapter = informationListAdapter - } - - if (informationListAdapter.isEmpty()) { - informationListAdapter.addEntireData(generateInformationList()) - } - } - - private fun onInformationClicked(information: Information) { - when (information.informationId) { - ID_LATEST_NEWS -> navigateToFragment(information) - ID_TOURIST_INFO -> Toast.makeText(requireContext(), requireContext().getText(R.string.error_still_not_finished), Toast.LENGTH_SHORT).show() - ID_GMAPS -> navigateToFragment(information) - ID_URBAN_MAP -> navigateToFragment(information) - ID_SUBURBAN_MAP -> navigateToFragment(information) - ID_TARIFF_ZONES_MAP -> navigateToFragment(information) - ID_CYCLES -> loadUrl(NEXT_BIKE_URL) - ID_PARKING -> loadUrl(PARKING_URL) - ID_GARAGES -> loadUrl(GARAZE_URL) - ID_PROMET_WEB -> loadUrl(PROMET_URL) - else -> throw IllegalArgumentException("Wrong information destination!") - } - } - - private fun navigateToFragment(information: Information) { - Navigation.findNavController(requireView()).navigate(let { - when (information.informationId) { - ID_LATEST_NEWS -> InformationListFragmentDirections.actionInformationListFragmentToInformationNewsListFragment() - //ID_TOURIST_INFO -> Toast.makeText(context, context!!.getText(R.string.error_still_not_finished), Toast.LENGTH_SHORT).show() - ID_GMAPS -> InformationListFragmentDirections.actionInformationListFragmentToInformationGmapsFragment(0) - ID_URBAN_MAP -> InformationListFragmentDirections.actionInformationListFragmentToInformationImageViewFragment(getString(R.string.information_urban_map_title), KARTA_GRAD_URL) - ID_SUBURBAN_MAP -> InformationListFragmentDirections.actionInformationListFragmentToInformationImageViewFragment(getString(R.string.information_suburban_map_title), KARTA_PRIGRAD_URL) - ID_TARIFF_ZONES_MAP -> InformationListFragmentDirections.actionInformationListFragmentToInformationImageViewFragment(getString(R.string.information_tariff_zones_title), TARIFNE_URL) - //ID_CYCLES -> InformationListFragmentDirections.actionInformationListFragmentToInformationWebViewFragment(getString(R.string.information_cycles_title), NEXT_BIKE_IFRAME) - else -> throw IllegalArgumentException("Wrong information destination!") - } - }) - } - - private fun startGmapsActivity() { - Toast.makeText(requireContext(), R.string.information_gmaps_not_ready, Toast.LENGTH_SHORT).show() -// val intent = Intent(context, GmapsActivity::class.java) -// startActivity(intent) - } - - private fun loadUrl(url: String) { - if (preferencesManager.getBoolean("open_urls", true)) { - val customTabsIntent : CustomTabsIntent = buildCustomTabsIntent() - customTabsIntent.launchUrl(requireContext(), Uri.parse(url)) - } else { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - requireContext().startActivity(intent) - } - } - - private fun buildCustomTabsIntent(): CustomTabsIntent { - val intentBuilder = CustomTabsIntent.Builder() - // Show the title - intentBuilder.setShowTitle(true) - // Set the color of Toolbar - intentBuilder.setToolbarColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary)) - return intentBuilder.build() - } - - private fun generateInformationList(): List { - return listOf( - Information(ID_GENERAL_CATEGORY, TYPE_HEADER, getString(R.string.information_news_general_category), ""), - Information(ID_LATEST_NEWS, TYPE_ITEM, getString(R.string.information_news_title), getString(R.string.information_news_desc)), - //Information(ID_TOURIST_INFO, TYPE_ITEM, getString(R.string.information_tourist_title), getString(R.string.information_tourist_desc)), - // Karte - Information(ID_MAPS_CATEGORY, TYPE_HEADER, getString(R.string.information_news_maps_category), ""), - Information(ID_GMAPS, TYPE_ITEM, getString(R.string.information_gmaps_title), getString(R.string.information_gmaps_desc)), - Information(ID_URBAN_MAP, TYPE_ITEM, getString(R.string.information_urban_map_title), getString(R.string.information_urban_map_desc)), - Information(ID_SUBURBAN_MAP, TYPE_ITEM, getString(R.string.information_suburban_map_title), getString(R.string.information_suburban_map_desc)), - Information(ID_TARIFF_ZONES_MAP, TYPE_ITEM, getString(R.string.information_tariff_zones_title), getString(R.string.information_tariff_zones_desc)), - // Ostali web - Information(ID_WEBSITES_CATEGORY, TYPE_HEADER, getString(R.string.information_websites_category), ""), - Information(ID_CYCLES, TYPE_ITEM, getString(R.string.information_cycles_title), getString(R.string.information_cycles_desc)), - Information(ID_PARKING, TYPE_ITEM, getString(R.string.information_parking_title), getString(R.string.information_parking_desc)), - Information(ID_GARAGES, TYPE_ITEM, getString(R.string.information_garage_title), getString(R.string.information_garage_desc)), - Information(ID_PROMET_WEB, TYPE_ITEM, getString(R.string.information_promet_web), getString(R.string.information_promet_desc)) - ) - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationGmapsFragment/InformationGmapsFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationGmapsFragment/InformationGmapsFragment.kt deleted file mode 100644 index 1bb4544..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationGmapsFragment/InformationGmapsFragment.kt +++ /dev/null @@ -1,332 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationGmapsFragment - -import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.appcompat.app.AlertDialog -import androidx.core.content.ContextCompat -import androidx.core.content.PermissionChecker -import androidx.core.content.PermissionChecker.checkSelfPermission -import androidx.core.view.ViewCompat -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import androidx.preference.PreferenceManager -import com.am.stbus.R -import com.am.stbus.common.extensions.toPx -import com.am.stbus.presentation.screens.gmaps.data.* -import com.am.stbus.presentation.screens.gmaps.data.CityRoutesData.GLAVNE_STANICE -import com.google.android.gms.location.FusedLocationProviderClient -import com.google.android.gms.location.LocationServices -import com.google.android.gms.maps.CameraUpdateFactory -import com.google.android.gms.maps.GoogleMap -import com.google.android.gms.maps.OnMapReadyCallback -import com.google.android.gms.maps.SupportMapFragment -import com.google.android.gms.maps.model.* -import kotlinx.android.synthetic.main.fragment_information_image_view.* - - -class InformationGmapsFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnInfoWindowClickListener { - - private val args: InformationGmapsFragmentArgs by navArgs() - - private lateinit var fusedLocationClient: FusedLocationProviderClient - - private lateinit var map: GoogleMap - - private var leftPadding: Int = 0 - private var topPadding: Int = 0 - private var rightPadding: Int = 0 - private var bottomPadding: Int = 0 - - private var selectedItems = mutableSetOf() - - private var initialPopupShown = false - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity()) - - requireActivity().window.apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - statusBarColor = ContextCompat.getColor(requireContext(), R.color.poluprozirniStatusBar) - } - } - - return inflater.inflate(R.layout.fragment_information_gmaps, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - setNavigationIcon(R.drawable.ic_arrow_back_black) - setNavigationOnClickListener { - findNavController().popBackStack() - } - inflateMenu(R.menu.menu_gmaps) - setOnMenuItemClickListener { - onOptionsItemSelected(it) - } - } - - ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> - leftPadding = insets.systemWindowInsetLeft - topPadding = insets.systemWindowInsetTop + 56.toPx() - rightPadding = insets.systemWindowInsetRight - bottomPadding = insets.systemWindowInsetBottom - insets - } - - val mapFragment: SupportMapFragment = childFragmentManager - .findFragmentById(R.id.map) as SupportMapFragment - mapFragment.getMapAsync(this) - } - - override fun onMapReady(googleMap: GoogleMap) { - map = googleMap - - map.apply { - uiSettings.isZoomControlsEnabled = true - animateCamera(CameraUpdateFactory.newLatLngZoom(Stanice.VUKKAMPUSS, 12f)) - uiSettings.isMapToolbarEnabled = true - } - - val customInfoWindow = GmapsBusStopInfoView(requireContext()) - map.setInfoWindowAdapter(customInfoWindow) - map.setPadding(leftPadding, topPadding, rightPadding, bottomPadding) - map.setOnInfoWindowClickListener(this) - - - val myPreference = PreferenceManager.getDefaultSharedPreferences(requireContext()) - when (myPreference.getString("maptype", "2")) { - "1" -> map.mapType = GoogleMap.MAP_TYPE_HYBRID - "2" -> map.mapType = GoogleMap.MAP_TYPE_NORMAL - "3" -> map.mapType = GoogleMap.MAP_TYPE_TERRAIN - } - - if (myPreference.getBoolean("traffic", false)) { - map.isTrafficEnabled = true - } - - map.setOnCameraMoveListener { - val cameraPosition = map.cameraPosition - if (cameraPosition.zoom == 12f && !initialPopupShown) { - showRouteDirectionPopup(args.gmapsId) - initialPopupShown = true - } - } -// googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { -// @Override -// public void onCameraMove() { -// CameraPosition cameraPosition = googleMap.getCameraPosition(); -// if(cameraPosition.zoom > 18.0) { -// googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); -// } else { -// googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); -// } -// } -// }); - - - setupUserLocation() - } - - override fun onInfoWindowClick(marker: Marker) { - createMarkerAlert(marker) - } - - - private fun setupUserLocation() { - if (checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PermissionChecker.PERMISSION_GRANTED) { - requestPermissions( - arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE) - return - } - - // 1 - map.isMyLocationEnabled = true - - /* // 2 - fusedLocationClient.lastLocation.addOnSuccessListener(this) { location -> - // Got last known location. In some rare situations this can be null. - // 3 - if (location != null) { - lastLocation = location - val currentLatLng = LatLng(location.latitude, location.longitude) - //map.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 12f)) - } - }*/ - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - when (requestCode) { - LOCATION_PERMISSION_REQUEST_CODE -> { - // If request is cancelled, the result arrays are empty. - if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { - // Dopusteno - setupUserLocation() - } else { - // TODO: Error message - // Dopustenje odbijeno - } - return - } - - // When se koristi za druga dopustenja kojih nemamo zasad - else -> { - // Ignoriraj sve ostalo - } - } - } - - - private fun showRouteDirectionPopup(gmapsId: Int) { - if (gmapsId == 0) { - showGeneralStops() - return - } - - // Prvo vidimo jeli ovaj gmapsId treba otvoriti - // menu izbornik sa opcijama smjera - val routeDirection = getListContainingRouteDirections(gmapsId) - - if (routeDirection.isEmpty()) { - selectedItems.add(gmapsId) - displayLine(gmapsId) - } else { - android.app.AlertDialog.Builder(requireContext()).apply { - setTitle(R.string.information_gmaps_route_selector_popup_title) - setItems(routeDirection.map { - getString(returnStringForGmapsId(it)) - }.toTypedArray()) { dialog, which -> - when (which) { - 0 -> { - selectedItems.add(routeDirection[0]) - displayLine(routeDirection[0]) - } - 1 -> { - selectedItems.add(routeDirection[1]) - displayLine(routeDirection[1]) - } - } - } - }.create().show() - } - } - - private fun createMarkerAlert(marker: Marker?) { - val markerTag = marker!!.tag as MarkerData - - val gmapsList = markerTag.gmapsIds - val booleans = BooleanArray(gmapsList.size) - - val routesList = ArrayList() - gmapsList.forEachIndexed { index, item -> - routesList.add(requireContext().getString(returnStringForGmapsId(item))) - booleans[index] = selectedItems.contains(item) - } - - val routesListStringArray = routesList.toArray(arrayOfNulls(routesList.size)) - - AlertDialog.Builder(requireContext()).apply { - setTitle(R.string.gmaps_select_title) - setMultiChoiceItems(routesListStringArray, booleans) { _, indexSelected, isChecked -> - if (isChecked) { - selectedItems.add(gmapsList[indexSelected]) - showRouteDirectionPopup(gmapsList[indexSelected]) - } else { - selectedItems.remove(gmapsList[indexSelected]) - clearAndRedrawMap() - } - } - setNegativeButton(R.string.information_gmaps_route_selector_popup_close) { _, _ -> - - } - create().show() - } - } - - private fun displayLine(gmapsId: Int) { - - val listOfStops = getStopForRoute(gmapsId) - - if (listOfStops.isEmpty()) { - Toast.makeText(requireContext(), R.string.nijejos, Toast.LENGTH_LONG).show() - return - } - - listOfStops.forEach { - map.addMarker(MarkerOptions().position(it.latLng!!) - .title(it.title) - .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_pin)) - )?.tag = MarkerData(null, it.title, it.lineNumbers, it.gmapsIds) - } - - map.addPolyline(PolylineOptions().addAll(listOfStops.map { it.latLng }) - .width(8f) - .color(ContextCompat.getColor(requireContext(), R.color.zutaGmapsRute)) - .geodesic(true)) - } - - - private fun clearAndRedrawMap() { - map.clear() - selectedItems.forEach { - displayLine(it) - } - } - - private fun showGeneralStops() { - GLAVNE_STANICE.forEach { - map.addMarker(MarkerOptions().position(it.latLng!!) - .title(it.title) - .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_pin)) - )?.tag = MarkerData(null, it.title, it.lineNumbers, it.gmapsIds) - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_main_markers -> { - showGeneralStops() - } - } - return super.onOptionsItemSelected(item) - } - - class MarkerData(val latLng: LatLng?, val title: String, val lineNumbers: List, val gmapsIds: List) - - companion object { - private const val LOCATION_PERMISSION_REQUEST_CODE = 1 - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationImageViewFragment/InformationImageViewFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationImageViewFragment/InformationImageViewFragment.kt deleted file mode 100644 index ba6f8c1..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationImageViewFragment/InformationImageViewFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationImageViewFragment - -import android.graphics.drawable.Drawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import com.am.stbus.R -import com.bumptech.glide.Glide -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target -import kotlinx.android.synthetic.main.fragment_information_image_view.* - -class InformationImageViewFragment : Fragment() { - - private val args: InformationImageViewFragmentArgs by navArgs() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_information_image_view, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - title = args.imageTitle - setNavigationIcon(R.drawable.ic_arrow_back) - setNavigationOnClickListener { - findNavController().popBackStack() - } - } - - Glide.with(requireContext()) - .load(args.imageUrl) - .listener(object : RequestListener{ - override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean { - if (isVisible) { - pb_loading.isVisible = false - } - return false - } - - override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { - if (isVisible) { - pb_loading.isVisible = false - } - return false - } - - }) - .into(iv_slika) - } - -} - diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListAdapter.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListAdapter.kt deleted file mode 100644 index 26ae76c..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListAdapter.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationNewsListFragment - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.am.stbus.R -import com.am.stbus.domain.models.NewsListItem -import kotlinx.android.synthetic.main.item_row_news.view.* - -class InformationNewsListAdapter(val context: Context?, - var onClickListener: (NewsListItem) -> Unit -) : RecyclerView.Adapter() { - - private var items = mutableListOf() - - fun addEntireData(news: List) { - items.addAll(news) - notifyDataSetChanged() - } - - fun addItem(news: NewsListItem) { - items.add(news) - } - - fun clear() { - items.clear() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationsViewHolder { - val itemView = LayoutInflater.from(parent.context) - .inflate(R.layout.item_row_news, parent, false) - return NotificationsViewHolder(itemView) - } - - override fun getItemCount(): Int { - return items.size - } - - override fun onBindViewHolder(holder: NotificationsViewHolder, position: Int) { - holder.bind(position) - } - - inner class NotificationsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - - fun bind(position: Int) { - - val item = items[position] - - itemView.apply { - titleTextView.text = item.title - dateTextView.text = item.date - summaryTextView.text = item.desc - setOnClickListener { - onClickListener(item) - } - } - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListFragment.kt deleted file mode 100644 index 6e054c8..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListFragment.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationNewsListFragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.navigation.findNavController -import androidx.recyclerview.widget.LinearLayoutManager -import com.am.stbus.R -import com.am.stbus.common.extensions.loadEmailReport -import com.am.stbus.common.extensions.loadPrometUrl -import com.am.stbus.domain.models.NewsListItem -import kotlinx.android.synthetic.main.fragment_information_news_list.* -import kotlinx.android.synthetic.main.snippet_error.* -import org.koin.androidx.viewmodel.ext.android.viewModel - -class InformationNewsListFragment : Fragment() { - - private val viewModel: InformationNewsListViewModel by viewModel() - - private val informationNewsListAdapter by lazy { - InformationNewsListAdapter(requireContext()) { onNewsItemClicked(it) } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_information_news_list, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - setNavigationIcon(R.drawable.ic_arrow_back) - setNavigationOnClickListener { - findNavController().popBackStack() - } - } - - viewModel.newsList.observe(viewLifecycleOwner, Observer> { - onNewsListAdded(it) - }) - - viewModel.loading.observe(viewLifecycleOwner, Observer{ - snippet_loading.isVisible = it - rv_news_list.isVisible = !it - }) - - viewModel.error.observe(viewLifecycleOwner, Observer { - if (informationNewsListAdapter.itemCount == 0) { - handleErrorScreen(it) - } - }) - - rv_news_list.apply { - layoutManager = LinearLayoutManager(requireContext()) - setHasFixedSize(true) - adapter = informationNewsListAdapter - } - } - - private fun handleErrorScreen(errorMessage: String?) { - rv_news_list.isVisible = false - snippet_loading.isVisible = false - snippet_error.isVisible = true - - btn_promet.setOnClickListener { - requireContext().loadPrometUrl() - } - - btn_error.setOnClickListener { - requireContext().loadEmailReport("newsList", errorMessage?:"") - } - - } - - private fun onNewsListAdded(it: List) { - informationNewsListAdapter.clear() - informationNewsListAdapter.addEntireData(it) - } - - private fun onNewsItemClicked(it: NewsListItem) { - view?.findNavController()?.navigate(InformationNewsListFragmentDirections - .actionInformationNewsListFragmentToInformationNewsDetailFragment( - it.title, - it.date, - it.url - )) - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListViewModel.kt deleted file mode 100644 index 06220f1..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/InformationNewsListViewModel.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationNewsListFragment - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.am.stbus.domain.models.NewsListItem -import com.am.stbus.domain.usecases.news.NewsListUseCase -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - - -class InformationNewsListViewModel( - private val getNewsListUseCase: NewsListUseCase -) : ViewModel() { - - private val _newsList = MutableLiveData>() - val newsList: LiveData> - get() = _newsList - - private val _loading = MutableLiveData() - val loading: LiveData - get() = _loading - - private val _error = MutableLiveData() - val error: LiveData - get() = _error - - init { - getLocalNewsList() - } - - private fun getLocalNewsList() { - viewModelScope.launch { - try { - val newsList = getNewsListUseCase.getLocalNewsList(Dispatchers.IO) - if (newsList.isNotEmpty()) { - _newsList.postValue(newsList) - _loading.postValue(false) - } else { - _loading.postValue(true) - } - getRemoteNewsList() - } catch (e: Exception) { - _loading.postValue(false) - _error.postValue(e.localizedMessage) - } - } - } - - private fun getRemoteNewsList() { - viewModelScope.launch { - try { - val newsList = getNewsListUseCase.getRemoteNewsList( Dispatchers.IO) - if (newsList.isNotEmpty()) { - _newsList.postValue(newsList) - _loading.postValue(false) - } - } catch (e: Exception) { - _loading.postValue(false) - _error.postValue(e.localizedMessage) - } - } - } -} - diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/informationNewsDetailFragment/InformationNewsDetailFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/informationNewsDetailFragment/InformationNewsDetailFragment.kt deleted file mode 100644 index 2768d66..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/informationNewsDetailFragment/InformationNewsDetailFragment.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationNewsListFragment.informationNewsDetailFragment - -import android.graphics.Color -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.webkit.WebView -import android.webkit.WebViewClient -import androidx.core.content.ContextCompat -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.navigation.findNavController -import androidx.navigation.fragment.navArgs -import com.am.stbus.R -import com.am.stbus.common.Constants -import com.am.stbus.common.extensions.loadUrl -import com.am.stbus.common.extensions.toPx -import com.am.stbus.domain.models.NewsItem -import kotlinx.android.synthetic.main.fragment_information_news_detail.* -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf - -class InformationNewsDetailFragment : Fragment() { - - private val viewModel: InformationNewsDetailViewModel by viewModel { parametersOf(args.newsUrl) } - - private val args: InformationNewsDetailFragmentArgs by navArgs() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_information_news_detail, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - setNavigationIcon(R.drawable.ic_arrow_back) - setNavigationOnClickListener { - findNavController().popBackStack() - } - } - - tv_title.text = args.newsTitle - tv_date.text = args.newsDate - - viewModel.newsItem.observe(viewLifecycleOwner, Observer { - onNewsLoaded(it) - }) - - viewModel.error.observe(viewLifecycleOwner, Observer { - handleErrorScreen(it) - }) - - viewModel.loading.observe(viewLifecycleOwner, Observer { - pb_loading.isVisible = it - web_view.isVisible = !it - }) - } - - private fun onNewsLoaded(it: NewsItem) { - web_view.apply { - setBackgroundColor(Color.TRANSPARENT) - isHorizontalScrollBarEnabled = false - loadDataWithBaseURL("file:///android_res/", formatNewsTextForWebView(it.newsItemContent), "text/html", "utf-8", null) - } - - web_view.webViewClient = object : WebViewClient() { - override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { - requireContext().loadUrl(Constants.PROMET_URL + url.replace("file://", "")) - return true - } - } - - } - - private fun formatNewsTextForWebView(newsContent: String): String { - val textColor = "#" + Integer.toHexString(ContextCompat.getColor(requireContext(), R.color.colorText) and 0x00ffffff) - val textFixedImageTags = newsContent.replace("src=\"/", "src=\"http://www.promet-split.hr/") - return "\n " + - "\n" + - "" + - "\n" + - textFixedImageTags + "\n" + - "" - - } - - private fun handleErrorScreen(errorMessage: String?) { - web_view.loadDataWithBaseURL( - "file:///android_res/", - errorMessage ?: "", - "text/html", - "utf-8", - null) - } -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/informationNewsDetailFragment/InformationNewsDetailViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/informationNewsDetailFragment/InformationNewsDetailViewModel.kt deleted file mode 100644 index 8d3cbee..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationNewsListFragment/informationNewsDetailFragment/InformationNewsDetailViewModel.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationNewsListFragment.informationNewsDetailFragment - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.am.stbus.domain.models.NewsItem -import com.am.stbus.domain.usecases.news.NewsDetailUseCase -import io.reactivex.SingleObserver -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers - -class InformationNewsDetailViewModel( - private val url: String, - private val getNewsDetailUseCase: NewsDetailUseCase -) : ViewModel() { - - private val schedulers = Schedulers.io() - private val thread = AndroidSchedulers.mainThread() - - private val _newsItem = MutableLiveData() - val newsItem: LiveData - get() = _newsItem - - private val _loading = MutableLiveData() - val loading: LiveData - get() = _loading - - private val _error = MutableLiveData() - val error: LiveData - get() = _error - - init { - fetchAndPopulateNewsItem(url) - } - - private fun fetchAndPopulateNewsItem(url: String) { - getNewsDetailUseCase.get(true, url) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object: SingleObserver { - override fun onSuccess(t: NewsItem) { - _newsItem.postValue(t) - _loading.postValue(false) - } - - override fun onSubscribe(d: Disposable) { - _loading.postValue(true) - } - - override fun onError(e: Throwable) { - _loading.postValue(false) - _error.postValue(e.localizedMessage) - } - }) - - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/information/informationWebViewFragment/InformationWebViewFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/information/informationWebViewFragment/InformationWebViewFragment.kt deleted file mode 100644 index 000898a..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/information/informationWebViewFragment/InformationWebViewFragment.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.information.informationWebViewFragment - -import android.annotation.SuppressLint -import android.graphics.Color -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.webkit.WebView -import android.webkit.WebViewClient -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import com.am.stbus.R -import com.am.stbus.common.Constants -import com.am.stbus.common.extensions.loadUrl -import kotlinx.android.synthetic.main.fragment_information_web_view.* - -class InformationWebViewFragment : Fragment() { - - private val args: InformationWebViewFragmentArgs by navArgs() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_information_web_view, container, false) - } - - @SuppressLint("SetJavaScriptEnabled") - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - toolbar.title = args.webViewTitle - setNavigationIcon(R.drawable.ic_arrow_back) - setNavigationOnClickListener { - findNavController().popBackStack() - } - } - - web_view.apply { - val webViewSettings = web_view.settings - webViewSettings.javaScriptEnabled = true - setBackgroundColor(Color.TRANSPARENT) - loadDataWithBaseURL("file:///android_res/", args.webViewContent, "text/html", "utf-8", null) - } - - web_view.webViewClient = object : WebViewClient() { - override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { - requireContext().loadUrl(Constants.PROMET_URL + url) - return true - } - } - - - } - -} - diff --git a/app/src/main/java/com/am/stbus/presentation/screens/settings/AboutFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/settings/AboutFragment.kt deleted file mode 100644 index 919c317..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/settings/AboutFragment.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.settings - -import android.content.Intent -import android.content.SharedPreferences -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.browser.customtabs.CustomTabsIntent -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment -import androidx.navigation.findNavController -import com.am.stbus.BuildConfig -import com.am.stbus.R -import com.am.stbus.common.Constants.GITHUB_URL -import com.am.stbus.common.extensions.loadEmailReport -import com.am.stbus.presentation.screens.settings.ContentFragment.Companion.CHANGELOG_CONTENT -import com.am.stbus.presentation.screens.settings.ContentFragment.Companion.FAQ_CONTENT -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity -import kotlinx.android.synthetic.main.fragment_about.* -import org.koin.android.ext.android.inject - - -class AboutFragment : Fragment() { - - private val preferencesManager: SharedPreferences by inject() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_about, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - setNavigationIcon(R.drawable.ic_arrow_back) - setNavigationOnClickListener { - findNavController().popBackStack() - } - } - - tv_app_version.text = BuildConfig.VERSION_NAME - - btn_changelog.setOnClickListener { loadContentFragment(CHANGELOG_CONTENT) } - btn_github.setOnClickListener { loadUrl(GITHUB_URL) } - btn_faq.setOnClickListener {loadContentFragment(FAQ_CONTENT) } - btn_licence.setOnClickListener { startActivity(Intent(requireContext(), OssLicensesMenuActivity::class.java)) } - btn_contact.setOnClickListener { requireContext().loadEmailReport("", "Kontakt") } - } - - private fun loadUrl(url: String) { - if (preferencesManager.getBoolean("open_urls", true)) { - val customTabsIntent : CustomTabsIntent = buildCustomTabsIntent() - customTabsIntent.launchUrl(requireContext(), Uri.parse(url)) - } else { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - requireContext().startActivity(intent) - } - } - - private fun buildCustomTabsIntent(): CustomTabsIntent { - val intentBuilder = CustomTabsIntent.Builder() - // Show the title - intentBuilder.setShowTitle(true) - // Set the color of Toolbar - intentBuilder.setToolbarColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary)) - return intentBuilder.build() - } - - private fun loadContentFragment(contentType: Int) { - view?.findNavController()?.navigate(AboutFragmentDirections.actionAboutFragmentToContentFragment(contentType)) - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/settings/ContentFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/settings/ContentFragment.kt deleted file mode 100644 index e847e12..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/settings/ContentFragment.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.settings - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import com.am.stbus.BuildConfig -import com.am.stbus.R -import com.am.stbus.common.extensions.toSpanned -import kotlinx.android.synthetic.main.fragment_content.* -import kotlinx.android.synthetic.main.fragment_favourites.toolbar -import org.threeten.bp.Instant -import org.threeten.bp.ZoneId -import org.threeten.bp.format.DateTimeFormatter -import org.threeten.bp.format.FormatStyle - -class ContentFragment : Fragment() { - - private val args: ContentFragmentArgs by navArgs() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_content, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - setNavigationIcon(R.drawable.ic_close) - setNavigationOnClickListener { - findNavController().popBackStack() - } - } - - when (args.type) { - FIRST_RUN_CONTENT -> updateViews(getString(R.string.content_first_run_toolbar), getString(R.string.content_first_run_title), getString(R.string.content_first_run_desc)) - UPDATE_APP_CONTENT -> updateViews(getString(R.string.content_update_toolbar), getString(R.string.content_update_title, generateChangelogTitle()), getString(R.string.content_update_desc)) - CHANGELOG_CONTENT -> updateViews(getString(R.string.about_changelog_toolbar), getString(R.string.content_update_title, generateChangelogTitle()), getString(R.string.content_update_desc)) - FAQ_CONTENT -> updateViews(getString(R.string.content_faq_toolbar), getString(R.string.content_faq_title), getString(R.string.content_faq_desc)) - LICENCES_CONTENT -> updateLicensesView() - } - } - - private fun updateViews(toolbarTitle: String, title: String?, content: String) { - toolbar.title = toolbarTitle - - tv_welcome_title.text = title ?: "" - tv_welcome_content.text = content.toSpanned() - } - - private fun generateChangelogTitle(): String { - val buildDate = Instant.ofEpochMilli(BuildConfig.BUILD_TIME.time).atZone(ZoneId.systemDefault()).toLocalDate() - return "${BuildConfig.VERSION_NAME} - ${buildDate.format(DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ))}" - } - - private fun updateLicensesView() { - toolbar.title = getString(R.string.content_licenses_toolbar) - tv_welcome_title.isVisible = false - web_view.loadUrl("file:///android_asset/licenses.html") - } - - companion object{ - const val FIRST_RUN_CONTENT = 0 - const val UPDATE_APP_CONTENT = 1 - const val CHANGELOG_CONTENT = 2 - const val FAQ_CONTENT = 3 - const val LICENCES_CONTENT = 4 - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/settings/SettingsFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/settings/SettingsFragment.kt deleted file mode 100644 index f831715..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/settings/SettingsFragment.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.settings - -import android.content.SharedPreferences -import android.os.Bundle -import android.view.View -import androidx.navigation.findNavController -import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager -import com.am.stbus.R -import com.am.stbus.presentation.screens.MainActivity -import kotlinx.android.synthetic.main.activity_main.* - -class SettingsFragment : PreferenceFragmentCompat() { - - private var listener: SharedPreferences.OnSharedPreferenceChangeListener? = null - private lateinit var mainActivity: MainActivity - - override fun onResume() { - super.onResume() - mainActivity = activity as MainActivity - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - mainActivity = activity as MainActivity - addPreferencesFromResource(R.xml.preferences) - - // Mijenjanje teme u postavkama - val sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) - listener = SharedPreferences.OnSharedPreferenceChangeListener { _, string -> - if (string == "darkMode") { - mainActivity.setupAppTheme() - } - } - sharedPreferences.registerOnSharedPreferenceChangeListener(listener) - - // About fragment - findPreference("about")?.setOnPreferenceClickListener { - view?.findNavController()?.navigate(SettingsFragmentDirections.actionSettingsFragmentToAboutFragment()) - true - } - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - mainActivity.toolbar.title = getString(R.string.nav_settings) - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/stops/detail/BusStopArrivalsDetailScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/stops/detail/BusStopArrivalsDetailScreen.kt new file mode 100644 index 0000000..b03b324 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/stops/detail/BusStopArrivalsDetailScreen.kt @@ -0,0 +1,232 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.stops.detail + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Favorite +import androidx.compose.material.icons.outlined.FavoriteBorder +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.pulltorefresh.PullToRefreshBox +import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle +import com.am.stbus.R +import com.am.stbus.data.models.BusStop +import com.am.stbus.presentation.screens.common.AppBarScreen +import com.am.stbus.presentation.screens.common.ErrorScreen +import com.am.stbus.presentation.screens.common.LoadingScreen +import kotlinx.coroutines.delay +import org.koin.androidx.compose.koinViewModel +import org.threeten.bp.Duration +import org.threeten.bp.ZonedDateTime + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun BusStopArrivalsDetailScreen( + viewModel: BusStopArrivalsDetailViewModel = koinViewModel(), + busStop: BusStop, + favourite: Boolean, + onFavouriteClicked: (BusStop) -> Unit, + onBackClicked: () -> Unit +) { + + val lifecycle = LocalLifecycleOwner.current.lifecycle + + LaunchedEffect(Unit) { + viewModel.getBusStopArrivals(onPullToRefresh = false, busStopId = busStop.id) + } + + var running by remember { mutableStateOf(true) } + + LaunchedEffect(Unit) { + lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + while (running) { + delay(30_000) + viewModel.getBusStopArrivals(onPullToRefresh = true, busStopId = busStop.id) + } + } + } + + val loading = viewModel.loading + + val pullToRefreshLoading = viewModel.pullToRefreshLoading + + val pullToRefreshState = rememberPullToRefreshState() + + val busStopTimes = viewModel.busStopTimes + + val title = busStop.title + + AppBarScreen( + title = stringResource(title), + titleColour = MaterialTheme.colorScheme.tertiary, + showBackButton = true, + onBackClicked = onBackClicked, + actions = { + IconButton(onClick = { + onFavouriteClicked(busStop) + }) { + Icon( + imageVector = if (favourite) { + Icons.Outlined.Favorite + } else { + Icons.Outlined.FavoriteBorder + }, + contentDescription = "Back", + tint = MaterialTheme.colorScheme.tertiary + ) + } + } + ) { + if (loading) { + LoadingScreen() + } else { + PullToRefreshBox( + isRefreshing = pullToRefreshLoading, + state = pullToRefreshState, + onRefresh = { + viewModel.getBusStopArrivals(onPullToRefresh = true, busStopId = busStop.id) + }, + indicator = { + PullToRefreshDefaults.LoadingIndicator( + modifier = Modifier.align(Alignment.TopCenter), + state = pullToRefreshState, + isRefreshing = pullToRefreshLoading + ) + } + ) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + if (busStopTimes == null) { + ErrorScreen(stringResource(title)) + } else { + val timeNow = ZonedDateTime.now() + + busStopTimes.forEach { + + val minutesUntil = if (it.time != null) { + Duration.between(timeNow, it.time).toMinutes() + } else { + "" + } + + Row( + modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = it.lineNumber, + color = MaterialTheme.colorScheme.secondary, + fontWeight = FontWeight.SemiBold, + fontSize = 18.sp, + ) + Spacer(Modifier.width(8.dp)) + Text(it.title) + Spacer(Modifier.weight(1f)) + Spacer(Modifier.width(8.dp)) + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "$minutesUntil", + color = MaterialTheme.colorScheme.primary, + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + ) + Text( + text = "min", + fontSize = 12.sp + ) + } + Spacer(Modifier.width(8.dp)) + } + HorizontalDivider( + modifier = Modifier.padding(horizontal = 16.dp), + thickness = 0.4.dp + ) + } + + if (busStopTimes.isNotEmpty()) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 32.dp), + text = stringResource(R.string.bus_stops_info_message), + fontStyle = FontStyle.Italic, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.secondary + ) + } else { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 32.dp), + text = stringResource(R.string.bus_stops_no_departures), + textAlign = TextAlign.Center, + fontWeight = FontWeight.SemiBold, + fontSize = 20.sp + ) + } + } + } + } + } + } +} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/stops/detail/BusStopArrivalsDetailViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/stops/detail/BusStopArrivalsDetailViewModel.kt new file mode 100644 index 0000000..58698e8 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/stops/detail/BusStopArrivalsDetailViewModel.kt @@ -0,0 +1,110 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.stops.detail + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.am.stbus.domain.usecases.GetBusStopArrivalsUseCase +import kotlinx.coroutines.launch +import org.threeten.bp.LocalDateTime +import org.threeten.bp.ZoneId +import org.threeten.bp.ZonedDateTime +import timber.log.Timber + +class BusStopArrivalsDetailViewModel( + private val getDeparturesUseCase: GetBusStopArrivalsUseCase +) : ViewModel() { + + var loading by mutableStateOf(true) + + var pullToRefreshLoading by mutableStateOf(false) + + var busStopTimes: List? = null + + init { + Timber.d("Debugging - BusStopArrivalsDetailViewModel init") + } + + fun getBusStopArrivals(onPullToRefresh: Boolean, busStopId: Int) { + if (!onPullToRefresh) { + busStopTimes = emptyList() + } + Timber.d("Debugging - get bus stop arrival $busStopId") + pullToRefreshLoading = onPullToRefresh + viewModelScope.launch { + val result = getDeparturesUseCase.run(busStopId) + + result.onSuccess { busStopArrivals -> + Timber.d("onSuccess $busStopId ${busStopArrivals}") + busStopTimes = busStopArrivals.map { + BusStopTimes( + lineNumber = it.lineNumber ?: "", + title = it.title ?: "", + time = convertStringToZonedDateTime(it.time) + ) + } + loading = false + pullToRefreshLoading = false + }.onFailure { + Timber.d("onFailure $busStopId $it") + busStopTimes = null + pullToRefreshLoading = false + loading = false + } + } + } + + fun convertStringToZonedDateTime(timeString: String?): ZonedDateTime? { + return try { + val localDateTime = LocalDateTime.parse(timeString) + localDateTime.atZone(ZoneId.of("Europe/Zagreb")) + } catch (exp: Exception) { + Timber.d("exp $exp") + null + } + } + + data class BusStopTimes( + val lineNumber: String, + val title: String, + val time: ZonedDateTime? + ) + +} + + +/* +@Serializable +data class BusStopArrival( + @SerialName("lineNumber") + val lineNumber: String?, + @SerialName("title") + val title: String?, + @SerialName("time") + val time: String?, +)*/ diff --git a/app/src/main/java/com/am/stbus/presentation/screens/stops/list/BusStopArrivalsListScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/stops/list/BusStopArrivalsListScreen.kt new file mode 100644 index 0000000..b25a0ed --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/stops/list/BusStopArrivalsListScreen.kt @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.stops.list + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.am.stbus.R +import com.am.stbus.data.models.BusStop +import com.am.stbus.data.static.BUS_ARRIVALS_STOPS +import com.am.stbus.presentation.screens.common.AppBarScreen +import com.am.stbus.presentation.theme.SplitBusTheme + +@Composable +fun BusStopArrivalsListScreen( + onBusStopClicked: (BusStop) -> Unit +) { + + val busStops = BUS_ARRIVALS_STOPS + + AppBarScreen( + title = stringResource(R.string.nav_bus_stops), + titleColour = MaterialTheme.colorScheme.tertiary + ) { + busStops.forEach { + BusStopItemView( + busStop = it, + onBusStopClicked = onBusStopClicked + ) + } + } +} + +@Composable +fun BusStopItemView( + busStop: BusStop, + onBusStopClicked: (BusStop) -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + onBusStopClicked(busStop) + } + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Text(text = stringResource(busStop.title)) + } + HorizontalDivider( + modifier = Modifier.padding(horizontal = 16.dp), + thickness = 0.4.dp + ) +} + +@Preview +@Composable +fun BusStopArrivalsListScreenPreview() { + SplitBusTheme { + BusStopArrivalsListScreen {} + } +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesFragment.kt deleted file mode 100644 index 8d9bfbf..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesFragment.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentStatePagerAdapter -import com.am.stbus.R -import com.am.stbus.common.TimetablesData -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment -import kotlinx.android.synthetic.main.fragment_timetables.* -import org.koin.androidx.navigation.koinNavGraphViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class TimetablesFragment : Fragment() { - - private val timetablesSharedViewModel: TimetablesSharedViewModel by koinNavGraphViewModel(R.id.nav_graph) - - private val viewModel: TimetablesViewModel by viewModel() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_timetables, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.title = getString(R.string.nav_timetables) - - viewModel.timetableList.observe(viewLifecycleOwner) { - timetablesSharedViewModel.saveTimetables(it) - } - - setupViewPager() - tab_layout.setupWithViewPager(view_pager) - } - - private fun setupViewPager() { - - val fragmentsList: List = listOf( - TimetablesListFragment.newInstance(TimetablesData.AREA_CITY), - TimetablesListFragment.newInstance(TimetablesData.AREA_URBAN), - TimetablesListFragment.newInstance(TimetablesData.AREA_SUBURBAN), - TimetablesListFragment.newInstance(TimetablesData.AREA_TROGIR), - TimetablesListFragment.newInstance(TimetablesData.AREA_SOLTA) - ) - - val titles: List = listOf( - R.string.timetables_area_city, - R.string.timetables_area_urban, - R.string.timetables_area_suburban, - R.string.timetables_area_trogir, - R.string.timetables_area_solta - ) - - val adapter = TimetableSliderAdapter(childFragmentManager, fragmentsList, titles) - - view_pager.adapter = adapter - - } - - private inner class TimetableSliderAdapter( - fragmentManager: FragmentManager, - val fragments: List, - val fragmentTitles: List - ) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - - override fun getItem(position: Int): Fragment = fragments[position] - - override fun getCount(): Int = fragments.size - - override fun getPageTitle(position: Int): CharSequence? = getString(fragmentTitles[position]) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesSharedViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesSharedViewModel.kt deleted file mode 100644 index 3db86d7..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesSharedViewModel.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.am.stbus.domain.models.Timetable - -class TimetablesSharedViewModel : ViewModel() { - - private val _timetables = MutableLiveData>() - val timetables: LiveData> - get() = _timetables - - private val _smallLoading = MutableLiveData() - val smallLoading: LiveData - get() = _smallLoading - - fun saveTimetables(timetables: List) { - _timetables.value = timetables - } - - fun setSmallLoading(loading: Boolean) { - _smallLoading.postValue(loading) - } - - fun updateFavourite(lineId: Int, favourite: Int) { - _timetables.value?.find { timetable -> timetable.lineId == lineId}?.favourite = favourite - _timetables.postValue(_timetables.value) - } - - fun updateTimetableContent(lineId: Int, content: String, contentDate: String) { - _timetables.value?.find { timetable -> timetable.lineId == lineId }?.let { - it.content = content - it.contentDate = contentDate - } - _timetables.postValue(_timetables.value) - } - -} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesViewModel.kt deleted file mode 100644 index c59da2d..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/TimetablesViewModel.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.am.stbus.common.TimetablesData -import com.am.stbus.domain.models.Timetable -import com.am.stbus.domain.usecases.timetables.TimetableListUseCase -import io.reactivex.CompletableObserver -import io.reactivex.SingleObserver -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers - -class TimetablesViewModel(private val timetableListUseCase: TimetableListUseCase) : ViewModel() { - - private val schedulers = Schedulers.io() - private val thread = AndroidSchedulers.mainThread() - - private val _timetableList = MutableLiveData>() - val timetableList: LiveData> - get() = _timetableList - - init { - getTimetables() - } - - private fun getTimetables() { - timetableListUseCase.getTimetables() - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object : SingleObserver> { - override fun onSuccess(timetables: List) { - if (timetables.isEmpty()) { - saveTimetables() - } else { - _timetableList.postValue(timetables) - } - } - - override fun onSubscribe(d: Disposable) { - // TODO - } - - override fun onError(e: Throwable) { - // TODO - } - - }) - } - - private fun saveTimetables() { - timetableListUseCase.saveTimetables(TimetablesData.list) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object: CompletableObserver { - override fun onComplete() { - _timetableList.postValue(TimetablesData.list) - } - - override fun onSubscribe(d: Disposable) { - } - - override fun onError(e: Throwable) { - } - }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/detail/TimetablesDetailScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/detail/TimetablesDetailScreen.kt new file mode 100644 index 0000000..13b276a --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/timetables/detail/TimetablesDetailScreen.kt @@ -0,0 +1,224 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.timetables.detail + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Favorite +import androidx.compose.material.icons.outlined.FavoriteBorder +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PrimaryTabRow +import androidx.compose.material3.Tab +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.am.stbus.data.models.BusLine +import com.am.stbus.presentation.screens.common.AppBarScreen +import com.am.stbus.presentation.screens.common.ErrorScreen +import com.am.stbus.presentation.screens.common.LoadingScreen +import com.am.stbus.presentation.theme.SplitBusTheme +import kotlinx.coroutines.launch +import org.koin.androidx.compose.koinViewModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TimetablesDetailScreen( + viewModel: TimetablesDetailViewModel = koinViewModel(), + busLine: BusLine, + favourite: Boolean, + onFavouriteClicked: (BusLine) -> Unit, + onBackClicked: () -> Unit +) { + + val scope = rememberCoroutineScope() + + val pagerState = rememberPagerState( + pageCount = { listOfDays.size } + ) + + val currentPage = pagerState.currentPage + + val loading = viewModel.loading + + val timetableData = viewModel.timetableData + + val title = "${busLine.number} ${stringResource(id = busLine.title)}" + + LaunchedEffect(Unit) { + viewModel.getTimetableData(busLine.websiteTitle) + } + + AppBarScreen( + title = title, + titleColour = MaterialTheme.colorScheme.secondary, + showBackButton = true, + onBackClicked = onBackClicked, + actions = { + IconButton(onClick = { + onFavouriteClicked(busLine) + }) { + Icon( + imageVector = if (favourite) { + Icons.Outlined.Favorite + } else { + Icons.Outlined.FavoriteBorder + }, + contentDescription = "Back", + tint = MaterialTheme.colorScheme.secondary + ) + } + } + ) { + if (loading) { + LoadingScreen() + } else { + if (timetableData == null) { + ErrorScreen(title) + } else { + PrimaryTabRow( + selectedTabIndex = currentPage, + divider = {} + ) { + listOfDays.forEachIndexed { index, day -> + Tab( + selected = currentPage == index, + onClick = { + scope.launch { + pagerState.animateScrollToPage(index) + } + }, + text = { + Text( + text = day.toString() + ) + } + ) + } + } + + HorizontalPager(state = pagerState) { page -> + val departureTimes = when (page) { + 0 -> timetableData.workdayItems + 1 -> timetableData.saturdayItems + 2 -> timetableData.sundayList + else -> emptyList() + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + ) { + Text( + modifier = Modifier.padding(top = 12.dp), + text = timetableData.validFrom + ) + + Spacer(modifier = Modifier.height(12.dp)) + + departureTimes.forEach { timetableRow -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + timetableRow.forEach { time -> + Text( + modifier = Modifier + .background( + color = MaterialTheme.colorScheme.secondaryContainer, + shape = RoundedCornerShape(6.dp) + ) + .padding(horizontal = 6.dp, vertical = 4.dp), + text = time, + color = MaterialTheme.colorScheme.secondary + ) + Spacer(Modifier.width(14.dp)) + } + } + + Spacer(modifier = Modifier.height(12.dp)) + } + + Text( + modifier = Modifier.padding(top = 12.dp), + text = timetableData.notes + ) + } + } + } + } + } +} + + +sealed class TimetableDetailScreenDay { + data object Workday : TimetableDetailScreenDay() + data object Saturday : TimetableDetailScreenDay() + data object Sunday : TimetableDetailScreenDay() +} + +private val listOfDays = listOf( + TimetableDetailScreenDay.Workday, + TimetableDetailScreenDay.Saturday, + TimetableDetailScreenDay.Sunday +) + + +@Preview +@Composable +fun TimetablesDetailScreenPreview() { + SplitBusTheme { + Row( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + //TimetablesDetailScreen() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/detail/TimetablesDetailViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/detail/TimetablesDetailViewModel.kt new file mode 100644 index 0000000..4c2180e --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/timetables/detail/TimetablesDetailViewModel.kt @@ -0,0 +1,64 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.timetables.detail + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.am.stbus.domain.usecases.GetTimetableDetailDataUseCase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import timber.log.Timber + +class TimetablesDetailViewModel( + private val getTimetableDetailDataUseCase: GetTimetableDetailDataUseCase +) : ViewModel() { + + var loading by mutableStateOf(true) + + var timetableData: GetTimetableDetailDataUseCase.TimetableDetailData? = null + + init { + Timber.d("Debugging - TimetablesDetailViewModel init") + } + + fun getTimetableData(websiteTitle: String) { + loading = true + viewModelScope.launch(Dispatchers.IO) { + val result = getTimetableDetailDataUseCase.run(websiteTitle) + + result.onSuccess { + timetableData = it + loading = false + }.onFailure { + timetableData = null + loading = false + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/list/TimetablesScreen.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/list/TimetablesScreen.kt new file mode 100644 index 0000000..e4bf5a7 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/screens/timetables/list/TimetablesScreen.kt @@ -0,0 +1,181 @@ +/* + * MIT License + * + * Copyright (c) 2013 - 2025 Antonio Marin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.am.stbus.presentation.screens.timetables.list + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PrimaryTabRow +import androidx.compose.material3.Tab +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.am.stbus.R +import com.am.stbus.data.models.BusLine +import com.am.stbus.data.models.BusLineArea +import com.am.stbus.data.models.BusLineArea.Companion.getPagerTitle +import com.am.stbus.data.static.CITY_BUS_LINES +import com.am.stbus.data.static.SUBURBAN_AREA_BUS_LINES +import com.am.stbus.data.static.URBAN_AREA_BUS_LINES +import com.am.stbus.presentation.screens.common.AppBarScreen +import com.am.stbus.presentation.theme.SplitBusTheme +import kotlinx.coroutines.launch + +@Composable +fun TimetablesScreen( + onBusLineClicked: (BusLine) -> Unit +) { + + val scope = rememberCoroutineScope() + + val pagerState = rememberPagerState( + pageCount = { timetableAreas.size } + ) + + val currentPage = pagerState.currentPage + + AppBarScreen( + title = stringResource(R.string.nav_timetables), + titleColour = MaterialTheme.colorScheme.secondary + ) { + PrimaryTabRow( + selectedTabIndex = currentPage, + divider = {} + ) { + timetableAreas.forEachIndexed { index, timetableArea -> + Tab( + selected = currentPage == index, + selectedContentColor = MaterialTheme.colorScheme.secondary, + onClick = { + scope.launch { + pagerState.animateScrollToPage(index) + } + }, + text = { + Text( + text = stringResource(timetableArea.busLineArea.getPagerTitle()) + ) + } + ) + } + } + + HorizontalPager(state = pagerState) { page -> + + val busLinesForArea = timetableAreas[page].busLines + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + busLinesForArea.forEach { + BusLineItemView( + busLine = it, + onBusLineClicked = onBusLineClicked + ) + } + } + } + } +} + +@Composable +fun BusLineItemView( + busLine: BusLine, + onBusLineClicked: (BusLine) -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + onBusLineClicked(busLine) + }, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier + .width(48.dp) + .padding(start = 0.dp, top = 12.dp, end = 8.dp, bottom = 12.dp), + text = busLine.number, + color = MaterialTheme.colorScheme.secondary, + textAlign = TextAlign.End, + fontSize = 18.sp, + ) + Text( + modifier = Modifier.padding(end = 16.dp, top = 12.dp, bottom = 12.dp), + text = stringResource(busLine.title) + ) + } + HorizontalDivider( + modifier = Modifier.padding(horizontal = 16.dp), + thickness = 0.4.dp + ) +} + +private val timetableAreas = listOf( + TimetableArea(busLineArea = BusLineArea.City, busLines = CITY_BUS_LINES), + TimetableArea(busLineArea = BusLineArea.Urban, busLines = URBAN_AREA_BUS_LINES), + TimetableArea(busLineArea = BusLineArea.Suburban, busLines = SUBURBAN_AREA_BUS_LINES) +) + +data class TimetableArea( + val busLineArea: BusLineArea, + val busLines: List +) + + + +@Preview +@Composable +fun TimetablesScreenPreview() { + SplitBusTheme { + Row( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + TimetablesScreen {} + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListAdapter.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListAdapter.kt deleted file mode 100644 index ec7ce5b..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListAdapter.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables.timetablesListFragment - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.PopupMenu -import androidx.appcompat.view.ContextThemeWrapper -import androidx.recyclerview.widget.RecyclerView -import com.am.stbus.R -import com.am.stbus.common.Constants.ACTIVE_GMAPS_IDS -import com.am.stbus.common.TimetablesData -import com.am.stbus.domain.models.Timetable -import kotlinx.android.synthetic.main.item_row_timetable.view.* - -class TimetablesListAdapter(val context: Context?, - var onClickListener: (Timetable) -> Unit, - var onClickFavourites: (Int, Timetable) -> Unit, - var onClickMenuGmaps: (Timetable) -> Unit -) : RecyclerView.Adapter() { - - var items = mutableListOf() - - fun addEntireData(timetables: List) { - items.addAll(timetables) - notifyDataSetChanged() - } - - fun updateFavourite(position: Int, favourite: Int) { - items[position].favourite = favourite - notifyItemChanged(position) - } - - fun addItem(news: Timetable) { - items.add(news) - } - - fun clear() { - items.clear() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationsViewHolder { - val itemView = LayoutInflater.from(parent.context) - .inflate(R.layout.item_row_timetable, parent, false) - return NotificationsViewHolder(itemView) - } - - override fun getItemCount(): Int { - return items.size - } - - override fun onBindViewHolder(holder: NotificationsViewHolder, position: Int) { - holder.bind(position) - } - - inner class NotificationsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - - fun bind(position: Int) { - - val item = items[position] - - itemView.apply { - tv_line_id.text = item.lineNumber - tv_line_name.text = context.getString(TimetablesData().getTimetableTitle(item.lineId)) - setOnClickListener { - onClickListener(item) - } - - iv_menu.setOnClickListener { - PopupMenu(ContextThemeWrapper(it.context, R.style.ListPopup), it).apply { - inflate(R.menu.menu_timetable_list) - - // Favourites labels - menu.findItem(R.id.action_favourites).let { menuItem -> - when (item.favourite) { - TimetablesListFragment.FAVOURITE_REMOVED -> menuItem.setTitle(R.string.timetables_menu_add_to_favourites) - TimetablesListFragment.FAVOURITE_ADDED -> menuItem.setTitle(R.string.timetables_menu_remove_from_favourites) - else -> throw IllegalArgumentException("Illegal favourite status ${item.favourite}") - } - } - - menu.findItem(R.id.action_gmaps).isVisible = ACTIVE_GMAPS_IDS.contains(item.gmapsId) - - // onClickListeners - setOnMenuItemClickListener { menuItem -> - when (menuItem.itemId) { - R.id.action_favourites -> onClickFavourites(position, item) - R.id.action_gmaps -> onClickMenuGmaps(item) - } - true - } - }.show() - } - } - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListFragment.kt deleted file mode 100644 index 62476d5..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListFragment.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables.timetablesListFragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.LinearLayoutManager -import com.am.stbus.R -import com.am.stbus.domain.models.Timetable -import com.am.stbus.presentation.screens.timetables.TimetablesFragmentDirections -import com.am.stbus.presentation.screens.timetables.TimetablesSharedViewModel -import kotlinx.android.synthetic.main.fragment_timetables_list.* -import org.koin.androidx.navigation.koinNavGraphViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class TimetablesListFragment : Fragment() { - - private val timetablesSharedViewModel: TimetablesSharedViewModel by koinNavGraphViewModel(R.id.nav_graph) - - private val viewModel: TimetablesListViewModel by viewModel() - - private val timetableListAdapter by lazy { - TimetablesListAdapter( - requireContext(), - { onTimetableClicked(it) }, - { position, timetable -> onTimetableFavouritesClicked(position, timetable) }, - { onTimetableGmapsClicked(it) } - ) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_timetables_list, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - rv_timetables.apply { - layoutManager = LinearLayoutManager(requireContext()) - setHasFixedSize(false) - adapter = timetableListAdapter - } - - val areaId = arguments?.get("areaId") - - timetablesSharedViewModel.timetables.observe(viewLifecycleOwner, Observer>{ - timetableListAdapter.clear() - - // Ne najljepsi fix, ali ovo se dogada zbog sharedViewModela! - // Ukratko, ako parentActivity bude unisten sharedViewModel nece imat odakle dobit podatke - // pa ce se app rusiti. Ako napravim da se rekreira onda korisnik nece dobit updejtani recyclerView - // (Ako se skine timetable ili promjeni fav status) to se nece odraziti u listi ovdje, stoga sharedViewModel - // Mora pratiti nav_graph. - if (!it.isNullOrEmpty()) { - timetableListAdapter.addEntireData(it.filter { timetable -> timetable.areaId == areaId }) - } - }) - - viewModel.updatedFavourite.observe(viewLifecycleOwner, Observer{ - timetablesSharedViewModel.updateFavourite(it.lineId, it.favourite) - }) - } - - private fun onTimetableClicked(timetable: Timetable) { - parentFragment?.findNavController()?.navigate(TimetablesFragmentDirections - .actionTimetablesFragmentToTimetablesDetailFragment( - timetable.lineId, - timetable.lineNumber, - timetable.gmapsId, - timetable.areaId, - timetable.favourite, - timetable.content, - timetable.contentDate - )) - } - - private fun onTimetableFavouritesClicked(position: Int, timetable: Timetable) { - viewModel.updateFavouritesStatus(position, timetable) - } - - private fun onTimetableGmapsClicked(timetable: Timetable) { - parentFragment?.findNavController()?.navigate(TimetablesFragmentDirections - .actionInformationListFragmentToInformationGmapsFragment(timetable.gmapsId)) - } - - companion object { - fun newInstance(areaId: Int): TimetablesListFragment { - val fragment = TimetablesListFragment() - fragment.arguments = Bundle().apply { - putInt("areaId", areaId) - } - return fragment - - } - - const val FAVOURITE_ADDED = 1 - const val FAVOURITE_REMOVED = 0 - } - - data class UpdatedFavourite(val position: Int, val lineId: Int, val favourite: Int) -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListViewModel.kt deleted file mode 100644 index 688c087..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/TimetablesListViewModel.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables.timetablesListFragment - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.am.stbus.domain.models.Timetable -import com.am.stbus.domain.usecases.timetables.TimetableListUseCase -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment.Companion.FAVOURITE_ADDED -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment.Companion.FAVOURITE_REMOVED -import io.reactivex.CompletableObserver -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers - -class TimetablesListViewModel(private val timetableListUseCase: TimetableListUseCase) : ViewModel() { - - private val schedulers = Schedulers.io() - private val thread = AndroidSchedulers.mainThread() - - private val _updatedFavourite = MutableLiveData() - val updatedFavourite: LiveData - get() = _updatedFavourite - - fun updateFavouritesStatus(position: Int, timetable: Timetable) { - - val favouritesToUpdate = if (timetable.favourite == FAVOURITE_REMOVED) { FAVOURITE_ADDED } else { FAVOURITE_REMOVED } - - timetableListUseCase.updateFavourites(timetable.lineId, favouritesToUpdate) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object: CompletableObserver { - override fun onComplete() { - _updatedFavourite.postValue( - TimetablesListFragment.UpdatedFavourite( - position, - timetable.lineId, - favouritesToUpdate - ) - ) - } - - override fun onSubscribe(d: Disposable) { - } - - override fun onError(e: Throwable) { - } - }) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailDayFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailDayFragment.kt deleted file mode 100644 index 5fdf3ce..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailDayFragment.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import com.am.stbus.R -import com.am.stbus.presentation.screens.timetables.TimetablesSharedViewModel -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment.TimetableDetailFragment.Companion.SATURDAY -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment.TimetableDetailFragment.Companion.SUNDAY -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment.TimetableDetailFragment.Companion.WORK_DAY -import kotlinx.android.synthetic.main.fragment_timetable_day.* -import org.koin.androidx.navigation.koinNavGraphViewModel - -class TimetableDetailDayFragment : Fragment() { - - private val timetablesSharedViewModel: TimetablesSharedViewModel by koinNavGraphViewModel(R.id.nav_graph) - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_timetable_day, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val day = arguments?.get("day") - val timetableContent = arguments?.getStringArrayList("timetable_content") - val timetable = when(day){ - WORK_DAY -> cleanTimetable(timetableContent?.get(2)) - SATURDAY -> cleanTimetable(timetableContent?.get(3)) - SUNDAY -> cleanTimetable(timetableContent?.get(4)) - else -> throw IllegalArgumentException("Wrong day $day") - } - - // "${timetableContent?.get(0)} - tv_valid_from.text = timetableContent?.get(1) - tv_timetable.text = timetable - tv_note.text = timetableContent?.get(5) - - swipe_to_refresh.setOnRefreshListener { - (parentFragment as TimetableDetailFragment).fetchAndPopulateTimetable() - } - - timetablesSharedViewModel.smallLoading.observe(viewLifecycleOwner, Observer { - swipe_to_refresh.isRefreshing = it - }) - } - - private fun cleanTimetable(timetable: String?): String { - return if (!timetable.isNullOrEmpty()) { - val cleanedString = timetable - .replace("[\\[\\],]".toRegex(), "") - .replace(" ", " \t\t\t ") - return if (cleanedString.isBlank()) { - getString(R.string.timetable_detail_no_buses) - } else { - cleanedString - } - } else { - getString(R.string.timetable_detail_no_buses) - } - } - - companion object { - fun newInstance(day: Int, timetableContent: ArrayList): TimetableDetailDayFragment { - val fragment = TimetableDetailDayFragment() - fragment.arguments = Bundle().apply { - putInt("day", day) - putStringArrayList("timetable_content", timetableContent) - } - return fragment - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailFragment.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailFragment.kt deleted file mode 100644 index 7273f50..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailFragment.kt +++ /dev/null @@ -1,245 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment - -import android.graphics.Color -import android.os.Bundle -import android.view.* -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentStatePagerAdapter -import androidx.lifecycle.Observer -import androidx.navigation.findNavController -import androidx.navigation.fragment.navArgs -import androidx.viewpager.widget.ViewPager -import com.am.stbus.R -import com.am.stbus.common.Constants -import com.am.stbus.common.Constants.ACTIVE_GMAPS_IDS -import com.am.stbus.common.TimetablesData -import com.am.stbus.common.extensions.loadEmailReport -import com.am.stbus.common.extensions.loadPrometUrl -import com.am.stbus.presentation.screens.timetables.TimetablesSharedViewModel -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment.Companion.FAVOURITE_ADDED -import com.am.stbus.presentation.screens.timetables.timetablesListFragment.TimetablesListFragment.Companion.FAVOURITE_REMOVED -import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.fragment_timetable.* -import kotlinx.android.synthetic.main.snippet_error.* -import org.koin.androidx.navigation.koinNavGraphViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf -import org.threeten.bp.LocalDateTime -import org.threeten.bp.format.DateTimeFormatter -import org.threeten.bp.format.FormatStyle - -class TimetableDetailFragment : Fragment() { - - private val timetablesSharedViewModel: TimetablesSharedViewModel by koinNavGraphViewModel(R.id.nav_graph) - - private val viewModel: TimetableDetailViewModel by viewModel { parametersOf(args) } - - private val args: TimetableDetailFragmentArgs by navArgs() - - private var activeViewPagerPage = 0 - - private lateinit var addFavorites: MenuItem - - private lateinit var removeFavorites: MenuItem - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_timetable, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - title = "${args.lineNumber} ${getText(TimetablesData().getTimetableTitle(args.lineId))}" - setNavigationIcon(R.drawable.ic_arrow_back) - setNavigationOnClickListener { - findNavController().popBackStack() - } - inflateMenu(R.menu.menu_timetable) - onCreateOptionsMenu(menu, MenuInflater(requireContext())) - onPrepareOptionsMenu(menu) - setOnMenuItemClickListener { - onOptionsItemSelected(it) - } - } - - view_pager.addOnPageChangeListener(object: ViewPager.OnPageChangeListener { - override fun onPageScrollStateChanged(state: Int) { - } - - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { - activeViewPagerPage = position - } - - override fun onPageSelected(position: Int) { - } - - }) - - viewModel.fullScreenLoading.observe(viewLifecycleOwner, Observer { - snippet_loading.isVisible = it - tab_layout.isVisible = !it - }) - - viewModel.timetableContent.observe(viewLifecycleOwner, Observer { - timetablesSharedViewModel.updateTimetableContent(args.lineId, it.content, it.contentDate) - setupViewPager(it.content) - }) - - viewModel.updatedFavourite.observe(viewLifecycleOwner, Observer { - timetablesSharedViewModel.updateFavourite(args.lineId, it) - when (it) { - FAVOURITE_ADDED -> { - addFavorites.isVisible = false - removeFavorites.isVisible = true - } - FAVOURITE_REMOVED -> { - addFavorites.isVisible = true - removeFavorites.isVisible = false - } - } - }) - - viewModel.smallLoading.observe(viewLifecycleOwner, Observer { - timetablesSharedViewModel.setSmallLoading(it) - }) - - viewModel.showSnackBar.observe(viewLifecycleOwner, Observer { - Snackbar.make(requireView(), generateMessage(it.peekContent()), Snackbar.LENGTH_INDEFINITE) - .setTextColor(Color.WHITE) - .setAction(getString(R.string.timetable_detail_snackbar_refresh)) { - viewModel.fetchAndPopulateTimetable(args.lineId, args.areaId, args.contentDate, true) - }.show() - }) - - viewModel.error.observe(viewLifecycleOwner, Observer { errorMessage -> - tab_layout.isVisible = false - view_pager.isVisible = false - snippet_error.isVisible = true - - btn_promet.setOnClickListener { - requireContext().loadPrometUrl() - } - - btn_error.setOnClickListener { - requireContext().loadEmailReport(args.lineNumber, errorMessage) - } - }) - - tab_layout.setupWithViewPager(view_pager) - } - - private fun generateMessage(date: String): String { - val formattedDate = LocalDateTime.parse(date).format(DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM )) - return getString(R.string.timetable_detail_snackbar_message, formattedDate) - } - - private fun setupViewPager(timetableContent: String) { - - val timetable = timetableContent.split(Constants.EMA_DELIMITER) - - val fragmentsList: List = listOf( - TimetableDetailDayFragment.newInstance(WORK_DAY, ArrayList(timetable)), - TimetableDetailDayFragment.newInstance(SATURDAY, ArrayList(timetable)), - TimetableDetailDayFragment.newInstance(SUNDAY, ArrayList(timetable)) - ) - - val titles: List = listOf( - R.string.timetable_detail_work_day, - R.string.timetable_detail_saturday, - R.string.timetable_detail_sunday - ) - - val adapter = TimetableSliderAdapter(childFragmentManager, fragmentsList, titles) - - view_pager.adapter = adapter - view_pager.currentItem = activeViewPagerPage - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - - addFavorites = menu.findItem(R.id.action_add_favorites) - removeFavorites = menu.findItem(R.id.action_remove_favorites) - - super.onCreateOptionsMenu(menu, inflater) - } - - override fun onPrepareOptionsMenu(menu: Menu) { - - addFavorites.isVisible = args.favourite == FAVOURITE_REMOVED - removeFavorites.isVisible = args.favourite == FAVOURITE_ADDED - - menu.findItem(R.id.action_gmaps).isVisible = ACTIVE_GMAPS_IDS.contains(args.gmapsId) - - super.onPrepareOptionsMenu(menu) - - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_add_favorites -> { - viewModel.updateFavouritesStatus(args.lineId, FAVOURITE_ADDED) - } - R.id.action_remove_favorites -> { - viewModel.updateFavouritesStatus(args.lineId, FAVOURITE_REMOVED) - } - R.id.action_gmaps -> { - view?.findNavController()?.navigate(TimetableDetailFragmentDirections - .actionTimetableDetailFragmentToInformationGmapsFragment(args.gmapsId)) - } - } - return super.onOptionsItemSelected(item) - } - - fun fetchAndPopulateTimetable() { - viewModel.fetchAndPopulateTimetable(args.lineId, args.areaId, args.contentDate, false) - } - - - companion object { - const val WORK_DAY = 0 - const val SATURDAY = 1 - const val SUNDAY = 2 - } - - private inner class TimetableSliderAdapter( - fragmentManager: FragmentManager, - val fragments: List, - val fragmentTitles: List - ) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - - override fun getItem(position: Int): Fragment = fragments[position] - - override fun getCount(): Int = fragments.size - - override fun getPageTitle(position: Int): CharSequence = getString(fragmentTitles[position]) - }} diff --git a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailViewModel.kt b/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailViewModel.kt deleted file mode 100644 index 3bbfc7d..0000000 --- a/app/src/main/java/com/am/stbus/presentation/screens/timetables/timetablesListFragment/timetableDetailFragment/TimetableDetailViewModel.kt +++ /dev/null @@ -1,190 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2013 - 2021 Antonio Marin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.am.stbus.presentation.screens.timetables.timetablesListFragment.timetableDetailFragment - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.am.stbus.common.Constants -import com.am.stbus.common.Constants.DOWNLOADED_RECENTLY -import com.am.stbus.common.TimetablesData -import com.am.stbus.common.helpers.Event -import com.am.stbus.domain.usecases.timetables.TimetableDetailUseCase -import com.am.stbus.domain.usecases.timetables.TimetableListUseCase -import io.reactivex.CompletableObserver -import io.reactivex.SingleObserver -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers -import org.threeten.bp.LocalDateTime -import java.net.UnknownHostException - -class TimetableDetailViewModel( - private val args: TimetableDetailFragmentArgs, - private val timetableListUseCase: TimetableListUseCase, - private val timetableDetailUseCase: TimetableDetailUseCase -) : ViewModel() { - - private val schedulers = Schedulers.io() - private val thread = AndroidSchedulers.mainThread() - - private val _timetableContent = MutableLiveData() - val timetableContent: LiveData - get() = _timetableContent - - private val _fullScreenLoading = MutableLiveData() - val fullScreenLoading: LiveData - get() = _fullScreenLoading - - private val _smallLoading = MutableLiveData() - val smallLoading: LiveData - get() = _smallLoading - - private val _showSnackBar = MutableLiveData>() - val showSnackBar: LiveData> - get() = _showSnackBar - - private val _error = MutableLiveData() - val error: LiveData - get() = _error - - private val _updatedFavourite = MutableLiveData() - val updatedFavourite: LiveData - get() = _updatedFavourite - - init { - populateTimetable(args.lineId, args.areaId, args.content, args.contentDate) - } - - private fun populateTimetable(lineId: Int, areaId: Int, timetableContent: String, date: String) { - if (timetableContent.isNotEmpty()) { - _timetableContent.postValue(UpdatedTimetable(timetableContent, date)) - if (isntDownloadedRecently(date)) { - // Download only if old data is older than 2 hours - fetchAndPopulateTimetable(lineId, areaId, date, true) - } - } else { - _fullScreenLoading.postValue(true) - fetchAndPopulateTimetable(lineId, areaId, date, false) - } - } - - fun fetchAndPopulateTimetable(lineId: Int, areaId: Int, date: String?, showSmallLoading: Boolean) { - timetableDetailUseCase.getTimetableDetail(lineId, getTimetableBaseUrl(areaId)) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object: SingleObserver { - override fun onSuccess(content: String) { - if (content.isNotBlank()) { - _timetableContent.postValue(UpdatedTimetable(content, LocalDateTime.now().toString())) - _fullScreenLoading.postValue(false) - _smallLoading.postValue(false) - saveTimetableContent(lineId, content) - } else { - onError(IllegalStateException("Empty timetable, possibly wrong url!")) - } - } - - override fun onSubscribe(d: Disposable) { - _smallLoading.postValue(showSmallLoading) - } - - override fun onError(e: Throwable) { - when (e) { - is UnknownHostException -> { - // No internet exception - if (date.isNullOrEmpty()) { - _fullScreenLoading.postValue(false) - _error.postValue(e.localizedMessage) - } else { - _smallLoading.postValue(false) - _showSnackBar.postValue(Event(date)) - } - } - else -> { - // All other errors, including Empty timetable - _fullScreenLoading.postValue(false) - _smallLoading.postValue(false) - _error.postValue(e.localizedMessage) - saveTimetableContent(args.lineId, "") - } - } - } - }) - } - - fun updateFavouritesStatus(lineId: Int, favouriteStatus: Int) { - timetableListUseCase.updateFavourites(lineId, favouriteStatus) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object: CompletableObserver { - override fun onComplete() { - _updatedFavourite.postValue(favouriteStatus) - } - - override fun onSubscribe(d: Disposable) { - } - - override fun onError(e: Throwable) { - } - }) - } - - - fun saveTimetableContent(lineId: Int, timetableContent: String) { - timetableDetailUseCase.saveTimetableDetail(lineId, timetableContent, LocalDateTime.now().toString()) - .subscribeOn(schedulers) - .observeOn(thread) - .subscribe(object: CompletableObserver{ - override fun onComplete() { - _smallLoading.postValue(false) - } - - override fun onSubscribe(d: Disposable) { - } - - override fun onError(e: Throwable) { - } - - }) - } - - private fun getTimetableBaseUrl(areaId: Int): String { - return when (areaId) { - TimetablesData.AREA_CITY -> Constants.AREA_CITY_URL - TimetablesData.AREA_URBAN -> Constants.AREA_URBAN_URL - TimetablesData.AREA_SUBURBAN -> Constants.AREA_SUBURBAN_URL - TimetablesData.AREA_TROGIR -> Constants.AREA_TROGIR_URL - TimetablesData.AREA_SOLTA -> Constants.AREA_SOLTA_URL - else -> throw IllegalArgumentException("Illegal areaId $areaId") - } - } - - private fun isntDownloadedRecently(date: String): Boolean { - return LocalDateTime.parse(date).isBefore(LocalDateTime.now().minusHours(DOWNLOADED_RECENTLY)) - } - - data class UpdatedTimetable(val content: String, val contentDate: String) -} diff --git a/app/src/main/java/com/am/stbus/presentation/theme/Color.kt b/app/src/main/java/com/am/stbus/presentation/theme/Color.kt new file mode 100644 index 0000000..3cfc403 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/theme/Color.kt @@ -0,0 +1,32 @@ +package com.am.breweyexplorer.presentation.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) + +val PastelRed = Color(0xFFFFADAD) +val PastelOrange = Color(0xFFFFD6A5) +val PastelYellow = Color(0xFFFDFFB6) +val PastelGreen = Color(0xFFCAFFBF) +val PastelBlue = Color(0xFF9BF6FF) +val PastelMauve = Color(0xFFA0C4FF) +val PastelPurple = Color(0xFFBDB2FF) +val PastelPurpleBg = Color(0xFFDFD6FD) +val PastelPink = Color(0xFFFFC6FF) + +val colors = listOf( + PastelRed, + PastelOrange, + //PastelYellow, + PastelGreen, + PastelBlue, + PastelMauve, + PastelPurple, + PastelPink +) diff --git a/app/src/main/java/com/am/stbus/presentation/theme/Theme.kt b/app/src/main/java/com/am/stbus/presentation/theme/Theme.kt new file mode 100644 index 0000000..7f91890 --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/theme/Theme.kt @@ -0,0 +1,64 @@ +package com.am.stbus.presentation.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import com.am.breweyexplorer.presentation.theme.Pink40 +import com.am.breweyexplorer.presentation.theme.Pink80 +import com.am.breweyexplorer.presentation.theme.Purple40 +import com.am.breweyexplorer.presentation.theme.Purple80 +import com.am.breweyexplorer.presentation.theme.PurpleGrey40 +import com.am.breweyexplorer.presentation.theme.PurpleGrey80 +import com.am.breweyexplorer.presentation.theme.Typography + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +// Dynamic color is available on Android 12+ + + +@Composable +fun SplitBusTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + + val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + val colors = when { + dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current) + dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current) + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colors, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/am/stbus/presentation/theme/Type.kt b/app/src/main/java/com/am/stbus/presentation/theme/Type.kt new file mode 100644 index 0000000..0166bae --- /dev/null +++ b/app/src/main/java/com/am/stbus/presentation/theme/Type.kt @@ -0,0 +1,34 @@ +package com.am.breweyexplorer.presentation.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/layout/activity_gmaps.xml b/app/src/main/res/layout/activity_gmaps.xml deleted file mode 100644 index 0171f4d..0000000 --- a/app/src/main/res/layout/activity_gmaps.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 15fc92b..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml deleted file mode 100644 index 76af597..0000000 --- a/app/src/main/res/layout/fragment_about.xml +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -