implement bulk attribute point allocation. Fixes #821

This commit is contained in:
Phillip Thelen 2017-10-30 13:27:19 +01:00
parent 54256023c2
commit 2aaa8ccdaa
10 changed files with 125 additions and 8 deletions

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp">
<rotate android:fromDegrees="45">
<shape>
<size
android:width="10dp"
android:height="10dp" />
<solid android:color="@color/white" />
<stroke
android:width="1dp"
android:color="@color/gray_400" />
</shape>
</rotate>
</item>
</layer-list>

View file

@ -709,4 +709,5 @@
<string name="distribute_evenly_help">Assigns the same number of points to each attribute.</string> <string name="distribute_evenly_help">Assigns the same number of points to each attribute.</string>
<string name="distribute_class_help">Assigns more points to the attributes important to your Class.</string> <string name="distribute_class_help">Assigns more points to the attributes important to your Class.</string>
<string name="distribute_task_help">Assigns points based on the Strength, Intelligence, Constitution, and Perception categories associated with the tasks you complete.</string> <string name="distribute_task_help">Assigns points based on the Strength, Intelligence, Constitution, and Perception categories associated with the tasks you complete.</string>
<string name="allocating_points">Allocating Points</string>
</resources> </resources>

View file

@ -350,4 +350,7 @@ public interface ApiService {
@POST("user/allocate") @POST("user/allocate")
Observable<HabitResponse<Stats>> allocatePoint(@Query("stat") String stat); Observable<HabitResponse<Stats>> allocatePoint(@Query("stat") String stat);
@POST("user/allocate-bulk")
Observable<HabitResponse<Stats>> bulkAllocatePoints(@Body Map<String, Integer> stats);
} }

View file

@ -249,4 +249,6 @@ public interface ApiClient {
Observable<Void> updatePassword(String newPassword, String oldPassword, String oldPasswordConfirmation); Observable<Void> updatePassword(String newPassword, String oldPassword, String oldPasswordConfirmation);
Observable<Stats> allocatePoint(String stat); Observable<Stats> allocatePoint(String stat);
Observable<Stats> bulkAllocatePoints(int strength, int intelligence, int constitution, int perception);
} }

View file

@ -77,4 +77,7 @@ public interface UserRepository extends BaseRepository {
@NotNull @NotNull
Observable<Stats> allocatePoint(@Nullable User user, @Stats.StatsTypes String s); Observable<Stats> allocatePoint(@Nullable User user, @Stats.StatsTypes String s);
@NotNull
Observable<Stats> bulkAllocatePoints(int strength, int intelligence, int constitution, int perception);
} }

View file

@ -986,4 +986,14 @@ public class ApiClientImpl implements Action1<Throwable>, ApiClient {
public Observable<Stats> allocatePoint(String stat) { public Observable<Stats> allocatePoint(String stat) {
return apiService.allocatePoint(stat).compose(configureApiCallObserver()); return apiService.allocatePoint(stat).compose(configureApiCallObserver());
} }
@Override
public Observable<Stats> bulkAllocatePoints(int strength, int intelligence, int constitution, int perception) {
Map<String, Integer> 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());
}
} }

View file

