diff --git a/app/build.gradle b/app/build.gradle index c81a3de99..080aff83d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,15 @@ android { } } + flavorDimensions "releaseChannel" + productFlavors { + play { + dimension "releaseChannel" + } + fdroid { + dimension "releaseChannel" + } + } kotlinOptions { jvmTarget = '1.8' } @@ -76,10 +85,7 @@ dependencies { def room_version = "2.2.6" def paging_version = "2.1.2" - implementation fileTree(include: [ - 'guava-28.2-android.jar', - 'failureaccess-1.0.1.jar' // Dependency of Guava - ], dir: 'libs') + implementation "com.google.guava:guava:30.1-android" /*implementation "com.google.guava:guava:30.1-android" implementation 'com.google.guava:failureaccess:jar:1.0'*/ @@ -121,7 +127,7 @@ dependencies { implementation "androidx.viewpager2:viewpager2:1.1.0-alpha01" implementation 'com.squareup.okhttp3:okhttp:4.9.0' - implementation 'com.google.android.play:core:1.9.0'//for new version updater + playImplementation 'com.google.android.play:core:1.9.0'//for new version updater // For go-tun2socks implementation project(":tun2socks") diff --git a/app/libs/failureaccess-1.0.1.jar b/app/libs/failureaccess-1.0.1.jar deleted file mode 100644 index 9b56dc751..000000000 Binary files a/app/libs/failureaccess-1.0.1.jar and /dev/null differ diff --git a/app/libs/guava-28.2-android.jar b/app/libs/guava-28.2-android.jar deleted file mode 100644 index cb1e8e12c..000000000 Binary files a/app/libs/guava-28.2-android.jar and /dev/null differ diff --git a/app/src/main/java/com/celzero/bravedns/NonStoreAppUpdater.kt b/app/src/main/java/com/celzero/bravedns/NonStoreAppUpdater.kt new file mode 100644 index 000000000..f89eb0bc8 --- /dev/null +++ b/app/src/main/java/com/celzero/bravedns/NonStoreAppUpdater.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2021 RethinkDNS and its authors + * + * 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 + * + * https://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 com.celzero.bravedns + +import android.app.Activity +import android.util.Log +import com.celzero.bravedns.service.AppUpdater +import com.celzero.bravedns.service.PersistentState +import com.celzero.bravedns.util.Constants +import okhttp3.* +import org.json.JSONObject +import java.io.IOException + +class NonStoreAppUpdater(val baseURL:String, private val persistentState: PersistentState):AppUpdater { + private val LOG_TAG = "${Constants.LOG_TAG}/NonStoreAppUpdater" + + override fun checkForAppUpdate(isUserInitiated: Boolean, activity: Activity, listener: AppUpdater.InstallStateListener) { + Log.d(LOG_TAG, "Beginning update check.") + val url = baseURL + BuildConfig.VERSION_CODE + + val client = OkHttpClient() + val request = Request.Builder() + .url(url) + .build() + + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.d(LOG_TAG, "onFailure - ${call.isCanceled()}, ${call.isExecuted()}") + if(isUserInitiated) { + listener.onUpdateCheckFailed(AppUpdater.InstallSource.OTHER) + } + call.cancel() + } + + override fun onResponse(call: Call, response: Response) { + Log.d(LOG_TAG, "onResponse") + try { + val stringResponse = response.body!!.string() + //creating json object + val jsonObject = JSONObject(stringResponse) + val responseVersion = jsonObject.getInt("version") + val updateValue = jsonObject.getBoolean("update") + val latestVersion = jsonObject.getInt("latest") + persistentState.lastAppUpdateCheck = System.currentTimeMillis() // FIXME move to NTP + Log.i(Constants.LOG_TAG, "Server response for the new version download is true, version number- $latestVersion") + if (responseVersion == 1) { + if (updateValue) { + listener.onUpdateAvailable(AppUpdater.InstallSource.OTHER) + } else { + if(isUserInitiated) listener.onUpToDate(AppUpdater.InstallSource.OTHER) + } + } + response.close() + client.connectionPool.evictAll() + } catch (e: Exception) { + if (isUserInitiated) { + listener.onUpdateCheckFailed(AppUpdater.InstallSource.OTHER) + } + } + } + }) + } + + override fun completeUpdate() { + /* no-op */ + } + + override fun unregisterListener(listener: AppUpdater.InstallStateListener) { + /* no-op */ + } +} diff --git a/app/src/main/java/com/celzero/bravedns/ServiceModuleProvider.kt b/app/src/main/java/com/celzero/bravedns/ServiceModuleProvider.kt index 8d5878f9d..8f030457e 100644 --- a/app/src/main/java/com/celzero/bravedns/ServiceModuleProvider.kt +++ b/app/src/main/java/com/celzero/bravedns/ServiceModuleProvider.kt @@ -3,7 +3,9 @@ package com.celzero.bravedns import android.content.ContentResolver import com.celzero.bravedns.data.DataModule import com.celzero.bravedns.database.DatabaseModule +import com.celzero.bravedns.service.AppUpdater import com.celzero.bravedns.service.ServiceModule +import com.celzero.bravedns.util.Constants import com.celzero.bravedns.viewmodel.ViewModelModule import org.koin.android.ext.koin.androidContext import org.koin.core.module.Module @@ -27,6 +29,10 @@ import org.koin.dsl.module private val RootModule = module { single { androidContext().contentResolver } } +private val updaterModule = module { + single { NonStoreAppUpdater(Constants.APP_DOWNLOAD_AVAILABLE_CHECK, get())} + single { get() } +} val AppModules:List by lazy { mutableListOf().apply { @@ -36,5 +42,6 @@ val AppModules:List by lazy { addAll(ViewModelModule.modules) addAll(DataModule.modules) addAll(ServiceModule.modules) + add(updaterModule) } } diff --git a/app/src/main/java/com/celzero/bravedns/service/AppUpdater.kt b/app/src/main/java/com/celzero/bravedns/service/AppUpdater.kt new file mode 100644 index 000000000..ace0b54f0 --- /dev/null +++ b/app/src/main/java/com/celzero/bravedns/service/AppUpdater.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2021 RethinkDNS and its authors + * + * 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 + * + * https://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 com.celzero.bravedns.service + +import android.app.Activity + +interface AppUpdater { + fun checkForAppUpdate(isUserInitiated:Boolean, activity: Activity, listener:InstallStateListener) + fun completeUpdate() + fun unregisterListener(listener: InstallStateListener) + + interface InstallStateListener { + fun onStateUpdate(state:InstallState) + fun onUpdateAvailable(installSource:InstallSource) + fun onUpToDate(installSource:InstallSource) + fun onUpdateCheckFailed(installSource:InstallSource) + fun onUpdateQuotaExceeded(installSource:InstallSource) + } + data class InstallState(val status:InstallStatus) + enum class InstallStatus { + CANCELED, DOWNLOADED, DOWNLOADING, FAILED, INSTALLED, INSTALLING, PENDING, UNKNOWN + } + enum class InstallSource { + STORE, OTHER + } +} diff --git a/app/src/main/java/com/celzero/bravedns/ui/AboutFragment.kt b/app/src/main/java/com/celzero/bravedns/ui/AboutFragment.kt index 14884ed26..b2d845b85 100644 --- a/app/src/main/java/com/celzero/bravedns/ui/AboutFragment.kt +++ b/app/src/main/java/com/celzero/bravedns/ui/AboutFragment.kt @@ -27,6 +27,8 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import com.celzero.bravedns.R +import com.celzero.bravedns.service.AppUpdater +import org.koin.android.ext.android.inject class AboutFragment : Fragment(), View.OnClickListener { @@ -43,6 +45,8 @@ class AboutFragment : Fragment(), View.OnClickListener { private lateinit var appUpdateTxt : TextView private lateinit var whatsNewTxt : TextView + private val appUpdater by inject() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -126,7 +130,7 @@ class AboutFragment : Fragment(), View.OnClickListener { startActivity(intent) } view == appUpdateTxt ->{ - (requireContext() as HomeScreenActivity).checkForAppUpdate(true) + (requireContext() as HomeScreenActivity).checkForUpdate(true) } view == whatsNewTxt ->{ showNewFeaturesDialog() diff --git a/app/src/main/java/com/celzero/bravedns/ui/HomeScreenActivity.kt b/app/src/main/java/com/celzero/bravedns/ui/HomeScreenActivity.kt index a06a01869..0d6384aba 100644 --- a/app/src/main/java/com/celzero/bravedns/ui/HomeScreenActivity.kt +++ b/app/src/main/java/com/celzero/bravedns/ui/HomeScreenActivity.kt @@ -31,6 +31,7 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.lifecycle.MutableLiveData +import com.celzero.bravedns.NonStoreAppUpdater import com.celzero.bravedns.R import com.celzero.bravedns.automaton.FirewallRules import com.celzero.bravedns.data.AppMode @@ -38,6 +39,7 @@ import com.celzero.bravedns.database.AppInfo import com.celzero.bravedns.database.BlockedConnectionsRepository import com.celzero.bravedns.database.DoHEndpointRepository import com.celzero.bravedns.database.RefreshDatabase +import com.celzero.bravedns.service.AppUpdater import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.ui.HomeScreenActivity.GlobalVariable.DEBUG import com.celzero.bravedns.ui.HomeScreenActivity.GlobalVariable.appMode @@ -47,13 +49,6 @@ import com.celzero.bravedns.util.Constants.Companion.LOG_TAG import com.celzero.bravedns.util.HttpRequestHelper.Companion.checkStatus import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.snackbar.Snackbar -import com.google.android.play.core.appupdate.AppUpdateManager -import com.google.android.play.core.appupdate.AppUpdateManagerFactory -import com.google.android.play.core.install.InstallState -import com.google.android.play.core.install.InstallStateUpdatedListener -import com.google.android.play.core.install.model.AppUpdateType -import com.google.android.play.core.install.model.InstallStatus -import com.google.android.play.core.install.model.UpdateAvailability import com.google.common.collect.HashMultimap import okhttp3.* import org.json.JSONObject @@ -73,7 +68,6 @@ class HomeScreenActivity : AppCompatActivity() { lateinit var aboutFragment: AboutFragment lateinit var context: Context private val refreshDatabase by inject() - lateinit var appUpdateManager: AppUpdateManager lateinit var downloadManager : DownloadManager private var timeStamp = 0L //lateinit var appSample : AppInfo @@ -81,6 +75,7 @@ class HomeScreenActivity : AppCompatActivity() { private val doHEndpointRepository by inject() private val blockedConnectionsRepository by inject() private val persistentState by inject() + private val appUpdateManager by inject() /*TODO : This task need to be completed. Add all the appinfo in the global variable during appload @@ -148,8 +143,6 @@ class HomeScreenActivity : AppCompatActivity() { val navView: BottomNavigationView = findViewById(R.id.nav_view) - appUpdateManager = AppUpdateManagerFactory.create(this) - if (savedInstanceState == null) { supportFragmentManager.beginTransaction().replace(R.id.fragment_container, homeScreenFragment, homeScreenFragment.javaClass.simpleName).commit() @@ -243,7 +236,7 @@ class HomeScreenActivity : AppCompatActivity() { if(day == Calendar.FRIDAY || day == Calendar.SATURDAY) { if (numOfDays > 0) { Log.i(LOG_TAG, "App update check initiated, number of days: $numOfDays") - checkForAppUpdate(false) + checkForUpdate() checkForBlockListUpdate() } else { Log.i(LOG_TAG, "App update check not initiated") @@ -251,6 +244,15 @@ class HomeScreenActivity : AppCompatActivity() { } } + fun checkForUpdate(userInitiation:Boolean = false) { + if (persistentState.downloadSource == Constants.DOWNLOAD_SOURCE_PLAY_STORE) { + appUpdateManager.checkForAppUpdate(userInitiation, this, installStateUpdatedListener) // Might be play updater or web updater + } else { + get().checkForAppUpdate(userInitiation, this, installStateUpdatedListener) // Always web updater + } + + } + private fun checkForBlockListUpdate() { val connectedDOH = doHEndpointRepository.getConnectedDoH() var isRethinkPlusConnected = false @@ -267,61 +269,43 @@ class HomeScreenActivity : AppCompatActivity() { } } - fun checkForAppUpdate(isUserInitiated : Boolean) { - var version = 0 - try { - val pInfo: PackageInfo = context.packageManager.getPackageInfo(context.packageName, 0) - version = pInfo.versionCode - persistentState.appVersion = version - } catch (e: PackageManager.NameNotFoundException) { - Log.e(LOG_TAG, "Error while fetching version code: ${e.message}", e) + private val installStateUpdatedListener = object : AppUpdater.InstallStateListener { + override fun onStateUpdate(state: AppUpdater.InstallState) { + if (state.status == AppUpdater.InstallStatus.DOWNLOADED) { + //CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip + popupSnackBarForCompleteUpdate() + //showDownloadDialog(true, getString(R.string.download_update_dialog_title), getString(R.string.download_update_dialog_message)) + } else if (state.status == AppUpdater.InstallStatus.INSTALLED) { + Log.i(LOG_TAG, "InstallStateUpdatedListener: state: " + state.status) + appUpdateManager.unregisterListener(this) + } else { + appUpdateManager.unregisterListener(this) + Log.i(LOG_TAG, "InstallStateUpdatedListener: state: " + state.status) + // checkForDownload() + } } - if (persistentState.downloadSource == Constants.DOWNLOAD_SOURCE_PLAY_STORE) { - appUpdateManager.registerListener(installStateUpdatedListener) + override fun onUpdateCheckFailed(installSource: AppUpdater.InstallSource) { + runOnUiThread { + showDownloadDialog(installSource, getString(R.string.download_update_dialog_failure_title), getString(R.string.download_update_dialog_failure_message)) + } + } - appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + override fun onUpToDate(installSource: AppUpdater.InstallSource) { + runOnUiThread { + showDownloadDialog(installSource, getString(R.string.download_update_dialog_message_ok_title), getString(R.string.download_update_dialog_message_ok)) + } + } - if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) { - try { - appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.FLEXIBLE, this, version) - } catch (e: IntentSender.SendIntentException) { - appUpdateManager.unregisterListener(installStateUpdatedListener) - Log.e(LOG_TAG, "SendIntentException: ${e.message} ", e) - } - } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { - try { - appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.IMMEDIATE, this, version) - } catch (e: IntentSender.SendIntentException) { - appUpdateManager.unregisterListener(installStateUpdatedListener) - Log.e(LOG_TAG, "SendIntentException: ${e.message} ", e) - } - } else { - appUpdateManager.unregisterListener(installStateUpdatedListener) - Log.e(LOG_TAG, "checkForAppUpdateAvailability: something else") - if(isUserInitiated) { - showDownloadDialog(false, getString(R.string.download_update_dialog_failure_title), getString(R.string.download_update_dialog_failure_message)) - } - } + override fun onUpdateAvailable(installSource: AppUpdater.InstallSource) { + runOnUiThread { + showDownloadDialog(installSource, getString(R.string.download_update_dialog_title), getString(R.string.download_update_dialog_message)) } - }else{ - checkForAppDownload(version, isUserInitiated) } - } - private val installStateUpdatedListener: InstallStateUpdatedListener = object : InstallStateUpdatedListener { - override fun onStateUpdate(state: InstallState) { - if (state.installStatus() == InstallStatus.DOWNLOADED) { - //CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip - popupSnackBarForCompleteUpdate() - //showDownloadDialog(true, getString(R.string.download_update_dialog_title), getString(R.string.download_update_dialog_message)) - } else if (state.installStatus() == InstallStatus.INSTALLED) { - Log.i(LOG_TAG, "InstallStateUpdatedListener: state: " + state.installStatus()) - appUpdateManager.unregisterListener(this) - } else { - appUpdateManager.unregisterListener(this) - Log.i(LOG_TAG, "InstallStateUpdatedListener: state: " + state.installStatus()) - // checkForDownload() + override fun onUpdateQuotaExceeded(installSource: AppUpdater.InstallSource) { + runOnUiThread { + showDownloadDialog(installSource, getString(R.string.download_update_dialog_trylater_title), getString(R.string.download_update_dialog_trylater_message)) } } } @@ -335,68 +319,6 @@ class HomeScreenActivity : AppCompatActivity() { snackbar.show() } - private fun checkForAppDownload(version: Int, isUserInitiated : Boolean) { - Log.i(LOG_TAG, "App update check initiated") - val url = Constants.APP_DOWNLOAD_AVAILABLE_CHECK + version - serverCheckForAppUpdate(url, isUserInitiated) - } - - /** - * TODO - Remove the function from the activity and place in the - * HttpRequestHelper file which will return the boolean to check if there is - * update available. - */ - private fun serverCheckForAppUpdate(url: String, isUserInitiatedUpdateCheck : Boolean) { - val client = OkHttpClient() - val request = Request.Builder() - .url(url) - .build() - - client.newCall(request).enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - Log.d(LOG_TAG, "onFailure - ${call.isCanceled()}, ${call.isExecuted()}") - (context as HomeScreenActivity).runOnUiThread { - if (isUserInitiatedUpdateCheck) { - showDownloadDialog(false, getString(R.string.download_update_dialog_failure_title), getString(R.string.download_update_dialog_failure_message)) - } - } - call.cancel() - } - - override fun onResponse(call: Call, response: Response) { - try { - val stringResponse = response.body!!.string() - //creating json object - val jsonObject = JSONObject(stringResponse) - val responseVersion = jsonObject.getInt("version") - val updateValue = jsonObject.getBoolean("update") - val latestVersion = jsonObject.getInt("latest") - persistentState.lastAppUpdateCheck = System.currentTimeMillis() - Log.i(LOG_TAG, "Server response for the new version download is true, version number- $latestVersion") - if (responseVersion == 1) { - if (updateValue) { - (context as HomeScreenActivity).runOnUiThread { - showDownloadDialog(false, getString(R.string.download_update_dialog_title), getString(R.string.download_update_dialog_message)) - } - } else { - (context as HomeScreenActivity).runOnUiThread { - if (isUserInitiatedUpdateCheck) { - showDownloadDialog(false, getString(R.string.download_update_dialog_message_ok_title), getString(R.string.download_update_dialog_message_ok)) - } - } - } - } - response.close() - client.connectionPool.evictAll() - } catch (e: Exception) { - if (isUserInitiatedUpdateCheck) { - showDownloadDialog(false, getString(R.string.download_update_dialog_failure_title), getString(R.string.download_update_dialog_failure_message)) - } - } - } - }) - } - private fun serverCheckForBlocklistUpdate(url: String) { val client = OkHttpClient() val request = Request.Builder() @@ -484,7 +406,7 @@ class HomeScreenActivity : AppCompatActivity() { } - private fun showDownloadDialog(isPlayStore: Boolean, title: String, message: String) { + private fun showDownloadDialog(source: AppUpdater.InstallSource, title: String, message: String) { val builder = AlertDialog.Builder(context) //set title for alert dialog builder.setTitle(title) @@ -492,13 +414,16 @@ class HomeScreenActivity : AppCompatActivity() { builder.setMessage(message) builder.setCancelable(true) //performing positive action - if(message == getString(R.string.download_update_dialog_message_ok) || message == getString(R.string.download_update_dialog_failure_message)){ + if (message == getString(R.string.download_update_dialog_message_ok) || + message == getString(R.string.download_update_dialog_failure_message) || + message == getString(R.string.download_update_dialog_trylater_message) + ) { builder.setPositiveButton("ok") { dialogInterface, which -> dialogInterface.dismiss() } - }else { + } else { builder.setPositiveButton("Visit website") { dialogInterface, which -> - if (isPlayStore) { + if (source == AppUpdater.InstallSource.STORE) { appUpdateManager.completeUpdate() } else { initiateDownload() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 85cfcdf7c..71dc520a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -515,6 +515,8 @@ You are already on the latest version. Something went wrong! Either our servers are down or connection could not be established. Please try again after sometime. + Can\'t update right now + The update cannot be executed right now, please try again later. Search apps or IPs Search apps diff --git a/app/src/play/AndroidManifest.xml b/app/src/play/AndroidManifest.xml new file mode 100644 index 000000000..1d9ad6a98 --- /dev/null +++ b/app/src/play/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/play/java/com/celzero/bravedns/RethinkDnsApplicationPlay.kt b/app/src/play/java/com/celzero/bravedns/RethinkDnsApplicationPlay.kt new file mode 100644 index 000000000..944e42f1b --- /dev/null +++ b/app/src/play/java/com/celzero/bravedns/RethinkDnsApplicationPlay.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2021 RethinkDNS and its authors + * + * 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 + * + * https://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 com.celzero.bravedns + +import android.app.Application +import com.celzero.bravedns.service.AppUpdater +import com.celzero.bravedns.util.Constants +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.context.startKoin +import org.koin.dsl.module + +class RethinkDnsApplicationPlay:Application() { + override fun onCreate() { + super.onCreate() + startKoin { + if(BuildConfig.DEBUG) androidLogger() + androidContext(this@RethinkDnsApplicationPlay) + koin.loadModules(AppModules) + koin.loadModules(listOf(module { + single(override = true) { StoreAppUpdater(androidContext(), get())} + })) + } + } +} diff --git a/app/src/play/java/com/celzero/bravedns/StoreAppUpdater.kt b/app/src/play/java/com/celzero/bravedns/StoreAppUpdater.kt new file mode 100644 index 000000000..eebc30c23 --- /dev/null +++ b/app/src/play/java/com/celzero/bravedns/StoreAppUpdater.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2021 RethinkDNS and its authors + * + * 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 + * + * https://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 com.celzero.bravedns + +import android.app.Activity +import android.content.Context +import android.content.IntentSender +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.util.Log +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.InstallState +import com.google.android.play.core.install.InstallStateUpdatedListener +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.InstallStatus +import com.google.android.play.core.install.model.UpdateAvailability +import com.celzero.bravedns.service.AppUpdater +import com.celzero.bravedns.service.PersistentState +import com.celzero.bravedns.util.Constants + +class StoreAppUpdater(context: Context, private val persistentState: PersistentState) : AppUpdater { + private val LOG_TAG = "${Constants.LOG_TAG}/StoreAppUpdater" + private val listenerMapping = mutableMapOf() + private val appUpdateManager by lazy { + AppUpdateManagerFactory.create(context) + } + + override fun completeUpdate() { + appUpdateManager.completeUpdate() + } + + override fun unregisterListener(listener: AppUpdater.InstallStateListener) { + listenerMapping.remove(listener)?.also { + appUpdateManager.unregisterListener(it) + } + } + + override fun checkForAppUpdate(isUserInitiated: Boolean, activity: Activity, listener: AppUpdater.InstallStateListener) { + Log.d(LOG_TAG, "Beginning update check.") + val playListener = InstallStateUpdatedListener { state -> + val mappedStatus = when (state.installStatus()) { + InstallStatus.DOWNLOADED -> AppUpdater.InstallStatus.DOWNLOADED + InstallStatus.CANCELED -> AppUpdater.InstallStatus.CANCELED + InstallStatus.DOWNLOADING -> AppUpdater.InstallStatus.DOWNLOADING + InstallStatus.FAILED -> AppUpdater.InstallStatus.FAILED + InstallStatus.INSTALLED -> AppUpdater.InstallStatus.INSTALLED + InstallStatus.INSTALLING -> AppUpdater.InstallStatus.INSTALLING + InstallStatus.PENDING -> AppUpdater.InstallStatus.PENDING + else -> AppUpdater.InstallStatus.UNKNOWN + } + listener.onStateUpdate(AppUpdater.InstallState(mappedStatus)) + } + listenerMapping[listener] = playListener + appUpdateManager.registerListener(playListener) + + appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) { + Log.i(LOG_TAG, "Update available, starting flexible update") + try { + appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.FLEXIBLE, activity, 1) + } catch (e: IntentSender.SendIntentException) { + unregisterListener(listener) + Log.e(LOG_TAG, "SendIntentException: ${e.message} ", e) + } + } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { + Log.i(LOG_TAG, "Update available, starting immediate update") + try { + appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.IMMEDIATE, activity, 1) + } catch (e: IntentSender.SendIntentException) { + unregisterListener(listener) + Log.e(LOG_TAG, "SendIntentException: ${e.message} ", e) + } + } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { + if (isUserInitiated) { + listener.onUpdateQuotaExceeded(AppUpdater.InstallSource.STORE) + } + } else if(appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_NOT_AVAILABLE){ + unregisterListener(listener) + Log.e(LOG_TAG, "no update") + if (isUserInitiated) { + listener.onUpToDate(AppUpdater.InstallSource.STORE) + } + } + } + appUpdateManager.appUpdateInfo.addOnFailureListener { e -> + Log.e(LOG_TAG, "Update check failed", e) + unregisterListener(listener) + if (isUserInitiated) { + listener.onUpdateCheckFailed(AppUpdater.InstallSource.STORE) + } + } + } +}