Add Activity info into Device Card. (#2453)

- this tries to address a common request to display user data up front
- i would make this via a fragment, but RecyclerView doesn't like Fragments
- (fragments would make it much easier to also add a charts like view,
  like we use on the Activity list tab)
- i added configuration that allows to disable it partially or fully
Co-authored-by: Petr Vaněk <vanous@noreply.codeberg.org>
Co-committed-by: Petr Vaněk <vanous@noreply.codeberg.org>
This commit is contained in:
Petr Vaněk 2021-11-18 12:22:43 +01:00 committed by Andreas Shimokawa
parent 9cd8de3202
commit 21ea7b6cc2
11 changed files with 429 additions and 11 deletions

View File

@ -53,6 +53,9 @@ import com.google.android.material.navigation.NavigationView;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -63,11 +66,16 @@ import de.cketti.library.changelog.ChangeLog;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAdapterv2; import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAdapterv2;
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -88,6 +96,9 @@ public class ControlCenterv2 extends AppCompatActivity
private RecyclerView deviceListView; private RecyclerView deviceListView;
private FloatingActionButton fab; private FloatingActionButton fab;
private boolean isLanguageInvalid = false; private boolean isLanguageInvalid = false;
List<GBDevice> deviceList;
private HashMap<String,long[]> deviceActivityHashMap = new HashMap();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@ -100,12 +111,13 @@ public class ControlCenterv2 extends AppCompatActivity
finish(); finish();
break; break;
case DeviceManager.ACTION_DEVICES_CHANGED: case DeviceManager.ACTION_DEVICES_CHANGED:
case GBApplication.ACTION_NEW_DATA:
createRefreshTask("get activity data", getApplication()).execute();
refreshPairedDevices(); refreshPairedDevices();
break; break;
case DeviceService.ACTION_REALTIME_SAMPLES: case DeviceService.ACTION_REALTIME_SAMPLES:
handleRealtimeSample(intent.getSerializableExtra(DeviceService.EXTRA_REALTIME_SAMPLE)); handleRealtimeSample(intent.getSerializableExtra(DeviceService.EXTRA_REALTIME_SAMPLE));
break; break;
} }
} }
}; };
@ -155,8 +167,12 @@ public class ControlCenterv2 extends AppCompatActivity
deviceListView.setHasFixedSize(true); deviceListView.setHasFixedSize(true);
deviceListView.setLayoutManager(new LinearLayoutManager(this)); deviceListView.setLayoutManager(new LinearLayoutManager(this));
List<GBDevice> deviceList = deviceManager.getDevices(); deviceList = deviceManager.getDevices();
mGBDeviceAdapter = new GBDeviceAdapterv2(this, deviceList); mGBDeviceAdapter = new GBDeviceAdapterv2(this, deviceList, deviceActivityHashMap);
// get activity data asynchronously, this fills the deviceActivityHashMap
// and calls refreshPairedDevices() notifyDataSetChanged
createRefreshTask("get activity data", getApplication()).execute();
deviceListView.setAdapter(this.mGBDeviceAdapter); deviceListView.setAdapter(this.mGBDeviceAdapter);
@ -210,6 +226,7 @@ public class ControlCenterv2 extends AppCompatActivity
IntentFilter filterLocal = new IntentFilter(); IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE); filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE);
filterLocal.addAction(GBApplication.ACTION_QUIT); filterLocal.addAction(GBApplication.ACTION_QUIT);
filterLocal.addAction(GBApplication.ACTION_NEW_DATA);
filterLocal.addAction(DeviceManager.ACTION_DEVICES_CHANGED); filterLocal.addAction(DeviceManager.ACTION_DEVICES_CHANGED);
filterLocal.addAction(DeviceService.ACTION_REALTIME_SAMPLES); filterLocal.addAction(DeviceService.ACTION_REALTIME_SAMPLES);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
@ -475,4 +492,38 @@ public class ControlCenterv2 extends AppCompatActivity
} }
AndroidUtils.setLanguage(this, language); AndroidUtils.setLanguage(this, language);
} }
private long[] getSteps(GBDevice device, DBHandler db) {
Calendar day = GregorianCalendar.getInstance();
DailyTotals ds = new DailyTotals();
return ds.getDailyTotalsForDevice(device, day, db);
}
protected RefreshTask createRefreshTask(String task, Context context) {
return new RefreshTask(task, context);
}
public class RefreshTask extends DBAccess {
public RefreshTask(String task, Context context) {
super(task, context);
}
@Override
protected void doInBackground(DBHandler db) {
for (GBDevice gbDevice : deviceList) {
final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
if (coordinator.supportsActivityDataFetching()) {
long[] steps = getSteps(gbDevice, db);
deviceActivityHashMap.put(gbDevice.getAddress(), steps);
}
}
}
@Override
protected void onPostExecute(Object o) {
refreshPairedDevices();
}
}
} }

