mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-09-01 03:55:47 +02:00
Add nested tabs to sleep and steps + steps refactor
This commit is contained in:
parent
b01169a307
commit
4ea933b53d
@ -122,7 +122,7 @@ public class GBApplication extends Application {
|
|||||||
private static SharedPreferences sharedPrefs;
|
private static SharedPreferences sharedPrefs;
|
||||||
private static final String PREFS_VERSION = "shared_preferences_version";
|
private static final String PREFS_VERSION = "shared_preferences_version";
|
||||||
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version
|
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version
|
||||||
private static final int CURRENT_PREFS_VERSION = 32;
|
private static final int CURRENT_PREFS_VERSION = 33;
|
||||||
|
|
||||||
private static final LimitedQueue<Integer, String> mIDSenderLookup = new LimitedQueue<>(16);
|
private static final LimitedQueue<Integer, String> mIDSenderLookup = new LimitedQueue<>(16);
|
||||||
private static GBPrefs prefs;
|
private static GBPrefs prefs;
|
||||||
@ -1514,7 +1514,7 @@ public class GBApplication extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldVersion < 32) {
|
if (oldVersion < 32) {
|
||||||
// Add the new HRV Status tab to all devices
|
// Add the new body energy tab to all devices
|
||||||
try (DBHandler db = acquireDB()) {
|
try (DBHandler db = acquireDB()) {
|
||||||
final DaoSession daoSession = db.getDaoSession();
|
final DaoSession daoSession = db.getDaoSession();
|
||||||
final List<Device> activeDevices = DBHelper.getActiveDevices(daoSession);
|
final List<Device> activeDevices = DBHelper.getActiveDevices(daoSession);
|
||||||
@ -1543,6 +1543,36 @@ public class GBApplication extends Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 33) {
|
||||||
|
// Remove sleep week tab from all devices, since it does not exist anymore
|
||||||
|
try (DBHandler db = acquireDB()) {
|
||||||
|
final DaoSession daoSession = db.getDaoSession();
|
||||||
|
final List<Device> activeDevices = DBHelper.getActiveDevices(daoSession);
|
||||||
|
|
||||||
|
for (final Device dbDevice : activeDevices) {
|
||||||
|
final SharedPreferences deviceSharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier());
|
||||||
|
|
||||||
|
final String chartsTabsValue = deviceSharedPrefs.getString("charts_tabs", null);
|
||||||
|
if (chartsTabsValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String newPrefValue;
|
||||||
|
if (!StringUtils.isBlank(chartsTabsValue)) {
|
||||||
|
newPrefValue = chartsTabsValue.replace(",sleepweek", "");
|
||||||
|
} else {
|
||||||
|
newPrefValue = chartsTabsValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SharedPreferences.Editor deviceSharedPrefsEdit = deviceSharedPrefs.edit();
|
||||||
|
deviceSharedPrefsEdit.putString("charts_tabs", newPrefValue);
|
||||||
|
deviceSharedPrefsEdit.apply();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "error acquiring DB lock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
|
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ public abstract class AbstractGBFragment extends Fragment {
|
|||||||
* @see #isVisibleInActivity()
|
* @see #isVisibleInActivity()
|
||||||
* @see #onMadeVisibleInActivity()
|
* @see #onMadeVisibleInActivity()
|
||||||
*/
|
*/
|
||||||
protected void onMadeInvisibleInActivity() {
|
public void onMadeInvisibleInActivity() {
|
||||||
mVisibleInActivity = false;
|
mVisibleInActivity = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,15 +20,11 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
|
||||||
|
|
||||||
import com.github.mikephil.charting.charts.BarChart;
|
import com.github.mikephil.charting.charts.BarChart;
|
||||||
import com.github.mikephil.charting.charts.PieChart;
|
import com.github.mikephil.charting.charts.PieChart;
|
||||||
import com.github.mikephil.charting.components.LimitLine;
|
import com.github.mikephil.charting.components.LimitLine;
|
||||||
@ -62,7 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
|||||||
|
|
||||||
public abstract class AbstractWeekChartFragment extends AbstractActivityChartFragment<AbstractWeekChartFragment.MyChartsData> {
|
public abstract class AbstractWeekChartFragment extends AbstractActivityChartFragment<AbstractWeekChartFragment.MyChartsData> {
|
||||||
protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class);
|
protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class);
|
||||||
protected final int TOTAL_DAYS = getRangeDays();
|
protected int TOTAL_DAYS = getRangeDays();
|
||||||
protected int TOTAL_DAYS_FOR_AVERAGE = 0;
|
protected int TOTAL_DAYS_FOR_AVERAGE = 0;
|
||||||
|
|
||||||
protected Locale mLocale;
|
protected Locale mLocale;
|
||||||
@ -73,7 +69,6 @@ public abstract class AbstractWeekChartFragment extends AbstractActivityChartFra
|
|||||||
protected TextView mBalanceView;
|
protected TextView mBalanceView;
|
||||||
|
|
||||||
private int mOffsetHours = getOffsetHours();
|
private int mOffsetHours = getOffsetHours();
|
||||||
ImageView stepsStreaksButton;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MyChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
protected MyChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||||
@ -101,20 +96,6 @@ public abstract class AbstractWeekChartFragment extends AbstractActivityChartFra
|
|||||||
mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter());
|
mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter());
|
||||||
|
|
||||||
mBalanceView.setText(mcd.getWeekBeforeData().getBalanceMessage());
|
mBalanceView.setText(mcd.getWeekBeforeData().getBalanceMessage());
|
||||||
|
|
||||||
//disable the streak FAB once we move away from today
|
|
||||||
Calendar day = Calendar.getInstance();
|
|
||||||
day.setTime(getChartsHost().getEndDate());
|
|
||||||
if (DateUtils.isToday(day.getTimeInMillis()) && enableStepStreaksButton()){
|
|
||||||
stepsStreaksButton.setVisibility(View.VISIBLE);
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
stepsStreaksButton.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean enableStepStreaksButton(){
|
|
||||||
return this.getClass().getSimpleName().equals("WeekStepsChartFragment");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -125,7 +106,7 @@ public abstract class AbstractWeekChartFragment extends AbstractActivityChartFra
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String getWeeksChartsLabel(Calendar day){
|
protected String getWeeksChartsLabel(Calendar day){
|
||||||
if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
|
if (TOTAL_DAYS > 7) {
|
||||||
//month, show day date
|
//month, show day date
|
||||||
return String.valueOf(day.get(Calendar.DAY_OF_MONTH));
|
return String.valueOf(day.get(Calendar.DAY_OF_MONTH));
|
||||||
}
|
}
|
||||||
@ -253,18 +234,6 @@ public abstract class AbstractWeekChartFragment extends AbstractActivityChartFra
|
|||||||
setupWeekChart();
|
setupWeekChart();
|
||||||
setupTodayPieChart();
|
setupTodayPieChart();
|
||||||
|
|
||||||
stepsStreaksButton = rootView.findViewById(R.id.steps_streaks_button);
|
|
||||||
if (enableStepStreaksButton()) {
|
|
||||||
stepsStreaksButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
FragmentManager fm = getActivity().getSupportFragmentManager();
|
|
||||||
StepStreaksDashboard stepStreaksDashboard = StepStreaksDashboard.newInstance(getGoal(), getChartsHost().getDevice());
|
|
||||||
stepStreaksDashboard.show(fm, "steps_streaks_dashboard");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// refresh immediately instead of use refreshIfVisible(), for perceived performance
|
// refresh immediately instead of use refreshIfVisible(), for perceived performance
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
|
@ -85,7 +85,6 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
}
|
}
|
||||||
if (!coordinator.supportsSleepMeasurement()) {
|
if (!coordinator.supportsSleepMeasurement()) {
|
||||||
tabList.remove("sleep");
|
tabList.remove("sleep");
|
||||||
tabList.remove("sleepweek");
|
|
||||||
}
|
}
|
||||||
if (!coordinator.supportsStressMeasurement()) {
|
if (!coordinator.supportsStressMeasurement()) {
|
||||||
tabList.remove("stress");
|
tabList.remove("stress");
|
||||||
@ -143,9 +142,7 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
case "activitylist":
|
case "activitylist":
|
||||||
return new ActivityListingChartFragment();
|
return new ActivityListingChartFragment();
|
||||||
case "sleep":
|
case "sleep":
|
||||||
return new SleepChartFragment();
|
return new SleepCollectionFragment();
|
||||||
case "sleepweek":
|
|
||||||
return new WeekSleepChartFragment();
|
|
||||||
case "hrvstatus":
|
case "hrvstatus":
|
||||||
return new HRVStatusFragment();
|
return new HRVStatusFragment();
|
||||||
case "bodyenergy":
|
case "bodyenergy":
|
||||||
@ -155,7 +152,7 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
case "pai":
|
case "pai":
|
||||||
return new PaiChartFragment();
|
return new PaiChartFragment();
|
||||||
case "stepsweek":
|
case "stepsweek":
|
||||||
return new WeekStepsChartFragment();
|
return new StepsCollectionFragment();
|
||||||
case "speedzones":
|
case "speedzones":
|
||||||
return new SpeedZonesFragment();
|
return new SpeedZonesFragment();
|
||||||
case "livestats":
|
case "livestats":
|
||||||
@ -183,14 +180,6 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStepsTitle() {
|
|
||||||
if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
|
|
||||||
return getString(R.string.weekstepschart_steps_a_month);
|
|
||||||
} else {
|
|
||||||
return getString(R.string.weekstepschart_steps_a_week);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
switch (enabledTabsList.get(position)) {
|
switch (enabledTabsList.get(position)) {
|
||||||
@ -200,8 +189,6 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
return getString(R.string.charts_activity_list);
|
return getString(R.string.charts_activity_list);
|
||||||
case "sleep":
|
case "sleep":
|
||||||
return getString(R.string.sleepchart_your_sleep);
|
return getString(R.string.sleepchart_your_sleep);
|
||||||
case "sleepweek":
|
|
||||||
return getSleepTitle();
|
|
||||||
case "hrvstatus":
|
case "hrvstatus":
|
||||||
return getString(R.string.pref_header_hrv_status);
|
return getString(R.string.pref_header_hrv_status);
|
||||||
case "bodyenergy":
|
case "bodyenergy":
|
||||||
@ -211,7 +198,7 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
case "pai":
|
case "pai":
|
||||||
return getString(getDevice().getDeviceCoordinator().getPaiName());
|
return getString(getDevice().getDeviceCoordinator().getPaiName());
|
||||||
case "stepsweek":
|
case "stepsweek":
|
||||||
return getStepsTitle();
|
return getString(R.string.steps);
|
||||||
case "speedzones":
|
case "speedzones":
|
||||||
return getString(R.string.stats_title);
|
return getString(R.string.stats_title);
|
||||||
case "livestats":
|
case "livestats":
|
||||||
|
@ -30,7 +30,6 @@ public class AngledLabelsChartRenderer extends BarChartRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawValue(Canvas canvas, String valueText, float x, float y, int color) {
|
public void drawValue(Canvas canvas, String valueText, float x, float y, int color) {
|
||||||
|
|
||||||
mValuePaint.setColor(color);
|
mValuePaint.setColor(color);
|
||||||
//move position to the center of bar
|
//move position to the center of bar
|
||||||
x = x + 8;
|
x = x + 8;
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toCollection;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -382,7 +382,7 @@ public class LiveActivityFragment extends AbstractActivityChartFragment<ChartsDa
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMadeInvisibleInActivity() {
|
public void onMadeInvisibleInActivity() {
|
||||||
enableRealtimeTracking(false);
|
enableRealtimeTracking(false);
|
||||||
super.onMadeInvisibleInActivity();
|
super.onMadeInvisibleInActivity();
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,9 @@ import android.text.method.ScrollingMovementMethod;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
|
||||||
|
|
||||||
import com.github.mikephil.charting.animation.Easing;
|
import com.github.mikephil.charting.animation.Easing;
|
||||||
import com.github.mikephil.charting.charts.Chart;
|
import com.github.mikephil.charting.charts.Chart;
|
||||||
import com.github.mikephil.charting.charts.LineChart;
|
import com.github.mikephil.charting.charts.LineChart;
|
||||||
@ -41,7 +38,6 @@ import com.github.mikephil.charting.data.LineData;
|
|||||||
import com.github.mikephil.charting.data.PieData;
|
import com.github.mikephil.charting.data.PieData;
|
||||||
import com.github.mikephil.charting.data.PieDataSet;
|
import com.github.mikephil.charting.data.PieDataSet;
|
||||||
import com.github.mikephil.charting.data.PieEntry;
|
import com.github.mikephil.charting.data.PieEntry;
|
||||||
import com.github.mikephil.charting.formatter.ValueFormatter;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Triple;
|
import org.apache.commons.lang3.tuple.Triple;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -239,7 +235,7 @@ public class SleepChartFragment extends AbstractActivityChartFragment<SleepChart
|
|||||||
highestHrText.setText(String.valueOf(heartRateMax != 0 ? heartRateMax : "-"));
|
highestHrText.setText(String.valueOf(heartRateMax != 0 ? heartRateMax : "-"));
|
||||||
movementIntensityText.setText(intensityTotal != 0 ? new DecimalFormat("###.#").format(intensityTotal) : "-");
|
movementIntensityText.setText(intensityTotal != 0 ? new DecimalFormat("###.#").format(intensityTotal) : "-");
|
||||||
|
|
||||||
mSleepAmountChart.setHoleRadius(75);
|
mSleepAmountChart.setHoleRadius(85);
|
||||||
mSleepAmountChart.setDrawEntryLabels(false);
|
mSleepAmountChart.setDrawEntryLabels(false);
|
||||||
mSleepAmountChart.getLegend().setEnabled(false);
|
mSleepAmountChart.getLegend().setEnabled(false);
|
||||||
|
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.adapter.SleepFragmentAdapter;
|
||||||
|
|
||||||
|
public class SleepCollectionFragment extends AbstractGBFragment {
|
||||||
|
protected SleepFragmentAdapter nestedFragmentsAdapter;
|
||||||
|
protected ViewPager2 viewPager;
|
||||||
|
private int last_position = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMadeVisibleInActivity() {
|
||||||
|
super.onMadeVisibleInActivity();
|
||||||
|
nestedFragmentsAdapter.updateFragments(last_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMadeInvisibleInActivity() {
|
||||||
|
if (nestedFragmentsAdapter != null) {
|
||||||
|
nestedFragmentsAdapter.updateFragments(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.fragment_nested_tabs, container, false);
|
||||||
|
nestedFragmentsAdapter = new SleepFragmentAdapter(this, getChildFragmentManager());
|
||||||
|
viewPager = rootView.findViewById(R.id.pager);
|
||||||
|
viewPager.setAdapter(nestedFragmentsAdapter);
|
||||||
|
viewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
|
||||||
|
viewPager.setUserInputEnabled(false);
|
||||||
|
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
super.onPageSelected(position);
|
||||||
|
last_position = position;
|
||||||
|
viewPager.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isVisibleInActivity()) {
|
||||||
|
nestedFragmentsAdapter.updateFragments(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
TabLayout tabLayout = view.findViewById(R.id.tab_layout);
|
||||||
|
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
tab.setText(getString(R.string.calendar_day));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
tab.setText(getString(R.string.calendar_week));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tab.setText(getString(R.string.calendar_month));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}).attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected CharSequence getTitle() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.adapter.StepsFragmentAdapter;
|
||||||
|
|
||||||
|
public class StepsCollectionFragment extends AbstractGBFragment {
|
||||||
|
protected StepsFragmentAdapter nestedFragmentsAdapter;
|
||||||
|
protected ViewPager2 viewPager;
|
||||||
|
private int last_position = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMadeVisibleInActivity() {
|
||||||
|
super.onMadeVisibleInActivity();
|
||||||
|
nestedFragmentsAdapter.updateFragments(last_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMadeInvisibleInActivity() {
|
||||||
|
if (nestedFragmentsAdapter != null) {
|
||||||
|
nestedFragmentsAdapter.updateFragments(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.fragment_nested_tabs, container, false);
|
||||||
|
nestedFragmentsAdapter = new StepsFragmentAdapter(this, getChildFragmentManager());
|
||||||
|
viewPager = rootView.findViewById(R.id.pager);
|
||||||
|
viewPager.setAdapter(nestedFragmentsAdapter);
|
||||||
|
viewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
|
||||||
|
viewPager.setUserInputEnabled(false);
|
||||||
|
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
super.onPageSelected(position);
|
||||||
|
last_position = position;
|
||||||
|
viewPager.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isVisibleInActivity()) {
|
||||||
|
nestedFragmentsAdapter.updateFragments(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
TabLayout tabLayout = view.findViewById(R.id.tab_layout);
|
||||||
|
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
tab.setText(getString(R.string.calendar_day));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
tab.setText(getString(R.string.calendar_week));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tab.setText(getString(R.string.calendar_month));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}).attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected CharSequence getTitle() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.charts.Chart;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||||
|
|
||||||
|
public class StepsDailyFragment extends StepsFragment {
|
||||||
|
protected static final Logger LOG = LoggerFactory.getLogger(BodyEnergyFragment.class);
|
||||||
|
|
||||||
|
private TextView mDateView;
|
||||||
|
private ImageView stepsGauge;
|
||||||
|
private TextView steps;
|
||||||
|
private TextView distance;
|
||||||
|
ImageView stepsStreaksButton;
|
||||||
|
|
||||||
|
protected int STEPS_GOAL;
|
||||||
|
protected int TOTAL_DAYS = 1;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.fragment_steps, container, false);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
rootView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||||
|
getChartsHost().enableSwipeRefresh(scrollY == 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mDateView = rootView.findViewById(R.id.steps_date_view);
|
||||||
|
stepsGauge = rootView.findViewById(R.id.steps_gauge);
|
||||||
|
steps = rootView.findViewById(R.id.steps_count);
|
||||||
|
distance = rootView.findViewById(R.id.steps_distance);
|
||||||
|
STEPS_GOAL = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal);
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
stepsStreaksButton = rootView.findViewById(R.id.steps_streaks_button);
|
||||||
|
stepsStreaksButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
FragmentManager fm = getActivity().getSupportFragmentManager();
|
||||||
|
StepStreaksDashboard stepStreaksDashboard = StepStreaksDashboard.newInstance(STEPS_GOAL, getChartsHost().getDevice());
|
||||||
|
stepStreaksDashboard.show(fm, "steps_streaks_dashboard");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return getString(R.string.steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StepsDailyFragment.StepsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||||
|
Calendar day = Calendar.getInstance();
|
||||||
|
day.setTime(chartsHost.getEndDate());
|
||||||
|
String formattedDate = new SimpleDateFormat("E, MMM dd").format(chartsHost.getEndDate());
|
||||||
|
mDateView.setText(formattedDate);
|
||||||
|
List<StepsDay> stepsDaysData = getMyStepsDaysData(db, day, device);
|
||||||
|
return new StepsDailyFragment.StepsData(stepsDaysData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateChartsnUIThread(StepsDailyFragment.StepsData stepsData) {
|
||||||
|
final int width = (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
300,
|
||||||
|
GBApplication.getContext().getResources().getDisplayMetrics()
|
||||||
|
);
|
||||||
|
|
||||||
|
stepsGauge.setImageBitmap(drawGauge(
|
||||||
|
width,
|
||||||
|
width / 15,
|
||||||
|
getResources().getColor(R.color.steps_color),
|
||||||
|
(int) stepsData.todayStepsDay.steps,
|
||||||
|
STEPS_GOAL
|
||||||
|
));
|
||||||
|
|
||||||
|
steps.setText(String.format(String.valueOf(stepsData.todayStepsDay.steps)));
|
||||||
|
distance.setText(getString(R.string.steps_distance_unit, stepsData.todayStepsDay.distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderCharts() {}
|
||||||
|
|
||||||
|
protected void setupLegend(Chart<?> chart) {}
|
||||||
|
|
||||||
|
Bitmap drawGauge(int width, int barWidth, @ColorInt int filledColor, int value, int maxValue) {
|
||||||
|
int height = width;
|
||||||
|
int barMargin = (int) Math.ceil(barWidth / 2f);
|
||||||
|
float filledFactor = (float) value / maxValue;
|
||||||
|
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
paint.setStrokeWidth(barWidth);
|
||||||
|
paint.setColor(getResources().getColor(R.color.gauge_line_color));
|
||||||
|
canvas.drawArc(
|
||||||
|
barMargin,
|
||||||
|
barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
90,
|
||||||
|
360,
|
||||||
|
false,
|
||||||
|
paint);
|
||||||
|
paint.setStrokeWidth(barWidth);
|
||||||
|
paint.setColor(filledColor);
|
||||||
|
canvas.drawArc(
|
||||||
|
barMargin,
|
||||||
|
barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
height - barMargin,
|
||||||
|
270,
|
||||||
|
360 * filledFactor,
|
||||||
|
false,
|
||||||
|
paint
|
||||||
|
);
|
||||||
|
|
||||||
|
Paint textPaint = new Paint();
|
||||||
|
textPaint.setColor(TEXT_COLOR);
|
||||||
|
float textPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.06f, requireContext().getResources().getDisplayMetrics());
|
||||||
|
textPaint.setTextSize(textPixels);
|
||||||
|
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
int yPos = (int) ((float) height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2)) ;
|
||||||
|
canvas.drawText(String.valueOf(value), width / 2f, yPos, textPaint);
|
||||||
|
Paint textLowerPaint = new Paint();
|
||||||
|
textLowerPaint.setColor(TEXT_COLOR);
|
||||||
|
textLowerPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
float textLowerPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.025f, requireContext().getResources().getDisplayMetrics());
|
||||||
|
textLowerPaint.setTextSize(textLowerPixels);
|
||||||
|
int yPosLowerText = (int) ((float) height / 2 - textPaint.ascent()) ;
|
||||||
|
canvas.drawText(String.valueOf(maxValue), width / 2f, yPosLowerText, textLowerPaint);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
||||||
|
|
||||||
|
abstract class StepsFragment extends AbstractChartFragment<StepsDailyFragment.StepsData> {
|
||||||
|
protected static final Logger LOG = LoggerFactory.getLogger(StepsDailyFragment.class);
|
||||||
|
|
||||||
|
protected int CHART_TEXT_COLOR;
|
||||||
|
protected int TEXT_COLOR;
|
||||||
|
|
||||||
|
protected int BACKGROUND_COLOR;
|
||||||
|
protected int DESCRIPTION_COLOR;
|
||||||
|
protected int TOTAL_DAYS = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return getString(R.string.steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
TEXT_COLOR = GBApplication.getTextColor(requireContext());
|
||||||
|
CHART_TEXT_COLOR = GBApplication.getSecondaryTextColor(requireContext());
|
||||||
|
BACKGROUND_COLOR = GBApplication.getBackgroundColor(getContext());
|
||||||
|
DESCRIPTION_COLOR = GBApplication.getTextColor(getContext());
|
||||||
|
CHART_TEXT_COLOR = GBApplication.getSecondaryTextColor(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<StepsFragment.StepsDay> getMyStepsDaysData(DBHandler db, Calendar day, GBDevice device) {
|
||||||
|
day = (Calendar) day.clone(); // do not modify the caller's argument
|
||||||
|
day.add(Calendar.DATE, -TOTAL_DAYS + 1);
|
||||||
|
|
||||||
|
List<StepsDay> daysData = new ArrayList<>();;
|
||||||
|
for (int counter = 0; counter < TOTAL_DAYS; counter++) {
|
||||||
|
long totalSteps = 0;
|
||||||
|
ActivityAmounts amounts = getActivityAmountsForDay(db, day, device);
|
||||||
|
for (ActivityAmount amount : amounts.getAmounts()) {
|
||||||
|
if (amount.getTotalSteps() > 0) {
|
||||||
|
totalSteps += amount.getTotalSteps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double distance = 0;
|
||||||
|
if (totalSteps > 0) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
int stepLength = activityUser.getStepLengthCm();
|
||||||
|
distance = ((stepLength * 1.0 / 100) * totalSteps) / 1000;
|
||||||
|
}
|
||||||
|
Calendar d = (Calendar) day.clone();
|
||||||
|
daysData.add(new StepsDay(d, totalSteps, distance));
|
||||||
|
day.add(Calendar.DATE, 1);
|
||||||
|
}
|
||||||
|
return daysData;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ActivityAmounts getActivityAmountsForDay(DBHandler db, Calendar day, GBDevice device) {
|
||||||
|
LimitedQueue<Integer, ActivityAmounts> activityAmountCache = null;
|
||||||
|
ActivityAmounts amounts = null;
|
||||||
|
|
||||||
|
Activity activity = getActivity();
|
||||||
|
int key = (int) (day.getTimeInMillis() / 1000);
|
||||||
|
if (activity != null) {
|
||||||
|
activityAmountCache = ((ActivityChartsActivity) activity).mActivityAmountCache;
|
||||||
|
amounts = activityAmountCache.lookup(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amounts == null) {
|
||||||
|
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||||
|
amounts = analysis.calculateActivityAmounts(getSamplesOfDay(db, day, 0, device));
|
||||||
|
if (activityAmountCache != null) {
|
||||||
|
activityAmountCache.add(key, amounts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return amounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) {
|
||||||
|
int startTs;
|
||||||
|
int endTs;
|
||||||
|
|
||||||
|
day = (Calendar) day.clone(); // do not modify the caller's argument
|
||||||
|
day.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
day.set(Calendar.MINUTE, 0);
|
||||||
|
day.set(Calendar.SECOND, 0);
|
||||||
|
day.add(Calendar.HOUR, offsetHours);
|
||||||
|
|
||||||
|
startTs = (int) (day.getTimeInMillis() / 1000);
|
||||||
|
endTs = startTs + 24 * 60 * 60 - 1;
|
||||||
|
|
||||||
|
return getSamples(db, device, startTs, endTs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<? extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||||
|
SampleProvider<? extends ActivitySample> provider = device.getDeviceCoordinator().getSampleProvider(device, db.getDaoSession());
|
||||||
|
return provider.getAllActivitySamples(tsFrom, tsTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class StepsDay {
|
||||||
|
public long steps;
|
||||||
|
public double distance;
|
||||||
|
public Calendar day;
|
||||||
|
|
||||||
|
protected StepsDay(Calendar day, long steps, double distance) {
|
||||||
|
this.steps = steps;
|
||||||
|
this.distance = distance;
|
||||||
|
this.day = day;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class StepsData extends ChartsData {
|
||||||
|
List<StepsDay> days;
|
||||||
|
long stepsDailyAvg = 0;
|
||||||
|
double distanceDailyAvg = 0;
|
||||||
|
long totalSteps = 0;
|
||||||
|
double totalDistance = 0;
|
||||||
|
StepsDay todayStepsDay;
|
||||||
|
protected StepsData(List<StepsDay> days) {
|
||||||
|
this.days = days;
|
||||||
|
int daysCounter = 0;
|
||||||
|
for(StepsDay day : days) {
|
||||||
|
this.totalSteps += day.steps;
|
||||||
|
this.totalDistance += day.distance;
|
||||||
|
if (day.steps > 0) {
|
||||||
|
daysCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (daysCounter > 0) {
|
||||||
|
this.stepsDailyAvg = this.totalSteps / daysCounter;
|
||||||
|
this.distanceDailyAvg = this.totalDistance / daysCounter;
|
||||||
|
}
|
||||||
|
this.todayStepsDay = days.get(days.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,200 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.charts.BarChart;
|
||||||
|
import com.github.mikephil.charting.charts.Chart;
|
||||||
|
import com.github.mikephil.charting.components.LimitLine;
|
||||||
|
import com.github.mikephil.charting.components.XAxis;
|
||||||
|
import com.github.mikephil.charting.components.YAxis;
|
||||||
|
import com.github.mikephil.charting.data.BarData;
|
||||||
|
import com.github.mikephil.charting.data.BarDataSet;
|
||||||
|
import com.github.mikephil.charting.data.BarEntry;
|
||||||
|
import com.github.mikephil.charting.formatter.ValueFormatter;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.wena3.protocol.packets.settings.DayStartHourSetting;
|
||||||
|
|
||||||
|
public class StepsPeriodFragment extends StepsFragment {
|
||||||
|
protected static final Logger LOG = LoggerFactory.getLogger(BodyEnergyFragment.class);
|
||||||
|
|
||||||
|
private TextView mDateView;
|
||||||
|
private TextView stepsAvg;
|
||||||
|
private TextView stepsTotal;
|
||||||
|
private TextView distanceAvg;
|
||||||
|
private TextView distanceTotal;
|
||||||
|
private BarChart stepsChart;
|
||||||
|
|
||||||
|
protected int CHART_TEXT_COLOR;
|
||||||
|
protected int TEXT_COLOR;
|
||||||
|
protected int STEPS_GOAL;
|
||||||
|
|
||||||
|
protected int BACKGROUND_COLOR;
|
||||||
|
protected int DESCRIPTION_COLOR;
|
||||||
|
|
||||||
|
public static StepsPeriodFragment newInstance ( int totalDays ) {
|
||||||
|
StepsPeriodFragment fragmentFirst = new StepsPeriodFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("totalDays", totalDays);
|
||||||
|
fragmentFirst.setArguments(args);
|
||||||
|
return fragmentFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
TOTAL_DAYS = getArguments() != null ? getArguments().getInt("totalDays") : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.fragment_steps_period, container, false);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
rootView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||||
|
getChartsHost().enableSwipeRefresh(scrollY == 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mDateView = rootView.findViewById(R.id.steps_date_view);
|
||||||
|
stepsChart = rootView.findViewById(R.id.steps_chart);
|
||||||
|
stepsAvg = rootView.findViewById(R.id.steps_avg);
|
||||||
|
distanceAvg = rootView.findViewById(R.id.distance_avg);
|
||||||
|
stepsTotal = rootView.findViewById(R.id.steps_total);
|
||||||
|
distanceTotal = rootView.findViewById(R.id.distance_total);
|
||||||
|
STEPS_GOAL = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal);
|
||||||
|
setupStepsChart();
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupStepsChart() {
|
||||||
|
stepsChart.getDescription().setEnabled(false);
|
||||||
|
if (TOTAL_DAYS <= 7) {
|
||||||
|
stepsChart.setTouchEnabled(false);
|
||||||
|
stepsChart.setPinchZoom(false);
|
||||||
|
}
|
||||||
|
stepsChart.setDoubleTapToZoomEnabled(false);
|
||||||
|
stepsChart.getLegend().setEnabled(false);
|
||||||
|
|
||||||
|
final XAxis xAxisBottom = stepsChart.getXAxis();
|
||||||
|
xAxisBottom.setPosition(XAxis.XAxisPosition.BOTTOM);
|
||||||
|
xAxisBottom.setDrawLabels(true);
|
||||||
|
xAxisBottom.setDrawGridLines(false);
|
||||||
|
xAxisBottom.setEnabled(true);
|
||||||
|
xAxisBottom.setDrawLimitLinesBehindData(true);
|
||||||
|
xAxisBottom.setTextColor(CHART_TEXT_COLOR);
|
||||||
|
|
||||||
|
final YAxis yAxisLeft = stepsChart.getAxisLeft();
|
||||||
|
yAxisLeft.setDrawGridLines(true);
|
||||||
|
yAxisLeft.setDrawTopYLabelEntry(true);
|
||||||
|
yAxisLeft.setEnabled(true);
|
||||||
|
yAxisLeft.setTextColor(CHART_TEXT_COLOR);
|
||||||
|
yAxisLeft.setAxisMinimum(0f);
|
||||||
|
LimitLine target = new LimitLine(STEPS_GOAL);
|
||||||
|
yAxisLeft.addLimitLine(target);
|
||||||
|
|
||||||
|
final YAxis yAxisRight = stepsChart.getAxisRight();
|
||||||
|
yAxisRight.setEnabled(true);
|
||||||
|
yAxisRight.setDrawLabels(false);
|
||||||
|
yAxisRight.setDrawGridLines(false);
|
||||||
|
yAxisRight.setDrawAxisLine(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return getString(R.string.steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
TEXT_COLOR = GBApplication.getTextColor(requireContext());
|
||||||
|
CHART_TEXT_COLOR = GBApplication.getSecondaryTextColor(requireContext());
|
||||||
|
BACKGROUND_COLOR = GBApplication.getBackgroundColor(getContext());
|
||||||
|
DESCRIPTION_COLOR = GBApplication.getTextColor(getContext());
|
||||||
|
CHART_TEXT_COLOR = GBApplication.getSecondaryTextColor(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StepsFragment.StepsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||||
|
Calendar day = Calendar.getInstance();
|
||||||
|
Date to = new Date((long) this.getTSEnd() * 1000);
|
||||||
|
Date from = DateUtils.addDays(to,-(TOTAL_DAYS - 1));
|
||||||
|
String toFormattedDate = new SimpleDateFormat("E, MMM dd").format(to);
|
||||||
|
String fromFormattedDate = new SimpleDateFormat("E, MMM dd").format(from);
|
||||||
|
mDateView.setText(fromFormattedDate + " - " + toFormattedDate);
|
||||||
|
day.setTime(to);
|
||||||
|
List<StepsDay> stepsDaysData = getMyStepsDaysData(db, day, device);
|
||||||
|
return new StepsFragment.StepsData(stepsDaysData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateChartsnUIThread(StepsPeriodFragment.StepsData stepsData) {
|
||||||
|
stepsChart.setData(null);
|
||||||
|
|
||||||
|
List<BarEntry> entries = new ArrayList<>();
|
||||||
|
int counter = 0;
|
||||||
|
for(StepsDay day : stepsData.days) {
|
||||||
|
entries.add(new BarEntry(counter, day.steps));
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
BarDataSet set = new BarDataSet(entries, "Steps");
|
||||||
|
set.setDrawValues(true);
|
||||||
|
set.setColors(getResources().getColor(R.color.steps_color));
|
||||||
|
final XAxis x = stepsChart.getXAxis();
|
||||||
|
x.setValueFormatter(getStepsChartDayValueFormatter(stepsData));
|
||||||
|
|
||||||
|
BarData barData = new BarData(set);
|
||||||
|
barData.setValueTextColor(Color.GRAY); //prevent tearing other graph elements with the black text. Another approach would be to hide the values cmpletely with data.setDrawValues(false);
|
||||||
|
barData.setValueTextSize(10f);
|
||||||
|
if (TOTAL_DAYS > 7) {
|
||||||
|
stepsChart.setRenderer(new AngledLabelsChartRenderer(stepsChart, stepsChart.getAnimator(), stepsChart.getViewPortHandler()));
|
||||||
|
}
|
||||||
|
stepsChart.setData(barData);
|
||||||
|
stepsAvg.setText(String.format(String.valueOf(stepsData.stepsDailyAvg)));
|
||||||
|
distanceAvg.setText(getString(R.string.steps_distance_unit, stepsData.distanceDailyAvg));
|
||||||
|
stepsTotal.setText(String.format(String.valueOf(stepsData.totalSteps)));
|
||||||
|
distanceTotal.setText(getString(R.string.steps_distance_unit, stepsData.totalDistance));
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueFormatter getStepsChartDayValueFormatter(StepsPeriodFragment.StepsData stepsData) {
|
||||||
|
return new ValueFormatter() {
|
||||||
|
@Override
|
||||||
|
public String getFormattedValue(float value) {
|
||||||
|
StepsPeriodFragment.StepsDay day = stepsData.days.get((int) value);
|
||||||
|
String pattern = TOTAL_DAYS > 7 ? "dd" : "EEE";
|
||||||
|
SimpleDateFormat formatLetterDay = new SimpleDateFormat(pattern, Locale.getDefault());
|
||||||
|
return formatLetterDay.format(new Date(day.day.getTimeInMillis()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderCharts() {
|
||||||
|
stepsChart.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupLegend(Chart<?> chart) {}
|
||||||
|
}
|
@ -23,6 +23,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.github.mikephil.charting.charts.Chart;
|
import com.github.mikephil.charting.charts.Chart;
|
||||||
import com.github.mikephil.charting.components.Legend;
|
import com.github.mikephil.charting.components.Legend;
|
||||||
import com.github.mikephil.charting.components.LegendEntry;
|
import com.github.mikephil.charting.components.LegendEntry;
|
||||||
@ -58,6 +59,20 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
|
|||||||
private TextView sleepDatesText;
|
private TextView sleepDatesText;
|
||||||
private MySleepWeeklyData mySleepWeeklyData;
|
private MySleepWeeklyData mySleepWeeklyData;
|
||||||
|
|
||||||
|
public static WeekSleepChartFragment newInstance ( int totalDays ) {
|
||||||
|
WeekSleepChartFragment fragmentFirst = new WeekSleepChartFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("totalDays", totalDays);
|
||||||
|
fragmentFirst.setArguments(args);
|
||||||
|
return fragmentFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
TOTAL_DAYS = getArguments() != null ? getArguments().getInt("totalDays") : 0;
|
||||||
|
}
|
||||||
|
|
||||||
private MySleepWeeklyData getMySleepWeeklyData(DBHandler db, Calendar day, GBDevice device) {
|
private MySleepWeeklyData getMySleepWeeklyData(DBHandler db, Calendar day, GBDevice device) {
|
||||||
day = (Calendar) day.clone(); // do not modify the caller's argument
|
day = (Calendar) day.clone(); // do not modify the caller's argument
|
||||||
day.add(Calendar.DATE, -TOTAL_DAYS + 1);
|
day.add(Calendar.DATE, -TOTAL_DAYS + 1);
|
||||||
@ -89,7 +104,6 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
|
|||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
mLocale = getResources().getConfiguration().locale;
|
mLocale = getResources().getConfiguration().locale;
|
||||||
View rootView = inflater.inflate(R.layout.fragment_weeksleep_chart, container, false);
|
View rootView = inflater.inflate(R.layout.fragment_weeksleep_chart, container, false);
|
||||||
|
|
||||||
final int goal = getGoal();
|
final int goal = getGoal();
|
||||||
if (goal >= 0) {
|
if (goal >= 0) {
|
||||||
mTargetValue = goal;
|
mTargetValue = goal;
|
||||||
@ -114,17 +128,17 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
|
|||||||
protected void updateChartsnUIThread(MyChartsData mcd) {
|
protected void updateChartsnUIThread(MyChartsData mcd) {
|
||||||
setupLegend(mWeekChart);
|
setupLegend(mWeekChart);
|
||||||
|
|
||||||
//set custom renderer for 30days bar charts
|
if (TOTAL_DAYS > 7) {
|
||||||
if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
|
|
||||||
mWeekChart.setRenderer(new AngledLabelsChartRenderer(mWeekChart, mWeekChart.getAnimator(), mWeekChart.getViewPortHandler()));
|
mWeekChart.setRenderer(new AngledLabelsChartRenderer(mWeekChart, mWeekChart.getAnimator(), mWeekChart.getViewPortHandler()));
|
||||||
|
} else {
|
||||||
|
mWeekChart.setScaleEnabled(false);
|
||||||
|
mWeekChart.setTouchEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
mWeekChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
|
mWeekChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
|
||||||
mWeekChart.setData(mcd.getWeekBeforeData().getData());
|
mWeekChart.setData(mcd.getWeekBeforeData().getData());
|
||||||
mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter());
|
mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter());
|
||||||
mWeekChart.getBarData().setValueTextSize(14f);
|
mWeekChart.getBarData().setValueTextSize(10f);
|
||||||
mWeekChart.setScaleEnabled(false);
|
|
||||||
mWeekChart.setTouchEnabled(false);
|
|
||||||
|
|
||||||
if (TOTAL_DAYS_FOR_AVERAGE > 0) {
|
if (TOTAL_DAYS_FOR_AVERAGE > 0) {
|
||||||
float avgDeep = Math.abs(this.mySleepWeeklyData.getTotalDeep() / TOTAL_DAYS_FOR_AVERAGE);
|
float avgDeep = Math.abs(this.mySleepWeeklyData.getTotalDeep() / TOTAL_DAYS_FOR_AVERAGE);
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
/* Copyright (C) 2015-2024 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
|
||||||
Gobbetti, José Rebelo, Pavel Elagin, Petr Vaněk
|
|
||||||
|
|
||||||
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 <https://www.gnu.org/licenses/>. */
|
|
||||||
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
|
||||||
|
|
||||||
import com.github.mikephil.charting.charts.Chart;
|
|
||||||
import com.github.mikephil.charting.formatter.ValueFormatter;
|
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
|
||||||
|
|
||||||
public class WeekStepsChartFragment extends AbstractWeekChartFragment {
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
|
|
||||||
return getString(R.string.weekstepschart_steps_a_month);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return getString(R.string.weekstepschart_steps_a_week);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String getPieDescription(int targetValue) {
|
|
||||||
return getString(R.string.weeksteps_today_steps_description, String.valueOf(targetValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
int getGoal() {
|
|
||||||
return GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
int getOffsetHours() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
float[] getTotalsForActivityAmounts(ActivityAmounts activityAmounts) {
|
|
||||||
long totalSteps = 0;
|
|
||||||
for (ActivityAmount amount : activityAmounts.getAmounts()) {
|
|
||||||
totalSteps += amount.getTotalSteps();
|
|
||||||
}
|
|
||||||
return new float[]{totalSteps};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long calculateBalance(ActivityAmounts activityAmounts) {
|
|
||||||
long balance = 0;
|
|
||||||
for (ActivityAmount amount : activityAmounts.getAmounts()) {
|
|
||||||
balance += amount.getTotalSteps();
|
|
||||||
}
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String formatPieValue(long value) {
|
|
||||||
return String.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String[] getPieLabels() {
|
|
||||||
return new String[]{""};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ValueFormatter getPieValueFormatter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ValueFormatter getBarValueFormatter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ValueFormatter getYAxisFormatter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
int[] getColors() {
|
|
||||||
return new int[]{akActivity.color};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setupLegend(Chart<?> chart) {
|
|
||||||
// no legend here, it is all about the steps here
|
|
||||||
chart.getLegend().setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getBalanceMessage(long balance, int targetValue) {
|
|
||||||
if (balance > 0) {
|
|
||||||
final long totalBalance = balance - ((long)targetValue * TOTAL_DAYS_FOR_AVERAGE);
|
|
||||||
if (totalBalance > 0)
|
|
||||||
return getString(R.string.overstep, Math.abs(totalBalance));
|
|
||||||
else
|
|
||||||
return getString(R.string.lack_of_step, Math.abs(totalBalance));
|
|
||||||
} else
|
|
||||||
return getString(R.string.no_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String getAverage(float value) {
|
|
||||||
return String.format("%.0f", value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,36 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
|
||||||
|
|
||||||
|
abstract class NestedFragmentAdapter extends FragmentStateAdapter {
|
||||||
|
protected FragmentManager fragmentManager;
|
||||||
|
|
||||||
|
public NestedFragmentAdapter(AbstractGBFragment fragment, FragmentManager childFragmentManager) {
|
||||||
|
super(fragment);
|
||||||
|
fragmentManager = childFragmentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateFragments(int position) {
|
||||||
|
List<AbstractGBFragment> fragments = fragmentManager.getFragments()
|
||||||
|
.stream()
|
||||||
|
.map(e -> (AbstractGBFragment) e)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (AbstractGBFragment fragment : fragments) {
|
||||||
|
if (position < 0 || fragment != fragmentManager.findFragmentByTag("f" + position)) {
|
||||||
|
fragment.onMadeInvisibleInActivity();
|
||||||
|
} else {
|
||||||
|
fragment.onMadeVisibleInActivityInternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.SleepChartFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.WeekSleepChartFragment;
|
||||||
|
|
||||||
|
public class SleepFragmentAdapter extends NestedFragmentAdapter {
|
||||||
|
public SleepFragmentAdapter(AbstractGBFragment fragment, FragmentManager childFragmentManager) {
|
||||||
|
super(fragment, childFragmentManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Fragment createFragment(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
return new SleepChartFragment();
|
||||||
|
case 1:
|
||||||
|
return WeekSleepChartFragment.newInstance(7);
|
||||||
|
case 2:
|
||||||
|
return WeekSleepChartFragment.newInstance(30);
|
||||||
|
}
|
||||||
|
return new SleepChartFragment();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.StepsDailyFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.StepsPeriodFragment;
|
||||||
|
|
||||||
|
public class StepsFragmentAdapter extends NestedFragmentAdapter {
|
||||||
|
protected FragmentManager fragmentManager;
|
||||||
|
|
||||||
|
public StepsFragmentAdapter(AbstractGBFragment fragment, FragmentManager childFragmentManager) {
|
||||||
|
super(fragment, childFragmentManager);
|
||||||
|
fragmentManager = childFragmentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Fragment createFragment(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
return new StepsDailyFragment();
|
||||||
|
case 1:
|
||||||
|
return StepsPeriodFragment.newInstance(7);
|
||||||
|
case 2:
|
||||||
|
return StepsPeriodFragment.newInstance(30);
|
||||||
|
}
|
||||||
|
return new StepsDailyFragment();
|
||||||
|
}
|
||||||
|
}
|
17
app/src/main/res/layout/fragment_nested_tabs.xml
Normal file
17
app/src/main/res/layout/fragment_nested_tabs.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tab_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
136
app/src/main/res/layout/fragment_steps.xml
Normal file
136
app/src/main/res/layout/fragment_steps.xml
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<ScrollView 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"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_date_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
/>
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:id="@+id/steps_streaks_button"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginTop="0dp"
|
||||||
|
android:layout_marginEnd="0dp"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:srcCompat="@drawable/ic_events" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="180dp"
|
||||||
|
android:layout_height="180dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:scaleType="fitStart"
|
||||||
|
android:id="@+id/steps_gauge" />
|
||||||
|
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:shrinkColumns="*"
|
||||||
|
android:stretchColumns="*">
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:weightSum="2">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:paddingTop="20dip"
|
||||||
|
android:paddingRight="20dip">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="5px"
|
||||||
|
android:background="@color/value_line_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:layout_marginTop="20dip"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:text="@string/steps"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:paddingTop="20dip"
|
||||||
|
android:paddingRight="20dip">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="5px"
|
||||||
|
android:background="@color/value_line_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_distance"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:layout_marginTop="20dip"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:text="@string/distance"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_chart_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:text=""
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
182
app/src/main/res/layout/fragment_steps_period.xml
Normal file
182
app/src/main/res/layout/fragment_steps_period.xml
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<ScrollView 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"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_date_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
/>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="250sp"
|
||||||
|
>
|
||||||
|
|
||||||
|
<com.github.mikephil.charting.charts.BarChart
|
||||||
|
android:id="@+id/steps_chart"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_weight="2" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:shrinkColumns="*"
|
||||||
|
android:stretchColumns="*">
|
||||||
|
<TableRow
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:weightSum="2">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:paddingTop="20dip"
|
||||||
|
android:paddingRight="20dip">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="5px"
|
||||||
|
android:background="@color/value_line_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_avg"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:layout_marginTop="20dip"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:text="@string/steps_avg"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:paddingTop="20dip"
|
||||||
|
android:paddingRight="20dip">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="5px"
|
||||||
|
android:background="@color/value_line_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_total"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:layout_marginTop="20dip"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:text="@string/steps_total"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:weightSum="2">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:paddingTop="20dip"
|
||||||
|
android:paddingRight="20dip">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="5px"
|
||||||
|
android:background="@color/value_line_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_avg"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:layout_marginTop="20dip"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:text="@string/distance_avg"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:paddingTop="20dip"
|
||||||
|
android:paddingRight="20dip">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="5px"
|
||||||
|
android:background="@color/value_line_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_total"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:layout_marginTop="20dip"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:text="@string/distance_total"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
@ -3015,7 +3015,6 @@
|
|||||||
<item>@string/activity_sleepchart_activity_and_sleep</item>
|
<item>@string/activity_sleepchart_activity_and_sleep</item>
|
||||||
<item>@string/charts_activity_list</item>
|
<item>@string/charts_activity_list</item>
|
||||||
<item>@string/sleepchart_your_sleep</item>
|
<item>@string/sleepchart_your_sleep</item>
|
||||||
<item>@string/weeksleepchart_sleep_a_week_or_month</item>
|
|
||||||
<item>@string/weekstepschart_steps_a_week_or_month</item>
|
<item>@string/weekstepschart_steps_a_week_or_month</item>
|
||||||
<item>@string/pref_header_hrv_status</item>
|
<item>@string/pref_header_hrv_status</item>
|
||||||
<item>@string/body_energy</item>
|
<item>@string/body_energy</item>
|
||||||
@ -3031,7 +3030,6 @@
|
|||||||
<item>@string/p_activity</item>
|
<item>@string/p_activity</item>
|
||||||
<item>@string/p_activity_list</item>
|
<item>@string/p_activity_list</item>
|
||||||
<item>@string/p_sleep</item>
|
<item>@string/p_sleep</item>
|
||||||
<item>@string/p_sleep_week</item>
|
|
||||||
<item>@string/p_steps_week</item>
|
<item>@string/p_steps_week</item>
|
||||||
<item>@string/p_hrv_status</item>
|
<item>@string/p_hrv_status</item>
|
||||||
<item>@string/p_body_energy</item>
|
<item>@string/p_body_energy</item>
|
||||||
@ -3048,7 +3046,6 @@
|
|||||||
<item>@string/p_activity</item>
|
<item>@string/p_activity</item>
|
||||||
<item>@string/p_activity_list</item>
|
<item>@string/p_activity_list</item>
|
||||||
<item>@string/p_sleep</item>
|
<item>@string/p_sleep</item>
|
||||||
<item>@string/p_sleep_week</item>
|
|
||||||
<item>@string/p_hrv_status</item>
|
<item>@string/p_hrv_status</item>
|
||||||
<item>@string/p_body_energy</item>
|
<item>@string/p_body_energy</item>
|
||||||
<item>@string/p_steps_week</item>
|
<item>@string/p_steps_week</item>
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
<color name="hrv_status_poor" type="color">#be03fc</color>
|
<color name="hrv_status_poor" type="color">#be03fc</color>
|
||||||
<color name="hrv_status_char_line_color" type="color">#d12a2a</color>
|
<color name="hrv_status_char_line_color" type="color">#d12a2a</color>
|
||||||
<color name="body_energy_level_color" type="color">#5ac234</color>
|
<color name="body_energy_level_color" type="color">#5ac234</color>
|
||||||
|
<color name="steps_color" type="color">#00c9bf</color>
|
||||||
|
|
||||||
<color name="value_line_color" type="color">#858585</color>
|
<color name="value_line_color" type="color">#858585</color>
|
||||||
<color name="gauge_line_color" type="color">#383838</color>
|
<color name="gauge_line_color" type="color">#383838</color>
|
||||||
|
@ -1576,6 +1576,7 @@
|
|||||||
<string name="hrv_status_unit">%1$d ms</string>
|
<string name="hrv_status_unit">%1$d ms</string>
|
||||||
<string name="hrv_status_baseline">%1$d-%2$d ms</string>
|
<string name="hrv_status_baseline">%1$d-%2$d ms</string>
|
||||||
<string name="hrv_status_baseline_label">Baseline</string>
|
<string name="hrv_status_baseline_label">Baseline</string>
|
||||||
|
<string name="steps_distance_unit">%1$,.2f km</string>
|
||||||
<string name="body_energy_gained">Gained</string>
|
<string name="body_energy_gained">Gained</string>
|
||||||
<string name="body_energy_lost">Lost</string>
|
<string name="body_energy_lost">Lost</string>
|
||||||
<string name="body_energy_legend_level">Body Energy Level</string>
|
<string name="body_energy_legend_level">Body Energy Level</string>
|
||||||
@ -2071,6 +2072,10 @@
|
|||||||
<string name="minAltitude">Minimum</string>
|
<string name="minAltitude">Minimum</string>
|
||||||
<string name="averageAltitude">Average</string>
|
<string name="averageAltitude">Average</string>
|
||||||
<string name="steps">Steps</string>
|
<string name="steps">Steps</string>
|
||||||
|
<string name="steps_avg">Steps AVG</string>
|
||||||
|
<string name="steps_total">Steps Total</string>
|
||||||
|
<string name="distance_avg">Distance AVG</string>
|
||||||
|
<string name="distance_total">Distance Total</string>
|
||||||
<string name="activeSeconds">Active</string>
|
<string name="activeSeconds">Active</string>
|
||||||
<string name="caloriesBurnt">Calories</string>
|
<string name="caloriesBurnt">Calories</string>
|
||||||
<string name="maxSpeed">Maximum</string>
|
<string name="maxSpeed">Maximum</string>
|
||||||
|
@ -100,7 +100,6 @@
|
|||||||
<item name="p_activity" type="string">activity</item>
|
<item name="p_activity" type="string">activity</item>
|
||||||
<item name="p_activity_list" type="string">activitylist</item>
|
<item name="p_activity_list" type="string">activitylist</item>
|
||||||
<item name="p_sleep" type="string">sleep</item>
|
<item name="p_sleep" type="string">sleep</item>
|
||||||
<item name="p_sleep_week" type="string">sleepweek</item>
|
|
||||||
<item name="p_steps_week" type="string">stepsweek</item>
|
<item name="p_steps_week" type="string">stepsweek</item>
|
||||||
<item name="p_stress" type="string">stress</item>
|
<item name="p_stress" type="string">stress</item>
|
||||||
<item name="p_pai" type="string">pai</item>
|
<item name="p_pai" type="string">pai</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user