diff --git a/Habitica/build.gradle b/Habitica/build.gradle index df75689a4..f775c402d 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' +apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'com.noveogroup.android.check' apply plugin: 'realm-android' @@ -15,7 +16,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:4.2.2' classpath('com.noveogroup.android:check:1.2.5') { exclude module: 'checkstyle' exclude module: 'pmd-java' @@ -49,8 +50,6 @@ dependencies { } implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0' - //Crash Logging - implementation 'com.google.firebase:firebase-crashlytics:18.0.0' //Dependency Injection implementation 'com.google.dagger:dagger:2.36' @@ -58,9 +57,9 @@ dependencies { compileOnly 'javax.annotation:javax.annotation-api:1.3.2' compileOnly 'com.github.pengrad:jdk9-deps:1.0' //App Compatibility and Material Design - implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.3.0' - implementation 'androidx.recyclerview:recyclerview:1.2.0' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation "androidx.preference:preference-ktx:1.1.1" @@ -92,7 +91,7 @@ dependencies { implementation("io.coil-kt:coil-gif:1.2.2") //Tests testImplementation 'junit:junit:4.12' - testImplementation 'androidx.test:core:1.3.0' + testImplementation 'androidx.test:core:1.4.0' testImplementation "com.google.truth:truth:1.0.1" testImplementation 'org.assertj:assertj-core:2.6.0' testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2' @@ -106,20 +105,22 @@ dependencies { //Leak Detection debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5' //Push Notifications - implementation 'com.google.firebase:firebase-core:19.0.0' - implementation 'com.google.firebase:firebase-messaging:22.0.0' - implementation 'com.google.firebase:firebase-config:21.0.0' - implementation 'com.google.firebase:firebase-perf:20.0.0' - implementation 'com.google.android.gms:play-services-auth:19.0.0' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.10" + implementation platform('com.google.firebase:firebase-bom:28.3.0') + implementation 'com.google.firebase:firebase-crashlytics' + implementation 'com.google.firebase:firebase-core' + implementation 'com.google.firebase:firebase-messaging' + implementation 'com.google.firebase:firebase-config' + implementation 'com.google.firebase:firebase-perf' + implementation 'com.google.android.gms:play-services-auth:19.2.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.20" implementation 'com.nex3z:flow-layout:1.2.2' - implementation 'androidx.core:core-ktx:1.5.0' + implementation 'androidx.core:core-ktx:1.6.0' implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1" implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' implementation 'androidx.navigation:navigation-ui-ktx:2.3.5' - implementation "androidx.paging:paging-runtime-ktx:3.0.0" + implementation "androidx.paging:paging-runtime-ktx:3.0.1" implementation 'com.plattysoft.leonids:LeonidsLib:1.3.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0' @@ -146,8 +147,8 @@ 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 3011 - versionName "3.3" + versionCode 3020 + versionName "3.3.1" } buildFeatures { diff --git a/Habitica/res/layout/widget_habit_button.xml b/Habitica/res/layout/widget_habit_button.xml index 63adf9e2f..658811657 100644 --- a/Habitica/res/layout/widget_habit_button.xml +++ b/Habitica/res/layout/widget_habit_button.xml @@ -4,8 +4,10 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/brand_100" - android:clipChildren="true"> + android:background="@drawable/widget_background" + android:id="@+id/widget" + android:clipChildren="true" + android:clipToPadding="true"> \ No newline at end of file diff --git a/Habitica/res/layout/widget_task_list.xml b/Habitica/res/layout/widget_task_list.xml index 75d57b887..7b7be4199 100644 --- a/Habitica/res/layout/widget_task_list.xml +++ b/Habitica/res/layout/widget_task_list.xml @@ -10,12 +10,12 @@ android:padding="4dp" android:textSize="18sp" android:id="@+id/widget_title" - android:textColor="@android:color/white"/> + android:textColor="@color/text_primary"/> + android:paddingStart="10dp" + android:paddingEnd="10dp"> diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index 305674ac3..4d87e89ab 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -1176,4 +1176,5 @@ You aren\'t a member of any Challenges. You aren\'t a member of any Guilds. Head over to the Discover tab to find some to join! + You completed all your tasks. Well done! diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt index 8d3dc726a..20c905a65 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt @@ -63,7 +63,7 @@ class AppConfigManager(contentRepository: ContentRepository?) { } fun testingLevel(): AppTestingLevel { - return AppTestingLevel.valueOf(BuildConfig.TESTING_LEVEL.toUpperCase(Locale.US)) + return AppTestingLevel.valueOf(BuildConfig.TESTING_LEVEL.uppercase()) } fun enableLocalTaskScoring(): Boolean { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt index afa69f054..d4ce76fdf 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt @@ -7,6 +7,10 @@ import android.text.TextUtils import android.util.AttributeSet import android.widget.FrameLayout import android.widget.ImageView +import androidx.core.graphics.drawable.toBitmap +import androidx.core.view.drawToBitmap +import androidx.core.view.marginStart +import androidx.core.view.marginTop import coil.clear import coil.load import com.habitrpg.android.habitica.BuildConfig @@ -62,12 +66,15 @@ class AvatarView : FrameLayout { if (BuildConfig.DEBUG && (avatar == null || avatarRectF == null)) { error("Assertion failed") } - val canvasRect = Rect() - avatarRectF?.round(canvasRect) + val canvasRect = Rect(0, 0, 140.dpToPx(context), 147.dpToPx(context)) if (canvasRect.isEmpty) return null avatarBitmap = Bitmap.createBitmap(canvasRect.width(), canvasRect.height(), Bitmap.Config.ARGB_8888) avatarBitmap?.let { avatarCanvas = Canvas(it) } - draw(avatarCanvas) + imageViewHolder.forEach { + val lp = it.layoutParams + val bitmap = it.drawable?.toBitmap(lp.width, lp.height) ?: return@forEach + avatarCanvas?.drawBitmap(bitmap, Rect(0, 0, bitmap.width, bitmap.height), Rect(it.marginStart, it.marginTop, bitmap.width, bitmap.height), null) + } return avatarBitmap } @@ -148,6 +155,8 @@ class AvatarView : FrameLayout { val layoutParams = imageView.layoutParams as? LayoutParams layoutParams?.topMargin = bounds.top layoutParams?.marginStart = bounds.left + layoutParams?.width = bounds.right + layoutParams?.height = bounds.bottom imageView.layoutParams = layoutParams onLayerComplete() }) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt index d0dbd4abf..9773d233e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt @@ -24,7 +24,6 @@ import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.modules.AppModule import com.habitrpg.android.habitica.ui.activities.TaskFormActivity import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment -import com.habitrpg.android.habitica.ui.fragments.inventory.items.ItemRecyclerFragment import com.habitrpg.android.habitica.ui.views.navigation.HabiticaBottomNavigationViewListener import com.habitrpg.android.habitica.ui.views.tasks.TaskFilterDialog import io.reactivex.rxjava3.disposables.Disposable @@ -86,10 +85,10 @@ class TasksFragment : BaseMainFragment(), SearchView.O switchToTaskTab(taskType) } else { when (sharedPreferences.getString("launch_screen", "")) { - "/user/tasks/habits" -> binding?.viewPager?.currentItem = 0 - "/user/tasks/dailies" -> binding?.viewPager?.currentItem = 1 - "/user/tasks/todos" -> binding?.viewPager?.currentItem = 2 - "/user/tasks/rewards" -> binding?.viewPager?.currentItem = 3 + "/user/tasks/habits" -> onTabSelected(Task.TYPE_HABIT, false) + "/user/tasks/dailies" -> onTabSelected(Task.TYPE_DAILY, false) + "/user/tasks/todos" -> onTabSelected(Task.TYPE_TODO, false) + "/user/tasks/rewards" -> onTabSelected(Task.TYPE_REWARD, false) } } } @@ -104,6 +103,7 @@ class TasksFragment : BaseMainFragment(), SearchView.O 3 -> Task.TYPE_REWARD else -> Task.TYPE_HABIT } + binding?.viewPager?.currentItem = binding?.viewPager?.currentItem ?: 0 bottomNavigation?.listener = this bottomNavigation?.canAddTasks = true } @@ -397,7 +397,7 @@ class TasksFragment : BaseMainFragment(), SearchView.O } - override fun onTabSelected(taskType: String) { + override fun onTabSelected(taskType: String, smooth: Boolean) { val newItem = when (taskType) { Task.TYPE_HABIT -> 0 Task.TYPE_DAILY -> 1 @@ -405,7 +405,7 @@ class TasksFragment : BaseMainFragment(), SearchView.O Task.TYPE_REWARD -> 3 else -> 0 } - binding?.viewPager?.currentItem = newItem + binding?.viewPager?.setCurrentItem(newItem, smooth) updateBottomBarBadges() } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt index 5c0080b35..91f6ee61c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt @@ -400,7 +400,7 @@ class TeamBoardFragment : BaseMainFragment(), SearchVi var lastTaskFormOpen: Date? = null } - override fun onTabSelected(taskType: String) { + override fun onTabSelected(taskType: String, smooth: Boolean) { val newItem = when (taskType) { Task.TYPE_HABIT -> 0 Task.TYPE_DAILY -> 1 @@ -408,7 +408,7 @@ class TeamBoardFragment : BaseMainFragment(), SearchVi Task.TYPE_REWARD -> 3 else -> 0 } - binding?.viewPager?.currentItem = newItem + binding?.viewPager?.setCurrentItem(newItem, smooth) updateBottomBarBadges() } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt index b19922156..72884454d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt @@ -22,7 +22,7 @@ import com.habitrpg.android.habitica.extensions.setTintWith import com.habitrpg.android.habitica.models.tasks.Task interface HabiticaBottomNavigationViewListener { - fun onTabSelected(taskType: String) + fun onTabSelected(taskType: String, smooth: Boolean) fun onAdd(taskType: String) } @@ -56,7 +56,7 @@ class HabiticaBottomNavigationView @JvmOverloads constructor( field = value if (wasChanged) { updateItemSelection() - listener?.onTabSelected(value) + listener?.onTabSelected(value, true) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt index e703ce359..84771a688 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt @@ -6,9 +6,11 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.view.View +import android.view.ViewGroup import android.widget.RemoteViews import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.extensions.dpToPx import com.habitrpg.android.habitica.helpers.HealthFormatter import com.habitrpg.android.habitica.helpers.NumberAbbreviator import com.habitrpg.android.habitica.helpers.RxErrorHandler @@ -22,6 +24,7 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() { private var appWidgetManager: AppWidgetManager? = null private var showManaBar: Boolean = true + private var showAvatar: Boolean = true override fun layoutResourceId(): Int { return R.layout.widget_avatar_stats @@ -44,18 +47,17 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() { } override fun configureRemoteViews(remoteViews: RemoteViews, widgetId: Int, columns: Int, rows: Int): RemoteViews { - if (columns > 3) { - remoteViews.setViewVisibility(R.id.avatar_view, View.GONE) + showAvatar = columns > 3 + if (showAvatar) { + remoteViews.setViewVisibility(R.id.avatar_view, View.VISIBLE) } else { remoteViews.setViewVisibility(R.id.avatar_view, View.GONE) } showManaBar = rows > 1 if (rows > 1) { - remoteViews.setViewVisibility(R.id.mp_wrapper, View.VISIBLE) remoteViews.setViewVisibility(R.id.detail_info_view, View.VISIBLE) } else { - remoteViews.setViewVisibility(R.id.mp_wrapper, View.GONE) remoteViews.setViewVisibility(R.id.detail_info_view, View.GONE) } @@ -109,13 +111,17 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() { remoteViews.setImageViewBitmap(R.id.gold_icon, HabiticaIconsHelper.imageOfGold()) remoteViews.setTextViewText(R.id.lvl_tv, context.getString(R.string.user_level, user.stats?.lvl ?: 0)) - val avatarView = AvatarView(context, showBackground = true, showMount = true, showPet = true) - - avatarView.setAvatar(user) - val finalRemoteViews = remoteViews - avatarView.onAvatarImageReady { bitmap -> - finalRemoteViews.setImageViewBitmap(R.id.avatar_view, bitmap) - appWidgetManager.partiallyUpdateAppWidget(allWidgetIds, finalRemoteViews) + if (showAvatar) { + val avatarView = + AvatarView(context, showBackground = true, showMount = true, showPet = true) + val layoutParams = ViewGroup.LayoutParams(140.dpToPx(context), 147.dpToPx(context)) + avatarView.layoutParams = layoutParams + avatarView.setAvatar(user) + val finalRemoteViews = remoteViews + avatarView.onAvatarImageReady { bitmap -> + finalRemoteViews.setImageViewBitmap(R.id.avatar_view, bitmap) + appWidgetManager.partiallyUpdateAppWidget(allWidgetIds, finalRemoteViews) + } } val openAppIntent = Intent(context.applicationContext, MainActivity::class.java) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt index 78a05c28b..8bd812370 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt @@ -11,6 +11,7 @@ import android.os.IBinder import android.text.SpannableStringBuilder import android.text.style.DynamicDrawableSpan import android.view.View +import android.view.ViewOutlineProvider import android.widget.RemoteViews import androidx.core.content.ContextCompat import com.habitrpg.android.habitica.HabiticaBaseApplication @@ -22,6 +23,7 @@ import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.ui.helpers.MarkdownParser import java.util.* import javax.inject.Inject +import kotlin.math.min class HabitButtonWidgetService : Service() { @Inject @@ -57,8 +59,7 @@ class HabitButtonWidgetService : Service() { val parsedText = MarkdownParser.parseMarkdown(task.text) val builder = SpannableStringBuilder(parsedText) - - remoteViews.setTextViewText(R.id.habit_title, builder) + remoteViews.setTextViewText(R.id.habit_title, builder.substring(0, min(builder.length, 70))) if (task.up != true) { remoteViews.setViewVisibility(R.id.btnPlusWrapper, View.GONE) diff --git a/build.gradle b/build.gradle index 20487b61d..baa00dc74 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.5.10' - ext.build_tools_version = '29.0.0' - ext.sdk_version = 30 + ext.kotlin_version = '1.5.20' repositories { google() @@ -11,10 +9,10 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:4.2.2' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.google.gms:google-services:4.3.8' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.6.1' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' classpath "io.realm:realm-gradle-plugin:10.3.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.1.0" diff --git a/fastlane/README.md b/fastlane/README.md index 305cebbb2..7507466dd 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -44,6 +44,6 @@ Deploy a new version to the Google Play ---- -This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/fastlane/changelog.txt b/fastlane/changelog.txt index de0221881..a0fb0b394 100644 --- a/fastlane/changelog.txt +++ b/fastlane/changelog.txt @@ -1 +1 @@ -In this update, we’ve improved more of our notifications so they bring you to the relevant screen when tapped. When customizing your avatar, you’ll be able to see your avatar updating in real time as you select different options. You can also see your current Gems when gifting Gems from your balance now. We've fixed a few bugs too, including one where tasks wouldn’t load when the app is left running in the background for awhile, and another where Dailies wouldn’t filter properly on launch. +We’ve improved more of our notifications so they bring you to the relevant screen when tapped. When customizing your avatar, you’ll be able to see your avatar updating in real time as you select different options. You can also see your current Gems when gifting Gems from your balance now. We’ve fixed a few bugs too, including one where tasks wouldn’t load when the app is left running in the background, the stats widget works again, and another where Dailies wouldn’t filter properly on launch.