From 79cb82e8d82f7eda13c64cf8bb8edcd2a930f549 Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Wed, 18 Oct 2017 13:19:10 +0200 Subject: [PATCH] stats screen design tweaks --- Habitica/AndroidManifest.xml | 2 +- Habitica/res/layout/avatar_with_bars.xml | 9 +- Habitica/res/layout/fixvalues_edittext.xml | 2 +- Habitica/res/layout/fragment_stats.xml | 70 +++++++++++---- Habitica/res/values-w320dp/dimens.xml | 1 + Habitica/res/values-w321dp/dimens.xml | 1 + Habitica/res/values/dimens.xml | 1 + Habitica/res/values/strings.xml | 3 + .../implementation/UserRepositoryImpl.java | 13 ++- .../extensions/AlertDialog-Extensions.kt | 31 +++++++ .../android/habitica/models/user/Stats.java | 8 +- .../habitica/ui/fragments/StatsFragment.kt | 88 +++++++++++++------ .../habitica/ui/views/HabiticaIcons.java | 82 ++++++++++++++++- .../ui/views/HabiticaIconsHelper.java | 14 +++ 14 files changed, 268 insertions(+), 57 deletions(-) create mode 100644 Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialog-Extensions.kt diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml index 19440d74c..df5ff7195 100644 --- a/Habitica/AndroidManifest.xml +++ b/Habitica/AndroidManifest.xml @@ -2,7 +2,7 @@ diff --git a/Habitica/res/layout/avatar_with_bars.xml b/Habitica/res/layout/avatar_with_bars.xml index 8c7ddf343..205fc263e 100644 --- a/Habitica/res/layout/avatar_with_bars.xml +++ b/Habitica/res/layout/avatar_with_bars.xml @@ -1,5 +1,4 @@ - + android:paddingLeft="@dimen/header_border_spacing" + android:paddingRight="@dimen/header_border_spacing" + android:paddingStart="@dimen/header_border_spacing" + android:paddingEnd="@dimen/header_border_spacing"> diff --git a/Habitica/res/layout/fixvalues_edittext.xml b/Habitica/res/layout/fixvalues_edittext.xml index 46d225257..8ac7d5b3f 100644 --- a/Habitica/res/layout/fixvalues_edittext.xml +++ b/Habitica/res/layout/fixvalues_edittext.xml @@ -29,7 +29,7 @@ android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="match_parent" - android:inputType="number" + android:inputType="numberDecimal" android:background="@android:color/transparent"/> + android:gravity="center" + android:layout_marginTop="6dp" + android:layout_marginBottom="18dp"> + android:layout_marginRight="12dp"/> - - + - + + + + - + + + + - + android:gravity="center_vertical"> + + + + 6dp + 8dp \ No newline at end of file diff --git a/Habitica/res/values-w321dp/dimens.xml b/Habitica/res/values-w321dp/dimens.xml index 32cc656fc..873817818 100644 --- a/Habitica/res/values-w321dp/dimens.xml +++ b/Habitica/res/values-w321dp/dimens.xml @@ -1,4 +1,5 @@ 21dp + 16dp \ No newline at end of file diff --git a/Habitica/res/values/dimens.xml b/Habitica/res/values/dimens.xml index e07324d23..6cf842931 100644 --- a/Habitica/res/values/dimens.xml +++ b/Habitica/res/values/dimens.xml @@ -120,4 +120,5 @@ 46dp 28dp 21dp + 16dp diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index 34705efe6..3dc1248e0 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -708,4 +708,7 @@ Distribute based on task activity No Points to Allocate %d Points to Allocate + Assigns the same number of points to each attribute. + Assigns more points to the attributes important to your Class. + Assigns points based on the Strength, Intelligence, Constitution, and Perception categories associated with the tasks you complete. diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.java b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.java index 92b52fae7..87e2d832c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.java @@ -283,7 +283,18 @@ public class UserRepositoryImpl extends BaseRepositoryImpl @Override public Observable allocatePoint(@Nullable User user, String stat) { if (user != null && user.isManaged()) { - localRepository.executeTransaction(realm -> user.getStats().points -= 1); + localRepository.executeTransaction(realm -> { + if (Stats.STRENGTH.equals(stat)) { + user.getStats().str += 1; + } else if (Stats.INTELLIGENCE.equals(stat)) { + user.getStats()._int += 1; + } else if (Stats.CONSTITUTION.equals(stat)) { + user.getStats().con += 1; + } else if (Stats.PERCEPTION.equals(stat)) { + user.getStats().per += 1; + } + user.getStats().points -= 1; + }); } return apiClient.allocatePoint(stat) .doOnNext(stats -> { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialog-Extensions.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialog-Extensions.kt new file mode 100644 index 000000000..89a0eb1b1 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialog-Extensions.kt @@ -0,0 +1,31 @@ +package com.habitrpg.android.habitica.extensions + +import android.app.AlertDialog +import android.content.DialogInterface +import com.habitrpg.android.habitica.R + +public fun AlertDialog.Builder.setOkButton(listener: ((DialogInterface, Int) -> Unit)? = null): AlertDialog.Builder { + this.setPositiveButton(R.string.ok, { dialog, which -> + listener?.invoke(dialog, which) + }) + return this +} + +public fun AlertDialog.Builder.setCloseButton(listener: ((DialogInterface, Int) -> Unit)? = null): AlertDialog.Builder { + this.setPositiveButton(R.string.close, { dialog, which -> + listener?.invoke(dialog, which) + }) + return this +} + +public fun AlertDialog.setOkButton(whichButton: Int = AlertDialog.BUTTON_POSITIVE, listener: ((DialogInterface, Int) -> Unit)? = null) { + this.setButton(whichButton, context.getString(R.string.ok), { dialog, which -> + listener?.invoke(dialog, which) + }) +} + +public fun AlertDialog.setCloseButton(whichButton: Int = AlertDialog.BUTTON_POSITIVE, listener: ((DialogInterface, Int) -> Unit)? = null) { + this.setButton(whichButton, context.getString(R.string.close), { dialog, which -> + listener?.invoke(dialog, which) + }) +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Stats.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Stats.java index 9646b3854..388f86edd 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Stats.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Stats.java @@ -19,11 +19,17 @@ public class Stats extends RealmObject { public static final String INTELLIGENCE = "int"; public static final String CONSTITUTION = "con"; public static final String PERCEPTION = "per"; - @StringDef({Stats.STRENGTH, Stats.INTELLIGENCE, Stats.CONSTITUTION, Stats.PERCEPTION}) @Retention(RetentionPolicy.SOURCE) public @interface StatsTypes {} + public static final String AUTO_ALLOCATE_FLAT = "flat"; + public static final String AUTO_ALLOCATE_CLASSBASED = "classbased"; + public static final String AUTO_ALLOCATE_TASKBASED = "taskbased"; + @StringDef({Stats.AUTO_ALLOCATE_FLAT, Stats.AUTO_ALLOCATE_CLASSBASED, Stats.AUTO_ALLOCATE_TASKBASED}) + @Retention(RetentionPolicy.SOURCE) + public @interface AutoAllocationTypes {} + @PrimaryKey private String userId; diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt index 52c72b9d8..1ebdc763c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt @@ -1,5 +1,6 @@ package com.habitrpg.android.habitica.ui.fragments +import android.app.AlertDialog import android.os.Bundle import android.support.v4.content.ContextCompat import android.view.LayoutInflater @@ -8,6 +9,7 @@ import android.view.ViewGroup import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.AppComponent import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.extensions.setOkButton import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.helpers.UserStatComputer import com.habitrpg.android.habitica.models.user.Stats @@ -51,14 +53,28 @@ class StatsFragment: BaseMainFragment() { override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) + hideToolbar() return inflater?.inflate(R.layout.fragment_stats, container, false) } + override fun onDestroyView() { + showToolbar() + super.onDestroyView() + } + + override fun onDestroy() { + inventoryRepository.close() + super.onDestroy() + } + override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) leftSparklesView.setImageBitmap(HabiticaIconsHelper.imageOfAttributeSparklesLeft()) rightSparklesView.setImageBitmap(HabiticaIconsHelper.imageOfAttributeSparklesRight()) + distributeEvenlyHelpButton.setImageBitmap(HabiticaIconsHelper.imageOfInfoIcon()) + distributeClassHelpButton.setImageBitmap(HabiticaIconsHelper.imageOfInfoIcon()) + distributeTaskHelpButton.setImageBitmap(HabiticaIconsHelper.imageOfInfoIcon()) compositeSubscription.add(userRepository.getUser(userId).subscribe(Action1 { user = it @@ -66,31 +82,49 @@ class StatsFragment: BaseMainFragment() { updateAttributePoints() }, RxErrorHandler.handleEmptyError())) - autoAllocationModeWrapper.setOnCheckedChangeListener { _, checkedId -> - val allocationMode = when (checkedId) { - R.id.distributeEvenlyButton -> "flat" - R.id.distributeClassButton -> "classbased" - R.id.distributeTaskButton -> "taskbased" - else -> "" + distributeEvenlyButton.setOnCheckedChangeListener { button, isChecked -> + if (isChecked) { + changeAutoAllocationMode(Stats.AUTO_ALLOCATE_FLAT) } - userRepository.updateUser(user, "preferences.allocationMode", allocationMode).subscribe(Action1 {}, RxErrorHandler.handleEmptyError()) } + distributeClassButton.setOnCheckedChangeListener { button, isChecked -> + if (isChecked) { + changeAutoAllocationMode(Stats.AUTO_ALLOCATE_CLASSBASED) + } + } + distributeTaskButton.setOnCheckedChangeListener { button, isChecked -> + if (isChecked) { + changeAutoAllocationMode(Stats.AUTO_ALLOCATE_TASKBASED) + } + } + automaticAllocationSwitch.setOnCheckedChangeListener{ _, isChecked -> userRepository.updateUser(user, "preferences.automaticAllocation", isChecked).subscribe(Action1 {}, RxErrorHandler.handleEmptyError()) } - strengthStatsView.allocateAction = { - allocatePoint(Stats.STRENGTH) - } - intelligenceStatsView.allocateAction = { - allocatePoint(Stats.INTELLIGENCE) - } - constitutionStatsView.allocateAction = { - allocatePoint(Stats.CONSTITUTION) - } - perceptionStatsView.allocateAction = { - allocatePoint(Stats.PERCEPTION) - } + strengthStatsView.allocateAction = { allocatePoint(Stats.STRENGTH) } + intelligenceStatsView.allocateAction = { allocatePoint(Stats.INTELLIGENCE) } + constitutionStatsView.allocateAction = { allocatePoint(Stats.CONSTITUTION) } + perceptionStatsView.allocateAction = { allocatePoint(Stats.PERCEPTION) } + + distributeEvenlyHelpButton.setOnClickListener { showHelpAlert(R.string.distribute_evenly_help) } + distributeClassHelpButton.setOnClickListener { showHelpAlert(R.string.distribute_class_help) } + distributeTaskHelpButton.setOnClickListener { showHelpAlert(R.string.distribute_task_help) } + } + + private fun changeAutoAllocationMode(@Stats.AutoAllocationTypes allocationMode: String) { + userRepository.updateUser(user, "preferences.allocationMode", allocationMode).subscribe(Action1 {}, RxErrorHandler.handleEmptyError()) + distributeEvenlyButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_FLAT + distributeClassButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_CLASSBASED + distributeTaskButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_TASKBASED + + } + + private fun showHelpAlert(resourceId: Int) { + val builder = AlertDialog.Builder(context) + .setMessage(resourceId) + .setOkButton() + builder.show() } private fun allocatePoint(@Stats.StatsTypes stat: String) { @@ -101,18 +135,17 @@ class StatsFragment: BaseMainFragment() { val automaticAllocation = user?.preferences?.automaticAllocation ?: false automaticAllocationSwitch.isChecked = automaticAllocation autoAllocationModeWrapper.visibility = if (automaticAllocation) View.VISIBLE else View.GONE - when (user?.preferences?.allocationMode ?: "") { - "flat" -> distributeEvenlyButton.isChecked = true - "classbased" -> distributeClassButton.isChecked = true - "taskbased" -> distributeTaskButton.isChecked = true - } - val canDistributePoints = !automaticAllocation && 0 < (user?.stats?.points ?: 0) + val allocationMode = user?.preferences?.allocationMode ?: "" + distributeEvenlyButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_FLAT + distributeClassButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_CLASSBASED + distributeTaskButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_TASKBASED + + val canDistributePoints =0 < (user?.stats?.points ?: 0) strengthStatsView.canDistributePoints = canDistributePoints intelligenceStatsView.canDistributePoints = canDistributePoints constitutionStatsView.canDistributePoints = canDistributePoints perceptionStatsView.canDistributePoints = canDistributePoints - numberOfPointsTextView.visibility = View.VISIBLE if (canDistributePoints) { val points = user?.stats?.points ?: 0 numberOfPointsTextView.text = getString(R.string.points_to_allocate, points) @@ -121,9 +154,6 @@ class StatsFragment: BaseMainFragment() { leftSparklesView.visibility = View.VISIBLE rightSparklesView.visibility = View.VISIBLE } else { - if (automaticAllocation) { - numberOfPointsTextView.visibility = View.GONE - } numberOfPointsTextView.text = getString(R.string.no_points_to_allocate) numberOfPointsTextView.setTextColor(ContextCompat.getColor(context, R.color.gray_300)) numberOfPointsTextView.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent)) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java index 882c387df..27269a547 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java @@ -16,7 +16,7 @@ import java.util.Stack; /** - * Created by Phillip Thelen on 17.10.17. + * Created by Phillip Thelen on 18.10.17. * Copyright © 2017 HabitRPG Inc.. All rights reserved. * * Generated by PaintCode @@ -7146,6 +7146,74 @@ public class HabiticaIcons { canvas.restore(); } + private static class CacheForInfoIcon { + private static Paint paint = new Paint(); + private static RectF originalFrame = new RectF(0f, 0f, 20f, 20f); + private static RectF resizedFrame = new RectF(); + private static RectF bezierRect = new RectF(); + private static Path bezierPath = new Path(); + } + + public static void drawInfoIcon(Canvas canvas) { + HabiticaIcons.drawInfoIcon(canvas, new RectF(0f, 0f, 20f, 20f), ResizingBehavior.AspectFit); + } + + public static void drawInfoIcon(Canvas canvas, RectF targetFrame, ResizingBehavior resizing) { + // General Declarations + Paint paint = CacheForInfoIcon.paint; + + // Local Colors + int fillColor18 = Color.argb(255, 154, 98, 255); + + // Resize to Target Frame + canvas.save(); + RectF resizedFrame = CacheForInfoIcon.resizedFrame; + HabiticaIcons.resizingBehaviorApply(resizing, CacheForInfoIcon.originalFrame, targetFrame, resizedFrame); + canvas.translate(resizedFrame.left, resizedFrame.top); + canvas.scale(resizedFrame.width() / 20f, resizedFrame.height() / 20f); + + // Bezier + RectF bezierRect = CacheForInfoIcon.bezierRect; + bezierRect.set(0f, 0f, 20f, 20f); + Path bezierPath = CacheForInfoIcon.bezierPath; + bezierPath.reset(); + bezierPath.moveTo(9f, 7f); + bezierPath.lineTo(11f, 7f); + bezierPath.lineTo(11f, 5f); + bezierPath.lineTo(9f, 5f); + bezierPath.lineTo(9f, 7f); + bezierPath.close(); + bezierPath.moveTo(10f, 18f); + bezierPath.cubicTo(5.59f, 18f, 2f, 14.41f, 2f, 10f); + bezierPath.cubicTo(2f, 5.59f, 5.59f, 2f, 10f, 2f); + bezierPath.cubicTo(14.41f, 2f, 18f, 5.59f, 18f, 10f); + bezierPath.cubicTo(18f, 14.41f, 14.41f, 18f, 10f, 18f); + bezierPath.lineTo(10f, 18f); + bezierPath.close(); + bezierPath.moveTo(10f, 0f); + bezierPath.cubicTo(4.48f, 0f, 0f, 4.48f, 0f, 10f); + bezierPath.cubicTo(0f, 15.52f, 4.48f, 20f, 10f, 20f); + bezierPath.cubicTo(15.52f, 20f, 20f, 15.52f, 20f, 10f); + bezierPath.cubicTo(20f, 4.48f, 15.52f, 0f, 10f, 0f); + bezierPath.lineTo(10f, 0f); + bezierPath.close(); + bezierPath.moveTo(9f, 15f); + bezierPath.lineTo(11f, 15f); + bezierPath.lineTo(11f, 9f); + bezierPath.lineTo(9f, 9f); + bezierPath.lineTo(9f, 15f); + bezierPath.close(); + + paint.reset(); + paint.setFlags(Paint.ANTI_ALIAS_FLAG); + bezierPath.setFillType(Path.FillType.EVEN_ODD); + paint.setStyle(Paint.Style.FILL); + paint.setColor(fillColor18); + canvas.drawPath(bezierPath, paint); + + canvas.restore(); + } + // Canvas Images // Tab @@ -7550,6 +7618,18 @@ public class HabiticaIcons { return imageOfAttributeAllocateButton; } + private static Bitmap imageOfInfoIcon = null; + public static Bitmap imageOfInfoIcon() { + if (imageOfInfoIcon != null) + return imageOfInfoIcon; + + imageOfInfoIcon = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(imageOfInfoIcon); + HabiticaIcons.drawInfoIcon(canvas); + + return imageOfInfoIcon; + } + // Resizing Behavior public static void resizingBehaviorApply(ResizingBehavior behavior, RectF rect, RectF target, RectF result) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java index c8d51335f..ba97f232e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java @@ -472,4 +472,18 @@ public class HabiticaIconsHelper { return imageOfAttributeAllocateButton; } + + private static Bitmap imageOfInfoIcon = null; + public static Bitmap imageOfInfoIcon() { + if (imageOfInfoIcon != null) + return imageOfInfoIcon; + + int size = scaleSize(20); + imageOfInfoIcon = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(imageOfInfoIcon); + canvas.scale(displayDensity, displayDensity); + HabiticaIcons.drawInfoIcon(canvas); + + return imageOfInfoIcon; + } }