mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-07-22 23:02:33 +02:00
874b510784
Add possibility to pick individual SportsItem items into filtered view Add quick time range selector add deviceFilter for devices that supportsActivityTracks add baseAltitude add average speed, calculated from from distance/active time make Activity Details layout scrollable
386 lines
17 KiB
Java
386 lines
17 KiB
Java
/* Copyright (C) 2015-2020 abettenburg, Andreas Shimokawa, Carsten Pfeiffer,
|
|
Daniele Gobbetti, Lem Dulfo
|
|
|
|
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 <http://www.gnu.org/licenses/>. */
|
|
package nodomain.freeyourgadget.gadgetbridge.activities;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.app.AlertDialog;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Typeface;
|
|
import android.os.Bundle;
|
|
import android.text.InputType;
|
|
import android.util.TypedValue;
|
|
import android.view.Gravity;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.animation.Animation;
|
|
import android.view.animation.AnimationUtils;
|
|
import android.widget.Button;
|
|
import android.widget.EditText;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.ImageView;
|
|
import android.widget.ScrollView;
|
|
import android.widget.TableLayout;
|
|
import android.widget.TableRow;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import org.json.JSONArray;
|
|
import org.json.JSONException;
|
|
import org.json.JSONObject;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.io.IOException;
|
|
import java.text.DecimalFormat;
|
|
import java.util.Date;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
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.ActivitySummaryJsonSummary;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.SwipeEvents;
|
|
|
|
//import nodomain.freeyourgadget.gadgetbridge.util.OnSwipeTouchListener;
|
|
|
|
public class ActivitySummaryDetail extends AbstractGBActivity {
|
|
private static final Logger LOG = LoggerFactory.getLogger(ActivitySummaryDetail.class);
|
|
private GBDevice gbDevice;
|
|
|
|
private boolean show_raw_data = false;
|
|
BaseActivitySummary currentItem = null;
|
|
private int alternateColor;
|
|
//private Object BottomSheetBehavior;
|
|
|
|
@SuppressLint("ClickableViewAccessibility")
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
final Context appContext = this.getApplicationContext();
|
|
if (appContext instanceof GBApplication) {
|
|
setContentView(R.layout.activity_summary_details);
|
|
}
|
|
|
|
Intent intent = getIntent();
|
|
|
|
Bundle bundle = intent.getExtras();
|
|
gbDevice = bundle.getParcelable(GBDevice.EXTRA_DEVICE);
|
|
final int position = bundle.getInt("position", 0);
|
|
final int activityFilter = bundle.getInt("activityFilter", 0);
|
|
final long dateFromFilter = bundle.getLong("dateFromFilter", 0);
|
|
final long dateToFilter = bundle.getLong("dateToFilter", 0);
|
|
final long deviceFilter = bundle.getLong("deviceFilter", 0);
|
|
final String nameContainsFilter = bundle.getString("nameContainsFilter");
|
|
final List itemsFilter = (List<Long>) bundle.getSerializable("itemsFilter");
|
|
|
|
final ActivitySummaryItems items = new ActivitySummaryItems(this, gbDevice, activityFilter, dateFromFilter, dateToFilter, nameContainsFilter, deviceFilter, itemsFilter);
|
|
final ScrollView layout = findViewById(R.id.activity_summary_detail_scroll_layout);
|
|
//final LinearLayout layout = findViewById(R.id.activity_summary_detail_relative_layout);
|
|
alternateColor = getAlternateColor(this);
|
|
|
|
final Animation animFadeRight;
|
|
final Animation animFadeLeft;
|
|
final Animation animBounceLeft;
|
|
final Animation animBounceRight;
|
|
|
|
animFadeRight = AnimationUtils.loadAnimation(
|
|
this,
|
|
R.anim.flyright);
|
|
animFadeLeft = AnimationUtils.loadAnimation(
|
|
this,
|
|
R.anim.flyleft);
|
|
animBounceLeft = AnimationUtils.loadAnimation(
|
|
this,
|
|
R.anim.bounceleft);
|
|
animBounceRight = AnimationUtils.loadAnimation(
|
|
this,
|
|
R.anim.bounceright);
|
|
|
|
layout.setOnTouchListener(new SwipeEvents(this) {
|
|
@Override
|
|
public void onSwipeRight() {
|
|
BaseActivitySummary newItem = items.getNextItem();
|
|
if (newItem != null) {
|
|
currentItem = newItem;
|
|
makeSummaryHeader(newItem);
|
|
makeSummaryContent(newItem);
|
|
layout.startAnimation(animFadeRight);
|
|
|
|
} else {
|
|
layout.startAnimation(animBounceRight);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onSwipeLeft() {
|
|
BaseActivitySummary newItem = items.getPrevItem();
|
|
if (newItem != null) {
|
|
currentItem = newItem;
|
|
makeSummaryHeader(newItem);
|
|
makeSummaryContent(newItem);
|
|
layout.startAnimation(animFadeLeft);
|
|
} else {
|
|
layout.startAnimation(animBounceLeft);
|
|
}
|
|
}
|
|
});
|
|
|
|
currentItem = items.getItem(position);
|
|
if (currentItem != null) {
|
|
makeSummaryHeader(currentItem);
|
|
makeSummaryContent(currentItem);
|
|
}
|
|
|
|
//allows long-press.switch of data being in raw form or recalculated
|
|
ImageView activity_icon = findViewById(R.id.item_image);
|
|
activity_icon.setOnLongClickListener(new View.OnLongClickListener() {
|
|
public boolean onLongClick(View v) {
|
|
show_raw_data = !show_raw_data;
|
|
if (currentItem != null) {
|
|
makeSummaryHeader(currentItem);
|
|
makeSummaryContent(currentItem);
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
ImageView activity_summary_detail_edit_name_image = findViewById(R.id.activity_summary_detail_edit_name);
|
|
activity_summary_detail_edit_name_image.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
final EditText input = new EditText(ActivitySummaryDetail.this);
|
|
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
|
String name = currentItem.getName();
|
|
input.setText((name != null) ? name : "");
|
|
FrameLayout container = new FrameLayout(ActivitySummaryDetail.this);
|
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
params.leftMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
|
params.rightMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
|
input.setLayoutParams(params);
|
|
container.addView(input);
|
|
|
|
new AlertDialog.Builder(ActivitySummaryDetail.this)
|
|
.setView(container)
|
|
.setCancelable(true)
|
|
.setTitle(ActivitySummaryDetail.this.getString(R.string.activity_summary_edit_name_title))
|
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
String name = input.getText().toString();
|
|
currentItem.setName(name);
|
|
currentItem.update();
|
|
makeSummaryHeader(currentItem);
|
|
makeSummaryContent(currentItem);
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
// do nothing
|
|
}
|
|
})
|
|
.show();
|
|
}
|
|
});
|
|
}
|
|
|
|
private void makeSummaryHeader(BaseActivitySummary item) {
|
|
//make view of data from main part of item
|
|
final String gpxTrack = item.getGpxTrack();
|
|
Button show_track_btn = findViewById(R.id.showTrack);
|
|
show_track_btn.setVisibility(View.GONE);
|
|
|
|
if (gpxTrack != null) {
|
|
show_track_btn.setVisibility(View.VISIBLE);
|
|
show_track_btn.setOnClickListener(new View.OnClickListener() {
|
|
public void onClick(View v) {
|
|
try {
|
|
AndroidUtils.viewFile(gpxTrack, Intent.ACTION_VIEW, ActivitySummaryDetail.this);
|
|
} catch (IOException e) {
|
|
GB.toast(getApplicationContext(), "Unable to display GPX track: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
String activitykindname = ActivityKind.asString(item.getActivityKind(), getApplicationContext());
|
|
String activityname = item.getName();
|
|
Date starttime = item.getStartTime();
|
|
Date endtime = item.getEndTime();
|
|
String starttimeS = DateTimeUtils.formatDateTime(starttime);
|
|
String endtimeS = DateTimeUtils.formatDateTime(endtime);
|
|
String durationhms = DateTimeUtils.formatDurationHoursMinutes((endtime.getTime() - starttime.getTime()), TimeUnit.MILLISECONDS);
|
|
|
|
ImageView activity_icon = findViewById(R.id.item_image);
|
|
activity_icon.setImageResource(ActivityKind.getIconId(item.getActivityKind()));
|
|
|
|
TextView activity_kind = findViewById(R.id.activitykind);
|
|
activity_kind.setText(activitykindname);
|
|
|
|
TextView activity_name = findViewById(R.id.activityname);
|
|
activity_name.setText(activityname);
|
|
|
|
if (activityname == null || (activityname != null && activityname.length() < 1)) {
|
|
activity_name.setVisibility(View.GONE);
|
|
} else {
|
|
activity_name.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
TextView start_time = findViewById(R.id.starttime);
|
|
start_time.setText(starttimeS);
|
|
TextView end_time = findViewById(R.id.endtime);
|
|
end_time.setText(endtimeS);
|
|
TextView activity_duration = findViewById(R.id.duration);
|
|
activity_duration.setText(durationhms);
|
|
|
|
}
|
|
|
|
private void makeSummaryContent(BaseActivitySummary item) {
|
|
//make view of data from summaryData of item
|
|
|
|
TableLayout fieldLayout = findViewById(R.id.summaryDetails);
|
|
fieldLayout.removeAllViews(); //remove old widgets
|
|
ActivitySummaryJsonSummary activitySummaryJsonSummary = new ActivitySummaryJsonSummary(item);
|
|
JSONObject data = activitySummaryJsonSummary.getSummaryGroupedList(); //get list, grouped by groups
|
|
if (data == null) return;
|
|
|
|
Iterator<String> keys = data.keys();
|
|
DecimalFormat df = new DecimalFormat("#.##");
|
|
|
|
while (keys.hasNext()) {
|
|
String key = keys.next();
|
|
try {
|
|
JSONArray innerList = (JSONArray) data.get(key);
|
|
|
|
TableRow label_row = new TableRow(ActivitySummaryDetail.this);
|
|
TextView label_field = new TextView(ActivitySummaryDetail.this);
|
|
label_field.setTextSize(16);
|
|
label_field.setPadding(0,10,0,0);
|
|
label_field.setTypeface(null, Typeface.BOLD);
|
|
label_field.setText(String.format("%s", getStringResourceByName(key)));
|
|
label_row.addView(label_field);
|
|
fieldLayout.addView(label_row);
|
|
|
|
for (int i = 0; i < innerList.length(); i++) {
|
|
TextView name_field = new TextView(ActivitySummaryDetail.this);
|
|
TextView value_field = new TextView(ActivitySummaryDetail.this);
|
|
name_field.setGravity(Gravity.START);
|
|
value_field.setGravity(Gravity.END);
|
|
|
|
JSONObject innerData = innerList.getJSONObject(i);
|
|
String unit = innerData.getString("unit");
|
|
String name = innerData.getString("name");
|
|
if (!unit.equals("string")) {
|
|
double value = innerData.getDouble("value");
|
|
|
|
if (!show_raw_data) {
|
|
//special casing here:
|
|
switch (unit) {
|
|
case "meters_second":
|
|
value = value * 3.6;
|
|
unit = "km_h";
|
|
break;
|
|
case "seconds_m":
|
|
value = value * (1000 / 60D);
|
|
unit = "minutes_km";
|
|
break;
|
|
case "seconds_km":
|
|
value = value / 60;
|
|
unit = "minutes_km";
|
|
break;
|
|
case "meters":
|
|
if (value > 2000) {
|
|
value = value / 1000;
|
|
unit = "km";
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if (unit.equals("seconds") && !show_raw_data) { //rather then plain seconds, show formatted duration
|
|
value_field.setText(DateTimeUtils.formatDurationHoursMinutes((long) value, TimeUnit.SECONDS));
|
|
} else {
|
|
value_field.setText(String.format("%s %s", df.format(value), getStringResourceByName(unit)));
|
|
}
|
|
} else {
|
|
value_field.setText(getStringResourceByName(innerData.getString("value"))); //we could optimize here a bit and only do this for particular activities (swim at the moment...)
|
|
}
|
|
|
|
TableRow field_row = new TableRow(ActivitySummaryDetail.this);
|
|
if (i % 2 == 0) field_row.setBackgroundColor(alternateColor);
|
|
|
|
name_field.setText(getStringResourceByName(name));
|
|
TableRow.LayoutParams params = new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1f);
|
|
value_field.setLayoutParams(params);
|
|
|
|
field_row.addView(name_field);
|
|
field_row.addView(value_field);
|
|
fieldLayout.addView(field_row);
|
|
}
|
|
} catch (JSONException e) {
|
|
LOG.error("SportsActivity", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public static int getAlternateColor(Context context) {
|
|
TypedValue typedValue = new TypedValue();
|
|
Resources.Theme theme = context.getTheme();
|
|
theme.resolveAttribute(R.attr.alternate_row_background, typedValue, true);
|
|
return typedValue.data;
|
|
}
|
|
|
|
private String getStringResourceByName(String aString) {
|
|
String packageName = getPackageName();
|
|
int resId = getResources().getIdentifier(aString, "string", packageName);
|
|
if (resId == 0) {
|
|
//LOG.warn("SportsActivity " + "Missing string in strings:" + aString);
|
|
return aString;
|
|
} else {
|
|
return getString(resId);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case android.R.id.home:
|
|
// back button
|
|
finish();
|
|
return true;
|
|
}
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
|
|
}
|