Wear: ad quick wizard tile

This commit is contained in:
Andries Smit 2022-01-18 20:54:39 +01:00
parent 2c7cf27047
commit 0233db8226
27 changed files with 790 additions and 499 deletions

View file

@ -1,12 +1,18 @@
package info.nightscout.androidaps.plugins.general.overview.activities
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.ItemTouchHelper.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import info.nightscout.androidaps.R
@ -20,6 +26,8 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject
@ -30,20 +38,95 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var quickWizard: QuickWizard
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var sp: SP
private var disposable: CompositeDisposable = CompositeDisposable()
private lateinit var binding: OverviewQuickwizardlistActivityBinding
private val itemTouchHelper by lazy {
val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val adapter = recyclerView.adapter as RecyclerViewAdapter
val from = viewHolder.layoutPosition
val to = target.layoutPosition
adapter.moveItem(from, to)
adapter.notifyItemMoved(from, to)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ACTION_STATE_DRAG) {
viewHolder?.itemView?.alpha = 0.5f
}
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.alpha = 1.0f
val adapter = recyclerView.adapter as RecyclerViewAdapter
adapter.onDrop()
}
}
ItemTouchHelper(simpleItemTouchCallback)
}
fun startDragging(viewHolder: RecyclerView.ViewHolder) {
itemTouchHelper.startDrag(viewHolder)
}
private inner class RecyclerViewAdapter(var fragmentManager: FragmentManager) : RecyclerView.Adapter<RecyclerViewAdapter.QuickWizardEntryViewHolder>() {
@SuppressLint("ClickableViewAccessibility")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuickWizardEntryViewHolder {
return QuickWizardEntryViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.overview_quickwizardlist_item, parent, false), fragmentManager)
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.overview_quickwizardlist_item, parent, false)
val viewHolder = QuickWizardEntryViewHolder(itemView, fragmentManager)
viewHolder.handleView.setOnTouchListener { _, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
startDragging(viewHolder)
}
return@setOnTouchListener true
}
return viewHolder
}
override fun onBindViewHolder(holder: QuickWizardEntryViewHolder, position: Int) {
holder.from.text = dateUtil.timeString(quickWizard[position].validFromDate())
holder.to.text = dateUtil.timeString(quickWizard[position].validToDate())
val wearControl = sp.getBoolean(R.string.key_wear_control, false)
if (wearControl) {
holder.handleView.visibility = View.VISIBLE
} else {
holder.handleView.visibility = View.GONE
}
if (quickWizard[position].device() == QuickWizardEntry.DEVICE_ALL) {
holder.device.visibility = View.GONE
} else {
holder.device.visibility = View.VISIBLE
holder.device.setImageResource(
when (quickWizard[position].device()) {
QuickWizardEntry.DEVICE_WATCH -> R.drawable.ic_watch
else -> R.drawable.ic_smartphone
}
)
}
holder.buttonText.text = quickWizard[position].buttonText()
holder.carbs.text = rh.gs(R.string.format_carbs, quickWizard[position].carbs())
}
@ -55,6 +138,8 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() {
val buttonText: TextView = itemView.findViewById(R.id.overview_quickwizard_item_buttonText)
val carbs: TextView = itemView.findViewById(R.id.overview_quickwizard_item_carbs)
val from: TextView = itemView.findViewById(R.id.overview_quickwizard_item_from)
val handleView: ImageView = itemView.findViewById(R.id.handleView)
val device: ImageView = itemView.findViewById(R.id.overview_quickwizard_item_device)
val to: TextView = itemView.findViewById(R.id.overview_quickwizard_item_to)
private val editButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_edit_button)
private val removeButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_remove_button)
@ -74,6 +159,16 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() {
}
}
}
fun moveItem(from: Int, to: Int) {
Log.i("QuickWizard", "moveItem")
quickWizard.move(from, to)
}
fun onDrop() {
Log.i("QuickWizard", "onDrop")
rxBus.send(EventQuickWizardChange())
}
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -84,6 +179,7 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() {
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(this)
binding.recyclerview.adapter = RecyclerViewAdapter(supportFragmentManager)
itemTouchHelper.attachToRecyclerView(binding.recyclerview)
binding.addButton.setOnClickListener {
val manager = supportFragmentManager
@ -98,13 +194,13 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() {
.toObservable(EventQuickWizardChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
val adapter = RecyclerViewAdapter(supportFragmentManager)
binding.recyclerview.swapAdapter(adapter, false)
}, fabricPrivacy::logException)
val adapter = RecyclerViewAdapter(supportFragmentManager)
binding.recyclerview.swapAdapter(adapter, false)
}, fabricPrivacy::logException)
}
override fun onPause() {
disposable.clear()
super.onPause()
}
}
}

View file

