mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-02-02 21:17:32 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
89f9f69b0d
@ -1003,6 +1003,13 @@ public class GBApplication extends Application {
|
|||||||
return typedValue.data;
|
return typedValue.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getWindowBackgroundColor(Context context) {
|
||||||
|
TypedValue typedValue = new TypedValue();
|
||||||
|
Resources.Theme theme = context.getTheme();
|
||||||
|
theme.resolveAttribute(android.R.attr.windowBackground, typedValue, true);
|
||||||
|
return typedValue.data;
|
||||||
|
}
|
||||||
|
|
||||||
public static Prefs getPrefs() {
|
public static Prefs getPrefs() {
|
||||||
return prefs;
|
return prefs;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
|
||||||
@ -29,6 +30,10 @@ public class AboutActivity extends AbstractGBActivity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_about);
|
setContentView(R.layout.activity_about);
|
||||||
|
TextView about_version = findViewById(R.id.about_version);
|
||||||
|
String versionName = BuildConfig.VERSION_NAME;
|
||||||
|
about_version.setText(String.format(getString(R.string.about_version), versionName));
|
||||||
|
|
||||||
TextView link1 = findViewById(R.id.links1);
|
TextView link1 = findViewById(R.id.links1);
|
||||||
link1.setMovementMethod(LinkMovementMethod.getInstance());
|
link1.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
TextView link2 = findViewById(R.id.links2);
|
TextView link2 = findViewById(R.id.links2);
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti, Dikay900, Pavel Elagin
|
||||||
|
|
||||||
|
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.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GpxParser;
|
||||||
|
|
||||||
|
import static android.graphics.Bitmap.createBitmap;
|
||||||
|
|
||||||
|
|
||||||
|
public class ActivitySummariesGpsFragment extends AbstractGBFragment {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ActivitySummariesGpsFragment.class);
|
||||||
|
private ImageView gpsView;
|
||||||
|
private int CANVAS_SIZE = 360;
|
||||||
|
private File inputFile;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.fragment_gps, container, false);
|
||||||
|
gpsView = rootView.findViewById(R.id.activitygpsview);
|
||||||
|
if (inputFile != null) {
|
||||||
|
processInBackgroundThread();
|
||||||
|
}
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_data(File inputFile) {
|
||||||
|
this.inputFile = inputFile;
|
||||||
|
if (gpsView != null) { //first fragment inflate is AFTER this is called
|
||||||
|
processInBackgroundThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processInBackgroundThread() {
|
||||||
|
final Canvas canvas = createCanvas(gpsView);
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
GpxParser gpxParser = null;
|
||||||
|
FileInputStream inputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputStream = new FileInputStream(inputFile);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
gpxParser = new GpxParser(inputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpxParser != null) {
|
||||||
|
drawTrack(canvas, gpxParser.getPoints());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
private void drawTrack(Canvas canvas, List<GPSCoordinate> trackPoints) {
|
||||||
|
double maxLat = (Collections.max(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
|
||||||
|
double minLat = (Collections.min(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
|
||||||
|
double maxLon = (Collections.max(trackPoints, new GPSCoordinate.compareLongitude())).getLongitude();
|
||||||
|
double minLon = (Collections.min(trackPoints, new GPSCoordinate.compareLongitude())).getLongitude();
|
||||||
|
double maxAlt = (Collections.max(trackPoints, new GPSCoordinate.compareElevation())).getAltitude();
|
||||||
|
double minAlt = (Collections.min(trackPoints, new GPSCoordinate.compareElevation())).getAltitude();
|
||||||
|
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setStrokeWidth(1);
|
||||||
|
paint.setColor(getResources().getColor(R.color.chart_activity_light));
|
||||||
|
|
||||||
|
for (GPSCoordinate p : trackPoints) {
|
||||||
|
float lat = (float) ((p.getLatitude() - minLat) / (maxLat - minLat));
|
||||||
|
float lon = (float) ((p.getLongitude() - minLon) / (maxLon - minLon));
|
||||||
|
float alt = (float) ((p.getAltitude() - minAlt) / (maxAlt - minAlt));
|
||||||
|
paint.setStrokeWidth(1 + alt); //make thicker with higher altitude, we could do more here
|
||||||
|
canvas.drawPoint(CANVAS_SIZE * lat, CANVAS_SIZE * lon, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Canvas createCanvas(ImageView imageView) {
|
||||||
|
Bitmap bitmap = createBitmap(CANVAS_SIZE, CANVAS_SIZE, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
canvas.drawColor(GBApplication.getWindowBackgroundColor(getActivity()));
|
||||||
|
//frame around, but it doesn't look so nice
|
||||||
|
/*
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setStrokeWidth(0);
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
paint.setColor(getResources().getColor(R.color.chart_activity_light));
|
||||||
|
canvas.drawRect(0,0,360,360,paint);
|
||||||
|
*/
|
||||||
|
imageView.setImageBitmap(bitmap);
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected CharSequence getTitle() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,6 @@ import android.content.Intent;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -97,11 +96,7 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
|||||||
public static Bitmap getScreenShot(View view, int height, int width, Context context) {
|
public static Bitmap getScreenShot(View view, int height, int width, Context context) {
|
||||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
Canvas canvas = new Canvas(bitmap);
|
Canvas canvas = new Canvas(bitmap);
|
||||||
if (GBApplication.isDarkThemeEnabled()) {
|
canvas.drawColor(GBApplication.getWindowBackgroundColor(context));
|
||||||
canvas.drawColor(GBApplication.getBackgroundColor(context));
|
|
||||||
} else {
|
|
||||||
canvas.drawColor(Color.WHITE);
|
|
||||||
}
|
|
||||||
view.draw(canvas);
|
view.draw(canvas);
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
@ -153,10 +148,12 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
|||||||
R.anim.bounceright);
|
R.anim.bounceright);
|
||||||
|
|
||||||
final ActivitySummariesChartFragment activitySummariesChartFragment = new ActivitySummariesChartFragment();
|
final ActivitySummariesChartFragment activitySummariesChartFragment = new ActivitySummariesChartFragment();
|
||||||
|
final ActivitySummariesGpsFragment activitySummariesGpsFragment = new ActivitySummariesGpsFragment();
|
||||||
|
|
||||||
getSupportFragmentManager()
|
getSupportFragmentManager()
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.fragmentHolder, activitySummariesChartFragment)
|
.replace(R.id.chartsFragmentHolder, activitySummariesChartFragment)
|
||||||
|
.replace(R.id.gpsFragmentHolder, activitySummariesGpsFragment)
|
||||||
.commit();
|
.commit();
|
||||||
|
|
||||||
layout.setOnTouchListener(new SwipeEvents(this) {
|
layout.setOnTouchListener(new SwipeEvents(this) {
|
||||||
@ -168,6 +165,13 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
|||||||
makeSummaryHeader(newItem);
|
makeSummaryHeader(newItem);
|
||||||
makeSummaryContent(newItem);
|
makeSummaryContent(newItem);
|
||||||
activitySummariesChartFragment.setDateAndGetData(gbDevice, currentItem.getStartTime().getTime() / 1000, currentItem.getEndTime().getTime() / 1000);
|
activitySummariesChartFragment.setDateAndGetData(gbDevice, currentItem.getStartTime().getTime() / 1000, currentItem.getEndTime().getTime() / 1000);
|
||||||
|
if (get_gpx_file() != null) {
|
||||||
|
showCanvas();
|
||||||
|
activitySummariesGpsFragment.set_data(get_gpx_file());
|
||||||
|
} else {
|
||||||
|
hideCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
layout.startAnimation(animFadeRight);
|
layout.startAnimation(animFadeRight);
|
||||||
show_hide_gpx_menu();
|
show_hide_gpx_menu();
|
||||||
} else {
|
} else {
|
||||||
@ -183,6 +187,14 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
|||||||
makeSummaryHeader(newItem);
|
makeSummaryHeader(newItem);
|
||||||
makeSummaryContent(newItem);
|
makeSummaryContent(newItem);
|
||||||
activitySummariesChartFragment.setDateAndGetData(gbDevice, currentItem.getStartTime().getTime() / 1000, currentItem.getEndTime().getTime() / 1000);
|
activitySummariesChartFragment.setDateAndGetData(gbDevice, currentItem.getStartTime().getTime() / 1000, currentItem.getEndTime().getTime() / 1000);
|
||||||
|
if (get_gpx_file() != null) {
|
||||||
|
showCanvas();
|
||||||
|
activitySummariesGpsFragment.set_data(get_gpx_file());
|
||||||
|
} else {
|
||||||
|
hideCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
layout.startAnimation(animFadeLeft);
|
layout.startAnimation(animFadeLeft);
|
||||||
show_hide_gpx_menu();
|
show_hide_gpx_menu();
|
||||||
} else {
|
} else {
|
||||||
@ -196,6 +208,13 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
|||||||
makeSummaryHeader(currentItem);
|
makeSummaryHeader(currentItem);
|
||||||
makeSummaryContent(currentItem);
|
makeSummaryContent(currentItem);
|
||||||
activitySummariesChartFragment.setDateAndGetData(gbDevice, currentItem.getStartTime().getTime() / 1000, currentItem.getEndTime().getTime() / 1000);
|
activitySummariesChartFragment.setDateAndGetData(gbDevice, currentItem.getStartTime().getTime() / 1000, currentItem.getEndTime().getTime() / 1000);
|
||||||
|
if (get_gpx_file() != null) {
|
||||||
|
showCanvas();
|
||||||
|
activitySummariesGpsFragment.set_data(get_gpx_file());
|
||||||
|
} else {
|
||||||
|
hideCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -464,6 +483,34 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showCanvas() {
|
||||||
|
View gpsView = findViewById(R.id.gpsFragmentHolder);
|
||||||
|
ViewGroup.LayoutParams params = gpsView.getLayoutParams();
|
||||||
|
params.height = (int) (300 * getApplicationContext().getResources().getDisplayMetrics().density);
|
||||||
|
gpsView.setLayoutParams(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideCanvas() {
|
||||||
|
View gpsView = findViewById(R.id.gpsFragmentHolder);
|
||||||
|
ViewGroup.LayoutParams params = gpsView.getLayoutParams();
|
||||||
|
params.height = 0;
|
||||||
|
gpsView.setLayoutParams(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File get_gpx_file() {
|
||||||
|
final String gpxTrack = currentItem.getGpxTrack();
|
||||||
|
if (gpxTrack != null) {
|
||||||
|
File file = new File(gpxTrack);
|
||||||
|
if (file.exists()) {
|
||||||
|
return file;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
|
@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.model;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
public final class GPSCoordinate {
|
public final class GPSCoordinate {
|
||||||
private final double latitude;
|
private final double latitude;
|
||||||
@ -78,4 +79,26 @@ public final class GPSCoordinate {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "lon: " + formatLocation(longitude) + ", lat: " + formatLocation(latitude) + ", alt: " + formatLocation(altitude) + "m";
|
return "lon: " + formatLocation(longitude) + ", lat: " + formatLocation(latitude) + ", alt: " + formatLocation(altitude) + "m";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class compareLatitude implements Comparator<GPSCoordinate> {
|
||||||
|
@Override
|
||||||
|
public int compare(GPSCoordinate trkPt1, GPSCoordinate trkPt2) {
|
||||||
|
return Double.compare(trkPt1.getLatitude(), trkPt2.getLatitude());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class compareLongitude implements Comparator<GPSCoordinate> {
|
||||||
|
@Override
|
||||||
|
public int compare(GPSCoordinate trkPt1, GPSCoordinate trkPt2) {
|
||||||
|
return Double.compare(trkPt1.getLongitude(), trkPt2.getLongitude());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class compareElevation implements Comparator<GPSCoordinate> {
|
||||||
|
@Override
|
||||||
|
public int compare(GPSCoordinate trkPt1, GPSCoordinate trkPt2) {
|
||||||
|
return Double.compare(trkPt1.getAltitude(), trkPt2.getAltitude());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -72,6 +73,10 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(PineTimeJFSupport.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PineTimeJFSupport.class);
|
||||||
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
||||||
private final DeviceInfoProfile<PineTimeJFSupport> deviceInfoProfile;
|
private final DeviceInfoProfile<PineTimeJFSupport> deviceInfoProfile;
|
||||||
|
private final int MaxNotificationLength = 100;
|
||||||
|
private int firmwareVersionMajor = 0;
|
||||||
|
private int firmwareVersionMinor = 0;
|
||||||
|
private int firmwareVersionPatch = 0;
|
||||||
/**
|
/**
|
||||||
* These are used to keep track when long strings haven't changed,
|
* These are used to keep track when long strings haven't changed,
|
||||||
* thus avoiding unnecessary transfers that are (potentially) very slow.
|
* thus avoiding unnecessary transfers that are (potentially) very slow.
|
||||||
@ -227,8 +232,19 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL
|
|||||||
@Override
|
@Override
|
||||||
public void onNotification(NotificationSpec notificationSpec) {
|
public void onNotification(NotificationSpec notificationSpec) {
|
||||||
TransactionBuilder builder = new TransactionBuilder("notification");
|
TransactionBuilder builder = new TransactionBuilder("notification");
|
||||||
NewAlert alert = new NewAlert(AlertCategory.CustomHuami, 1, notificationSpec.body + " "); // HACK: no idea why the last byte is swallowed
|
String message = notificationSpec.body;
|
||||||
|
if(!IsFirmwareAtLeastVersion0_9()) {
|
||||||
|
// Firmware versions prior to 0.9 ignore the last characters of the notification message
|
||||||
|
// Add an space character so that the whole message will be displayed
|
||||||
|
message += " ";
|
||||||
|
}
|
||||||
|
NewAlert alert = new NewAlert(AlertCategory.CustomHuami, 1, message);
|
||||||
AlertNotificationProfile<?> profile = new AlertNotificationProfile<>(this);
|
AlertNotificationProfile<?> profile = new AlertNotificationProfile<>(this);
|
||||||
|
if(IsFirmwareAtLeastVersion0_9()) {
|
||||||
|
// InfiniTime 0.9+ support notification message of up to 100 characters
|
||||||
|
// Instead of 18 by default
|
||||||
|
profile.setMaxLength(MaxNotificationLength);
|
||||||
|
}
|
||||||
profile.newAlert(builder, alert, OverflowStrategy.TRUNCATE);
|
profile.newAlert(builder, alert, OverflowStrategy.TRUNCATE);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
}
|
}
|
||||||
@ -573,9 +589,24 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL
|
|||||||
LOG.warn("Device info: " + info);
|
LOG.warn("Device info: " + info);
|
||||||
versionCmd.hwVersion = info.getHardwareRevision();
|
versionCmd.hwVersion = info.getHardwareRevision();
|
||||||
versionCmd.fwVersion = info.getFirmwareRevision();
|
versionCmd.fwVersion = info.getFirmwareRevision();
|
||||||
|
|
||||||
|
if(versionCmd.fwVersion != null && !versionCmd.fwVersion.isEmpty()) {
|
||||||
|
// FW version format : "major.minor.patch". Ex : "0.8.2"
|
||||||
|
String[] tokens = StringUtils.split(versionCmd.fwVersion, ".");
|
||||||
|
if(tokens.length == 3) {
|
||||||
|
firmwareVersionMajor = Integer.parseInt(tokens[0]);
|
||||||
|
firmwareVersionMinor = Integer.parseInt(tokens[1]);
|
||||||
|
firmwareVersionPatch = Integer.parseInt(tokens[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleGBDeviceEvent(versionCmd);
|
handleGBDeviceEvent(versionCmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean IsFirmwareAtLeastVersion0_9() {
|
||||||
|
return firmwareVersionMajor > 0 || firmwareVersionMinor >= 9;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nordic DFU needs this function to log DFU-related messages
|
* Nordic DFU needs this function to log DFU-related messages
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
|
||||||
|
|
||||||
|
public class GpxParser {
|
||||||
|
private XmlPullParser parser;
|
||||||
|
private List<GPSCoordinate> points;
|
||||||
|
private int eventType;
|
||||||
|
|
||||||
|
public GpxParser(InputStream stream) {
|
||||||
|
points = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
parser = createXmlParser(stream);
|
||||||
|
parseGpx();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static XmlPullParser createXmlParser(InputStream stream) throws XmlPullParserException {
|
||||||
|
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||||
|
factory.setNamespaceAware(true);
|
||||||
|
XmlPullParser parser = factory.newPullParser();
|
||||||
|
parser.setInput(stream, null);
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseGpx() throws XmlPullParserException, IOException {
|
||||||
|
eventType = parser.getEventType();
|
||||||
|
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (eventType == XmlPullParser.START_TAG && parser.getName().equals("trkpt"))
|
||||||
|
points.add(parsePoint(parser));
|
||||||
|
else
|
||||||
|
eventType = parser.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double parseElevation(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
String eleString = "";
|
||||||
|
while (eventType != XmlPullParser.END_TAG) {
|
||||||
|
if (eventType == XmlPullParser.TEXT) {
|
||||||
|
eleString = parser.getText();
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
}
|
||||||
|
return Double.parseDouble(eleString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GPSCoordinate parsePoint(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
double lat;
|
||||||
|
double lon;
|
||||||
|
double ele = 0;
|
||||||
|
String latString = parser.getAttributeValue(null, "lat");
|
||||||
|
String lonString = parser.getAttributeValue(null, "lon");
|
||||||
|
lat = latString != null ? Double.parseDouble(latString) : 0;
|
||||||
|
lon = lonString != null ? Double.parseDouble(lonString) : 0;
|
||||||
|
while (eventType != XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getEventType() == XmlPullParser.START_TAG && parser.getName().equals("ele")) {
|
||||||
|
ele = parseElevation(parser);
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
}
|
||||||
|
return new GPSCoordinate(lat, lon, ele);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GPSCoordinate> getPoints() {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
}
|
@ -40,6 +40,9 @@ public class SwipeEvents implements View.OnTouchListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||||
|
if (e1 == null || e2 == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
float distanceX = e2.getX() - e1.getX();
|
float distanceX = e2.getX() - e1.getX();
|
||||||
float distanceY = e2.getY() - e1.getY();
|
float distanceY = e2.getY() - e1.getY();
|
||||||
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
|
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:alpha="0.1"
|
android:alpha="0.1"
|
||||||
android:contentDescription="@string/icon_placeholder"
|
android:contentDescription="@string/icon_placeholder"
|
||||||
android:tint="?attr/textColorPrimary"
|
app:tint="?attr/textColorPrimary"
|
||||||
app:srcCompat="@drawable/gadgetbridge_img" />
|
app:srcCompat="@drawable/gadgetbridge_img" />
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
@ -48,6 +48,11 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/about_description" />
|
android:text="@string/about_description" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/about_version"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/activity_summary_detail_scroll_layout"
|
android:id="@+id/activity_summary_detail_scroll_layout"
|
||||||
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
android:scrollbars="vertical">
|
android:scrollbars="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@ -141,10 +142,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:gravity="end" />
|
android:gravity="end" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
</TableLayout>
|
</TableLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -166,13 +164,14 @@
|
|||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/fragmentHolder"
|
android:id="@+id/chartsFragmentHolder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="300dp">
|
android:layout_height="300dp"></FrameLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/gpsFragmentHolder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="300dp"></FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
28
app/src/main/res/layout/fragment_gps.xml
Normal file
28
app/src/main/res/layout/fragment_gps.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity$PlaceholderFragment">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView4"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/gps_track"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/activitygpsview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -930,6 +930,7 @@
|
|||||||
<string name="codeberg_url" translatable="false">Code: <a href="https://codeberg.org/Freeyourgadget/Gadgetbridge">https://codeberg.org/Freeyourgadget/Gadgetbridge</a></string>
|
<string name="codeberg_url" translatable="false">Code: <a href="https://codeberg.org/Freeyourgadget/Gadgetbridge">https://codeberg.org/Freeyourgadget/Gadgetbridge</a></string>
|
||||||
<string name="fdroid_url" translatable="false">F-Droid: <a href="https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/">https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/</a></string>
|
<string name="fdroid_url" translatable="false">F-Droid: <a href="https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/">https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/</a></string>
|
||||||
<string name="about_title">About</string>
|
<string name="about_title">About</string>
|
||||||
|
<string name="about_version">Version %s</string>
|
||||||
<string name="about_activity_title">About Gadgetbridge</string>
|
<string name="about_activity_title">About Gadgetbridge</string>
|
||||||
<string name="about_description">Cloudless copylefted libre replacement for closed source Android gadget apps from vendors.</string>
|
<string name="about_description">Cloudless copylefted libre replacement for closed source Android gadget apps from vendors.</string>
|
||||||
<string name="about_core_team_title">Core Team (in order of first code contribution)</string>
|
<string name="about_core_team_title">Core Team (in order of first code contribution)</string>
|
||||||
@ -1022,6 +1023,7 @@
|
|||||||
<string name="activity_detail_end_label">End</string>
|
<string name="activity_detail_end_label">End</string>
|
||||||
<string name="activity_detail_duration_label">Duration</string>
|
<string name="activity_detail_duration_label">Duration</string>
|
||||||
<string name="activity_detail_show_gps_label">Show GPS Track</string>
|
<string name="activity_detail_show_gps_label">Show GPS Track</string>
|
||||||
|
<string name="gps_track">GPS track</string>
|
||||||
<!-- Device Actions Preferences -->
|
<!-- Device Actions Preferences -->
|
||||||
<string name="prefs_events_forwarding_summary">Use device events to trigger actions and Android broadcasts</string>
|
<string name="prefs_events_forwarding_summary">Use device events to trigger actions and Android broadcasts</string>
|
||||||
<string name="prefs_events_forwarding_title">Device actions</string>
|
<string name="prefs_events_forwarding_title">Device actions</string>
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.test;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GpxParser;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.anyOf;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
public class GPXParserTest extends TestBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldReadGPXCorrectly() throws IOException {
|
||||||
|
try (final InputStream inputStream = getClass().getResourceAsStream("/gpx-exporter-test-SampleTrack.gpx")) {
|
||||||
|
GpxParser gpxParser = new GpxParser(inputStream);
|
||||||
|
List<GPSCoordinate> trackPoints = gpxParser.getPoints();
|
||||||
|
Assert.assertEquals(trackPoints.size(), 14);
|
||||||
|
DecimalFormat df = new DecimalFormat("###.##");
|
||||||
|
for (GPSCoordinate tp : trackPoints) {
|
||||||
|
Assert.assertEquals(df.format(tp.getLongitude()), "44.15");
|
||||||
|
Assert.assertEquals(df.format(tp.getLatitude()), "-68.2");
|
||||||
|
Assert.assertThat(df.format(tp.getAltitude()), anyOf(is("40"), is("46")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
app/src/test/resources/gpx-exporter-test-SampleTrack.gpx
Normal file
70
app/src/test/resources/gpx-exporter-test-SampleTrack.gpx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="Gadgetbridge Test" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
|
||||||
|
<metadata>
|
||||||
|
<name>Test Track</name>
|
||||||
|
<author>
|
||||||
|
<name>Test User</name>
|
||||||
|
</author>
|
||||||
|
<time>2020-10-25T14:44:45+01:00</time>
|
||||||
|
</metadata>
|
||||||
|
<trk>
|
||||||
|
<trkseg>
|
||||||
|
<trkpt lon="-68.200293" lat="44.152462">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:00Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200270" lat="44.152460">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:01Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200260" lat="44.152462">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:02Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200242" lat="44.152493">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:03Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200237" lat="44.152528">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:04Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200232" lat="44.152567">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:05Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200248" lat="44.152612">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:06Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200253" lat="44.152657">
|
||||||
|
<ele>40.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:07Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200245" lat="44.152675">
|
||||||
|
<ele>46.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:08Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200232" lat="44.152695">
|
||||||
|
<ele>46.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:09Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200215" lat="44.152720">
|
||||||
|
<ele>46.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:10Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200205" lat="44.152753">
|
||||||
|
<ele>46.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:11Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200197" lat="44.152808">
|
||||||
|
<ele>46.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:12Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lon="-68.200203" lat="44.152877">
|
||||||
|
<ele>46.000000</ele>
|
||||||
|
<time>2019-01-01T00:00:13Z</time>
|
||||||
|
</trkpt>
|
||||||
|
</trkseg>
|
||||||
|
</trk>
|
||||||
|
</gpx>
|
Loading…
x
Reference in New Issue
Block a user