View File

@ -57,6 +57,7 @@ public class DeviceSettingsActivity extends AbstractGBActivity implements
} }
if (coordinator.supportsActivityTracking()) { if (coordinator.supportsActivityTracking()) {
supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_chartstabs); supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_chartstabs);
supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_device_card_activity_card_preferences);
} }
fragment = DeviceSpecificSettingsFragment.newInstance(device.getAddress(), supportedSettings, supportedLanguages); fragment = DeviceSpecificSettingsFragment.newInstance(device.getAddress(), supportedSettings, supportedLanguages);
@ -81,6 +82,7 @@ public class DeviceSettingsActivity extends AbstractGBActivity implements
if (coordinator.supportsActivityTracking()) { if (coordinator.supportsActivityTracking()) {
supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_chartstabs); supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_chartstabs);
supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_device_card_activity_card_preferences);
} }
PreferenceFragmentCompat fragment = DeviceSpecificSettingsFragment.newInstance(device.getAddress(), supportedSettings, supportedLanguages); PreferenceFragmentCompat fragment = DeviceSpecificSettingsFragment.newInstance(device.getAddress(), supportedSettings, supportedLanguages);

View File

@ -140,6 +140,11 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF = "pref_sony_wh1000xm3_automatic_power_off"; public static final String PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF = "pref_sony_wh1000xm3_automatic_power_off";
public static final String PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE = "pref_sony_wh1000xm3_notification_voice_guide"; public static final String PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE = "pref_sony_wh1000xm3_notification_voice_guide";
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD = "prefs_activity_in_device_card";
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS = "prefs_activity_in_device_card_steps";
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP = "prefs_activity_in_device_card_sleep";
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD_DISTANCE = "prefs_activity_in_device_card_distance";
public static final String PREF_SOUNDS = "sounds"; public static final String PREF_SOUNDS = "sounds";
public static final String PREF_AUTH_KEY = "authkey"; public static final String PREF_AUTH_KEY = "authkey";
} }

View File

@ -16,12 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings; package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.text.InputType; import android.text.InputType;
import android.widget.EditText; import android.widget.EditText;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.EditTextPreference; import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
@ -39,6 +41,7 @@ import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants; import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
@ -149,6 +152,11 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_TOUCH_SENSOR; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_TOUCH_SENSOR;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_DISTANCE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION;
@ -778,6 +786,35 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
if (deviceActionsStartNonWearBroadcast != null) { if (deviceActionsStartNonWearBroadcast != null) {
deviceActionsStartNonWearBroadcast.setEnabled(deviceActionsStartNonWearSelectionBroadcast); deviceActionsStartNonWearBroadcast.setEnabled(deviceActionsStartNonWearSelectionBroadcast);
} }
final Preference activityInDeviceCard = findPreference(PREFS_ACTIVITY_IN_DEVICE_CARD);
final Preference activityInDeviceSteps = findPreference(PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS);
final Preference activityInDeviceSleep = findPreference(PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP);
final Preference activityInDeviceDistance = findPreference(PREFS_ACTIVITY_IN_DEVICE_CARD_DISTANCE);
Preference.OnPreferenceClickListener sendIntentRefreshDeviceListListener = new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
Intent refreshIntent = new Intent(DeviceManager.ACTION_REFRESH_DEVICELIST);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(refreshIntent);
return true;
}
};
if (activityInDeviceCard != null) {
activityInDeviceCard.setOnPreferenceClickListener(sendIntentRefreshDeviceListListener);
}
if (activityInDeviceSteps != null) {
activityInDeviceSteps.setOnPreferenceClickListener(sendIntentRefreshDeviceListListener);
}
if (activityInDeviceSleep != null) {
activityInDeviceSleep.setOnPreferenceClickListener(sendIntentRefreshDeviceListListener);
}
if (activityInDeviceDistance != null) {
activityInDeviceDistance.setOnPreferenceClickListener(sendIntentRefreshDeviceListListener);
}
} }
static DeviceSpecificSettingsFragment newInstance(String settingsFileSuffix, @NonNull int[] supportedSettings, String[] supportedLanguages) { static DeviceSpecificSettingsFragment newInstance(String settingsFileSuffix, @NonNull int[] supportedSettings, String[] supportedLanguages) {

View File

@ -41,6 +41,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView; import androidx.cardview.widget.CardView;
import androidx.fragment.app.FragmentActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -51,8 +52,13 @@ import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
@ -61,9 +67,13 @@ import nodomain.freeyourgadget.gadgetbridge.activities.BatteryInfoActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms; import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateDialog; import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateDialog;
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity; import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityListingDashboard;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
@ -72,9 +82,12 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes; import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -88,10 +101,12 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
private List<GBDevice> deviceList; private List<GBDevice> deviceList;
private int expandedDevicePosition = RecyclerView.NO_POSITION; private int expandedDevicePosition = RecyclerView.NO_POSITION;
private ViewGroup parent; private ViewGroup parent;
private HashMap<String, long[]> deviceActivityMap = new HashMap();
public GBDeviceAdapterv2(Context context, List<GBDevice> deviceList) { public GBDeviceAdapterv2(Context context, List<GBDevice> deviceList, HashMap<String,long[]> deviceMap) {
this.context = context; this.context = context;
this.deviceList = deviceList; this.deviceList = deviceList;
this.deviceActivityMap = deviceMap;
} }
@NonNull @NonNull
@ -105,12 +120,18 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
final GBDevice device = deviceList.get(position); final GBDevice device = deviceList.get(position);
long[] dailyTotals = new long[]{0, 0};
if (deviceActivityMap.containsKey(device.getAddress())) {
dailyTotals = deviceActivityMap.get(device.getAddress());
}
final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device); final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
holder.container.setOnClickListener(new View.OnClickListener() { holder.container.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (device.isInitialized() || device.isConnected()) { if (device.isInitialized() || device.isConnected()) {
showTransientSnackbar(R.string.controlcenter_snackbar_need_longpress); showTransientSnackbar(R.string.controlcenter_snackbar_need_longpress);
} else { } else {
@ -596,6 +617,22 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
.show(); .show();
} }
}); });
holder.cardViewActivityCardLayout.setVisibility(coordinator.supportsActivityTracking() ? View.VISIBLE : View.GONE);
holder.cardViewActivityCardLayout.setMinimumWidth(coordinator.supportsActivityTracking() ? View.VISIBLE : View.GONE);
holder.cardViewActivityCardLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, ChartsActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
if (coordinator.supportsActivityDataFetching()) {
setActivityCard(holder, device, dailyTotals);
}
} }
@Override @Override
@ -647,8 +684,20 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
TextView fmFrequencyLabel; TextView fmFrequencyLabel;
ImageView ledColor; ImageView ledColor;
LinearLayout cardViewActivityCardLayout;
LinearLayout cardViewActivityCardStepsLayout;
LinearLayout cardViewActivityCardSleepLayout;
LinearLayout cardViewActivityCardDistanceLayout;
TextView cardViewActivityCardSteps;
TextView cardViewActivityCardDistance;
TextView cardViewActivityCardSleep;
ProgressBar cardViewActivityCardStepsProgress;
ProgressBar cardViewActivityCardDistanceProgress;
ProgressBar cardViewActivityCardSleepProgress;
ViewHolder(View view) { ViewHolder(View view) {
super(view); super(view);
container = view.findViewById(R.id.card_view); container = view.findViewById(R.id.card_view);
deviceImageView = view.findViewById(R.id.device_image); deviceImageView = view.findViewById(R.id.device_image);
@ -692,6 +741,19 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
heartRateStatusBox = view.findViewById(R.id.device_heart_rate_status_box); heartRateStatusBox = view.findViewById(R.id.device_heart_rate_status_box);
heartRateStatusLabel = view.findViewById(R.id.heart_rate_status); heartRateStatusLabel = view.findViewById(R.id.heart_rate_status);
heartRateIcon = view.findViewById(R.id.device_heart_rate_status); heartRateIcon = view.findViewById(R.id.device_heart_rate_status);
cardViewActivityCardLayout = view.findViewById(R.id.card_view_activity_card_layout);
cardViewActivityCardStepsLayout = view.findViewById(R.id.card_view_activity_card_steps_layout);
cardViewActivityCardSleepLayout = view.findViewById(R.id.card_view_activity_card_sleep_layout);
cardViewActivityCardDistanceLayout = view.findViewById(R.id.card_view_activity_card_distance_layout);
cardViewActivityCardSteps = view.findViewById(R.id.card_view_activity_card_steps);
cardViewActivityCardDistance = view.findViewById(R.id.card_view_activity_card_distance);
cardViewActivityCardSleep = view.findViewById(R.id.card_view_activity_card_sleep);
cardViewActivityCardStepsProgress = view.findViewById(R.id.card_view_activity_card_steps_progress);
cardViewActivityCardDistanceProgress = view.findViewById(R.id.card_view_activity_card_distance_progress);
cardViewActivityCardSleepProgress = view.findViewById(R.id.card_view_activity_card_sleep_progress);
} }
} }
@ -757,4 +819,65 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
snackbar.show(); snackbar.show();
} }
private void setActivityCard(ViewHolder holder, GBDevice device, long[] dailyTotals) {
int steps = (int) dailyTotals[0];
int sleep = (int) dailyTotals[1];
ActivityUser activityUser = new ActivityUser();
int stepGoal = activityUser.getStepsGoal();
int sleepGoal = activityUser.getSleepDuration();
int sleepGoalMinutes = sleepGoal * 60;
int distanceGoal = activityUser.getDistanceMeters() * 100;
int stepLength = activityUser.getStepLengthCm();
double distanceMeters = dailyTotals[0] * stepLength / 100;
double distanceFeet = distanceMeters * 3.28084f;
double distanceFormatted = 0;
String unit = "###m";
distanceFormatted = distanceMeters;
if (distanceMeters > 2000) {
distanceFormatted = distanceMeters / 1000;
unit = "###.#km";
}
String units = GBApplication.getPrefs().getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, GBApplication.getContext().getString(R.string.p_unit_metric));
if (units.equals(GBApplication.getContext().getString(R.string.p_unit_imperial))) {
unit = "###ft";
distanceFormatted = distanceFeet;
if (distanceFeet > 6000) {
distanceFormatted = distanceFeet * 0.0001893939f;
unit = "###.#mi";
}
}
DecimalFormat df = new DecimalFormat(unit);
holder.cardViewActivityCardSteps.setText(String.format("%1s", steps));
holder.cardViewActivityCardSleep.setText(String.format("%1s", getHM(sleep)));
holder.cardViewActivityCardDistance.setText(df.format(distanceFormatted));
holder.cardViewActivityCardStepsProgress.setMax(stepGoal);
holder.cardViewActivityCardStepsProgress.setProgress(steps);
holder.cardViewActivityCardSleepProgress.setMax(sleepGoalMinutes);
holder.cardViewActivityCardSleepProgress.setProgress(sleep);
holder.cardViewActivityCardDistanceProgress.setMax(distanceGoal);
holder.cardViewActivityCardDistanceProgress.setProgress(steps * stepLength);
boolean showActivityCard = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD, true);
holder.cardViewActivityCardLayout.setVisibility(showActivityCard ? View.VISIBLE : View.GONE);
boolean showActivitySteps = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS, true);
holder.cardViewActivityCardStepsLayout.setVisibility(showActivitySteps ? View.VISIBLE : View.GONE);
boolean showActivitySleep = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP, true);
holder.cardViewActivityCardSleepLayout.setVisibility(showActivitySleep ? View.VISIBLE : View.GONE);
boolean showActivityDistance = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_DISTANCE, true);
holder.cardViewActivityCardDistanceLayout.setVisibility(showActivityDistance ? View.VISIBLE : View.GONE);
}
private String getHM(long value) {
return DateTimeUtils.formatDurationHoursMinutes(value, TimeUnit.MINUTES);
}
} }

View File

@ -68,6 +68,15 @@ public class DailyTotals {
public long[] getDailyTotalsForDevice(GBDevice device, Calendar day) { public long[] getDailyTotalsForDevice(GBDevice device, Calendar day) {
try (DBHandler handler = GBApplication.acquireDB()) { try (DBHandler handler = GBApplication.acquireDB()) {
return getDailyTotalsForDevice(device, day, handler);
} catch (Exception e) {
//GB.toast("Error loading sleep/steps widget data for device: " + device, Toast.LENGTH_SHORT, GB.ERROR, e);
return new long[]{0, 0};
}
}
public long[] getDailyTotalsForDevice(GBDevice device, Calendar day, DBHandler handler) {
ActivityAnalysis analysis = new ActivityAnalysis(); ActivityAnalysis analysis = new ActivityAnalysis();
ActivityAmounts amountsSteps; ActivityAmounts amountsSteps;
ActivityAmounts amountsSleep; ActivityAmounts amountsSleep;
@ -79,11 +88,6 @@ public class DailyTotals {
long steps = getTotalsStepsForActivityAmounts(amountsSteps); long steps = getTotalsStepsForActivityAmounts(amountsSteps);
return new long[]{steps, sleep[0] + sleep[1]}; return new long[]{steps, sleep[0] + sleep[1]};
} catch (Exception e) {
//GB.toast("Error loading sleep/steps widget data for device: " + device, Toast.LENGTH_SHORT, GB.ERROR, e);
return new long[]{0, 0};
}
} }
private long[] getTotalsSleepForActivityAmounts(ActivityAmounts activityAmounts) { private long[] getTotalsSleepForActivityAmounts(ActivityAmounts activityAmounts) {

View File

@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="45sp" android:width="24sp"
android:height="45sp" android:height="24sp"
android:tint="#7E7E7E" android:tint="#7E7E7E"
android:viewportWidth="25" android:viewportWidth="25"
android:viewportHeight="25"> android:viewportHeight="25">

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#7E7E7E">
<path
android:fillColor="@android:color/white"
android:pathData="M6.21,13.97l1.2,2.07c0.08,0.13 0.23,0.18 0.37,0.13l1.49,-0.6c0.31,0.24 0.64,0.44 1.01,0.59l0.22,1.59c0.03,0.14 0.15,0.25 0.3,0.25h2.4c0.15,0 0.27,-0.11 0.3,-0.26l0.22,-1.59c0.36,-0.15 0.7,-0.35 1.01,-0.59l1.49,0.6c0.14,0.05 0.29,0 0.37,-0.13l1.2,-2.07c0.08,-0.13 0.04,-0.29 -0.07,-0.39l-1.27,-0.99c0.03,-0.19 0.04,-0.39 0.04,-0.58 0,-0.2 -0.02,-0.39 -0.04,-0.59l1.27,-0.99c0.11,-0.09 0.15,-0.26 0.07,-0.39l-1.2,-2.07c-0.08,-0.13 -0.23,-0.18 -0.37,-0.13l-1.49,0.6c-0.31,-0.24 -0.64,-0.44 -1.01,-0.59l-0.22,-1.59c-0.03,-0.14 -0.15,-0.25 -0.3,-0.25h-2.4c-0.15,0 -0.27,0.11 -0.3,0.26l-0.22,1.59c-0.36,0.15 -0.71,0.34 -1.01,0.58l-1.49,-0.6c-0.14,-0.05 -0.29,0 -0.37,0.13l-1.2,2.07c-0.08,0.13 -0.04,0.29 0.07,0.39l1.27,0.99c-0.03,0.2 -0.05,0.39 -0.05,0.59 0,0.2 0.02,0.39 0.04,0.59l-1.27,0.99c-0.11,0.1 -0.14,0.26 -0.06,0.39zM12,10.29c0.94,0 1.71,0.77 1.71,1.71s-0.77,1.71 -1.71,1.71 -1.71,-0.77 -1.71,-1.71 0.77,-1.71 1.71,-1.71zM19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM19,19L5,19L5,5h14v14z"/>
</vector>

View File

@ -126,6 +126,7 @@
card_view:tint="@color/secondarytext" /> card_view:tint="@color/secondarytext" />
<com.google.android.flexbox.FlexboxLayout <com.google.android.flexbox.FlexboxLayout
android:id="@+id/device_info_icons"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/device_image" android:layout_below="@id/device_image"
@ -462,6 +463,137 @@
</com.google.android.flexbox.FlexboxLayout> </com.google.android.flexbox.FlexboxLayout>
<LinearLayout
android:id="@+id/card_view_activity_card_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/device_info_icons"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:paddingBottom="5dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/card_view_activity_card_steps_layout"
android:orientation="horizontal">
<ImageView
android:id="@+id/card_view_activity_card_steps_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="start|center_vertical"
android:background="@drawable/ic_shoe_prints_many"
android:contentDescription="@string/icon_placeholder"
android:padding="0dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<TextView
android:id="@+id/card_view_activity_card_steps"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="59999"
android:textSize="18sp" />
<ProgressBar
android:id="@+id/card_view_activity_card_steps_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="#FFFFFF" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/card_view_activity_card_distance_layout"
android:orientation="horizontal">
<ImageView
android:id="@+id/card_view_activity_card_distance_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="start|center_vertical"
android:background="@drawable/ic_distance_total"
android:contentDescription="@string/icon_placeholder"
android:padding="0dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<TextView
android:id="@+id/card_view_activity_card_distance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="1"
android:textSize="18sp" />
<ProgressBar
android:id="@+id/card_view_activity_card_distance_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_weight="1"
android:background="#FFFFFF" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/card_view_activity_card_sleep_layout"
android:orientation="horizontal">
<ImageView
android:id="@+id/card_view_activity_card_sleep_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="start|center_vertical"
android:background="@drawable/ic_activity_sleep"
android:contentDescription="@string/icon_placeholder" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<TextView
android:id="@+id/card_view_activity_card_sleep"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="300"
android:textSize="18sp" />
<ProgressBar
android:id="@+id/card_view_activity_card_sleep_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_weight="1"
android:background="#FFFFFF" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout> </RelativeLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -25,6 +25,14 @@
<string name="controlcenter_snackbar_connecting">Connecting…</string> <string name="controlcenter_snackbar_connecting">Connecting…</string>
<string name="controlcenter_snackbar_requested_screenshot">Taking a screenshot of the device</string> <string name="controlcenter_snackbar_requested_screenshot">Taking a screenshot of the device</string>
<string name="controlcenter_calibrate_device">Calibrate Device</string> <string name="controlcenter_calibrate_device">Calibrate Device</string>
<string name="device_card_activity_card_title">Activity info on device card</string>
<string name="device_card_activity_card_title_summary">Choose what activity details are displayed on device card</string>
<string name="prefs_activity_in_device_card_title">Show Activity info on device card</string>
<string name="prefs_activity_in_device_card_title_summary">Show current steps, distance or sleep on device card</string>
<string name="prefs_activity_in_device_card_sleep_title">Sleep</string>
<string name="prefs_activity_in_device_card_sleep_title_summary">Show sleep duration</string>
<string name="prefs_activity_in_device_card_distance_title_summary">Distance is calculated from steps and step length (adjustable in Settings - About you)</string>
<string name="prefs_activity_in_device_card_steps_title_summary">Show total steps</string>
<!-- Strings related to battery Info Activity --> <!-- Strings related to battery Info Activity -->
<string name="battery_detail_activity_title">Battery info</string> <string name="battery_detail_activity_title">Battery info</string>
<string name="battery_level">Battery level</string> <string name="battery_level">Battery level</string>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_settings_applications"
android:key="device_card_activity_card"
android:persistent="false"
android:summary="@string/device_card_activity_card_title_summary"
android:title="@string/device_card_activity_card_title">
<SwitchPreference
android:defaultValue="true"
android:icon="@drawable/ic_activity_unknown_small"
android:key="prefs_activity_in_device_card"
android:persistent="true"
android:summary="@string/prefs_activity_in_device_card_title_summary"
android:title="@string/prefs_activity_in_device_card_title" />
<SwitchPreference
android:defaultValue="true"
android:dependency="prefs_activity_in_device_card"
android:icon="@drawable/ic_shoe_prints_many"
android:key="prefs_activity_in_device_card_steps"
android:persistent="true"
android:summary="@string/prefs_activity_in_device_card_steps_title_summary"
android:title="@string/Steps" />
<SwitchPreference
android:defaultValue="true"
android:dependency="prefs_activity_in_device_card"
android:icon="@drawable/ic_distance_total"
android:key="prefs_activity_in_device_card_distance"
android:persistent="true"
android:summary="@string/prefs_activity_in_device_card_distance_title_summary"
android:title="@string/Distance" />
<SwitchPreference
android:defaultValue="true"
android:dependency="prefs_activity_in_device_card"
android:icon="@drawable/ic_activity_sleep"
android:key="prefs_activity_in_device_card_sleep"
android:persistent="true"
android:summary="@string/prefs_activity_in_device_card_sleep_title_summary"
android:title="@string/prefs_activity_in_device_card_sleep_title" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>