@ -9,6 +9,7 @@ import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.OverviewEditquickwizardDialogBinding
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBus
@ -21,6 +22,7 @@ import info.nightscout.androidaps.utils.extensions.setEnableForChildren
import info.nightscout.androidaps.utils.extensions.setSelection
import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONException
import javax.inject.Inject
@ -30,6 +32,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var quickWizard: QuickWizard
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var sp: SP
var position = -1
@ -57,6 +60,14 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener {
position = bundle.getInt("position", -1)
}
val entry = if (position == -1) quickWizard.newEmptyItem() else quickWizard[position]
if (sp.getBoolean(R.string.key_wear_control, false)) {
binding.deviceLabel.visibility = View.VISIBLE
binding.device.visibility = View.VISIBLE
} else {
binding.deviceLabel.visibility = View.GONE
binding.device.visibility = View.GONE
}
binding.okcancel.ok.setOnClickListener {
try {
entry.storage.put("buttonText", binding.buttonEdit.text.toString())
@ -66,6 +77,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener {
entry.storage.put("useBG", binding.useBg.selectedItemPosition)
entry.storage.put("useCOB", binding.useCob.selectedItemPosition)
entry.storage.put("useBolusIOB", binding.useBolusIob.selectedItemPosition)
entry.storage.put("device", binding.device.selectedItemPosition)
entry.storage.put("useBasalIOB", binding.useBasalIob.selectedItemPosition)
entry.storage.put("useTrend", binding.useTrend.selectedItemPosition)
entry.storage.put("useSuperBolus", binding.useSuperBolus.selectedItemPosition)
@ -122,6 +134,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener {
binding.useCob.setSelection(entry.useCOB())
binding.useBolusIob.setSelection(entry.useBolusIOB())
binding.useBasalIob.setSelection(entry.useBasalIOB())
binding.device.setSelection(entry.device())
binding.useTrend.setSelection(entry.useTrend())
binding.useSuperBolus.setSelection(entry.useSuperBolus())
binding.useTempTarget.setSelection(entry.useTempTarget())

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.general.wear
import android.app.NotificationManager
import android.content.Context
import android.util.Log
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
@ -39,6 +40,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.shared.SafeParse
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
@ -71,6 +73,7 @@ class ActionStringHandler @Inject constructor(
private val activePlugin: ActivePlugin,
private val iobCobCalculator: IobCobCalculator,
private val localInsightPlugin: LocalInsightPlugin,
private val quickWizard: QuickWizard,
private val danaRPlugin: DanaRPlugin,
private val danaRKoreanPlugin: DanaRKoreanPlugin,
private val danaRv2Plugin: DanaRv2Plugin,
@ -144,34 +147,39 @@ class ActionStringHandler @Inject constructor(
}
rAction += "bolus $insulinAfterConstraints $carbsAfterConstraints"
} else if ("temptarget" == act[0]) { ///////////////////////////////////////////////////////// TEMPTARGET
aapsLogger.info(LTag.WEAR, "temptarget received:" + act)
aapsLogger.info(LTag.WEAR, "temptarget received: $act")
if ("cancel" == act[1]) {
rMessage += rh.gs(R.string.wear_action_tempt_cancel_message)
rAction = "temptarget true 0 0 0"
} else if ("preset" == act[1]) {
val presetIsMGDL = profileFunction.getUnits() == GlucoseUnit.MGDL
val preset = act[2]
if ("activity" == preset) {
val activityTTDuration = defaultValueHelper.determineActivityTTDuration()
val activityTT = defaultValueHelper.determineActivityTT()
val reason = rh.gs(R.string.activity)
rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, activityTT, activityTTDuration)
rAction = "temptarget $presetIsMGDL $activityTTDuration $activityTT $activityTT"
} else if ("hypo" == preset) {
val hypoTTDuration = defaultValueHelper.determineHypoTTDuration()
val hypoTT = defaultValueHelper.determineHypoTT()
val reason = rh.gs(R.string.hypo)
rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, hypoTT, hypoTTDuration)
rAction = "temptarget $presetIsMGDL $hypoTTDuration $hypoTT $hypoTT"
} else if ("eating" == preset) {
val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration()
val eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
val reason = rh.gs(R.string.eatingsoon)
rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, eatingSoonTT, eatingSoonTTDuration)
rAction = "temptarget $presetIsMGDL $eatingSoonTTDuration $eatingSoonTT $eatingSoonTT"
} else {
sendError(rh.gs(R.string.wear_action_tempt_preset_error, preset))
return
when (preset) {
"activity" -> {
val activityTTDuration = defaultValueHelper.determineActivityTTDuration()
val activityTT = defaultValueHelper.determineActivityTT()
val reason = rh.gs(R.string.activity)
rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, activityTT, activityTTDuration)
rAction = "temptarget $presetIsMGDL $activityTTDuration $activityTT $activityTT"
}
"hypo" -> {
val hypoTTDuration = defaultValueHelper.determineHypoTTDuration()
val hypoTT = defaultValueHelper.determineHypoTT()
val reason = rh.gs(R.string.hypo)
rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, hypoTT, hypoTTDuration)
rAction = "temptarget $presetIsMGDL $hypoTTDuration $hypoTT $hypoTT"
}
"eating" -> {
val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration()
val eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
val reason = rh.gs(R.string.eatingsoon)
rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, eatingSoonTT, eatingSoonTTDuration)
rAction = "temptarget $presetIsMGDL $eatingSoonTTDuration $eatingSoonTT $eatingSoonTT"
}
else -> {
sendError(rh.gs(R.string.wear_action_tempt_preset_error, preset))
return
}
}
} else {
val isMGDL = java.lang.Boolean.parseBoolean(act[1])
@ -281,6 +289,34 @@ class ActionStringHandler @Inject constructor(
rMessage += "\nPercentage: " + format.format(bolusWizard.totalBeforePercentageAdjustment) + "U * " + percentage + "% -> ~" + format.format(bolusWizard.calculatedTotalInsulin) + "U"
}
lastBolusWizard = bolusWizard
} else if ("quick_wizard" == act[0]) {
val guid = act[1]
val actualBg = iobCobCalculator.ads.actualBg()
val profile = profileFunction.getProfile()
val profileName = profileFunction.getProfileName()
val pump = activePlugin.activePump
val quickWizardEntry = quickWizard.get(guid)
Log.i("QuickWizard", "handleInitiate: quick_wizard " + quickWizardEntry?.buttonText() + " c "+ quickWizardEntry?.carbs())
if (quickWizardEntry != null && actualBg != null && profile != null) {
// Logic related from Overview.kt
val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true)
if (wizard.calculatedTotalInsulin > 0.0 && quickWizardEntry.carbs() > 0.0) {
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(quickWizardEntry.carbs())).value()
val insulinAfterConstraints = wizard.insulinAfterConstraints
if (abs(insulinAfterConstraints - wizard.calculatedTotalInsulin) >= pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) || carbsAfterConstraints != quickWizardEntry.carbs()) {
// TODO check error is correct
sendError(rh.gs(R.string.constraints_violation) + "\n" + rh.gs(R.string.changeyourinput))
return
}
rMessage = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs())
rAction = "bolus $insulinAfterConstraints $carbsAfterConstraints"
Log.i("QuickWizard", "handleInitiate: quick_wizard action=$rAction")
} else {
sendError(rh.gs(R.string.quick_wizard_no_action))
}
} else {
sendError(rh.gs(R.string.quick_wizard_can_not_calculate))
}
} else if ("opencpp" == act[0]) {
val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values
@ -364,7 +400,10 @@ class ActionStringHandler @Inject constructor(
rAction = "cancelChangeRequest"
wearPlugin.requestNotificationCancel(rAction)
return
} else return
} else {
sendError("Unknown action command: " + act[0] )
return
}
// send result
wearPlugin.requestActionConfirmation(rTitle, rMessage, rAction)
lastSentTimestamp = System.currentTimeMillis()
@ -698,4 +737,4 @@ class ActionStringHandler @Inject constructor(
lastConfirmActionString = null
lastBolusWizard = null
}
}
}

View file

