2020-01-09 10:44:32 +01:00
/ * Copyright ( C ) 2017 - 2020 Andreas Shimokawa , Carsten Pfeiffer , Daniele
2018-06-25 18:35:46 +02:00
Gobbetti
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/>. */
2017-10-19 21:52:38 +02:00
package nodomain.freeyourgadget.gadgetbridge.activities ;
2018-04-12 22:11:58 +02:00
import android.app.DatePickerDialog ;
2018-04-02 10:15:26 +02:00
import android.content.BroadcastReceiver ;
import android.content.Context ;
2017-10-19 21:52:38 +02:00
import android.content.Intent ;
2018-04-02 10:15:26 +02:00
import android.content.IntentFilter ;
2018-04-12 22:11:58 +02:00
import android.content.SharedPreferences ;
2018-04-04 21:38:27 +02:00
import android.net.Uri ;
2017-10-19 21:52:38 +02:00
import android.os.Bundle ;
2018-04-04 21:38:27 +02:00
import android.util.SparseBooleanArray ;
import android.view.ActionMode ;
import android.view.Menu ;
2017-10-19 21:52:38 +02:00
import android.view.MenuItem ;
import android.view.View ;
2018-04-04 21:38:27 +02:00
import android.widget.AbsListView ;
2017-10-19 21:52:38 +02:00
import android.widget.AdapterView ;
2020-08-11 21:54:02 +02:00
import android.widget.ArrayAdapter ;
2018-04-12 22:11:58 +02:00
import android.widget.DatePicker ;
2018-04-04 21:38:27 +02:00
import android.widget.ListView ;
2020-08-11 21:54:02 +02:00
import android.widget.Spinner ;
2017-10-19 21:52:38 +02:00
import android.widget.Toast ;
2019-08-14 19:24:03 +02:00
import androidx.core.content.FileProvider ;
import androidx.localbroadcastmanager.content.LocalBroadcastManager ;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout ;
2019-01-26 15:52:40 +01:00
import com.google.android.material.floatingactionbutton.FloatingActionButton ;
2018-04-04 21:38:27 +02:00
import java.io.File ;
2017-10-19 21:52:38 +02:00
import java.io.IOException ;
2018-04-04 21:38:27 +02:00
import java.util.ArrayList ;
2018-04-12 22:11:58 +02:00
import java.util.Calendar ;
2020-08-11 21:54:02 +02:00
import java.util.LinkedHashMap ;
2018-04-04 21:38:27 +02:00
import java.util.List ;
2018-04-02 10:15:26 +02:00
import java.util.Objects ;
2017-10-19 21:52:38 +02:00
2018-03-31 16:21:25 +02:00
import nodomain.freeyourgadget.gadgetbridge.GBApplication ;
import nodomain.freeyourgadget.gadgetbridge.R ;
2017-10-19 21:52:38 +02:00
import nodomain.freeyourgadget.gadgetbridge.adapter.ActivitySummariesAdapter ;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary ;
2018-03-31 16:21:25 +02:00
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice ;
2020-08-11 21:54:02 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind ;
2017-10-19 21:52:38 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummary ;
2018-03-31 16:21:25 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes ;
2017-10-19 21:52:38 +02:00
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils ;
import nodomain.freeyourgadget.gadgetbridge.util.GB ;
2020-08-07 09:21:14 +02:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2017-10-19 21:52:38 +02:00
2020-08-07 09:21:14 +02:00
public class ActivitySummariesActivity extends AbstractListActivity < BaseActivitySummary > {
private static final Logger LOG = LoggerFactory . getLogger ( ActivitySummariesActivity . class ) ;
2018-03-31 16:21:25 +02:00
private GBDevice mGBDevice ;
private SwipeRefreshLayout swipeLayout ;
2020-08-11 21:54:02 +02:00
LinkedHashMap < String , Integer > activityKindMap = new LinkedHashMap < > ( 1 ) ;
2020-08-14 22:48:03 +02:00
int activityFilter = 0 ;
2018-03-31 16:21:25 +02:00
2018-04-02 10:15:26 +02:00
private final BroadcastReceiver mReceiver = new BroadcastReceiver ( ) {
@Override
public void onReceive ( Context context , Intent intent ) {
String action = intent . getAction ( ) ;
switch ( Objects . requireNonNull ( action ) ) {
case GBDevice . ACTION_DEVICE_CHANGED :
GBDevice device = intent . getParcelableExtra ( GBDevice . EXTRA_DEVICE ) ;
mGBDevice = device ;
if ( device . isBusy ( ) ) {
swipeLayout . setRefreshing ( true ) ;
} else {
boolean wasBusy = swipeLayout . isRefreshing ( ) ;
swipeLayout . setRefreshing ( false ) ;
if ( wasBusy ) {
refresh ( ) ;
}
}
break ;
}
}
} ;
2018-04-12 22:11:58 +02:00
@Override
public boolean onCreateOptionsMenu ( Menu menu ) {
super . onCreateOptionsMenu ( menu ) ;
getMenuInflater ( ) . inflate ( R . menu . activity_list_menu , menu ) ;
return true ;
}
@Override
public boolean onOptionsItemSelected ( MenuItem item ) {
boolean processed = false ;
switch ( item . getItemId ( ) ) {
case R . id . activity_action_manage_timestamp :
resetFetchTimestampToChosenDate ( ) ;
processed = true ;
break ;
}
return processed ;
}
2017-10-19 21:52:38 +02:00
@Override
protected void onCreate ( Bundle savedInstanceState ) {
2018-03-31 16:21:25 +02:00
Bundle extras = getIntent ( ) . getExtras ( ) ;
if ( extras ! = null ) {
mGBDevice = extras . getParcelable ( GBDevice . EXTRA_DEVICE ) ;
} else {
throw new IllegalArgumentException ( " Must provide a device when invoking this activity " ) ;
}
2018-04-02 10:15:26 +02:00
IntentFilter filterLocal = new IntentFilter ( ) ;
filterLocal . addAction ( GBDevice . ACTION_DEVICE_CHANGED ) ;
LocalBroadcastManager . getInstance ( this ) . registerReceiver ( mReceiver , filterLocal ) ;
2017-10-19 21:52:38 +02:00
super . onCreate ( savedInstanceState ) ;
2020-08-14 22:48:03 +02:00
2020-08-11 21:54:02 +02:00
setItemAdapter ( new ActivitySummariesAdapter ( this , mGBDevice , activityFilter ) ) ;
2017-10-19 21:52:38 +02:00
getItemListView ( ) . setOnItemClickListener ( new AdapterView . OnItemClickListener ( ) {
@Override
public void onItemClick ( AdapterView < ? > parent , View view , int position , long id ) {
2017-11-03 22:04:01 +01:00
Object item = parent . getItemAtPosition ( position ) ;
2017-10-19 21:52:38 +02:00
if ( item ! = null ) {
ActivitySummary summary = ( ActivitySummary ) item ;
2020-08-07 09:21:14 +02:00
try {
2020-08-14 22:48:03 +02:00
showActivityDetail ( position ) ;
2020-08-07 09:21:14 +02:00
} catch ( Exception e ) {
GB . toast ( getApplicationContext ( ) , " Unable to display Activity Detail, maybe the activity is not available yet: " + e . getMessage ( ) , Toast . LENGTH_LONG , GB . ERROR , e ) ;
2017-10-19 21:52:38 +02:00
}
2020-08-07 09:21:14 +02:00
2017-10-19 21:52:38 +02:00
}
}
} ) ;
2018-04-04 21:38:27 +02:00
getItemListView ( ) . setChoiceMode ( ListView . CHOICE_MODE_MULTIPLE_MODAL ) ;
getItemListView ( ) . setMultiChoiceModeListener ( new AbsListView . MultiChoiceModeListener ( ) {
2017-10-19 21:52:38 +02:00
@Override
2018-04-04 21:38:27 +02:00
public void onItemCheckedStateChanged ( ActionMode actionMode , int position , long id , boolean checked ) {
final int selectedItems = getItemListView ( ) . getCheckedItemCount ( ) ;
actionMode . setTitle ( selectedItems + " selected " ) ;
2017-10-19 21:52:38 +02:00
}
@Override
2018-04-04 21:38:27 +02:00
public boolean onCreateActionMode ( ActionMode actionMode , Menu menu ) {
2018-04-12 22:11:58 +02:00
getMenuInflater ( ) . inflate ( R . menu . activity_list_context_menu , menu ) ;
2018-04-04 21:38:27 +02:00
findViewById ( R . id . fab ) . setVisibility ( View . INVISIBLE ) ;
return true ;
}
@Override
public boolean onPrepareActionMode ( ActionMode actionMode , Menu menu ) {
return false ;
}
@Override
public boolean onActionItemClicked ( ActionMode actionMode , MenuItem menuItem ) {
boolean processed = false ;
SparseBooleanArray checked = getItemListView ( ) . getCheckedItemPositions ( ) ;
switch ( menuItem . getItemId ( ) ) {
case R . id . activity_action_delete :
2018-04-05 22:41:03 +02:00
List < BaseActivitySummary > toDelete = new ArrayList < > ( ) ;
for ( int i = 0 ; i < checked . size ( ) ; i + + ) {
if ( checked . valueAt ( i ) ) {
toDelete . add ( getItemAdapter ( ) . getItem ( checked . keyAt ( i ) ) ) ;
}
}
deleteItems ( toDelete ) ;
2018-04-04 21:38:27 +02:00
processed = true ;
break ;
case R . id . activity_action_export :
List < String > paths = new ArrayList < > ( ) ;
for ( int i = 0 ; i < checked . size ( ) ; i + + ) {
if ( checked . valueAt ( i ) ) {
BaseActivitySummary item = getItemAdapter ( ) . getItem ( checked . keyAt ( i ) ) ;
if ( item ! = null ) {
2018-09-15 23:51:00 +02:00
ActivitySummary summary = item ;
2018-04-04 21:38:27 +02:00
String gpxTrack = summary . getGpxTrack ( ) ;
if ( gpxTrack ! = null ) {
paths . add ( gpxTrack ) ;
}
}
}
}
shareMultiple ( paths ) ;
processed = true ;
2018-04-05 22:41:03 +02:00
break ;
case R . id . activity_action_select_all :
for ( int i = 0 ; i < getItemListView ( ) . getCount ( ) ; i + + ) {
getItemListView ( ) . setItemChecked ( i , true ) ;
}
return true ; //don't finish actionmode in this case!
2018-04-04 21:38:27 +02:00
default :
break ;
}
actionMode . finish ( ) ;
return processed ;
}
@Override
public void onDestroyActionMode ( ActionMode actionMode ) {
findViewById ( R . id . fab ) . setVisibility ( View . VISIBLE ) ;
2017-10-19 21:52:38 +02:00
}
} ) ;
2018-04-04 21:38:27 +02:00
2018-03-31 16:21:25 +02:00
swipeLayout = findViewById ( R . id . list_activity_swipe_layout ) ;
swipeLayout . setOnRefreshListener ( new SwipeRefreshLayout . OnRefreshListener ( ) {
@Override
public void onRefresh ( ) {
fetchTrackData ( ) ;
}
} ) ;
2018-04-02 21:22:28 +02:00
FloatingActionButton fab = findViewById ( R . id . fab ) ;
fab . setOnClickListener ( new View . OnClickListener ( ) {
@Override
public void onClick ( View v ) {
fetchTrackData ( ) ;
}
} ) ;
2020-08-11 21:54:02 +02:00
activityKindMap = fillKindMap ( ) ;
addItemsOnSpinner ( ) ;
addListenerOnSpinnerItemSelection ( ) ;
}
private LinkedHashMap fillKindMap ( ) {
LinkedHashMap < String , Integer > newMap = new LinkedHashMap < > ( 1 ) ; //reset
newMap . put ( " All Activities " , 0 ) ;
for ( BaseActivitySummary item : getItemAdapter ( ) . getItems ( ) ) {
String activityName = ActivityKind . asString ( item . getActivityKind ( ) , this ) ;
if ( ! newMap . containsKey ( item . getActivityKind ( ) ) ) {
newMap . put ( activityName , item . getActivityKind ( ) ) ;
}
}
return newMap ;
}
public void addListenerOnSpinnerItemSelection ( ) {
Spinner spinner = ( Spinner ) findViewById ( R . id . select_kind ) ;
spinner . setOnItemSelectedListener ( new CustomOnItemSelectedListener ( ) ) ;
}
public class CustomOnItemSelectedListener implements AdapterView . OnItemSelectedListener {
public void onItemSelected ( AdapterView < ? > parent , View view , int pos , long id ) {
2020-08-14 22:48:03 +02:00
activityFilter = activityKindMap . get ( parent . getItemAtPosition ( pos ) ) ;
setActivityKindFilter ( activityFilter ) ;
2020-08-11 21:54:02 +02:00
refresh ( ) ;
}
@Override
public void onNothingSelected ( AdapterView < ? > arg0 ) {
// TODO Auto-generated method stub
}
}
public void addItemsOnSpinner ( ) {
Spinner spinner = ( Spinner ) findViewById ( R . id . select_kind ) ;
ArrayList < String > spinnerArray = new ArrayList < > ( activityKindMap . keySet ( ) ) ;
ArrayAdapter < String > dataAdapter = new ArrayAdapter < String > ( this ,
android . R . layout . simple_spinner_dropdown_item , spinnerArray ) ;
spinner . setAdapter ( dataAdapter ) ;
2017-10-19 21:52:38 +02:00
}
2018-04-12 22:11:58 +02:00
public void resetFetchTimestampToChosenDate ( ) {
final Calendar currentDate = Calendar . getInstance ( ) ;
new DatePickerDialog ( this , new DatePickerDialog . OnDateSetListener ( ) {
@Override
public void onDateSet ( DatePicker view , int year , int monthOfYear , int dayOfMonth ) {
Calendar date = Calendar . getInstance ( ) ;
date . set ( year , monthOfYear , dayOfMonth ) ;
2018-09-15 23:51:00 +02:00
long timestamp = date . getTimeInMillis ( ) - 1000 ;
2019-08-14 19:24:03 +02:00
SharedPreferences . Editor editor = GBApplication . getDeviceSpecificSharedPrefs ( mGBDevice . getAddress ( ) ) . edit ( ) ;
editor . remove ( " lastSportsActivityTimeMillis " ) ; //FIXME: key reconstruction is BAD
editor . putLong ( " lastSportsActivityTimeMillis " , timestamp ) ;
editor . apply ( ) ;
2018-04-12 22:11:58 +02:00
}
} , currentDate . get ( Calendar . YEAR ) , currentDate . get ( Calendar . MONTH ) , currentDate . get ( Calendar . DATE ) ) . show ( ) ;
}
2018-04-02 10:15:26 +02:00
@Override
protected void onDestroy ( ) {
LocalBroadcastManager . getInstance ( this ) . unregisterReceiver ( mReceiver ) ;
super . onDestroy ( ) ;
}
2018-04-05 22:41:03 +02:00
private void deleteItems ( List < BaseActivitySummary > items ) {
for ( BaseActivitySummary item : items ) {
2017-10-19 21:52:38 +02:00
item . delete ( ) ;
getItemAdapter ( ) . remove ( item ) ;
}
2018-04-05 22:41:03 +02:00
refresh ( ) ;
2017-10-19 21:52:38 +02:00
}
2020-08-14 22:48:03 +02:00
private void showActivityDetail ( int position ) {
2020-08-07 09:21:14 +02:00
Intent ActivitySummaryDetailIntent = new Intent ( this , ActivitySummaryDetail . class ) ;
2020-08-14 22:48:03 +02:00
ActivitySummaryDetailIntent . putExtra ( " position " , position ) ;
ActivitySummaryDetailIntent . putExtra ( " filter " , activityFilter ) ;
2020-08-07 09:21:14 +02:00
ActivitySummaryDetailIntent . putExtra ( GBDevice . EXTRA_DEVICE , mGBDevice ) ;
startActivity ( ActivitySummaryDetailIntent ) ;
2020-08-14 22:48:03 +02:00
2020-08-07 09:21:14 +02:00
}
2018-03-31 16:21:25 +02:00
private void fetchTrackData ( ) {
2018-04-02 10:15:26 +02:00
if ( mGBDevice . isInitialized ( ) & & ! mGBDevice . isBusy ( ) ) {
2018-03-31 16:21:25 +02:00
GBApplication . deviceService ( ) . onFetchRecordedData ( RecordedDataTypes . TYPE_GPS_TRACKS ) ;
} else {
swipeLayout . setRefreshing ( false ) ;
2018-04-02 10:15:26 +02:00
if ( ! mGBDevice . isInitialized ( ) ) {
GB . toast ( this , getString ( R . string . device_not_connected ) , Toast . LENGTH_SHORT , GB . ERROR ) ;
}
2018-03-31 16:21:25 +02:00
}
}
2018-04-04 21:38:27 +02:00
private void shareMultiple ( List < String > paths ) {
ArrayList < Uri > uris = new ArrayList < > ( ) ;
for ( String path : paths ) {
File file = new File ( path ) ;
uris . add ( FileProvider . getUriForFile ( this , getApplicationContext ( ) . getPackageName ( ) + " .screenshot_provider " , file ) ) ;
}
if ( uris . size ( ) > 0 ) {
final Intent intent = new Intent ( Intent . ACTION_SEND_MULTIPLE ) ;
2018-04-25 15:06:02 +02:00
intent . setType ( " application/gpx+xml " ) ;
2018-04-04 21:38:27 +02:00
intent . putParcelableArrayListExtra ( Intent . EXTRA_STREAM , uris ) ;
startActivity ( Intent . createChooser ( intent , " SHARE " ) ) ;
} else {
GB . toast ( this , " No selected activity contains a GPX track to share " , Toast . LENGTH_SHORT , GB . ERROR ) ;
}
}
2020-08-11 21:54:02 +02:00
2017-10-19 21:52:38 +02:00
}