Add Activity list dashboard summary calculations

Accessible via FAB in Activity list
Adds DebugActivity checkbox to show large date range
This commit is contained in:
vanous 2021-03-03 19:03:02 +01:00
parent 1959f1c579
commit e848a7cb71
17 changed files with 697 additions and 180 deletions

View File

@ -37,6 +37,8 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Spinner;
@ -371,6 +373,20 @@ public class DebugActivity extends AbstractGBActivity {
}
});
CheckBox activity_list_debug_extra_time_range = findViewById(R.id.activity_list_debug_extra_time_range);
activity_list_debug_extra_time_range.setAllCaps(true);
boolean activity_list_debug_extra_time_range_value = GBApplication.getPrefs().getPreferences().getBoolean("activity_list_debug_extra_time_range", false);
activity_list_debug_extra_time_range.setChecked(activity_list_debug_extra_time_range_value);
activity_list_debug_extra_time_range.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
GBApplication.getPrefs().getPreferences().getBoolean("activity_list_debug_extra_time_range", false);
SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit();
editor.putBoolean("activity_list_debug_extra_time_range", b).apply();
}
});
}
private void deleteWidgetsPrefs() {

View File

@ -661,6 +661,11 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
}
}
@Override
protected void onPreExecute() {
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);

View File

@ -38,6 +38,7 @@ public class ActivityListingAdapter extends AbstractActivityListingAdapter<Activ
protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class);
protected final int ANIM_TIME = 250;
private final int SESSION_SUMMARY = ActivitySession.SESSION_SUMMARY;
private final int SESSION_EMPTY = ActivitySession.SESSION_EMPTY;
ActivityUser activityUser = new ActivityUser();
int stepsGoal = activityUser.getStepsGoal();
int distanceGoalMeters = activityUser.getDistanceMeters();
@ -257,6 +258,11 @@ public class ActivityListingAdapter extends AbstractActivityListingAdapter<Activ
unit = "###.#km";
}
if (distanceMeters > 999000) {
distanceFormatted = distanceMeters / 1000;
unit = "###km";
}
String units = GBApplication.getPrefs().getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, GBApplication.getContext().getString(R.string.p_unit_metric));
if (units.equals(GBApplication.getContext().getString(R.string.p_unit_imperial))) {
unit = "###ft";
@ -265,6 +271,10 @@ public class ActivityListingAdapter extends AbstractActivityListingAdapter<Activ
distanceFormatted = distanceFeet * 0.0001893939f;
unit = "###.#mi";
}
if (distanceFeet > 5274721) {
distanceFormatted = distanceFeet * 0.0001893939f;
unit = "###mi";
}
}
DecimalFormat df = new DecimalFormat(unit);
return df.format(distanceFormatted);
@ -339,6 +349,12 @@ public class ActivityListingAdapter extends AbstractActivityListingAdapter<Activ
return sessionType == SESSION_SUMMARY;
}
@Override
protected boolean isEmptySession(ActivitySession item, int position) {
int sessionType = item.getSessionType();
return sessionType == SESSION_EMPTY;
}
@Override
protected boolean isEmptySummary(ActivitySession item) {
return item.getIsEmptySummary();

View File

@ -27,6 +27,8 @@ import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;
import androidx.fragment.app.FragmentManager;
import com.github.mikephil.charting.charts.Chart;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
@ -48,7 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
public class ActivityListingChartFragment extends AbstractChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(ActivityListingChartFragment.class);
int tsDateFrom;
int tsDateTo;
private View rootView;
private ActivityListingAdapter stepListAdapter;
@ -65,7 +67,13 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
stepsDateView = rootView.findViewById(R.id.stepsDateView);
FloatingActionButton fab;
fab = rootView.findViewById(R.id.fab);
fab.setVisibility(View.GONE);
fab.setVisibility(View.VISIBLE);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showDashboard(tsDateTo, getChartsHost().getDevice());
}
});
refresh();
return rootView;
@ -103,7 +111,11 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
if (stepSessions.toArray().length == 0) {
isEmptySummary = true;
}
stepSessions = stepAnalysis.calculateSummary(stepSessions, isEmptySummary);
ActivitySession stepSessionsSummary = stepAnalysis.calculateSummary(stepSessions, isEmptySummary);
stepSessions.add(0, stepSessionsSummary);
ActivitySession emptySession = new ActivitySession();
emptySession.setSessionType(ActivitySession.SESSION_EMPTY);
stepSessions.add(emptySession); //this is to have an empty item at the end to be able to use FAB without it blocking anything
ongoingSession = stepAnalysis.getOngoingSessions(stepSessions);
}
return new MyChartsData(stepSessions, ongoingSession);
@ -118,7 +130,7 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
getChartsHost().enableSwipeRefresh(false); //disable pull to refresh as it collides with swipable view
}
stepsDateView.setText(DateTimeUtils.formatDate(new Date(tsDateFrom * 1000L)));
stepsDateView.setText(DateTimeUtils.formatDate(new Date(tsDateTo * 1000L)));
if (GBApplication.getPrefs().getBoolean("charts_show_ongoing_activity", true)) {
if (mcd.getOngoingSession() != null) {
showOngoingActivitySnackbar(mcd.getOngoingSession());
@ -144,7 +156,7 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
day.set(Calendar.SECOND, 0);
tsFrom = (int) (day.getTimeInMillis() / 1000);
tsTo = tsFrom + 24 * 60 * 60 - 1;
tsDateFrom = tsFrom;
tsDateTo = tsTo;
return getAllSamples(db, device, tsFrom, tsTo);
}
@ -176,6 +188,14 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
snackbar.show();
}
private void showDashboard(int date, GBDevice device) {
FragmentManager fm = getActivity().getSupportFragmentManager();
ActivityListingDashboard editNameDialogFragment = ActivityListingDashboard.newInstance(date, device);
editNameDialogFragment.show(fm, "activity_list_total_dashboard");
}
private static class MyChartsData extends ChartsData {
private final List<ActivitySession> stepSessions;
private final ActivitySession ongoingSession;

View File

@ -0,0 +1,327 @@
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import android.app.DatePickerDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.DatePicker;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
public class ActivityListingDashboard extends DialogFragment {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class);
GBDevice gbDevice;
ActivityListingAdapter stepListAdapter;
ActivitySession stepSessionsSummary;
private int timeFrom;
private int timeTo;
private View fragmentView;
public ActivityListingDashboard() {
}
public static ActivityListingDashboard newInstance(int timestamp, GBDevice device) {
ActivityListingDashboard frag = new ActivityListingDashboard();
Bundle args = new Bundle();
args.putInt("time", timestamp);
args.putParcelable(GBDevice.EXTRA_DEVICE, device);
frag.setArguments(args);
return frag;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.activity_list_total_dashboard, container);
}
@Override
public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
int time = getArguments().getInt("time", 1);
Calendar day = Calendar.getInstance();
day.setTimeInMillis(time * 1000L);
day.set(Calendar.HOUR_OF_DAY, 23);
day.set(Calendar.MINUTE, 59);
day.set(Calendar.SECOND, 59);
timeTo = (int) (day.getTimeInMillis() / 1000);
gbDevice = getArguments().getParcelable(GBDevice.EXTRA_DEVICE);
if (gbDevice == null) {
throw new IllegalArgumentException("Must provide a device when invoking this activity");
}
stepListAdapter = new ActivityListingAdapter(getContext());
fragmentView = view;
final TextView battery_status_date_from_text = (TextView) view.findViewById(R.id.battery_status_date_from_text);
final TextView battery_status_date_to_text = (TextView) view.findViewById(R.id.battery_status_date_to_text);
LinearLayout battery_status_date_to_layout = (LinearLayout) view.findViewById(R.id.battery_status_date_to_layout);
final SeekBar battery_status_time_span_seekbar = (SeekBar) view.findViewById(R.id.battery_status_time_span_seekbar);
boolean activity_list_debug_extra_time_range_value = GBApplication.getPrefs().getPreferences().getBoolean("activity_list_debug_extra_time_range", false);
if (!activity_list_debug_extra_time_range_value) {
battery_status_time_span_seekbar.setMax(3);
}
final TextView battery_status_time_span_text = (TextView) view.findViewById(R.id.battery_status_time_span_text);
battery_status_time_span_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
String text;
switch (i) {
case 0:
text = getString(R.string.calendar_day);
timeFrom = shiftDateBackDays(timeTo, 1);
break;
case 1:
text = getString(R.string.calendar_week);
timeFrom = shiftDateBackDays(timeTo, 7);
break;
case 2:
text = getString(R.string.calendar_two_weeks);
timeFrom = shiftDateBackDays(timeTo, 14);
break;
case 3:
text = getString(R.string.calendar_month);
timeFrom = subtractMonths(timeTo, -1);
break;
case 4:
text = getString(R.string.calendar_three_months);
timeFrom = subtractMonths(timeTo, -3);
break;
case 5:
text = getString(R.string.calendar_six_months);
timeFrom = subtractMonths(timeTo, -6);
break;
case 6:
text = getString(R.string.calendar_year);
timeFrom = subtractMonths(timeTo, -12);
break;
default:
text = getString(R.string.calendar_two_weeks);
timeFrom = shiftDateBackDays(timeTo, 14);
}
battery_status_time_span_text.setText(text);
battery_status_date_from_text.setText(DateTimeUtils.formatDate(new Date(timeFrom * 1000L)));
battery_status_date_to_text.setText(DateTimeUtils.formatDate(new Date(timeTo * 1000L)));
createRefreshTask("Visualizing data", getActivity()).execute();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
battery_status_date_to_layout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Calendar currentDate = Calendar.getInstance();
currentDate.setTimeInMillis(timeTo * 1000L);
new DatePickerDialog(getContext(), new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
Calendar date = Calendar.getInstance();
date.set(year, monthOfYear, dayOfMonth);
int time = (int) (date.getTimeInMillis() / 1000);
Calendar day = Calendar.getInstance();
day.setTimeInMillis(time * 1000L);
day.set(Calendar.HOUR_OF_DAY, 23);
day.set(Calendar.MINUTE, 59);
day.set(Calendar.SECOND, 59);
timeTo = (int) (day.getTimeInMillis() / 1000);
battery_status_date_to_text.setText(DateTimeUtils.formatDate(new Date(timeTo * 1000L)));
battery_status_time_span_seekbar.setProgress(0);
battery_status_time_span_seekbar.setProgress(1);
}
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
}
});
battery_status_time_span_seekbar.setProgress(2);
}
private int subtractMonths(int timeTo, int month) {
Calendar day = Calendar.getInstance();
day.setTimeInMillis(timeTo * 1000L);
day.add(Calendar.MONTH, month);
return (int) (day.getTimeInMillis() / 1000);
}
private int daysBetweenTimes(int time1, int time2) {
return (int) TimeUnit.MILLISECONDS.toDays((time2 - time1) * 1000L);
}
private int shiftDateBackDays(int timeTo, int days) {
int time = timeTo - ((24 * 3600) - 1) * days;
Calendar day = Calendar.getInstance();
day.setTimeInMillis(time * 1000L);
day.set(Calendar.HOUR_OF_DAY, 0);
day.set(Calendar.MINUTE, 0);
day.set(Calendar.SECOND, 0);
return (int) (day.getTimeInMillis() / 1000);
}
protected RefreshTask createRefreshTask(String task, Context context) {
return new RefreshTask(task, context);
}
private ActivitySession get_data(GBDevice gbDevice, DBHandler db, int timeFrom, int timeTo) {
List<ActivitySession> stepSessions;
List<? extends ActivitySample> activitySamples = getAllSamples(db, gbDevice, timeFrom, timeTo);
StepAnalysis stepAnalysis = new StepAnalysis();
boolean isEmptySummary = false;
if (activitySamples != null) {
stepSessions = stepAnalysis.calculateStepSessions(activitySamples);
if (stepSessions.toArray().length == 0) {
isEmptySummary = true;
}
stepSessionsSummary = stepAnalysis.calculateSummary(stepSessions, isEmptySummary);
}
return stepSessionsSummary;
}
SampleProvider<? extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
return coordinator.getSampleProvider(device, db.getDaoSession());
}
protected List<? extends ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
SampleProvider<? extends ActivitySample> provider = getProvider(db, device);
return provider.getAllActivitySamples(tsFrom, tsTo);
}
void indicate_progress(boolean inProgress) {
LinearLayout activity_list_dashboard_results_layout = fragmentView.findViewById(R.id.activity_list_dashboard_results_layout);
RelativeLayout activity_list_dashboard_loading_layout = fragmentView.findViewById(R.id.activity_list_dashboard_loading_layout);
if (inProgress) {
activity_list_dashboard_results_layout.setVisibility(View.GONE);
activity_list_dashboard_loading_layout.setVisibility(View.VISIBLE);
} else {
activity_list_dashboard_results_layout.setVisibility(View.VISIBLE);
activity_list_dashboard_loading_layout.setVisibility(View.GONE);
}
}
void populateData(ActivitySession item) {
TextView stepLabel = fragmentView.findViewById(R.id.line_layout_step_label);
TextView stepTotalLabel = fragmentView.findViewById(R.id.line_layout_total_step_label);
TextView distanceLabel = fragmentView.findViewById(R.id.line_layout_distance_label);
TextView durationLabel = fragmentView.findViewById(R.id.line_layout_duration_label);
TextView sessionCountLabel = fragmentView.findViewById(R.id.line_layout_count_label);
LinearLayout durationLayout = fragmentView.findViewById(R.id.line_layout_duration);
LinearLayout countLayout = fragmentView.findViewById(R.id.line_layout_count);
LinearLayout stepsLayout = fragmentView.findViewById(R.id.line_layout_step);
LinearLayout stepsTotalLayout = fragmentView.findViewById(R.id.line_layout_total_step);
LinearLayout distanceLayout = fragmentView.findViewById(R.id.line_layout_distance);
stepLabel.setText(stepListAdapter.getStepLabel(item));
stepTotalLabel.setText(stepListAdapter.getStepTotalLabel(item));
distanceLabel.setText(stepListAdapter.getDistanceLabel(item));
durationLabel.setText(stepListAdapter.getDurationLabel(item));
sessionCountLabel.setText(stepListAdapter.getSessionCountLabel(item));
if (!stepListAdapter.hasDistance(item)) {
distanceLayout.setVisibility(View.GONE);
} else {
distanceLayout.setVisibility(View.VISIBLE);
}
if (!stepListAdapter.hasSteps(item)) {
stepsLayout.setVisibility(View.GONE);
} else {
stepsLayout.setVisibility(View.VISIBLE);
}
if (!stepListAdapter.hasTotalSteps(item)) {
stepsTotalLayout.setVisibility(View.GONE);
countLayout.setVisibility(View.GONE);
durationLayout.setVisibility(View.GONE);
} else {
stepsTotalLayout.setVisibility(View.VISIBLE);
countLayout.setVisibility(View.VISIBLE);
durationLayout.setVisibility(View.VISIBLE);
}
}
public class RefreshTask extends DBAccess {
public RefreshTask(String task, Context context) {
super(task, context);
}
@Override
protected void doInBackground(DBHandler db) {
stepSessionsSummary = get_data(gbDevice, db, timeFrom, timeTo);
}
@Override
protected void onPreExecute() {
indicate_progress(true);
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
FragmentActivity activity = getActivity();
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
populateData(stepSessionsSummary);
indicate_progress(false);
} else {
LOG.info("Not filling data because activity is not available anymore");
}
}
}
}

