diff --git a/app/src/local/assets/mock_files/get_user_device_rewards.json b/app/src/local/assets/mock_files/get_user_device_rewards.json index 71d915bbe..1dd883f9d 100644 --- a/app/src/local/assets/mock_files/get_user_device_rewards.json +++ b/app/src/local/assets/mock_files/get_user_device_rewards.json @@ -108,11 +108,11 @@ "pol": [ { "annotation": "LOCATION_NOT_VERIFIED", - "ratio": 0.8 + "ratio": 8 }, { "annotation": "NO_LOCATION_DATA", - "ratio": 0.8 + "ratio": 8 } ], "rm": [ @@ -129,7 +129,30 @@ "annotation": "QOD_THRESHOLD_NOT_REACHED" } ] - } + }, + "annotation_summary": [ + { + "severity_level": "WARNING", + "group": "NO_WALLET", + "title": "No Wallet", + "message": "Station excluded from rewards because no connected wallet was found.", + "doc_url": "https://docs.weatherxm.com/" + }, + { + "severity_level": "INFO", + "group": "SENSOR_PROBLEMS", + "title": "Sensor Problems", + "message": "Station is doing great! Minor sensor issues detected during our checks.", + "doc_url": "https://docs.weatherxm.com/" + }, + { + "severity_level": "ERROR", + "group": "LOCATION_NOT_VERIFIED", + "title": "Invalid Location", + "message": "Station rewards were temporarily suspended, because the station’s location could not be verified. The station’s location needs to be revalidated by the owner and it will be again eligible for rewards in 2 days.", + "doc_url": "https://docs.weatherxm.com/" + } + ] }, "weekly": { "total_rewards": 1252.0, @@ -166,10 +189,10 @@ 100, 50, 100, - 0.26, - 0.26, - 0.26, - 0.26, + 26, + 26, + 26, + 26, 100, 100, 10, diff --git a/app/src/local/assets/mock_files/get_user_device_transactions.json b/app/src/local/assets/mock_files/get_user_device_transactions.json index 2fd3bb8de..bd16fbb9a 100644 --- a/app/src/local/assets/mock_files/get_user_device_transactions.json +++ b/app/src/local/assets/mock_files/get_user_device_transactions.json @@ -111,7 +111,30 @@ "annotation": "NO_WALLET" } ] - } + }, + "annotation_summary": [ + { + "severity_level": "WARNING", + "group": "NO_WALLET", + "title": "No Wallet", + "message": "Station excluded from rewards because no connected wallet was found.", + "doc_url": "https://docs.weatherxm.com/" + }, + { + "severity_level": "INFO", + "group": "SENSOR_PROBLEMS", + "title": "Sensor Problems", + "message": "Station is doing great! Minor sensor issues detected during our checks.", + "doc_url": "https://docs.weatherxm.com/" + }, + { + "severity_level": "ERROR", + "group": "LOCATION_NOT_VERIFIED", + "title": "Invalid Location", + "message": "Station rewards were temporarily suspended, because the station’s location could not be verified. The station’s location needs to be revalidated by the owner and it will be again eligible for rewards in 2 days.", + "doc_url": "https://docs.weatherxm.com/" + } + ] }, { "timestamp": "2022-06-02T12:00:02+00:00", diff --git a/app/src/main/java/com/weatherxm/data/ApiModels.kt b/app/src/main/java/com/weatherxm/data/ApiModels.kt index 456b7294d..014442af3 100644 --- a/app/src/main/java/com/weatherxm/data/ApiModels.kt +++ b/app/src/main/java/com/weatherxm/data/ApiModels.kt @@ -5,6 +5,7 @@ import androidx.annotation.Keep import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import com.weatherxm.ui.common.AnnotationCode +import com.weatherxm.ui.common.AnnotationGroupCode import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.UIDevice import com.weatherxm.ui.common.empty @@ -196,7 +197,9 @@ data class Transaction( @Json(name = "lost_rewards") val lostRewards: Float?, val timeline: RewardsTimeline?, - val annotations: RewardsAnnotations? + val annotations: RewardsAnnotations?, + @Json(name = "annotation_summary") + val annotationSummary: List?, ) : Parcelable @Keep @@ -470,7 +473,9 @@ data class RewardsObject( @Json(name = "lost_rewards") val lostRewards: Float?, val timeline: RewardsTimeline?, - val annotations: RewardsAnnotations? + val annotations: RewardsAnnotations?, + @Json(name = "annotation_summary") + val annotationSummary: List?, ) : Parcelable @Keep @@ -510,6 +515,28 @@ data class RewardsAnnotation( } } +@Keep +@JsonClass(generateAdapter = true) +@Parcelize +data class RewardsAnnotationGroup( + @Json(name = "severity_level") + val severityLevel: SeverityLevel?, + val group: String?, + val title: String?, + val message: String?, + @Json(name = "doc_url") + val docUrl: String?, +) : Parcelable { + fun toAnnotationGroupCode(): AnnotationGroupCode { + return try { + AnnotationGroupCode.valueOf(group ?: String.empty()) + } catch (e: IllegalArgumentException) { + Timber.w(e) + AnnotationGroupCode.UNKNOWN + } + } +} + @Keep @JsonClass(generateAdapter = true) @Parcelize @@ -555,3 +582,9 @@ enum class Relation { followed } +enum class SeverityLevel { + INFO, + WARNING, + ERROR +} + diff --git a/app/src/main/java/com/weatherxm/ui/cellinfo/CellInfoActivity.kt b/app/src/main/java/com/weatherxm/ui/cellinfo/CellInfoActivity.kt index 30e4610d3..cc68fcb91 100644 --- a/app/src/main/java/com/weatherxm/ui/cellinfo/CellInfoActivity.kt +++ b/app/src/main/java/com/weatherxm/ui/cellinfo/CellInfoActivity.kt @@ -8,7 +8,6 @@ import com.weatherxm.data.Resource import com.weatherxm.data.Status import com.weatherxm.databinding.ActivityCellInfoBinding import com.weatherxm.ui.common.Contracts -import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.UIDevice import com.weatherxm.ui.common.applyInsets import com.weatherxm.ui.common.parcelable @@ -127,15 +126,15 @@ class CellInfoActivity : BaseActivity(), CellDeviceListener { return } - if (device.relation == DeviceRelation.FOLLOWED) { + if (device.isFollowed()) { navigator.showHandleFollowDialog(this, false, device.name) { model.unFollowStation(device.id) } - } else if (device.relation == DeviceRelation.UNFOLLOWED && !device.isOnline()) { + } else if (device.isUnfollowed() && !device.isOnline()) { navigator.showHandleFollowDialog(this, true, device.name) { model.followStation(device.id) } - } else if (device.relation == DeviceRelation.UNFOLLOWED) { + } else if (device.isUnfollowed()) { model.followStation(device.id) } } diff --git a/app/src/main/java/com/weatherxm/ui/common/UIModels.kt b/app/src/main/java/com/weatherxm/ui/common/UIModels.kt index d750163f9..0e9606d11 100644 --- a/app/src/main/java/com/weatherxm/ui/common/UIModels.kt +++ b/app/src/main/java/com/weatherxm/ui/common/UIModels.kt @@ -11,6 +11,7 @@ import com.weatherxm.data.Hex import com.weatherxm.data.HourlyWeather import com.weatherxm.data.Location import com.weatherxm.data.QoDErrorAffects +import com.weatherxm.data.RewardsAnnotationGroup import com.weatherxm.data.RewardsAnnotations import com.weatherxm.data.RewardsObject import com.weatherxm.data.Transaction @@ -57,7 +58,8 @@ data class UIRewardObject( var periodMaxReward: Float? = null, var timelineTitle: String? = null, var timelineScores: List = emptyList(), - var annotations: MutableList = mutableListOf() + var annotations: MutableList = mutableListOf(), + var annotationSummary: List = mutableListOf(), ) : Parcelable { constructor( context: Context, @@ -89,6 +91,9 @@ data class UIRewardObject( periodMaxReward = rewards.periodMaxReward setTimelineTitle(context, rewards.timeline?.referenceDate, fromDate, toDate) timelineScores = rewards.timeline?.rewardScores ?: mutableListOf() + annotationSummary = rewards.annotationSummary?.sortedByDescending { + it.severityLevel?.ordinal + } ?: mutableListOf() setAnnotations(hideAnnotationsThreshold, rewards.annotations) } @@ -104,6 +109,9 @@ data class UIRewardObject( periodMaxReward = tx.dailyReward setTimelineTitle(context, tx.timeline?.referenceDate) timelineScores = tx.timeline?.rewardScores ?: mutableListOf() + annotationSummary = tx.annotationSummary?.sortedByDescending { + it.severityLevel?.ordinal + } ?: mutableListOf() setAnnotations(hideAnnotationsThreshold, tx.annotations) } @@ -234,6 +242,12 @@ data class UIDevice( ) } + fun isOwned(): Boolean = relation == DeviceRelation.OWNED + + fun isUnfollowed(): Boolean = relation == DeviceRelation.UNFOLLOWED + + fun isFollowed(): Boolean = relation == DeviceRelation.FOLLOWED + fun needsUpdate(): Boolean { return !currentFirmware.equals(assignedFirmware) && !assignedFirmware.isNullOrEmpty() } @@ -338,7 +352,7 @@ data class DevicesSortFilterOptions( return when (filterType) { DevicesFilterType.ALL -> devices DevicesFilterType.OWNED -> devices.filter { - it.relation == DeviceRelation.OWNED + it.isOwned() } DevicesFilterType.FAVORITES -> devices.filter { it.relation == DeviceRelation.FOLLOWED @@ -453,3 +467,10 @@ enum class AnnotationCode : Parcelable { UNIDENTIFIED_ANOMALOUS_CHANGE, UNKNOWN } + +@Parcelize +enum class AnnotationGroupCode : Parcelable { + NO_WALLET, + LOCATION_NOT_VERIFIED, + UNKNOWN +} diff --git a/app/src/main/java/com/weatherxm/ui/components/MessageCardView.kt b/app/src/main/java/com/weatherxm/ui/components/MessageCardView.kt index ea75f5a66..7b3dddc63 100644 --- a/app/src/main/java/com/weatherxm/ui/components/MessageCardView.kt +++ b/app/src/main/java/com/weatherxm/ui/components/MessageCardView.kt @@ -8,7 +8,9 @@ import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes import androidx.annotation.StringRes +import androidx.core.content.res.ResourcesCompat import com.google.android.material.button.MaterialButton.ICON_GRAVITY_END import com.google.android.material.button.MaterialButton.ICON_GRAVITY_START import com.weatherxm.R @@ -111,10 +113,10 @@ class MessageCardView : LinearLayout { return this } - fun title(subtitle: String?): MessageCardView { + fun title(title: String?): MessageCardView { binding.title.apply { - text = subtitle - visibility = if (subtitle != null) View.VISIBLE else View.GONE + text = title + visibility = if (title != null) View.VISIBLE else View.GONE } return this } @@ -137,12 +139,21 @@ class MessageCardView : LinearLayout { return this } + fun setIcon(@DrawableRes drawableResId: Int): MessageCardView { + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable(resources, drawableResId, context.theme) + ) + binding.icon.setVisible(true) + return this + } + fun setIconColor(@ColorRes colorResId: Int): MessageCardView { binding.icon.setColor(colorResId) return this } fun setStrokeColor(@ColorRes colorResId: Int): MessageCardView { + binding.card.strokeWidth = 2 binding.card.strokeColor = context.getColor(colorResId) return this } diff --git a/app/src/main/java/com/weatherxm/ui/devicealerts/DeviceAlertsAdapter.kt b/app/src/main/java/com/weatherxm/ui/devicealerts/DeviceAlertsAdapter.kt index 618123fb9..68a23ed73 100644 --- a/app/src/main/java/com/weatherxm/ui/devicealerts/DeviceAlertsAdapter.kt +++ b/app/src/main/java/com/weatherxm/ui/devicealerts/DeviceAlertsAdapter.kt @@ -8,7 +8,6 @@ import androidx.recyclerview.widget.RecyclerView import com.weatherxm.R import com.weatherxm.databinding.ListItemAlertBinding import com.weatherxm.ui.common.DeviceAlert -import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.UIDevice import com.weatherxm.ui.common.setVisible @@ -46,7 +45,7 @@ class DeviceAlertsAdapter( } showWarningCard() } else if (item == DeviceAlert.OFFLINE) { - val messageResId = if (device?.relation == DeviceRelation.OWNED) { + val messageResId = if (device?.isOwned() == true) { R.string.station_offline_alert_message } else { R.string.no_data_message_public_device diff --git a/app/src/main/java/com/weatherxm/ui/devicedetails/DeviceDetailsActivity.kt b/app/src/main/java/com/weatherxm/ui/devicedetails/DeviceDetailsActivity.kt index 175dc0cf8..37a89f9c3 100644 --- a/app/src/main/java/com/weatherxm/ui/devicedetails/DeviceDetailsActivity.kt +++ b/app/src/main/java/com/weatherxm/ui/devicedetails/DeviceDetailsActivity.kt @@ -234,11 +234,11 @@ class DeviceDetailsActivity : BaseActivity() { } with(binding.toolbar) { - if (device.relation == DeviceRelation.FOLLOWED) { + if (device.isFollowed()) { if (menu.findItem(R.id.settings) == null) { menu.add(Menu.NONE, R.id.settings, 1, R.string.station_settings) } - } else if (device.relation == DeviceRelation.UNFOLLOWED) { + } else if (device.isUnfollowed()) { menu.removeItem(R.id.settings) } } @@ -268,15 +268,15 @@ class DeviceDetailsActivity : BaseActivity() { return } - if (model.device.relation == DeviceRelation.FOLLOWED) { + if (model.device.isFollowed()) { navigator.showHandleFollowDialog(this, false, model.device.name) { model.unFollowStation() } - } else if (model.device.relation == DeviceRelation.UNFOLLOWED && !model.device.isOnline()) { + } else if (model.device.isUnfollowed() && !model.device.isOnline()) { navigator.showHandleFollowDialog(this, true, model.device.name) { model.followStation() } - } else if (model.device.relation == DeviceRelation.UNFOLLOWED) { + } else if (model.device.isUnfollowed()) { model.followStation() } } diff --git a/app/src/main/java/com/weatherxm/ui/devicedetails/current/CurrentFragment.kt b/app/src/main/java/com/weatherxm/ui/devicedetails/current/CurrentFragment.kt index d8ace9051..2a8d45650 100644 --- a/app/src/main/java/com/weatherxm/ui/devicedetails/current/CurrentFragment.kt +++ b/app/src/main/java/com/weatherxm/ui/devicedetails/current/CurrentFragment.kt @@ -80,12 +80,8 @@ class CurrentFragment : BaseFragment() { navigator.showHistoryActivity(requireContext(), model.device) } - binding.followCard.setVisible( - model.device.relation == DeviceRelation.UNFOLLOWED - ) - binding.historicalCharts.isEnabled = - model.device.relation != DeviceRelation.UNFOLLOWED - + binding.followCard.setVisible(model.device.isUnfollowed()) + binding.historicalCharts.isEnabled = !model.device.isUnfollowed() binding.followPromptBtn.setOnClickListener { handleFollowClick() } @@ -109,11 +105,11 @@ class CurrentFragment : BaseFragment() { return } - if (model.device.relation == DeviceRelation.UNFOLLOWED && !model.device.isOnline()) { + if (model.device.isUnfollowed() && !model.device.isOnline()) { navigator.showHandleFollowDialog(activity, true, model.device.name) { parentModel.followStation() } - } else if (model.device.relation == DeviceRelation.UNFOLLOWED) { + } else if (model.device.isUnfollowed()) { parentModel.followStation() } } @@ -122,12 +118,8 @@ class CurrentFragment : BaseFragment() { binding.progress.visibility = View.INVISIBLE setAlerts(device) binding.currentWeatherCard.setData(device.currentWeather) - - binding.followCard.setVisible( - device.relation == DeviceRelation.UNFOLLOWED - ) - binding.historicalCharts.isEnabled = - device.relation != DeviceRelation.UNFOLLOWED + binding.followCard.setVisible(device.isUnfollowed()) + binding.historicalCharts.isEnabled = !device.isUnfollowed() } private fun setAlerts(device: UIDevice) { diff --git a/app/src/main/java/com/weatherxm/ui/devicedetails/forecast/ForecastViewModel.kt b/app/src/main/java/com/weatherxm/ui/devicedetails/forecast/ForecastViewModel.kt index b0bc896bc..dac6e5693 100644 --- a/app/src/main/java/com/weatherxm/ui/devicedetails/forecast/ForecastViewModel.kt +++ b/app/src/main/java/com/weatherxm/ui/devicedetails/forecast/ForecastViewModel.kt @@ -9,7 +9,6 @@ import com.weatherxm.data.ApiError import com.weatherxm.data.Failure import com.weatherxm.data.NetworkError.ConnectionTimeoutError import com.weatherxm.data.NetworkError.NoConnectionError -import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.UIDevice import com.weatherxm.ui.common.UIError import com.weatherxm.ui.common.UIForecast @@ -45,7 +44,7 @@ class ForecastViewModel( * * Or do not fetch forecast if we this device is UNFOLLOWED */ - if (device.isDeviceFromSearchResult || device.relation == DeviceRelation.UNFOLLOWED) { + if (device.isDeviceFromSearchResult || device.isUnfollowed()) { return } onLoading.postValue(true) diff --git a/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsActivity.kt b/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsActivity.kt index 5482f6a46..759ba70c9 100644 --- a/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsActivity.kt +++ b/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsActivity.kt @@ -132,7 +132,7 @@ class DeviceSettingsActivity : BaseActivity() { binding.editLocationBtn.setOnClickListener { navigator.showEditLocation(editLocationLauncher, this, model.device) } - binding.editLocationBtn.setVisible(model.device.relation == DeviceRelation.OWNED) + binding.editLocationBtn.setVisible(model.device.isOwned()) if (model.device.relation == DeviceRelation.FOLLOWED) { binding.locationDesc.setHtml( @@ -155,7 +155,7 @@ class DeviceSettingsActivity : BaseActivity() { } private fun updateMinimap() { - val deviceMapLocation = if (model.device.relation == DeviceRelation.OWNED) { + val deviceMapLocation = if (model.device.isOwned()) { model.device.location } else { null diff --git a/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsViewModel.kt b/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsViewModel.kt index 30fed58c9..66d3f87f3 100644 --- a/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsViewModel.kt +++ b/app/src/main/java/com/weatherxm/ui/devicesettings/DeviceSettingsViewModel.kt @@ -8,7 +8,6 @@ import com.weatherxm.R import com.weatherxm.data.ApiError import com.weatherxm.data.BatteryState import com.weatherxm.data.DeviceProfile -import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.UIDevice import com.weatherxm.ui.common.UIError import com.weatherxm.ui.common.capitalizeWords @@ -220,7 +219,7 @@ class DeviceSettingsViewModel( } } device.currentFirmware?.let { current -> - if (device.needsUpdate() && device.relation == DeviceRelation.OWNED + if (device.needsUpdate() && device.isOwned() && usecase.shouldShowOTAPrompt(device) ) { add( diff --git a/app/src/main/java/com/weatherxm/ui/home/HomeViewModel.kt b/app/src/main/java/com/weatherxm/ui/home/HomeViewModel.kt index 46e65f6a9..3007539f9 100644 --- a/app/src/main/java/com/weatherxm/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/weatherxm/ui/home/HomeViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.weatherxm.data.DataError import com.weatherxm.data.SingleLiveEvent -import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.UIDevice import com.weatherxm.usecases.UserUseCase import com.weatherxm.util.Analytics @@ -34,7 +33,7 @@ class HomeViewModel( fun hasDevices() = hasDevices fun getWalletMissing(devices: List?) { - if (devices?.firstOrNull { it.relation == DeviceRelation.OWNED } == null) { + if (devices?.firstOrNull { it.isOwned() } == null) { hasDevices = false return } diff --git a/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardDetailsActivity.kt b/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardDetailsActivity.kt index 8d0945c39..dc791b09d 100644 --- a/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardDetailsActivity.kt +++ b/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardDetailsActivity.kt @@ -5,10 +5,8 @@ import android.view.MenuItem import com.google.firebase.analytics.FirebaseAnalytics import com.weatherxm.R import com.weatherxm.databinding.ActivityRewardDetailsBinding -import com.weatherxm.ui.common.AnnotationCode import com.weatherxm.ui.common.Contracts.ARG_DEVICE import com.weatherxm.ui.common.Contracts.ARG_REWARDS_OBJECT -import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.UIDevice import com.weatherxm.ui.common.UIRewardObject import com.weatherxm.ui.common.applyInsets @@ -52,7 +50,7 @@ class RewardDetailsActivity : BaseActivity(), RewardProblemsListener { binding.contactSupportBtn.setOnClickListener { navigator.openSupportCenter(this, Analytics.ParamValue.REWARD_ANNOTATIONS.paramValue) } - binding.contactSupportBtn.setVisible(device.relation == DeviceRelation.OWNED) + binding.contactSupportBtn.setVisible(device.isOwned()) binding.rewardsContentCard.updateUI( rewardsObject, @@ -85,14 +83,14 @@ class RewardDetailsActivity : BaseActivity(), RewardProblemsListener { binding.problemsFoundDesc.setHtml(getString(R.string.problems_found_desc, lostRewards)) } - val adapter = RewardProblemsAdapter(device, data.rewardScore, this) + val adapter = RewardProblemsAdapter(device, this) binding.problemsList.adapter = adapter - adapter.submitList(data.annotations) + adapter.submitList(data.annotationSummary) } private fun onMenuItem(menuItem: MenuItem): Boolean { - return if(menuItem.itemId == R.id.read_more) { + return if (menuItem.itemId == R.id.read_more) { analytics.trackEventSelectContent( contentType = Analytics.ParamValue.REWARD_DETAILS_READ_MORE.paramValue ) @@ -103,36 +101,26 @@ class RewardDetailsActivity : BaseActivity(), RewardProblemsListener { } } - override fun onAddWallet(annotation: AnnotationCode?) { - trackUserActionOnErrors(annotation) + override fun onAddWallet(group: String?) { + trackUserActionOnErrors(group) navigator.showConnectWallet(this) } - override fun onUpdateFirmware(device: UIDevice, annotation: AnnotationCode?) { - trackUserActionOnErrors(annotation) - navigator.showDeviceHeliumOTA(this, device, false) - } - - override fun onContactSupport(device: UIDevice, annotation: AnnotationCode?) { - trackUserActionOnErrors(annotation) - navigator.openSupportCenter(this, Analytics.ParamValue.REWARD_ANNOTATIONS.paramValue) - } - - override fun onDocumentation(url: String, annotation: AnnotationCode?) { - trackUserActionOnErrors(annotation) + override fun onDocumentation(url: String, group: String?) { + trackUserActionOnErrors(group) navigator.openWebsite(this, url) } - override fun onEditLocation(device: UIDevice, annotation: AnnotationCode?) { - trackUserActionOnErrors(annotation) + override fun onEditLocation(device: UIDevice, group: String?) { + trackUserActionOnErrors(group) navigator.showEditLocation(null, this, device) } - private fun trackUserActionOnErrors(annotation: AnnotationCode?) { + private fun trackUserActionOnErrors(group: String?) { analytics.trackEventUserAction( actionName = Analytics.ParamValue.REWARD_DETAILS_ERROR.paramValue, contentType = null, - Pair(FirebaseAnalytics.Param.ITEM_ID, annotation?.name ?: String.empty()) + Pair(FirebaseAnalytics.Param.ITEM_ID, group ?: String.empty()) ) } } diff --git a/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardProblemsAdapter.kt b/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardProblemsAdapter.kt index eba7f5a44..53a0e0c77 100644 --- a/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardProblemsAdapter.kt +++ b/app/src/main/java/com/weatherxm/ui/rewarddetails/RewardProblemsAdapter.kt @@ -6,24 +6,16 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.weatherxm.R +import com.weatherxm.data.RewardsAnnotationGroup +import com.weatherxm.data.SeverityLevel import com.weatherxm.databinding.ListItemRewardProblemBinding -import com.weatherxm.ui.common.AnnotationCode -import com.weatherxm.ui.common.DeviceRelation +import com.weatherxm.ui.common.AnnotationGroupCode import com.weatherxm.ui.common.UIDevice -import com.weatherxm.ui.common.UIRewardsAnnotation -import com.weatherxm.util.Rewards.getMessage -import com.weatherxm.util.Rewards.getRewardAnnotationBackgroundColor -import com.weatherxm.util.Rewards.getRewardAnnotationColor -import com.weatherxm.util.Rewards.getTitleResId -import com.weatherxm.util.Rewards.pointToDocsHome -import com.weatherxm.util.Rewards.pointToDocsTroubleshooting -import com.weatherxm.util.Rewards.pointToPoLPreLaunch class RewardProblemsAdapter( private val device: UIDevice, - private val rewardScore: Int?, private val listener: RewardProblemsListener -) : ListAdapter( +) : ListAdapter( RewardProblemsDiffCallback() ) { @@ -43,110 +35,83 @@ class RewardProblemsAdapter( inner class RewardProblemsViewHolder(private val binding: ListItemRewardProblemBinding) : RecyclerView.ViewHolder(binding.root) { - fun bind(item: UIRewardsAnnotation) { - with(binding.error) { - setBackground(getRewardAnnotationBackgroundColor(rewardScore)) - val annotationColor = getRewardAnnotationColor(rewardScore) - setIconColor(annotationColor) - setStrokeColor(annotationColor) - item.annotation?.getTitleResId()?.let { - title(it) - } - htmlMessage(item.getMessage(context, device)) { - if (item.annotation.pointToPoLPreLaunch()) { - listener.onDocumentation( - itemView.context.getString(R.string.docs_url_pol_algorithm), - item.annotation - ) + fun bind(item: RewardsAnnotationGroup) { + with(binding.annotationContainer) { + when (item.severityLevel) { + SeverityLevel.INFO -> { + setBackground(R.color.blueTint) + setStrokeColor(R.color.colorPrimaryVariant) + setIcon(R.drawable.ic_info) + setIconColor(R.color.colorPrimaryVariant) + } + SeverityLevel.WARNING -> { + setBackground(R.color.warningTint) + setStrokeColor(R.color.warning) + setIcon(R.drawable.ic_warn) + setIconColor(R.color.warning) + } + SeverityLevel.ERROR -> { + setBackground(R.color.errorTint) + setStrokeColor(R.color.error) + setIcon(R.drawable.ic_warn) + setIconColor(R.color.error) + } + else -> { + setBackground(R.color.blueTint) + setStrokeColor(R.color.colorPrimaryVariant) + setIcon(R.drawable.ic_info) + setIconColor(R.color.colorPrimaryVariant) } } - if (device.relation == DeviceRelation.OWNED) { - getAction(item.annotation) - } + title(item.title) + message(item.message) + setAction(item.toAnnotationGroupCode(), item.docUrl) } } - private fun getAction(annotation: AnnotationCode?) { + private fun setAction(code: AnnotationGroupCode?, docUrl: String?) { with(itemView.context) { - if (annotation == AnnotationCode.OBC && device.needsUpdate()) { - binding.error.action(getString(R.string.action_update_firmware)) { - listener.onUpdateFirmware(device, annotation) - } - } else if (annotation.pointToDocsHome()) { - binding.error.action(getString(R.string.read_more)) { - listener.onDocumentation(getString(R.string.docs_url), annotation) - } - } else if (annotation.pointToDocsTroubleshooting()) { - binding.error.action(getString(R.string.read_more)) { - listener.onDocumentation( - getString(R.string.docs_url_troubleshooting), - annotation - ) - } - } else if (annotation == AnnotationCode.NO_WALLET) { - binding.error.action(getString(R.string.add_wallet_now)) { - listener.onAddWallet(annotation) - } - } else if (annotation == AnnotationCode.CELL_CAPACITY_REACHED) { - binding.error.action(getString(R.string.read_more)) { - listener.onDocumentation( - getString(R.string.docs_url_cell_capacity), - annotation - ) - } - } else if (annotation == AnnotationCode.POL_THRESHOLD_NOT_REACHED) { - binding.error.action(getString(R.string.read_more)) { - listener.onDocumentation( - getString(R.string.docs_url_pol_algorithm), - annotation - ) - } - } else if (annotation == AnnotationCode.QOD_THRESHOLD_NOT_REACHED) { - binding.error.action(getString(R.string.read_more)) { - listener.onDocumentation( - getString(R.string.docs_url_qod_algorithm), - annotation - ) + if (code == AnnotationGroupCode.NO_WALLET && device.isOwned()) { + binding.annotationContainer.action(getString(R.string.add_wallet_now)) { + listener.onAddWallet(code.toString()) } - } else if (annotation == AnnotationCode.LOCATION_NOT_VERIFIED) { - binding.error.action(getString(R.string.edit_location)) { - listener.onEditLocation(device, annotation) + } else if (code == AnnotationGroupCode.LOCATION_NOT_VERIFIED && device.isOwned()) { + binding.annotationContainer.action(getString(R.string.edit_location)) { + listener.onEditLocation(device, code.toString()) } - } else if (annotation == AnnotationCode.UNKNOWN) { - binding.error.action(getString(R.string.contact_support_title)) { - listener.onContactSupport(device, annotation) + } else if (!docUrl.isNullOrEmpty()) { + binding.annotationContainer.action(getString(R.string.read_more)) { + listener.onDocumentation(docUrl, code.toString()) } } else { - // Do nothing, no action should be set. + // Do nothing } } } } } -class RewardProblemsDiffCallback : DiffUtil.ItemCallback() { +class RewardProblemsDiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame( - oldItem: UIRewardsAnnotation, - newItem: UIRewardsAnnotation + oldItem: RewardsAnnotationGroup, + newItem: RewardsAnnotationGroup ): Boolean { return oldItem == newItem } override fun areContentsTheSame( - oldItem: UIRewardsAnnotation, - newItem: UIRewardsAnnotation + oldItem: RewardsAnnotationGroup, + newItem: RewardsAnnotationGroup ): Boolean { - return oldItem.annotation == newItem.annotation && - oldItem.ratioOfAnnotation == newItem.ratioOfAnnotation && - oldItem.qodParametersAffected.size == newItem.qodParametersAffected.size + return oldItem.title == newItem.title && oldItem.message == newItem.message && + oldItem.severityLevel == newItem.severityLevel && oldItem.group == newItem.group && + oldItem.docUrl == newItem.docUrl } } interface RewardProblemsListener { - fun onAddWallet(annotation: AnnotationCode?) - fun onUpdateFirmware(device: UIDevice, annotation: AnnotationCode?) - fun onContactSupport(device: UIDevice, annotation: AnnotationCode?) - fun onDocumentation(url: String, annotation: AnnotationCode?) - fun onEditLocation(device: UIDevice, annotation: AnnotationCode?) + fun onAddWallet(group: String?) + fun onDocumentation(url: String, group: String?) + fun onEditLocation(device: UIDevice, group: String?) } diff --git a/app/src/main/java/com/weatherxm/usecases/DeviceDetailsUseCaseImpl.kt b/app/src/main/java/com/weatherxm/usecases/DeviceDetailsUseCaseImpl.kt index 309bf4abb..b0e793faa 100644 --- a/app/src/main/java/com/weatherxm/usecases/DeviceDetailsUseCaseImpl.kt +++ b/app/src/main/java/com/weatherxm/usecases/DeviceDetailsUseCaseImpl.kt @@ -37,7 +37,7 @@ class DeviceDetailsUseCaseImpl( ) : DeviceDetailsUseCase { override suspend fun getUserDevice(device: UIDevice): Either { - return if (device.relation == DeviceRelation.UNFOLLOWED) { + return if (device.isUnfollowed()) { explorerRepository.getCellDevice(device.cellIndex, device.id).map { it.toUIDevice().apply { this.relation = DeviceRelation.UNFOLLOWED @@ -50,9 +50,8 @@ class DeviceDetailsUseCaseImpl( deviceRepository.getUserDevice(device.id).map { it.toUIDevice().apply { val shouldShowOTAPrompt = deviceOTARepository.shouldShowOTAPrompt( - id, - assignedFirmware - ) && relation == DeviceRelation.OWNED + id, assignedFirmware + ) && isOwned() val alerts = mutableListOf() if (shouldShowOTAPrompt && profile == Helium && needsUpdate()) { alerts.add(DeviceAlert.NEEDS_UPDATE) diff --git a/app/src/main/java/com/weatherxm/usecases/DeviceListUseCaseImpl.kt b/app/src/main/java/com/weatherxm/usecases/DeviceListUseCaseImpl.kt index 7fe238bee..4caef8262 100644 --- a/app/src/main/java/com/weatherxm/usecases/DeviceListUseCaseImpl.kt +++ b/app/src/main/java/com/weatherxm/usecases/DeviceListUseCaseImpl.kt @@ -7,7 +7,6 @@ import com.weatherxm.data.repository.DeviceOTARepository import com.weatherxm.data.repository.DeviceRepository import com.weatherxm.data.repository.UserPreferencesRepository import com.weatherxm.ui.common.DeviceAlert -import com.weatherxm.ui.common.DeviceRelation import com.weatherxm.ui.common.DevicesFilterType import com.weatherxm.ui.common.DevicesGroupBy import com.weatherxm.ui.common.DevicesSortFilterOptions @@ -24,9 +23,8 @@ class DeviceListUseCaseImpl( val devices = response.map { val device = it.toUIDevice() val shouldShowOTAPrompt = deviceOTARepository.shouldShowOTAPrompt( - device.id, - device.assignedFirmware - ) && device.relation == DeviceRelation.OWNED + device.id, device.assignedFirmware + ) && device.isOwned() val alerts = mutableListOf() if (device.isActive == false) { alerts.add(DeviceAlert.OFFLINE) diff --git a/app/src/main/java/com/weatherxm/util/Rewards.kt b/app/src/main/java/com/weatherxm/util/Rewards.kt index 8c9ef3c0f..a58a548cc 100644 --- a/app/src/main/java/com/weatherxm/util/Rewards.kt +++ b/app/src/main/java/com/weatherxm/util/Rewards.kt @@ -104,7 +104,7 @@ object Rewards { context.getString(R.string.annotation_obc_desc, getAffectedParameters()) } AnnotationCode.SPIKE_INST -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_spikes_desc, getAffectedParameters()) } else { context.getString( @@ -113,21 +113,21 @@ object Rewards { } } AnnotationCode.NO_DATA -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_no_data_desc) } else { context.getString(R.string.annotation_no_data_public_desc) } } AnnotationCode.NO_MEDIAN -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_no_median_desc) } else { context.getString(R.string.annotation_no_median_public_desc) } } AnnotationCode.SHORT_CONST -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_short_const_desc, getAffectedParameters()) } else { context.getString( @@ -136,7 +136,7 @@ object Rewards { } } AnnotationCode.LONG_CONST -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_long_const_desc, getAffectedParameters()) } else { context.getString( @@ -148,7 +148,7 @@ object Rewards { context.getString(R.string.annotation_frozen_sensor_desc) } AnnotationCode.ANOMALOUS_INCREASE -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString( R.string.annotation_anom_increase_desc, getAffectedParameters() ) @@ -171,14 +171,14 @@ object Rewards { } } AnnotationCode.NO_WALLET -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_no_wallet_desc) } else { context.getString(R.string.annotation_no_wallet_public_desc) } } AnnotationCode.CELL_CAPACITY_REACHED -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_cell_capacity_reached_desc) } else { context.getString(R.string.annotation_cell_capacity_reached_public_desc) @@ -186,21 +186,21 @@ object Rewards { } AnnotationCode.RELOCATED -> context.getString(R.string.annotation_relocated_desc) AnnotationCode.POL_THRESHOLD_NOT_REACHED -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_pol_threshold_desc) } else { context.getString(R.string.annotation_pol_threshold_public_desc) } } AnnotationCode.QOD_THRESHOLD_NOT_REACHED -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_qod_threshold_desc) } else { context.getString(R.string.annotation_qod_threshold_public_desc) } } AnnotationCode.UNIDENTIFIED_ANOMALOUS_CHANGE -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString( R.string.annotation_unidentified_change_desc, getAffectedParameters() ) @@ -211,7 +211,7 @@ object Rewards { } } AnnotationCode.UNIDENTIFIED_SPIKE -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString( R.string.annotation_unidentified_spike_desc, getAffectedParameters() ) @@ -222,7 +222,7 @@ object Rewards { } } AnnotationCode.UNKNOWN -> { - if (device.relation == DeviceRelation.OWNED) { + if (device.isOwned()) { context.getString(R.string.annotation_unknown_desc) } else { context.getString(R.string.annotation_unknown_public_desc) diff --git a/app/src/main/res/layout/list_item_reward_problem.xml b/app/src/main/res/layout/list_item_reward_problem.xml index 84dd773d3..faf613bd6 100644 --- a/app/src/main/res/layout/list_item_reward_problem.xml +++ b/app/src/main/res/layout/list_item_reward_problem.xml @@ -8,15 +8,15 @@ android:orientation="vertical">