@ -311,6 +311,12 @@ public class UserRepositoryImpl extends BaseRepositoryImpl<UserLocalRepository>
}); });
} }
@NotNull
@Override
public Observable<Stats> bulkAllocatePoints(int strength, int intelligence, int constitution, int perception) {
return apiClient.bulkAllocatePoints(strength, intelligence, constitution, perception);
}
@Override @Override
public void runCron(List<Task> tasks) { public void runCron(List<Task> tasks) {
Observable<List<TaskScoringResult>> observable; Observable<List<TaskScoringResult>> observable;

View file

@ -122,8 +122,11 @@ class StatsFragment: BaseMainFragment() {
} }
private fun showBulkAllocateDialog() { private fun showBulkAllocateDialog() {
val dialog = BulkAllocateStatsDialog(context, HabiticaBaseApplication.getComponent()) val context = context
dialog.show() if (context != null) {
val dialog = BulkAllocateStatsDialog(context, HabiticaBaseApplication.getComponent())
dialog.show()
}
} }
private fun changeAutoAllocationMode(@Stats.AutoAllocationTypes allocationMode: String) { private fun changeAutoAllocationMode(@Stats.AutoAllocationTypes allocationMode: String) {

View file

@ -1,9 +1,10 @@
package com.habitrpg.android.habitica.ui.views package com.habitrpg.android.habitica.ui.views
import android.app.AlertDialog import android.app.ProgressDialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.view.LayoutInflater import android.view.LayoutInflater
import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent 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 com.habitrpg.android.habitica.models.user.User
import kotlinx.android.synthetic.main.dialog_bulk_allocate.* import kotlinx.android.synthetic.main.dialog_bulk_allocate.*
import rx.Subscription import rx.Subscription
import rx.functions.Action0
import rx.functions.Action1 import rx.functions.Action1
import javax.inject.Inject import javax.inject.Inject
class BulkAllocateStatsDialog(context: Context?, component: AppComponent) : AlertDialog(context) { class BulkAllocateStatsDialog(context: Context, component: AppComponent) : AlertDialog(context) {
@Inject @Inject
lateinit var userRepository: UserRepository 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) val view = inflater.inflate(R.layout.dialog_bulk_allocate, null)
setView(view) 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() 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?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
subscription = userRepository.user.subscribe(Action1 { subscription = userRepository.user.subscribe(Action1 {
@ -74,19 +92,59 @@ class BulkAllocateStatsDialog(context: Context?, component: AppComponent) : Aler
}, RxErrorHandler.handleEmptyError()) }, RxErrorHandler.handleEmptyError())
strengthSliderView.allocateAction = { strengthSliderView.allocateAction = {
checkRedistribution(strengthSliderView)
updateTitle() updateTitle()
} }
intelligenceSliderView.allocateAction = { intelligenceSliderView.allocateAction = {
checkRedistribution(intelligenceSliderView)
updateTitle() updateTitle()
} }
constitutionSliderView.allocateAction = { constitutionSliderView.allocateAction = {
checkRedistribution(constitutionSliderView)
updateTitle() updateTitle()
} }
perceptionSliderView.allocateAction = { perceptionSliderView.allocateAction = {
checkRedistribution(perceptionSliderView)
updateTitle() 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() { override fun dismiss() {
subscription?.unsubscribe() subscription?.unsubscribe()
super.dismiss() super.dismiss()
@ -99,5 +157,7 @@ class BulkAllocateStatsDialog(context: Context?, component: AppComponent) : Aler
} else { } else {
titleView.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_400)) titleView.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_400))
} }
getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = allocatedPoints > 0
} }
} }

View file

@ -1,7 +1,10 @@
package com.habitrpg.android.habitica.ui.views package com.habitrpg.android.habitica.ui.views
import android.content.Context import android.content.Context
import android.content.res.ColorStateList
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.os.Build
import android.support.v4.content.ContextCompat
import android.util.AttributeSet import android.util.AttributeSet
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
@ -11,7 +14,7 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.styledAttributes import com.habitrpg.android.habitica.extensions.styledAttributes
import kotlinx.android.synthetic.main.stats_slider_view.view.* 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 var previousValue = 0
set(value) { set(value) {
@ -44,8 +47,14 @@ class StatsSliderView(context: Context?, attrs: AttributeSet?) : LinearLayout(co
statTypeTitle.text = attributes.getString(R.styleable.StatsSliderView_statsTitle) statTypeTitle.text = attributes.getString(R.styleable.StatsSliderView_statsTitle)
val statColor = attributes.getColor(R.styleable.StatsSliderView_statsColor, 0) val statColor = attributes.getColor(R.styleable.StatsSliderView_statsColor, 0)
statTypeTitle.setTextColor(attributes.getColor(R.styleable.StatsSliderView_statsTextColor, 0)) statTypeTitle.setTextColor(attributes.getColor(R.styleable.StatsSliderView_statsTextColor, 0))
statsSeekBar.progressDrawable.setColorFilter(statColor, PorterDuff.Mode.MULTIPLY) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
statsSeekBar.thumb.setColorFilter(statColor, PorterDuff.Mode.MULTIPLY) 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 { statsSeekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {