mirror of
https://github.com/tateisu/SubwayTooter
synced 2024-12-22 15:24:12 +01:00
Fix:
- remove ForegroundPollingService - check notifications in MyFirebaseMessagingService.onMessageReceived - dont send empty search request. Dependencies: - coreLibraryDesugaringEnabled true - desugar_jdk_libs:1.2.0 - material:1.7.0 - exifinterface:1.3.5 - annotation:1.5.0 - firebase-messaging:23.1.0 - work-runtime-ktx:2.8.0-beta01 - appcompat_version = 1.5.1 - kotlin_version = 1.7.20 - Android Gradle plugin 7.3.1 - google-services:4.3.14
This commit is contained in:
parent
0a73842052
commit
19d626e5f1
@ -19,6 +19,7 @@ android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
@ -128,6 +129,10 @@ dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation fileTree(include: ['*.aar'], dir: 'src/main/libs')
|
||||
|
||||
// desugar_jdk_libs 2.0.0 は AGP 7.4.0-alpha10 以降を要求する
|
||||
//noinspection GradleDependency
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.0'
|
||||
|
||||
// targetSdkVersion 31 で androidTest 時に android:exported 云々で怒られる問題の対策
|
||||
// https://github.com/android/android-test/issues/1022
|
||||
androidTestImplementation "androidx.test:core:1.4.0"
|
||||
@ -158,9 +163,9 @@ dependencies {
|
||||
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
|
||||
|
||||
// NavigationView
|
||||
implementation "com.google.android.material:material:1.6.1"
|
||||
implementation "com.google.android.material:material:1.7.0"
|
||||
|
||||
implementation "androidx.exifinterface:exifinterface:1.3.3"
|
||||
implementation "androidx.exifinterface:exifinterface:1.3.5"
|
||||
|
||||
// CustomTabs
|
||||
implementation "androidx.browser:browser:1.4.0"
|
||||
@ -168,10 +173,10 @@ dependencies {
|
||||
// Recyclerview
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
|
||||
kapt 'androidx.annotation:annotation:1.4.0'
|
||||
kapt 'androidx.annotation:annotation:1.5.0'
|
||||
|
||||
// https://firebase.google.com/support/release-notes/android
|
||||
implementation "com.google.firebase:firebase-messaging:23.0.8"
|
||||
implementation "com.google.firebase:firebase-messaging:23.1.0"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test"
|
||||
@ -262,7 +267,7 @@ dependencies {
|
||||
// optional - Test helpers for LiveData
|
||||
testImplementation "androidx.arch.core:core-testing:$arch_version"
|
||||
|
||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.8.0-beta01'
|
||||
|
||||
def roomVersion = "2.4.3"
|
||||
implementation "androidx.room:room-runtime:$roomVersion"
|
||||
|
@ -354,9 +354,6 @@
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name="jp.juggler.subwaytooter.notification.ForegroundPollingService"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
|
@ -2,12 +2,13 @@ package jp.juggler.subwaytooter
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
import jp.juggler.subwaytooter.notification.ForegroundPollingService
|
||||
import jp.juggler.subwaytooter.notification.PollingChecker
|
||||
import jp.juggler.subwaytooter.notification.restartAllWorker
|
||||
import jp.juggler.subwaytooter.pref.PrefDevice
|
||||
import jp.juggler.subwaytooter.table.NotificationCache
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.util.LogCategory
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.*
|
||||
|
||||
class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
@ -33,9 +34,22 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewToken(token: String) {
|
||||
try {
|
||||
log.w("onTokenRefresh: token=$token")
|
||||
PrefDevice.from(this).edit().putString(PrefDevice.KEY_DEVICE_TOKEN, token).apply()
|
||||
restartAllWorker(this)
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex, "onNewToken failed")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
val context = this
|
||||
|
||||
val messageId = remoteMessage.messageId ?: return
|
||||
if (isDuplicateMessage(messageId)) return
|
||||
|
||||
val accounts = ArrayList<SavedAccount>()
|
||||
for ((key, value) in remoteMessage.data) {
|
||||
log.w("onMessageReceived: $key=$value")
|
||||
@ -60,19 +74,26 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
NotificationCache.resetLastLoad()
|
||||
accounts.addAll(SavedAccount.loadAccountList(context))
|
||||
}
|
||||
log.i("accounts.size=${accounts.size}")
|
||||
accounts.forEach {
|
||||
ForegroundPollingService.start(this, remoteMessage.messageId, it.db_id)
|
||||
|
||||
log.i("accounts.size=${accounts.size} thred=${Thread.currentThread().name}")
|
||||
runBlocking {
|
||||
accounts.forEach {
|
||||
check(it.db_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewToken(token: String) {
|
||||
private suspend fun check(accountDbId: Long) {
|
||||
try {
|
||||
log.w("onTokenRefresh: token=$token")
|
||||
PrefDevice.from(this).edit().putString(PrefDevice.KEY_DEVICE_TOKEN, token).apply()
|
||||
restartAllWorker(this)
|
||||
PollingChecker(
|
||||
context = this,
|
||||
accountDbId = accountDbId
|
||||
).check { a, s ->
|
||||
val text = "[${a.acct.pretty}]${s.desc}"
|
||||
log.i(text)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex, "onNewToken failed")
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1034,7 +1034,7 @@ class TootApiClient(
|
||||
if (result.error != null) return result
|
||||
|
||||
val instance = result.caption // same to instance
|
||||
val clientName = if (clientNameArg.isNotEmpty()) clientNameArg else DEFAULT_CLIENT_NAME
|
||||
val clientName = clientNameArg.ifEmpty { DEFAULT_CLIENT_NAME }
|
||||
val clientInfo =
|
||||
ClientInfo.load(instance, clientName) ?: return result.setError("missing client id")
|
||||
|
||||
@ -1271,12 +1271,24 @@ class TootApiClient(
|
||||
}
|
||||
}
|
||||
|
||||
// query: query_string after ? ( ? itself is excluded )
|
||||
suspend fun TootApiClient.requestMastodonSearch(
|
||||
parser: TootParser,
|
||||
query: String,
|
||||
// 検索文字列
|
||||
q: String,
|
||||
// リモートサーバの情報を解決するなら真
|
||||
resolve: Boolean,
|
||||
// ギャップ読み込み時の追加パラメータ
|
||||
extra: String = "",
|
||||
): Pair<TootApiResult?, TootResults?> {
|
||||
|
||||
if (q.all { CharacterGroup.isWhitespace(it.code) }) {
|
||||
return Pair(null, null)
|
||||
}
|
||||
|
||||
val query = "q=${q.encodePercent()}&resolve=$resolve${
|
||||
if (extra.isEmpty()) "" else "&$extra"
|
||||
}"
|
||||
|
||||
var searchApiVersion = 2
|
||||
var apiResult = request("/api/v2/search?$query")
|
||||
?: return Pair(null, null)
|
||||
@ -1338,7 +1350,8 @@ suspend fun TootApiClient.syncAccountByUrl(
|
||||
} else {
|
||||
val (apiResult, searchResult) = requestMastodonSearch(
|
||||
parser,
|
||||
"q=${whoUrl.encodePercent()}&resolve=true"
|
||||
q = whoUrl,
|
||||
resolve = true,
|
||||
)
|
||||
val ar = searchResult?.accounts?.firstOrNull()
|
||||
if (apiResult != null && apiResult.error == null && ar == null) {
|
||||
@ -1380,7 +1393,8 @@ suspend fun TootApiClient.syncAccountByAcct(
|
||||
} else {
|
||||
val (apiResult, searchResult) = requestMastodonSearch(
|
||||
parser,
|
||||
"q=${acct.ascii.encodePercent()}&resolve=true"
|
||||
q = acct.ascii,
|
||||
resolve = true,
|
||||
)
|
||||
val ar = searchResult?.accounts?.firstOrNull()
|
||||
if (apiResult != null && apiResult.error == null && ar == null) {
|
||||
@ -1450,7 +1464,8 @@ suspend fun TootApiClient.syncStatus(
|
||||
} else {
|
||||
val (apiResult, searchResult) = requestMastodonSearch(
|
||||
parser,
|
||||
"q=${url.encodePercent()}&resolve=true"
|
||||
q = url,
|
||||
resolve = true,
|
||||
)
|
||||
val targetStatus = searchResult?.statuses?.firstOrNull()
|
||||
if (apiResult != null && apiResult.error == null && targetStatus == null) {
|
||||
|
@ -921,10 +921,13 @@ class ColumnTask_Gap(
|
||||
column.listData.forEach { counter(it) }
|
||||
|
||||
// https://mastodon2.juggler.jp/api/v2/search?q=gargron&type=accounts&offset=5
|
||||
var query = "q=${column.searchQuery.encodePercent()}&type=$type&offset=$offset"
|
||||
if (column.searchResolve) query += "&resolve=1"
|
||||
|
||||
val (apiResult, searchResult) = client.requestMastodonSearch(parser, query)
|
||||
val (apiResult, searchResult) = client.requestMastodonSearch(
|
||||
parser,
|
||||
q= column.searchQuery,
|
||||
resolve = column.searchResolve,
|
||||
extra = "type=$type&offset=$offset",
|
||||
)
|
||||
if (searchResult != null) {
|
||||
listTmp = ArrayList()
|
||||
addAll(listTmp, searchResult.hashtags)
|
||||
|
@ -1220,10 +1220,11 @@ class ColumnTask_Loading(
|
||||
val (instance, instanceResult) = TootInstance.get(client)
|
||||
instance ?: return instanceResult
|
||||
|
||||
var query = "q=${column.searchQuery.encodePercent()}"
|
||||
if (column.searchResolve) query += "&resolve=1"
|
||||
|
||||
val (apiResult, searchResult) = client.requestMastodonSearch(parser, query)
|
||||
val (apiResult, searchResult) = client.requestMastodonSearch(
|
||||
parser,
|
||||
q=column.searchQuery,
|
||||
resolve = column.searchResolve,
|
||||
)
|
||||
if (searchResult != null) {
|
||||
listTmp = ArrayList()
|
||||
addAll(listTmp, searchResult.hashtags)
|
||||
|
@ -1,130 +1,130 @@
|
||||
package jp.juggler.subwaytooter.notification
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.os.SystemClock
|
||||
import androidx.core.content.ContextCompat
|
||||
import jp.juggler.subwaytooter.global.appDispatchers
|
||||
import jp.juggler.subwaytooter.notification.CheckerWakeLocks.Companion.checkerWakeLocks
|
||||
import jp.juggler.util.EmptyScope
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.launchMain
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.max
|
||||
|
||||
class ForegroundPollingService : Service() {
|
||||
companion object {
|
||||
private val log = LogCategory("ForegroundPollingService")
|
||||
private const val NOTIFICATION_ID_FOREGROUND_POLLING = 4
|
||||
private const val EXTRA_ACCOUNT_DB_ID = "accountDbId"
|
||||
private const val EXTRA_MESSAGE_ID = "messageId"
|
||||
|
||||
fun start(
|
||||
context: Context,
|
||||
messageId: String?,
|
||||
dbId: Long,
|
||||
) {
|
||||
val intent = Intent(context, ForegroundPollingService::class.java).apply {
|
||||
putExtra(EXTRA_ACCOUNT_DB_ID, dbId)
|
||||
putExtra(EXTRA_MESSAGE_ID, messageId)
|
||||
}
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
}
|
||||
|
||||
private class Item(
|
||||
val accountDbId: Long,
|
||||
var lastRequired: Long = 0L,
|
||||
var lastHandled: Long = 0L,
|
||||
var lastStartId: Int = 0,
|
||||
)
|
||||
|
||||
private val map = HashMap<Long, Item>()
|
||||
private val channel = Channel<Long>()
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
override fun onCreate() {
|
||||
log.i("onCreate")
|
||||
super.onCreate()
|
||||
checkerWakeLocks(this).acquireWakeLocks()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
log.i("onDestroy")
|
||||
super.onDestroy()
|
||||
checkerWakeLocks(this).releasePowerLocks()
|
||||
channel.close()
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val accountDbId = intent?.getLongExtra(EXTRA_ACCOUNT_DB_ID, -1L) ?: -1L
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
log.i("onStartCommand startId=$startId, accountDbId=$accountDbId")
|
||||
synchronized(map) {
|
||||
map.getOrPut(accountDbId) {
|
||||
Item(accountDbId = accountDbId)
|
||||
}.apply {
|
||||
lastRequired = now
|
||||
lastStartId = startId
|
||||
}
|
||||
}
|
||||
launchMain {
|
||||
try {
|
||||
channel.send(now)
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
init {
|
||||
EmptyScope.launch(appDispatchers.default) {
|
||||
var lastStartId = 0
|
||||
while (true) {
|
||||
try {
|
||||
val target = synchronized(map) {
|
||||
map.values
|
||||
.filter { it.lastRequired > it.lastHandled }
|
||||
.minByOrNull { it.lastRequired }
|
||||
?.also { it.lastHandled = it.lastRequired }
|
||||
}
|
||||
if (target != null) {
|
||||
lastStartId = max(lastStartId, target.lastStartId)
|
||||
check(target.accountDbId)
|
||||
continue
|
||||
}
|
||||
log.i("stopSelf lastStartId=$lastStartId")
|
||||
if (lastStartId != 0) stopSelf(lastStartId)
|
||||
channel.receive()
|
||||
} catch (ignored: ClosedReceiveChannelException) {
|
||||
log.i("channel closed.")
|
||||
break
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun check(accountDbId: Long) {
|
||||
try {
|
||||
PollingChecker(
|
||||
context = this@ForegroundPollingService,
|
||||
accountDbId = accountDbId
|
||||
).check { a, s ->
|
||||
val text = "[${a.acct.pretty}]${s.desc}"
|
||||
CheckerNotification.showMessage(this, text) {
|
||||
startForeground(NOTIFICATION_ID_FOREGROUND_POLLING, it)
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
//package jp.juggler.subwaytooter.notification
|
||||
//
|
||||
//import android.app.Service
|
||||
//import android.content.Context
|
||||
//import android.content.Intent
|
||||
//import android.os.IBinder
|
||||
//import android.os.SystemClock
|
||||
//import androidx.core.content.ContextCompat
|
||||
//import jp.juggler.subwaytooter.global.appDispatchers
|
||||
//import jp.juggler.subwaytooter.notification.CheckerWakeLocks.Companion.checkerWakeLocks
|
||||
//import jp.juggler.util.EmptyScope
|
||||
//import jp.juggler.util.LogCategory
|
||||
//import jp.juggler.util.launchMain
|
||||
//import kotlinx.coroutines.channels.Channel
|
||||
//import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||
//import kotlinx.coroutines.launch
|
||||
//import kotlin.math.max
|
||||
//
|
||||
//class ForegroundPollingService : Service() {
|
||||
// companion object {
|
||||
// private val log = LogCategory("ForegroundPollingService")
|
||||
// private const val NOTIFICATION_ID_FOREGROUND_POLLING = 4
|
||||
// private const val EXTRA_ACCOUNT_DB_ID = "accountDbId"
|
||||
// private const val EXTRA_MESSAGE_ID = "messageId"
|
||||
//
|
||||
// fun start(
|
||||
// context: Context,
|
||||
// messageId: String?,
|
||||
// dbId: Long,
|
||||
// ) {
|
||||
// val intent = Intent(context, ForegroundPollingService::class.java).apply {
|
||||
// putExtra(EXTRA_ACCOUNT_DB_ID, dbId)
|
||||
// putExtra(EXTRA_MESSAGE_ID, messageId)
|
||||
// }
|
||||
// ContextCompat.startForegroundService(context, intent)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private class Item(
|
||||
// val accountDbId: Long,
|
||||
// var lastRequired: Long = 0L,
|
||||
// var lastHandled: Long = 0L,
|
||||
// var lastStartId: Int = 0,
|
||||
// )
|
||||
//
|
||||
// private val map = HashMap<Long, Item>()
|
||||
// private val channel = Channel<Long>()
|
||||
//
|
||||
// override fun onBind(intent: Intent?): IBinder? = null
|
||||
//
|
||||
// override fun onCreate() {
|
||||
// log.i("onCreate")
|
||||
// super.onCreate()
|
||||
// checkerWakeLocks(this).acquireWakeLocks()
|
||||
// }
|
||||
//
|
||||
// override fun onDestroy() {
|
||||
// log.i("onDestroy")
|
||||
// super.onDestroy()
|
||||
// checkerWakeLocks(this).releasePowerLocks()
|
||||
// channel.close()
|
||||
// }
|
||||
//
|
||||
// override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
// val accountDbId = intent?.getLongExtra(EXTRA_ACCOUNT_DB_ID, -1L) ?: -1L
|
||||
// val now = SystemClock.elapsedRealtime()
|
||||
// log.i("onStartCommand startId=$startId, accountDbId=$accountDbId")
|
||||
// synchronized(map) {
|
||||
// map.getOrPut(accountDbId) {
|
||||
// Item(accountDbId = accountDbId)
|
||||
// }.apply {
|
||||
// lastRequired = now
|
||||
// lastStartId = startId
|
||||
// }
|
||||
// }
|
||||
// launchMain {
|
||||
// try {
|
||||
// channel.send(now)
|
||||
// } catch (ex: Throwable) {
|
||||
// log.trace(ex)
|
||||
// }
|
||||
// }
|
||||
// return START_NOT_STICKY
|
||||
// }
|
||||
//
|
||||
// init {
|
||||
// EmptyScope.launch(appDispatchers.default) {
|
||||
// var lastStartId = 0
|
||||
// while (true) {
|
||||
// try {
|
||||
// val target = synchronized(map) {
|
||||
// map.values
|
||||
// .filter { it.lastRequired > it.lastHandled }
|
||||
// .minByOrNull { it.lastRequired }
|
||||
// ?.also { it.lastHandled = it.lastRequired }
|
||||
// }
|
||||
// if (target != null) {
|
||||
// lastStartId = max(lastStartId, target.lastStartId)
|
||||
// check(target.accountDbId)
|
||||
// continue
|
||||
// }
|
||||
// log.i("stopSelf lastStartId=$lastStartId")
|
||||
// if (lastStartId != 0) stopSelf(lastStartId)
|
||||
// channel.receive()
|
||||
// } catch (ignored: ClosedReceiveChannelException) {
|
||||
// log.i("channel closed.")
|
||||
// break
|
||||
// } catch (ex: Throwable) {
|
||||
// log.trace(ex)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private suspend fun check(accountDbId: Long) {
|
||||
// try {
|
||||
// PollingChecker(
|
||||
// context = this@ForegroundPollingService,
|
||||
// accountDbId = accountDbId
|
||||
// ).check { a, s ->
|
||||
// val text = "[${a.acct.pretty}]${s.desc}"
|
||||
// CheckerNotification.showMessage(this, text) {
|
||||
// startForeground(NOTIFICATION_ID_FOREGROUND_POLLING, it)
|
||||
// }
|
||||
// }
|
||||
// } catch (ex: Throwable) {
|
||||
// log.trace(ex)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package jp.juggler.subwaytooter.notification
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import jp.juggler.subwaytooter.App1
|
||||
@ -91,10 +92,18 @@ class PollingWorker2(
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAppForehround(): Boolean {
|
||||
val processInfo = ActivityManager.RunningAppProcessInfo()
|
||||
ActivityManager.getMyMemoryState(processInfo)
|
||||
return processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||
}
|
||||
|
||||
private suspend fun showMessage(text: String) =
|
||||
CheckerNotification.showMessage(applicationContext, text) {
|
||||
try {
|
||||
setForeground(ForegroundInfo(NOTIFICATION_ID_POLLING_WORKER, it))
|
||||
if(!isAppForehround()) error("app is not foreground.")
|
||||
setForegroundAsync(ForegroundInfo(NOTIFICATION_ID_POLLING_WORKER,it))
|
||||
.await()
|
||||
} catch (ex: Throwable) {
|
||||
log.e(ex, "showMessage failed.")
|
||||
}
|
||||
|
10
build.gradle
10
build.gradle
@ -7,11 +7,11 @@ buildscript {
|
||||
ext.compile_sdk_version = 33
|
||||
ext.build_tools_version = "33.0.0"
|
||||
|
||||
ext.appcompat_version = "1.5.0"
|
||||
ext.appcompat_version = "1.5.1"
|
||||
ext.lifecycle_version = "2.5.1"
|
||||
ext.arch_version = "2.1.0"
|
||||
|
||||
ext.kotlin_version = '1.7.10'
|
||||
ext.kotlin_version = '1.7.20'
|
||||
ext.kotlinx_coroutines_version = '1.6.4'
|
||||
|
||||
ext.anko_version = '0.10.8'
|
||||
@ -30,13 +30,11 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.2'
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
|
||||
// room のバージョンの影響で google-services を上げられない場合がある
|
||||
//noinspection GradleDependency
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath 'com.google.gms:google-services:4.3.14'
|
||||
|
||||
//noinspection DifferentKotlinGradleVersion
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user