@ -46,6 +46,7 @@ import info.nightscout.androidaps.interfaces.Loop;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.Profile;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry;
import info.nightscout.shared.logging.AAPSLogger;
import info.nightscout.shared.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
@ -62,6 +63,7 @@ import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.DefaultValueHelper;
import info.nightscout.androidaps.utils.TrendCalculator;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.wizard.QuickWizard;
import info.nightscout.shared.sharedPreferences.SP;
public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
@ -81,6 +83,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
@Inject ReceiverStatusStore receiverStatusStore;
@Inject Config config;
@Inject public TrendCalculator trendCalculator;
@Inject public QuickWizard quickWizard;
public static final String ACTION_RESEND = WatchUpdaterService.class.getName().concat(".Resend");
public static final String ACTION_OPEN_SETTINGS = WatchUpdaterService.class.getName().concat(".OpenSettings");
@ -101,12 +104,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private static final String OPEN_SETTINGS_PATH = "/openwearsettings";
private static final String NEW_STATUS_PATH = "/sendstatustowear";
private static final String NEW_PREFERENCES_PATH = "/sendpreferencestowear";
private static final String QUICK_WIZARD_PATH = "/send_quick_wizard";
public static final String BASAL_DATA_PATH = "/nightscout_watch_basal";
public static final String BOLUS_PROGRESS_PATH = "/nightscout_watch_bolusprogress";
public static final String ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest";
public static final String ACTION_CHANGECONFIRMATION_REQUEST_PATH = "/nightscout_watch_changeconfirmationrequest";
public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest";
String TAG = "WatchUpdateService";
private static boolean lastLoopStatus;
@ -156,7 +161,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent != null ? intent.getAction() : null;
// Log.d(TAG, logPrefix + "onStartCommand: " + action);
// Log.d(TAG, "onStartCommand: " + action);
if (wearIntegration()) {
handler.post(() -> {
@ -235,7 +240,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
super.onPeerConnected(peer);
String id = peer.getId();
String name = peer.getDisplayName();
// Log.d(TAG, logPrefix + "onPeerConnected peer name & ID: " + name + "|" + id);
Log.d(TAG, "onPeerConnected peer name & ID: " + name + "|" + id);
}
@ -244,14 +249,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
super.onPeerDisconnected(peer);
String id = peer.getId();
String name = peer.getDisplayName();
// Log.d(TAG, logPrefix + "onPeerDisconnected peer name & ID: " + name + "|" + id);
Log.d(TAG, "onPeerDisconnected peer name & ID: " + name + "|" + id);
}
@Override
public void onMessageReceived(MessageEvent event) {
// Log.d(TAG, logPrefix + "onMessageRecieved: " + event);
// Log.d(TAG, "onMessageRecieved: " + event);
if (wearIntegration()) {
if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) {
@ -283,7 +288,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendData() {
GlucoseValue lastBG = iobCobCalculator.getAds().lastBg();
// Log.d(TAG, logPrefix + "LastBg=" + lastBG);
// Log.d(TAG, "LastBg=" + lastBG);
if (lastBG != null) {
GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData();
@ -364,6 +369,10 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
googleApiConnect();
}
sendPreferences();
sendQuickWizard();
long startTime = System.currentTimeMillis() - (long) (60000 * 60 * 5.5);
GlucoseValue last_bg = iobCobCalculator.getAds().lastBg();
@ -382,7 +391,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
entries.putDataMapArrayList("entries", dataMaps);
(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, entries);
}
sendPreferences();
sendBasals();
sendStatus();
}
@ -738,6 +746,41 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
}
}
private void sendQuickWizard() {
if (googleApiClient != null && googleApiClient.isConnected()) {
int size = quickWizard.size();
ArrayList<DataMap> entities = new ArrayList<>();
for(int i=0; i < size; i++) {
QuickWizardEntry q = quickWizard.get(i);
if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) {
entities.add(quickMap(q));
}
}
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(QUICK_WIZARD_PATH);
DataMap dm = dataMapRequest.getDataMap();
dm.putLong("timestamp", System.currentTimeMillis());
dm.putDataMapArrayList("quick_wizard", entities);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
Log.i(TAG, "sendQuickWizard: " + putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("sendQuickWizard", "No connection to wearable available!");
}
}
private DataMap quickMap(QuickWizardEntry q) {
DataMap dm = new DataMap();
dm.putString("guid", q.guid());
dm.putString("button_text", q.buttonText());
dm.putInt("carbs", q.carbs());
dm.putInt("from", q.validFrom());
dm.putInt("to", q.validTo());
return dm;
}
@NonNull
private String generateStatusString(Profile profile, String currentBasal, String iobSum, String iobDetail, String bgiString) {

View file

@ -1,10 +1,12 @@
package info.nightscout.androidaps.utils.wizard
import android.util.Log
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@ -18,6 +20,18 @@ class QuickWizard @Inject constructor(
init {
setData(JSONArray(sp.getString(R.string.key_quickwizard, "[]")))
setGuidsForOldEntries()
}
private fun setGuidsForOldEntries() {
// for migration purposes; guid is a new required property
for (i in 0 until storage.length()) {
val entry = QuickWizardEntry(injector).from(storage.get(i) as JSONObject, i)
if (entry.guid() == "") {
val guid = UUID.randomUUID().toString()
entry.storage.put("guid", guid)
}
}
}
fun getActive(): QuickWizardEntry? {
@ -41,6 +55,38 @@ class QuickWizard @Inject constructor(
operator fun get(position: Int): QuickWizardEntry =
QuickWizardEntry(injector).from(storage.get(position) as JSONObject, position)
fun get(guid: String): QuickWizardEntry? {
for (i in 0 until storage.length()) {
val entry = QuickWizardEntry(injector).from(storage.get(i) as JSONObject, i)
if (entry.guid() == guid) {
return entry
}
}
return null
}
fun move(from: Int, to: Int) {
Log.i("QuickWizard", "moveItem: $from $to")
val fromEntry = storage[from] as JSONObject
storage.remove(from)
addToPos(to, fromEntry, storage)
save()
}
fun removePos(pos: Int, jsonObj: JSONObject?, jsonArr: JSONArray) {
for (i in jsonArr.length() downTo pos + 1) {
jsonArr.put(i, jsonArr[i - 1])
}
jsonArr.put(pos, jsonObj)
}
private fun addToPos(pos: Int, jsonObj: JSONObject?, jsonArr: JSONArray) {
for (i in jsonArr.length() downTo pos + 1) {
jsonArr.put(i, jsonArr[i - 1])
}
jsonArr.put(pos, jsonObj)
}
fun newEmptyItem(): QuickWizardEntry {
return QuickWizardEntry(injector)
}
@ -57,4 +103,5 @@ class QuickWizard @Inject constructor(
storage.remove(position)
save()
}
}

View file

@ -19,6 +19,7 @@ import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjector) {
@ -41,11 +42,15 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
const val NO = 1
private const val POSITIVE_ONLY = 2
private const val NEGATIVE_ONLY = 3
const val DEVICE_ALL = 0
const val DEVICE_PHONE = 1
const val DEVICE_WATCH = 2
}
init {
injector.androidInjector().inject(this)
val emptyData = "{\"buttonText\":\"\",\"carbs\":0,\"validFrom\":0,\"validTo\":86340}"
val guid = UUID.randomUUID().toString()
val emptyData = "{\"guid\": \"$guid\",\"buttonText\":\"\",\"carbs\":0,\"validFrom\":0,\"validTo\":86340, \"device\": \"all\"}"
try {
storage = JSONObject(emptyData)
} catch (e: JSONException) {
@ -55,6 +60,8 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
/*
{
guid: string,
device: string, // (phone, watch, all)
buttonText: "Meal",
carbs: 36,
validFrom: 8 * 60 * 60, // seconds from midnight
@ -69,12 +76,13 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
}
*/
fun from(entry: JSONObject, position: Int): QuickWizardEntry {
// TODO set guid if missing for migration
storage = entry
this.position = position
return this
}
fun isActive(): Boolean = profileFunction.secondsFromMidnight() >= validFrom() && profileFunction.secondsFromMidnight() <= validTo()
fun isActive(): Boolean = profileFunction.secondsFromMidnight() >= validFrom() && profileFunction.secondsFromMidnight() <= validTo() && forDevice(DEVICE_PHONE)
fun doCalc(profile: Profile, profileName: String, lastBG: GlucoseValue, _synchronized: Boolean): BolusWizard {
val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
@ -123,6 +131,12 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
return BolusWizard(injector).doCalc(profile, profileName, tempTarget, carbs(), cob, bg, 0.0, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, false, buttonText(), quickWizard = true) //tbc, ok if only quickwizard, but if other sources elsewhere use Sources.QuickWizard
}
fun guid(): String = safeGetString(storage, "guid", "")
fun device(): Int = safeGetInt(storage, "device", DEVICE_ALL)
fun forDevice(device: Int) = device() == device || device() == DEVICE_ALL
fun buttonText(): String = safeGetString(storage, "buttonText", "")
fun carbs(): Int = safeGetInt(storage, "carbs")
@ -148,4 +162,4 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
fun useSuperBolus(): Int = safeGetInt(storage, "useSuperBolus", NO)
fun useTempTarget(): Int = safeGetInt(storage, "useTempTarget", NO)
}
}

View file

@ -108,6 +108,35 @@
</LinearLayout>
<TextView
android:id="@+id/device_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/overview_editquickwizard_show_on_device"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<RadioGroup
android:id="@+id/device"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp">
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="All" />
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Phone" />
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Watch" />
</RadioGroup>
<TextView
android:layout_width="match_parent"

View file

@ -16,9 +16,9 @@
android:orientation="vertical">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
@ -29,37 +29,64 @@
card_view:srcCompat="@drawable/ic_quick_wizard" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:baselineAligned="true"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:id="@+id/overview_quickwizard_item_buttonText"
<TextView
android:id="@+id/overview_quickwizard_item_buttonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="Sample button text"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/cardObjectiveText"
android:textStyle="normal|bold" />
<TextView
android:id="@+id/overview_quickwizard_item_carbs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="36g"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/cardObjectiveText"
android:textStyle="normal|bold" />
</LinearLayout>
<ImageView
android:id="@+id/overview_quickwizard_item_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="Sample button text"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="normal|bold"
android:textColor="@color/cardObjectiveText" />
android:adjustViewBounds="false"
android:cropToPadding="false"
android:paddingRight="10dp"
android:scaleType="fitStart"
card_view:srcCompat="@drawable/ic_smartphone" />
<TextView
android:id="@+id/overview_quickwizard_item_carbs"
<ImageView
android:id="@+id/handleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="36g"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/cardObjectiveText"
android:textStyle="normal|bold" />
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:adjustViewBounds="false"
android:cropToPadding="false"
android:scaleType="fitStart"
card_view:srcCompat="@drawable/ic_reorder_gray_24dp" />
</LinearLayout>
@ -88,9 +115,9 @@
android:textStyle="normal|bold" />
<TextView
android:text="-"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:text="-" />
<TextView
android:id="@+id/overview_quickwizard_item_to"

View file

@ -1175,5 +1175,9 @@
<string name="wear_action_tempt_manual_range_message">Temptarget:\nMin: %1$s\nMax: %2$s\nDuration: %3$s</string>
<string name="wear_action_tempt_manual_message">Temptarget:\nTarget: %1$s\nDuration: %2$s</string>
<string name="wear_action_tempt_preset_message">Temptarget:\Reason: %1$s\nTarget: %2$s\nDuration: %3$s</string>
<string name="quick_wizard_no_action">No insulin needed nor are carbs added.</string>
<string name="quick_wizard_can_not_calculate">Can not calculate wizard, requires actual blood glucose and active profile.</string>
<string name="quick_wizard_message">Quick Wizard: %1$s\nInsulin: %2$.2fU\nCarbs: %3$dg</string>
<string name="overview_editquickwizard_show_on_device">Show entry on device:</string>
</resources>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
</vector>

View file

@ -258,6 +258,10 @@
android:host="*"
android:pathPrefix="/openwearsettings"
android:scheme="wear" />
<data
android:host="*"
android:pathPrefix="/send_quick_wizard"
android:scheme="wear" />
<data
android:host="*"
android:pathPrefix="/sendstatustowear"
@ -544,6 +548,20 @@
android:resource="@drawable/temp_target_tile_preview" />
</service>
<service
android:name=".tile.QuickWizardTileService"
android:exported="true"
android:label="@string/label_quick_wizard_tile"
android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
<intent-filter>
<action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
</intent-filter>
<meta-data
android:name="androidx.wear.tiles.PREVIEW"
android:resource="@drawable/quick_wizard_tile_preview" />
</service>
<receiver android:name=".complications.ComplicationTapBroadcastReceiver" />
<activity
@ -595,6 +613,10 @@
android:exported="true"
android:label="@string/menu_tempt" />
<activity
android:name=".interaction.actions.BackgroundActionActivity"
android:exported="true" />
<activity android:name=".interaction.ConfigurationActivity">
<intent-filter>
<!-- action-name must be equal with name of xml-ressource where the configuration is describe -->

View file

@ -13,6 +13,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
import androidx.core.app.NotificationCompat;
@ -22,10 +23,6 @@ import androidx.wear.tiles.TileService;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.wearable.CapabilityApi;
import com.google.android.gms.wearable.CapabilityInfo;
import com.google.android.gms.wearable.ChannelApi;
import com.google.android.gms.wearable.DataEvent;
import com.google.android.gms.wearable.DataEventBuffer;
@ -36,7 +33,8 @@ import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.WearableListenerService;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@ -47,11 +45,11 @@ import info.nightscout.androidaps.interaction.AAPSPreferences;
import info.nightscout.androidaps.interaction.actions.AcceptActivity;
import info.nightscout.androidaps.interaction.actions.CPPActivity;
import info.nightscout.androidaps.interaction.utils.Persistence;
import info.nightscout.androidaps.interaction.utils.WearUtil;
import info.nightscout.androidaps.tile.ActionsTileService;
import info.nightscout.androidaps.tile.QuickWizardTileService;
import info.nightscout.androidaps.tile.TempTargetTileService;
import info.nightscout.shared.SafeParse;
import info.nightscout.androidaps.interaction.utils.WearUtil;
/**
* Created by emmablack on 12/26/14.
@ -62,7 +60,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
@Inject WearUtil wearUtil;
@Inject Persistence persistence;
private static final String WEARABLE_DATA_PATH = "/nightscout_watch_data";
private static final String WEARABLE_RESEND_PATH = "/nightscout_watch_data_resend";
private static final String WEARABLE_CANCELBOLUS_PATH = "/nightscout_watch_cancel_bolus";
public static final String WEARABLE_CONFIRM_ACTIONSTRING_PATH = "/nightscout_watch_confirmactionstring";
@ -71,13 +68,13 @@ public class ListenerService extends WearableListenerService implements GoogleAp
private static final String OPEN_SETTINGS = "/openwearsettings";
private static final String NEW_STATUS_PATH = "/sendstatustowear";
private static final String NEW_PREFERENCES_PATH = "/sendpreferencestowear";
private static final String QUICK_WIZARD_PATH = "/send_quick_wizard";
public static final String BASAL_DATA_PATH = "/nightscout_watch_basal";
public static final String BOLUS_PROGRESS_PATH = "/nightscout_watch_bolusprogress";
public static final String ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest";
public static final String NEW_CHANGECONFIRMATIONREQUEST_PATH = "/nightscout_watch_changeconfirmationrequest";
public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest";
public static final int BOLUS_PROGRESS_NOTIF_ID = 1;
public static final int CONFIRM_NOTIF_ID = 2;
public static final int CHANGE_NOTIF_ID = 556677;
@ -88,29 +85,15 @@ public class ListenerService extends WearableListenerService implements GoogleAp
private static final String ACTION_CONFIRMCHANGE = "com.dexdrip.stephenblack.nightwatch.CONFIRMCHANGE";
private static final String ACTION_INITIATE_ACTION = "com.dexdrip.stephenblack.nightwatch.INITIATE_ACTION";
private static final String ACTION_RESEND_BULK = "com.dexdrip.stephenblack.nightwatch.RESEND_BULK_DATA";
private static final String AAPS_NOTIFY_CHANNEL_ID_OPENLOOP = "AndroidAPS-OpenLoop";
private static final String AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS = "bolus progress vibration";
private static final String AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT = "bolus progress silent";
GoogleApiClient googleApiClient;
private long lastRequest = 0;
private DismissThread bolusprogressThread;
private static final String TAG = "ListenerService";
private DataRequester mDataRequester = null;
private static final int GET_CAPABILITIES_TIMEOUT_MS = 5000;
// Phone
private static final String CAPABILITY_PHONE_APP = "phone_app_sync_bgs";
private static final String MESSAGE_PATH_PHONE = "/phone_message_path";
// Wear
private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs";
private static final String MESSAGE_PATH_WEAR = "/wear_message_path";
private final String mPhoneNodeId = null;
private String localnode = null;
private final String logPrefix = ""; // "WR: "
// Not derived from DaggerService, do injection here
@ -120,143 +103,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
super.onCreate();
}
public class DataRequester extends AsyncTask<Void, Void, Void> {
Context mContext;
String path;
byte[] payload;
DataRequester(Context context, String thispath, byte[] thispayload) {
path = thispath;
payload = thispayload;
// Log.d(TAG, logPrefix + "DataRequester DataRequester: " + thispath + " lastRequest:" + lastRequest);
}
@Override
protected Void doInBackground(Void... params) {
// Log.d(TAG, logPrefix + "DataRequester: doInBack: " + params);
try {
forceGoogleApiConnect();
DataMap datamap;
if (isCancelled()) {
Log.d(TAG, "doInBackground CANCELLED programmatically");
return null;
}
if (googleApiClient != null) {
if (!googleApiClient.isConnected())
googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
}
// this code might not be needed in this way, but we need to see that later
if ((googleApiClient != null) && (googleApiClient.isConnected())) {
if ((System.currentTimeMillis() - lastRequest > 20 * 1000)) {
// enforce 20-second debounce period
lastRequest = System.currentTimeMillis();
// NodeApi.GetConnectedNodesResult nodes =
// Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
if (localnode == null || (localnode != null && localnode.isEmpty()))
setLocalNodeName();
CapabilityInfo capabilityInfo = getCapabilities();
int count = 0;
Node phoneNode = null;
if (capabilityInfo != null) {
phoneNode = updatePhoneSyncBgsCapability(capabilityInfo);
count = capabilityInfo.getNodes().size();
}
Log.d(TAG, "doInBackground connected. CapabilityApi.GetCapabilityResult mPhoneNodeID="
+ (phoneNode != null ? phoneNode.getId() : "") + " count=" + count + " localnode="
+ localnode);// KS
if (count > 0) {
for (Node node : capabilityInfo.getNodes()) {
// Log.d(TAG, "doInBackground path: " + path);
switch (path) {
// simple send as is payloads
case WEARABLE_RESEND_PATH:
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(),
WEARABLE_RESEND_PATH, null);
break;
case WEARABLE_DATA_PATH:
case WEARABLE_CANCELBOLUS_PATH:
case WEARABLE_CONFIRM_ACTIONSTRING_PATH:
case WEARABLE_INITIATE_ACTIONSTRING_PATH:
case OPEN_SETTINGS:
case NEW_STATUS_PATH:
case NEW_PREFERENCES_PATH:
case BASAL_DATA_PATH:
case BOLUS_PROGRESS_PATH:
case ACTION_CONFIRMATION_REQUEST_PATH:
case NEW_CHANGECONFIRMATIONREQUEST_PATH:
case ACTION_CANCELNOTIFICATION_REQUEST_PATH: {
Log.w(TAG, logPrefix + "Unhandled path");
// sendMessagePayload(node, path, path, payload);
}
default:// SYNC_ALL_DATA
// this fall through is messy and non-deterministic for new paths
}
}
} else {
Log.d(TAG, logPrefix + "doInBackground connected but getConnectedNodes returns 0.");
}
} else {
// no resend
Log.d(TAG, logPrefix + "Inside the timeout, will not be executed");
}
} else {
Log.d(TAG, logPrefix + "Not connected for sending: api "
+ ((googleApiClient == null) ? "is NULL!" : "not null"));
if (googleApiClient != null) {
googleApiClient.connect();
} else {
googleApiConnect();
}
}
} catch (Exception ex) {
Log.e(TAG, logPrefix + "Error executing DataRequester in background. Exception: " + ex.getMessage());
}
return null;
}
}
public CapabilityInfo getCapabilities() {
CapabilityApi.GetCapabilityResult capabilityResult = Wearable.CapabilityApi.getCapability(googleApiClient,
CAPABILITY_PHONE_APP, CapabilityApi.FILTER_REACHABLE).await(GET_CAPABILITIES_TIMEOUT_MS,
TimeUnit.MILLISECONDS);
if (!capabilityResult.getStatus().isSuccess()) {
Log.e(TAG, logPrefix + "doInBackground Failed to get capabilities, status: "
+ capabilityResult.getStatus().getStatusMessage());
return null;
}
return capabilityResult.getCapability();
}
public class BolusCancelTask extends AsyncTask<Void, Void, Void> {
Context mContext;
@ -266,8 +112,11 @@ public class ListenerService extends WearableListenerService implements GoogleAp
@Override
protected Void doInBackground(Void... params) {
// Log.d(TAG, logPrefix + "BolusCancelTask: doInBack: " + params);
Log.d(TAG, logPrefix + "BolusCancelTask.doInBackground: " + params);
if (!googleApiClient.isConnected()) {
Log.i(TAG, "BolusCancelTask.doInBackground: not connected");
googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
}
if (googleApiClient.isConnected()) {
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
@ -275,16 +124,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_CANCELBOLUS_PATH, null);
}
} else {
googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
if (googleApiClient.isConnected()) {
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
for (Node node : nodes.getNodes()) {
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_CANCELBOLUS_PATH, null);
}
}
}
return null;
}
@ -303,9 +142,12 @@ public class ListenerService extends WearableListenerService implements GoogleAp
@Override
protected Void doInBackground(Void... params) {
Log.i(TAG, "MessageActionTask.doInBackground: ");
forceGoogleApiConnect();
if (!googleApiClient.isConnected()) {
Log.i(TAG, "MessageActionTask.doInBackground: not connected");
googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
}
if (googleApiClient.isConnected()) {
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
@ -313,22 +155,42 @@ public class ListenerService extends WearableListenerService implements GoogleAp
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), mMessagePath, mActionstring.getBytes());
}
} else {
googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
if (googleApiClient.isConnected()) {
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
for (Node node : nodes.getNodes()) {
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), mMessagePath, mActionstring.getBytes());
}
}
}
return null;
}
}
public class ResendDataTask extends AsyncTask<Void, Void, Void> {
Context mContext;
ResendDataTask(Context context) {
mContext = context;
}
@Override
protected Void doInBackground(Void... params) {
Log.d(TAG, logPrefix + "ResendDataTask.doInBackground: " + params);
if (!googleApiClient.isConnected()) {
Log.i(TAG, "ResendDataTask.doInBackground: not connected");
googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
}
if (googleApiClient.isConnected()) {
Log.i(TAG, "ResendDataTask.doInBackground: connected");
NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
for (Node node : nodes.getNodes()) {
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_RESEND_PATH, null);
}
} else {
Log.i(TAG, "ResendDataTask.doInBackground: could not connect");
}
return null;
}
}
public void requestData() {
sendData(WEARABLE_RESEND_PATH, null);
new ResendDataTask(this).execute();
}
public void cancelBolus() {
@ -343,59 +205,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
new MessageActionTask(this, WEARABLE_INITIATE_ACTIONSTRING_PATH, actionstring).execute();
}
private Node updatePhoneSyncBgsCapability(CapabilityInfo capabilityInfo) {
// Log.d(TAG, "CapabilityInfo: " + capabilityInfo);
Set<Node> connectedNodes = capabilityInfo.getNodes();
return pickBestNode(connectedNodes);
// mPhoneNodeId = pickBestNodeId(connectedNodes);
}
private Node pickBestNode(Set<Node> nodes) {
Node bestNode = null;
// Find a nearby node or pick one arbitrarily
for (Node node : nodes) {
if (node.isNearby()) {
return node;
}
bestNode = node;
}
return bestNode;
}
private synchronized void sendData(String path, byte[] payload) {
// Log.d(TAG, "WR: sendData: path: " + path + ", payload=" + payload);
if (path == null)
return;
if (mDataRequester != null) {
// Log.d(TAG, logPrefix + "sendData DataRequester != null lastRequest:" +
// WearUtil.dateTimeText(lastRequest));
if (mDataRequester.getStatus() != AsyncTask.Status.FINISHED) {
// Log.d(TAG, logPrefix + "sendData Should be canceled? Let run 'til finished.");
// mDataRequester.cancel(true);
}
}
Log.d(TAG,
logPrefix + "sendData: execute lastRequest:" + wearUtil.dateTimeText(lastRequest));
mDataRequester = (DataRequester) new DataRequester(this, path, payload).execute();
// executeTask(mDataRequester);
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Log.d(TAG, "sendData SDK < M call execute lastRequest:" + WearUtil.dateTimeText(lastRequest));
// mDataRequester = (DataRequester) new DataRequester(this, path, payload).execute();
// } else {
// Log.d(TAG, "sendData SDK >= M call executeOnExecutor lastRequest:" + WearUtil.dateTimeText(lastRequest));
// // TODO xdrip executor
// mDataRequester = (DataRequester) new DataRequester(this, path, payload).executeOnExecutor(xdrip.executor);
// }
}
private void googleApiConnect() {
if (googleApiClient != null) {
// Remove old listener(s)
@ -419,20 +228,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
Wearable.MessageApi.addListener(googleApiClient, this);
}
private void forceGoogleApiConnect() {
if (googleApiClient == null || (!googleApiClient.isConnected() && !googleApiClient.isConnecting())) {
try {
Log.d(TAG, "forceGoogleApiConnect: forcing google api reconnection");
googleApiConnect();
Thread.sleep(2000);
} catch (InterruptedException e) {
//
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
@ -486,7 +281,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
return START_STICKY;
}
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
@ -548,10 +342,13 @@ public class ListenerService extends WearableListenerService implements GoogleAp
SharedPreferences.Editor editor = sharedPreferences.edit();
String keyControl = getString(R.string.key_wear_control);
if (dataMap.containsKey(keyControl)) {
boolean previousWearControl = sharedPreferences.getBoolean(keyControl, false);
boolean wearControl = dataMap.getBoolean(keyControl, false);
editor.putBoolean(keyControl, wearControl);
editor.apply();
updateTiles();
if (wearControl != previousWearControl) {
updateTiles();
}
}
String keyPercentage = getString(R.string.key_boluswizard_percentage);
if (dataMap.containsKey(keyPercentage)) {
@ -565,6 +362,26 @@ public class ListenerService extends WearableListenerService implements GoogleAp
editor.putBoolean(keyUnits, mgdl);
editor.apply();
}
} else if (path.equals(QUICK_WIZARD_PATH)) {
dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
Log.i(TAG, "onDataChanged: QUICK_WIZARD_PATH" + dataMap);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
dataMap.remove("timestamp");
String key = getString(R.string.key_quick_wizard_data_map);
String dataString = Base64.encodeToString(dataMap.toByteArray(), Base64.DEFAULT);
if (!dataString.equals(sharedPreferences.getString(key, ""))) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(key, dataString);
editor.apply();
// Todo maybe add debounce function, due to 20 seconds update limit?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
TileService.getUpdater(this)
.requestUpdate(QuickWizardTileService.class);
}
Log.i(TAG, "onDataChanged: updated QUICK_WIZARD");
} else {
Log.i(TAG, "onDataChanged: ignore update");
}
} else if (path.equals(NEW_CHANGECONFIRMATIONREQUEST_PATH)) {
String title = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("title");
String message = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("message");
@ -592,6 +409,9 @@ public class ListenerService extends WearableListenerService implements GoogleAp
TileService.getUpdater(this)
.requestUpdate(TempTargetTileService.class);
TileService.getUpdater(this)
.requestUpdate(QuickWizardTileService.class);
}
}
@ -666,7 +486,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp
PendingIntent cancelPendingIntent = PendingIntent.getService(this, 0, cancelIntent, 0);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, vibrate ? AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS: AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT)
new NotificationCompat.Builder(this, vibrate ? AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS : AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle(getString(R.string.bolus_progress))
.setContentText(progresspercent + "% - " + progresstatus)
@ -723,7 +543,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
bolusprogressThread.start();
}
private class DismissThread extends Thread {
private final int notificationID;
private final int seconds;
@ -757,7 +576,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp
context.startService(intent);
}
public static void initiateAction(Context context, String actionstring) {
public static void initiateAction(Context context, @NotNull String actionstring) {
Intent intent = new Intent(context, ListenerService.class);
intent.putExtra("actionstring", actionstring);
intent.setAction(ACTION_INITIATE_ACTION);
@ -780,18 +599,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
public void onConnected(Bundle bundle) {
// Log.d(TAG, logPrefix + "onConnected call requestData");
CapabilityApi.CapabilityListener capabilityListener = new CapabilityApi.CapabilityListener() {
@Override
public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
updatePhoneSyncBgsCapability(capabilityInfo);
Log.d(TAG, logPrefix + "onConnected onCapabilityChanged mPhoneNodeID:" + mPhoneNodeId
+ ", Capability: " + capabilityInfo);
}
};
Wearable.CapabilityApi.addCapabilityListener(googleApiClient, capabilityListener, CAPABILITY_PHONE_APP);
Wearable.ChannelApi.addListener(googleApiClient, this);
requestData();
}
@ -806,28 +613,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp
}
private void setLocalNodeName() {
forceGoogleApiConnect();
PendingResult<NodeApi.GetLocalNodeResult> result = Wearable.NodeApi.getLocalNode(googleApiClient);
result.setResultCallback(new ResultCallback<NodeApi.GetLocalNodeResult>() {
@Override
public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
if (!getLocalNodeResult.getStatus().isSuccess()) {
Log.e(TAG, "ERROR: failed to getLocalNode Status="
+ getLocalNodeResult.getStatus().getStatusMessage());
} else {
Log.d(TAG, "getLocalNode Status=: " + getLocalNodeResult.getStatus().getStatusMessage());
Node getnode = getLocalNodeResult.getNode();
localnode = getnode != null ? getnode.getDisplayName() + "|" + getnode.getId() : "";
Log.d(TAG, "setLocalNodeName. localnode=" + localnode);
}
}
});
}
@Override
public void onDestroy() {
super.onDestroy();

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.interaction.actions
import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import info.nightscout.androidaps.data.ListenerService
const val TAG = "QuickWizard"
class BackgroundActionActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val actionString = intent.extras?.getString("actionString")
Log.i(TAG, "QuickWizardActivity.onCreate: actionString=$actionString")
if (actionString != null) {
ListenerService.initiateAction(this, actionString)
val message = intent.extras?.getString("message")
if (message != null) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
} else {
Log.e(TAG, "BackgroundActionActivity.onCreate extras 'actionString' required")
}
finishAffinity()
}
}

View file

@ -31,9 +31,7 @@ public class TempTargetActivity extends ViewSelectorActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (executeInBackground()){
return;
}
setAdapter(new MyGridViewPagerAdapter());
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
@ -41,21 +39,6 @@ public class TempTargetActivity extends ViewSelectorActivity {
isSingleTarget = sp.getBoolean("singletarget", true);
}
private boolean executeInBackground() {
Bundle extras = getIntent().getExtras();
if (extras != null) {
String actionString = extras.getString("actionString", "");
boolean inBackground = extras.getBoolean("inBackground", false);
if (inBackground) {
ListenerService.initiateAction(this, actionString);
confirmAction(this, R.string.action_tempt_confirmation);
finishAffinity();
return true;
}
}
return false;
}
@Override
protected void onPause() {
super.onPause();

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.tile
import android.content.res.Resources
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interaction.actions.BolusActivity
import info.nightscout.androidaps.interaction.actions.TreatmentActivity
@ -7,48 +8,49 @@ import info.nightscout.androidaps.interaction.actions.ECarbActivity
import info.nightscout.androidaps.interaction.actions.TempTargetActivity
import info.nightscout.androidaps.interaction.actions.WizardActivity
object ActionSource : TileSource {
object ActionSource : StaticTileSource(), TileSource {
override fun getActions(): List<Action> {
override val preferencePrefix = "tile_action_"
override fun getActions(resources: Resources): List<StaticAction> {
return listOf(
Action(
id = 0,
StaticAction(
settingName = "wizard",
nameRes = R.string.menu_wizard_short,
buttonText = resources.getString(R.string.menu_wizard_short),
iconRes = R.drawable.ic_calculator_green,
activityClass = WizardActivity::class.java.name,
),
Action(
id = 1,
StaticAction(
settingName = "treatment",
nameRes = R.string.menu_treatment_short,
buttonText = resources.getString(R.string.menu_treatment_short),
iconRes = R.drawable.ic_bolus_carbs,
activityClass = TreatmentActivity::class.java.name,
),
Action(
id = 2,
StaticAction(
settingName = "bolus",
nameRes = R.string.action_insulin,
buttonText = resources.getString(R.string.action_insulin),
iconRes = R.drawable.ic_bolus,
activityClass = BolusActivity::class.java.name,
),
Action(
id = 3,
StaticAction(
settingName = "carbs",
nameRes = R.string.action_carbs,
buttonText = resources.getString(R.string.action_carbs),
iconRes = R.drawable.ic_carbs_orange,
activityClass = ECarbActivity::class.java.name,
),
Action(
id = 4,
StaticAction(
settingName = "temp_target",
nameRes = R.string.menu_tempt,
buttonText = resources.getString(R.string.menu_tempt),
iconRes = R.drawable.ic_temptarget_flat,
activityClass = TempTargetActivity::class.java.name,
)
)
}
override fun getResourceReferences(resources: Resources): List<Int> {
return getActions(resources).map { it.iconRes }
}
override fun getDefaultConfig(): Map<String, String> {
return mapOf(
"tile_action_1" to "wizard",

View file

@ -1,10 +1,6 @@
package info.nightscout.androidaps.tile
class ActionsTileService : TileBase() {
override val preferencePrefix = "tile_action_"
override val resourceVersion = "1"
override val idIconActionPrefix = "ic_action_"
override val resourceVersion = "ActionsTileService"
override val source = ActionSource
}

View file

@ -0,0 +1,79 @@
package info.nightscout.androidaps.tile
import android.content.Context
import android.content.res.Resources
import android.util.Base64
import android.util.Log
import androidx.preference.PreferenceManager
import com.google.android.gms.wearable.DataMap
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity
import java.util.*
object QuickWizardSource : TileSource {
override fun getSelectedActions(context: Context): List<Action> {
val quickList = mutableListOf<Action>()
val quickMap = getDataMap(context)
val sfm = secondsFromMidnight()
for (quick in quickMap) {
val validFrom = quick.getInt("from", 0)
val validTo = quick.getInt("to", 0)
val isActive = sfm in validFrom..validTo
// use from and to to schedule new update for timeline, for now just refresh every minute
val guid = quick.getString("guid", "")
if (isActive && guid != "") {
quickList.add(
Action(
buttonText = quick.getString("button_text", "?"),
buttonTextSub = "${quick.getInt("carbs", 0)} g",
iconRes = R.drawable.ic_quick_wizard,
activityClass = BackgroundActionActivity::class.java.name,
actionString = "quick_wizard $guid",
message = context.resources.getString(R.string.action_quick_wizard_confirmation),
)
)
Log.i(TAG, "getSelectedActions: active " + quick.getString("button_text", "?") + " guid=" + guid)
} else {
Log.i(TAG, "getSelectedActions: not active " + quick.getString("button_text", "?") + " guid=" + guid)
}
}
return quickList
}
private fun getDataMap(context: Context): ArrayList<DataMap> {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
val key = context.resources.getString(R.string.key_quick_wizard_data_map)
if (sharedPrefs.contains(key)) {
val rawB64Data: String? = sharedPrefs.getString(key, null)
val rawData: ByteArray = Base64.decode(rawB64Data, Base64.DEFAULT)
try {
val map = DataMap.fromByteArray(rawData)
return map.getDataMapArrayList("quick_wizard")
} catch (ex: IllegalArgumentException) {
Log.e(TAG, "getSelectedActions: IllegalArgumentException ", ex)
}
}
return arrayListOf()
}
private fun secondsFromMidnight(): Int {
val c = Calendar.getInstance()
c.set(Calendar.HOUR_OF_DAY, 0)
c.set(Calendar.MINUTE, 0)
c.set(Calendar.SECOND, 0)
c.set(Calendar.MILLISECOND, 0)
val passed: Long = System.currentTimeMillis() - c.timeInMillis
return (passed / 1000).toInt()
}
override fun getResourceReferences(resources: Resources): List<Int> {
return listOf(R.drawable.ic_quick_wizard)
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.tile
const val TAG = "QuickWizard"
class QuickWizardTileService : TileBase() {
override val resourceVersion = "QuickWizardTileService"
override val source = QuickWizardSource
}

View file

@ -0,0 +1,59 @@
package info.nightscout.androidaps.tile
import android.content.Context
import android.content.SharedPreferences
import android.content.res.Resources
import androidx.annotation.DrawableRes
import androidx.preference.PreferenceManager
class StaticAction(
val settingName: String,
buttonText: String,
buttonTextSub: String? = null,
activityClass: String,
@DrawableRes iconRes: Int,
actionString: String? = null,
message: String? = null,
) : Action(buttonText, buttonTextSub, activityClass, iconRes, actionString, message)
abstract class StaticTileSource {
abstract fun getActions(resources: Resources): List<StaticAction>
abstract val preferencePrefix: String
abstract fun getDefaultConfig(): Map<String, String>
open fun getSelectedActions(context: Context): List<Action> {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
setDefaultSettings(sharedPrefs)
val actionList: MutableList<Action> = mutableListOf()
for (i in 1..4) {
val action = getActionFromPreference(context.resources, sharedPrefs, i)
if (action != null) {
actionList.add(action)
}
}
if (actionList.isEmpty()) {
return getActions(context.resources).take(4)
}
return actionList
}
private fun getActionFromPreference(resources: Resources, sharedPrefs: SharedPreferences, index: Int): Action? {
val actionPref = sharedPrefs.getString(preferencePrefix + index, "none")
return getActions(resources).find { action -> action.settingName == actionPref }
}
open fun setDefaultSettings(sharedPrefs: SharedPreferences) {
val defaults = getDefaultConfig()
val firstKey = defaults.firstNotNullOf { settings -> settings.key }
if (!sharedPrefs.contains(firstKey)) {
val editor = sharedPrefs.edit()
for ((key, value) in defaults) {
editor.putString(key, value)
}
editor.apply()
}
}
}

View file

@ -1,61 +1,64 @@
package info.nightscout.androidaps.tile
import android.content.res.Resources
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity
import info.nightscout.androidaps.interaction.actions.TempTargetActivity
object TempTargetSource : TileSource {
object TempTargetSource : StaticTileSource(), TileSource {
override val preferencePrefix= "tile_tempt_"
override fun getActions(): List<Action> {
override fun getActions(resources: Resources): List<StaticAction> {
val message = resources.getString(R.string.action_tempt_confirmation)
return listOf(
Action(
id = 0,
StaticAction(
settingName = "activity",
nameRes = R.string.temp_target_activity,
buttonText = resources.getString(R.string.temp_target_activity),
iconRes = R.drawable.ic_target_activity,
activityClass = TempTargetActivity::class.java.name,
background = true,
activityClass = BackgroundActionActivity::class.java.name,
message = message,
// actionString = "temptarget false 90 8.0 8.0",
actionString = "temptarget preset activity",
),
Action(
id = 1,
StaticAction(
settingName = "eating_soon",
nameRes = R.string.temp_target_eating_soon,
buttonText = resources.getString(R.string.temp_target_eating_soon),
iconRes = R.drawable.ic_target_eatingsoon,
activityClass = TempTargetActivity::class.java.name,
background = true,
activityClass = BackgroundActionActivity::class.java.name,
message = message,
// actionString = "temptarget false 45 4.5 4.5",
actionString = "temptarget preset eating",
),
Action(
id = 2,
StaticAction(
settingName = "hypo",
nameRes = R.string.temp_target_hypo,
buttonText = resources.getString(R.string.temp_target_hypo),
iconRes = R.drawable.ic_target_hypo,
activityClass = TempTargetActivity::class.java.name,
background = true,
activityClass = BackgroundActionActivity::class.java.name,
message = message,
// actionString = "temptarget false 45 7.0 7.0",
actionString = "temptarget preset hypo",
),
Action(
id = 3,
StaticAction(
settingName = "manual",
nameRes = R.string.temp_target_manual,
buttonText = resources.getString(R.string.temp_target_manual),
iconRes = R.drawable.ic_target_manual,
activityClass = TempTargetActivity::class.java.name,
),
Action(
id = 4,
StaticAction(
settingName = "cancel",
nameRes = R.string.generic_cancel,
buttonText = resources.getString(R.string.generic_cancel),
iconRes = R.drawable.ic_target_cancel,
activityClass = TempTargetActivity::class.java.name,
activityClass = BackgroundActionActivity::class.java.name,
message = message,
actionString = "temptarget cancel",
background = true,
)
)
}
override fun getResourceReferences(resources: Resources): List<Int> {
return getActions(resources).map { it.iconRes }
}
override fun getDefaultConfig(): Map<String, String> {
return mapOf(
"tile_tempt_1" to "activity",

View file

@ -2,9 +2,7 @@ package info.nightscout.androidaps.tile
class TempTargetTileService : TileBase() {
override val preferencePrefix = "tile_tempt_"
override val resourceVersion = "1"
override val idIconActionPrefix = "ic_tempt_"
override val resourceVersion = "TempTargetTileService"
override val source = TempTargetSource;
}

View file

@ -1,8 +1,9 @@
package info.nightscout.androidaps.tile
import android.content.SharedPreferences
import android.content.Context
import android.os.Build
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import androidx.wear.tiles.ActionBuilders
@ -42,18 +43,17 @@ private const val LARGE_SCREEN_WIDTH_DP = 210
interface TileSource {
fun getActions(): List<Action>
fun getDefaultConfig(): Map<String, String>
fun getResourceReferences(resources: android.content.res.Resources): List<Int>
fun getSelectedActions(context: Context): List<Action>
}
data class Action(
val id: Int,
val settingName: String,
@StringRes val nameRes: Int,
open class Action(
val buttonText: String,
val buttonTextSub: String? = null,
val activityClass: String,
@DrawableRes val iconRes: Int,
val background: Boolean = false,
val actionString: String? = null,
val message: String? = null,
)
enum class WearControl {
@ -62,10 +62,7 @@ enum class WearControl {
abstract class TileBase : TileService() {
open val resourceVersion = "1"
open val idIconActionPrefix = "ic_action_"
abstract val preferencePrefix: String
abstract val resourceVersion: String
abstract val source: TileSource
private val serviceJob = Job()
@ -89,19 +86,25 @@ abstract class TileBase : TileService() {
.build()
}
private fun getSelectedActions(): List<Action> {
// TODO check why thi scan not be don in scope of the coroutine
return source.getSelectedActions(this)
}
@RequiresApi(Build.VERSION_CODES.N)
override fun onResourcesRequest(
requestParams: ResourcesRequest
): ListenableFuture<Resources> = serviceScope.future {
Resources.Builder()
.setVersion(resourceVersion)
.apply {
source.getActions().mapNotNull { action ->
source.getResourceReferences(resources).forEach { resourceId ->
addIdToImageMapping(
idIconActionPrefix + action.id,
resourceId.toString(),
ImageResource.Builder()
.setAndroidResourceByResId(
AndroidImageResourceByResId.Builder()
.setResourceId(action.iconRes)
.setResourceId(resourceId)
.build()
)
.build()
@ -157,15 +160,17 @@ abstract class TileBase : TileService() {
.build()
private fun doAction(action: Action): ActionBuilders.Action {
val inBackground = ActionBuilders.AndroidBooleanExtra.Builder().setValue(action.background).build()
val builder = ActionBuilders.AndroidActivity.Builder()
.setClassName(action.activityClass)
.setPackageName(this.packageName)
.addKeyToExtraMapping("inBackground", inBackground)
if (action.actionString != null) {
val actionString = ActionBuilders.AndroidStringExtra.Builder().setValue(action.actionString).build()
builder.addKeyToExtraMapping("actionString", actionString)
}
if (action.message != null) {
val message = ActionBuilders.AndroidStringExtra.Builder().setValue(action.message).build()
builder.addKeyToExtraMapping("message", message)
}
return ActionBuilders.LaunchAction.Builder()
.setAndroidActivity(builder.build())
@ -174,8 +179,8 @@ abstract class TileBase : TileService() {
private fun action(action: Action, deviceParameters: DeviceParameters): LayoutElement {
val circleDiameter = circleDiameter(deviceParameters)
val iconSize = dp(circleDiameter * ICON_SIZE_FRACTION)
val text = resources.getString(action.nameRes)
val text = action.buttonText
val textSub = action.buttonTextSub
return Box.Builder()
.setWidth(dp(circleDiameter))
.setHeight(dp(circleDiameter))
@ -193,7 +198,7 @@ abstract class TileBase : TileService() {
)
.setSemantics(
Semantics.Builder()
.setContentDescription(text)
.setContentDescription("$text $textSub")
.build()
)
.setClickable(
@ -203,32 +208,55 @@ abstract class TileBase : TileService() {
)
.build()
)
.addContent(
Column.Builder()
.addContent(
Image.Builder()
.setWidth(iconSize)
.setHeight(iconSize)
.setResourceId(idIconActionPrefix + action.id)
.build()
).addContent(
Text.Builder()
.setText(text)
.setFontStyle(
FontStyle.Builder()
.setWeight(FONT_WEIGHT_BOLD)
.setColor(
argb(ContextCompat.getColor(baseContext, R.color.white))
)
.setSize(buttonTextSize(deviceParameters, text))
.build()
)
.build()
).build()
)
.addContent(addTextContent(action, deviceParameters))
.build()
}
private fun addTextContent(action: Action, deviceParameters: DeviceParameters): LayoutElement {
val circleDiameter = circleDiameter(deviceParameters)
val iconSize = dp(circleDiameter * ICON_SIZE_FRACTION)
val text = action.buttonText
val textSub = action.buttonTextSub
val col = Column.Builder()
.addContent(
Image.Builder()
.setWidth(iconSize)
.setHeight(iconSize)
.setResourceId(action.iconRes.toString())
.build()
).addContent(
Text.Builder()
.setText(text)
.setFontStyle(
FontStyle.Builder()
.setWeight(FONT_WEIGHT_BOLD)
.setColor(
argb(ContextCompat.getColor(baseContext, R.color.white))
)
.setSize(buttonTextSize(deviceParameters, text))
.build()
)
.build()
)
if (textSub != null) {
col.addContent(
Text.Builder()
.setText(textSub)
.setFontStyle(
FontStyle.Builder()
.setColor(
argb(ContextCompat.getColor(baseContext, R.color.white))
)
.setSize(buttonTextSize(deviceParameters, textSub))
.build()
)
.build()
)
}
return col.build()
}
private fun circleDiameter(deviceParameters: DeviceParameters) = when (deviceParameters.screenShape) {
SCREEN_SHAPE_ROUND -> ((sqrt(2f) - 1) * deviceParameters.screenHeightDp) - (2 * SPACING_ACTIONS)
else -> 0.5f * deviceParameters.screenHeightDp - SPACING_ACTIONS
@ -257,37 +285,4 @@ abstract class TileBase : TileService() {
return WearControl.DISABLED
}
private fun getSelectedActions(): List<Action> {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
setDefaultSettings(sharedPrefs)
val actionList: MutableList<Action> = mutableListOf()
for (i in 1..4) {
val action = getActionFromPreference(sharedPrefs, i)
if (action != null) {
actionList.add(action)
}
}
if (actionList.isEmpty()) {
return source.getActions().take(4)
}
return actionList
}
private fun getActionFromPreference(sharedPrefs: SharedPreferences, index: Int): Action? {
val actionPref = sharedPrefs.getString(preferencePrefix + index, "none")
return source.getActions().find { action -> action.settingName == actionPref }
}
private fun setDefaultSettings(sharedPrefs: SharedPreferences) {
val defaults = source.getDefaultConfig()
val firstKey = defaults.firstNotNullOf { settings -> settings.key }
if (!sharedPrefs.contains(firstKey)) {
val editor = sharedPrefs.edit()
for ((key, value) in defaults) {
editor.putString(key, value)
}
editor.apply()
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,7 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="6.35"
android:viewportHeight="6.3500004">
<path

View file

@ -0,0 +1,12 @@
<vector
android:height="48dp"
android:width="48dp"
android:viewportHeight="24"
android:viewportWidth="24"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FEAF05" android:pathData="M10.751,6.59c-0.561,0.69 -0.796,1.057 -1.25,1.797c-2.517,0.238 -3.932,2.751 -3.379,4.664c0.244,0.843 0.779,1.476 1.498,1.958c2.348,1.573 6.243,1.429 8.487,-0.306c1.187,-0.918 1.723,-2.084 1.334,-3.576c-0.412,-1.58 -1.445,-2.585 -3.039,-2.87c-1.112,-0.199 -1.973,0.37 -2.558,1.313c-0.494,0.796 -0.72,1.616 -1.628,2.345c-0.389,-0.431 -0.458,-0.961 -0.483,-1.481c-0.12,-2.486 2.019,-4.439 4.595,-4.213c2.365,0.207 4.466,1.956 5.114,4.256c0.611,2.172 -0.226,4.432 -2.19,5.909c-2.995,2.253 -7.895,2.327 -10.943,0.165c-2.057,-1.459 -2.88,-3.881 -2.099,-6.18C5.023,7.978 7.368,6.304 9.77,6.4C10.072,6.412 10.365,6.459 10.751,6.59z"/>
<path android:fillColor="#FEAF06" android:pathData="M7.141,13.406c0.526,-0.103 1.178,-0.22 1.665,-0.322c1.522,-0.319 2.625,-1.229 3.273,-2.678c0.289,-0.647 0.702,-1.125 1.448,-1.42c0.418,0.811 0.454,1.641 0.223,2.493c-0.284,1.049 -0.889,1.863 -1.819,2.429c-0.616,0.376 -1.284,0.638 -1.966,0.867C8.814,15.163 7.797,14.773 7.141,13.406z"/>
<path android:fillColor="#FEAF06" android:pathData="M12.313,14.203c0.734,-0.537 1.25,-1.089 1.469,-1.612c0.894,0.159 1.92,0.314 2.804,0.471C16.036,14.799 13.922,15.203 12.313,14.203z"/>
<path android:fillColor="#FEAF06" android:pathData="M2.377,13.882c0.179,-0.123 0.756,-0.189 1.192,-0.123c0.259,0.631 0.587,1.225 1.108,1.923c-0.572,0.116 -1.037,0.253 -1.567,0.224c-0.493,-0.027 -0.993,-0.353 -1.063,-0.989C2.013,14.61 2.047,14.109 2.377,13.882z"/>
<path android:fillColor="#FEAF06" android:pathData="M19.143,15.22c0.196,-0.497 0.362,-0.961 0.561,-1.411c0.235,-0.531 0.618,-0.76 1.121,-0.681c0.419,0.066 0.575,0.376 0.644,0.756c0.098,0.541 -0.078,0.989 -0.529,1.213C20.38,15.377 19.787,15.293 19.143,15.22z"/>
</vector>

View file

@ -16,6 +16,7 @@
<string name="label_actions_tile">AAPS(Actions)</string>
<string name="label_temp_target_tile">AAPS(Temp Target)</string>
<string name="label_quick_wizard_tile">AAPS(Quick Wizard)</string>
<string name="label_warning_sync">No data!</string>
<string name="label_warning_old">Old data!</string>
@ -86,9 +87,11 @@
<string name="menu_none">None</string>
<string name="menu_default">Default</string>
<string name="menu_menu">Menu</string>
<string name="quick_wizard_short">XL</string>
<string name="action_duration">Duration</string>
<string name="action_tempt_confirmation">Temp Target Requested</string>
<string name="action_quick_wizard_confirmation">Quick Wizard Requested</string>
<string name="action_treatment_confirmation">Treatment Requested</string>
<string name="action_bolus_confirmation">Bolus Requested</string>
<string name="action_wizard_confirmation">Calculation Requested</string>
@ -165,7 +168,7 @@
<string name="bolus_progress_silent_channel_name">AAPS Bolus Progress Silent</string>
<string name="bolus_progress_channel_description">Bolus progress and cancel</string>
<string name="bolus_progress_silent_channel_description">Bolus progress and cancel with less vibrations</string>
<string name="key_quickwizard" translatable="false">QuickWizard</string>
<string name="key_wear_control" translatable="false">wearcontrol</string>
<string name="key_units_mgdl" translatable="false">units_mgdl</string>
<string name="key_boluswizard_percentage" translatable="false">boluswizard_percentage</string>
@ -182,5 +185,6 @@
<string name="tile_no_config">No config available</string>
<string name="wear_control_not_enabled">Wear controls disabled</string>
<string name="wear_control_no_data">No data available</string>
<string name="key_quick_wizard_data_map" translatable="false">quick_wizard_data_map</string>
</resources>