1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-08-24 16:21:16 +02:00

SwipingView of Sports Activity Detail (#1977)

Swipable view done

WIP. Basic POC.

Co-authored-by: vanous <petr@linuks.cz>
Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/1977
This commit is contained in:
vanous 2020-08-14 22:48:03 +02:00 committed by Andreas Shimokawa
parent 6e62e26331
commit ed5b4ac793
8 changed files with 243 additions and 45 deletions

View File

@ -72,6 +72,7 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
private GBDevice mGBDevice;
private SwipeRefreshLayout swipeLayout;
LinkedHashMap<String , Integer> activityKindMap = new LinkedHashMap<>(1);
int activityFilter=0;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@ -130,7 +131,7 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
super.onCreate(savedInstanceState);
int activityFilter=0;
setItemAdapter(new ActivitySummariesAdapter(this, mGBDevice,activityFilter));
getItemListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@ -140,7 +141,7 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
if (item != null) {
ActivitySummary summary = (ActivitySummary) item;
try {
showActivityDetail(summary);
showActivityDetail(position);
} 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);
}
@ -267,7 +268,8 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
public class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
setActivityKindFilter(activityKindMap.get(parent.getItemAtPosition(pos)));
activityFilter=activityKindMap.get(parent.getItemAtPosition(pos));
setActivityKindFilter(activityFilter);
refresh();
}
@ -317,16 +319,13 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
refresh();
}
private void showActivityDetail(ActivitySummary summary){
private void showActivityDetail(int position){
Intent ActivitySummaryDetailIntent = new Intent(this, ActivitySummaryDetail.class);
ActivitySummaryDetailIntent.putExtra("name", summary.getName());
ActivitySummaryDetailIntent.putExtra("ActivityKind", summary.getActivityKind());
ActivitySummaryDetailIntent.putExtra("StartTime", summary.getStartTime());
ActivitySummaryDetailIntent.putExtra("EndTime", summary.getEndTime());
ActivitySummaryDetailIntent.putExtra("GpxTrack", summary.getGpxTrack());
ActivitySummaryDetailIntent.putExtra("SummaryData", summary.getSummaryData());
ActivitySummaryDetailIntent.putExtra("position", position);
ActivitySummaryDetailIntent.putExtra("filter", activityFilter);
ActivitySummaryDetailIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice);
startActivity(ActivitySummaryDetailIntent);
}
private void fetchTrackData() {

View File

@ -17,14 +17,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
@ -43,26 +47,94 @@ import java.util.Iterator;
import java.util.concurrent.TimeUnit;
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.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.SwipeEvents;
public class ActivitySummaryDetail extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(ActivitySummaryDetail.class);
private GBDevice mGBDevice;
private JSONObject groupData = setGroups();
private boolean show_raw_data = false;
BaseActivitySummary currentItem = null;
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_summary_details);
Intent intent = getIntent();
mGBDevice = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
final int filter = intent.getIntExtra("filter",0);
final int position = intent.getIntExtra("position",0);
final ActivitySummaryItems items = new ActivitySummaryItems(this, mGBDevice, filter);
final RelativeLayout layout = findViewById(R.id.activity_summary_detail_relative_layout);
final String gpxTrack = intent.getStringExtra("GpxTrack");
final Animation animFadein;
final Animation animFadeout;
animFadein = AnimationUtils.loadAnimation(
this,
R.anim.flyright);
animFadeout = AnimationUtils.loadAnimation(
this,
R.anim.flyleft);
layout.setOnTouchListener(new SwipeEvents(this) {
@Override
public void onSwipeRight() {
currentItem = items.getNextItem();
if (currentItem != null) {
makeSummaryHeader(currentItem);
makeSummaryContent(currentItem);
layout.startAnimation(animFadein);
}else{
GB.toast("No more items", Toast.LENGTH_SHORT,0);
}
}
@Override
public void onSwipeLeft() {
currentItem = items.getPrevItem();
if (currentItem != null) {
makeSummaryHeader(currentItem);
makeSummaryContent(currentItem);
layout.startAnimation(animFadeout);
}else{
GB.toast("No more items", Toast.LENGTH_SHORT,0);
}
}
});
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 = (ImageView) 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;
}
});
}
private void makeSummaryHeader(BaseActivitySummary item){
//make view of data from main part of item
final String gpxTrack = item.getGpxTrack();
Button show_track_btn = (Button) findViewById(R.id.showTrack);
show_track_btn.setVisibility(View.GONE);
@ -78,15 +150,16 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
}
});
}
String activitykindname = ActivityKind.asString(intent.getIntExtra("ActivityKind",0), getApplicationContext());
Date starttime = (Date) intent.getSerializableExtra("StartTime");
Date endtime = (Date) intent.getSerializableExtra("EndTime");
String activitykindname = ActivityKind.asString(item.getActivityKind(), getApplicationContext());
Date starttime = (Date) item.getStartTime();
Date endtime = (Date) 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 = (ImageView) findViewById(R.id.item_image);
activity_icon.setImageResource(ActivityKind.getIconId(intent.getIntExtra("ActivityKind",0)));
activity_icon.setImageResource(ActivityKind.getIconId(item.getActivityKind()));
TextView activity_kind = (TextView) findViewById(R.id.activitykind);
activity_kind.setText(activitykindname);
TextView start_time = (TextView) findViewById(R.id.starttime);
@ -96,37 +169,32 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
TextView activity_duration = (TextView) findViewById(R.id.duration);
activity_duration.setText(durationhms);
JSONObject summaryData = null;
String sumData = intent.getStringExtra("SummaryData");
}
private void makeSummaryContent (BaseActivitySummary item){
//make view of data from summaryData of item
TableLayout fieldLayout = findViewById(R.id.summaryDetails);
fieldLayout.removeAllViews(); //remove old widgets
JSONObject summarySubdata = null;
JSONObject data = null;
String sumData = item.getSummaryData();
if (sumData != null) {
try {
summaryData = new JSONObject(sumData);
summarySubdata = new JSONObject(sumData);
} catch (JSONException e) {
LOG.error("SportsActivity", e);
}
}
if (summaryData == null) return;
if (summarySubdata == null) return;
data = makeSummaryList(summarySubdata); //make new list, grouped by groups
JSONObject listOfSummaries = makeSummaryList(summaryData);
makeSummaryContent(listOfSummaries);
if (data == null) return;
final JSONObject finalSummaryData = summaryData;
activity_icon.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {
show_raw_data=!show_raw_data;
TableLayout fieldLayout = findViewById(R.id.summaryDetails);
fieldLayout.removeAllViews();
JSONObject listOfSummaries = makeSummaryList(finalSummaryData);
makeSummaryContent(listOfSummaries);
return false;
}
});
}
private void makeSummaryContent (JSONObject data){
//build view, use localized names
Iterator<String> keys = data.keys();
DecimalFormat df = new DecimalFormat("#.##");
@ -136,7 +204,6 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
LOG.error("SportsActivity:" + key + ": " + data.get(key) + "\n");
JSONArray innerList = (JSONArray) data.get(key);
TableLayout fieldLayout = findViewById(R.id.summaryDetails);
TableRow label_row = new TableRow(ActivitySummaryDetail.this);
TextView label_field = new TextView(ActivitySummaryDetail.this);
label_field.setTextSize(16);

View File

@ -27,6 +27,7 @@ import java.util.Date;
* // TODO: split into separate entities?
*/
public interface ActivitySummary extends Serializable {
Long getId();
String getName();
Date getStartTime();
Date getEndTime();

View File

@ -0,0 +1,57 @@
package nodomain.freeyourgadget.gadgetbridge.model;
import android.content.Context;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.adapter.ActivitySummariesAdapter;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public class ActivitySummaryItems {
private final GBDevice device;
private int activityKindFilter;
List<BaseActivitySummary> allItems;
ActivitySummariesAdapter itemsAdapter;
private int current_position = 0;
public ActivitySummaryItems(Context context, GBDevice device, int activityKindFilter) {
this.device = device;
this.activityKindFilter = activityKindFilter;
this.itemsAdapter = new ActivitySummariesAdapter(context, device, activityKindFilter);
}
public BaseActivitySummary getItem(int position){
current_position=position;
return itemsAdapter.getItem(position);
}
public int getPosition(BaseActivitySummary item){
return itemsAdapter.getPosition(item);
}
public List<BaseActivitySummary> getAllItems(){
return itemsAdapter.getItems();
}
public BaseActivitySummary getNextItem(){
if (current_position+1 < itemsAdapter.getCount()){
current_position+=1;
return itemsAdapter.getItem(current_position);
}
return null;
}
public BaseActivitySummary getPrevItem(){
if (current_position-1 >= 0){
current_position-=1;
return itemsAdapter.getItem(current_position);
}
return null;
}
public int getCurrent_position(){
return current_position;
}
}

View File

@ -0,0 +1,50 @@
package nodomain.freeyourgadget.gadgetbridge.util;
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
//simple swipe detector based on GestureDetector, inspired by https://stackoverflow.com/a/19506010
public class SwipeEvents implements View.OnTouchListener {
private final GestureDetector gestureDetector;
public SwipeEvents(Context context) {
gestureDetector = new GestureDetector(context, new GestureListener());
}
public void onSwipeLeft() {
}
public void onSwipeRight() {
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_DISTANCE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float distanceX = e2.getX() - e1.getX();
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 (distanceX > 0)
onSwipeRight();
else
onSwipeLeft();
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="150" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="150" />
</set>

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activity_summary_detail_relative_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="?android:attr/activatedBackgroundIndicator"
android:clickable="true"
android:focusable="auto"
android:padding="8dp">
<ImageView
@ -13,8 +16,8 @@
android:contentDescription="activity image" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/item_image"
android:orientation="vertical"
@ -32,9 +35,9 @@
android:textSize="18sp" />
<TableLayout
android:id="@+id/summaryDetails"
android:id="@+id/summaryHeader"
android:layout_width="match_parent"
android:layout_height="match_parent" >
android:layout_height="wrap_content">
<TableRow
android:layout_width="match_parent"
@ -79,7 +82,7 @@
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent" >
android:layout_height="match_parent">
<TextView
android:id="@+id/duration_label"
@ -99,6 +102,11 @@
</TableLayout>
<TableLayout
android:id="@+id/summaryDetails"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/showTrack"