1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-23 09:07:44 +01:00

Dashboard: Show widgets preview in preferences

This commit is contained in:
José Rebelo 2024-09-28 16:40:38 +01:00
parent 2ad7f8aef9
commit d71c50150e
11 changed files with 252 additions and 68 deletions

View File

@ -238,6 +238,7 @@ public class DashboardFragment extends Fragment implements MenuProvider {
if (widget == null) {
continue;
}
widget.reloadPreferences();
if ("today".equals(widgetName) || "goals".equals(widgetName)) {
columnSpan = prefs.getBoolean("dashboard_widget_" + widgetName + "_2columns", true) ? 2 : 1;
}

View File

@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.text.InputType;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@ -31,6 +32,7 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.preferences.DashboardWidgetPreviewPreference;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public class DashboardPreferencesActivity extends AbstractSettingsActivityV2 {
@ -51,6 +53,9 @@ public class DashboardPreferencesActivity extends AbstractSettingsActivityV2 {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
setPreferencesFromResource(R.xml.dashboard_preferences, rootKey);
final DashboardWidgetPreviewPreference previewToday = findPreference("dashboard_widget_preview_today");
final DashboardWidgetPreviewPreference previewGoals = findPreference("dashboard_widget_preview_goals");
setInputTypeFor("dashboard_widget_today_hr_interval", InputType.TYPE_CLASS_NUMBER);
final MultiSelectListPreference dashboardDevices = findPreference("dashboard_devices_multiselect");
@ -78,11 +83,21 @@ public class DashboardPreferencesActivity extends AbstractSettingsActivityV2 {
"dashboard_devices_multiselect"
);
Preference pref;
final Handler handler = new Handler(requireContext().getMainLooper());
for (String dashboardPref : dashboardPrefs) {
pref = findPreference(dashboardPref);
if (pref != null) {
pref.setOnPreferenceChangeListener((preference, autoExportEnabled) -> {
sendDashboardConfigChangedIntent();
// Delay so preferences are persisted
handler.postDelayed(() -> {
if (previewToday != null) {
previewToday.refresh();
}
if (previewGoals != null) {
previewGoals.refresh();
}
}, 500);
return true;
});
}

View File

@ -28,9 +28,6 @@ import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@ -43,8 +40,6 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public abstract class AbstractDashboardWidget extends Fragment {
private static final Logger LOG = LoggerFactory.getLogger(AbstractDashboardWidget.class);
protected static String ARG_DASHBOARD_DATA = "dashboard_widget_argument_data";
protected DashboardData dashboardData;
@ -70,9 +65,14 @@ public abstract class AbstractDashboardWidget extends Fragment {
}
public void update() {
reloadPreferences();
fillData();
}
public void reloadPreferences() {
}
protected abstract void fillData();
protected boolean isSupportedBy(final GBDevice device) {

View File

@ -49,6 +49,7 @@ public class DashboardGoalsWidget extends AbstractDashboardWidget {
private static final Logger LOG = LoggerFactory.getLogger(DashboardGoalsWidget.class);
private View goalsView;
private ImageView goalsChart;
private TextView legend;
public DashboardGoalsWidget() {
// Required empty public constructor
@ -75,7 +76,7 @@ public class DashboardGoalsWidget extends AbstractDashboardWidget {
goalsChart = goalsView.findViewById(R.id.dashboard_goals_chart);
// Initialize legend
TextView legend = goalsView.findViewById(R.id.dashboard_goals_legend);
legend = goalsView.findViewById(R.id.dashboard_goals_legend);
SpannableString l_steps = new SpannableString("" + getString(R.string.steps));
l_steps.setSpan(new ForegroundColorSpan(color_activity), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableString l_distance = new SpannableString("" + getString(R.string.distance));
@ -87,9 +88,6 @@ public class DashboardGoalsWidget extends AbstractDashboardWidget {
SpannableStringBuilder legendBuilder = new SpannableStringBuilder();
legend.setText(legendBuilder.append(l_steps).append(" ").append(l_distance).append("\n").append(l_active_time).append(" ").append(l_sleep));
Prefs prefs = GBApplication.getPrefs();
legend.setVisibility(prefs.getBoolean("dashboard_widget_goals_legend", true) ? View.VISIBLE : View.GONE);
return goalsView;
}
@ -99,6 +97,14 @@ public class DashboardGoalsWidget extends AbstractDashboardWidget {
if (goalsChart != null) fillData();
}
@Override
public void reloadPreferences() {
super.reloadPreferences();
final Prefs prefs = GBApplication.getPrefs();
legend.setVisibility(prefs.getBoolean("dashboard_widget_goals_legend", true) ? View.VISIBLE : View.GONE);
}
@Override
protected void fillData() {
if (goalsView == null) return;

View File

@ -70,6 +70,7 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
private View todayView;
private ImageView todayChart;
private TextView legend;
private boolean mode_24h;
@ -92,18 +93,26 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
return fragment;
}
@Override
public void reloadPreferences() {
super.reloadPreferences();
final Prefs prefs = GBApplication.getPrefs();
// Determine whether to draw a single or a double chart. In case 24h mode is selected,
// use just the outer chart (chart_12_24) for all data.
mode_24h = prefs.getBoolean("dashboard_widget_today_24h", false);
legend.setVisibility(prefs.getBoolean("dashboard_widget_today_legend", true) ? View.VISIBLE : View.GONE);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
todayView = inflater.inflate(R.layout.dashboard_widget_today, container, false);
todayChart = todayView.findViewById(R.id.dashboard_today_chart);
// Determine whether to draw a single or a double chart. In case 24h mode is selected,
// use just the outer chart (chart_12_24) for all data.
Prefs prefs = GBApplication.getPrefs();
mode_24h = prefs.getBoolean("dashboard_widget_today_24h", false);
// Initialize legend
TextView legend = todayView.findViewById(R.id.dashboard_piechart_legend);
legend = todayView.findViewById(R.id.dashboard_piechart_legend);
SpannableString l_not_worn = new SpannableString("" + getString(R.string.abstract_chart_fragment_kind_not_worn));
l_not_worn.setSpan(new ForegroundColorSpan(color_not_worn), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableString l_worn = new SpannableString("" + getString(R.string.activity_type_worn));
@ -121,8 +130,6 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
SpannableStringBuilder legendBuilder = new SpannableStringBuilder();
legend.setText(legendBuilder.append(l_not_worn).append(" ").append(l_worn).append("\n").append(l_activity).append(" ").append(l_exercise).append("\n").append(l_light_sleep).append(" ").append(l_deep_sleep).append(" ").append(l_rem_sleep));
legend.setVisibility(prefs.getBoolean("dashboard_widget_today_legend", true) ? View.VISIBLE : View.GONE);
if (!dashboardData.generalizedActivities.isEmpty()) {
draw();
}

View File

@ -0,0 +1,97 @@
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard.preferences;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentContainerView;
import androidx.gridlayout.widget.GridLayout;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import java.util.GregorianCalendar;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.AbstractDashboardWidget;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.data.DashboardData;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.widgets.DashboardWidgetFactory;
public class DashboardWidgetPreviewPreference extends Preference {
private final String widgetName;
private AbstractDashboardWidget widget;
private DashboardData dashboardData;
public DashboardWidgetPreviewPreference(@NonNull final Context context,
@Nullable final AttributeSet attrs) {
super(context, attrs);
setPersistent(false);
// Obtain custom attributes
try (TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.DashboardWidgetPreviewPreference,
0, 0)) {
if (attrs != null) {
widgetName = a.getString(R.styleable.DashboardWidgetPreviewPreference_widgetName);
} else {
widgetName = null;
}
}
setLayoutResource(R.layout.dashboard_widget_preview_preference);
setWidgetLayoutResource(R.layout.dashboard_widget_preview_empty);
}
@Override
public void onBindViewHolder(@NonNull final PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
dashboardData = new DashboardData();
dashboardData.reloadPreferences(GregorianCalendar.getInstance());
if (widget == null) {
widget = DashboardWidgetFactory.createWidget(widgetName, dashboardData);
if (widget == null) {
return;
}
}
final FragmentActivity activity = (FragmentActivity) getContext();
final int columnSpan = 1;
final GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(
GridLayout.spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f),
GridLayout.spec(GridLayout.UNDEFINED, columnSpan, GridLayout.FILL, 1f)
);
final float scale = activity.getResources().getDisplayMetrics().density;
layoutParams.width = 0;
final int pixels_8dp = (int) (8 * scale + 0.5f);
layoutParams.setMargins(pixels_8dp, pixels_8dp, pixels_8dp, pixels_8dp);
final FragmentContainerView fragment = new FragmentContainerView(activity);
int fragmentId = View.generateViewId();
fragment.setId(fragmentId);
fragment.setLayoutParams(layoutParams);
activity.getSupportFragmentManager()
.beginTransaction()
.replace(fragmentId, widget)
.commitAllowingStateLoss();
final GridLayout gridlayout = (GridLayout) holder.findViewById(R.id.widget_preview_gridlayout);
gridlayout.addView(fragment);
}
public void refresh() {
if (dashboardData != null) {
dashboardData.reloadPreferences(GregorianCalendar.getInstance());
}
if (widget != null) {
widget.update();
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- FIXME we need something to pass to setWidgetLayoutResource or nothing shows up ? -->
</LinearLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@android:id/widget_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.gridlayout.widget.GridLayout
android:id="@+id/widget_preview_gridlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:columnCount="1" />
</LinearLayout>

View File

@ -22,4 +22,8 @@
<attr name="deviceIconOnPrimary" format="color" />
<attr name="deviceIconLight" format="color" />
<attr name="deviceIconDark" format="color" />
<declare-styleable name="DashboardWidgetPreviewPreference">
<attr name="widgetName" format="string" />
</declare-styleable>
</resources>

View File

@ -2916,6 +2916,7 @@
<string name="title">Title</string>
<string name="description">Description</string>
<string name="preview_image">Preview image</string>
<string name="preview">Preview</string>
<string name="status_icon">Status icon</string>
<string name="changelog_full_title">Changelog</string>
<string name="changelog_show_full">More…</string>

View File

@ -39,62 +39,94 @@
android:key="pref_key_dashboard_today"
android:title="@string/pref_dashboard_widget_today_title"
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="dashboard_widget_today_24h"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_today_24h_title"
android:summary="@string/pref_dashboard_widget_today_24h_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="dashboard_widget_today_24h_upside_down"
android:dependency="dashboard_widget_today_24h"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_today_upside_down_title"
android:summary="@string/pref_dashboard_widget_today_upside_down_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_today_2columns"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_double_size_title"
android:summary="@string/pref_dashboard_widget_double_size_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_today_legend"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_show_legend_title"
android:summary="@string/pref_dashboard_widget_show_legend_summary"
app:iconSpaceReserved="false" />
<EditTextPreference
android:defaultValue="1"
android:inputType="number"
android:key="dashboard_widget_today_hr_interval"
android:maxLength="4"
android:summary="@string/pref_dashboard_widget_today_hr_interval_summary"
android:title="@string/pref_dashboard_widget_today_hr_interval_title"
app:iconSpaceReserved="false" />
<PreferenceCategory
android:key="pref_key_dashboard_today_preview"
android:title="@string/preview"
app:iconSpaceReserved="false">
<nodomain.freeyourgadget.gadgetbridge.activities.dashboard.preferences.DashboardWidgetPreviewPreference
android:key="dashboard_widget_preview_today"
app:widgetName="today"
android:title=""
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory
android:key="pref_key_dashboard_today_settings"
android:title="@string/action_settings"
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="dashboard_widget_today_24h"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_today_24h_title"
android:summary="@string/pref_dashboard_widget_today_24h_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="dashboard_widget_today_24h_upside_down"
android:dependency="dashboard_widget_today_24h"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_today_upside_down_title"
android:summary="@string/pref_dashboard_widget_today_upside_down_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_today_2columns"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_double_size_title"
android:summary="@string/pref_dashboard_widget_double_size_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_today_legend"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_show_legend_title"
android:summary="@string/pref_dashboard_widget_show_legend_summary"
app:iconSpaceReserved="false" />
<EditTextPreference
android:defaultValue="1"
android:inputType="number"
android:key="dashboard_widget_today_hr_interval"
android:maxLength="4"
android:summary="@string/pref_dashboard_widget_today_hr_interval_summary"
android:title="@string/pref_dashboard_widget_today_hr_interval_title"
app:iconSpaceReserved="false" />
</PreferenceCategory>
</PreferenceScreen>
<PreferenceScreen
android:key="pref_key_dashboard_goals"
android:title="@string/pref_dashboard_widget_goals_chart_title"
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_goals_2columns"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_double_size_title"
android:summary="@string/pref_dashboard_widget_double_size_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_goals_legend"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_show_legend_title"
android:summary="@string/pref_dashboard_widget_show_legend_summary"
app:iconSpaceReserved="false" />
<PreferenceCategory
android:key="pref_key_dashboard_goals_preview"
android:title="@string/preview"
app:iconSpaceReserved="false">
<nodomain.freeyourgadget.gadgetbridge.activities.dashboard.preferences.DashboardWidgetPreviewPreference
android:key="dashboard_widget_preview_goals"
app:widgetName="goals"
android:title=""
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory
android:key="pref_key_dashboard_goals_settings"
android:title="@string/action_settings"
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_goals_2columns"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_double_size_title"
android:summary="@string/pref_dashboard_widget_double_size_summary"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="dashboard_widget_goals_legend"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_dashboard_widget_show_legend_title"
android:summary="@string/pref_dashboard_widget_show_legend_summary"
app:iconSpaceReserved="false" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>