/* Copyright (C) 2023-2024 Arjan Schrijver, José Rebelo This file is part of Gadgetbridge. Gadgetbridge is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gadgetbridge is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities.dashboard; import android.os.AsyncTask; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment; public abstract class AbstractGaugeWidget extends AbstractDashboardWidget { private static final Logger LOG = LoggerFactory.getLogger(AbstractGaugeWidget.class); private TextView gaugeValue; private ImageView gaugeBar; protected GaugeDrawer gaugeDrawer; private final int label; private final String targetActivityTab; private String mode = ""; public AbstractGaugeWidget(@StringRes final int label, @Nullable final String targetActivityTab) { this.label = label; this.targetActivityTab = targetActivityTab; } public AbstractGaugeWidget(@StringRes final int label, @Nullable final String targetActivityTab, final String mode) { this(label, targetActivityTab); this.mode = mode; } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { final View fragmentView = inflater.inflate(R.layout.dashboard_widget_generic_gauge, container, false); if (targetActivityTab != null) { onClickOpenChart(fragmentView, targetActivityTab, label, mode); } gaugeValue = fragmentView.findViewById(R.id.gauge_value); gaugeBar = fragmentView.findViewById(R.id.gauge_bar); gaugeDrawer = new GaugeDrawer(); final TextView gaugeLabel = fragmentView.findViewById(R.id.gauge_label); gaugeLabel.setText(label); fillData(); return fragmentView; } @Override public void onResume() { super.onResume(); if (gaugeValue != null && gaugeBar != null) fillData(); } @Override protected void fillData() { if (gaugeBar == null) return; gaugeBar.post(() -> { final FillDataAsyncTask myAsyncTask = new FillDataAsyncTask(); myAsyncTask.execute(); }); } /** * This is called from the async task, outside of the UI thread. It's expected that * {@link nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment.DashboardData} be * populated with the necessary data for display. * * @param dashboardData the DashboardData to populate */ protected abstract void populateData(DashboardFragment.DashboardData dashboardData); /** * This is called from the UI thread. * * @param dashboardData populated DashboardData */ protected abstract void draw(DashboardFragment.DashboardData dashboardData); private class FillDataAsyncTask extends AsyncTask { @Override protected Void doInBackground(final Void... params) { final long nanoStart = System.nanoTime(); try { populateData(dashboardData); } catch (final Exception e) { LOG.error("fillData for {} failed", AbstractGaugeWidget.this.getClass().getSimpleName(), e); } final long nanoEnd = System.nanoTime(); final long executionTime = (nanoEnd - nanoStart) / 1000000; LOG.debug("fillData for {} took {}ms", AbstractGaugeWidget.this.getClass().getSimpleName(), executionTime); return null; } @Override protected void onPostExecute(final Void unused) { super.onPostExecute(unused); try { draw(dashboardData); } catch (final Exception e) { LOG.error("draw for {} failed", AbstractGaugeWidget.this.getClass().getSimpleName(), e); } } } protected void setText(final CharSequence text) { gaugeValue.setText(text); } /** * Draw a simple gauge. * * @param color the gauge color * @param value the gauge value. Range: [0, 1] */ protected void drawSimpleGauge(final int color, final float value) { gaugeDrawer.drawSimpleGauge(gaugeBar, color, value); } /** * Draws a segmented gauge. * * @param colors the colors of each segment * @param segments the size of each segment. The sum of all segments should be 1 * @param value the gauge value, in range [0, 1], or -1 for no value and only segments * @param fadeOutsideDot whether to fade out colors outside the dot value * @param gapBetweenSegments whether to introduce a small gap between the segments */ protected void drawSegmentedGauge(final int[] colors, final float[] segments, final float value, final boolean fadeOutsideDot, final boolean gapBetweenSegments) { gaugeDrawer.drawSegmentedGauge(gaugeBar, colors, segments, value, fadeOutsideDot, gapBetweenSegments); } }