mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-01-24 00:27:33 +01:00
Add Activity Summary Statistics Drawer
Move SummaryStatistics JSON Summary to own place
This commit is contained in:
parent
ebc70b3fd5
commit
b236a6af16
@ -17,7 +17,6 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DatePickerDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@ -31,12 +30,14 @@ import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
@ -45,26 +46,33 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.ActivitySummariesAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryJsonSummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
|
||||
@ -76,6 +84,7 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
|
||||
int activityFilter=0;
|
||||
long dateFromFilter=0;
|
||||
long dateToFilter=0;
|
||||
boolean offscreen = true;
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@ -112,11 +121,20 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
boolean processed = false;
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
// back button, close drawer if open, otherwise exit
|
||||
if (!offscreen){
|
||||
processSummaryStatistics();
|
||||
}else{
|
||||
finish();
|
||||
}
|
||||
return true;
|
||||
case R.id.activity_action_manage_timestamp:
|
||||
resetFetchTimestampToChosenDate();
|
||||
processed = true;
|
||||
break;
|
||||
case R.id.activity_action_filter:
|
||||
if (!offscreen) processSummaryStatistics(); //hide drawer with stats if shown
|
||||
Intent filterIntent = new Intent(this, ActivitySummariesFilter.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putSerializable("activityKindMap",activityKindMap);
|
||||
@ -126,10 +144,83 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
|
||||
filterIntent.putExtras(bundle);
|
||||
startActivityForResult(filterIntent,1);
|
||||
return true;
|
||||
case R.id.activity_action_calculate_summary_stats:
|
||||
processSummaryStatistics();
|
||||
return true;
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
private void processSummaryStatistics() {
|
||||
View hiddenLayout = findViewById(R.id.activity_summary_statistics_relative_layout);
|
||||
hiddenLayout.setVisibility(View.VISIBLE);
|
||||
//hide or show drawer
|
||||
int yOffset = (offscreen) ? 0 : -1 * hiddenLayout.getHeight();
|
||||
LinearLayout.LayoutParams rlParams =
|
||||
(LinearLayout.LayoutParams) hiddenLayout.getLayoutParams();
|
||||
rlParams.setMargins(0, yOffset, 0, 0);
|
||||
hiddenLayout.setLayoutParams(rlParams);
|
||||
|
||||
Animation animFadeDown;
|
||||
animFadeDown = AnimationUtils.loadAnimation(
|
||||
this,
|
||||
R.anim.slidefromtop);
|
||||
setTitle(R.string.activity_summaries);
|
||||
if (offscreen) {
|
||||
setTitle(R.string.activity_summaries_statistics);
|
||||
hiddenLayout.startAnimation(animFadeDown);
|
||||
double durationSum = 0;
|
||||
double caloriesBurntSum = 0;
|
||||
double distanceSum = 0;
|
||||
double activeSecondsSum = 0;
|
||||
double firstItemDate = 0;
|
||||
double lastItemDate = 0;
|
||||
|
||||
TextView durationSumView = findViewById(R.id.activity_stats_duration_sum_value);
|
||||
TextView caloriesBurntSumView = findViewById(R.id.activity_stats_calories_burnt_sum_value);
|
||||
TextView distanceSumView = findViewById(R.id.activity_stats_distance_sum_value);
|
||||
TextView activeSecondsSumView = findViewById(R.id.activity_stats_activeSeconds_sum_value);
|
||||
TextView timeStartView = findViewById(R.id.activity_stats_timeFrom_value);
|
||||
TextView timeEndView = findViewById(R.id.activity_stats_timeTo_value);
|
||||
|
||||
for (BaseActivitySummary sportitem : getItemAdapter().getItems()) {
|
||||
|
||||
if (firstItemDate == 0) firstItemDate = sportitem.getStartTime().getTime();
|
||||
lastItemDate = sportitem.getEndTime().getTime();
|
||||
durationSum += sportitem.getEndTime().getTime() - sportitem.getStartTime().getTime();
|
||||
|
||||
ActivitySummaryJsonSummary activitySummaryJsonSummary = new ActivitySummaryJsonSummary(sportitem);
|
||||
JSONObject summarySubdata = activitySummaryJsonSummary.getSummaryData();
|
||||
|
||||
if (summarySubdata != null) {
|
||||
try {
|
||||
if (summarySubdata.has("caloriesBurnt")) {
|
||||
caloriesBurntSum += summarySubdata.getJSONObject("caloriesBurnt").getDouble("value");
|
||||
}
|
||||
if (summarySubdata.has("distanceMeters")) {
|
||||
distanceSum += summarySubdata.getJSONObject("distanceMeters").getDouble("value");
|
||||
}
|
||||
if (summarySubdata.has("activeSeconds")) {
|
||||
activeSecondsSum += summarySubdata.getJSONObject("activeSeconds").getDouble("value");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
durationSumView.setText(String.format("%s", DateTimeUtils.formatDurationHoursMinutes((long) durationSum, TimeUnit.MILLISECONDS)));
|
||||
caloriesBurntSumView.setText(String.format("%s %s", (long) caloriesBurntSum, getString(R.string.calories_unit)));
|
||||
distanceSumView.setText(String.format("%s %s", df.format(distanceSum / 1000), getString(R.string.km)));
|
||||
activeSecondsSumView.setText(String.format("%s", DateTimeUtils.formatDurationHoursMinutes((long) activeSecondsSum, TimeUnit.SECONDS)));
|
||||
|
||||
//start and end are inverted when filer not applied, because items are sorted the other way
|
||||
timeStartView.setText((dateFromFilter != 0) ? DateTimeUtils.formatDate(new Date(dateFromFilter)) : DateTimeUtils.formatDate(new Date((long) lastItemDate)));
|
||||
timeEndView.setText((dateToFilter != 0) ? DateTimeUtils.formatDate(new Date(dateToFilter)) : DateTimeUtils.formatDate(new Date((long) firstItemDate)));
|
||||
}
|
||||
offscreen = !offscreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData);
|
||||
@ -177,6 +268,9 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
|
||||
});
|
||||
|
||||
getItemListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
//hide top drawer on init
|
||||
View hiddenLayout = findViewById(R.id.activity_summary_statistics_relative_layout);
|
||||
hiddenLayout.setVisibility(View.GONE);
|
||||
|
||||
getItemListView().setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
||||
@Override
|
||||
|
@ -37,8 +37,6 @@ import android.widget.TableRow;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@ -52,12 +50,11 @@ import java.util.Iterator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryItems;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryJsonSummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
@ -65,7 +62,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.SwipeEvents;
|
||||
|
||||
public class ActivitySummaryDetail extends AbstractGBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ActivitySummaryDetail.class);
|
||||
private JSONObject groupData = setGroups();
|
||||
|
||||
|
||||
private boolean show_raw_data = false;
|
||||
BaseActivitySummary currentItem = null;
|
||||
private int alternateColor;
|
||||
@ -197,28 +195,10 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
||||
|
||||
TableLayout fieldLayout = findViewById(R.id.summaryDetails);
|
||||
fieldLayout.removeAllViews(); //remove old widgets
|
||||
|
||||
JSONObject summarySubdata = null;
|
||||
JSONObject data;
|
||||
|
||||
// Parse on the fly if we have binary data available
|
||||
if (item.getRawSummaryData() != null) {
|
||||
ActivitySummaryParser parser = new HuamiActivitySummaryParser(); // FIXME: if something else than huami supports that make sure to have the right parser
|
||||
item = parser.parseBinaryData(item);
|
||||
}
|
||||
|
||||
String sumData = item.getSummaryData();
|
||||
|
||||
if (sumData != null) {
|
||||
try {
|
||||
summarySubdata = new JSONObject(sumData);
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (summarySubdata == null) return;
|
||||
data = makeSummaryList(summarySubdata); //make new list, grouped by groups
|
||||
ActivitySummaryJsonSummary activitySummaryJsonSummary = new ActivitySummaryJsonSummary(item);
|
||||
//JSONObject summarySubdata = activitySummaryJsonSummary.getSummaryData();
|
||||
JSONObject data = activitySummaryJsonSummary.getSummaryGroupedList(); //get list, grouped by groups
|
||||
if (data == null) return;
|
||||
|
||||
Iterator<String> keys = data.keys();
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
@ -294,76 +274,9 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject setGroups(){
|
||||
String groupDefinitions = "{'Strokes':['averageStrokeDistance','averageStrokesPerSecond','strokes'], " +
|
||||
"'Swimming':['swolfIndex','swimStyle'], " +
|
||||
"'Elevation':['ascentMeters','descentMeters','maxAltitude','minAltitude','ascentSeconds','descentSeconds','flatSeconds'], " +
|
||||
"'Speed':['maxSpeed','minPace','maxPace','averageKMPaceSeconds'], " +
|
||||
"'Activity':['distanceMeters','steps','activeSeconds','caloriesBurnt','totalStride'," +
|
||||
"'averageHR','averageStride'], " +
|
||||
"'Laps':['averageLapPace','laps']}";
|
||||
JSONObject data = null;
|
||||
try {
|
||||
data = new JSONObject(groupDefinitions);
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private String getGroup(String searchItem) {
|
||||
String defaultGroup = "Activity";
|
||||
if (groupData == null) return defaultGroup;
|
||||
Iterator<String> keys = groupData.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
try {
|
||||
JSONArray itemList = (JSONArray) groupData.get(key);
|
||||
for (int i = 0; i < itemList.length(); i++) {
|
||||
if (itemList.getString(i).equals(searchItem)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
}
|
||||
return defaultGroup;
|
||||
}
|
||||
|
||||
private JSONObject makeSummaryList(JSONObject summaryData){
|
||||
//make dictionary with data for each group
|
||||
JSONObject list = new JSONObject();
|
||||
Iterator<String> keys = summaryData.keys();
|
||||
LOG.error("SportsActivity JSON:" + summaryData + keys);
|
||||
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
|
||||
try {
|
||||
LOG.error("SportsActivity:" + key + ": " + summaryData.get(key) + "\n");
|
||||
JSONObject innerData = (JSONObject) summaryData.get(key);
|
||||
Object value = innerData.get("value");
|
||||
String unit = innerData.getString("unit");
|
||||
String group = getGroup(key);
|
||||
|
||||
if (!list.has(group)) {
|
||||
list.put(group,new JSONArray());
|
||||
}
|
||||
|
||||
JSONArray tmpl = (JSONArray) list.get(group);
|
||||
JSONObject innernew = new JSONObject();
|
||||
innernew.put("name", key);
|
||||
innernew.put("value", value);
|
||||
innernew.put("unit", unit);
|
||||
tmpl.put(innernew);
|
||||
list.put(group, tmpl);
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
public static int getAlternateColor(Context context) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
|
@ -21,6 +21,9 @@ import org.json.JSONObject;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
|
||||
/**
|
||||
* Summarized information about a temporal activity.
|
||||
*
|
||||
|
@ -0,0 +1,131 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
|
||||
public class ActivitySummaryJsonSummary {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ActivitySummaryJsonSummary.class);
|
||||
private JSONObject groupData;
|
||||
private JSONObject summaryData;
|
||||
private JSONObject summaryGroupedList;
|
||||
private BaseActivitySummary baseActivitySummary;
|
||||
|
||||
public ActivitySummaryJsonSummary(BaseActivitySummary baseActivitySummary){
|
||||
this.baseActivitySummary=baseActivitySummary;
|
||||
}
|
||||
|
||||
private JSONObject setSummaryData(BaseActivitySummary item){
|
||||
String summary = getCorrectSummary(item);
|
||||
JSONObject jsonSummary = getJSONSummary(summary);
|
||||
return jsonSummary;
|
||||
}
|
||||
|
||||
public JSONObject getSummaryData(){
|
||||
//returns json with summaryData
|
||||
if (summaryData==null) summaryData=setSummaryData(baseActivitySummary);
|
||||
return summaryData;
|
||||
}
|
||||
|
||||
private String getCorrectSummary(BaseActivitySummary item){
|
||||
if (item.getRawSummaryData() != null) {
|
||||
ActivitySummaryParser parser = new HuamiActivitySummaryParser(); // FIXME: if something else than huami supports that make sure to have the right parser
|
||||
item = parser.parseBinaryData(item);
|
||||
}
|
||||
return item.getSummaryData();
|
||||
}
|
||||
|
||||
private JSONObject getJSONSummary(String sumData){
|
||||
JSONObject summarySubdata = null;
|
||||
if (sumData != null) {
|
||||
try {
|
||||
summarySubdata = new JSONObject(sumData);
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
}
|
||||
return summarySubdata;
|
||||
}
|
||||
|
||||
public JSONObject getSummaryGroupedList() {
|
||||
//returns list grouped by activity groups as per createActivitySummaryGroups
|
||||
if (summaryData==null) summaryData=setSummaryData(baseActivitySummary);
|
||||
if (summaryGroupedList==null) summaryGroupedList=setSummaryGroupedList(summaryData);
|
||||
return summaryGroupedList;
|
||||
}
|
||||
private JSONObject setSummaryGroupedList(JSONObject summaryDatalist){
|
||||
this.groupData = createActivitySummaryGroups(); //structure for grouping activities into groups, when vizualizing
|
||||
|
||||
if (summaryDatalist ==null ) return null;
|
||||
Iterator<String> keys = summaryDatalist.keys();
|
||||
JSONObject list=new JSONObject();
|
||||
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
try {
|
||||
JSONObject innerData = (JSONObject) summaryDatalist.get(key);
|
||||
Object value = innerData.get("value");
|
||||
String unit = innerData.getString("unit");
|
||||
String group = getGroup(key);
|
||||
|
||||
if (!list.has(group)) {
|
||||
list.put(group,new JSONArray());
|
||||
}
|
||||
|
||||
JSONArray tmpl = (JSONArray) list.get(group);
|
||||
JSONObject innernew = new JSONObject();
|
||||
innernew.put("name", key);
|
||||
innernew.put("value", value);
|
||||
innernew.put("unit", unit);
|
||||
tmpl.put(innernew);
|
||||
list.put(group, tmpl);
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private String getGroup(String searchItem) {
|
||||
String defaultGroup = "Activity";
|
||||
if (groupData == null) return defaultGroup;
|
||||
Iterator<String> keys = groupData.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
try {
|
||||
JSONArray itemList = (JSONArray) groupData.get(key);
|
||||
for (int i = 0; i < itemList.length(); i++) {
|
||||
if (itemList.getString(i).equals(searchItem)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
}
|
||||
return defaultGroup;
|
||||
}
|
||||
private JSONObject createActivitySummaryGroups(){
|
||||
String groupDefinitions = "{'Strokes':['averageStrokeDistance','averageStrokesPerSecond','strokes'], " +
|
||||
"'Swimming':['swolfIndex','swimStyle'], " +
|
||||
"'Elevation':['ascentMeters','descentMeters','maxAltitude','minAltitude','ascentSeconds','descentSeconds','flatSeconds'], " +
|
||||
"'Speed':['maxSpeed','minPace','maxPace','averageKMPaceSeconds'], " +
|
||||
"'Activity':['distanceMeters','steps','activeSeconds','caloriesBurnt','totalStride'," +
|
||||
"'averageHR','averageStride'], " +
|
||||
"'Laps':['averageLapPace','laps']}";
|
||||
JSONObject data = null;
|
||||
try {
|
||||
data = new JSONObject(groupDefinitions);
|
||||
} catch (JSONException e) {
|
||||
LOG.error("SportsActivity", e);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
11
app/src/main/res/anim/slidefromtop.xml
Normal file
11
app/src/main/res/anim/slidefromtop.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
|
||||
<translate
|
||||
android:fromXDelta="0%" android:toXDelta="0%"
|
||||
android:fromYDelta="-200%" android:toYDelta="0%"
|
||||
android:duration="100" />
|
||||
|
||||
</set>
|
10
app/src/main/res/drawable/ic_outline_filter_9_plus.xml
Normal file
10
app/src/main/res/drawable/ic_outline_filter_9_plus.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<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="M3,5L1,5v16c0,1.1 0.9,2 2,2h16v-2L3,21L3,5zM14,12L14,8c0,-1.11 -0.9,-2 -2,-2h-1c-1.1,0 -2,0.89 -2,2v1c0,1.11 0.9,2 2,2h1v1L9,12v2h3c1.1,0 2,-0.89 2,-2zM11,9L11,8h1v1h-1zM21,1L7,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L23,3c0,-1.1 -0.9,-2 -2,-2zM21,9h-2L19,7h-2v2h-2v2h2v2h2v-2h2v6L7,17L7,3h14v6z"/>
|
||||
</vector>
|
@ -17,6 +17,7 @@
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<include layout="@layout/activity_summary_statistics"/>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/list_activity_swipe_layout"
|
||||
|
202
app/src/main/res/layout/activity_summary_statistics.xml
Normal file
202
app/src/main/res/layout/activity_summary_statistics.xml
Normal file
@ -0,0 +1,202 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_summary_statistics_relative_layout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/activatedBackgroundIndicator"
|
||||
android:clickable="false"
|
||||
android:focusable="auto"
|
||||
android:padding="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_timeFrom_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="@string/activity_filter_date_from"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_timeFrom_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_timeTo_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="@string/activity_filter_date_to"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_timeTo_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_duration_sum_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="@string/activity_detail_duration_label"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_duration_sum_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_distance_sum_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="@string/Distance"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_distance_sum_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_activeSeconds_sum_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="@string/activeSeconds"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_activeSeconds_sum_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="18sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_calories_burnt_sum_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="@string/caloriesBurnt"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_stats_calories_burnt_sum_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight=".5"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@android:color/darker_gray" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
@ -13,5 +13,11 @@
|
||||
android:title="@string/pref_header_filter"
|
||||
app:iconTint="@color/primarytext_dark"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/activity_action_calculate_summary_stats"
|
||||
android:icon="@drawable/ic_outline_filter_9_plus"
|
||||
android:title="@string/pref_header_filter"
|
||||
app:iconTint="@color/primarytext_dark"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
@ -952,6 +952,7 @@
|
||||
<string name="seconds_m">sec/m</string>
|
||||
<string name="minutes_km">min/km</string>
|
||||
<string name="bpm">bpm</string>
|
||||
<string name="km">km</string>
|
||||
<!-- activity summary groups-->
|
||||
<string name="Strokes">Strokes</string>
|
||||
<string name="Swimming">Swimming</string>
|
||||
@ -961,9 +962,9 @@
|
||||
<string name="Activity">Activity</string>
|
||||
<string name="Steps">Steps</string>
|
||||
<!-- Sports Activity Detail -->
|
||||
<string name="activity_detail_start_label">Start:</string>
|
||||
<string name="activity_detail_end_label">End:</string>
|
||||
<string name="activity_detail_duration_label">Duration:</string>
|
||||
<string name="activity_detail_start_label">Start</string>
|
||||
<string name="activity_detail_end_label">End</string>
|
||||
<string name="activity_detail_duration_label">Duration</string>
|
||||
<string name="activity_detail_show_gps_label">Show GPS Track</string>
|
||||
<!-- Device Actions Preferences -->
|
||||
<string name="prefs_events_forwarding_summary">Use device events to trigger actions and Android broadcasts</string>
|
||||
@ -977,11 +978,12 @@
|
||||
<string name="prefs_events_forwarding_broadcast_title">Broadcast message</string>
|
||||
<string name="prefs_events_forwarding_action_title">Run action</string>
|
||||
<string name="pref_header_filter">Sports Activities Filter</string>
|
||||
<string name="activity_filter_date_from">From:</string>
|
||||
<string name="activity_filter_date_to">To:</string>
|
||||
<string name="activity_filter_date_from">From</string>
|
||||
<string name="activity_filter_date_to">To</string>
|
||||
<string name="activity_filter_reset_filter">Reset Filter</string>
|
||||
<string name="activity_filter_filter_title">Filter</string>
|
||||
<string name="activity_filter_apply_filter">Apply Filter</string>
|
||||
<string name="activity_summaries_statistics">Statistics</string>
|
||||
|
||||
|
||||
<plurals name="widget_alarm_target_hours">
|
||||
|
Loading…
x
Reference in New Issue
Block a user