From 4e7034cd59db8bd1529308ce18c3d4f336b69db8 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 14 Mar 2020 11:39:33 +0200 Subject: [PATCH] Application update checker --- app/build.gradle | 6 +- app/src/main/AndroidManifest.xml | 1 + .../kotatsu/core/github/AppVersion.kt | 19 +++ .../kotatsu/core/github/GithubRepository.kt | 28 +++++ .../koitharu/kotatsu/core/github/VersionId.kt | 57 +++++++++ .../ui/download/DownloadNotification.kt | 5 +- .../koitharu/kotatsu/ui/main/MainActivity.kt | 5 + .../ui/settings/AboutSettingsFragment.kt | 12 ++ .../kotatsu/ui/settings/UpdateService.kt | 109 ++++++++++++++++++ .../koitharu/kotatsu/utils/ext/JsoupExt.kt | 30 +++-- .../main/res/drawable-hdpi/ic_stat_update.png | Bin 0 -> 403 bytes .../main/res/drawable-mdpi/ic_stat_update.png | Bin 0 -> 358 bytes .../res/drawable-xhdpi/ic_stat_update.png | Bin 0 -> 591 bytes .../res/drawable-xxhdpi/ic_stat_update.png | Bin 0 -> 824 bytes .../res/drawable-xxxhdpi/ic_stat_update.png | Bin 0 -> 1101 bytes app/src/main/res/drawable/ic_information.xml | 17 +-- app/src/main/res/values-ru/strings.xml | 3 + app/src/main/res/values/constants.xml | 1 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/xml/pref_about.xml | 6 + app/src/main/res/xml/pref_headers.xml | 5 + build.gradle | 4 +- 22 files changed, 289 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/github/AppVersion.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/github/GithubRepository.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/github/VersionId.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/settings/AboutSettingsFragment.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/settings/UpdateService.kt create mode 100644 app/src/main/res/drawable-hdpi/ic_stat_update.png create mode 100644 app/src/main/res/drawable-mdpi/ic_stat_update.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_stat_update.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_stat_update.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_stat_update.png create mode 100644 app/src/main/res/xml/pref_about.xml diff --git a/app/build.gradle b/app/build.gradle index 0181471fd..71d2696b3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,11 +61,11 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3' - implementation 'androidx.core:core-ktx:1.3.0-alpha01' + implementation 'androidx.core:core-ktx:1.3.0-alpha02' implementation 'androidx.fragment:fragment-ktx:1.2.2' - implementation 'androidx.appcompat:appcompat:1.2.0-alpha02' + implementation 'androidx.appcompat:appcompat:1.2.0-alpha03' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4' - implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-beta01' implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha01' implementation 'androidx.preference:preference:1.1.0' implementation 'com.google.android.material:material:1.2.0-alpha05' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fbb64b0c9..3cbcdd36c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -47,6 +47,7 @@ + () + + suspend fun getLatestVersion(): AppVersion { + val request = Request.Builder() + .get() + .url("https://api.github.com/repos/nv95/Kotatsu/releases/latest") + val json = okHttp.newCall(request.build()).await().parseJson() + val asset = json.getJSONArray("assets").getJSONObject(0) + return AppVersion( + id = json.getLong("id"), + url = json.getString("html_url"), + name = json.getString("name").removePrefix("v"), + apkSize = asset.getLong("size"), + apkUrl = asset.getString("browser_download_url") + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/github/VersionId.kt b/app/src/main/java/org/koitharu/kotatsu/core/github/VersionId.kt new file mode 100644 index 000000000..0ee6affdf --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/github/VersionId.kt @@ -0,0 +1,57 @@ +package org.koitharu.kotatsu.core.github + +import java.util.* + +data class VersionId( + val major: Int, + val minor: Int, + val build: Int, + val variantType: String, + val variantNumber: Int +) : Comparable { + + override fun compareTo(other: VersionId): Int { + var diff = major.compareTo(other.major) + if (diff != 0) { + return diff + } + diff = minor.compareTo(other.minor) + if (diff != 0) { + return diff + } + diff = build.compareTo(other.build) + if (diff != 0) { + return diff + } + diff = variantWeight(variantType).compareTo(variantWeight(other.variantType)) + if (diff != 0) { + return diff + } + return variantNumber.compareTo(other.variantNumber) + } + + companion object { + + @JvmStatic + private fun variantWeight(variantType: String) = + when (variantType.toLowerCase(Locale.ROOT)) { + "a" -> 1 + "b" -> 2 + "rc" -> 4 + else -> 8 + } + + @JvmStatic + fun parse(versionName: String): VersionId { + val parts = versionName.substringBeforeLast('-').split('.') + val variant = versionName.substringAfterLast('-', "") + return VersionId( + major = parts.getOrNull(0)?.toIntOrNull() ?: 0, + minor = parts.getOrNull(1)?.toIntOrNull() ?: 0, + build = parts.getOrNull(2)?.toIntOrNull() ?: 0, + variantType = variant.filter(Char::isLetter), + variantNumber = variant.filter(Char::isDigit).toIntOrNull() ?: 0 + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt b/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt index 398478a4b..c75b7674c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt @@ -23,7 +23,8 @@ class DownloadNotification(private val context: Context) { context.applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager init { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && manager.getNotificationChannel(CHANNEL_ID) == null) { val channel = NotificationChannel( CHANNEL_ID, context.getString(R.string.downloads), @@ -67,6 +68,7 @@ class DownloadNotification(private val context: Context) { builder.setSmallIcon(android.R.drawable.stat_notify_error) builder.setSubText(context.getString(R.string.error)) builder.setContentText(e.getDisplayMessage(context.resources)) + builder.setAutoCancel(true) builder.setContentIntent(null) } @@ -92,6 +94,7 @@ class DownloadNotification(private val context: Context) { builder.setProgress(0, 0, false) builder.setContentText(context.getString(R.string.download_complete)) builder.setContentIntent(createIntent(context, manga)) + builder.setAutoCancel(true) builder.setSmallIcon(android.R.drawable.stat_sys_download_done) } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt index 5be3bd96a..302e2ac1f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt @@ -9,6 +9,7 @@ import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.ActionBarDrawerToggle import androidx.core.view.isVisible +import androidx.core.view.postDelayed import androidx.fragment.app.Fragment import androidx.swiperefreshlayout.widget.CircularProgressDrawable import com.google.android.material.navigation.NavigationView @@ -28,6 +29,7 @@ import org.koitharu.kotatsu.ui.main.list.remote.RemoteListFragment import org.koitharu.kotatsu.ui.reader.ReaderActivity import org.koitharu.kotatsu.ui.reader.ReaderState import org.koitharu.kotatsu.ui.settings.SettingsActivity +import org.koitharu.kotatsu.ui.settings.UpdateService import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.resolveDp @@ -64,6 +66,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList navigationView.setCheckedItem(R.id.nav_history) setPrimaryFragment(HistoryListFragment.newInstance()) } + drawer.postDelayed(4000) { + UpdateService.startIfRequired(applicationContext) + } } override fun onDestroy() { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/AboutSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/AboutSettingsFragment.kt new file mode 100644 index 000000000..aa687a78f --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/AboutSettingsFragment.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.ui.settings + +import android.os.Bundle +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.ui.common.BasePreferenceFragment + +class AboutSettingsFragment : BasePreferenceFragment(R.string.about_app) { + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.pref_about) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/UpdateService.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/UpdateService.kt new file mode 100644 index 000000000..9ff734e80 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/UpdateService.kt @@ -0,0 +1,109 @@ +package org.koitharu.kotatsu.ui.settings + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.graphics.BitmapFactory +import android.net.Uri +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.content.edit +import androidx.preference.PreferenceManager +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.github.AppVersion +import org.koitharu.kotatsu.core.github.GithubRepository +import org.koitharu.kotatsu.core.github.VersionId +import org.koitharu.kotatsu.ui.common.BaseService +import org.koitharu.kotatsu.utils.FileSizeUtils +import java.util.concurrent.TimeUnit + +class UpdateService : BaseService() { + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + launch(Dispatchers.IO) { + try { + val repo = GithubRepository() + val version = repo.getLatestVersion() + val newVersionId = VersionId.parse(version.name) + val currentVersionId = VersionId.parse(BuildConfig.VERSION_NAME) + if (newVersionId > currentVersionId) { + withContext(Dispatchers.Main) { + showUpdateNotification(version) + } + } + PreferenceManager.getDefaultSharedPreferences(this@UpdateService).edit(true) { + putLong(getString(R.string.key_app_update), System.currentTimeMillis()) + } + } catch (_: CancellationException) { + } catch (e: Throwable) { + if (BuildConfig.DEBUG) { + e.printStackTrace() + } + } finally { + withContext(Dispatchers.Main) { + stopSelf(startId) + } + } + } + return START_NOT_STICKY + } + + private fun showUpdateNotification(newVersion: AppVersion) { + val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && manager.getNotificationChannel(CHANNEL_ID) == null + ) { + val channel = NotificationChannel( + CHANNEL_ID, + getString(R.string.application_update), + NotificationManager.IMPORTANCE_DEFAULT + ) + manager.createNotificationChannel(channel) + } + val builder = NotificationCompat.Builder(this, CHANNEL_ID) + builder.setContentTitle(getString(R.string.app_update_available)) + builder.setContentText(buildString { + append(newVersion.name) + append(" (") + append(FileSizeUtils.formatBytes(this@UpdateService, newVersion.apkSize)) + append(')') + }) + builder.setContentIntent( + PendingIntent.getActivity( + this, + NOTIFICATION_ID, + Intent(Intent.ACTION_VIEW, Uri.parse(newVersion.url)), + PendingIntent.FLAG_CANCEL_CURRENT + ) + ) + builder.setSmallIcon(R.drawable.ic_stat_update) + builder.setAutoCancel(true) + builder.setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)) + manager.notify(NOTIFICATION_ID, builder.build()) + } + + companion object { + + private const val NOTIFICATION_ID = 202 + private const val CHANNEL_ID = "update" + private val PERIOD = TimeUnit.HOURS.toMillis(10) + + fun start(context: Context) = + context.startService(Intent(context, UpdateService::class.java)) + + fun startIfRequired(context: Context) { + val lastUpdate = PreferenceManager.getDefaultSharedPreferences(context) + .getLong(context.getString(R.string.key_app_update), 0) + if (lastUpdate + PERIOD < System.currentTimeMillis()) { + start(context) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/JsoupExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/JsoupExt.kt index 75e3eada3..cee7c55b2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/JsoupExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/JsoupExt.kt @@ -2,20 +2,32 @@ package org.koitharu.kotatsu.utils.ext import okhttp3.Response import okhttp3.internal.closeQuietly +import org.json.JSONObject import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element fun Response.parseHtml(): Document { - val stream = body?.byteStream() ?: throw NullPointerException("Response body is null") - val charset = body!!.contentType()?.charset()?.name() - val doc = Jsoup.parse( - stream, - charset, - this.request.url.toString() - ) - closeQuietly() - return doc + try { + val stream = body?.byteStream() ?: throw NullPointerException("Response body is null") + val charset = body!!.contentType()?.charset()?.name() + return Jsoup.parse( + stream, + charset, + request.url.toString() + ) + } finally { + closeQuietly() + } +} + +fun Response.parseJson(): JSONObject { + try { + val string = body?.string() ?: throw NullPointerException("Response body is null") + return JSONObject(string) + } finally { + closeQuietly() + } } fun Element.firstChild(): Element? = children().first() \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_stat_update.png b/app/src/main/res/drawable-hdpi/ic_stat_update.png new file mode 100644 index 0000000000000000000000000000000000000000..0c727d5b0208e118fc0aaeaa88842ed28d224198 GIT binary patch literal 403 zcmV;E0c`$>P)8-LIA zSgfT_iaEkz-9qNiRriCpIrDLN;CwT~Ok3MqEOcTVGnmE{eqs_6;o4Zp4~$|2!x+LK zKIet`jzip{8s)Y@ZtC(cSJ5Zybj#Hv?!T>RD}pTA&svD+mktgY}TzsAoS~U&F65K;1aP zW0a?g*Ur&XI4G=l;BOr!Ms1@r>MwqV@D*DPPn&%f(T9`hm@e_HL0_qNSs!*J z1N{tBIwjw7yxhK4$bY z+*f2d=bn3S7Ee9Pb576i?L6mw-?Fm52&6EJUF={RTiC<~*0F|FtY8}HxXr6V^L%3g z)p1w9mjF0IDlV{(54_?rB46;1<9vaa@NEYoasu~BfCpq^yx)ndf&o;Z0ZphZ1<-;i z%%H6lKnD&&qwas;3+O{LdNGI-oMAZBTG3M|(2ixC;wfytbGb99t56_=I*j1bcidny z{I-S90O^2S;5syKEXE9e4!I6P7(>I4dU1g8tOm8eYKehojH5pyM{%13c$YgRuOspf zZ}|cxy>O~9mjGB2^U5qoJ?4?cVMJ!JD7UN2BY|SaClkq0Ya>jOyZ`_I07*qoM6N<$ Ef;6L-XaE2J literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_update.png b/app/src/main/res/drawable-xhdpi/ic_stat_update.png new file mode 100644 index 0000000000000000000000000000000000000000..1ebbddc98a34fa78e237c0610f9c87ff0304dd1b GIT binary patch literal 591 zcmV-V0lLivSfUAQ#=+dzc4h2U+R4Q6pr4@^)gD#z%DhP^;;@-iuEafa1AN7OTumwO;Y*%?MNFi)|0rG; zfDJBC0221^EdWZw*{K?{f>J*Lc3=i~V_wFtegX{PK}-E33>Sa`Pyh-*0r>v_C}Ue& z?#IKH`p3ewp}nr$Re*407{Nz;!)na${``Tm79U$}$xp&OXj8ZEKt&i~Du?G!IKf9K?H6@T;RGr#{cPJg$z_@1$}S&P%cLIJgp8fhPD z&YlmQx3;qSX?k|uvS+zd)3fHS`%`i@I{RLe`i7Mz-JypL|BzT-z{x!||6$rw=^4jM zV-AP@>9j9k-T%1g>HQDcQ%&3B3k2;StH-RUk$e6@`P`%GIR~mMR+voReaQTWwC&pq zvND=ypWe?bU189(^~&U|**csVZo9VJUS)Y^8q1fylKRZm{|^0DWvo@WTYYO)s*D!r z3uPVAwhK#AYr>>16eV)_MhP}Zi8dH^vpwQ$3{etEcf7ZL?$KRve2Li3Y~DCYh85E#i~0JmpEI7G|4a^g{(5f~{Pp*8iR#xSY+u$oKPlx7F#T7W`D)El z%Qwa^`WYPw$M2 zu@0fvSmLJs+?FMAKi7eO0eT>9WZcs8zMx`mZH|NPT9zwntP6UW0^Ari7Kbr(>CWVB zu)FflcklcuEL-kBaNWP{g&E&87L(OKI}|yboY1ht*FE!t=X1Vy%rAy>Lh{^$zg8d;ko7TCPc7=29*V&tsnVc!N dFd#$YFSG6PLN#eKT~Hoj@O1TaS?83{1OS_Tb(jDE literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_update.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_update.png new file mode 100644 index 0000000000000000000000000000000000000000..f341fd42656cf3c0fb4af706261bb5f991d21e61 GIT binary patch literal 1101 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>U=i_jaSW-L^Y*UqnwVsn;~(#3 z?sZ$MGVPw0M%CACzQMOVA_TNIPKfI8ZQU^SM#Qa%i5@yS0@99qM7|2Cn#8TE@V>ZU z>8qt4OR9vo7jFH2?)W21r@S<)^mlVV?l=DYU+w&-Kl9c1=RcccZ2bJs{M6HvR6HlC zNct6U=^bzW!D0V6{7+N!4?+6}4Bs9{zd0ON)2qLcJ^q35n`6s=ELrtk(eA+S3i0;| z;`fe4R|s2ww2C{txoCEU)$4p4=rcNo@zZkFs&}h?-xQxi9a2AV2oXPR{J-Yg1KN=k&Hu z`Ofz3x`c7)jkPQ?Ya<_PFa3XbRb{rMCu8!BMPEK!-g>qpt|;WCX35k_*?^u!4X0E8 zj^a)X%go-kGWN&SKZ|u2SnX@<{utMzo<08M z_b~kPvWEXxGBxt`{IY8A-|t*@DYI~q-;b+vG!x?1K0UQE*?+pwEHCLZ+r;mfeO}ri z_Vrl7#3HTCudHiAf85u+zhU>Qk{L|5uFC}Long1~$k4jG>so}v`tu?ec79v+*yD%# zA6D~g-oQ{^A67SEw*JHNm|dWNeJ1nxhv!ZMbHmn7xhuYmv1g1In*6Zq;I&;flWEtS zY+18!8{)V+RG@JL4erHC5A5b_b~$c)ST8|L{_#guE48iv?W;6cc#KeELQ;cqXQ#i& zsmJy0&4*vH2k$TgrYDX6A#b_)R?ELRJ{6RpZvOi^^TCE+2fZU?=jOXl;hmFz`Jeez z#}u~mhg)9MWG}RF`5KawW}CYE)Bn#2Z!~*EPnkWlc20iHySu2` rBvrroN6D#MuN|~{3d)_#$M4UaqRf@cxu+OdOfY!5`njxgN@xNAyLAYv literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_information.xml b/app/src/main/res/drawable/ic_information.xml index 8364044a5..f34c14224 100644 --- a/app/src/main/res/drawable/ic_information.xml +++ b/app/src/main/res/drawable/ic_information.xml @@ -1,8 +1,11 @@ - - - + + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 065acbd5e..eb4597986 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -104,4 +104,7 @@ Внешнее хранилище Домен По умолчанию + Обновление приложения + Доступно обновление приложения + О программе \ No newline at end of file diff --git a/app/src/main/res/values/constants.xml b/app/src/main/res/values/constants.xml index 16dd185c8..faba55904 100644 --- a/app/src/main/res/values/constants.xml +++ b/app/src/main/res/values/constants.xml @@ -10,6 +10,7 @@ reading_history_clear grid_size reader_switchers + app_update domain diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8313b024c..b96150489 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -105,4 +105,7 @@ External storage Domain Default + Application update + Application update is available + About \ No newline at end of file diff --git a/app/src/main/res/xml/pref_about.xml b/app/src/main/res/xml/pref_about.xml new file mode 100644 index 000000000..5699e19c1 --- /dev/null +++ b/app/src/main/res/xml/pref_about.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/pref_headers.xml b/app/src/main/res/xml/pref_headers.xml index a71f6ece5..53e6ccad8 100644 --- a/app/src/main/res/xml/pref_headers.xml +++ b/app/src/main/res/xml/pref_headers.xml @@ -22,4 +22,9 @@ android:icon="@drawable/ic_history" android:title="@string/history_and_cache" /> + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 98c3a4a01..aaa2d2e8f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.3.61" + ext.kotlin_version = "1.3.70" repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0-beta01' + classpath 'com.android.tools.build:gradle:4.0.0-beta02' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong