Implement getJSONStatus() and shortStatus()

This commit is contained in:
jbr7rr 2023-06-17 15:29:35 +02:00
parent 082b365520
commit 996ef8e5ef
7 changed files with 234 additions and 69 deletions

View file

@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.os.IBinder import android.os.IBinder
import android.text.format.DateFormat
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.core.ui.toast.ToastUtils import info.nightscout.core.ui.toast.ToastUtils
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
@ -31,6 +32,7 @@ import info.nightscout.interfaces.pump.defs.PumpType
import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.queue.CustomCommand import info.nightscout.interfaces.queue.CustomCommand
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.DecimalFormatter
import info.nightscout.interfaces.utils.TimeChangeType import info.nightscout.interfaces.utils.TimeChangeType
import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState
import info.nightscout.pump.medtrum.ui.MedtrumOverviewFragment import info.nightscout.pump.medtrum.ui.MedtrumOverviewFragment
@ -222,7 +224,7 @@ import kotlin.math.round
get() = medtrumPump.reservoir get() = medtrumPump.reservoir
override val batteryLevel: Int override val batteryLevel: Int
get() = 0 // TODO get() = 0 // We cannot determine battery level (yet)
@Synchronized @Synchronized
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
@ -317,7 +319,43 @@ import kotlin.math.round
} }
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject { override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
return JSONObject() // TODO val now = System.currentTimeMillis()
if (medtrumPump.lastConnection + 60 * 60 * 1000L < System.currentTimeMillis()) {
return JSONObject()
}
val pumpJson = JSONObject()
val status = JSONObject()
val extended = JSONObject()
try {
status.put(
"status", if (!isSuspended()) "normal"
else if (isInitialized() && isSuspended()) "suspended"
else "no active patch"
)
status.put("timestamp", dateUtil.toISOString(medtrumPump.lastConnection))
if (medtrumPump.lastBolusTime != 0L) {
extended.put("lastBolus", dateUtil.dateAndTimeString(medtrumPump.lastBolusTime))
extended.put("lastBolusAmount", medtrumPump.lastBolusAmount)
}
val tb = pumpSync.expectedPumpState().temporaryBasal
if (tb != null) {
extended.put("TempBasalAbsoluteRate", tb.convertedToAbsolute(now, profile))
extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.timestamp))
extended.put("TempBasalRemaining", tb.plannedRemainingMinutes)
}
extended.put("BaseBasalRate", baseBasalRate)
try {
extended.put("ActiveProfile", profileName)
} catch (ignored: Exception) {
}
pumpJson.put("status", status)
pumpJson.put("extended", extended)
pumpJson.put("reservoir", medtrumPump.reservoir.toInt())
pumpJson.put("clock", dateUtil.toISOString(now))
} catch (e: JSONException) {
aapsLogger.error(LTag.PUMP, "Unhandled exception: $e")
}
return pumpJson
} }
override fun manufacturer(): ManufacturerType { override fun manufacturer(): ManufacturerType {
@ -336,7 +374,20 @@ import kotlin.math.round
get() = PumpDescription(medtrumPump.pumpType) get() = PumpDescription(medtrumPump.pumpType)
override fun shortStatus(veryShort: Boolean): String { override fun shortStatus(veryShort: Boolean): String {
return ""// TODO var ret = ""
if (medtrumPump.lastConnection != 0L) {
val agoMillis = System.currentTimeMillis() - medtrumPump.lastConnection
val agoMin = (agoMillis / 60.0 / 1000.0).toInt()
ret += "LastConn: $agoMin minAgo\n"
}
if (medtrumPump.lastBolusTime != 0L)
ret += "LastBolus: ${DecimalFormatter.to2Decimal(medtrumPump.lastBolusAmount)}U @${DateFormat.format("HH:mm", medtrumPump.lastBolusTime)}\n"
if (medtrumPump.tempBasalInProgress)
ret += "Temp: ${medtrumPump.temporaryBasalToString()}\n"
ret += "Res: ${DecimalFormatter.to0Decimal(medtrumPump.reservoir)}U\n"
return ret
} }
override val isFakingTempsByExtendedBoluses: Boolean = false override val isFakingTempsByExtendedBoluses: Boolean = false
@ -375,7 +426,7 @@ import kotlin.math.round
} }
override fun setUserOptions(): PumpEnactResult { override fun setUserOptions(): PumpEnactResult {
if (!isInitialized()) { if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false) val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized" result.comment = "pump not initialized"
return result return result

View file

@ -68,19 +68,13 @@ class MedtrumPump @Inject constructor(
private var _lastBasalType: MutableStateFlow<BasalType> = MutableStateFlow(BasalType.NONE) private var _lastBasalType: MutableStateFlow<BasalType> = MutableStateFlow(BasalType.NONE)
val lastBasalTypeFlow: StateFlow<BasalType> = _lastBasalType val lastBasalTypeFlow: StateFlow<BasalType> = _lastBasalType
var lastBasalType: BasalType val lastBasalType: BasalType
get() = _lastBasalType.value get() = _lastBasalType.value
set(value) {
_lastBasalType.value = value
}
private val _lastBasalRate = MutableStateFlow(0.0) private val _lastBasalRate = MutableStateFlow(0.0)
val lastBasalRateFlow: StateFlow<Double> = _lastBasalRate val lastBasalRateFlow: StateFlow<Double> = _lastBasalRate
var lastBasalRate: Double val lastBasalRate: Double
get() = _lastBasalRate.value get() = _lastBasalRate.value
set(value) {
_lastBasalRate.value = value
}
private val _reservoir = MutableStateFlow(0.0) private val _reservoir = MutableStateFlow(0.0)
val reservoirFlow: StateFlow<Double> = _reservoir val reservoirFlow: StateFlow<Double> = _reservoir
@ -141,6 +135,22 @@ class MedtrumPump @Inject constructor(
sp.putString(R.string.key_actual_basal_profile, encodedString ?: "") sp.putString(R.string.key_actual_basal_profile, encodedString ?: "")
} }
private var _lastBolusTime = 0L // Time in ms!
var lastBolusTime: Long
get() = _lastBolusTime
set(value) {
_lastBolusTime = value
sp.putLong(R.string.key_last_bolus_time, value)
}
private var _lastBolusAmount = 0.0
var lastBolusAmount: Double
get() = _lastBolusAmount
set(value) {
_lastBolusAmount = value
sp.putDouble(R.string.key_last_bolus_amount, value)
}
private var _lastConnection = 0L // Time in ms! private var _lastConnection = 0L // Time in ms!
var lastConnection: Long var lastConnection: Long
get() = _lastConnection get() = _lastConnection
@ -181,7 +191,6 @@ class MedtrumPump @Inject constructor(
var lastTimeReceivedFromPump = 0L // Time in ms! // TODO: Consider removing as is not used? var lastTimeReceivedFromPump = 0L // Time in ms! // TODO: Consider removing as is not used?
var suspendTime = 0L // Time in ms! var suspendTime = 0L // Time in ms!
var patchAge = 0L // Time in seconds?! // TODO: Not used var patchAge = 0L // Time in seconds?! // TODO: Not used
@ -196,11 +205,18 @@ class MedtrumPump @Inject constructor(
var bolusStopForced = false // bolus forced to stop by user var bolusStopForced = false // bolus forced to stop by user
var bolusDone = false // success end var bolusDone = false // success end
// Last basal status update // Last basal status update (from pump)
// TODO maybe make basal parameters private? So we are forced to update trough handleBasalStatusUpdate private var _lastBasalSequence = 0
var lastBasalSequence = 0 val lastBasalSequence: Int
var lastBasalPatchId = 0L get() = _lastBasalSequence
var lastBasalStartTime = 0L
private var _lastBasalPatchId = 0L
val lastBasalPatchId: Long
get() = _lastBasalPatchId
private var _lastBasalStartTime = 0L
val lastBasalStartTime: Long
get() = _lastBasalStartTime
val baseBasalRate: Double val baseBasalRate: Double
get() = getHourlyBasalFromMedtrumProfileArray(actualBasalProfile, dateUtil.now()) get() = getHourlyBasalFromMedtrumProfileArray(actualBasalProfile, dateUtil.now())
@ -225,6 +241,8 @@ class MedtrumPump @Inject constructor(
// Load stuff from SP // Load stuff from SP
_patchSessionToken = sp.getLong(R.string.key_session_token, 0L) _patchSessionToken = sp.getLong(R.string.key_session_token, 0L)
_lastConnection = sp.getLong(R.string.key_last_connection, 0L) _lastConnection = sp.getLong(R.string.key_last_connection, 0L)
_lastBolusTime = sp.getLong(R.string.key_last_bolus_time, 0L)
_lastBolusAmount = sp.getDouble(R.string.key_last_bolus_amount, 0.0)
_currentSequenceNumber = sp.getInt(R.string.key_current_sequence_number, 0) _currentSequenceNumber = sp.getInt(R.string.key_current_sequence_number, 0)
_patchId = sp.getLong(R.string.key_patch_id, 0L) _patchId = sp.getLong(R.string.key_patch_id, 0L)
_syncedSequenceNumber = sp.getInt(R.string.key_synced_sequence_number, 0) _syncedSequenceNumber = sp.getInt(R.string.key_synced_sequence_number, 0)
@ -243,7 +261,7 @@ class MedtrumPump @Inject constructor(
fun loadUserSettingsFromSP() { fun loadUserSettingsFromSP() {
desiredPatchExpiration = sp.getBoolean(info.nightscout.pump.medtrum.R.string.key_patch_expiration, false) desiredPatchExpiration = sp.getBoolean(info.nightscout.pump.medtrum.R.string.key_patch_expiration, false)
val alarmSettingCode = sp.getString(info.nightscout.pump.medtrum.R.string.key_alarm_setting, AlarmSetting.LIGHT_VIBRATE_AND_BEEP.code.toString())?.toByte() val alarmSettingCode = sp.getString(info.nightscout.pump.medtrum.R.string.key_alarm_setting, AlarmSetting.LIGHT_VIBRATE_AND_BEEP.code.toString()).toByte()
desiredAlarmSetting = AlarmSetting.values().firstOrNull { it.code == alarmSettingCode } ?: AlarmSetting.LIGHT_VIBRATE_AND_BEEP desiredAlarmSetting = AlarmSetting.values().firstOrNull { it.code == alarmSettingCode } ?: AlarmSetting.LIGHT_VIBRATE_AND_BEEP
desiredHourlyMaxInsulin = sp.getInt(info.nightscout.pump.medtrum.R.string.key_hourly_max_insulin, 40) desiredHourlyMaxInsulin = sp.getInt(info.nightscout.pump.medtrum.R.string.key_hourly_max_insulin, 40)
desiredDailyMaxInsulin = sp.getInt(info.nightscout.pump.medtrum.R.string.key_daily_max_insulin, 180) desiredDailyMaxInsulin = sp.getInt(info.nightscout.pump.medtrum.R.string.key_daily_max_insulin, 180)
@ -370,17 +388,17 @@ class MedtrumPump @Inject constructor(
} }
// Update medtrum pump state // Update medtrum pump state
lastBasalType = basalType _lastBasalType.value = basalType
lastBasalRate = basalRate _lastBasalRate.value = basalRate
lastBasalSequence = basalSequence _lastBasalSequence = basalSequence
if (basalSequence > currentSequenceNumber) { if (basalSequence > currentSequenceNumber) {
currentSequenceNumber = basalSequence currentSequenceNumber = basalSequence
} }
lastBasalPatchId = basalPatchId _lastBasalPatchId = basalPatchId
if (basalPatchId != patchId) { if (basalPatchId != patchId) {
aapsLogger.error(LTag.PUMP, "handleBasalStatusUpdate: WTF? PatchId in status update does not match current patchId!") aapsLogger.error(LTag.PUMP, "handleBasalStatusUpdate: WTF? PatchId in status update does not match current patchId!")
} }
lastBasalStartTime = basalStartTime _lastBasalStartTime = basalStartTime
} }
fun handleStopStatusUpdate(stopSequence: Int, stopPatchId: Long) { fun handleStopStatusUpdate(stopSequence: Int, stopPatchId: Long) {
@ -418,4 +436,8 @@ class MedtrumPump @Inject constructor(
"handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START (FAKE)" "handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START (FAKE)"
) )
} }
fun temporaryBasalToString(): String {
if (!tempBasalInProgress) return ""
return tempBasalAbsoluteRate.toString() + "U/h"
}
} }

View file

@ -125,7 +125,12 @@ class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int
// detailedInfo can be from another similar record. Reinsert // detailedInfo can be from another similar record. Reinsert
detailedBolusInfoStorage.add(detailedBolusInfo) detailedBolusInfoStorage.add(detailedBolusInfo)
} }
if (bolusStartTime > medtrumPump.lastBolusTime) {
medtrumPump.lastBolusTime = bolusStartTime
medtrumPump.lastBolusAmount = bolusNormalDelivered
}
} else { } else {
// TODO: at least record the bolus
aapsLogger.error( aapsLogger.error(
LTag.PUMPCOMM, LTag.PUMPCOMM,
"from record: EVENT BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) " + "Bolus type: $bolusType not supported" "from record: EVENT BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) " + "Bolus type: $bolusType not supported"

View file

@ -53,6 +53,10 @@ class MedtrumOverviewViewModel @Inject constructor(
val lastConnectionMinAgo: LiveData<String> val lastConnectionMinAgo: LiveData<String>
get() = _lastConnectionMinAgo get() = _lastConnectionMinAgo
private val _lastBolus = SingleLiveEvent<String>()
val lastBolus: LiveData<String>
get() = _lastBolus
private val _activeAlarms = SingleLiveEvent<String>() private val _activeAlarms = SingleLiveEvent<String>()
val activeAlarms: LiveData<String> val activeAlarms: LiveData<String>
get() = _activeAlarms get() = _activeAlarms
@ -64,7 +68,7 @@ class MedtrumOverviewViewModel @Inject constructor(
private val _fwVersion = SingleLiveEvent<String>() private val _fwVersion = SingleLiveEvent<String>()
val fwVersion: LiveData<String> val fwVersion: LiveData<String>
get() = _fwVersion get() = _fwVersion
private val _patchNo = SingleLiveEvent<String>() private val _patchNo = SingleLiveEvent<String>()
val patchNo: LiveData<String> val patchNo: LiveData<String>
get() = _patchNo get() = _patchNo
@ -148,6 +152,21 @@ class MedtrumOverviewViewModel @Inject constructor(
val agoMilliseconds = System.currentTimeMillis() - medtrumPump.lastConnection val agoMilliseconds = System.currentTimeMillis() - medtrumPump.lastConnection
val agoMinutes = agoMilliseconds / 1000 / 60 val agoMinutes = agoMilliseconds / 1000 / 60
_lastConnectionMinAgo.postValue(rh.gs(info.nightscout.shared.R.string.minago, agoMinutes)) _lastConnectionMinAgo.postValue(rh.gs(info.nightscout.shared.R.string.minago, agoMinutes))
if (medtrumPump.lastBolusTime != 0L) {
val agoMilliseconds = System.currentTimeMillis() - medtrumPump.lastBolusTime
val agoHours = agoMilliseconds.toDouble() / 60.0 / 60.0 / 1000.0
if (agoHours < 6)
// max 6h back
_lastBolus.postValue(
dateUtil.timeString(medtrumPump.lastBolusTime) + " " + dateUtil.sinceString(medtrumPump.lastBolusTime, rh) + " " + rh.gs(
info.nightscout.interfaces.R.string
.format_insulin_units, medtrumPump
.lastBolusAmount
)
)
else
_lastBolus.postValue("")
}
// TODO: Update these values // TODO: Update these values
// _activeAlarms.postValue(rh.gs(R.string.active_alarms, pump.activeAlarms)) // _activeAlarms.postValue(rh.gs(R.string.active_alarms, pump.activeAlarms))

View file

@ -34,6 +34,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginVertical="3dp"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
@ -68,10 +69,20 @@
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginStart="20dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="5dp"
android:background="?android:attr/dividerHorizontal" />
<!-- Last Connected --> <!-- Last Connected -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginVertical="3dp"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
@ -236,6 +247,104 @@
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<!-- Last bolus -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="3dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/last_bolus_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/last_bolus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text='@{viewmodel.lastBolus}'
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginStart="20dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="5dp"
android:background="?android:attr/dividerHorizontal" />
<!-- Active Alarms -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="3dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/active_alarms_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/active_alarms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@{viewmodel.activeAlarms}"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginStart="20dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="5dp"
android:background="?android:attr/dividerHorizontal" />
<!-- Reservoir --> <!-- Reservoir -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -316,47 +425,6 @@
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<!-- Active Alarms -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="3dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/active_alarms_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/active_alarms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@{viewmodel.activeAlarms}"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View <View
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="2dip" android:layout_height="2dip"

View file

@ -10,6 +10,8 @@
<string name="key_medtrumpump_settings" translatable="false">medtrumpump_settings</string> <string name="key_medtrumpump_settings" translatable="false">medtrumpump_settings</string>
<string name="key_pump_state" translatable="false">pump_state</string> <string name="key_pump_state" translatable="false">pump_state</string>
<string name="key_last_connection" translatable="false">last_connection</string> <string name="key_last_connection" translatable="false">last_connection</string>
<string name="key_last_bolus_time" translatable="false">last_bolus_time</string>
<string name="key_last_bolus_amount" translatable="false">last_bolus_amount</string>
<string name="key_session_token" translatable="false">medtrum_session_token</string> <string name="key_session_token" translatable="false">medtrum_session_token</string>
<string name="key_patch_id" translatable="false">patch_id</string> <string name="key_patch_id" translatable="false">patch_id</string>
<string name="key_device_type" translatable="false">device_type</string> <string name="key_device_type" translatable="false">device_type</string>
@ -35,9 +37,7 @@
<string name="last_connection_label">Last connected</string> <string name="last_connection_label">Last connected</string>
<string name="pump_state_label">Pump state</string> <string name="pump_state_label">Pump state</string>
<string name="active_alarms_label">Active alarms</string> <string name="active_alarms_label">Active alarms</string>
<string name="reservoir_label"> Reservoir</string>
<string name="reservoir_level"> %.2f U</string> <string name="reservoir_level"> %.2f U</string>
<string name="battery_label">Battery</string>
<string name="battery_voltage"> %.2f V</string> <string name="battery_voltage"> %.2f V</string>
<string name="basal_type_label">Basal type</string> <string name="basal_type_label">Basal type</string>
<string name="basal_rate_label">Basal rate</string> <string name="basal_rate_label">Basal rate</string>

View file

@ -9,7 +9,7 @@
<EditTextPreference <EditTextPreference
android:key="@string/key_sn_input" android:key="@string/key_sn_input"
android:summary="@string/sn_input_summary" android:dialogMessage="@string/sn_input_summary"
android:singleLine="true" android:singleLine="true"
android:title="@string/sn_input_title" /> android:title="@string/sn_input_title" />