View File

@ -35,6 +35,7 @@ public class StepAnalysis {
private int totalDailySteps = 0;
public List<ActivitySession> calculateStepSessions(List<? extends ActivitySample> samples) {
LOG.debug("get all samples activitysessions: " + samples.toArray().length);
List<ActivitySession> result = new ArrayList<>();
ActivityUser activityUser = new ActivityUser();
double STEP_LENGTH_M;
@ -154,7 +155,7 @@ public class StepAnalysis {
return result;
}
public List<ActivitySession> calculateSummary(List<ActivitySession> sessions, boolean empty) {
public ActivitySession calculateSummary(List<ActivitySession> sessions, boolean empty) {
Date startTime = null;
Date endTime = null;
@ -164,7 +165,7 @@ public class StepAnalysis {
int distanceSum = 0;
float intensitySum = 0;
int sessionCount;
int durationSum = 0;
long durationSum = 0;
for (ActivitySession session : sessions) {
startTime = session.getStartTime();
@ -192,11 +193,7 @@ public class StepAnalysis {
stepSessionSummary.setTotalDaySteps(totalDailySteps);
List<ActivitySession> newList = new ArrayList<>();
newList.add(stepSessionSummary);
newList.addAll(sessions);
return newList;
return stepSessionSummary;
}
public ActivitySession getOngoingSessions(List<ActivitySession> sessions) {

View File

@ -76,6 +76,8 @@ public abstract class AbstractActivityListingAdapter<T> extends ArrayAdapter<T>
if (isSummary(item, position)) {
view = fill_dashboard(item, position, view, parent, context);
} else if (isEmptySession(item, position)) {
view = fill_empty(item, position, view, parent);
} else {
view = fill_item(item, position, view, parent);
}
@ -174,6 +176,14 @@ public abstract class AbstractActivityListingAdapter<T> extends ArrayAdapter<T>
return view;
}
private View fill_empty(T item, int position, View view, ViewGroup parent) {
view = null;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.activity_list_item, parent, false);
view.setVisibility(View.GONE);
return view;
}
protected abstract View fill_dashboard(T item, int position, View view, ViewGroup parent, Context context);
@ -215,6 +225,8 @@ public abstract class AbstractActivityListingAdapter<T> extends ArrayAdapter<T>
protected abstract boolean isSummary(T item, int position);
protected abstract boolean isEmptySession(T item, int position);
protected abstract boolean isEmptySummary(T item);
protected abstract String getStepTotalLabel(T item);

View File

@ -397,6 +397,9 @@ public class ActivitySummariesAdapter extends AbstractActivityListingAdapter<Bas
return position == 0;
}
@Override
protected boolean isEmptySession(BaseActivitySummary item, int position) { return false; }
@Override
protected boolean isEmptySummary(BaseActivitySummary item) {
return false;

View File

@ -39,6 +39,7 @@ public abstract class DBAccess extends AsyncTask {
}
protected abstract void doInBackground(DBHandler handler);
protected abstract void onPreExecute();
@Override
protected Object doInBackground(Object[] params) {
@ -57,6 +58,8 @@ public abstract class DBAccess extends AsyncTask {
}
}
protected void displayError(Throwable error) {
GB.toast(getContext(), getContext().getString(R.string.dbaccess_error_executing, error.getMessage()), Toast.LENGTH_LONG, GB.ERROR, error);
}

View File

@ -9,6 +9,7 @@ public class ActivitySession {
public static int SESSION_NORMAL = 1;
public static int SESSION_SUMMARY = 2;
public static int SESSION_ONGOING = 3;
public static int SESSION_EMPTY = 4;
private final Date startTime;
private final Date endTime;
@ -37,6 +38,16 @@ public class ActivitySession {
this.activityKind = activityKind;
}
public ActivitySession(){
this.startTime = null;
this.endTime = null;
this.activeSteps=0;
this.heartRateAverage = 0;
this.intensity = 0;
this.distance = 0;
this.activityKind = 0;
};
public Date getStartTime() {
return startTime;
}

View File

@ -0,0 +1,4 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal">
<path android:fillColor="@android:color/white" android:pathData="M21 8c-1.45 0-2.26 1.44-1.93 2.51l-3.55 3.56c-0.3-0.09-0.74-0.09-1.04 0l-2.55-2.55C12.27 10.45 11.46 9 10 9c-1.45 0-2.27 1.44-1.93 2.52l-4.56 4.55C2.44 15.74 1 16.55 1 18c0 1.1 0.9 2 2 2 1.45 0 2.26-1.44 1.93-2.51l4.55-4.56c0.3 0.09 0.74 0.09 1.04 0l2.55 2.55C12.73 16.55 13.54 18 15 18c1.45 0 2.27-1.44 1.93-2.52l3.56-3.55C21.56 12.26 23 11.45 23 10c0-1.1-0.9-2-2-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M15 9l0.94-2.07L18 6l-2.06-0.93L15 3l-0.92 2.07L12 6l2.08 0.93zM3.5 11L4 9l2-0.5L4 8 3.5 6 3 8 1 8.5 3 9z"/>
</vector>

View File

@ -84,167 +84,7 @@
android:layout_height="350dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/layout_widget_duration_selector" />
<SeekBar
android:id="@+id/battery_status_time_span_seekbar"
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="rtl"
android:max="5"
android:progress="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginBottom="10dp"
android:baselineAligned="false"
android:gravity="bottom|center"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_weight="1"
android:gravity="center|top"
android:orientation="vertical">
<ImageView
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_gravity="center"
android:contentDescription="@string/icon_placeholder"
android:scaleX="-1"
app:srcCompat="@drawable/ic_calendar_to" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:maxLines="2"
android:scrollHorizontally="false"
android:text="@string/activity_filter_date_from"
android:textAllCaps="true"
android:textColor="@color/accent"
android:textStyle="bold" />
<TextView
android:id="@+id/battery_status_date_from_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-black"
android:gravity="center"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="@string/lorem_ipsum"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_weight="1"
android:gravity="center|top"
android:orientation="vertical">
<ImageView
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_gravity="center"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_date_range" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:maxLines="2"
android:scrollHorizontally="false"
android:text="@string/activity_detail_duration_label"
android:textAllCaps="true"
android:textColor="@color/accent"
android:textStyle="bold" />
<TextView
android:id="@+id/battery_status_time_span_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-black"
android:gravity="center"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="@string/lorem_ipsum"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/battery_status_date_to_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_weight="1"
android:gravity="center|top"
android:orientation="vertical">
<ImageView
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_gravity="center"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_calendar_to" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:maxLines="2"
android:scrollHorizontally="false"
android:text="@string/activity_filter_date_to"
android:textAllCaps="true"
android:textColor="@color/accent"
android:textStyle="bold" />
<TextView
android:id="@+id/battery_status_date_to_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-black"
android:gravity="center"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="@string/lorem_ipsum"
android:textColor="@color/accent"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -1,4 +1,5 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -13,8 +14,7 @@
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<androidx.gridlayout.widget.GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto"
<androidx.gridlayout.widget.GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_vertical_margin"
@ -195,7 +195,22 @@
grid:layout_gravity="fill_horizontal"
android:text="Delete app Widgets Preferences" />
<CheckBox
android:id="@+id/activity_list_debug_extra_time_range"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_marginTop="7dp"
android:background="@color/accent"
android:buttonTint="@color/btn_dark"
android:padding="4dp"
android:paddingLeft="10dp"
android:text="Activity summary extra date range (takes long to generate when used)"
android:textColor="@android:color/white"
android:textStyle="bold"
grid:layout_columnSpan="2" />
</androidx.gridlayout.widget.GridLayout>
</ScrollView>
</RelativeLayout>

View File

@ -0,0 +1,87 @@
<?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="@+id/activity_battery_info_master_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:gravity="center|center_vertical"
android:minWidth="1000dp"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/activity_list_dashboard_loading_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="35dp"
android:layout_marginTop="35dp"
app:srcCompat="@drawable/gadgetbridge_img" />
<ProgressBar
android:layout_width="171dp"
android:layout_height="171dp"
android:indeterminate="true" />
</RelativeLayout>
<LinearLayout
android:id="@+id/activity_list_dashboard_results_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="30dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="bottom|center"
android:orientation="horizontal">
<include layout="@layout/layout_widget_active_steps" />
<include layout="@layout/layout_widget_distance" />
<include layout="@layout/layout_widget_duration" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:baselineAligned="false"
android:gravity="center"
android:orientation="horizontal">
<include layout="@layout/layout_widget_total_steps" />
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
app:srcCompat="@drawable/gadgetbridge_img" />
<include layout="@layout/layout_widget_activities_count" />
</LinearLayout>
</LinearLayout>
<include
layout="@layout/layout_widget_duration_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -22,9 +22,8 @@
android:layout_height="wrap_content"
android:layout_below="@id/stepsDateView"
android:layout_alignParentBottom="false"
android:layout_marginBottom="0dp"
android:layout_marginTop="0dp">
</ListView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
@ -37,8 +36,8 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
app:srcCompat="@drawable/ic_show_chart" />
android:layout_marginBottom="9dp"
app:srcCompat="@drawable/ic_insights" />

View File

@ -0,0 +1,161 @@
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<SeekBar
android:id="@+id/battery_status_time_span_seekbar"
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="rtl"
android:max="6"
android:progress="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginBottom="10dp"
android:baselineAligned="false"
android:gravity="bottom|center"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_weight="1"
android:gravity="center|top"
android:orientation="vertical">
<ImageView
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_gravity="center"
android:contentDescription="@string/icon_placeholder"
android:scaleX="-1"
app:srcCompat="@drawable/ic_calendar_to" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:maxLines="2"
android:scrollHorizontally="false"
android:text="@string/activity_filter_date_from"
android:textAllCaps="true"
android:textColor="@color/accent"
android:textStyle="bold" />
<TextView
android:id="@+id/battery_status_date_from_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-black"
android:gravity="center"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="@string/lorem_ipsum"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_weight="1"
android:gravity="center|top"
android:orientation="vertical">
<ImageView
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_gravity="center"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_date_range" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:maxLines="2"
android:scrollHorizontally="false"
android:text="@string/activity_detail_duration_label"
android:textAllCaps="true"
android:textColor="@color/accent"
android:textStyle="bold" />
<TextView
android:id="@+id/battery_status_time_span_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-black"
android:gravity="center"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="@string/lorem_ipsum"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/battery_status_date_to_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_weight="1"
android:gravity="center|top"
android:orientation="vertical">
<ImageView
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_gravity="center"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_calendar_to" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:maxLines="2"
android:scrollHorizontally="false"
android:text="@string/activity_filter_date_to"
android:textAllCaps="true"
android:textColor="@color/accent"
android:textStyle="bold" />
<TextView
android:id="@+id/battery_status_date_to_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-black"
android:gravity="center"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="@string/lorem_ipsum"
android:textColor="@color/accent"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -32,7 +32,8 @@
<string name="calendar_week">Week</string>
<string name="calendar_two_weeks">Two weeks</string>
<string name="calendar_month">Month</string>
<string name="calendar_six_months">Six months</string>
<string name="calendar_three_months">3 months</string>
<string name="calendar_six_months">6 months</string>
<string name="calendar_year">Year</string>
<!-- Strings related to Debug Activity -->
<string name="title_activity_debug">Debug</string>