2024-01-10 18:54:00 +01:00
/ * Copyright ( C ) 2020 - 2024 Andreas Shimokawa , Arjan Schrijver , Daniel Dakhno ,
José Rebelo , Petr Vaněk
2020-08-07 09:21:14 +02:00
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
2024-01-10 18:54:00 +01:00
along with this program . If not , see < https : //www.gnu.org/licenses/>. */
2020-08-07 09:21:14 +02:00
package nodomain.freeyourgadget.gadgetbridge.activities ;
2024-01-06 14:54:06 +01:00
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.* ;
2020-08-14 22:48:03 +02:00
import android.annotation.SuppressLint ;
2020-10-11 18:05:49 +02:00
import android.content.ActivityNotFoundException ;
2020-08-16 20:45:03 +02:00
import android.content.Context ;
2020-08-24 00:09:10 +02:00
import android.content.DialogInterface ;
2020-08-07 09:21:14 +02:00
import android.content.Intent ;
2020-08-16 20:45:03 +02:00
import android.content.res.Resources ;
2020-10-11 18:05:49 +02:00
import android.graphics.Bitmap ;
import android.graphics.Canvas ;
2020-08-10 10:56:54 +02:00
import android.graphics.Typeface ;
2020-10-11 18:05:49 +02:00
import android.net.Uri ;
2020-08-07 09:21:14 +02:00
import android.os.Bundle ;
2020-08-24 00:09:10 +02:00
import android.text.InputType ;
2020-08-16 20:45:03 +02:00
import android.util.TypedValue ;
2020-08-10 10:56:54 +02:00
import android.view.Gravity ;
2020-10-11 18:05:49 +02:00
import android.view.Menu ;
2020-08-15 14:45:43 +02:00
import android.view.MenuItem ;
2020-08-07 09:21:14 +02:00
import android.view.View ;
2020-08-24 13:50:10 +02:00
import android.view.ViewGroup ;
2020-08-14 22:48:03 +02:00
import android.view.animation.Animation ;
import android.view.animation.AnimationUtils ;
2021-07-08 22:36:43 +02:00
import android.widget.ArrayAdapter ;
2020-08-24 00:09:10 +02:00
import android.widget.EditText ;
2020-08-24 13:50:10 +02:00
import android.widget.FrameLayout ;
2020-08-07 09:21:14 +02:00
import android.widget.ImageView ;
2020-08-31 23:22:17 +02:00
import android.widget.ScrollView ;
2020-08-10 10:56:54 +02:00
import android.widget.TableLayout ;
import android.widget.TableRow ;
2020-08-07 09:21:14 +02:00
import android.widget.TextView ;
import android.widget.Toast ;
2023-07-22 21:23:27 +02:00
import androidx.appcompat.app.AlertDialog ;
2020-10-11 18:05:49 +02:00
import androidx.core.content.FileProvider ;
2023-07-22 21:23:27 +02:00
import com.google.android.material.dialog.MaterialAlertDialogBuilder ;
2020-08-09 13:11:10 +02:00
import org.json.JSONArray ;
2020-08-08 16:22:55 +02:00
import org.json.JSONException ;
import org.json.JSONObject ;
2020-08-07 09:21:14 +02:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2020-10-11 18:05:49 +02:00
import java.io.File ;
2021-07-08 22:36:43 +02:00
import java.io.FileFilter ;
2020-10-11 18:05:49 +02:00
import java.io.FileOutputStream ;
2020-08-07 09:21:14 +02:00
import java.io.IOException ;
2020-08-08 16:22:55 +02:00
import java.text.DecimalFormat ;
2021-07-08 22:36:43 +02:00
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Comparator ;
2020-08-09 13:11:10 +02:00
import java.util.Date ;
2020-08-08 16:22:55 +02:00
import java.util.Iterator ;
2020-08-31 23:22:17 +02:00
import java.util.List ;
2020-08-07 09:21:14 +02:00
import java.util.concurrent.TimeUnit ;
2020-08-24 00:09:10 +02:00
import nodomain.freeyourgadget.gadgetbridge.GBApplication ;
2020-08-07 09:21:14 +02:00
import nodomain.freeyourgadget.gadgetbridge.R ;
2020-11-26 21:08:03 +01:00
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler ;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper ;
2022-09-18 13:19:23 +02:00
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator ;
2020-08-14 22:48:03 +02:00
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary ;
2020-11-26 21:08:03 +01:00
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession ;
import nodomain.freeyourgadget.gadgetbridge.entities.Device ;
2020-08-07 09:21:14 +02:00
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice ;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind ;
2020-08-14 22:48:03 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryItems ;
2020-08-20 09:03:26 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryJsonSummary ;
2022-09-18 13:19:23 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser ;
2020-08-07 09:21:14 +02:00
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils ;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils ;
2020-10-11 18:05:49 +02:00
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils ;
2020-08-07 09:21:14 +02:00
import nodomain.freeyourgadget.gadgetbridge.util.GB ;
2020-08-14 22:48:03 +02:00
import nodomain.freeyourgadget.gadgetbridge.util.SwipeEvents ;
2020-08-31 23:22:17 +02:00
2020-08-07 09:21:14 +02:00
public class ActivitySummaryDetail extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory . getLogger ( ActivitySummaryDetail . class ) ;
2020-10-11 18:05:49 +02:00
BaseActivitySummary currentItem = null ;
2020-08-24 00:09:10 +02:00
private GBDevice gbDevice ;
2020-08-10 21:44:04 +02:00
private boolean show_raw_data = false ;
2020-08-16 20:45:03 +02:00
private int alternateColor ;
2020-10-11 18:05:49 +02:00
private Menu mOptionsMenu ;
2021-07-08 22:36:43 +02:00
List < String > filesGpxList = new ArrayList < > ( ) ;
int selectedGpxIndex ;
String selectedGpxFile ;
File export_path = null ;
2020-10-11 18:05:49 +02:00
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 ;
}
public static Bitmap getScreenShot ( View view , int height , int width , Context context ) {
Bitmap bitmap = Bitmap . createBitmap ( width , height , Bitmap . Config . ARGB_8888 ) ;
Canvas canvas = new Canvas ( bitmap ) ;
2020-10-22 06:32:21 +02:00
canvas . drawColor ( GBApplication . getWindowBackgroundColor ( context ) ) ;
2020-10-11 18:05:49 +02:00
view . draw ( canvas ) ;
return bitmap ;
}
2020-08-07 09:21:14 +02:00
2020-08-14 22:48:03 +02:00
@SuppressLint ( " ClickableViewAccessibility " )
2020-08-07 09:21:14 +02:00
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
2020-10-11 18:05:49 +02:00
2020-08-24 00:09:10 +02:00
final Context appContext = this . getApplicationContext ( ) ;
if ( appContext instanceof GBApplication ) {
setContentView ( R . layout . activity_summary_details ) ;
}
2020-10-11 18:05:49 +02:00
2020-08-07 09:21:14 +02:00
Intent intent = getIntent ( ) ;
2020-08-31 23:22:17 +02:00
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);
2020-08-16 20:45:03 +02:00
alternateColor = getAlternateColor ( this ) ;
2020-08-14 22:48:03 +02:00
2020-08-15 12:12:06 +02:00
final Animation animFadeRight ;
final Animation animFadeLeft ;
final Animation animBounceLeft ;
final Animation animBounceRight ;
animFadeRight = AnimationUtils . loadAnimation (
2020-08-14 22:48:03 +02:00
this ,
R . anim . flyright ) ;
2020-08-15 12:12:06 +02:00
animFadeLeft = AnimationUtils . loadAnimation (
2020-08-14 22:48:03 +02:00
this ,
R . anim . flyleft ) ;
2020-08-15 12:12:06 +02:00
animBounceLeft = AnimationUtils . loadAnimation (
this ,
R . anim . bounceleft ) ;
animBounceRight = AnimationUtils . loadAnimation (
this ,
R . anim . bounceright ) ;
2020-08-14 22:48:03 +02:00
2020-09-06 21:23:18 +02:00
final ActivitySummariesChartFragment activitySummariesChartFragment = new ActivitySummariesChartFragment ( ) ;
2020-10-22 06:32:21 +02:00
final ActivitySummariesGpsFragment activitySummariesGpsFragment = new ActivitySummariesGpsFragment ( ) ;
2020-09-06 21:23:18 +02:00
getSupportFragmentManager ( )
. beginTransaction ( )
2020-10-22 06:32:21 +02:00
. replace ( R . id . chartsFragmentHolder , activitySummariesChartFragment )
. replace ( R . id . gpsFragmentHolder , activitySummariesGpsFragment )
2020-09-06 21:23:18 +02:00
. commit ( ) ;
2020-08-14 22:48:03 +02:00
layout . setOnTouchListener ( new SwipeEvents ( this ) {
@Override
public void onSwipeRight ( ) {
2020-08-26 21:12:54 +02:00
BaseActivitySummary newItem = items . getNextItem ( ) ;
if ( newItem ! = null ) {
currentItem = newItem ;
makeSummaryHeader ( newItem ) ;
makeSummaryContent ( newItem ) ;
2020-11-26 21:08:03 +01:00
activitySummariesChartFragment . setDateAndGetData ( getGBDevice ( currentItem . getDevice ( ) ) , currentItem . getStartTime ( ) . getTime ( ) / 1000 , currentItem . getEndTime ( ) . getTime ( ) / 1000 ) ;
2020-10-22 06:32:21 +02:00
if ( get_gpx_file ( ) ! = null ) {
showCanvas ( ) ;
activitySummariesGpsFragment . set_data ( get_gpx_file ( ) ) ;
} else {
hideCanvas ( ) ;
}
2020-08-15 12:12:06 +02:00
layout . startAnimation ( animFadeRight ) ;
2023-05-14 17:10:25 +02:00
updateMenuItems ( ) ;
2020-08-24 00:09:10 +02:00
} else {
2020-08-15 12:12:06 +02:00
layout . startAnimation ( animBounceRight ) ;
2020-08-14 22:48:03 +02:00
}
}
2020-08-24 00:09:10 +02:00
2020-08-14 22:48:03 +02:00
@Override
public void onSwipeLeft ( ) {
2020-08-26 21:12:54 +02:00
BaseActivitySummary newItem = items . getPrevItem ( ) ;
if ( newItem ! = null ) {
currentItem = newItem ;
makeSummaryHeader ( newItem ) ;
makeSummaryContent ( newItem ) ;
2020-11-26 21:08:03 +01:00
activitySummariesChartFragment . setDateAndGetData ( getGBDevice ( currentItem . getDevice ( ) ) , currentItem . getStartTime ( ) . getTime ( ) / 1000 , currentItem . getEndTime ( ) . getTime ( ) / 1000 ) ;
2020-10-22 06:32:21 +02:00
if ( get_gpx_file ( ) ! = null ) {
showCanvas ( ) ;
activitySummariesGpsFragment . set_data ( get_gpx_file ( ) ) ;
} else {
hideCanvas ( ) ;
}
2020-08-15 12:12:06 +02:00
layout . startAnimation ( animFadeLeft ) ;
2023-05-14 17:10:25 +02:00
updateMenuItems ( ) ;
2020-08-24 00:09:10 +02:00
} else {
2020-08-15 12:12:06 +02:00
layout . startAnimation ( animBounceLeft ) ;
2020-08-14 22:48:03 +02:00
}
}
} ) ;
currentItem = items . getItem ( position ) ;
if ( currentItem ! = null ) {
makeSummaryHeader ( currentItem ) ;
makeSummaryContent ( currentItem ) ;
2020-11-26 21:08:03 +01:00
activitySummariesChartFragment . setDateAndGetData ( getGBDevice ( currentItem . getDevice ( ) ) , currentItem . getStartTime ( ) . getTime ( ) / 1000 , currentItem . getEndTime ( ) . getTime ( ) / 1000 ) ;
2020-10-22 06:32:21 +02:00
if ( get_gpx_file ( ) ! = null ) {
showCanvas ( ) ;
activitySummariesGpsFragment . set_data ( get_gpx_file ( ) ) ;
} else {
hideCanvas ( ) ;
}
2020-08-14 22:48:03 +02:00
}
2020-09-06 21:23:18 +02:00
2020-08-14 22:48:03 +02:00
//allows long-press.switch of data being in raw form or recalculated
2020-08-18 10:39:44 +02:00
ImageView activity_icon = findViewById ( R . id . item_image ) ;
2020-08-14 22:48:03 +02:00
activity_icon . setOnLongClickListener ( new View . OnLongClickListener ( ) {
public boolean onLongClick ( View v ) {
2020-08-24 00:09:10 +02:00
show_raw_data = ! show_raw_data ;
2020-08-14 22:48:03 +02:00
if ( currentItem ! = null ) {
makeSummaryHeader ( currentItem ) ;
makeSummaryContent ( currentItem ) ;
}
return false ;
}
} ) ;
2020-08-07 09:21:14 +02:00
2020-08-24 00:09:10 +02:00
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 : " " ) ;
2020-08-24 13:50:10 +02:00
FrameLayout container = new FrameLayout ( ActivitySummaryDetail . this ) ;
2020-10-11 18:05:49 +02:00
FrameLayout . LayoutParams params = new FrameLayout . LayoutParams ( ViewGroup . LayoutParams . MATCH_PARENT , ViewGroup . LayoutParams . WRAP_CONTENT ) ;
2020-08-24 13:50:10 +02:00
params . leftMargin = getResources ( ) . getDimensionPixelSize ( R . dimen . dialog_margin ) ;
params . rightMargin = getResources ( ) . getDimensionPixelSize ( R . dimen . dialog_margin ) ;
input . setLayoutParams ( params ) ;
container . addView ( input ) ;
2023-07-22 21:23:27 +02:00
new MaterialAlertDialogBuilder ( ActivitySummaryDetail . this )
2020-08-24 13:50:10 +02:00
. setView ( container )
2020-08-24 00:09:10 +02:00
. 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 ( ) ;
2020-11-26 21:08:03 +01:00
if ( name . length ( ) < 1 ) name = null ;
2020-08-24 00:09:10 +02:00
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 ( ) ;
}
} ) ;
2021-07-08 22:36:43 +02:00
ImageView activity_summary_detail_edit_gps = findViewById ( R . id . activity_summary_detail_edit_gps ) ;
activity_summary_detail_edit_gps . setOnClickListener ( new View . OnClickListener ( ) {
@Override
public void onClick ( View view ) {
export_path = get_path ( ) ;
filesGpxList = get_gpx_file_list ( ) ;
2021-07-10 22:13:33 +02:00
2023-07-22 21:23:27 +02:00
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder ( ActivitySummaryDetail . this ) ;
2021-07-08 22:36:43 +02:00
builder . setTitle ( R . string . activity_summary_detail_select_gpx_track ) ;
ArrayAdapter < String > directory_listing = new ArrayAdapter < String > ( ActivitySummaryDetail . this , android . R . layout . simple_list_item_1 , filesGpxList ) ;
builder . setSingleChoiceItems ( directory_listing , 0 , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
selectedGpxIndex = which ;
selectedGpxFile = export_path + " / " + filesGpxList . get ( selectedGpxIndex ) ;
String message = String . format ( " %s %s? " , getString ( R . string . set ) , filesGpxList . get ( selectedGpxIndex ) ) ;
if ( selectedGpxIndex = = 0 ) {
selectedGpxFile = null ;
message = String . format ( " %s? " , getString ( R . string . activity_summary_detail_clear_gpx_track ) ) ;
}
2023-07-22 21:23:27 +02:00
new MaterialAlertDialogBuilder ( ActivitySummaryDetail . this )
2021-07-08 22:36:43 +02:00
. setCancelable ( true )
. setIcon ( R . drawable . ic_warning )
. setTitle ( R . string . activity_summary_detail_editing_gpx_track )
. setMessage ( message )
. setPositiveButton ( R . string . ok , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
currentItem . setGpxTrack ( selectedGpxFile ) ;
currentItem . update ( ) ;
if ( get_gpx_file ( ) ! = null ) {
showCanvas ( ) ;
activitySummariesGpsFragment . set_data ( get_gpx_file ( ) ) ;
} else {
hideCanvas ( ) ;
}
}
} )
. setNegativeButton ( R . string . Cancel , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
}
} )
. show ( ) ;
dialog . dismiss ( ) ;
}
} ) ;
AlertDialog dialog = builder . create ( ) ;
dialog . show ( ) ;
}
} ) ;
2020-08-14 22:48:03 +02:00
}
2020-08-24 00:09:10 +02:00
private void makeSummaryHeader ( BaseActivitySummary item ) {
2020-08-14 22:48:03 +02:00
//make view of data from main part of item
String activitykindname = ActivityKind . asString ( item . getActivityKind ( ) , getApplicationContext ( ) ) ;
2020-08-24 00:09:10 +02:00
String activityname = item . getName ( ) ;
2020-08-18 10:39:44 +02:00
Date starttime = item . getStartTime ( ) ;
Date endtime = item . getEndTime ( ) ;
2020-10-14 22:28:42 +02:00
String starttimeS = String . format ( " %s, %s " , DateTimeUtils . formatDate ( starttime ) , DateTimeUtils . formatTime ( starttime . getHours ( ) , starttime . getMinutes ( ) ) ) ;
String endtimeS = String . format ( " %s, %s " , DateTimeUtils . formatDate ( endtime ) , DateTimeUtils . formatTime ( endtime . getHours ( ) , endtime . getMinutes ( ) ) ) ;
2020-08-09 13:11:10 +02:00
String durationhms = DateTimeUtils . formatDurationHoursMinutes ( ( endtime . getTime ( ) - starttime . getTime ( ) ) , TimeUnit . MILLISECONDS ) ;
2020-08-07 09:21:14 +02:00
2020-08-18 10:39:44 +02:00
ImageView activity_icon = findViewById ( R . id . item_image ) ;
2020-08-14 22:48:03 +02:00
activity_icon . setImageResource ( ActivityKind . getIconId ( item . getActivityKind ( ) ) ) ;
2020-08-18 10:39:44 +02:00
TextView activity_kind = findViewById ( R . id . activitykind ) ;
2020-08-09 13:11:10 +02:00
activity_kind . setText ( activitykindname ) ;
2020-08-24 00:09:10 +02:00
TextView activity_name = findViewById ( R . id . activityname ) ;
activity_name . setText ( activityname ) ;
2020-08-26 20:45:38 +02:00
if ( activityname = = null | | ( activityname ! = null & & activityname . length ( ) < 1 ) ) {
activity_name . setVisibility ( View . GONE ) ;
} else {
activity_name . setVisibility ( View . VISIBLE ) ;
}
2020-08-18 10:39:44 +02:00
TextView start_time = findViewById ( R . id . starttime ) ;
2020-08-09 13:11:10 +02:00
start_time . setText ( starttimeS ) ;
2020-08-18 10:39:44 +02:00
TextView end_time = findViewById ( R . id . endtime ) ;
2020-08-09 13:11:10 +02:00
end_time . setText ( endtimeS ) ;
2020-08-18 10:39:44 +02:00
TextView activity_duration = findViewById ( R . id . duration ) ;
2020-08-07 09:21:14 +02:00
activity_duration . setText ( durationhms ) ;
2020-08-08 16:22:55 +02:00
2020-08-14 22:48:03 +02:00
}
2021-07-08 22:36:43 +02:00
private File get_path ( ) {
File path = null ;
try {
path = FileUtils . getExternalFilesDir ( ) ;
} catch ( IOException e ) {
2021-07-10 22:13:33 +02:00
LOG . error ( " Error getting path " , e ) ;
2021-07-08 22:36:43 +02:00
}
return path ;
}
private List < String > get_gpx_file_list ( ) {
List < String > list = new ArrayList < > ( ) ;
File [ ] fileListing = export_path . listFiles ( new FileFilter ( ) {
@Override
public boolean accept ( File file ) {
return file . getPath ( ) . toLowerCase ( ) . endsWith ( " .gpx " ) ;
}
} ) ;
if ( fileListing ! = null & & fileListing . length > 1 ) {
Arrays . sort ( fileListing , new Comparator < File > ( ) {
@Override
public int compare ( File fileA , File fileB ) {
if ( fileA . lastModified ( ) < fileB . lastModified ( ) ) {
return 1 ;
}
if ( fileA . lastModified ( ) > fileB . lastModified ( ) ) {
return - 1 ;
}
return 0 ;
}
} ) ;
}
list . add ( getString ( R . string . activity_summary_detail_clear_gpx_track ) ) ;
for ( File file : fileListing ) {
list . add ( file . getName ( ) ) ;
}
return list ;
}
2020-08-18 10:39:44 +02:00
private void makeSummaryContent ( BaseActivitySummary item ) {
2023-09-27 23:11:02 +02:00
final DeviceCoordinator coordinator = gbDevice . getDeviceCoordinator ( ) ;
2022-09-18 13:19:23 +02:00
final ActivitySummaryParser summaryParser = coordinator . getActivitySummaryParser ( gbDevice ) ;
2020-08-14 22:48:03 +02:00
//make view of data from summaryData of item
2021-01-23 21:42:12 +01:00
String units = GBApplication . getPrefs ( ) . getString ( SettingsActivity . PREF_MEASUREMENT_SYSTEM , GBApplication . getContext ( ) . getString ( R . string . p_unit_metric ) ) ;
String UNIT_IMPERIAL = GBApplication . getContext ( ) . getString ( R . string . p_unit_imperial ) ;
2020-08-14 22:48:03 +02:00
TableLayout fieldLayout = findViewById ( R . id . summaryDetails ) ;
fieldLayout . removeAllViews ( ) ; //remove old widgets
2022-09-18 13:19:23 +02:00
ActivitySummaryJsonSummary activitySummaryJsonSummary = new ActivitySummaryJsonSummary ( summaryParser , item ) ;
2020-08-20 09:03:26 +02:00
JSONObject data = activitySummaryJsonSummary . getSummaryGroupedList ( ) ; //get list, grouped by groups
if ( data = = null ) return ;
2020-08-10 21:44:04 +02:00
2020-08-09 13:11:10 +02:00
Iterator < String > keys = data . keys ( ) ;
DecimalFormat df = new DecimalFormat ( " #.## " ) ;
while ( keys . hasNext ( ) ) {
String key = keys . next ( ) ;
try {
JSONArray innerList = ( JSONArray ) data . get ( key ) ;
2020-08-10 10:56:54 +02:00
TableRow label_row = new TableRow ( ActivitySummaryDetail . this ) ;
TextView label_field = new TextView ( ActivitySummaryDetail . this ) ;
label_field . setTextSize ( 16 ) ;
2020-10-11 18:05:49 +02:00
label_field . setPadding ( 0 , 10 , 0 , 0 ) ;
2020-08-10 10:56:54 +02:00
label_field . setTypeface ( null , Typeface . BOLD ) ;
label_field . setText ( String . format ( " %s " , getStringResourceByName ( key ) ) ) ;
label_row . addView ( label_field ) ;
fieldLayout . addView ( label_row ) ;
2020-08-09 13:11:10 +02:00
for ( int i = 0 ; i < innerList . length ( ) ; i + + ) {
2020-08-20 22:27:33 +02:00
TextView name_field = new TextView ( ActivitySummaryDetail . this ) ;
TextView value_field = new TextView ( ActivitySummaryDetail . this ) ;
name_field . setGravity ( Gravity . START ) ;
value_field . setGravity ( Gravity . END ) ;
2020-08-09 13:11:10 +02:00
JSONObject innerData = innerList . getJSONObject ( i ) ;
2020-08-20 22:27:33 +02:00
String unit = innerData . getString ( " unit " ) ;
2020-08-09 13:11:10 +02:00
String name = innerData . getString ( " name " ) ;
2020-08-20 22:27:33 +02:00
if ( ! unit . equals ( " string " ) ) {
double value = innerData . getDouble ( " value " ) ;
if ( ! show_raw_data ) {
2021-01-23 21:42:12 +01:00
//special casing here + imperial units handling
2020-08-20 22:27:33 +02:00
switch ( unit ) {
2024-01-06 14:54:06 +01:00
case UNIT_CM :
2021-01-23 21:42:12 +01:00
if ( units . equals ( UNIT_IMPERIAL ) ) {
value = value * 0 . 0328084 ;
unit = " ft " ;
}
break ;
2024-01-06 14:54:06 +01:00
case UNIT_METERS_PER_SECOND :
2021-01-23 21:42:12 +01:00
if ( units . equals ( UNIT_IMPERIAL ) ) {
value = value * 2 . 236936D ;
unit = " mi_h " ;
} else { //metric
value = value * 3 . 6 ;
unit = " km_h " ;
}
2020-08-20 22:27:33 +02:00
break ;
2024-01-06 14:54:06 +01:00
case UNIT_SECONDS_PER_M :
2021-01-23 21:42:12 +01:00
if ( units . equals ( UNIT_IMPERIAL ) ) {
value = value * ( 1609 . 344 / 60D ) ;
unit = " minutes_mi " ;
} else { //metric
value = value * ( 1000 / 60D ) ;
unit = " minutes_km " ;
}
2020-08-20 22:27:33 +02:00
break ;
2024-01-06 14:54:06 +01:00
case UNIT_SECONDS_PER_KM :
2021-01-23 21:42:12 +01:00
if ( units . equals ( UNIT_IMPERIAL ) ) {
value = value / 60D * 1 . 609344 ;
unit = " minutes_mi " ;
} else { //metric
value = value / 60D ;
unit = " minutes_km " ;
}
2020-08-20 22:27:33 +02:00
break ;
2024-01-06 14:54:06 +01:00
case UNIT_METERS :
2021-01-23 21:42:12 +01:00
if ( units . equals ( UNIT_IMPERIAL ) ) {
value = value * 3 . 28084D ;
unit = " ft " ;
if ( value > 6000 ) {
value = value * 0 . 0001893939D ;
unit = " mi " ;
}
} else { //metric
if ( value > 2000 ) {
value = value / 1000 ;
unit = " km " ;
}
2020-08-31 23:22:17 +02:00
}
break ;
2020-08-20 22:27:33 +02:00
}
}
2020-08-09 13:11:10 +02:00
2020-08-20 22:27:33 +02:00
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 ) ) ) ;
2020-08-10 21:44:04 +02:00
}
2020-08-20 22:27:33 +02:00
} else {
2020-08-22 10:31:32 +02:00
value_field . setText ( getStringResourceByName ( innerData . getString ( " value " ) ) ) ; //we could optimize here a bit and only do this for particular activities (swim at the moment...)
2020-08-09 13:11:10 +02:00
}
2020-08-20 22:27:33 +02:00
2020-08-10 10:56:54 +02:00
TableRow field_row = new TableRow ( ActivitySummaryDetail . this ) ;
2020-08-16 20:45:03 +02:00
if ( i % 2 = = 0 ) field_row . setBackgroundColor ( alternateColor ) ;
2020-08-10 14:55:57 +02:00
2020-08-10 10:56:54 +02:00
name_field . setText ( getStringResourceByName ( name ) ) ;
2020-08-10 14:55:57 +02:00
TableRow . LayoutParams params = new TableRow . LayoutParams ( 0 , TableRow . LayoutParams . WRAP_CONTENT , 1f ) ;
value_field . setLayoutParams ( params ) ;
2020-08-10 10:56:54 +02:00
field_row . addView ( name_field ) ;
field_row . addView ( value_field ) ;
fieldLayout . addView ( field_row ) ;
2020-08-09 13:11:10 +02:00
}
} catch ( JSONException e ) {
LOG . error ( " SportsActivity " , e ) ;
}
}
2020-08-10 10:56:54 +02:00
}
2020-08-09 13:11:10 +02:00
private String getStringResourceByName ( String aString ) {
String packageName = getPackageName ( ) ;
int resId = getResources ( ) . getIdentifier ( aString , " string " , packageName ) ;
2020-08-24 00:09:10 +02:00
if ( resId = = 0 ) {
2020-08-22 10:31:32 +02:00
//LOG.warn("SportsActivity " + "Missing string in strings:" + aString);
2020-08-09 13:11:10 +02:00
return aString ;
2020-08-24 00:09:10 +02:00
} else {
2020-08-09 13:11:10 +02:00
return getString ( resId ) ;
}
}
2020-08-07 09:21:14 +02:00
2020-08-15 14:45:43 +02:00
@Override
public boolean onOptionsItemSelected ( MenuItem item ) {
switch ( item . getItemId ( ) ) {
case android . R . id . home :
// back button
finish ( ) ;
return true ;
2020-10-11 18:05:49 +02:00
case R . id . activity_action_take_screenshot :
take_share_screenshot ( ActivitySummaryDetail . this ) ;
return true ;
2023-05-14 17:10:25 +02:00
case R . id . activity_action_show_gpx :
viewGpxTrack ( ActivitySummaryDetail . this ) ;
return true ;
2020-10-11 18:05:49 +02:00
case R . id . activity_action_share_gpx :
2023-05-14 17:10:25 +02:00
shareGpxTrack ( ActivitySummaryDetail . this , currentItem ) ;
return true ;
case R . id . activity_action_dev_share_raw_summary :
shareRawSummary ( ActivitySummaryDetail . this , currentItem ) ;
return true ;
case R . id . activity_action_dev_share_raw_details :
shareRawDetails ( ActivitySummaryDetail . this , currentItem ) ;
2020-10-11 18:05:49 +02:00
return true ;
2020-08-15 14:45:43 +02:00
}
return super . onOptionsItemSelected ( item ) ;
}
2020-10-11 18:05:49 +02:00
private void take_share_screenshot ( Context context ) {
final ScrollView layout = findViewById ( R . id . activity_summary_detail_scroll_layout ) ;
int width = layout . getChildAt ( 0 ) . getHeight ( ) ;
int height = layout . getChildAt ( 0 ) . getWidth ( ) ;
Bitmap screenShot = getScreenShot ( layout , width , height , context ) ;
String fileName = FileUtils . makeValidFileName ( " Screenshot- " + ActivityKind . asString ( currentItem . getActivityKind ( ) , context ) . toLowerCase ( ) + " - " + DateTimeUtils . formatIso8601 ( currentItem . getStartTime ( ) ) + " .png " ) ;
try {
File targetFile = new File ( FileUtils . getExternalFilesDir ( ) , fileName ) ;
FileOutputStream fOut = new FileOutputStream ( targetFile ) ;
screenShot . compress ( Bitmap . CompressFormat . PNG , 85 , fOut ) ;
fOut . flush ( ) ;
fOut . close ( ) ;
shareScreenshot ( targetFile , context ) ;
GB . toast ( getApplicationContext ( ) , " Screenshot saved " , Toast . LENGTH_LONG , GB . INFO ) ;
} catch ( IOException e ) {
2021-07-10 22:13:33 +02:00
LOG . error ( " Error getting screenshot " , e ) ;
2020-10-11 18:05:49 +02:00
}
}
private void shareScreenshot ( File targetFile , Context context ) {
Uri contentUri = FileProvider . getUriForFile ( context ,
context . getApplicationContext ( ) . getPackageName ( ) + " .screenshot_provider " , targetFile ) ;
Intent sharingIntent = new Intent ( android . content . Intent . ACTION_SEND ) ;
sharingIntent . setType ( " image/* " ) ;
String shareBody = " Sports Activity " ;
sharingIntent . putExtra ( android . content . Intent . EXTRA_SUBJECT , " Sports Activity " ) ;
sharingIntent . putExtra ( android . content . Intent . EXTRA_TEXT , shareBody ) ;
sharingIntent . putExtra ( Intent . EXTRA_STREAM , contentUri ) ;
try {
startActivity ( Intent . createChooser ( sharingIntent , " Share via " ) ) ;
} catch ( ActivityNotFoundException e ) {
Toast . makeText ( context , R . string . activity_error_no_app_for_png , Toast . LENGTH_LONG ) . show ( ) ;
}
}
2023-05-14 17:10:25 +02:00
private void viewGpxTrack ( Context context ) {
2020-10-11 18:05:49 +02:00
final String gpxTrack = currentItem . getGpxTrack ( ) ;
2020-08-24 00:09:10 +02:00
2020-10-11 18:05:49 +02:00
if ( gpxTrack ! = null ) {
try {
AndroidUtils . viewFile ( gpxTrack , Intent . ACTION_VIEW , context ) ;
} catch ( IOException e ) {
GB . toast ( getApplicationContext ( ) , " Unable to display GPX track: " + e . getMessage ( ) , Toast . LENGTH_LONG , GB . ERROR , e ) ;
}
} else {
GB . toast ( getApplicationContext ( ) , " No GPX track in this activity " , Toast . LENGTH_LONG , GB . INFO ) ;
}
}
2023-05-14 17:10:25 +02:00
private static void shareGpxTrack ( final Context context , final BaseActivitySummary summary ) {
try {
AndroidUtils . shareFile ( context , new File ( summary . getGpxTrack ( ) ) ) ;
} catch ( final Exception e ) {
GB . toast ( context , " Unable to share GPX track: " + e . getMessage ( ) , Toast . LENGTH_LONG , GB . ERROR , e ) ;
2020-11-22 22:33:55 +01:00
}
2023-05-14 17:10:25 +02:00
}
private static void shareRawSummary ( final Context context , final BaseActivitySummary summary ) {
if ( summary . getRawSummaryData ( ) = = null ) {
GB . toast ( context , " No raw summary in this activity " , Toast . LENGTH_LONG , GB . WARN ) ;
return ;
}
final String rawSummaryFilename = FileUtils . makeValidFileName ( String . format ( " %s_summary.bin " , DateTimeUtils . formatIso8601 ( summary . getStartTime ( ) ) ) ) ;
try {
AndroidUtils . shareBytesAsFile ( context , rawSummaryFilename , summary . getRawSummaryData ( ) ) ;
} catch ( final Exception e ) {
GB . toast ( context , " Unable to share GPX track: " + e . getMessage ( ) , Toast . LENGTH_LONG , GB . ERROR , e ) ;
}
}
private static void shareRawDetails ( final Context context , final BaseActivitySummary summary ) {
if ( summary . getRawDetailsPath ( ) = = null ) {
GB . toast ( context , " No raw details in this activity " , Toast . LENGTH_LONG , GB . WARN ) ;
return ;
2020-10-11 18:05:49 +02:00
}
2023-05-14 17:10:25 +02:00
try {
AndroidUtils . shareFile ( context , new File ( summary . getRawDetailsPath ( ) ) ) ;
} catch ( final Exception e ) {
GB . toast ( context , " Unable to share raw details: " + e . getMessage ( ) , Toast . LENGTH_LONG , GB . ERROR , e ) ;
}
}
private void updateMenuItems ( ) {
boolean hasGpx = false ;
boolean hasRawSummary = false ;
boolean hasRawDetails = false ;
if ( currentItem ! = null ) {
hasGpx = currentItem . getGpxTrack ( ) ! = null ;
hasRawSummary = currentItem . getRawSummaryData ( ) ! = null ;
final String rawDetailsPath = currentItem . getRawDetailsPath ( ) ;
if ( rawDetailsPath ! = null ) {
hasRawDetails = new File ( rawDetailsPath ) . exists ( ) ;
}
}
mOptionsMenu . findItem ( R . id . activity_detail_overflowMenu ) . getSubMenu ( ) . findItem ( R . id . activity_action_show_gpx ) . setVisible ( hasGpx ) ;
mOptionsMenu . findItem ( R . id . activity_detail_overflowMenu ) . getSubMenu ( ) . findItem ( R . id . activity_action_share_gpx ) . setVisible ( hasGpx ) ;
mOptionsMenu . findItem ( R . id . activity_detail_overflowMenu ) . getSubMenu ( ) . findItem ( R . id . activity_action_dev_share_raw_summary ) . setVisible ( hasRawSummary ) ;
mOptionsMenu . findItem ( R . id . activity_detail_overflowMenu ) . getSubMenu ( ) . findItem ( R . id . activity_action_dev_share_raw_details ) . setVisible ( hasRawDetails ) ;
final MenuItem devToolsMenu = mOptionsMenu . findItem ( R . id . activity_detail_overflowMenu ) . getSubMenu ( ) . findItem ( R . id . activity_action_dev_tools ) ;
devToolsMenu . setVisible ( devToolsMenu . getSubMenu ( ) . hasVisibleItems ( ) ) ;
2020-10-11 18:05:49 +02:00
}
2020-10-22 06:32:21 +02:00
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 ;
}
2020-10-11 18:05:49 +02:00
@Override
public boolean onCreateOptionsMenu ( Menu menu ) {
super . onCreateOptionsMenu ( menu ) ;
mOptionsMenu = menu ;
getMenuInflater ( ) . inflate ( R . menu . activity_take_screenshot_menu , menu ) ;
2023-05-14 17:10:25 +02:00
updateMenuItems ( ) ;
2020-10-11 18:05:49 +02:00
return true ;
}
2020-11-26 21:08:03 +01:00
private GBDevice getGBDevice ( Device findDevice ) {
DaoSession daoSession ;
GBApplication gbApp = ( GBApplication ) getApplicationContext ( ) ;
List < ? extends GBDevice > devices = gbApp . getDeviceManager ( ) . getDevices ( ) ;
try ( DBHandler handler = GBApplication . acquireDB ( ) ) {
daoSession = handler . getDaoSession ( ) ;
for ( GBDevice device : devices ) {
Device dbDevice = DBHelper . findDevice ( device , daoSession ) ;
if ( dbDevice . equals ( findDevice ) ) return device ;
}
} catch ( Exception e ) {
LOG . debug ( " Error getting device: " + e ) ;
}
return null ;
}
2020-08-07 09:21:14 +02:00
}