diff --git a/Habitica/res/drawable/seekbar_thumb.xml b/Habitica/res/drawable/seekbar_thumb.xml new file mode 100644 index 000000000..9babaf6f2 --- /dev/null +++ b/Habitica/res/drawable/seekbar_thumb.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index e9f18edee..3e5e0472e 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -711,4 +711,5 @@ 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. + Allocating Points diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java index c276e202e..a3633d5a7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java @@ -350,4 +350,7 @@ public interface ApiService { @POST("user/allocate") Observable> allocatePoint(@Query("stat") String stat); + + @POST("user/allocate-bulk") + Observable> bulkAllocatePoints(@Body Map stats); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.java b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.java index 025542e56..d6bb7d381 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.java @@ -249,4 +249,6 @@ public interface ApiClient { Observable updatePassword(String newPassword, String oldPassword, String oldPasswordConfirmation); Observable allocatePoint(String stat); + + Observable bulkAllocatePoints(int strength, int intelligence, int constitution, int perception); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.java b/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.java index a2473d79d..e926d74b7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.java @@ -77,4 +77,7 @@ public interface UserRepository extends BaseRepository { @NotNull Observable allocatePoint(@Nullable User user, @Stats.StatsTypes String s); + + @NotNull + Observable bulkAllocatePoints(int strength, int intelligence, int constitution, int perception); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.java b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.java index 75c384e67..e46f402de 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.java @@ -986,4 +986,14 @@ public class ApiClientImpl implements Action1, ApiClient { public Observable allocatePoint(String stat) { return apiService.allocatePoint(stat).compose(configureApiCallObserver()); } + + @Override + public Observable bulkAllocatePoints(int strength, int intelligence, int constitution, int perception) { + Map stats = new HashMap<>(); + stats.put("str", strength); + stats.put("int", intelligence); + stats.put("con", constitution); + stats.put("per", perception); + return apiService.bulkAllocatePoints(stats).compose(configureApiCallObserver()); + } } 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 87e2d832c..9f3ec3e0b 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 @@ -311,6 +311,12 @@ public class UserRepositoryImpl extends BaseRepositoryImpl }); } + @NotNull + @Override + public Observable bulkAllocatePoints(int strength, int intelligence, int constitution, int perception) { + return apiClient.bulkAllocatePoints(strength, intelligence, constitution, perception); + } + @Override public void runCron(List tasks) { Observable> observable; 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 98a588dd4..79b07e451 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 @@ -122,8 +122,11 @@ class StatsFragment: BaseMainFragment() { } private fun showBulkAllocateDialog() { - val dialog = BulkAllocateStatsDialog(context, HabiticaBaseApplication.getComponent()) - dialog.show() + val context = context + if (context != null) { + val dialog = BulkAllocateStatsDialog(context, HabiticaBaseApplication.getComponent()) + dialog.show() + } } private fun changeAutoAllocationMode(@Stats.AutoAllocationTypes allocationMode: String) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BulkAllocateStatsDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BulkAllocateStatsDialog.kt index b8f9c15c9..71fa6b428 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BulkAllocateStatsDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BulkAllocateStatsDialog.kt @@ -1,9 +1,10 @@ package com.habitrpg.android.habitica.ui.views -import android.app.AlertDialog +import android.app.ProgressDialog import android.content.Context import android.os.Bundle import android.support.v4.content.ContextCompat +import android.support.v7.app.AlertDialog import android.view.LayoutInflater import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.AppComponent @@ -12,10 +13,11 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.user.User import kotlinx.android.synthetic.main.dialog_bulk_allocate.* import rx.Subscription +import rx.functions.Action0 import rx.functions.Action1 import javax.inject.Inject -class BulkAllocateStatsDialog(context: Context?, component: AppComponent) : AlertDialog(context) { +class BulkAllocateStatsDialog(context: Context, component: AppComponent) : AlertDialog(context) { @Inject lateinit var userRepository: UserRepository @@ -62,11 +64,27 @@ class BulkAllocateStatsDialog(context: Context?, component: AppComponent) : Aler val view = inflater.inflate(R.layout.dialog_bulk_allocate, null) setView(view) - this.setButton(android.support.v7.app.AlertDialog.BUTTON_POSITIVE, context?.getString(R.string.done)) { _, _ -> + this.setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.save)) { _, _ -> + saveChanges() + } + this.setButton(AlertDialog.BUTTON_NEUTRAL, context.getString(R.string.action_cancel)) { _, _ -> this.dismiss() } } + private fun saveChanges() { + val progressDialog = ProgressDialog.show(context, context.getString(R.string.allocating_points), null, true) + userRepository.bulkAllocatePoints(strengthSliderView.currentValue, intelligenceSliderView.currentValue, constitutionSliderView.currentValue, perceptionSliderView.currentValue) + .subscribe({ + progressDialog.dismiss() + this.dismiss() + }, { + RxErrorHandler.reportError(it) + progressDialog.dismiss() + this.dismiss() + }) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) subscription = userRepository.user.subscribe(Action1 { @@ -74,19 +92,59 @@ class BulkAllocateStatsDialog(context: Context?, component: AppComponent) : Aler }, RxErrorHandler.handleEmptyError()) strengthSliderView.allocateAction = { + checkRedistribution(strengthSliderView) updateTitle() } intelligenceSliderView.allocateAction = { + checkRedistribution(intelligenceSliderView) updateTitle() } constitutionSliderView.allocateAction = { + checkRedistribution(constitutionSliderView) updateTitle() } perceptionSliderView.allocateAction = { + checkRedistribution(perceptionSliderView) updateTitle() } } + private fun checkRedistribution(excludedSlider: StatsSliderView) { + val diff = allocatedPoints - pointsToAllocate + if (diff > 0) { + var highestSlider: StatsSliderView? = null + if (excludedSlider != strengthSliderView) { + highestSlider = getSliderWithHigherValue(highestSlider, strengthSliderView) + } + if (excludedSlider != intelligenceSliderView) { + highestSlider = getSliderWithHigherValue(highestSlider, intelligenceSliderView) + } + if (excludedSlider != constitutionSliderView) { + highestSlider = getSliderWithHigherValue(highestSlider, constitutionSliderView) + } + if (excludedSlider != perceptionSliderView) { + highestSlider = getSliderWithHigherValue(highestSlider, perceptionSliderView) + } + if (highestSlider != null) { + highestSlider.currentValue -= diff + } + } + } + + private fun getSliderWithHigherValue(firstSlider: StatsSliderView?, secondSlider: StatsSliderView?): StatsSliderView? { + if (firstSlider == null) { + return secondSlider + } + if (secondSlider == null) { + return firstSlider + } + if (firstSlider.currentValue > secondSlider.currentValue) { + return firstSlider + } else { + return secondSlider + } + } + override fun dismiss() { subscription?.unsubscribe() super.dismiss() @@ -99,5 +157,7 @@ class BulkAllocateStatsDialog(context: Context?, component: AppComponent) : Aler } else { titleView.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_400)) } + + getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = allocatedPoints > 0 } } \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/StatsSliderView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/StatsSliderView.kt index 25e0f68fa..e95278078 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/StatsSliderView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/StatsSliderView.kt @@ -1,7 +1,10 @@ package com.habitrpg.android.habitica.ui.views import android.content.Context +import android.content.res.ColorStateList import android.graphics.PorterDuff +import android.os.Build +import android.support.v4.content.ContextCompat import android.util.AttributeSet import android.view.Gravity import android.view.View @@ -11,7 +14,7 @@ import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.extensions.styledAttributes import kotlinx.android.synthetic.main.stats_slider_view.view.* -class StatsSliderView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) { +class StatsSliderView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { var previousValue = 0 set(value) { @@ -44,8 +47,14 @@ class StatsSliderView(context: Context?, attrs: AttributeSet?) : LinearLayout(co statTypeTitle.text = attributes.getString(R.styleable.StatsSliderView_statsTitle) val statColor = attributes.getColor(R.styleable.StatsSliderView_statsColor, 0) statTypeTitle.setTextColor(attributes.getColor(R.styleable.StatsSliderView_statsTextColor, 0)) - statsSeekBar.progressDrawable.setColorFilter(statColor, PorterDuff.Mode.MULTIPLY) - statsSeekBar.thumb.setColorFilter(statColor, PorterDuff.Mode.MULTIPLY) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + statsSeekBar.progressTintList = ColorStateList.valueOf(statColor) + } else { + statsSeekBar.progressDrawable.setColorFilter(statColor, PorterDuff.Mode.SRC_IN) + } + val thumbDrawable = ContextCompat.getDrawable(context, R.drawable.seekbar_thumb) + thumbDrawable?.setColorFilter(statColor, PorterDuff.Mode.MULTIPLY) + statsSeekBar.thumb = thumbDrawable } statsSeekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {