diff --git a/app/build.gradle b/app/build.gradle index b6eb470a..0fee4d11 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId 'org.radarcns.detail' minSdkVersion 21 targetSdkVersion 30 - versionCode 51 - versionName '1.0.12' + versionCode 52 + versionName '1.0.13' manifestPlaceholders = ['appAuthRedirectScheme': 'org.radarbase.passive.app'] multiDexEnabled true } @@ -104,6 +104,25 @@ dependencies { testImplementation 'junit:junit:4.13.2' testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.30' + + implementation("com.android.volley:volley:1.2.0") + + // Kotlin + coroutines + implementation("androidx.work:work-runtime-ktx:2.5.0") + + // optional - RxJava2 support + implementation("androidx.work:work-rxjava2:2.5.0") + + // optional - GCMNetworkManager support + implementation("androidx.work:work-gcm:2.5.0") + + // optional - Test helpers + androidTestImplementation("androidx.work:work-testing:2.5.0") + + // optional - Multiprocess support + implementation "androidx.work:work-multiprocess:2.5.0" + + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1" } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4c31ee8a..094e3ecb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,6 +17,8 @@ + + @@ -29,6 +31,17 @@ android:name=".RadarApplicationImpl" android:fullBackupContent="@xml/backup_descriptor" android:installLocation="internalOnly"> + + + + + @@ -88,9 +101,11 @@ android:parentActivityName=".MainActivityImpl"/> - + + diff --git a/app/src/main/res/drawable/ic_baseline_update_24.xml b/app/src/main/res/drawable/ic_baseline_update_24.xml new file mode 100644 index 00000000..3b923020 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_update_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 2ff624bc..efcc63eb 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -65,7 +65,7 @@ android:onClick="startReset" app:layout_constraintTop_toBottomOf="@id/enableDataHighPrioritySwitch" app:layout_constraintBottom_toBottomOf="@id/constraintLayout" - app:layout_constraintStart_toStartOf="@id/constraintLayout" /> + app:layout_constraintStart_toStartOf="@id/constraintLayout"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6dc7be46..7937656b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -121,4 +121,16 @@ com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior Please start RADAR pRMT RADAR pRMT has automatically been stopped. Please press this notification to start it again. + + + Updates + New version is available.\nClick to download. + Version: %1$s + A new version of %1$s App (%2$s) is available. + You installed the latest version of %1$s App. + No + Don\'t Show Again + Start Downloading + Do you want to install it? + Error in package name! diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 00000000..b2389ae9 --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/remote_config_defaults.xml b/app/src/main/res/xml/remote_config_defaults.xml index dc69a1e6..f701a7c5 100644 --- a/app/src/main/res/xml/remote_config_defaults.xml +++ b/app/src/main/res/xml/remote_config_defaults.xml @@ -86,5 +86,21 @@ privacy_policy http://info.thehyve.nl/radar-cns-privacy-policy + + update_check + true + + + update_check_notification + true + + + update_check_frequency + day + + + update_releases_url + https://api.github.com/repos/RADAR-base/radar-prmt-android/releases + diff --git a/app/src/main/java/org/radarcns/detail/MainActivityImpl.kt b/app/src/playStore/java/org/radarcns/detail/MainActivityImpl.kt similarity index 100% rename from app/src/main/java/org/radarcns/detail/MainActivityImpl.kt rename to app/src/playStore/java/org/radarcns/detail/MainActivityImpl.kt diff --git a/app/src/main/res/layout/compact_overview.xml b/app/src/playStore/res/layout/compact_overview.xml similarity index 100% rename from app/src/main/res/layout/compact_overview.xml rename to app/src/playStore/res/layout/compact_overview.xml diff --git a/app/src/selfRelease/java/org/radarcns/detail/ComparePackageNames.kt b/app/src/selfRelease/java/org/radarcns/detail/ComparePackageNames.kt new file mode 100644 index 00000000..6c82e1b8 --- /dev/null +++ b/app/src/selfRelease/java/org/radarcns/detail/ComparePackageNames.kt @@ -0,0 +1,19 @@ +package org.radarcns.detail + +import android.content.Context +import android.content.pm.PackageManager.NameNotFoundException +import android.util.Log + +fun isPackageNameSame(context: Context, apkPath: String): Boolean { + return context.packageName == getUpdatePackageName(context, apkPath) +} + +fun getUpdatePackageName(context: Context, apkPath: String): String? { + try { + return context.packageManager.getPackageArchiveInfo(apkPath, 0)?.packageName + } catch (e: NameNotFoundException) { + Log.e("ComparePackageNames", "Cannot resolve package name for $apkPath", e) + e.printStackTrace() + } + return null +} \ No newline at end of file diff --git a/app/src/selfRelease/java/org/radarcns/detail/CompareVersions.kt b/app/src/selfRelease/java/org/radarcns/detail/CompareVersions.kt new file mode 100644 index 00000000..c4f4b1de --- /dev/null +++ b/app/src/selfRelease/java/org/radarcns/detail/CompareVersions.kt @@ -0,0 +1,50 @@ +package org.radarcns.detail + +import android.content.Context +import android.content.pm.PackageManager +import android.util.Log +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import org.json.JSONObject +import org.radarcns.detail.UpdateScheduledService.Companion.UPDATE_VERSION_NAME_KEY +import org.radarcns.detail.UpdateScheduledService.Companion.UPDATE_VERSION_URL_KEY + +fun getUpdatePackage(context: Context, response: String): JSONObject? { + val updatePackage = getUpdatePackageVersionAndUrl(response) + val currentPackageVersion = getInstalledPackageVersion(context) + + val pattern = Regex("\\d+(\\.\\d+)+") + + val rawCurrentPackageVersion = currentPackageVersion?.let { pattern.find(it)?.value } + val rawUpdatePackageVersion = updatePackage?.getString(UPDATE_VERSION_NAME_KEY)?.let { pattern.find(it)?.value } + + if (rawCurrentPackageVersion != rawUpdatePackageVersion) { + return updatePackage + } + return null +} + +fun getInstalledPackageVersion(context: Context): String? { + try { + return context.packageManager.getPackageInfo(context.packageName, 0).versionName + } catch (e: PackageManager.NameNotFoundException) { + Log.e("CompareVersions", "Cannot resolve version for " + context.packageName, e) + e.printStackTrace() + } + return null +} + +fun getUpdatePackageVersionAndUrl(response: String): JSONObject? { + val responseObject = Json.parseToJsonElement(response) + if (responseObject.jsonArray.size > 0) { + val latestRelease = responseObject.jsonArray[0] + val tagName = latestRelease.jsonObject["tag_name"]?.toString()?.replace("\"", "") + val browserDownloadUrl = latestRelease.jsonObject["assets"]?.jsonArray?.get(0)?.jsonObject?.getValue("browser_download_url").toString().replace("\"", "") + val updateApk = JSONObject() + updateApk.put(UPDATE_VERSION_URL_KEY, browserDownloadUrl) + updateApk.put(UPDATE_VERSION_NAME_KEY, tagName) + return updateApk + } + return null +} diff --git a/app/src/selfRelease/java/org/radarcns/detail/DownloadFileFromUrl.kt b/app/src/selfRelease/java/org/radarcns/detail/DownloadFileFromUrl.kt new file mode 100644 index 00000000..2eda7837 --- /dev/null +++ b/app/src/selfRelease/java/org/radarcns/detail/DownloadFileFromUrl.kt @@ -0,0 +1,100 @@ +package org.radarcns.detail + +import android.content.Context +import android.os.AsyncTask +import android.widget.ProgressBar +import java.io.* +import java.net.HttpURLConnection +import java.net.URL + +class DownloadFileFromUrl(context: Context, delegate: TaskDelegate) : + AsyncTask() { + + private val mDelegate: TaskDelegate = delegate + + private val mContext: Context = context + + var bar: ProgressBar? = null + + override fun doInBackground(vararg params: String?): String? { + var input: InputStream? = null + var output: OutputStream? = null + var connection: HttpURLConnection? = null + try { + val url = URL(params[0]) + connection = url.openConnection() as HttpURLConnection + connection.connect() + + // expect HTTP 200 OK, so we don't mistakenly save error report + // instead of the file + if (connection.responseCode !== HttpURLConnection.HTTP_OK) { + return "Server returned HTTP " + connection.responseCode + .toString() + " " + connection.responseMessage + } + + // this will be useful to display download percentage + // might be -1: server did not report the length + val fileLength: Int = connection.contentLength + + // download the file + input = connection.inputStream + + val outputFile = File(mContext.filesDir, DOWNLOADED_FILE) + if (outputFile.exists()) { + outputFile.delete() + } + + outputFile.setReadable(true, false) + output = FileOutputStream(outputFile) + + val data = ByteArray(1024) //4096 + var total: Long = 0 + var count: Int + while (input.read(data).also { count = it } != -1) { + // allow canceling with back button + if (isCancelled) { + input.close() + return null + } + total += count.toLong() + // publishing the progress.... + if (fileLength > 0) // only if total length is known + publishProgress((total * 100 / fileLength).toInt()) + output.write(data, 0, count) + } + } catch (e: Exception) { + return e.toString() + } finally { + try { + output?.close() + input?.close() + } catch (ignored: IOException) { + } + connection?.disconnect() + } + return null + } + + override fun onProgressUpdate(vararg values: Int?) { + super.onProgressUpdate(*values) + bar?.progress = values[0]!! + } + + override fun onPostExecute(s: String?) { + super.onPostExecute(s) + mDelegate.taskCompletionResult("Post Exec") + } + + fun setProgressBar(bar: ProgressBar?) { + this.bar = bar + } + + companion object { + const val DOWNLOADED_FILE = "app-release.apk" + } +} + +interface TaskDelegate { + fun taskCompletionResult(result: String?) +} + diff --git a/app/src/selfRelease/java/org/radarcns/detail/MainActivityImpl.kt b/app/src/selfRelease/java/org/radarcns/detail/MainActivityImpl.kt new file mode 100644 index 00000000..b51d75e1 --- /dev/null +++ b/app/src/selfRelease/java/org/radarcns/detail/MainActivityImpl.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2017 The Hyve + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.radarcns.detail + +import android.content.Intent +import android.os.Bundle +import android.view.View +import org.radarbase.android.MainActivity +import org.radarbase.android.MainActivityView +import org.radarbase.android.RadarApplication.Companion.radarApp + +class MainActivityImpl : MainActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + radarApp.notificationHandler.cancel(MainActivityBootStarter.BOOT_START_NOTIFICATION_ID) + super.onCreate(savedInstanceState) + } + + override fun createView(): MainActivityView { + return MainActivityViewImpl(this) + } + + fun logout(@Suppress("UNUSED_PARAMETER")view: View) { + logout(true) + } + + fun showInfo(@Suppress("UNUSED_PARAMETER")view: View) { + startActivity(Intent(this, InfoActivity::class.java)) + } + + fun showSettings(@Suppress("UNUSED_PARAMETER")view: View) { + startActivity(Intent(this, SettingsActivity::class.java)) + } + + fun showUpdates(@Suppress("UNUSED_PARAMETER")view: View) { + startActivity(Intent(this, UpdatesActivity::class.java)) + } +} diff --git a/app/src/selfRelease/java/org/radarcns/detail/OneTimeScheduleWorker.kt b/app/src/selfRelease/java/org/radarcns/detail/OneTimeScheduleWorker.kt new file mode 100644 index 00000000..0554a44f --- /dev/null +++ b/app/src/selfRelease/java/org/radarcns/detail/OneTimeScheduleWorker.kt @@ -0,0 +1,84 @@ +package org.radarcns.detail + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.work.Worker +import androidx.work.WorkerParameters +import org.radarcns.detail.UpdateScheduledService.Companion.UPDATE_VERSION_NAME_KEY +import org.radarcns.detail.UpdateScheduledService.Companion.UPDATE_VERSION_URL_KEY +import kotlin.random.Random + +class OneTimeScheduleWorker( + private val context: Context, + workerParams: WorkerParameters +) : Worker(context, workerParams) { + + override fun doWork(): Result { + val url = inputData.getString(UPDATE_VERSION_URL_KEY) + val versionName = inputData.getString(UPDATE_VERSION_NAME_KEY) + + val intent = Intent(context, UpdatesActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + putExtra(FROM_NOTIFICATION_KEY, true) + putExtra(UPDATE_VERSION_NAME_KEY, versionName) + putExtra(UPDATE_VERSION_URL_KEY, url) + } + + val uniqueInt = (System.currentTimeMillis() and 0xfffffff).toInt() + val pendingIntent = PendingIntent.getActivity( + context, + uniqueInt, + intent, + PendingIntent.FLAG_CANCEL_CURRENT + ) + + val title = context.getString(R.string.app_name) + val content = context.getString(R.string.update_notification_content) + + val builder = NotificationCompat.Builder(context, UPDATE_NOTIFICATION_ID) + .setSmallIcon(R.drawable.ic_baseline_update_24) + .setContentTitle(title) + .setContentText(content) + .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_MAX) + + createNotificationChannel() + + with(NotificationManagerCompat.from(context)) { + notify(Random.nextInt(), builder.build()) + } + + return Result.success() + } + + private fun createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val descriptionText = context.getString(R.string.update_notification_content) + val importance = NotificationManager.IMPORTANCE_HIGH + val channel = NotificationChannel( + UPDATE_NOTIFICATION_ID, + UPDATE_NOTIFICATION_CHANNEL_NAME, + importance + ).apply { + description = descriptionText + } + + val notificationManager: NotificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } + + companion object { + const val FROM_NOTIFICATION_KEY = "from_notification" + const val UPDATE_NOTIFICATION_ID = "com.radarcns.detail.update" + const val UPDATE_NOTIFICATION_CHANNEL_NAME = "update_notification" + } + +} \ No newline at end of file diff --git a/app/src/selfRelease/java/org/radarcns/detail/RadarServiceImpl.kt b/app/src/selfRelease/java/org/radarcns/detail/RadarServiceImpl.kt index 3d3e66df..f9a5685e 100644 --- a/app/src/selfRelease/java/org/radarcns/detail/RadarServiceImpl.kt +++ b/app/src/selfRelease/java/org/radarcns/detail/RadarServiceImpl.kt @@ -18,8 +18,8 @@ package org.radarcns.detail import android.Manifest.permission.RECEIVE_BOOT_COMPLETED import android.Manifest.permission.SYSTEM_ALERT_WINDOW +import android.content.Intent import android.os.Build -import org.radarbase.android.RadarConfiguration import org.radarbase.android.RadarConfiguration.Companion.START_AT_BOOT import org.radarbase.android.RadarService import org.radarbase.android.config.SingleRadarConfiguration @@ -68,5 +68,6 @@ class RadarServiceImpl : RadarService() { override fun doConfigure(config: SingleRadarConfiguration) { super.doConfigure(config) configureRunAtBoot(config, MainActivityBootStarter::class.java) + startService(Intent(this, UpdateScheduledService::class.java)) } } diff --git a/app/src/selfRelease/java/org/radarcns/detail/UpdateScheduledService.kt b/app/src/selfRelease/java/org/radarcns/detail/UpdateScheduledService.kt new file mode 100644 index 00000000..77ac3672 --- /dev/null +++ b/app/src/selfRelease/java/org/radarcns/detail/UpdateScheduledService.kt @@ -0,0 +1,88 @@ +package org.radarcns.detail + +import android.content.Intent +import android.util.Log +import androidx.lifecycle.LifecycleService +import java.util.* +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import com.android.volley.Request +import com.android.volley.toolbox.StringRequest +import com.android.volley.toolbox.Volley +import java.util.concurrent.TimeUnit +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import org.json.JSONObject +import org.radarbase.android.RadarApplication.Companion.radarConfig +import org.radarcns.detail.UpdatesActivity.Companion.DAY +import org.radarcns.detail.UpdatesActivity.Companion.UPDATE_CHECK_PERIOD_KEY +import org.radarcns.detail.UpdatesActivity.Companion.UPDATE_RELEASES_URL_KEY + +class UpdateScheduledService: LifecycleService() { + private var timerStarted: Boolean = false + private var timer: Timer = Timer() + + private var updateNotificationConfig = true + private var updateCheckPeriod: Long = DAY + private var releasesUrl: String? = null + + override fun onRebind(intent: Intent?) { + super.onRebind(intent) + } + + override fun onCreate() { + super.onCreate() + + radarConfig.config.observe(this, { config -> + updateCheckPeriod = config.getLong(UPDATE_CHECK_PERIOD_KEY, DAY) + releasesUrl = config.getString(UPDATE_RELEASES_URL_KEY) + + if (timerStarted) { + timer.cancel() + timerStarted = false + } + timer = Timer() + timer.scheduleAtFixedRate(CheckUpdateTask(), updateCheckPeriod, updateCheckPeriod) + timerStarted = true + }) + } + + fun scheduleOneTimeNotification(initialDelay: Long, newUpdate: JSONObject) { + val data = Data.Builder() + data.putString(UPDATE_VERSION_URL_KEY, newUpdate.get(UPDATE_VERSION_URL_KEY) as String?) + data.putString(UPDATE_VERSION_NAME_KEY, newUpdate.get(UPDATE_VERSION_NAME_KEY) as String?) + val work = + OneTimeWorkRequestBuilder() + .setInputData(data.build()) + .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS) + .build() + + WorkManager.getInstance(this@UpdateScheduledService).enqueue(work) + } + + inner class CheckUpdateTask : TimerTask() { + override fun run() { + val queue = Volley.newRequestQueue(this@UpdateScheduledService) + val url = releasesUrl + val stringRequest = StringRequest( + Request.Method.GET, url, + { response -> + val updatePackage = getUpdatePackage(this@UpdateScheduledService, response) + if (updatePackage != null && updateNotificationConfig) { + scheduleOneTimeNotification(0, updatePackage) + } + }, + { + Log.v("ScheduleService", "Error") + }) + queue.add(stringRequest) + } + } + + companion object { + const val UPDATE_VERSION_URL_KEY = "updateVersionUrl" + const val UPDATE_VERSION_NAME_KEY = "updateVersionName" + const val LAST_UPDATE_CHECK_TIMESTAMP = "last_update_check_timestamp" + } +} \ No newline at end of file diff --git a/app/src/selfRelease/java/org/radarcns/detail/UpdatesActivity.kt b/app/src/selfRelease/java/org/radarcns/detail/UpdatesActivity.kt new file mode 100644 index 00000000..5bd32149 --- /dev/null +++ b/app/src/selfRelease/java/org/radarcns/detail/UpdatesActivity.kt @@ -0,0 +1,210 @@ +package org.radarcns.detail + +import android.app.NotificationManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.Button +import android.widget.LinearLayout +import android.widget.ProgressBar +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.FileProvider +import com.android.volley.Request +import com.android.volley.toolbox.StringRequest +import com.android.volley.toolbox.Volley +import org.radarbase.android.RadarApplication.Companion.radarConfig +import org.radarbase.android.RadarConfiguration +import org.radarcns.detail.DownloadFileFromUrl.Companion.DOWNLOADED_FILE +import org.radarcns.detail.OneTimeScheduleWorker.Companion.FROM_NOTIFICATION_KEY +import org.radarcns.detail.UpdateScheduledService.Companion.LAST_UPDATE_CHECK_TIMESTAMP +import org.radarcns.detail.UpdateScheduledService.Companion.UPDATE_VERSION_NAME_KEY +import org.radarcns.detail.UpdateScheduledService.Companion.UPDATE_VERSION_URL_KEY +import java.io.File + +class UpdatesActivity : AppCompatActivity(), TaskDelegate { + private lateinit var config: RadarConfiguration + private var releasesUrl: String? = null + + private lateinit var currentVersion: TextView + private lateinit var updateStatus: TextView + + private lateinit var updateLinearLayout: LinearLayout + private lateinit var startDownloadingButton: Button + private lateinit var cancelUpdateButton: Button + private lateinit var stopNotificationButton: TextView + + private lateinit var progressBarContainerLayout: ConstraintLayout + private lateinit var progressBar: ProgressBar + private lateinit var progressBarPercent: TextView + + private lateinit var newVersionApkUrl: String + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_updates) + setSupportActionBar(findViewById(R.id.toolbar).apply { + setTitle(R.string.updates) + }) + supportActionBar?.apply { + setDisplayShowHomeEnabled(true) + setDisplayHomeAsUpEnabled(true) + } + config = radarConfig + + config.config.observe(this, { config -> + val newReleaseUrl = config.getString(UPDATE_RELEASES_URL_KEY) + if( releasesUrl != newReleaseUrl ) { + releasesUrl = newReleaseUrl + checkForUpdates() + } + }) + + currentVersion = findViewById(R.id.current_version) + updateStatus = findViewById(R.id.update_status) + + updateLinearLayout = findViewById(R.id.update_linearLayout) + startDownloadingButton = findViewById(R.id.start_downloading_button) + cancelUpdateButton = findViewById(R.id.cancel_update_button) + stopNotificationButton = findViewById(R.id.stop_notification_button) + + progressBarContainerLayout = findViewById(R.id.progressbar_container) + progressBar = findViewById(R.id.progressBar) + progressBarPercent = findViewById(R.id.progressbar_percent) + + // TODO not all notifications should be canceled + val notificationMng = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationMng.cancelAll() + + setCurrentVersion() + + if (intent.extras?.getBoolean(FROM_NOTIFICATION_KEY, false) == true){ + updateLinearLayout.visibility = View.VISIBLE + updateStatus.text = getString( + R.string.new_version_available, + getString(R.string.app_name), + intent.extras?.getString( + "versionName", + "" + ) + ) + stopNotificationButton.visibility = View.VISIBLE + } + + startDownloadingButton.setOnClickListener { + startDownloading() + } + + cancelUpdateButton.setOnClickListener { + finish() + } + + stopNotificationButton.setOnClickListener { + config.put(UPDATE_CHECK_PERIOD_KEY, WEEK) + config.persistChanges() + finish() + } + } + + private fun setCurrentVersion() { + currentVersion.text = getString(R.string.currentVersion, getInstalledPackageVersion(this)) + } + + private fun checkForUpdates() { + val queue = Volley.newRequestQueue(this) + val url = releasesUrl + val stringRequest = StringRequest( + Request.Method.GET, url, + { response -> + config.put(LAST_UPDATE_CHECK_TIMESTAMP, System.currentTimeMillis()) + config.persistChanges() + + val updatePackage = getUpdatePackage(this, response) + if (updatePackage != null) { + val updateStatus: TextView = findViewById(R.id.update_status) + newVersionApkUrl = updatePackage.get(UPDATE_VERSION_URL_KEY) as String + updateStatus.text = getString( + R.string.new_version_available, + getString(R.string.app_name), + updatePackage.get(UPDATE_VERSION_NAME_KEY) + ) + updateLinearLayout.visibility = View.VISIBLE + } else { + val updateStatus: TextView = findViewById(R.id.update_status) + updateStatus.text = getString( + R.string.new_version_not_available, getString( + R.string.app_name + ) + ) + updateLinearLayout.visibility = View.GONE + } + }, + { + Log.v("ScheduleService", "Error") + }) + queue.add(stringRequest) + } + + private fun startDownloading() { + updateLinearLayout.visibility = View.GONE + progressBarContainerLayout.visibility = View.VISIBLE + progressBar.progress = 0 + val downloader = DownloadFileFromUrl(this, this) + downloader.setProgressBar(progressBar) + downloader.execute(newVersionApkUrl) + } + + private fun isInstallApkValid(): Boolean { + val fileName = DOWNLOADED_FILE + val fileLocation = File(filesDir, fileName) + + return isPackageNameSame(this, fileLocation.absolutePath) + } + + private fun install() { + val fileName = DOWNLOADED_FILE + val fileLocation = File(filesDir, fileName) + + val uri = FileProvider.getUriForFile( + this, + applicationContext.packageName.toString() + PROVIDER_PATH, + fileLocation + ) + val install = Intent(Intent.ACTION_VIEW) + install.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + install.setDataAndType( + uri, + MIME_TYPE + ) + startActivity(install) + } + + override fun taskCompletionResult(result: String?) { + if(isInstallApkValid()) { + install() + progressBarContainerLayout.visibility = View.GONE + updateLinearLayout.visibility = View.VISIBLE + }else{ + updateStatus.text = getString(R.string.error_in_package_name) + progressBarContainerLayout.visibility = View.GONE + } + } + + companion object { + private const val MIME_TYPE = "application/vnd.android.package-archive" + private const val PROVIDER_PATH = ".provider" + private const val HOUR = 60 * 60 * 1000L + const val DAY = 24 * HOUR + const val WEEK = 7 * DAY + const val UPDATE_RELEASES_URL_KEY = "update_releases_url" + const val UPDATE_CHECK_PERIOD_KEY = "update_check_period" + } +} + diff --git a/app/src/selfRelease/res/layout/activity_updates.xml b/app/src/selfRelease/res/layout/activity_updates.xml new file mode 100644 index 00000000..429d630b --- /dev/null +++ b/app/src/selfRelease/res/layout/activity_updates.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + +