Fix a lot of warnings

This commit is contained in:
Phillip Thelen 2022-05-10 17:04:32 +02:00
parent efa21e28c0
commit a78e5c0034
56 changed files with 195 additions and 302 deletions

View file

@ -46,17 +46,18 @@ dependencies {
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
//Dependency Injection
implementation 'com.google.dagger:dagger:2.40.5'
kapt 'com.google.dagger:dagger-compiler:2.40.5'
implementation 'com.google.dagger:dagger:2.42'
kapt 'com.google.dagger:dagger-compiler:2.42'
compileOnly 'javax.annotation:javax.annotation-api:1.3.2'
//App Compatibility and Material Design
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "androidx.preference:preference:1.2.0"
implementation "androidx.preference:preference-ktx:1.2.0"
//Desugaring
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// Markdown
implementation "io.noties.markwon:core:4.6.2"
@ -69,13 +70,13 @@ dependencies {
// IAP Handling / Verification
implementation "com.android.billingclient:billing-ktx:4.1.0"
//Facebook
implementation('com.facebook.android:facebook-android-sdk:12.2.0') {
implementation('com.facebook.android:facebook-android-sdk:13.2.0') {
transitive = true
}
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1@aar'
//RxJava
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.1.3'
implementation 'io.reactivex.rxjava3:rxjava:3.1.4'
implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
implementation "com.github.akarnokd:rxjava3-bridge:3.0.2"
@ -87,13 +88,13 @@ dependencies {
implementation("io.coil-kt:coil-gif:1.4.0")
//Tests
testImplementation 'io.kotest:kotest-runner-junit5:5.0.3'
testImplementation 'io.kotest:kotest-runner-junit5:5.3.0'
testImplementation 'androidx.test:core:1.4.0'
testImplementation "io.mockk:mockk:1.12.2"
testImplementation "io.mockk:mockk-android:1.12.2"
testImplementation 'io.kotest:kotest-assertions-core:5.0.3'
testImplementation 'io.kotest:kotest-framework-datatest:4.6.2'
androidTestImplementation ('com.kaspersky.android-components:kaspresso:1.4.0') {
testImplementation 'io.mockk:mockk:1.12.3'
testImplementation 'io.mockk:mockk-android:1.12.3'
testImplementation 'io.kotest:kotest-assertions-core:5.3.0'
testImplementation 'io.kotest:kotest-framework-datatest:5.3.0'
androidTestImplementation ('com.kaspersky.android-components:kaspresso:1.4.1') {
exclude module: "protobuf-lite"
}
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
@ -103,8 +104,8 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test:core-ktx:1.4.0'
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'
androidTestImplementation "io.mockk:mockk-android:1.12.2"
androidTestImplementation 'io.kotest:kotest-assertions-core:5.0.3'
androidTestImplementation 'io.mockk:mockk-android:1.12.3'
androidTestImplementation 'io.kotest:kotest-assertions-core:5.3.0'
androidTestUtil("androidx.test:orchestrator:1.4.1")
implementation 'androidx.activity:activity-compose:1.4.0'
@ -115,9 +116,9 @@ dependencies {
implementation "com.google.accompanist:accompanist-appcompat-theme:0.16.0"
//Leak Detection
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
//Push Notifications
implementation platform('com.google.firebase:firebase-bom:29.0.0')
implementation platform('com.google.firebase:firebase-bom:30.0.0')
implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-core'
implementation 'com.google.firebase:firebase-messaging-ktx'
@ -135,19 +136,19 @@ dependencies {
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'
implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation "androidx.paging:paging-runtime-ktx:3.1.1"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
implementation 'com.willowtreeapps:signinwithapplebutton:0.3'
implementation project(':shared')
ktlint("com.pinterest:ktlint:0.45.0") {
ktlint('com.pinterest:ktlint:0.45.2') {
attributes {
attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL))
}
}
androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.6.20"
androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.6.21"
}
android {
@ -168,7 +169,7 @@ android {
buildConfigField "String", "TESTING_LEVEL", "\"production\""
resConfigs 'en', 'bg', 'de', 'en-rGB', 'es', 'fr', 'hr-rHR', 'in', 'it', 'iw', 'ja', 'ko', 'lt', 'nl', 'pl', 'pt-rBR', 'pt-rPT', 'ru', 'tr', 'zh', 'zh-rTW'
versionCode 3501
versionCode 3502
versionName "3.6"
targetSdkVersion 32

View file

@ -195,15 +195,6 @@
android:layout_marginTop="@dimen/spacing_xlarge"
style="@style/LoginButton"/>
<Button
android:id="@+id/fb_login_button"
android:layout_width="match_parent"
android:layout_marginTop="@dimen/spacing_xlarge"
android:layout_height="@dimen/diamond_button_height"
android:text="@string/login_btn_fb"
android:drawableStart="@drawable/facebook_icon"
style="@style/LoginButton"/>
<Button
android:id="@+id/google_login_button"
android:layout_width="match_parent"

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<!-- TODO Remove the following "exclude" elements to make them a part of the auto backup -->
<!-- Exclude the shared preferences file that contains the GCM registrationId -->
<exclude
domain="sharedpref"

View file

@ -121,8 +121,9 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
val configuration: Configuration = resources.configuration
val languageHelper = LanguageHelper(sharedPrefs.getString("language", "en"))
if (if (SDK_INT >= Build.VERSION_CODES.N) {
configuration.locales.isEmpty || configuration.locales[0] != languageHelper.locale
} else {
configuration.locales.isEmpty || configuration.locales[0] != languageHelper.locale
} else {
@Suppress("DEPRECATION")
configuration.locale != languageHelper.locale
}
) {

View file

@ -248,7 +248,7 @@ interface ApiClient {
fun retrieveInboxMessages(uuid: String, page: Int): Flowable<List<ChatMessage>>
fun retrieveInboxConversations(): Flowable<List<InboxConversation>>
fun <T> configureApiCallObserver(): FlowableTransformer<HabitResponse<T>, T>
fun <T : Any> configureApiCallObserver(): FlowableTransformer<HabitResponse<T>, T>
fun openMysteryItem(): Flowable<Equipment>

View file

@ -33,7 +33,7 @@ interface TaskRepository : BaseRepository {
up: Boolean,
force: Boolean,
notifyFunc: ((TaskScoringResult) -> Unit)?
): Maybe<TaskScoringResult?>
): Maybe<TaskScoringResult>
fun scoreChecklistItem(taskId: String, itemId: String): Flowable<Task>
fun getTask(taskId: String): Flowable<Task>

View file

@ -50,8 +50,8 @@ interface UserRepository : BaseRepository {
fun changeClass(selectedClass: String): Flowable<User>
fun unlockPath(user: User?, path: String, price: Int): Flowable<UnlockResponse>
fun unlockPath(user: User?, customization: Customization): Flowable<UnlockResponse>
fun unlockPath(path: String, price: Int): Flowable<UnlockResponse>
fun unlockPath(customization: Customization): Flowable<UnlockResponse>
fun runCron(tasks: MutableList<Task>)
fun runCron()

View file

@ -2,7 +2,6 @@ package com.habitrpg.android.habitica.data.implementation
import android.content.Context
import com.amplitude.api.Amplitude
import com.facebook.FacebookSdk.getCacheDir
import com.google.gson.JsonSyntaxException
import com.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.HabiticaBaseApplication
@ -12,6 +11,7 @@ import com.habitrpg.android.habitica.api.GSonFactoryCreator
import com.habitrpg.android.habitica.api.HostConfig
import com.habitrpg.android.habitica.api.Server
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.extensions.filterMap
import com.habitrpg.android.habitica.helpers.NotificationsManager
import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.android.habitica.models.ContentResult
@ -66,6 +66,7 @@ import retrofit2.HttpException
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.io.IOException
import java.net.SocketException
import java.net.SocketTimeoutException
@ -89,8 +90,7 @@ class ApiClientImpl(
private val apiCallTransformer = FlowableTransformer<HabitResponse<Any>, Any> { observable ->
observable
.filter { it.data != null }
.map { habitResponse ->
.filterMap { habitResponse ->
habitResponse.notifications?.let {
notificationsManager.setNotifications(it)
}
@ -127,7 +127,7 @@ class ApiClientImpl(
val cacheSize: Long = 10 * 1024 * 1024 // 10 MB
val cache = getCacheDir()?.let { Cache(it, cacheSize) }
val cache = Cache(File(context.cacheDir, "http_cache"), cacheSize)
val client = OkHttpClient.Builder()
.cache(cache)
@ -276,12 +276,11 @@ class ApiClientImpl(
val tasksObservable = this.tasks
userObservable = Flowable.zip(
userObservable, tasksObservable,
{ habitRPGUser, tasks ->
habitRPGUser.tasks = tasks
habitRPGUser
}
)
userObservable, tasksObservable
) { habitRPGUser, tasks ->
habitRPGUser.tasks = tasks
habitRPGUser
}
}
return userObservable
}
@ -330,7 +329,8 @@ class ApiClientImpl(
See here for more info: http://blog.danlew.net/2015/03/02/dont-break-the-chain/
*/
override fun <T> configureApiCallObserver(): FlowableTransformer<HabitResponse<T>, T> {
override fun <T : Any> configureApiCallObserver(): FlowableTransformer<HabitResponse<T>, T> {
@Suppress("UNCHECKED_CAST")
return apiCallTransformer as FlowableTransformer<HabitResponse<T>, T>
}

View file

@ -171,16 +171,16 @@ class InventoryRepositoryImpl(
val liveUser = localRepository.getLiveUser(userID)
if (liveUser != null) {
localRepository.modify(liveUser) { liveUser ->
localRepository.modify(liveUser) { user ->
if (type == "mount") {
liveUser.items?.currentMount = key
user.items?.currentMount = key
} else if (type == "pet") {
liveUser.items?.currentPet = key
user.items?.currentPet = key
}
val outfit = if (type == "costume") {
liveUser.items?.gear?.costume
user.items?.gear?.costume
} else {
liveUser.items?.gear?.equipped
user.items?.gear?.equipped
}
when (key.split("_").firstOrNull()) {
"weapon" -> outfit?.weapon = key

View file

@ -191,7 +191,7 @@ class TaskRepositoryImpl(
up: Boolean,
force: Boolean,
notifyFunc: ((TaskScoringResult) -> Unit)?
): Maybe<TaskScoringResult?> {
): Maybe<TaskScoringResult> {
return localRepository.getTask(taskId).firstElement()
.flatMap { task -> taskChecked(user, task, up, force, notifyFunc).singleElement() }
}

View file

@ -6,7 +6,8 @@ import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.data.local.UserLocalRepository
import com.habitrpg.android.habitica.data.local.UserQuestStatus
import com.habitrpg.android.habitica.extensions.skipNull
import com.habitrpg.android.habitica.extensions.Optional
import com.habitrpg.android.habitica.extensions.filterMapEmpty
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.Achievement
@ -161,7 +162,7 @@ class UserRepositoryImpl(
override fun changeClass(selectedClass: String): Flowable<User> = apiClient.changeClass(selectedClass)
.flatMap { retrieveUser(false) }
override fun unlockPath(user: User?, path: String, price: Int): Flowable<UnlockResponse> {
override fun unlockPath(path: String, price: Int): Flowable<UnlockResponse> {
return zipWithLiveUser(apiClient.unlockPath(path)) { unlockResponse, copiedUser ->
val user = localRepository.getUnmanagedCopy(copiedUser)
user.preferences = unlockResponse.preferences
@ -173,12 +174,8 @@ class UserRepositoryImpl(
}
}
override fun unlockPath(user: User?, customization: Customization): Flowable<UnlockResponse> {
var path = customization.path
if (path.last() == '.' && customization.type == "background") {
path += user?.preferences?.background
}
return unlockPath(user, path, customization.price ?: 0)
override fun unlockPath(customization: Customization): Flowable<UnlockResponse> {
return unlockPath(customization.path, customization.price ?: 0)
}
override fun runCron() {
@ -323,22 +320,22 @@ class UserRepositoryImpl(
if (appConfigManager.enableLocalChanges()) {
localRepository.getUser(userID).firstElement().subscribe(
{ liveUser ->
localRepository.modify(liveUser) { liveUser ->
localRepository.modify(liveUser) { user ->
when (type) {
"skin" -> liveUser.preferences?.skin = identifier
"shirt" -> liveUser.preferences?.shirt = identifier
"skin" -> user.preferences?.skin = identifier
"shirt" -> user.preferences?.shirt = identifier
"hair" -> {
when (category) {
"color" -> liveUser.preferences?.hair?.color = identifier
"flower" -> liveUser.preferences?.hair?.flower = identifier.toInt()
"mustache" -> liveUser.preferences?.hair?.mustache = identifier.toInt()
"beard" -> liveUser.preferences?.hair?.beard = identifier.toInt()
"bangs" -> liveUser.preferences?.hair?.bangs = identifier.toInt()
"base" -> liveUser.preferences?.hair?.base = identifier.toInt()
"color" -> user.preferences?.hair?.color = identifier
"flower" -> user.preferences?.hair?.flower = identifier.toInt()
"mustache" -> user.preferences?.hair?.mustache = identifier.toInt()
"beard" -> user.preferences?.hair?.beard = identifier.toInt()
"bangs" -> user.preferences?.hair?.bangs = identifier.toInt()
"base" -> user.preferences?.hair?.base = identifier.toInt()
}
}
"background" -> liveUser.preferences?.background = identifier
"chair" -> liveUser.preferences?.chair = identifier
"background" -> user.preferences?.background = identifier
"chair" -> user.preferences?.chair = identifier
}
}
},
@ -401,11 +398,11 @@ class UserRepositoryImpl(
private fun getLiveUser(): Flowable<User> {
return localRepository.getUser(userID)
.map { localRepository.getLiveObject(it) }
.skipNull()
.map { Optional(localRepository.getLiveObject(it)) }
.filterMapEmpty()
}
private fun <T> zipWithLiveUser(flowable: Flowable<T>, mergeFunc: BiFunction<T, User, T>): Flowable<T> {
private fun <T : Any> zipWithLiveUser(flowable: Flowable<T>, mergeFunc: BiFunction<T, User, T>): Flowable<T> {
return Flowable.zip(flowable, getLiveUser().firstElement().toFlowable(), mergeFunc)
}

View file

@ -13,7 +13,7 @@ interface TaskLocalRepository : BaseLocalRepository {
fun getTasks(taskType: TaskType, userID: String): Flowable<out List<Task>>
fun getTasks(userId: String): Flowable<out List<Task>>
fun saveTasks(userId: String, tasksOrder: TasksOrder, tasks: TaskList)
fun saveTasks(ownerID: String, tasksOrder: TasksOrder, tasks: TaskList)
fun deleteTask(taskID: String)

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.data.local.implementation
import com.habitrpg.android.habitica.data.local.BaseLocalRepository
import com.habitrpg.android.habitica.extensions.filterMap
import com.habitrpg.android.habitica.models.BaseMainObject
import com.habitrpg.android.habitica.models.BaseObject
import com.habitrpg.android.habitica.models.user.User
@ -93,6 +94,7 @@ abstract class RealmBaseLocalRepository internal constructor(override var realm:
if (isClosed) return null
if (obj !is RealmObject || !obj.isManaged) return obj
val baseObject = obj as? BaseMainObject ?: return null
@Suppress("UNCHECKED_CAST")
return realm.where(baseObject.realmClass).equalTo(baseObject.primaryIdentifierName, baseObject.primaryIdentifier).findFirst() as? T
}
@ -104,6 +106,6 @@ abstract class RealmBaseLocalRepository internal constructor(override var realm:
.asFlowable()
)
.filter { it.isLoaded && it.isValid && !it.isEmpty() }
.map { it.first() }
.filterMap { it.first() }
}
}

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.data.local.implementation
import com.habitrpg.android.habitica.data.local.ChallengeLocalRepository
import com.habitrpg.android.habitica.extensions.filterMap
import com.habitrpg.android.habitica.models.social.Challenge
import com.habitrpg.android.habitica.models.social.ChallengeMembership
import com.habitrpg.android.habitica.models.tasks.Task
@ -28,7 +29,7 @@ class RealmChallengeLocalRepository(realm: Realm) : RealmBaseLocalRepository(rea
.findAll()
.asFlowable()
.filter { it.isLoaded }
).map { it.first() }
).filterMap { it.first() }
override fun getChallengeMemberships(userId: String): Flowable<out List<ChallengeMembership>> = RxJavaBridge.toV3Flowable(
realm.where(ChallengeMembership::class.java)

View file

@ -1,7 +1,6 @@
package com.habitrpg.android.habitica.data.local.implementation
import com.habitrpg.android.habitica.data.local.ContentLocalRepository
import com.habitrpg.android.habitica.extensions.skipNull
import com.habitrpg.android.habitica.models.ContentResult
import com.habitrpg.android.habitica.models.WorldState
import com.habitrpg.android.habitica.models.inventory.Quest
@ -43,7 +42,6 @@ open class RealmContentLocalRepository(realm: Realm) : RealmBaseLocalRepository(
.filter { it.isLoaded && it.size > 0 }
.map { it.first() }
)
.skipNull()
}
override fun saveWorldState(worldState: WorldState) {

View file

@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.data.local.implementation
import com.habitrpg.android.habitica.data.local.UserLocalRepository
import com.habitrpg.android.habitica.data.local.UserQuestStatus
import com.habitrpg.android.habitica.extensions.filterMap
import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.android.habitica.models.QuestAchievement
import com.habitrpg.android.habitica.models.Skill
@ -27,12 +28,11 @@ class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
.findAll()
.asFlowable()
.filter { groups -> groups.size > 0 }
.map { groups -> groups.first() }
)
).filterMap { it.first() }
}
.map {
when {
it?.quest?.members?.find { questMember -> questMember.key == userID } === null -> UserQuestStatus.NO_QUEST
it.quest?.members?.find { questMember -> questMember.key == userID } === null -> UserQuestStatus.NO_QUEST
it.quest?.progress?.collect?.isNotEmpty() ?: false -> UserQuestStatus.QUEST_COLLECT
it.quest?.progress?.hp ?: 0.0 > 0.0 -> UserQuestStatus.QUEST_BOSS
else -> UserQuestStatus.QUEST_UNKNOWN

View file

@ -5,11 +5,6 @@ import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.functions.Consumer
fun <T> Flowable<T>.subscribeWithErrorHandler(function: Consumer<T>): Disposable {
fun <T : Any> Flowable<T>.subscribeWithErrorHandler(function: Consumer<T>): Disposable {
return subscribe(function, RxErrorHandler.handleEmptyError())
}
fun <T> Flowable<T?>.skipNull(): Flowable<T> {
@Suppress("UNCHECKED_CAST")
return skipWhile { it == null } as? Flowable<T> ?: Flowable.empty()
}
}

View file

@ -2,5 +2,11 @@ package com.habitrpg.android.habitica.extensions
data class Optional<T>(val value: T?) {
val isEmpty = value == null
val assertedValue: T
get() {
assert(!isEmpty)
return value!!
}
}
fun <T> T?.asOptional() = Optional(this)

View file

@ -5,46 +5,58 @@ import io.reactivex.rxjava3.core.Maybe
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
fun <S, T : Optional<S>> Flowable<T>.filterOptionalDoOnEmpty(function: () -> Unit): Flowable<S> {
fun <S : Any, T : Optional<S>> Flowable<T>.filterOptionalDoOnEmpty(function: () -> Unit): Flowable<S> {
return this.doOnNext { if (it.isEmpty) function() }
.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}
fun <S, T : Optional<S>> Flowable<T>.filterMapEmpty(): Flowable<S> {
fun <S : Any, T : Optional<S>> Flowable<T>.filterMapEmpty(): Flowable<S> {
return this.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}
fun <S, T : Optional<S>> Observable<T>.filterOptionalDoOnEmpty(function: () -> Unit): Observable<S> {
fun <S : Any, T : Any> Flowable<T>.filterMap(function: (T) -> S?): Flowable<S> {
return this.map { Optional(function(it)) }
.filter { !it.isEmpty }
.map { it.assertedValue }
}
fun <S : Any, T : Optional<S>> Observable<T>.filterOptionalDoOnEmpty(function: () -> Unit): Observable<S> {
return this.doOnNext { if (it.isEmpty) function() }
.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}
fun <S, T : Optional<S>> Observable<T>.filterMapEmpty(): Observable<S> {
fun <S : Any, T : Optional<S>> Observable<T>.filterMapEmpty(): Observable<S> {
return this.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}
fun <S : Any, T : Any> Observable<T>.filterMap(function: (T) -> S?): Observable<S> {
return this.map { Optional(function(it)) }
.filter { !it.isEmpty }
.map { it.assertedValue }
}
fun <S, T : Optional<S>> Single<T>.filterOptionalDoOnEmpty(function: () -> Unit): Maybe<S> {
return this.doOnSuccess { if (it.isEmpty) function() }
.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}
fun <S, T : Optional<S>> Single<T>.filterMapEmpty(): Maybe<S> {
return this.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}
fun <S, T : Optional<S>> Maybe<T>.filterOptionalDoOnEmpty(function: () -> Unit): Maybe<S> {
return this.doOnSuccess { if (it.isEmpty) function() }
.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}
fun <S, T : Optional<S>> Maybe<T>.filterMapEmpty(): Maybe<S> {
return this.filter { !it.isEmpty }
.map { it.value }
.map { it.assertedValue }
}

View file

@ -5,7 +5,7 @@ import java.util.Date
class AprilFoolsHandler {
companion object {
var eventEnd = Date(2020, 3, 1)
private var eventEnd: Date? = null
fun handle(name: String?, endDate: Date?) {
if (endDate != null) {

View file

@ -11,15 +11,15 @@ import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import androidx.annotation.WorkerThread
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.util.Locale
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
/*
* Copyright (C) 2017 Jared Rummler
@ -120,32 +120,18 @@ object DeviceName {
* @see .getDeviceName
*/
val deviceName: String?
get() = getDeviceName(Build.DEVICE, Build.MODEL, Build.MODEL.capitalize(Locale.getDefault()))
get() = getDeviceName(Build.MODEL.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() })
/**
* Get the consumer friendly name of a device.
*
* @param codename the value of the system property "ro.product.device" ([Build.DEVICE])
* *or*
* the value of the system property "ro.product.model" ([Build.MODEL])
* @param fallback the fallback name if the device is unknown. Usually the value of the system property
* "ro.product.model" ([Build.MODEL])
* @return the market name of a device or `fallback` if the device is unknown.
*/
fun getDeviceName(codename: String?, fallback: String?): String? {
return getDeviceName(codename, codename, fallback)
}
/**
* Get the consumer friendly name of a device.
*
* @param codename the value of the system property "ro.product.device" ([Build.DEVICE]).
* @param model the value of the system property "ro.product.model" ([Build.MODEL]).
* @param fallback the fallback name if the device is unknown. Usually the value of the system property
* "ro.product.model" ([Build.MODEL])
* @return the market name of a device or `fallback` if the device is unknown.
*/
fun getDeviceName(codename: String?, model: String?, fallback: String?): String? {
private fun getDeviceName(fallback: String?): String? {
val codename = Build.DEVICE
val model = Build.MODEL
// ----------------------------------------------------------------------------
// Google
if (codename != null && codename == "walleye") {
@ -371,20 +357,6 @@ object DeviceName {
return getDeviceInfo(context.applicationContext, Build.DEVICE, Build.MODEL)
}
/**
* Get the [DeviceInfo] for the current device. Do not run on the UI thread, as this may
* download JSON to retrieve the [DeviceInfo]. JSON is only downloaded once and then
* stored to [SharedPreferences].
*
* @param context the application context.
* @param codename the codename of the device
* @return [DeviceInfo] for the current device.
*/
@WorkerThread
fun getDeviceInfo(context: Context, codename: String?): DeviceInfo {
return getDeviceInfo(context, codename, null)
}
/**
* Get the [DeviceInfo] for the current device. Do not run on the UI thread, as this may
* download JSON to retrieve the [DeviceInfo]. JSON is only downloaded once and then
@ -502,18 +474,6 @@ object DeviceName {
return this
}
/**
* Set the device model to query. You should also set the codename.
*
* @param model the value of the system property "ro.product.model"
* @return This Request object to allow for chaining of calls to set methods.
* @see Build.MODEL
*/
fun setModel(model: String?): Request {
this.model = model
return this
}
/**
* Download information about the device. This saves the results in shared-preferences so
* future requests will not need a network connection.
@ -601,6 +561,6 @@ object DeviceName {
val name: String?
get() = if (!TextUtils.isEmpty(marketName)) {
marketName
} else model?.capitalize(Locale.getDefault())
} else model?.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
}
}

View file

@ -167,7 +167,7 @@ class TaskAlarmManager(
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, 1)
notificationIntent.putExtra(NotificationPublisher.CHECK_DAILIES, false)
val alarmManager = context?.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val previousSender = PendingIntent.getBroadcast(
context,
0,
@ -186,9 +186,7 @@ class TaskAlarmManager(
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
)
if (context != null) {
setAlarm(context, triggerTime, pendingIntent)
}
setAlarm(context, triggerTime, pendingIntent)
}
}

View file

@ -50,7 +50,7 @@ class GroupActivityNotification(context: Context, identifier: String?) : Habitic
private fun makeMessageFromData(data: Map<String, String>): NotificationCompat.MessagingStyle.Message {
val sender = Person.Builder().setName(data["senderName"]).build()
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US)
val timestamp = dateFormat.parse(data["timestamp"]) ?: Date()
val timestamp = data["timestamp"]?.let { dateFormat.parse(it) } ?: Date()
val messageText = EmojiParser.parseEmojis(data["message"]?.trim { it <= ' ' })
return NotificationCompat.MessagingStyle.Message(
messageText,

View file

@ -71,7 +71,7 @@ class PushNotificationManager(
val notificationFactory = HabiticaLocalNotificationFactory()
val notification = notificationFactory.build(remoteMessageIdentifier, context)
if (userIsSubscribedToNotificationType(remoteMessageIdentifier) && notification != null) {
if (userIsSubscribedToNotificationType(remoteMessageIdentifier)) {
if (remoteMessage.data.containsKey("sendAnalytics")) {
val additionalData = HashMap<String, Any>()
additionalData["identifier"] = remoteMessageIdentifier ?: ""

View file

@ -14,6 +14,7 @@ import androidx.core.util.Pair
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.executors.PostExecutionThread
import com.habitrpg.android.habitica.extensions.filterMap
import com.habitrpg.android.habitica.extensions.round
import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User
@ -48,7 +49,7 @@ constructor(
if (requestValues.hasLeveledUp == true) {
return@defer levelUpUseCase.observable(LevelUpUseCase.RequestValues(requestValues.user, requestValues.level, requestValues.context, requestValues.snackbarTargetView))
.flatMap { userRepository.retrieveUser(true) }
.map { it.stats }
.filterMap { it.stats }
} else {
return@defer Flowable.just(stats)
}

View file

@ -85,13 +85,13 @@ class ShowNotificationInteractor(
val view = factory.inflate(R.layout.dialog_login_incentive, null)
val imageView = view.findViewById(R.id.imageView) as? ImageView
var imageKey = notificationData?.rewardKey?.get(0)
var imageKey = notificationData.rewardKey?.get(0)
if (imageKey?.contains("armor") == true) {
imageKey = "slim_$imageKey"
}
DataBindingUtils.loadImage(imageView, imageKey)
val youEarnedMessage = activity.getString(R.string.checkInRewardEarned, notificationData?.rewardText)
val youEarnedMessage = activity.getString(R.string.checkInRewardEarned, notificationData.rewardText)
val youEarnedTexView = view.findViewById(R.id.you_earned_message) as? TextView
youEarnedTexView?.text = youEarnedMessage

View file

@ -73,9 +73,9 @@ class UserRepositoryModule {
return TaskRepositoryImpl(
localRepository,
apiClient,
userId!!,
appConfigManager!!,
analyticsManager!!
userId,
appConfigManager,
analyticsManager
)
}

View file

@ -43,7 +43,7 @@ class AvatarView : FrameLayout {
private var avatarRectF: RectF? = null
private val avatarMatrix = Matrix()
private val numberLayersInProcess = AtomicInteger(0)
private var avatarImageConsumer: Consumer<Bitmap?>? = null
private var avatarImageConsumer: ((Bitmap?) -> Unit)? = null
private var avatarBitmap: Bitmap? = null
private var avatarCanvas: Canvas? = null
private var currentLayers: Map<LayerType, String>? = null
@ -405,14 +405,14 @@ class AvatarView : FrameLayout {
private fun onLayerComplete() {
if (numberLayersInProcess.decrementAndGet() == 0) {
avatarImageConsumer?.accept(avatarImage)
avatarImageConsumer?.invoke(avatarImage)
}
}
fun onAvatarImageReady(consumer: Consumer<Bitmap?>) {
fun onAvatarImageReady(consumer: ((Bitmap?) -> Unit)) {
avatarImageConsumer = consumer
if (imageViewHolder.size > 0 && numberLayersInProcess.get() == 0) {
avatarImageConsumer?.accept(avatarImage)
avatarImageConsumer?.invoke(avatarImage)
} else {
initAvatarRectMatrix()
showLayers(layerMap)

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.ui
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.WindowManager
@ -47,9 +48,15 @@ class MaxHeightLinearLayout : LinearLayout {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var heightMeasurement = heightMeasureSpec
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as? WindowManager
windowManager?.defaultDisplay?.getMetrics(displaymetrics)
val height = (displaymetrics.heightPixels * maxHeight).toInt()
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val height = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
windowManager.currentWindowMetrics.bounds.height()
} else {
@Suppress("DEPRECATION")
windowManager.defaultDisplay?.getMetrics(displaymetrics)
(displaymetrics.heightPixels * maxHeight).toInt()
}
heightMeasurement = min(heightMeasurement, MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST))
super.onMeasure(widthMeasureSpec, heightMeasurement)

View file

@ -27,6 +27,7 @@ import com.plattysoft.leonids.ParticleSystem
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
import com.habitrpg.android.habitica.ui.helpers.loadImage
class ArmoireActivity: BaseActivity() {

View file

@ -349,9 +349,8 @@ class ChallengeFormActivity : BaseActivity() {
return@flatMap Flowable.empty<Group>()
}
socialRepository.retrieveGroup(it)
},
{ user, groups -> Pair(user, groups) }
)
}
) { user, groups -> Pair(user, groups) }
.subscribe(
{ groups ->
val mutableGroups = groups.first.toMutableList()

View file

@ -264,7 +264,7 @@ class ClassSelectionActivity : BaseActivity(), Consumer<User> {
}
private fun displayProgressDialog(progressText: String) {
val dialog = HabiticaProgressDialog.show(this, progressText)
HabiticaProgressDialog.show(this, progressText)
}
override fun accept(user: User) {

View file

@ -14,6 +14,7 @@ import android.widget.TableRow
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope
import coil.load
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@ -40,14 +41,13 @@ import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import io.reactivex.rxjava3.core.Flowable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import javax.inject.Inject
import kotlin.math.floor
import kotlin.math.min
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class FullProfileActivity : BaseActivity() {
private var blocks: List<String> = listOf()
@ -193,7 +193,7 @@ class FullProfileActivity : BaseActivity() {
private fun showSendMessageToUserDialog() {
finish()
GlobalScope.launch(context = Dispatchers.Main) {
lifecycleScope.launch(context = Dispatchers.Main) {
delay(1000L)
MainNavigationController.navigate(R.id.inboxMessageListFragment, bundleOf(Pair("username", username), Pair("userID", userID)))
}

View file

@ -115,10 +115,6 @@ class LoginActivity : BaseActivity() {
// Set default values to avoid null-responses when requesting unedited settings
PreferenceManager.setDefaultValues(this, R.xml.preferences_fragment, false)
viewModel.setupFacebookLogin {
handleAuthResponse(it)
}
binding.loginBtn.setOnClickListener(loginClick)
val content = SpannableString(binding.forgotPassword.text)
@ -141,7 +137,6 @@ class LoginActivity : BaseActivity() {
binding.showLoginButton.setOnClickListener { showLoginButtonClicked() }
binding.backButton.setOnClickListener { backButtonClicked() }
binding.forgotPassword.setOnClickListener { onForgotPasswordClicked() }
binding.fbLoginButton.setOnClickListener { viewModel.handleFacebookLogin(this) }
binding.googleLoginButton.setOnClickListener { viewModel.handleGoogleLogin(this, pickAccountResult) }
binding.appleLoginButton.setOnClickListener {
viewModel.connectApple(supportFragmentManager) {
@ -213,7 +208,6 @@ class LoginActivity : BaseActivity() {
binding.password.setAutofillHints("newPassword")
}
binding.password.imeOptions = EditorInfo.IME_ACTION_NEXT
binding.fbLoginButton.visibility = View.GONE
binding.googleLoginButton.setText(R.string.register_btn_google)
} else {
binding.loginBtn.text = getString(R.string.login_btn)
@ -223,8 +217,6 @@ class LoginActivity : BaseActivity() {
binding.password.setAutofillHints("password")
}
binding.password.imeOptions = EditorInfo.IME_ACTION_DONE
binding.fbLoginButton.setText(R.string.login_btn_fb)
binding.fbLoginButton.visibility = if (configManager.hideFacebook()) View.GONE else View.VISIBLE
binding.googleLoginButton.setText(R.string.login_btn_google)
}
this.resetLayout()
@ -263,13 +255,6 @@ class LoginActivity : BaseActivity() {
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
viewModel.onActivityResult(requestCode, resultCode, data) {
handleAuthResponse(it)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_toggleRegistering -> toggleRegistering()

View file

@ -35,6 +35,7 @@ import com.habitrpg.android.habitica.extensions.dpToPx
import com.habitrpg.android.habitica.extensions.getThemeColor
import com.habitrpg.android.habitica.extensions.hideKeyboard
import com.habitrpg.android.habitica.extensions.isUsingNightModeResources
import com.habitrpg.android.habitica.extensions.observeOnce
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.extensions.updateStatusBarColor
import com.habitrpg.android.habitica.helpers.AdHandler

View file

@ -35,7 +35,7 @@ class SkillsRecyclerViewAdapter : RecyclerView.Adapter<SkillsRecyclerViewAdapter
field = value
notifyDataSetChanged()
}
var specialItems: RealmList<OwnedItem>?? = null
var specialItems: RealmList<OwnedItem>? = null
set(value) {
field = value
notifyDataSetChanged()

View file

@ -74,7 +74,7 @@ class TaskSetupAdapter : RecyclerView.Adapter<TaskSetupAdapter.TaskViewHolder>()
}
override fun onClick(v: View) {
val position = this.adapterPosition
val position = this.bindingAdapterPosition
checkedList[position] = !checkedList[position]
notifyItemChanged(position)
}

View file

@ -147,7 +147,8 @@ class SystemChatMessageViewHolder(itemView: View) : RecyclerView.ViewHolder(item
fun bind(chatMessage: ChatMessage?, isExpanded: Boolean) {
textView.text = chatMessage?.text?.removePrefix("`")?.removeSuffix("`")
timestamp.text = dateTime.format(chatMessage?.timestamp?.let { java.util.Date(it) })
timestamp.text = chatMessage?.timestamp?.let { java.util.Date(it) }
?.let { dateTime.format(it) }
if (isExpanded) {
binding.systemMessageTimestamp.visibility = View.VISIBLE
} else {

View file

@ -128,7 +128,11 @@ class GuildListAdapter : BaseRecyclerViewAdapter<Group, RecyclerView.ViewHolder>
binding.tagWrapper.removeAllViews()
guild.categories?.forEach { category ->
val textView = PillTextviewBinding.inflate(itemView.context.layoutInflater, binding.tagWrapper, true)
textView.root.text = category.name?.split("_")?.joinToString(" ") { it.capitalize(Locale.getDefault()) }
textView.root.text = category.name?.split("_")?.joinToString(" ") { it.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(
Locale.getDefault()
) else it.toString()
} }
textView.root.background = if (category.slug == "habitica_official") {
textView.root.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
ContextCompat.getDrawable(itemView.context, R.drawable.pill_bg_purple_400)

View file

@ -78,7 +78,7 @@ class AvatarCustomizationFragment :
adapter.getSelectCustomizationEvents()
.flatMap { customization ->
if (customization.type == "background") {
userRepository.unlockPath(userViewModel.user.value, customization)
userRepository.unlockPath(customization)
.flatMap { userRepository.retrieveUser(false, true, true) }
} else {
userRepository.useCustomization(customization.type ?: "", customization.category, customization.identifier ?: "")
@ -103,7 +103,6 @@ class AvatarCustomizationFragment :
binding?.refreshLayout?.setOnRefreshListener(this)
layoutManager.justifyContent = JustifyContent.CENTER
layoutManager.alignItems = AlignItems.FLEX_START
setGridSpanCount(view.width)
binding?.recyclerView?.layoutManager = layoutManager
binding?.recyclerView?.addItemDecoration(MarginDecoration(context))
@ -212,16 +211,6 @@ class AvatarCustomizationFragment :
}
}
private fun setGridSpanCount(width: Int) {
val itemWidth = context?.resources?.getDimension(R.dimen.customization_width) ?: 0F
var spanCount = (width / itemWidth).toInt()
if (spanCount == 0) {
spanCount = 1
} else if (type == "backgrounds") {
spanCount = 3
}
}
fun updateUser(user: User?) {
if (user == null) return
this.updateActiveCustomization(user)

View file

@ -342,7 +342,7 @@ class ItemRecyclerFragment : BaseFragment<FragmentItemsBinding>(), SwipeRefreshL
compositeSubscription.add(
observable.subscribe(
{ skillResponse -> this.displaySpecialItemResult(specialItem) },
{ this.displaySpecialItemResult(specialItem) },
RxErrorHandler.handleEmptyError()
)
)

View file

@ -206,13 +206,6 @@ class AccountPreferenceFragment :
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
viewModel.onActivityResult(requestCode, resultCode, data) {
viewModel.handleAuthResponse(it)
}
}
private val pickAccountResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
viewModel.googleEmail = it?.data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)

View file

@ -50,7 +50,7 @@ class TaskSetupFragment : BaseFragment<FragmentSetupTasksBinding>() {
this.updateAvatar()
}
binding?.heartIcon?.setImageDrawable(BitmapDrawable(HabiticaIconsHelper.imageOfHeartLightBg()))
binding?.heartIcon?.setImageDrawable(BitmapDrawable(resources, HabiticaIconsHelper.imageOfHeartLightBg()))
}
override fun onResume() {

View file

@ -21,7 +21,12 @@ import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBind
import com.habitrpg.android.habitica.extensions.observeOnce
import com.habitrpg.android.habitica.extensions.setScaledPadding
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.*
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.HapticFeedbackManager
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.NotificationsManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.SoundManager
import com.habitrpg.android.habitica.models.responses.TaskDirection
import com.habitrpg.android.habitica.models.responses.TaskScoringResult
import com.habitrpg.android.habitica.models.tasks.Task
@ -121,7 +126,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
RxErrorHandler.handleEmptyError()
)?.let { recyclerSubscription.add(it) }
recyclerAdapter?.taskOpenEvents?.subscribeWithErrorHandler {
openTaskForm(it.first, it.second)
openTaskForm(it.first)
}?.let { recyclerSubscription.add(it) }
recyclerAdapter?.taskScoreEvents
?.doOnNext {
@ -485,7 +490,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
}
}
private fun openTaskForm(task: Task, containerView: View) {
private fun openTaskForm(task: Task) {
if (Date().time - (TasksFragment.lastTaskFormOpen?.time ?: 0) < 2000 || !task.isValid || !canEditTasks) {
return
}

View file

@ -125,7 +125,7 @@ class PetViewHolder(
}
}
menu.setSelectionRunnable { index ->
val pet = animal as? Pet ?: return@setSelectionRunnable
val pet = animal ?: return@setSelectionRunnable
when (index) {
0 -> {
animal?.let {

View file

@ -124,7 +124,7 @@ abstract class ChecklistedViewHolder(
textView?.text = item.text
textView?.setTextColor(ContextCompat.getColor(context, if (item.completed) R.color.text_dimmed else R.color.text_secondary))
if (item.text != null) {
Observable.just(item.text)
Observable.just(item.text ?: "")
.map { MarkdownParser.parseMarkdown(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -188,11 +188,11 @@ abstract class ChecklistedViewHolder(
}
private fun onChecklistIndicatorClicked() {
expandedChecklistRow = if (this.shouldDisplayExpandedChecklist()) null else adapterPosition
expandedChecklistRow = if (this.shouldDisplayExpandedChecklist()) null else bindingAdapterPosition
if (this.shouldDisplayExpandedChecklist()) {
val recyclerView = this.checklistView.parent.parent as? RecyclerView
val layoutManager = recyclerView?.layoutManager as? LinearLayoutManager
layoutManager?.scrollToPositionWithOffset(this.adapterPosition, 15)
layoutManager?.scrollToPositionWithOffset(this.bindingAdapterPosition, 15)
}
updateChecklistDisplay()
}
@ -210,7 +210,7 @@ abstract class ChecklistedViewHolder(
}
private fun shouldDisplayExpandedChecklist(): Boolean {
return expandedChecklistRow != null && adapterPosition == expandedChecklistRow
return expandedChecklistRow != null && bindingAdapterPosition == expandedChecklistRow
}
private fun onCheckedChanged(isChecked: Boolean) {

View file

@ -9,13 +9,6 @@ import android.os.Build
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.edit
import androidx.fragment.app.FragmentManager
import com.facebook.AccessToken
import com.facebook.CallbackManager
import com.facebook.FacebookCallback
import com.facebook.FacebookException
import com.facebook.FacebookSdk
import com.facebook.login.LoginManager
import com.facebook.login.LoginResult
import com.google.android.gms.auth.GoogleAuthException
import com.google.android.gms.auth.GoogleAuthUtil
import com.google.android.gms.auth.GooglePlayServicesAvailabilityException
@ -33,7 +26,6 @@ import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.helpers.KeyHelper
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.SignInWithAppleResult
import com.habitrpg.android.habitica.helpers.SignInWithAppleService
import com.habitrpg.android.habitica.models.auth.UserAuthResponse
@ -65,9 +57,7 @@ class AuthenticationViewModel() {
private var compositeSubscription = CompositeDisposable()
private var callbackManager = CallbackManager.Factory.create()
var googleEmail: String? = null
private var loginManager = LoginManager.getInstance()
init {
HabiticaBaseApplication.userComponent?.inject(this)
@ -96,56 +86,6 @@ class AuthenticationViewModel() {
}.show()
}
fun setupFacebookLogin(onSuccess: (UserAuthResponse) -> Unit) {
callbackManager = CallbackManager.Factory.create()
loginManager.registerCallback(
callbackManager,
object : FacebookCallback<LoginResult> {
override fun onSuccess(result: LoginResult) {
val accessToken = AccessToken.getCurrentAccessToken()
compositeSubscription.add(
apiClient.connectSocial("facebook", accessToken?.userId ?: "", accessToken?.token ?: "")
.subscribe({
onSuccess(it)
}, RxErrorHandler.handleEmptyError())
)
}
override fun onCancel() { /* no-on */ }
override fun onError(error: FacebookException) {
RxErrorHandler.reportError(error)
}
}
)
}
fun handleFacebookLogin(activity: Activity) {
loginManager.logInWithReadPermissions(activity, listOf("user_friends"))
}
fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?,
onSuccess: (UserAuthResponse) -> Unit
) {
callbackManager.onActivityResult(requestCode, resultCode, data)
if (requestCode == FacebookSdk.getCallbackRequestCodeOffset()) {
// This is necessary because the regular login callback is not called for some reason
val accessToken = AccessToken.getCurrentAccessToken()
if (accessToken?.token != null) {
compositeSubscription.add(
apiClient.connectSocial("facebook", accessToken.userId, accessToken.token)
.subscribe({
onSuccess(it)
}, { })
)
}
}
}
fun handleGoogleLogin(
activity: Activity,
pickAccountResult: ActivityResultLauncher<Intent>

View file

@ -193,7 +193,7 @@ open class NotificationsViewModel : BaseViewModel() {
if (isCustomNotification(notification)) {
if (isCustomNewStuffNotification(notification)) {
customNotifications.onNext(
customNotifications.value?.filterNot { it.id == notification.id }
customNotifications.value?.filterNot { it.id == notification.id } ?: listOf()
)
}
return

View file

@ -63,7 +63,7 @@ class TasksViewModel: BaseViewModel() {
init {
compositeSubscription.add(userRepository.getTeamPlans()
.subscribe({
owners = listOf(Pair(userID ?: "", userViewModel.displayName)) + it.map {
owners = listOf(Pair(userID, userViewModel.displayName)) + it.map {
Pair(
it.id,
it.summary

View file

@ -47,7 +47,7 @@ class ValidatingEditText @JvmOverloads constructor(
if (isEditing) return@setOnFocusChangeListener
showErrorIfNecessary()
}
binding.editText.doOnTextChanged { text, start, before, count ->
binding.editText.doOnTextChanged { text, _, _, _ ->
if (binding.errorText.visibility == View.VISIBLE) {
showErrorIfNecessary(text.toString())
}

View file

@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.ui.views.login
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.View
@ -39,8 +40,13 @@ class LoginBackgroundView(context: Context, attrs: AttributeSet?) : RelativeLayo
init {
val metrics = DisplayMetrics()
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay.getMetrics(metrics)
viewHeight = (metrics.heightPixels * SIZE_FACTOR).toInt()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
viewHeight = windowManager.currentWindowMetrics.bounds.height()
} else {
@Suppress("DEPRECATION")
windowManager.defaultDisplay.getMetrics(metrics)
viewHeight = (metrics.heightPixels * SIZE_FACTOR).toInt()
}
}
override fun onFinishInflate() {

View file

@ -362,7 +362,7 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
} else if (shopItem.purchaseType == "debuffPotion") {
observable = userRepository.useSkill(shopItem.key, null).cast(Any::class.java)
} else if (shopItem.purchaseType == "customization" || shopItem.purchaseType == "background" || shopItem.purchaseType == "customizationSet") {
observable = userRepository.unlockPath(user, item.path ?: "", item.value).cast(Any::class.java)
observable = userRepository.unlockPath(item.path ?: "", item.value).cast(Any::class.java)
} else if (shopItem.purchaseType == "debuffPotion") {
observable = userRepository.useSkill(shopItem.key, null).cast(Any::class.java)
} else if (shopItem.purchaseType == "card") {

View file

@ -22,14 +22,14 @@ class QuestMenuView : LinearLayout {
private var questContent: QuestContent? = null
constructor(context: Context) : super(context) {
setupView(context)
setupView()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
setupView(context)
setupView()
}
private fun setupView(context: Context) {
private fun setupView() {
orientation = VERTICAL
binding.heartIconView.setImageBitmap(HabiticaIconsHelper.imageOfHeartDarkBg())

View file

@ -59,7 +59,7 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() {
this.context = context
if (user == null) {
userRepository.getUser().firstElement()?.subscribe({
userRepository.getUser().firstElement().subscribe({
user = it
updateData(appWidgetIds)
}, RxErrorHandler.handleEmptyError())

View file

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.6.21'
repositories {
google()
@ -12,10 +12,10 @@ buildscript {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "io.realm:realm-gradle-plugin:10.8.1"
classpath "io.realm:realm-gradle-plugin:10.10.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.19.0"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-alpha03"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-beta01"
classpath 'com.google.firebase:perf-plugin:1.4.1'
}
}

View file

@ -1,6 +1,6 @@
#Sun May 08 23:57:52 EDT 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME