Skip to content

Commit 0aafd61

Browse files
authored
Merge pull request #126 from hannesa2/BackgroundCheck
Background check
2 parents c6b0d87 + 35b1554 commit 0aafd61

File tree

7 files changed

+219
-12
lines changed

7 files changed

+219
-12
lines changed

githubAppUpdate/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ android {
1717
dependencies {
1818
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
1919
implementation 'com.google.code.gson:gson:2.10'
20+
implementation "androidx.work:work-runtime-ktx:2.7.1"
2021

2122
implementation "com.squareup.retrofit2:retrofit:2.9.0"
2223
implementation "androidx.appcompat:appcompat:1.5.1"

githubAppUpdate/src/main/java/info/hannes/github/AppUpdateHelper.kt

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.app.Activity
44
import android.app.AlertDialog
55
import android.content.Context
66
import android.content.Intent
7+
import android.content.pm.ApplicationInfo
78
import android.content.pm.PackageInfo
89
import android.content.pm.PackageManager
910
import android.net.Uri
@@ -13,13 +14,15 @@ import android.widget.Toast
1314
import androidx.appcompat.app.AppCompatActivity
1415
import androidx.lifecycle.coroutineScope
1516
import androidx.preference.PreferenceManager
17+
import androidx.work.*
1618
import info.hannes.github.model.Asset
1719
import info.hannes.github.model.GithubVersion
1820
import kotlinx.coroutines.Dispatchers
1921
import kotlinx.coroutines.launch
2022
import kotlinx.coroutines.withContext
2123
import okhttp3.logging.HttpLoggingInterceptor
2224
import retrofit2.Response
25+
import java.util.concurrent.TimeUnit
2326

2427

2528
object AppUpdateHelper {
@@ -39,7 +42,14 @@ object AppUpdateHelper {
3942
}
4043
}
4144

42-
fun checkForNewVersion(
45+
// silently in background
46+
fun checkForNewVersion(activity: AppCompatActivity, gitRepoUrl: String, repeatTime : Long = 6, timeUnit: TimeUnit = TimeUnit.HOURS) {
47+
val currentVersionName = activity.getVersionName()
48+
DownloadWorker.run(activity, currentVersionName, gitRepoUrl, repeatTime, timeUnit)
49+
}
50+
51+
// with user feedback
52+
fun checkWithDialog(
4353
activity: AppCompatActivity,
4454
gitRepoUrl: String,
4555
callback: ((String) -> Unit)? = null,
@@ -74,6 +84,34 @@ object AppUpdateHelper {
7484
}
7585
}
7686

87+
internal fun checkForNewVersionSilent(
88+
appContext: Context,
89+
currentVersionName: String,
90+
gitRepoUrl: String
91+
){
92+
try {
93+
val versionList = requestVersionsSync(gitRepoUrl)
94+
95+
versionList.body()?.firstOrNull()?.let { release ->
96+
val assetApk = release.assets.find { it.name.endsWith("release.apk") }
97+
98+
Log.d("AppUpdateHelper", release.tagName + " > " + currentVersionName + " " + (release.tagName > currentVersionName))
99+
val text = "You use version $currentVersionName\n" +
100+
"and there is a new version ${release.tagName}\n"
101+
if (release.tagName > currentVersionName) {
102+
Notify.notification(appContext, text, "New version for '${getAppName(appContext)}'", assetApk, release)
103+
}
104+
}
105+
} catch (e: Exception) {
106+
Log.e("AppUpdateHelper", "git check deliver: ${e.message}")
107+
}
108+
}
109+
110+
private fun requestVersionsSync(gitRepoUrl: String): Response<MutableList<GithubVersion>> {
111+
val client = GithubClient(HttpLoggingInterceptor.Level.BODY)
112+
return client.github.getGithubVersions(gitRepoUrl.user(), gitRepoUrl.repo()).execute()
113+
}
114+
77115
private suspend fun requestVersions(gitRepoUrl: String): Response<MutableList<GithubVersion>> {
78116
val versionList = withContext(Dispatchers.Default) {
79117
val client = GithubClient(HttpLoggingInterceptor.Level.BODY)
@@ -88,9 +126,10 @@ object AppUpdateHelper {
88126
release: GithubVersion,
89127
assetApk: Asset?
90128
): AlertDialog? {
129+
91130
@Suppress("DEPRECATION")
92131
val dialog = AlertDialog.Builder(activity)
93-
.setTitle("New Version on Github")
132+
.setTitle("New version for ${getAppName(activity)}")
94133
.setMessage(
95134
"You use version \n$currentVersionName\n" +
96135
"and there is a new version \n${release.tagName}\n" +
@@ -119,4 +158,11 @@ object AppUpdateHelper {
119158
}
120159
return dialog.show()
121160
}
161+
162+
private fun getAppName(context: Context): CharSequence {
163+
val pm = context.applicationContext.packageManager
164+
val appInfo: ApplicationInfo = pm.getApplicationInfo(context.applicationContext.packageName, 0)
165+
return context.applicationContext.packageManager.getApplicationLabel(appInfo)
166+
}
167+
122168
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package info.hannes.github
2+
3+
import android.content.Context
4+
import androidx.work.*
5+
import com.google.common.util.concurrent.ListenableFuture
6+
import kotlinx.coroutines.async
7+
import kotlinx.coroutines.coroutineScope
8+
import java.util.concurrent.TimeUnit
9+
10+
class DownloadWorker(private val appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) {
11+
12+
override suspend fun doWork(): Result = coroutineScope {
13+
// Get the input
14+
val currentVersion = inputData.getString(CURRENT_VERSION)!!
15+
val repoUrl = inputData.getString(REPO_URL)!!
16+
17+
val d = async {
18+
check(currentVersion, repoUrl)
19+
"done"
20+
}
21+
val value = d.await()
22+
23+
// output param is just a test
24+
val outputData = workDataOf(Pair("state", value))
25+
Result.success(outputData)
26+
}
27+
28+
private fun check(currentVersion: String, repoUrl: String) {
29+
AppUpdateHelper.checkForNewVersionSilent(appContext, currentVersion, repoUrl)
30+
}
31+
32+
companion object {
33+
fun run(context: Context, currentVersionName: String, repoUrl: String, repeatTime : Long, timeUnit: TimeUnit): ListenableFuture<MutableList<WorkInfo>> {
34+
val data = workDataOf(
35+
Pair(CURRENT_VERSION, currentVersionName),
36+
Pair(REPO_URL, repoUrl)
37+
)
38+
39+
val constraints = Constraints.Builder()
40+
.setRequiresBatteryNotLow(true)
41+
.setRequiredNetworkType(NetworkType.CONNECTED)
42+
.setRequiresStorageNotLow(true)
43+
.build()
44+
45+
val periodicWorkRequest = PeriodicWorkRequest
46+
.Builder(DownloadWorker::class.java, repeatTime, timeUnit)
47+
.setConstraints(constraints)
48+
.setInputData(data)
49+
.build()
50+
51+
WorkManager.getInstance(context)
52+
.cancelUniqueWork(uniqueWorkName)
53+
WorkManager.getInstance(context)
54+
.enqueueUniquePeriodicWork(uniqueWorkName, ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest)
55+
56+
return WorkManager.getInstance(context).getWorkInfosForUniqueWork(uniqueWorkName)
57+
}
58+
59+
const val CURRENT_VERSION = "CURRENT_VERSION"
60+
const val REPO_URL = "REPO_URL"
61+
private const val uniqueWorkName = "PWD"
62+
}
63+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package info.hannes.github
2+
3+
import android.app.NotificationChannel
4+
import android.app.NotificationManager
5+
import android.app.PendingIntent
6+
import android.content.Context
7+
import android.content.Intent
8+
import android.graphics.Color
9+
import android.net.Uri
10+
import android.os.Build
11+
import androidx.core.app.NotificationCompat
12+
import info.hannes.github.model.Asset
13+
import info.hannes.github.model.GithubVersion
14+
15+
internal object Notify {
16+
17+
private var MessageID = 120
18+
private const val channelId = "chn-01"
19+
private const val channelFireBaseMsg = "Channel appUpdate"
20+
21+
private val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
22+
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
23+
} else {
24+
PendingIntent.FLAG_UPDATE_CURRENT
25+
}
26+
27+
fun notification(context: Context, messageString: String, notificationTitle: String, assetApk: Asset?, release: GithubVersion) {
28+
29+
//Get the notification manage which we will use to display the notification
30+
val ns = Context.NOTIFICATION_SERVICE
31+
val notificationManager = context.getSystemService(ns) as NotificationManager
32+
33+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
34+
val notificationChannel = NotificationChannel(channelId, channelFireBaseMsg, NotificationManager.IMPORTANCE_LOW)
35+
notificationChannel.enableLights(true)
36+
notificationChannel.lightColor = Color.RED
37+
notificationChannel.enableVibration(true)
38+
notificationChannel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
39+
40+
notificationManager.createNotificationChannel(notificationChannel)
41+
}
42+
43+
//get the notification title from the application's strings.xml file
44+
val contentTitle: CharSequence = notificationTitle
45+
46+
//the message that will be displayed as the ticker
47+
val ticker = "$contentTitle $messageString"
48+
49+
val showUrl = Uri.parse(release.htmlUrl)
50+
val showPendingIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, showUrl), pendingIntentFlags)
51+
52+
//build the notification
53+
val notificationCompat = NotificationCompat.Builder(context, channelId)
54+
.setAutoCancel(true)
55+
.setContentTitle(contentTitle)
56+
.setContentIntent(showPendingIntent)
57+
.setStyle(NotificationCompat.BigTextStyle().bigText(messageString))
58+
.setContentText(messageString)
59+
.setTicker(ticker)
60+
.setWhen(System.currentTimeMillis())
61+
.setSmallIcon(R.drawable.githib_logo)
62+
.addAction(R.drawable.githib_logo, context.getString(R.string.showRelease), showPendingIntent)
63+
64+
assetApk?.let {
65+
val uriUrl = Uri.parse(it.browserDownloadUrl)
66+
val directPendingIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, uriUrl), pendingIntentFlags)
67+
notificationCompat.addAction(R.drawable.githib_logo, context.getString(R.string.directDownload), directPendingIntent)
68+
}
69+
70+
val notification = notificationCompat.build()
71+
72+
notificationManager.notify(MessageID, notification)
73+
}
74+
75+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<vector android:alpha="0.9" android:height="64dp"
2+
android:viewportHeight="1000" android:viewportWidth="1000"
3+
android:width="64dp" xmlns:android="http://schemas.android.com/apk/res/android">
4+
<path android:fillColor="#FF000000" android:pathData="M500,22.1c-270.6,0 -490,219.4 -490,490C10,728.6 150.4,912.2 345.1,977c24.5,4.5 33.4,-10.6 33.4,-23.6c0,-11.7 -0.4,-50.3 -0.7,-91.2c-136.3,29.6 -165.1,-57.8 -165.1,-57.8c-22.3,-56.6 -54.4,-71.7 -54.4,-71.7c-44.5,-30.4 3.4,-29.8 3.4,-29.8c49.2,3.4 75.1,50.5 75.1,50.5c43.7,74.9 114.7,53.2 142.6,40.7c4.5,-31.6 17.1,-53.2 31.1,-65.4c-108.8,-12.4 -223.2,-54.4 -223.2,-242.2c0,-53.5 19.1,-97.2 50.4,-131.5c-5,-12.4 -21.9,-62.3 4.8,-129.7c0,0 41.1,-13.2 134.8,50.2c39.1,-10.9 81,-16.3 122.6,-16.5c41.6,0.2 83.6,5.6 122.7,16.5c93.5,-63.5 134.6,-50.2 134.6,-50.2c26.8,67.5 9.9,117.3 4.9,129.7c31.4,34.3 50.4,78 50.4,131.5c0,188.2 -114.6,229.7 -223.8,241.8c17.6,15.2 33.3,45 33.3,90.7c0,65.5 -0.7,118.3 -0.7,134.5c0,13 8.9,28.3 33.7,23.5C849.7,912.1 990,728.5 990,512.1C990,241.5 770.6,22.1 500,22.1z"/>
5+
</vector>

sample/src/main/java/info/hannes/github/sample/MainActivity.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,17 @@ class MainActivity : AppCompatActivity() {
2424

2525
AppUpdateHelper.checkForNewVersion(
2626
this,
27-
BuildConfig.GIT_REPOSITORY,
28-
{ msg -> Log.d("result", msg) },
29-
force = true // just to enable debugging, without you can only debug once a day
27+
BuildConfig.GIT_REPOSITORY
3028
)
29+
30+
binding.button.setOnClickListener {
31+
AppUpdateHelper.checkWithDialog(
32+
this,
33+
BuildConfig.GIT_REPOSITORY,
34+
{ msg -> Log.d("result", msg) },
35+
force = true // just to enable debugging, without you can only debug once a day
36+
)
37+
}
3138
}
3239

3340
}

sample/src/main/res/layout/activity_main.xml

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
2+
<androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/main_content"
3+
xmlns:android="http://schemas.android.com/apk/res/android"
34
xmlns:app="http://schemas.android.com/apk/res-auto"
4-
android:id="@+id/main_content"
5+
xmlns:tools="http://schemas.android.com/tools"
56
android:layout_width="match_parent"
67
android:layout_height="match_parent">
78

9+
<FrameLayout
10+
android:id="@+id/fragment_main"
11+
android:layout_width="match_parent"
12+
android:layout_height="match_parent">
13+
14+
<Button
15+
android:id="@+id/button"
16+
android:layout_gravity="center|center"
17+
android:layout_width="wrap_content"
18+
android:layout_height="wrap_content"
19+
android:text="check"
20+
tools:ignore="HardcodedText" />
21+
</FrameLayout>
22+
823
<com.google.android.material.appbar.AppBarLayout
924
android:id="@+id/appbar"
1025
android:layout_width="match_parent"
@@ -20,9 +35,4 @@
2035

2136
</com.google.android.material.appbar.AppBarLayout>
2237

23-
<FrameLayout
24-
android:id="@+id/fragment_main"
25-
android:layout_width="match_parent"
26-
android:layout_height="match_parent" />
27-
2838
</androidx.coordinatorlayout.widget.CoordinatorLayout>

0 commit comments

Comments
 (0)