diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 4cb61aa..23f80b4 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -5,66 +5,36 @@ on: [ push, pull_request ]
jobs:
build:
name: Gradle Build
+ timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- name: Checkout repo
- uses: actions/checkout@v1
-
- - uses: actions/cache@v2
- with:
- path: |
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: ${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('**/buildSrc/**/*.kt') }}
+ uses: actions/checkout@v3
- - name: set up JDK 11
- uses: actions/setup-java@v2
+ - name: set up JDK
+ uses: actions/setup-java@v3
with:
- java-version: '11'
+ java-version: '17'
distribution: 'adopt'
+ - name: Setup Gradle with cache
+ uses: gradle/actions/setup-gradle@v3
+
- name: Setup Gradle
run: chmod +x gradlew
- - name: Gradle build
- run: bash ./gradlew assembleDebug
+ - name: Build library
+ run: bash ./gradlew library:assembleDebug
- test:
- name: Run Tests and lint checks
- runs-on: macos-latest
- timeout-minutes: 60
- needs: build
+ - name: Build sample
+ run: bash ./gradlew sample:assembleDebug
- steps:
- - name: Checkout repo
- uses: actions/checkout@v2
+ - name: Run kltlint in library
+ run: ./gradlew library:ktlintCheck
- - uses: actions/cache@v2
- with:
- path: |
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: ${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('**/buildSrc/**/*.kt') }}
-
- - name: Set up JDK 11
- uses: actions/setup-java@v2
- with:
- java-version: '11'
- distribution: 'adopt'
-
- - name: Setup Gradle
- run: chmod +x gradlew
-
- - name: Run kltlint
- run: ./gradlew ktlintCheck
+ - name: Run kltlint in sample
+ run: ./gradlew sample:ktlintCheck
- name: Unit tests
- run: ./gradlew test --stacktrace
-
- - name: Upload Reports
- uses: actions/upload-artifact@v2
- with:
- name: Test-Reports
- path: AppUpdateChecker/build/reports
- if: always()
\ No newline at end of file
+ run: ./gradlew test --stacktrace
\ No newline at end of file
diff --git a/README.md b/README.md
index 130a7b0..84d8962 100644
--- a/README.md
+++ b/README.md
@@ -1,156 +1,308 @@
-
-
AppUpdateChecker
-
- An Android library that checks for app updates.
-
-
-
+# AppUpdateChecker
-![Downloads](https://img.shields.io/github/downloads/Sharkaboi/AppUpdateChecker/total) ![Contributors](https://img.shields.io/github/contributors/Sharkaboi/AppUpdateChecker?color=dark-green) ![Issues](https://img.shields.io/github/issues/Sharkaboi/AppUpdateChecker) ![License](https://img.shields.io/github/license/Sharkaboi/AppUpdateChecker) ![Forks](https://img.shields.io/github/forks/Sharkaboi/AppUpdateChecker?style=social) ![Stargazers](https://img.shields.io/github/stars/Sharkaboi/AppUpdateChecker?style=social)
+An Android library that checks for app updates.
-## TechStack
-* Kotlin
-* Coroutines
-* Retrofit
-* Moshi
-* SimpleXML
+[![](https://jitpack.io/v/Sharkaboi/AppUpdateChecker.svg)](https://jitpack.io/#Sharkaboi/AppUpdateChecker) ![License](https://img.shields.io/github/license/Sharkaboi/AppUpdateChecker) ![Contributors](https://img.shields.io/github/contributors/Sharkaboi/AppUpdateChecker?color=dark-green) ![Issues](https://img.shields.io/github/issues/Sharkaboi/AppUpdateChecker) ![Forks](https://img.shields.io/github/forks/Sharkaboi/AppUpdateChecker?style=social) ![Stargazers](https://img.shields.io/github/stars/Sharkaboi/AppUpdateChecker?style=social)
## Instructions
-* Add Jitpack to your project
+- Add Jitpack to your project
```groovy
-allprojects {
- repositories {
- ...
- maven { url 'https://jitpack.io' }
- }
+repositories {
+ ...
+ maven { url 'https://jitpack.io' }
}
```
-* Add Dependency
-[![](https://jitpack.io/v/Sharkaboi/AppUpdateChecker.svg)](https://jitpack.io/#Sharkaboi/AppUpdateChecker)
+- Add
+ Dependency [![](https://jitpack.io/v/Sharkaboi/AppUpdateChecker.svg)](https://jitpack.io/#Sharkaboi/AppUpdateChecker)
+
```groovy
dependencies {
- implementation 'com.github.Sharkaboi:AppUpdateChecker:'
+ implementation 'com.github.Sharkaboi:AppUpdateChecker:'
}
```
## Usage
-* Initialize update checker instance
+### Initialize update checker instance
+
```kotlin
val updateChecker = AppUpdateChecker(
- context = context,
- // Any of the sources mentioned below
- source = AppUpdateCheckerSource.*(),
- // Optional : Takes versionName mentioned in gradle by default
- currentVersionTag = "v1.0.0"
+ // Any of the sources mentioned below
+ source = >(
+ ... // params,
+ currentVersion = "v1.0.0"
+ )
)
```
+
This is so that you can instantiate with your existing DI setup.
-* Available sources :
+### Available update check sources
+
+#### Github release tags
+
+##### Using github public API
+
+Github public API has rate limit as
+mentioned [here](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api).
+
```kotlin
-// Github source
-AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = "AppUpdateChecker"
+GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "AppUpdateChecker",
+ currentVersion = "v1.0.0"
)
+```
+
+##### Using github authenticated API
+
+Github authenticated API has higher rate limit. Check
+github's [docs](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#getting-a-higher-rate-limit)
+to obtain a token.
-// FDroid source
-AppUpdateCheckerSource.FDroidSource(
- // Optional : Takes packageName from context by default
- packageName = "com.sharkaboi.appupdatechecker"
+```kotlin
+GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "AppUpdateChecker",
+ currentVersion = "v1.0.0",
+ bearerToken = ""
)
+```
+
+#### FDroid
-// JSON source
-AppUpdateCheckerSource.JsonSource(
- // JSON structure mentioned below
- jsonEndpoint = "https://mywebsite.com/version.json"
+##### With version name
+
+```kotlin
+FDroidVersionNameSource(
+ packageName = "com.sharkaboi.appupdatechecker",
+ currentVersion = "v1.0.0"
)
+```
+
+##### With version code
-// XML source
-AppUpdateCheckerSource.XMLSource(
- // XML structure mentioned below
- xmlEndpoint = "https://mywebsite.com/version.xml"
+```kotlin
+FDroidVersionCodeSource(
+ packageName = "com.sharkaboi.appupdatechecker",
+ currentVersion = 10100
)
```
-JSON structure :
+
+#### JSON
+
+##### With version name
+
+```kotlin
+JsonVersionNameSource(
+ jsonEndpoint = "https://mywebsite.com/version.json",
+ currentVersion = "v1.0.0"
+)
+```
+
+JSON structure supported by default :
+
```javascript
{
- "latestVersion": "v1.2",
+ "latestVersionName": "v1.2",
"latestVersionUrl": "https://mywebsite.com/v1.2",
"releaseNotes": "My epic changelog"
}
```
-XML structure :
+
+##### With version code
+
+```kotlin
+JsonVersionCodeSource(
+ jsonEndpoint = "https://mywebsite.com/version.json",
+ currentVersion = 10100
+)
+```
+
+JSON structure supported by default :
+
+```javascript
+{
+ "latestVersionCode": 10102,
+ "latestVersionUrl": "https://mywebsite.com/v1.2",
+ "releaseNotes": "My epic changelog"
+}
+```
+
+#### XML
+
+##### With version name
+
+```kotlin
+XMLVersionNameSource(
+ xmlEndpoint = "https://mywebsite.com/version.xml",
+ currentVersion = "v1.0.0"
+)
+```
+
+XML structure supported by default :
+
+```xml
+
+
+ v1.2
+ https://mywebsite.com/v1.2
+ My epic changelog
+
+```
+
+##### With version code
+
+```kotlin
+XMLVersionCodeSource(
+ xmlEndpoint = "https://mywebsite.com/version.xml",
+ currentVersion = 10100
+)
+```
+
+XML structure supported by default :
+
```xml
+
- v1.2
- https://mywebsite.com/v1.2
- My epic changelog
+ 10102
+ https://mywebsite.com/v1.2
+ My epic changelog
```
-* Check update
+#### Custom source
+
+```kotlin
+class CustomVersionSource(
+ override val currentVersion: String,
+ override var versionComparator: VersionComparator = DefaultStringVersionComparator
+) : AppUpdateCheckerSource() {
+ private val customSource = "https://mywebsite.com/latestVersion"
+
+ override suspend fun queryVersionDetails(): VersionDetails {
+ // Do your processing to fetch from source here
+ ...
+ // Handle exceptions if needed here, wrap with AppUpdateCheckerException if needed
+ return VersionDetails(
+ latestVersion = version,
+ latestVersionUrl = "https://mywebsite.com/download.apk",
+ releaseNotes = null
+ )
+ }
+}
+```
+
+### Check for update
+
```kotlin
viewModelScope.launch {
- val result = updateChecker.checkUpdate()
-
- //or
-
- val deferred = updateChecker.checkUpdateAsync()
- val result = deferred.await()
-
- when(result){
- // Update found
- is UpdateState.UpdateAvailable ->
-
- // Already has the latest version installed
- UpdateState.LatestVersionInstalled ->
-
- // Mentioned package name not found in FDroid
- UpdateState.FDroidInvalid ->
-
- // Mentioned package name was invalid
- UpdateState.FDroidMalformed ->
-
- // Mentioned repo with username not found in Github
- UpdateState.GithubInvalid ->
-
- // Mentioned repo or username was invalid
- UpdateState.GithubMalformed ->
-
- // Mentioned JSON endpoint had invalid structure or wasn't reachable
- UpdateState.JSONInvalid ->
-
- // Mentioned JSON endpoint was not a valid URL
- UpdateState.JSONMalformed ->
-
- // Mentioned XML endpoint had invalid structure or wasn't reachable
- UpdateState.XMLInvalid ->
-
- // Mentioned XML endpoint was not a valid URL
- UpdateState.XMLMalformed ->
-
- // No network found
- UpdateState.NoNetworkFound ->
-
- // Generic error to wrap other errors
- is UpdateState.GenericError ->
+ val result = updateChecker.checkUpdate()
+
+ // or
+
+ val deferred = updateChecker.checkUpdateAsync()
+ val result = deferred.await()
+
+ when (result) {
+ // Update found
+ is UpdateResult.NoUpdate -> println(result.versionDetails.toString())
+ // Already has the latest version installed
+ UpdateState.NoUpdate ->..
+ }
+}
+```
+
+### Error handling
+
+```kotlin
+
+if (throwable is AppUpdateCheckerException) {
+ when (throwable) {
+ is GenericError -> {
+ // Generic errors (network, ssl, parse)
+ }
+
+ is InvalidEndPointException -> {
+ // Invalid endpoint passed for sources with endpoints (json/xml)
}
+
+ is InvalidPackageNameException -> {
+ // Invalid package name passed
+ }
+
+ is InvalidRepositoryNameException -> {
+ // Invalid repository name passed
+ }
+
+ is InvalidUserNameException -> {
+ // Invalid username passed
+ }
+
+ is InvalidVersionException -> {
+ // Invalid version format for default version name comparator
+ }
+
+ is PackageNotFoundException -> {
+ // Remote Service returned 404
+ }
+
+ is RemoteError -> {
+ // Other remote server error code
+ }
+ }
+}
+```
+
+### Custom version comparator
+
+```kotlin
+val source = JsonVersionNameSource(
+ jsonEndpoint = "https://mywebsite.com/version.json",
+ currentVersion = "v1.0-alpha"
+)
+
+val customVersionComparator = object : VersionComparator {
+ override fun isNewerVersion(
+ currentVersion: String,
+ newVersion: String
+ ): Boolean {
+ // Perform custom logic here or can use default comparators provided with library.
+ return DefaultStringVersionComparator.isNewerVersion(
+ currentVersion.substringBefore('-'),
+ newVersion.substringBefore('-')
+ )
}
+}
+source.setCustomVersionComparator(customVersionComparator)
+val testChecker = AppUpdateChecker(source = source)
+val result = testChecker.checkUpdate()
+...
```
+
+## TechStack
+
+- Kotlin
+- Coroutines
+- Retrofit
+- Moshi
+- SimpleXML
+
## Background
-This project is heavily inspired by [javiersantos/AppUpdater](https://github.com/javiersantos/AppUpdater). The project had been stale for a while and had been on AsyncTasks so decided to write my own library.
+This project is heavily inspired
+by [javiersantos/AppUpdater](https://github.com/javiersantos/AppUpdater). The project had been stale
+for a while and had been on AsyncTasks so decided to write my own library.
Of course, the library is very early stage and doesn't have all the features but is WIP.
## Roadmap
-See the [open issues](https://github.com/Sharkaboi/AppUpdateChecker/issues) for a list of proposed features (and known issues).
+See the [open issues](https://github.com/Sharkaboi/AppUpdateChecker/issues) for a list of proposed
+features (and known issues).
## Contributing
@@ -164,15 +316,14 @@ PR's are welcome. Please try to follow the template.
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
-
## Authors
-* [Sharkaboi](https://github.com/Sharkaboi)
+- [Sharkaboi](https://github.com/Sharkaboi)
## Credits
-* [javiersantos](https://github.com/javiersantos)
-* [Shields](https://shields.io/)
+- [javiersantos](https://github.com/javiersantos)
+- [Shields](https://shields.io/)
## License
diff --git a/build.gradle b/build.gradle
index 6e00895..63dff84 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,19 +1,4 @@
buildscript {
- ext {
- agpVersion = "4.2.2"
- kotlinVersion = "1.5.30"
- ktxCoreVersion = "1.6.0"
- appCompatVersion = "1.3.1"
- coroutinesVersion = "1.5.2"
- retrofitVersion = "2.9.0"
- moshiVersion = "1.12.0"
- moshiRetrofitVersion = "2.9.0"
- simpleXmlVersion = "2.9.0"
- ktLintVersion = "10.1.0"
- coroutineCallAdapterVersion = "0.9.2"
- jUnitVersion = "4.13.2"
- mockkVersion = "1.12.0"
- }
repositories {
google()
mavenCentral()
@@ -21,10 +6,34 @@ buildscript {
url "https://plugins.gradle.org/m2/"
}
}
+
dependencies {
- classpath "com.android.tools.build:gradle:$agpVersion"
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
- classpath "org.jlleitschuh.gradle:ktlint-gradle:$ktLintVersion"
+ classpath "com.android.tools.build:gradle:8.2.2"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23"
+ }
+}
+
+plugins {
+ id 'com.google.devtools.ksp' version '1.9.23-1.0.19' apply false
+ id "org.jlleitschuh.gradle.ktlint" version "12.1.0" apply false
+}
+
+subprojects {
+ apply plugin: "org.jlleitschuh.gradle.ktlint"
+
+ ktlint {
+ version.set("1.2.1")
+ android = true
+ outputToConsole = true
+ disabledRules = ["no-wildcard-imports"]
+ filter {
+ exclude { element ->
+ element.file.path.contains("/test/")
+ || element.file.path.contains("\\test\\")
+ || element.file.path.contains("/androidTest/")
+ || element.file.path.contains("\\androidTest\\")
+ }
+ }
}
}
@@ -37,8 +46,4 @@ allprojects {
}
maven { url "https://jitpack.io" }
}
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 30a925a..44c1065 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Sep 12 11:25:08 IST 2021
+#Sat Mar 23 21:51:53 IST 2024
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/library/build.gradle b/library/build.gradle
index 113246f..5621924 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -1,79 +1,110 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
- id 'kotlin-kapt'
- id 'org.jlleitschuh.gradle.ktlint'
+ id 'com.google.devtools.ksp'
id 'maven-publish'
}
-group = 'com.github.Sharkaboi'
-version = 'v1.0.2'
-
-repositories {
- google()
- mavenCentral()
- maven {
- url "https://plugins.gradle.org/m2/"
- }
- maven { url "https://jitpack.io" }
+project.ext {
+ group_id = "com.github.sharkaboi"
+ package_name = "com.github.sharkaboi.appupdatechecker"
+ artifact_id = "appupdatechecker"
+ version_name = "1.0.3"
+ version_code = 10103
+ github_url = "com.github.sharkaboi.appupdatechecker"
+ license_url = "https://opensource.org/license/mit"
+ license_name = "MIT License"
+ dev_name = "sharkaboi"
+ dev_email = "cybersharkbusiness@gmail.com"
+ lib_description = "An android library that checks for app updates."
}
-android {
- compileSdkVersion 31
+group = project.ext.group_id
+version = project.ext.version_name
+android {
defaultConfig {
- minSdkVersion 21
- targetSdkVersion 31
+ namespace project.ext.package_name
+ minSdk 19
+ compileSdk 34
+ targetSdkVersion 34
versionCode 1
- versionName "1.0.2"
+ versionName project.ext.version_name
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
- minifyEnabled false
+ minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
- jvmTarget = "1.8"
+ jvmTarget = "17"
+ freeCompilerArgs = ["-Xstring-concat=inline"]
}
}
dependencies {
- implementation "androidx.core:core-ktx:$ktxCoreVersion"
- implementation "androidx.appcompat:appcompat:$appCompatVersion"
- api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
- api "com.squareup.retrofit2:retrofit:$retrofitVersion"
- api "com.squareup.moshi:moshi-kotlin:$moshiVersion"
- kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
- api "com.squareup.retrofit2:converter-moshi:$moshiRetrofitVersion"
- api "com.squareup.retrofit2:converter-simplexml:$simpleXmlVersion"
- api "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$coroutineCallAdapterVersion"
- testImplementation "junit:junit:$jUnitVersion"
- testImplementation "io.mockk:mockk:$mockkVersion"
-}
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0"
+ def moshiVersion = '1.15.1'
+ implementation "com.squareup.moshi:moshi-kotlin:$moshiVersion"
+ ksp "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
+ def retrofitVersion = '2.10.0'
+ implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
+ implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion"
+ implementation "com.squareup.retrofit2:converter-simplexml:$retrofitVersion"
-ktlint {
- disabledRules.set(["no-wildcard-imports"])
+ // For unit test
+ testImplementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion"
+ testImplementation "junit:junit:4.13.2"
+ testImplementation "io.mockk:mockk:1.13.10"
}
afterEvaluate {
publishing {
publications {
- // Creates a Maven publication called "release".
- release(MavenPublication) {
+
+ def pomConfig = {
+ licenses {
+ license {
+ name project.ext.license_name
+ url project.ext.license_url
+ distribution "repo"
+ }
+ }
+ developers {
+ developer {
+ id project.ext.dev_name
+ name project.ext.dev_name
+ email project.ext.dev_email
+ }
+ }
+
+ scm {
+ url project.ext.github_url
+ }
+ }
+
+ mavenPublication(MavenPublication) {
from components.release
- groupId = 'com.github.Sharkaboi'
- artifactId = 'AppUpdateChecker'
- version = 'v1.0.2'
+ groupId = project.ext.group_id
+ artifactId = project.ext.artifact_id
+ version = project.ext.version_name
+ pom.withXml {
+ def root = asNode(null)
+ root.appendNode('description', project.ext.lib_description)
+ root.appendNode('name', project.ext.artifact_id)
+ root.appendNode('url', project.ext.github_url)
+ root.children().last() + pomConfig
+ }
}
}
}
diff --git a/library/consumer-rules.pro b/library/consumer-rules.pro
index e69de29..ebb610c 100644
--- a/library/consumer-rules.pro
+++ b/library/consumer-rules.pro
@@ -0,0 +1,6 @@
+-dontwarn org.xmlpull.v1.**
+-dontwarn org.kxml2.io.**
+-dontwarn android.content.res.**
+
+-keep class org.xmlpull.** { *; }
+-keepclassmembers class org.xmlpull.** { *; }
\ No newline at end of file
diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml
index 227dea5..a880029 100644
--- a/library/src/main/AndroidManifest.xml
+++ b/library/src/main/AndroidManifest.xml
@@ -1,6 +1,4 @@
-
-
-
+
+
\ No newline at end of file
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/AppUpdateChecker.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/AppUpdateChecker.kt
index 51b9eb3..bddb2bd 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/AppUpdateChecker.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/AppUpdateChecker.kt
@@ -1,133 +1,25 @@
package com.sharkaboi.appupdatechecker
-import android.content.Context
-import com.sharkaboi.appupdatechecker.extensions.*
-import com.sharkaboi.appupdatechecker.interfaces.IAppUpdateChecker
-import com.sharkaboi.appupdatechecker.mappers.toUpdateAvailableState
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import com.sharkaboi.appupdatechecker.provider.AppUpdateServices
-import com.sharkaboi.appupdatechecker.sources.fdroid.isAfterVersion
-import kotlinx.coroutines.*
-
-class AppUpdateChecker(
- private val context: Context,
- private val source: AppUpdateCheckerSource,
- private val currentVersionTag: String = context.installedVersionTag
-) : IAppUpdateChecker {
-
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.sources.AppUpdateCheckerSource
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.withContext
+
+class AppUpdateChecker(private val source: AppUpdateCheckerSource) {
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
- override suspend fun checkUpdate(): UpdateState = withContext(dispatcher) {
- return@withContext checkUpdateAsync().await()
- }
-
- override suspend fun checkUpdateAsync(): Deferred = withContext(dispatcher) {
- return@withContext async {
- try {
- if (!context.isInternetConnected) {
- return@async UpdateState.NoNetworkFound
- }
- require(currentVersionTag.matches(versionRegex)) { "Invalid current version tag" }
- return@async when (source) {
- is AppUpdateCheckerSource.FDroidSource -> handleFDroidCheck()
- is AppUpdateCheckerSource.GithubSource -> handleGithubCheck()
- is AppUpdateCheckerSource.JsonSource -> handleJsonCheck()
- is AppUpdateCheckerSource.XMLSource -> handleXmlCheck()
- }
- } catch (e: Exception) {
- e.printStackTrace()
- return@async UpdateState.GenericError(e)
- }
- }
- }
-
- private suspend fun handleFDroidCheck(): UpdateState {
- require(source is AppUpdateCheckerSource.FDroidSource) { "Invalid source" }
- val packageName = source.packageName ?: context.packageName
- val modifiedSource = source.copy(
- packageName = packageName
- )
- if (modifiedSource.isValid()) {
- return try {
- val release = AppUpdateServices.fDroidService.getReleasesAsync(
- packageName = packageName
- ).await()
- if (release.isAfterVersion(currentVersionTag)) {
- release.toUpdateAvailableState(modifiedSource)
- } else {
- UpdateState.LatestVersionInstalled
- }
- } catch (e: Exception) {
- e.printStackTrace()
- UpdateState.FDroidInvalid
- }
- } else {
- return UpdateState.FDroidMalformed
- }
- }
-
- private suspend fun handleGithubCheck(): UpdateState {
- require(source is AppUpdateCheckerSource.GithubSource) { "Invalid source" }
- if (source.isValid()) {
- return try {
- val release = AppUpdateServices.githubService.getLatestReleaseAsync(
- owner = source.ownerUsername,
- repo = source.repoName
- ).await()
- if (release.tagName.isAfterVersion(currentVersionTag)) {
- release.toUpdateAvailableState(source)
- } else {
- UpdateState.LatestVersionInstalled
- }
- } catch (e: Exception) {
- e.printStackTrace()
- UpdateState.GithubInvalid
- }
- } else {
- return UpdateState.GithubMalformed
- }
- }
-
- private suspend fun handleJsonCheck(): UpdateState {
- require(source is AppUpdateCheckerSource.JsonSource) { "Invalid source" }
- if (source.isValid()) {
- return try {
- val release = AppUpdateServices.jsonService.getJsonReleaseMetaDataAsync(
- url = source.jsonEndpoint
- ).await()
- if (release.latestVersion.isAfterVersion(currentVersionTag)) {
- release.toUpdateAvailableState(source)
- } else {
- UpdateState.LatestVersionInstalled
- }
- } catch (e: Exception) {
- e.printStackTrace()
- UpdateState.JSONInvalid
- }
- } else {
- return UpdateState.JSONMalformed
+ suspend fun checkUpdate(): UpdateResult =
+ withContext(dispatcher) {
+ return@withContext checkUpdateAsync().await()
}
- }
- private suspend fun handleXmlCheck(): UpdateState {
- require(source is AppUpdateCheckerSource.XMLSource) { "Invalid source" }
- if (source.isValid()) {
- return try {
- val release = AppUpdateServices.xmlService.getXMLReleaseMetaDataAsync(
- url = source.xmlEndpoint
- ).await()
- if (release.latestVersion.isAfterVersion(currentVersionTag)) {
- release.toUpdateAvailableState(source)
- } else {
- UpdateState.LatestVersionInstalled
- }
- } catch (e: Exception) {
- e.printStackTrace()
- UpdateState.XMLInvalid
+ suspend fun checkUpdateAsync(): Deferred =
+ withContext(dispatcher) {
+ return@withContext async {
+ return@async source.getUpdateState()
}
- } else {
- return UpdateState.XMLMalformed
}
- }
}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/ContextExtensions.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/ContextExtensions.kt
deleted file mode 100644
index a53afa9..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/ContextExtensions.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.sharkaboi.appupdatechecker.extensions
-
-import android.content.Context
-import android.net.ConnectivityManager
-import android.net.NetworkCapabilities
-import android.os.Build
-
-internal val Context.isInternetConnected: Boolean
- get() {
- val connectivityManager =
- this.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
- return connectivityManager?.let { cm ->
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- cm.activeNetwork?.let { an ->
- val networkCapabilities = cm.getNetworkCapabilities(an)
- networkCapabilities?.let { nc ->
- nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
- nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
- }
- }
- } else {
- val netInfo = cm.activeNetworkInfo
- netInfo != null && netInfo.isConnectedOrConnecting
- }
- } ?: false
- }
-
-internal val Context.installedVersionTag: String
- get() {
- return this.packageManager.getPackageInfo(this.packageName, 0).versionName
- }
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/StringExtensions.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/StringExtensions.kt
deleted file mode 100644
index 4315a57..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/StringExtensions.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.sharkaboi.appupdatechecker.extensions
-
-internal fun String.isAfterVersion(other: String): Boolean {
- val thisVersionTrimmed = this.trim()
- val otherVersionTrimmed = other.trim()
- require(thisVersionTrimmed.matches(versionRegex)) { "Current version tag is invalid" }
- require(otherVersionTrimmed.matches(versionRegex)) { "Incoming version tag is invalid" }
- val thisVersion = thisVersionTrimmed.removePrefix("v").removePrefix("V")
- val otherVersion = otherVersionTrimmed.removePrefix("v").removePrefix("V")
- if (thisVersion == otherVersion) {
- return false
- }
- val thisParts = thisVersion.split(".").map { it.toLong() }
- val otherParts = otherVersion.split(".").map { it.toLong() }
- val length = thisParts.size.coerceAtLeast(otherParts.size)
- for (i in 0 until length) {
- val thisPart = if (i < thisParts.size) thisParts[i] else 0
- val otherPart = if (i < otherParts.size) otherParts[i] else 0
- if (thisPart == otherPart) continue
- if (thisPart < otherPart) return false
- if (thisPart > otherPart) return true
- }
- return false
-}
-
-// Starts with v or V and followed by matched numbers and dots.
-internal val versionRegex = Regex("[v|V]?\\d+(.\\d+)*")
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/ValidationExtensions.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/ValidationExtensions.kt
deleted file mode 100644
index a6bcc58..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/extensions/ValidationExtensions.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.sharkaboi.appupdatechecker.extensions
-
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import okhttp3.HttpUrl
-
-internal fun AppUpdateCheckerSource.GithubSource.isValid(): Boolean {
- return ownerUsername.isNotBlank() && repoName.isNotBlank()
-}
-
-internal fun AppUpdateCheckerSource.JsonSource.isValid(): Boolean {
- return HttpUrl.parse(jsonEndpoint) != null && jsonEndpoint.isNotBlank()
-}
-
-internal fun AppUpdateCheckerSource.XMLSource.isValid(): Boolean {
- return HttpUrl.parse(xmlEndpoint) != null && xmlEndpoint.isNotBlank()
-}
-
-internal fun AppUpdateCheckerSource.FDroidSource.isValid(): Boolean {
- return packageName != null && packageName.isNotBlank() && packageName.contains('.')
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/interfaces/IAppUpdateChecker.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/interfaces/IAppUpdateChecker.kt
deleted file mode 100644
index e87a1df..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/interfaces/IAppUpdateChecker.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.sharkaboi.appupdatechecker.interfaces
-
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import kotlinx.coroutines.Deferred
-
-internal interface IAppUpdateChecker {
- suspend fun checkUpdate(): UpdateState
- suspend fun checkUpdateAsync(): Deferred
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/mappers/UpdateStateMappers.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/mappers/UpdateStateMappers.kt
deleted file mode 100644
index 90bd46b..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/mappers/UpdateStateMappers.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.sharkaboi.appupdatechecker.mappers
-
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import com.sharkaboi.appupdatechecker.sources.fdroid.FdroidConstants
-import com.sharkaboi.appupdatechecker.sources.fdroid.FdroidResponse
-import com.sharkaboi.appupdatechecker.sources.github.GithubResponse
-import com.sharkaboi.appupdatechecker.sources.json.JsonResponse
-import com.sharkaboi.appupdatechecker.sources.xml.XMLResponse
-
-internal fun GithubResponse.toUpdateAvailableState(source: AppUpdateCheckerSource): UpdateState.UpdateAvailable {
- return UpdateState.UpdateAvailable(
- releaseNotes = body,
- latestVersionUrl = htmlUrl,
- latestVersion = tagName,
- source = source
- )
-}
-
-internal fun JsonResponse.toUpdateAvailableState(source: AppUpdateCheckerSource): UpdateState.UpdateAvailable {
- return UpdateState.UpdateAvailable(
- releaseNotes = releaseNotes,
- latestVersionUrl = latestVersionUrl,
- latestVersion = latestVersion,
- source = source
- )
-}
-
-internal fun FdroidResponse.toUpdateAvailableState(source: AppUpdateCheckerSource): UpdateState.UpdateAvailable {
- return UpdateState.UpdateAvailable(
- releaseNotes = null,
- latestVersionUrl = FdroidConstants.HTML_BASE_URL + this.packageName,
- latestVersion = this.packages.first().versionName,
- source = source
- )
-}
-
-internal fun XMLResponse.toUpdateAvailableState(source: AppUpdateCheckerSource): UpdateState.UpdateAvailable {
- return UpdateState.UpdateAvailable(
- releaseNotes = this.releaseNotes,
- latestVersionUrl = this.latestVersionUrl,
- latestVersion = this.latestVersion,
- source = source
- )
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/models/AppUpdateCheckerSource.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/models/AppUpdateCheckerSource.kt
deleted file mode 100644
index c0f2901..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/models/AppUpdateCheckerSource.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.sharkaboi.appupdatechecker.models
-
-sealed class AppUpdateCheckerSource {
-
- data class GithubSource(
- val ownerUsername: String,
- val repoName: String
- ) : AppUpdateCheckerSource()
-
- data class FDroidSource(
- val packageName: String? = null
- ) : AppUpdateCheckerSource()
-
- data class JsonSource(
- val jsonEndpoint: String
- ) : AppUpdateCheckerSource()
-
- data class XMLSource(
- val xmlEndpoint: String
- ) : AppUpdateCheckerSource()
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/models/Exceptions.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/models/Exceptions.kt
new file mode 100644
index 0000000..689ebff
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/models/Exceptions.kt
@@ -0,0 +1,21 @@
+package com.sharkaboi.appupdatechecker.models
+
+sealed interface AppUpdateCheckerException
+
+class InvalidVersionException(message: String) : Exception(message), AppUpdateCheckerException
+
+class InvalidPackageNameException(message: String) : Exception(message), AppUpdateCheckerException
+
+class PackageNotFoundException(message: String) : Exception(message), AppUpdateCheckerException
+
+class InvalidUserNameException(message: String) : Exception(message), AppUpdateCheckerException
+
+class InvalidRepositoryNameException(message: String) :
+ Exception(message),
+ AppUpdateCheckerException
+
+class InvalidEndPointException(message: String) : Exception(message), AppUpdateCheckerException
+
+class RemoteError(throwable: Throwable) : Exception(throwable), AppUpdateCheckerException
+
+class GenericError(throwable: Throwable) : Exception(throwable), AppUpdateCheckerException
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/models/UpdateResult.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/models/UpdateResult.kt
new file mode 100644
index 0000000..d504889
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/models/UpdateResult.kt
@@ -0,0 +1,9 @@
+package com.sharkaboi.appupdatechecker.models
+
+sealed interface UpdateResult {
+ data class UpdateAvailable(
+ val versionDetails: VersionDetails,
+ ) : UpdateResult
+
+ data object NoUpdate : UpdateResult
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/models/UpdateState.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/models/UpdateState.kt
deleted file mode 100644
index a321d1e..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/models/UpdateState.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.sharkaboi.appupdatechecker.models
-
-sealed class UpdateState {
-
- data class UpdateAvailable(
- val latestVersion: String,
- val latestVersionUrl: String,
- val releaseNotes: String?,
- val source: AppUpdateCheckerSource
- ) : UpdateState()
-
- // Latest version already installed
- object LatestVersionInstalled : UpdateState()
-
- // GitHub user or repo is empty string
- object GithubMalformed : UpdateState()
-
- // GitHub repo is private or no releases found of matching type
- object GithubInvalid : UpdateState()
-
- // FDroid package name not provided or empty or does not contain '.'
- object FDroidMalformed : UpdateState()
-
- // Package not found in FDroid
- object FDroidInvalid : UpdateState()
-
- // No Internet connection available
- object NoNetworkFound : UpdateState()
-
- // URL for the XML file is not valid
- object XMLMalformed : UpdateState()
-
- // XML file is invalid or is unreachable
- object XMLInvalid : UpdateState()
-
- // URL for the JSON file is not valid
- object JSONMalformed : UpdateState()
-
- // URL for the JSON file is not valid
- object JSONInvalid : UpdateState()
-
- // Generic UpdateState type to handle other UpdateStates
- data class GenericError(
- val exception: Exception
- ) : UpdateState()
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/models/VersionDetails.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/models/VersionDetails.kt
new file mode 100644
index 0000000..53e92fb
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/models/VersionDetails.kt
@@ -0,0 +1,7 @@
+package com.sharkaboi.appupdatechecker.models
+
+data class VersionDetails(
+ val latestVersion: T,
+ val latestVersionUrl: String,
+ val releaseNotes: String?,
+)
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/provider/AppUpdateServices.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/provider/AppUpdateServices.kt
deleted file mode 100644
index 8e6fbf2..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/provider/AppUpdateServices.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.sharkaboi.appupdatechecker.provider
-
-import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
-import com.sharkaboi.appupdatechecker.sources.fdroid.FdroidConstants
-import com.sharkaboi.appupdatechecker.sources.fdroid.FdroidService
-import com.sharkaboi.appupdatechecker.sources.github.GithubConstants
-import com.sharkaboi.appupdatechecker.sources.github.GithubService
-import com.sharkaboi.appupdatechecker.sources.json.JsonConstants
-import com.sharkaboi.appupdatechecker.sources.json.JsonService
-import com.sharkaboi.appupdatechecker.sources.xml.XMLConstants
-import com.sharkaboi.appupdatechecker.sources.xml.XMLService
-import retrofit2.Retrofit
-import retrofit2.converter.moshi.MoshiConverterFactory
-import retrofit2.converter.simplexml.SimpleXmlConverterFactory
-
-internal object AppUpdateServices {
-
- val fDroidService: FdroidService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- Retrofit.Builder()
- .baseUrl(FdroidConstants.BASE_URL)
- .addCallAdapterFactory(CoroutineCallAdapterFactory())
- .addConverterFactory(MoshiConverterFactory.create())
- .build()
- .create(FdroidService::class.java)
- }
-
- val jsonService: JsonService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- Retrofit.Builder()
- .baseUrl(JsonConstants.BASE_URL)
- .addCallAdapterFactory(CoroutineCallAdapterFactory())
- .addConverterFactory(MoshiConverterFactory.create())
- .build()
- .create(JsonService::class.java)
- }
-
- val xmlService: XMLService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- Retrofit.Builder()
- .baseUrl(XMLConstants.BASE_URL)
- .addCallAdapterFactory(CoroutineCallAdapterFactory())
- .addConverterFactory(SimpleXmlConverterFactory.create())
- .build()
- .create(XMLService::class.java)
- }
-
- val githubService: GithubService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- Retrofit.Builder()
- .baseUrl(GithubConstants.BASE_URL)
- .addCallAdapterFactory(CoroutineCallAdapterFactory())
- .addConverterFactory(MoshiConverterFactory.create())
- .build()
- .create(GithubService::class.java)
- }
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/AppUpdateCheckerSource.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/AppUpdateCheckerSource.kt
new file mode 100644
index 0000000..377e39f
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/AppUpdateCheckerSource.kt
@@ -0,0 +1,43 @@
+package com.sharkaboi.appupdatechecker.sources
+
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidEndPointException
+import com.sharkaboi.appupdatechecker.models.InvalidPackageNameException
+import com.sharkaboi.appupdatechecker.models.InvalidRepositoryNameException
+import com.sharkaboi.appupdatechecker.models.InvalidUserNameException
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+import com.sharkaboi.appupdatechecker.models.PackageNotFoundException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.models.VersionDetails
+import com.sharkaboi.appupdatechecker.versions.VersionComparator
+
+abstract class AppUpdateCheckerSource {
+ protected abstract val currentVersion: T
+ protected abstract var versionComparator: VersionComparator
+
+ protected abstract suspend fun queryVersionDetails(): VersionDetails
+
+ @Throws(
+ InvalidVersionException::class,
+ InvalidPackageNameException::class,
+ PackageNotFoundException::class,
+ InvalidUserNameException::class,
+ InvalidRepositoryNameException::class,
+ InvalidEndPointException::class,
+ RemoteError::class,
+ GenericError::class,
+ )
+ suspend fun getUpdateState(): UpdateResult {
+ val versionDetails = queryVersionDetails()
+ if (versionComparator.isNewerVersion(currentVersion, versionDetails.latestVersion)) {
+ return UpdateResult.UpdateAvailable(versionDetails)
+ }
+
+ return UpdateResult.NoUpdate
+ }
+
+ fun setCustomVersionComparator(comparator: VersionComparator) {
+ this.versionComparator = comparator
+ }
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidResponse.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidResponse.kt
similarity index 70%
rename from library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidResponse.kt
rename to library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidResponse.kt
index ce17c02..5bcbd01 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidResponse.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidResponse.kt
@@ -1,23 +1,20 @@
package com.sharkaboi.appupdatechecker.sources.fdroid
-import androidx.annotation.Keep
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-@Keep
@JsonClass(generateAdapter = true)
-data class FdroidResponse(
+data class FDroidResponse(
@Json(name = "packageName")
val packageName: String,
@Json(name = "packages")
- val packages: List
+ val packages: List,
) {
- @Keep
@JsonClass(generateAdapter = true)
data class Package(
@Json(name = "versionCode")
- val versionCode: Int,
+ val versionCode: Long,
@Json(name = "versionName")
- val versionName: String
+ val versionName: String,
)
}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidService.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidService.kt
similarity index 60%
rename from library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidService.kt
rename to library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidService.kt
index 3e2e8b8..98593d1 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidService.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidService.kt
@@ -1,13 +1,12 @@
package com.sharkaboi.appupdatechecker.sources.fdroid
-import kotlinx.coroutines.Deferred
+import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
-internal interface FdroidService {
-
+internal interface FDroidService {
@GET(FdroidConstants.PATH)
- fun getReleasesAsync(
- @Path(FdroidConstants.PACKAGE_PATH_ID) packageName: String
- ): Deferred
+ suspend fun getReleases(
+ @Path(FdroidConstants.PACKAGE_PATH_ID) packageName: String,
+ ): Response
}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidSource.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidSource.kt
new file mode 100644
index 0000000..4341315
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FDroidSource.kt
@@ -0,0 +1,82 @@
+package com.sharkaboi.appupdatechecker.sources.fdroid
+
+import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerException
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidPackageNameException
+import com.sharkaboi.appupdatechecker.models.PackageNotFoundException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.VersionDetails
+import com.sharkaboi.appupdatechecker.sources.AppUpdateCheckerSource
+import com.sharkaboi.appupdatechecker.versions.DefaultStringVersionComparator
+import com.sharkaboi.appupdatechecker.versions.DefaultVersionCodeComparator
+import com.sharkaboi.appupdatechecker.versions.VersionComparator
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+
+sealed class FDroidSource : AppUpdateCheckerSource() {
+ abstract val packageName: String
+
+ private val service =
+ Retrofit.Builder()
+ .baseUrl(FdroidConstants.BASE_URL)
+ .addConverterFactory(MoshiConverterFactory.create())
+ .build()
+ .create(FDroidService::class.java)
+
+ protected suspend fun queryResponse(): FDroidResponse.Package {
+ if (packageName.isBlank()) {
+ throw InvalidPackageNameException("Invalid package name $packageName")
+ }
+
+ if (!packageName.contains('.')) {
+ throw InvalidPackageNameException("Invalid package name $packageName")
+ }
+ try {
+ val response = service.getReleases(packageName = packageName)
+ if (response.code() == 404) {
+ throw PackageNotFoundException("No details found for package name $packageName")
+ }
+
+ val release =
+ response.body()
+ ?: throw RemoteError(Throwable(response.errorBody()?.string()))
+ return release.packages.firstOrNull()
+ ?: throw PackageNotFoundException("No details found for package name $packageName")
+ } catch (e: Exception) {
+ if (e is AppUpdateCheckerException) {
+ throw e
+ }
+ throw GenericError(e)
+ }
+ }
+}
+
+data class FDroidVersionNameSource(
+ override val packageName: String,
+ override val currentVersion: String,
+ override var versionComparator: VersionComparator = DefaultStringVersionComparator,
+) : FDroidSource() {
+ override suspend fun queryVersionDetails(): VersionDetails {
+ val response = queryResponse()
+ return VersionDetails(
+ releaseNotes = null,
+ latestVersionUrl = FdroidConstants.HTML_BASE_URL + this.packageName,
+ latestVersion = response.versionName,
+ )
+ }
+}
+
+data class FDroidVersionCodeSource(
+ override val packageName: String,
+ override val currentVersion: Long,
+ override var versionComparator: VersionComparator = DefaultVersionCodeComparator,
+) : FDroidSource() {
+ override suspend fun queryVersionDetails(): VersionDetails {
+ val response = queryResponse()
+ return VersionDetails(
+ releaseNotes = null,
+ latestVersionUrl = FdroidConstants.HTML_BASE_URL + this.packageName,
+ latestVersion = response.versionCode,
+ )
+ }
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidExtensions.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidExtensions.kt
deleted file mode 100644
index 694de3f..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/fdroid/FdroidExtensions.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.sharkaboi.appupdatechecker.sources.fdroid
-
-import com.sharkaboi.appupdatechecker.extensions.isAfterVersion
-
-internal fun FdroidResponse.isAfterVersion(currentVersion: String): Boolean {
- require(this.packages.isNotEmpty()) { "No package found in FDroid for $packageName" }
- val latestVersion = this.packages.first()
- return latestVersion.versionName.isAfterVersion(currentVersion)
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubResponse.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubResponse.kt
index cfc0c24..b1e0326 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubResponse.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubResponse.kt
@@ -1,10 +1,8 @@
package com.sharkaboi.appupdatechecker.sources.github
-import androidx.annotation.Keep
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-@Keep
@JsonClass(generateAdapter = true)
internal data class GithubResponse(
@Json(name = "body")
@@ -12,5 +10,5 @@ internal data class GithubResponse(
@Json(name = "html_url")
val htmlUrl: String,
@Json(name = "tag_name")
- val tagName: String
+ val tagName: String,
)
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubService.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubService.kt
index cc80222..8c22bd3 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubService.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubService.kt
@@ -1,14 +1,21 @@
package com.sharkaboi.appupdatechecker.sources.github
-import kotlinx.coroutines.Deferred
+import retrofit2.Response
import retrofit2.http.GET
+import retrofit2.http.Header
import retrofit2.http.Path
internal interface GithubService {
+ @GET(GithubConstants.PATH)
+ suspend fun getLatestRelease(
+ @Path(GithubConstants.OWNER_PATH_ID) owner: String,
+ @Path(GithubConstants.REPO_PATH_ID) repo: String,
+ ): Response
@GET(GithubConstants.PATH)
- fun getLatestReleaseAsync(
+ suspend fun getLatestReleaseWithToken(
@Path(GithubConstants.OWNER_PATH_ID) owner: String,
@Path(GithubConstants.REPO_PATH_ID) repo: String,
- ): Deferred
+ @Header("Authorization") authHeader: String,
+ ): Response
}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubTagSource.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubTagSource.kt
new file mode 100644
index 0000000..a0d0456
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/github/GithubTagSource.kt
@@ -0,0 +1,69 @@
+package com.sharkaboi.appupdatechecker.sources.github
+
+import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerException
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidRepositoryNameException
+import com.sharkaboi.appupdatechecker.models.InvalidUserNameException
+import com.sharkaboi.appupdatechecker.models.PackageNotFoundException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.VersionDetails
+import com.sharkaboi.appupdatechecker.sources.AppUpdateCheckerSource
+import com.sharkaboi.appupdatechecker.versions.DefaultStringVersionComparator
+import com.sharkaboi.appupdatechecker.versions.VersionComparator
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+
+data class GithubTagSource(
+ val ownerUsername: String,
+ val repoName: String,
+ val bearerToken: String? = null,
+ override val currentVersion: String,
+ override var versionComparator: VersionComparator = DefaultStringVersionComparator,
+) : AppUpdateCheckerSource() {
+ private val service =
+ Retrofit.Builder()
+ .baseUrl(GithubConstants.BASE_URL)
+ .addConverterFactory(MoshiConverterFactory.create())
+ .build()
+ .create(GithubService::class.java)
+
+ override suspend fun queryVersionDetails(): VersionDetails {
+ if (ownerUsername.isBlank()) {
+ throw InvalidUserNameException("Invalid username $ownerUsername")
+ }
+
+ if (repoName.isBlank()) {
+ throw InvalidRepositoryNameException("Invalid repository name $repoName")
+ }
+
+ try {
+ val response =
+ if (!bearerToken.isNullOrBlank()) {
+ service.getLatestReleaseWithToken(
+ owner = ownerUsername,
+ repo = repoName,
+ authHeader = "Bearer $bearerToken",
+ )
+ } else {
+ service.getLatestRelease(owner = ownerUsername, repo = repoName)
+ }
+
+ if (response.code() == 404) {
+ throw PackageNotFoundException("Project not found in github with username $ownerUsername and repo $repoName")
+ }
+ val githubResponse =
+ response.body() ?: throw RemoteError(Throwable(response.errorBody()?.string()))
+
+ return VersionDetails(
+ releaseNotes = githubResponse.body,
+ latestVersionUrl = githubResponse.htmlUrl,
+ latestVersion = githubResponse.tagName,
+ )
+ } catch (e: Exception) {
+ if (e is AppUpdateCheckerException) {
+ throw e
+ }
+ throw GenericError(e)
+ }
+ }
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonConstants.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonConstants.kt
deleted file mode 100644
index 868ab59..0000000
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonConstants.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.sharkaboi.appupdatechecker.sources.json
-
-internal object JsonConstants {
- const val BASE_URL = "https://google.com"
-}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonResponse.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonResponse.kt
index 3211fc5..8d648e6 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonResponse.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonResponse.kt
@@ -1,16 +1,16 @@
package com.sharkaboi.appupdatechecker.sources.json
-import androidx.annotation.Keep
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-@Keep
@JsonClass(generateAdapter = true)
data class JsonResponse(
- @Json(name = "latestVersion")
- val latestVersion: String,
+ @Json(name = "latestVersionName")
+ val latestVersionName: String?,
+ @Json(name = "latestVersionCode")
+ val latestVersionCode: Long?,
@Json(name = "latestVersionUrl")
val latestVersionUrl: String,
@Json(name = "releaseNotes")
- val releaseNotes: String?
+ val releaseNotes: String?,
)
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonService.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonService.kt
index 3ef7db2..112afa1 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonService.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonService.kt
@@ -1,12 +1,12 @@
package com.sharkaboi.appupdatechecker.sources.json
-import kotlinx.coroutines.Deferred
+import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Url
internal interface JsonService {
@GET
- fun getJsonReleaseMetaDataAsync(
- @Url url: String
- ): Deferred
+ suspend fun getJsonReleaseMetaData(
+ @Url url: String,
+ ): Response
}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonSource.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonSource.kt
new file mode 100644
index 0000000..dad6267
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/json/JsonSource.kt
@@ -0,0 +1,79 @@
+package com.sharkaboi.appupdatechecker.sources.json
+
+import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerException
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidEndPointException
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.VersionDetails
+import com.sharkaboi.appupdatechecker.sources.AppUpdateCheckerSource
+import com.sharkaboi.appupdatechecker.versions.DefaultStringVersionComparator
+import com.sharkaboi.appupdatechecker.versions.DefaultVersionCodeComparator
+import com.sharkaboi.appupdatechecker.versions.VersionComparator
+import okhttp3.HttpUrl
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+
+sealed class JsonSource : AppUpdateCheckerSource() {
+ abstract val jsonEndpoint: String
+
+ private val service =
+ Retrofit.Builder()
+ .baseUrl("https://google.com")
+ .addConverterFactory(MoshiConverterFactory.create())
+ .build()
+ .create(JsonService::class.java)
+
+ protected suspend fun queryResponse(): JsonResponse {
+ if (HttpUrl.parse(jsonEndpoint) == null) {
+ throw InvalidEndPointException("Invalid endpoint $jsonEndpoint")
+ }
+
+ try {
+ val response = service.getJsonReleaseMetaData(url = jsonEndpoint)
+ return response.body()
+ ?: throw RemoteError(Throwable(response.errorBody()?.string()))
+ } catch (e: Exception) {
+ if (e is AppUpdateCheckerException) {
+ throw e
+ }
+ throw GenericError(e)
+ }
+ }
+}
+
+data class JsonVersionNameSource(
+ override val jsonEndpoint: String,
+ override val currentVersion: String,
+ override var versionComparator: VersionComparator = DefaultStringVersionComparator,
+) : JsonSource() {
+ override suspend fun queryVersionDetails(): VersionDetails {
+ val response = queryResponse()
+ val latestVersion =
+ response.latestVersionName
+ ?: throw InvalidVersionException("Version name was not found or null in response")
+ return VersionDetails(
+ releaseNotes = response.releaseNotes,
+ latestVersionUrl = response.latestVersionUrl,
+ latestVersion = latestVersion,
+ )
+ }
+}
+
+data class JsonVersionCodeSource(
+ override val jsonEndpoint: String,
+ override val currentVersion: Long,
+ override var versionComparator: VersionComparator = DefaultVersionCodeComparator,
+) : JsonSource() {
+ override suspend fun queryVersionDetails(): VersionDetails {
+ val response = queryResponse()
+ val latestVersion =
+ response.latestVersionCode
+ ?: throw InvalidVersionException("Version code was not found or null in response")
+ return VersionDetails(
+ releaseNotes = response.releaseNotes,
+ latestVersionUrl = response.latestVersionUrl,
+ latestVersion = latestVersion,
+ )
+ }
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLResponse.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLResponse.kt
index a19d8b1..9ed5213 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLResponse.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLResponse.kt
@@ -1,19 +1,20 @@
package com.sharkaboi.appupdatechecker.sources.xml
-import androidx.annotation.Keep
import org.simpleframework.xml.Element
import org.simpleframework.xml.Root
-@Keep
@Root(name = "version")
data class XMLResponse(
- @field:Element(name = "latestVersion")
- @param:Element(name = "latestVersion")
- val latestVersion: String,
+ @field:Element(name = "latestVersionName", required = false)
+ @param:Element(name = "latestVersionName", required = false)
+ val latestVersionName: String?,
+ @field:Element(name = "latestVersionCode", required = false)
+ @param:Element(name = "latestVersionCode", required = false)
+ val latestVersionCode: Long?,
@field:Element(name = "latestVersionUrl")
@param:Element(name = "latestVersionUrl")
val latestVersionUrl: String,
@field:Element(name = "releaseNotes")
@param:Element(name = "releaseNotes")
- val releaseNotes: String?
+ val releaseNotes: String?,
)
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLService.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLService.kt
index f7afc21..14fc2c8 100644
--- a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLService.kt
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLService.kt
@@ -1,12 +1,12 @@
package com.sharkaboi.appupdatechecker.sources.xml
-import kotlinx.coroutines.Deferred
+import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Url
interface XMLService {
@GET
- fun getXMLReleaseMetaDataAsync(
- @Url url: String
- ): Deferred
+ suspend fun getXMLReleaseMetaData(
+ @Url url: String,
+ ): Response
}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLSource.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLSource.kt
new file mode 100644
index 0000000..f5a9cd1
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/sources/xml/XMLSource.kt
@@ -0,0 +1,79 @@
+package com.sharkaboi.appupdatechecker.sources.xml
+
+import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerException
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidEndPointException
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.VersionDetails
+import com.sharkaboi.appupdatechecker.sources.AppUpdateCheckerSource
+import com.sharkaboi.appupdatechecker.versions.DefaultStringVersionComparator
+import com.sharkaboi.appupdatechecker.versions.DefaultVersionCodeComparator
+import com.sharkaboi.appupdatechecker.versions.VersionComparator
+import okhttp3.HttpUrl
+import retrofit2.Retrofit
+import retrofit2.converter.simplexml.SimpleXmlConverterFactory
+
+sealed class XMLSource : AppUpdateCheckerSource() {
+ abstract val xmlEndpoint: String
+
+ private val service =
+ Retrofit.Builder()
+ .baseUrl("https://google.com")
+ .addConverterFactory(SimpleXmlConverterFactory.create())
+ .build()
+ .create(XMLService::class.java)
+
+ protected suspend fun queryResponse(): XMLResponse {
+ if (HttpUrl.parse(xmlEndpoint) == null) {
+ throw InvalidEndPointException("Invalid endpoint $xmlEndpoint")
+ }
+
+ try {
+ val response = service.getXMLReleaseMetaData(url = xmlEndpoint)
+ return response.body()
+ ?: throw RemoteError(Throwable(response.errorBody()?.string()))
+ } catch (e: Exception) {
+ if (e is AppUpdateCheckerException) {
+ throw e
+ }
+ throw GenericError(e)
+ }
+ }
+}
+
+data class XMLVersionNameSource(
+ override val xmlEndpoint: String,
+ override val currentVersion: String,
+ override var versionComparator: VersionComparator = DefaultStringVersionComparator,
+) : XMLSource() {
+ override suspend fun queryVersionDetails(): VersionDetails {
+ val response = queryResponse()
+ val latestVersion =
+ response.latestVersionName
+ ?: throw InvalidVersionException("Version name was not found or null in response")
+ return VersionDetails(
+ releaseNotes = response.releaseNotes,
+ latestVersionUrl = response.latestVersionUrl,
+ latestVersion = latestVersion,
+ )
+ }
+}
+
+data class XMLVersionCodeSource(
+ override val xmlEndpoint: String,
+ override val currentVersion: Long,
+ override var versionComparator: VersionComparator = DefaultVersionCodeComparator,
+) : XMLSource() {
+ override suspend fun queryVersionDetails(): VersionDetails {
+ val response = queryResponse()
+ val latestVersion =
+ response.latestVersionCode
+ ?: throw InvalidVersionException("Version code was not found or null in response")
+ return VersionDetails(
+ releaseNotes = response.releaseNotes,
+ latestVersionUrl = response.latestVersionUrl,
+ latestVersion = latestVersion,
+ )
+ }
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/versions/DefaultStringVersionComparator.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/versions/DefaultStringVersionComparator.kt
new file mode 100644
index 0000000..a916a3f
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/versions/DefaultStringVersionComparator.kt
@@ -0,0 +1,45 @@
+package com.sharkaboi.appupdatechecker.versions
+
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+import kotlin.math.max
+
+object DefaultStringVersionComparator : VersionComparator {
+ override fun isNewerVersion(
+ currentVersion: String,
+ newVersion: String,
+ ): Boolean {
+ val currentVersionSubParts = parseVersion(currentVersion)
+ val newVersionSubParts = parseVersion(newVersion)
+ val length = max(currentVersionSubParts.size, newVersionSubParts.size)
+
+ val paddedCurrentVersionParts = padUntil(currentVersionSubParts, length)
+ val paddedNewVersionParts = padUntil(newVersionSubParts, length)
+
+ if (paddedCurrentVersionParts == paddedNewVersionParts) return false
+
+ for (i in 0 until length) {
+ val newVersionPart = paddedNewVersionParts[i]
+ val currentVersionPart = paddedCurrentVersionParts[i]
+ if (newVersionPart == currentVersionPart) continue
+
+ return newVersionPart > currentVersionPart
+ }
+
+ return false
+ }
+
+ private fun padUntil(
+ list: List,
+ length: Int,
+ ): List {
+ return list + List(length - list.size) { 0L }
+ }
+
+ private fun parseVersion(version: String): List {
+ return version
+ .trimStart('v', 'V', ' ')
+ .trimEnd(' ')
+ .split('.')
+ .map { it.toLongOrNull() ?: throw InvalidVersionException("Invalid version $version") }
+ }
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/versions/DefaultVersionCodeComparator.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/versions/DefaultVersionCodeComparator.kt
new file mode 100644
index 0000000..f7a21a6
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/versions/DefaultVersionCodeComparator.kt
@@ -0,0 +1,16 @@
+package com.sharkaboi.appupdatechecker.versions
+
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+
+object DefaultVersionCodeComparator : VersionComparator {
+ override fun isNewerVersion(
+ currentVersion: Long,
+ newVersion: Long,
+ ): Boolean {
+ if (currentVersion < 0) throw InvalidVersionException("Invalid current version $currentVersion")
+
+ if (newVersion < 0) throw InvalidVersionException("Invalid new version $newVersion")
+
+ return newVersion > currentVersion
+ }
+}
diff --git a/library/src/main/java/com/sharkaboi/appupdatechecker/versions/VersionComparator.kt b/library/src/main/java/com/sharkaboi/appupdatechecker/versions/VersionComparator.kt
new file mode 100644
index 0000000..616a274
--- /dev/null
+++ b/library/src/main/java/com/sharkaboi/appupdatechecker/versions/VersionComparator.kt
@@ -0,0 +1,11 @@
+package com.sharkaboi.appupdatechecker.versions
+
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+
+interface VersionComparator {
+ @Throws(InvalidVersionException::class)
+ fun isNewerVersion(
+ currentVersion: T,
+ newVersion: T,
+ ): Boolean
+}
diff --git a/library/src/test/java/com/github/sharkaboi/appupdatechecker/CustomSourceTest.kt b/library/src/test/java/com/github/sharkaboi/appupdatechecker/CustomSourceTest.kt
new file mode 100644
index 0000000..d386682
--- /dev/null
+++ b/library/src/test/java/com/github/sharkaboi/appupdatechecker/CustomSourceTest.kt
@@ -0,0 +1,83 @@
+package com.github.sharkaboi.appupdatechecker
+
+import com.sharkaboi.appupdatechecker.AppUpdateChecker
+import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerException
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidEndPointException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.models.VersionDetails
+import com.sharkaboi.appupdatechecker.sources.AppUpdateCheckerSource
+import com.sharkaboi.appupdatechecker.versions.DefaultStringVersionComparator
+import com.sharkaboi.appupdatechecker.versions.VersionComparator
+import kotlinx.coroutines.runBlocking
+import okhttp3.HttpUrl
+import org.junit.Assert
+import org.junit.Test
+import retrofit2.Response
+import retrofit2.Retrofit
+import retrofit2.converter.scalars.ScalarsConverterFactory
+import retrofit2.http.GET
+import retrofit2.http.Url
+
+class CustomSourceTest {
+ class CustomVersionSource(
+ override val currentVersion: String,
+ override var versionComparator: VersionComparator = DefaultStringVersionComparator,
+ ) : AppUpdateCheckerSource() {
+ private val customSource =
+ """https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/087bdb3151dc54eda2e7a98e88f1264184972313/custom-source/"""
+
+ interface CustomService {
+ @GET
+ suspend fun getReleaseVersion(
+ @Url url: String,
+ ): Response
+ }
+
+ private val service =
+ Retrofit.Builder()
+ .baseUrl("https://gist.githubusercontent.com")
+ .addConverterFactory(ScalarsConverterFactory.create())
+ .build()
+ .create(CustomService::class.java)
+
+ override suspend fun queryVersionDetails(): VersionDetails {
+ if (HttpUrl.parse(customSource) == null) {
+ throw InvalidEndPointException("Invalid endpoint $customSource")
+ }
+
+ try {
+ val response = service.getReleaseVersion(customSource)
+ val version =
+ response.body()
+ ?: throw RemoteError(Throwable(response.errorBody()?.string()))
+ return VersionDetails(
+ latestVersion = version,
+ latestVersionUrl = "https://mywebsite.com/download.apk",
+ releaseNotes = null,
+ )
+ } catch (e: Exception) {
+ if (e is AppUpdateCheckerException) {
+ throw e
+ }
+ throw GenericError(e)
+ }
+ }
+ }
+
+ @Test
+ fun `Setting custom version comparator returns proper update status`() =
+ runBlocking {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ CustomVersionSource(
+ currentVersion = "v1.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ Assert.assertTrue(result is UpdateResult.UpdateAvailable<*>)
+ }
+}
diff --git a/library/src/test/java/com/github/sharkaboi/appupdatechecker/FDroidTest.kt b/library/src/test/java/com/github/sharkaboi/appupdatechecker/FDroidTest.kt
new file mode 100644
index 0000000..55414c2
--- /dev/null
+++ b/library/src/test/java/com/github/sharkaboi/appupdatechecker/FDroidTest.kt
@@ -0,0 +1,131 @@
+package com.github.sharkaboi.appupdatechecker
+
+import com.sharkaboi.appupdatechecker.AppUpdateChecker
+import com.sharkaboi.appupdatechecker.models.InvalidPackageNameException
+import com.sharkaboi.appupdatechecker.models.PackageNotFoundException
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.sources.fdroid.FDroidVersionCodeSource
+import com.sharkaboi.appupdatechecker.sources.fdroid.FDroidVersionNameSource
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class FDroidTest {
+ private val packageName = "org.fdroid.fdroid"
+
+ @Test
+ fun `Checker on older installed version returns new version`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionNameSource(
+ packageName = packageName,
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.UpdateAvailable<*>)
+
+ val versionCodeChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionCodeSource(
+ packageName = packageName,
+ currentVersion = 0,
+ ),
+ )
+ val versionCodeResult = versionCodeChecker.checkUpdate()
+ println(versionCodeResult)
+ assertTrue(versionCodeResult is UpdateResult.UpdateAvailable<*>)
+ }
+
+ @Test
+ fun `Checker on newer installed version returns no update`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionNameSource(
+ packageName = packageName,
+ currentVersion = "v${Long.MAX_VALUE}.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.NoUpdate)
+
+ val versionCodeChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionCodeSource(
+ packageName = packageName,
+ currentVersion = Long.MAX_VALUE,
+ ),
+ )
+ val versionCodeResult = versionCodeChecker.checkUpdate()
+ println(versionCodeResult)
+ assertTrue(versionCodeResult is UpdateResult.NoUpdate)
+ }
+
+ @Test
+ fun `Checker on invalid fdroid package name returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionCodeSource(
+ packageName = "invalid.app.package.name.fdroid",
+ currentVersion = 0,
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is PackageNotFoundException)
+ }
+
+ @Test
+ fun `Checker on blank package name returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionCodeSource(
+ packageName = " ",
+ currentVersion = 0,
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidPackageNameException)
+ }
+
+ @Test
+ fun `Checker on repo name without dot returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionCodeSource(
+ packageName = "comsimplemobiletoolsgallerypro",
+ currentVersion = 0,
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidPackageNameException)
+ }
+}
diff --git a/library/src/test/java/com/github/sharkaboi/appupdatechecker/GithubTest.kt b/library/src/test/java/com/github/sharkaboi/appupdatechecker/GithubTest.kt
new file mode 100644
index 0000000..1307c0a
--- /dev/null
+++ b/library/src/test/java/com/github/sharkaboi/appupdatechecker/GithubTest.kt
@@ -0,0 +1,178 @@
+package com.github.sharkaboi.appupdatechecker
+
+import com.sharkaboi.appupdatechecker.AppUpdateChecker
+import com.sharkaboi.appupdatechecker.models.InvalidRepositoryNameException
+import com.sharkaboi.appupdatechecker.models.InvalidUserNameException
+import com.sharkaboi.appupdatechecker.models.PackageNotFoundException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.sources.github.GithubTagSource
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class GithubTest {
+ private val ownerUsername = "Sharkaboi"
+ private val repoName = "MediaHub"
+
+ @Test
+ fun `Checker on older installed version returns new version`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = ownerUsername,
+ repoName = repoName,
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.UpdateAvailable<*>)
+ }
+
+ @Test
+ fun `Checker on newer installed version returns no update`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = ownerUsername,
+ repoName = repoName,
+ currentVersion = "v${Long.MAX_VALUE}.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.NoUpdate)
+ }
+
+ @Test
+ fun `Checker on invalid github repo returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "adadadadadadadadaadada",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is PackageNotFoundException)
+ }
+
+ @Test
+ fun `Checker on invalid github user returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "adadadadadadadadaadada",
+ repoName = "MediaHub",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is PackageNotFoundException)
+ }
+
+ @Test
+ fun `Checker on github with no release returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "sharkaboi.github.io",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is PackageNotFoundException)
+ }
+
+ @Test
+ fun `Checker on blank username returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = " ",
+ repoName = "sharkaboi.github.io",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidUserNameException)
+ }
+
+ @Test
+ fun `Checker on empty repo name returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidRepositoryNameException)
+ }
+
+ @Test
+ fun `Checker with invalid auth token returns error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = ownerUsername,
+ repoName = repoName,
+ currentVersion = "v0.0.0",
+ bearerToken = "asdhaskdhakjhd",
+ ),
+ )
+ val result = versionNameChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is RemoteError)
+ }
+}
diff --git a/library/src/test/java/com/github/sharkaboi/appupdatechecker/JsonTest.kt b/library/src/test/java/com/github/sharkaboi/appupdatechecker/JsonTest.kt
new file mode 100644
index 0000000..e03e234
--- /dev/null
+++ b/library/src/test/java/com/github/sharkaboi/appupdatechecker/JsonTest.kt
@@ -0,0 +1,152 @@
+package com.github.sharkaboi.appupdatechecker
+
+import com.sharkaboi.appupdatechecker.AppUpdateChecker
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidEndPointException
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.sources.json.JsonVersionCodeSource
+import com.sharkaboi.appupdatechecker.sources.json.JsonVersionNameSource
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class JsonTest {
+ private val jsonEndpoint =
+ """https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/3b168fc906490cc7f3cf0fc2b843461abf52422e/test.json"""
+
+ @Test
+ fun `Checker on older installed version returns new version`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionNameSource(
+ jsonEndpoint = jsonEndpoint,
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.UpdateAvailable<*>)
+
+ val versionCodeChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionCodeSource(
+ jsonEndpoint = jsonEndpoint,
+ currentVersion = 0,
+ ),
+ )
+ val versionCodeResult = versionCodeChecker.checkUpdate()
+ println(versionCodeResult)
+ assertTrue(versionCodeResult is UpdateResult.UpdateAvailable<*>)
+ }
+
+ @Test
+ fun `Checker on newer installed version returns no update`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionNameSource(
+ jsonEndpoint = jsonEndpoint,
+ currentVersion = "v${Long.MAX_VALUE}.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.NoUpdate)
+
+ val versionCodeChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionCodeSource(
+ jsonEndpoint = jsonEndpoint,
+ currentVersion = Long.MAX_VALUE,
+ ),
+ )
+ val versionCodeResult = versionCodeChecker.checkUpdate()
+ println(versionCodeResult)
+ assertTrue(versionCodeResult is UpdateResult.NoUpdate)
+ }
+
+ @Test
+ fun `Checker on invalid json repo returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionNameSource(
+ jsonEndpoint = "https://google.com",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is GenericError)
+ }
+
+ @Test
+ fun `Checker on invalid json schema returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionNameSource(
+ jsonEndpoint = """https://gist.github.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/3b168fc906490cc7f3cf0fc2b843461abf52422e/invalid.json""",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is GenericError)
+ }
+
+ @Test
+ fun `Checker on invalid url returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionNameSource(
+ jsonEndpoint = "invalid url",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidEndPointException)
+ }
+
+ @Test
+ fun `Checker on blank endpoint returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ JsonVersionNameSource(
+ jsonEndpoint = " ",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidEndPointException)
+ }
+}
diff --git a/library/src/test/java/com/github/sharkaboi/appupdatechecker/VersionComparatorTest.kt b/library/src/test/java/com/github/sharkaboi/appupdatechecker/VersionComparatorTest.kt
new file mode 100644
index 0000000..b8f9fcc
--- /dev/null
+++ b/library/src/test/java/com/github/sharkaboi/appupdatechecker/VersionComparatorTest.kt
@@ -0,0 +1,147 @@
+package com.github.sharkaboi.appupdatechecker
+
+import com.sharkaboi.appupdatechecker.AppUpdateChecker
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.sources.fdroid.FDroidVersionCodeSource
+import com.sharkaboi.appupdatechecker.sources.github.GithubTagSource
+import com.sharkaboi.appupdatechecker.sources.json.JsonVersionNameSource
+import com.sharkaboi.appupdatechecker.versions.DefaultStringVersionComparator
+import com.sharkaboi.appupdatechecker.versions.VersionComparator
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class VersionComparatorTest {
+ @Test
+ fun `Checker on invalid current version name returns invalid version name error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "MediaHub",
+ currentVersion = "vdasd.ada.adas.jj",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidVersionException)
+ }
+
+ @Test
+ fun `Checker on invalid current version name ending returns invalid version name error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "MediaHub",
+ currentVersion = "1.",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidVersionException)
+ }
+
+ @Test
+ fun `Checker on invalid current version name start returns invalid version name error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "MediaHub",
+ currentVersion = ".1.2",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidVersionException)
+ }
+
+ @Test
+ fun `Checker on invalid current version name char returns invalid version name error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "MediaHub",
+ currentVersion = "g.1.2",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidVersionException)
+ }
+
+ @Test
+ fun `Checker on invalid current version code returns invalid version code error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ FDroidVersionCodeSource(
+ packageName = "org.fdroid.fdroid",
+ currentVersion = -5000,
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidVersionException)
+ }
+
+ @Test
+ fun `Setting custom version comparator returns proper update status`() =
+ runBlocking {
+ val source =
+ JsonVersionNameSource(
+ jsonEndpoint = """https://gist.github.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/1b99f59babe56a63aa95a7fb31b5d3682c4b18db/test-custom.json""",
+ currentVersion = "v1.0-alpha",
+ )
+
+ val customVersionComparator =
+ object : VersionComparator {
+ override fun isNewerVersion(
+ currentVersion: String,
+ newVersion: String,
+ ): Boolean {
+ return DefaultStringVersionComparator.isNewerVersion(
+ currentVersion.substringBefore('-'),
+ newVersion.substringBefore('-'),
+ )
+ }
+ }
+ source.setCustomVersionComparator(customVersionComparator)
+ val testChecker = AppUpdateChecker(source = source)
+ val result = testChecker.checkUpdate()
+ println(result)
+ assertTrue(result is UpdateResult.UpdateAvailable<*>)
+ }
+}
diff --git a/library/src/test/java/com/github/sharkaboi/appupdatechecker/XMLTest.kt b/library/src/test/java/com/github/sharkaboi/appupdatechecker/XMLTest.kt
new file mode 100644
index 0000000..71ec4e7
--- /dev/null
+++ b/library/src/test/java/com/github/sharkaboi/appupdatechecker/XMLTest.kt
@@ -0,0 +1,152 @@
+package com.github.sharkaboi.appupdatechecker
+
+import com.sharkaboi.appupdatechecker.AppUpdateChecker
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidEndPointException
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.sources.xml.XMLVersionCodeSource
+import com.sharkaboi.appupdatechecker.sources.xml.XMLVersionNameSource
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class XMLTest {
+ private val xmlEndpoint =
+ "https://gist.github.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/3b168fc906490cc7f3cf0fc2b843461abf52422e/test.xml"
+
+ @Test
+ fun `Checker on older installed version returns new version`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionNameSource(
+ xmlEndpoint = xmlEndpoint,
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.UpdateAvailable<*>)
+
+ val versionCodeChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionCodeSource(
+ xmlEndpoint = xmlEndpoint,
+ currentVersion = 0,
+ ),
+ )
+ val versionCodeResult = versionCodeChecker.checkUpdate()
+ println(versionCodeResult)
+ assertTrue(versionCodeResult is UpdateResult.UpdateAvailable<*>)
+ }
+
+ @Test
+ fun `Checker on newer installed version returns no update`() =
+ runBlocking {
+ val versionNameChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionNameSource(
+ xmlEndpoint = xmlEndpoint,
+ currentVersion = "v${Long.MAX_VALUE}.0.0",
+ ),
+ )
+ val versionNameResult = versionNameChecker.checkUpdate()
+ println(versionNameResult)
+ assertTrue(versionNameResult is UpdateResult.NoUpdate)
+
+ val versionCodeChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionCodeSource(
+ xmlEndpoint = xmlEndpoint,
+ currentVersion = Long.MAX_VALUE,
+ ),
+ )
+ val versionCodeResult = versionCodeChecker.checkUpdate()
+ println(versionCodeResult)
+ assertTrue(versionCodeResult is UpdateResult.NoUpdate)
+ }
+
+ @Test
+ fun `Checker on invalid xml repo returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionNameSource(
+ xmlEndpoint = "https://google.com",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is GenericError)
+ }
+
+ @Test
+ fun `Checker on invalid xml schema returns invalid error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionNameSource(
+ xmlEndpoint = """https://gist.github.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/3b168fc906490cc7f3cf0fc2b843461abf52422e/invalid.xml""",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is GenericError)
+ }
+
+ @Test
+ fun `Checker on invalid url returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionNameSource(
+ xmlEndpoint = "invalid url",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidEndPointException)
+ }
+
+ @Test
+ fun `Checker on blank endpoint returns malformed error`() =
+ runBlocking {
+ val exception =
+ runCatching {
+ val testChecker =
+ AppUpdateChecker(
+ source =
+ XMLVersionNameSource(
+ xmlEndpoint = " ",
+ currentVersion = "v0.0.0",
+ ),
+ )
+ val result = testChecker.checkUpdate()
+ println(result)
+ }.exceptionOrNull()
+ println(exception)
+ assertTrue(exception is InvalidEndPointException)
+ }
+}
diff --git a/library/src/test/java/com/sharkaboi/appupdatechecker/AppUpdateCheckerTest.kt b/library/src/test/java/com/sharkaboi/appupdatechecker/AppUpdateCheckerTest.kt
deleted file mode 100644
index b12870b..0000000
--- a/library/src/test/java/com/sharkaboi/appupdatechecker/AppUpdateCheckerTest.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.sharkaboi.appupdatechecker
-
-import android.content.Context
-import com.sharkaboi.appupdatechecker.extensions.isInternetConnected
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import kotlinx.coroutines.runBlocking
-import org.junit.Before
-import org.junit.Test
-
-class AppUpdateCheckerTest {
- private lateinit var context: Context
-
- @Before
- fun init() {
- context = mockk(relaxed = true)
- mockkStatic("com.sharkaboi.appupdatechecker.extensions.ContextExtensionsKt")
- }
-
- @Test
- fun `Checker on invalid current version tag returns generic error`() = runBlocking {
- every { context.isInternetConnected } returns true
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = "MediaHub"
- ),
- currentVersionTag = "vdasd.ada.adas.jj"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.GenericError)
- }
-
- @Test
- fun `Checker on no internet returns network error`() = runBlocking {
- every { context.isInternetConnected } returns false
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = "MediaHub"
- ),
- currentVersionTag = "v1.1"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.NoNetworkFound)
- }
-}
diff --git a/library/src/test/java/com/sharkaboi/appupdatechecker/FdroidTest.kt b/library/src/test/java/com/sharkaboi/appupdatechecker/FdroidTest.kt
deleted file mode 100644
index 42336d5..0000000
--- a/library/src/test/java/com/sharkaboi/appupdatechecker/FdroidTest.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.sharkaboi.appupdatechecker
-
-import android.content.Context
-import com.sharkaboi.appupdatechecker.extensions.isInternetConnected
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import kotlinx.coroutines.runBlocking
-import org.junit.Before
-import org.junit.Test
-
-class FdroidTest {
- private lateinit var context: Context
-
- @Before
- fun init() {
- context = mockk(relaxed = true)
- mockkStatic("com.sharkaboi.appupdatechecker.extensions.ContextExtensionsKt")
- every { context.isInternetConnected } returns true
- every { context.packageName } returns "com.simplemobiletools.gallery.pro"
- }
-
- @Test
- fun `Checker on older installed version returns new version`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.FDroidSource(),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.UpdateAvailable)
- }
-
- @Test
- fun `Checker on newer installed version returns no update`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.FDroidSource(),
- currentVersionTag = "v${Int.MAX_VALUE}.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.LatestVersionInstalled)
- }
-
- @Test
- fun `Checker on invalid fdroid package name returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.FDroidSource(
- packageName = "invalid.app.package.name.fdroid",
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.FDroidInvalid)
- }
-
- @Test
- fun `Checker on blank package name returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.FDroidSource(
- packageName = " "
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.FDroidMalformed)
- }
-
- @Test
- fun `Checker on repo name without dot returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.FDroidSource(
- packageName = "comsimplemobiletoolsgallerypro"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.FDroidMalformed)
- }
-}
diff --git a/library/src/test/java/com/sharkaboi/appupdatechecker/GithubTest.kt b/library/src/test/java/com/sharkaboi/appupdatechecker/GithubTest.kt
deleted file mode 100644
index e2db4f6..0000000
--- a/library/src/test/java/com/sharkaboi/appupdatechecker/GithubTest.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-package com.sharkaboi.appupdatechecker
-
-import android.content.Context
-import com.sharkaboi.appupdatechecker.extensions.isInternetConnected
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import kotlinx.coroutines.runBlocking
-import org.junit.Before
-import org.junit.Test
-
-class GithubTest {
- private lateinit var context: Context
-
- @Before
- fun init() {
- context = mockk(relaxed = true)
- mockkStatic("com.sharkaboi.appupdatechecker.extensions.ContextExtensionsKt")
- every { context.isInternetConnected } returns true
- }
-
- @Test
- fun `Checker on older installed version returns new version`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = "MediaHub"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.UpdateAvailable)
- }
-
- @Test
- fun `Checker on newer installed version returns no update`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = "MediaHub"
- ),
- currentVersionTag = "v${Int.MAX_VALUE}.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.LatestVersionInstalled)
- }
-
- @Test
- fun `Checker on invalid github repo returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = "adadadadadadadadaadada"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.GithubInvalid)
- }
-
- @Test
- fun `Checker on invalid github user returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "adadadadadadadadaadada",
- repoName = "MediaHub"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.GithubInvalid)
- }
-
- @Test
- fun `Checker on github with no release returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = "sharkaboi.github.io"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.GithubInvalid)
- }
-
- @Test
- fun `Checker on blank username returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = " ",
- repoName = "sharkaboi.github.io"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.GithubMalformed)
- }
-
- @Test
- fun `Checker on empty repo name returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.GithubSource(
- ownerUsername = "Sharkaboi",
- repoName = ""
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.GithubMalformed)
- }
-}
diff --git a/library/src/test/java/com/sharkaboi/appupdatechecker/JsonTest.kt b/library/src/test/java/com/sharkaboi/appupdatechecker/JsonTest.kt
deleted file mode 100644
index 8f14e5f..0000000
--- a/library/src/test/java/com/sharkaboi/appupdatechecker/JsonTest.kt
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.sharkaboi.appupdatechecker
-
-import android.content.Context
-import com.sharkaboi.appupdatechecker.extensions.isInternetConnected
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import kotlinx.coroutines.runBlocking
-import org.junit.Before
-import org.junit.Test
-
-class JsonTest {
- private lateinit var context: Context
-
- @Before
- fun init() {
- context = mockk(relaxed = true)
- mockkStatic("com.sharkaboi.appupdatechecker.extensions.ContextExtensionsKt")
- every { context.isInternetConnected } returns true
- }
-
- @Test
- fun `Checker on older installed version returns new version`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.JsonSource(
- jsonEndpoint = "https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/e0efd6ebdaf88615945fc8c7727a7fc620ad61bf/test.json"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.UpdateAvailable)
- }
-
- @Test
- fun `Checker on newer installed version returns no update`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.JsonSource(
- jsonEndpoint = "https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/e0efd6ebdaf88615945fc8c7727a7fc620ad61bf/test.json"
- ),
- currentVersionTag = "v${Int.MAX_VALUE}.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.LatestVersionInstalled)
- }
-
- @Test
- fun `Checker on invalid json repo returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.JsonSource(
- jsonEndpoint = "https://google.com"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.JSONInvalid)
- }
-
- @Test
- fun `Checker on invalid json schema returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.JsonSource(
- jsonEndpoint = "https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/a3998cc1728cd42ba02b5b9789f505de42090c84/invalid.json"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.JSONInvalid)
- }
-
- @Test
- fun `Checker on invalid url returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.JsonSource(
- jsonEndpoint = "invalid url"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.JSONMalformed)
- }
-
- @Test
- fun `Checker on blank endpoint returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.JsonSource(
- jsonEndpoint = " "
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.JSONMalformed)
- }
-}
diff --git a/library/src/test/java/com/sharkaboi/appupdatechecker/XMLTest.kt b/library/src/test/java/com/sharkaboi/appupdatechecker/XMLTest.kt
deleted file mode 100644
index 76e8b9a..0000000
--- a/library/src/test/java/com/sharkaboi/appupdatechecker/XMLTest.kt
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.sharkaboi.appupdatechecker
-
-import android.content.Context
-import com.sharkaboi.appupdatechecker.extensions.isInternetConnected
-import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerSource
-import com.sharkaboi.appupdatechecker.models.UpdateState
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import kotlinx.coroutines.runBlocking
-import org.junit.Before
-import org.junit.Test
-
-class XMLTest {
- private lateinit var context: Context
-
- @Before
- fun init() {
- context = mockk(relaxed = true)
- mockkStatic("com.sharkaboi.appupdatechecker.extensions.ContextExtensionsKt")
- every { context.isInternetConnected } returns true
- }
-
- @Test
- fun `Checker on older installed version returns new version`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.XMLSource(
- xmlEndpoint = "https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/132296a0e85d254cd982959bc8f198ca61c213b7/test.xml"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.UpdateAvailable)
- }
-
- @Test
- fun `Checker on newer installed version returns no update`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.XMLSource(
- xmlEndpoint = "https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/132296a0e85d254cd982959bc8f198ca61c213b7/test.xml"
- ),
- currentVersionTag = "v${Int.MAX_VALUE}.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.LatestVersionInstalled)
- }
-
- @Test
- fun `Checker on invalid xml repo returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.XMLSource(
- xmlEndpoint = "https://google.com"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.XMLInvalid)
- }
-
- @Test
- fun `Checker on invalid xml schema returns invalid error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.XMLSource(
- xmlEndpoint = "https://gist.githubusercontent.com/Sharkaboi/66b45a22afde23a9b2781eeec6f10c56/raw/132296a0e85d254cd982959bc8f198ca61c213b7/invalid.xml"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.XMLInvalid)
- }
-
- @Test
- fun `Checker on invalid url returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.XMLSource(
- xmlEndpoint = "invalid url"
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.XMLMalformed)
- }
-
- @Test
- fun `Checker on blank endpoint returns malformed error`() = runBlocking {
- val testChecker = AppUpdateChecker(
- context,
- source = AppUpdateCheckerSource.XMLSource(
- xmlEndpoint = " "
- ),
- currentVersionTag = "v0.0.0"
- )
- val result = testChecker.checkUpdate()
- println(result)
- assert(result is UpdateState.XMLMalformed)
- }
-}
diff --git a/sample/.gitignore b/sample/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/sample/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/sample/build.gradle b/sample/build.gradle
new file mode 100644
index 0000000..566057f
--- /dev/null
+++ b/sample/build.gradle
@@ -0,0 +1,49 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'com.github.sharkaboi.appupdatechecker.sample'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.github.sharkaboi.appupdatechecker.sample"
+ minSdk 21
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.debug
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+ kotlinOptions {
+ jvmTarget = '17'
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+ implementation(project(":library"))
+
+ implementation 'androidx.core:core-ktx:1.12.0'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.11.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+}
\ No newline at end of file
diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/sample/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3503454
--- /dev/null
+++ b/sample/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/java/com/github/sharkaboi/appupdatechecker/sample/MainActivity.kt b/sample/src/main/java/com/github/sharkaboi/appupdatechecker/sample/MainActivity.kt
new file mode 100644
index 0000000..4fc99fa
--- /dev/null
+++ b/sample/src/main/java/com/github/sharkaboi/appupdatechecker/sample/MainActivity.kt
@@ -0,0 +1,92 @@
+package com.github.sharkaboi.appupdatechecker.sample
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
+import com.github.sharkaboi.appupdatechecker.sample.databinding.ActivityMainBinding
+import com.sharkaboi.appupdatechecker.AppUpdateChecker
+import com.sharkaboi.appupdatechecker.models.AppUpdateCheckerException
+import com.sharkaboi.appupdatechecker.models.GenericError
+import com.sharkaboi.appupdatechecker.models.InvalidEndPointException
+import com.sharkaboi.appupdatechecker.models.InvalidPackageNameException
+import com.sharkaboi.appupdatechecker.models.InvalidRepositoryNameException
+import com.sharkaboi.appupdatechecker.models.InvalidUserNameException
+import com.sharkaboi.appupdatechecker.models.InvalidVersionException
+import com.sharkaboi.appupdatechecker.models.PackageNotFoundException
+import com.sharkaboi.appupdatechecker.models.RemoteError
+import com.sharkaboi.appupdatechecker.models.UpdateResult
+import com.sharkaboi.appupdatechecker.sources.github.GithubTagSource
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityMainBinding
+ private val errorHandler =
+ CoroutineExceptionHandler { _, throwable ->
+ if (throwable is AppUpdateCheckerException) {
+ when (throwable) {
+ is GenericError -> {
+ // Handle explicitly if needed
+ }
+
+ is InvalidEndPointException -> {
+ // Handle explicitly if needed
+ }
+
+ is InvalidPackageNameException -> {
+ // Handle explicitly if needed
+ }
+
+ is InvalidRepositoryNameException -> {
+ // Handle explicitly if needed
+ }
+
+ is InvalidUserNameException -> {
+ // Handle explicitly if needed
+ }
+
+ is InvalidVersionException -> {
+ // Handle explicitly if needed
+ }
+
+ is PackageNotFoundException -> {
+ // Handle explicitly if needed
+ }
+
+ is RemoteError -> {
+ // Handle explicitly if needed
+ }
+ }
+ }
+
+ binding.tvUpdateDetails.text = "Error occurred when checking for updates:\n\n$throwable"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ // Ideally manage this using your DI implementation, creates a retrofit service underneath.
+ val updateChecker =
+ AppUpdateChecker(
+ source =
+ GithubTagSource(
+ ownerUsername = "Sharkaboi",
+ repoName = "AppUpdateChecker",
+ currentVersion = "v0.0.0",
+ ),
+ )
+
+ binding.tvUpdateDetails.text = "Checking for updates"
+
+ lifecycleScope.launch(errorHandler) {
+ // Automatically switches to IO thread behind the scenes.
+ binding.tvUpdateDetails.text =
+ when (val result = updateChecker.checkUpdate()) {
+ UpdateResult.NoUpdate -> "Checking for updates"
+ is UpdateResult.UpdateAvailable<*> -> "Update found : " + result.versionDetails.toString()
+ }
+ }
+ }
+}
diff --git a/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/drawable/ic_launcher_background.xml b/sample/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/sample/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..0688f95
--- /dev/null
+++ b/sample/src/main/res/layout/activity_main.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.webp b/sample/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.webp b/sample/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/sample/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/sample/src/main/res/values-night/themes.xml b/sample/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..a22d511
--- /dev/null
+++ b/sample/src/main/res/values-night/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c8524cd
--- /dev/null
+++ b/sample/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
new file mode 100644
index 0000000..82aa71e
--- /dev/null
+++ b/sample/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ sample
+
\ No newline at end of file
diff --git a/sample/src/main/res/values/themes.xml b/sample/src/main/res/values/themes.xml
new file mode 100644
index 0000000..a4d87e5
--- /dev/null
+++ b/sample/src/main/res/values/themes.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index d8f14a1..612d9e4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,2 @@
include ':library'
+include ':sample'