Android Question Samsung S-Health

Rusty

Well-Known Member
Licensed User
Longtime User
I am trying to use the Samsung S-Health application to monitor activity etc.
I've got the .jar file samsung-digital-health-healthdata-1.2.1.jar in my additional libraries folder.
I also have ported the Java code:
B4X:
#if java
import com.samsung.android.sdk.healthdata.HealthConstants;
import com.samsung.android.sdk.healthdata.HealthDataObserver;
import com.samsung.android.sdk.healthdata.HealthDataResolver;
import com.samsung.android.sdk.healthdata.HealthDataResolver.Filter;
import com.samsung.android.sdk.healthdata.HealthDataResolver.ReadRequest;
import com.samsung.android.sdk.healthdata.HealthDataResolver.ReadResult;
import com.samsung.android.sdk.healthdata.HealthDataStore;
import com.samsung.android.sdk.healthdata.HealthResultHolder;

import android.database.Cursor;
import android.util.Log;

import java.util.Calendar;

public class StepCountReporter {
    private final HealthDataStore mStore;

    public StepCountReporter(HealthDataStore store) {
        mStore = store;
    }

    public void start() {
        // Register an observer to listen changes of step count and get today step count
        HealthDataObserver.addObserver(mStore, HealthConstants.StepCount.HEALTH_DATA_TYPE, mObserver);
        readTodayStepCount();
    }

    // Read the today's step count on demand
    private void readTodayStepCount() {
        HealthDataResolver resolver = new HealthDataResolver(mStore, null);

        // Set time range from start time of today to the current time
        long startTime = getStartTimeOfToday();
        long endTime = System.currentTimeMillis();
        Filter filter = Filter.and(Filter.greaterThanEquals(HealthConstants.StepCount.START_TIME, startTime),
                                   Filter.lessThanEquals(HealthConstants.StepCount.START_TIME, endTime));

        HealthDataResolver.ReadRequest request = new ReadRequest.Builder()
                                                        .setDataType(HealthConstants.StepCount.HEALTH_DATA_TYPE)
                                                        .setProperties(new String[] {HealthConstants.StepCount.COUNT})
                                                        .setFilter(filter)
                                                        .build();

        try {
            resolver.read(request).setResultListener(mListener);
        } catch (Exception e) {
            Log.e(MainActivity.APP_TAG, e.getClass().getName() + " - " + e.getMessage());
            Log.e(MainActivity.APP_TAG, "Getting step count fails.");
        }
    }

    private long getStartTimeOfToday() {
        Calendar today = Calendar.getInstance();

        today.set(Calendar.HOUR_OF_DAY, 0);
        today.set(Calendar.MINUTE, 0);
        today.set(Calendar.SECOND, 0);
        today.set(Calendar.MILLISECOND, 0);

        return today.getTimeInMillis();
    }

    private final HealthResultHolder.ResultListener<ReadResult> mListener = new HealthResultHolder.ResultListener<ReadResult>() {
        @Override
        public void onResult(ReadResult result) {
            int count = 0;
            Cursor c = null;

            try {
                c = result.getResultCursor();
                if (c != null) {
                    while (c.moveToNext()) {
                        count += c.getInt(c.getColumnIndex(HealthConstants.StepCount.COUNT));
                    }
                }
            } finally {
                if (c != null) {
                    c.close();
                }
            }
            MainActivity.getInstance().drawStepCount(String.valueOf(count));
        }
    };

    private final HealthDataObserver mObserver = new HealthDataObserver(null) {

        // Update the step count when a change event is received
        @Override
        public void onChange(String dataTypeName) {
            Log.d(MainActivity.APP_TAG, "Observer receives a data changed event");
            readTodayStepCount();
        }
    };

}
    #end if
into my new application.
However, when I refresh my libraries, the Samsung jar doesn't show.
Any ideas why it doesn't show? (Also, any advice on making the Java code above work would be GREATLY appreciated!)
Rusty
 

DonManfred

Expert
Licensed User
Longtime User
Any ideas why it doesn't show?
Yes.
You are missing some Annotations.

B4X:
@Version(1.02f)
@ShortName("FirebaseDatabase")
@Author(value = "DonManfred (wrapper)")
//@Permissions(values={"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE"})
//@Events(values={"onSigned(sign As Object)"})
@DependsOn(values={"nameofadditionaljarwithoutext"})

ShortName for ex. is used to list the objects inside b4a.

any advice on making the Java code above work
Can you upload the 3rd party jar (plus documentation)?

Edit: Sorry! I have not realized that you are using inline java. My answer was talking about a java-library

Inline java does not add a item in the library tab.
It is part of your activity.
 
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
I have in-line Java that is using the .jar file mentioned. Note the IMPORTS.
The attached file is Samsung's, not mine, but is available for free from Samsung in the SDK, so I assume it is ok to attach it here.

Any ideas? (I didn't understand your first statements about missing some annotations.)
Rusty
 

Attachments

  • samsung-digital-health-healthdata-1.2.1.jar
    205.7 KB · Views: 456
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
I may be wrong, but I think that

1. For the samsung jar to appear, there ought to be an assotiated xml file. But this would be useful if it was a B4A wrapper of the original library

2. But you don't need to include it, as you are already importing the classes from inline java

Does the code compile ok? If so, there is no problem with the jar.
 
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
It doesn't compile because it can't find the library...
Compiling generated Java code. Error
javac 1.8.0_51
src\b4a\example\main.java:3: error: package com.samsung.android.sdk.healthdata does not exist
import com.samsung.android.sdk.healthdata.HealthConstants;
^
Note: src\b4a\example\starter.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
1 error
 
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
Place the jar in your additional libraries folder and write this before Sub Process_Globals

B4X:
#AdditionalJar: samsung-digital-health-healthdata-1.2.1

Now it will find the jar.;)

However, the compiler finds a couple of errors:
  • Log.e(..) --> i would replace them by BA.Log(..)
  • MainActivity.getInstance().drawStepCount(String.valueOf(count)); --> surely with the lib there was an activity example written in Java. The purpose of this sems to pass the result to the activity so that it is presented somewhere. I guess You should implement a callback to B4A to have this result
 
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
Thanks JordiCP
I've added the addionaljar and replaced the log.e with ba.log, but the ba.log is not found either.
ompiling generated Java code. Error
javac 1.8.0_51
src\b4a\example\main.java:451: error: cannot find symbol
ba.Log(MainActivity.APP_TAG, e.getClass().getName() + " - " + e.getMessage());
^
symbol: variable MainActivity
location: class main.StepCountReporter
1 error

I'm trying to merely retrieve values from the Samsung database for use in calculations or to export to .csv.
the code:
B4X:
Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region
#AdditionalJar: samsung-digital-health-healthdata-1.2.1
Sub Process_Globals
    Private NativeMe As JavaObject        'use Java to access
End Sub

Sub Globals
End Sub

Sub Activity_Create(FirstTime As Boolean)

End Sub

Sub Activity_Resume
    NativeMe.InitializeContext
    Dim x As Object = NativeMe.RunMethod("StepCountReporter", Null)
    Log("x " & x )
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

#if java
import com.samsung.android.sdk.healthdata.HealthConstants;
import com.samsung.android.sdk.healthdata.HealthDataObserver;
import com.samsung.android.sdk.healthdata.HealthDataResolver;
import com.samsung.android.sdk.healthdata.HealthDataResolver.Filter;
import com.samsung.android.sdk.healthdata.HealthDataResolver.ReadRequest;
import com.samsung.android.sdk.healthdata.HealthDataResolver.ReadResult;
import com.samsung.android.sdk.healthdata.HealthDataStore;
import com.samsung.android.sdk.healthdata.HealthResultHolder;

import android.database.Cursor;
import android.util.Log;

import java.util.Calendar;

public class StepCountReporter {
    private final HealthDataStore mStore;

    public StepCountReporter(HealthDataStore store) {
        mStore = store;
    }

    public void start() {
        // Register an observer to listen changes of step count and get today step count
        HealthDataObserver.addObserver(mStore, HealthConstants.StepCount.HEALTH_DATA_TYPE, mObserver);
        readTodayStepCount();
    }

    // Read the today's step count on demand
    private void readTodayStepCount() {
        HealthDataResolver resolver = new HealthDataResolver(mStore, null);

        // Set time range from start time of today to the current time
        long startTime = getStartTimeOfToday();
        long endTime = System.currentTimeMillis();
        Filter filter = Filter.and(Filter.greaterThanEquals(HealthConstants.StepCount.START_TIME, startTime),
                                   Filter.lessThanEquals(HealthConstants.StepCount.START_TIME, endTime));

        HealthDataResolver.ReadRequest request = new ReadRequest.Builder()
                                                        .setDataType(HealthConstants.StepCount.HEALTH_DATA_TYPE)
                                                        .setProperties(new String[] {HealthConstants.StepCount.COUNT})
                                                        .setFilter(filter)
                                                        .build();

        try {
            resolver.read(request).setResultListener(mListener);
        } catch (Exception e) {
            ba.Log(MainActivity.APP_TAG, e.getClass().getName() + " - " + e.getMessage());
            ba.Log(MainActivity.APP_TAG, "Getting step count fails.");
        }
    }

    private long getStartTimeOfToday() {
        Calendar today = Calendar.getInstance();

        today.set(Calendar.HOUR_OF_DAY, 0);
        today.set(Calendar.MINUTE, 0);
        today.set(Calendar.SECOND, 0);
        today.set(Calendar.MILLISECOND, 0);

        return today.getTimeInMillis();
    }

    private final HealthResultHolder.ResultListener<ReadResult> mListener = new HealthResultHolder.ResultListener<ReadResult>() {
        @Override
        public void onResult(ReadResult result) {
            int count = 0;
            Cursor c = null;

            try {
                c = result.getResultCursor();
                if (c != null) {
                    while (c.moveToNext()) {
                        count += c.getInt(c.getColumnIndex(HealthConstants.StepCount.COUNT));
                    }
                }
            } finally {
                if (c != null) {
                    c.close();
                }
            }
            MainActivity.getInstance().drawStepCount(String.valueOf(count));
        }
    };

    private final HealthDataObserver mObserver = new HealthDataObserver(null) {

        // Update the step count when a change event is received
        @Override
        public void onChange(String dataTypeName) {
            Log.d(MainActivity.APP_TAG, "Observer receives a data changed event");
            readTodayStepCount();
        }
    };

}
    #end if

Your suggestions are appreciated. Java is completely foreign to me.
 
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
Yes, Java is case sensitive, so it should be BA.Log("Hello"); (or whatever)

I am trying to figure how the class works. You will not get any result directly from StepCountReporter since it is the class itself.

Will do some tests and see if I can get something...
 
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
Thanks very much for your interest and time.
Let me know what you find.
My objective is to merely read the data within the Samsung database and use it eleswhere. (even as a CSV, if required)
Rusty
 
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
Yes, attached zip.
From the Samsung website.
 

Attachments

  • simplehealth.zip
    4 KB · Views: 421
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
Hi Rusty

Sorry, I looked at the MainActivity and it is not easy for me, since in order to make use of the stepCounter class, you must first init a Service, a Store, some runtime permissions and a connectionlistener.... it is all new for me :(

Will take a deeper look on weekend because it will require a bit longer than I thought (unless someone else can solve it before)
 
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
As a note, I can get the pure Android Java version to work, but am having difficulty porting it to B4a. I don't know how to get the variable values (i.e. Step Count, etc.) in Java...
Thanks to both of you for your help :)
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Update:

I´ve done parts of the SDK. Including - to test - permissionrequest for the step_counter

For now i´m at the end of my possibilities... To be able to use the S-Health SDK your app needs to be listed on Samsungs SHealth Whitelist. Your app must be published on GooglePlay to be able to get confirmed... Once your app is Whitelisted then you can use Requests on the SDK.

I do not have any published app so i dont get approved

I´ll post the lib as written today as library files (xml, jar) plus B4A Example so far i could succeed. And the java-source written so far. Hope it helps...

AGAIN: You´ll fail at the end at

B4X:
Sub Health_onConnected()  
    Log($"Health_onConnected()"$)
    pms.Initialize("HealthPermission",hds)
    If pms.getPermissionGranted(pms.StepCountPermission) Then
        Log("Request read")
        Dim readreq As ReadRequestBuilder
        readreq.Initialize.setResultCount(0,100).setDataType("com.samsung.health.step_count")
        resolv.read(readreq.build) ' Here the exception will be raised...
    Else      
    End If
    pms.checkandrequestPermissionAcquired(pms.StepCountPermission)  
End Sub


LogCat connected to: 9885e6514556383552
--------- beginning of system
--------- beginning of main
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
true
0
com.sec.android.app.shealth
null
** Activity (main) Resume **
lib:Raising.. health_onconnected()
Health_onConnected()
Request read
main_health_onconnected (java line: 396)
java.lang.SecurityException: Not registered on the white list for 262 (99)
at android.os.Parcel.readException(Parcel.java:1620)
at android.os.Parcel.readException(Parcel.java:1573)
at com.samsung.android.sdk.healthdata.IDataResolver$Stub$a.readData2(IDataResolver.java:454)
at com.samsung.android.sdk.healthdata.HealthDataResolver.read(HealthDataResolver.java:447)
at de.donmanfred.HealthDataResolverWrapper.read(HealthDataResolverWrapper.java:120)
at b4a.example.main._health_onconnected(main.java:396)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
at anywheresoftware.b4a.BA$2.run(BA.java:328)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)


SamsungDigitalHealth
Author:
DonManfred (wrapper)
Version: 1
  • DeleteRequestBuilder
    Methods:
    • Initialize As DeleteRequestBuilderWrapper
    • IsInitialized As Boolean
    • build As DeleteRequest
    • setDataType (type As String) As DeleteRequestBuilderWrapper
    • setFilter (filter As Filter) As DeleteRequestBuilderWrapper
    • setSourceDevices (uuidList As List) As DeleteRequestBuilderWrapper
  • HealthDataResolver
    Events:
    • onAggregateResult (count As Int, valuetype As String, status As Int, data As Cursor)
    • onDeleteResult (count As Int, status As Int)
    • onInsertResult (count As Int, status As Int)
    • onReadResult (count As Int, valuetype As String, status As Int, data As Cursor)
    • onUpdateResult (count As Int, status As Int)
    Methods:
    • Initialize (EventName As String, store As HealthDataStore)
    • IsInitialized As Boolean
    • aggregate (request As AggregateRequest)
    • delete (request As DeleteRequest)
    • insert (request As InsertRequest)
    • read (request As ReadRequest)
    • update (request As UpdateRequest)
  • HealthDataService
    Methods:
    • Initialize (EventName As String)
    • IsInitialized As Boolean
    • getisFeatureEnabled (type As Int) As Boolean
    Properties:
    • VersionCode As Int [read only]
    • VersionName As String [read only]
  • HealthDataStore
    Events:
    • onConnected ( As )
    • onConnectionFailed (error As String)
    • onDisConnected ( As )
    Methods:
    • ConnectService
    • ConnectService2 (timeout As Long)
    • DisConnectService
    • Initialize (EventName As String)
    • IsInitialized As Boolean
    • getInterface (store As HealthDataStore) As IHealth
    Properties:
    • MyUserId As Long [read only]
    • PlatformPackageName As String [read only]
    • SocketKey As String [read only]
  • HealthPermissionManager
    Events:
    • onPermissionResult (Count As Int, Result As Boolean, status As Int)
    Methods:
    • Initialize (EventName As String, ds As HealthDataStore) As HealthPermissionManagerWrapper
    • IsInitialized As Boolean
    • checkandrequestPermissionAcquired (permissionKeys As Set)
    • getPermissionGranted (permissionKeys As Set) As Boolean
    • requestPermissions (permissionKeys As Set)
    Properties:
    • StepCountPermission As HashSet [read only]
  • InsertRequestBuilder
    Methods:
    • Initialize As InsertRequestBuilderWrapper
    • IsInitialized As Boolean
    • build As InsertRequest
    • setDataType (type As String) As InsertRequestBuilderWrapper
  • PermissionKey
    Methods:
    • Initialize (dataType As String, permissionType As PermissionType)
    • IsInitialized As Boolean
    Properties:
    • DataType As String [read only]
    • PermissionType As PermissionType [read only]
  • PermissionType
    Methods:
    • Initialize (type As Int)
    • IsInitialized As Boolean
    • getType (value As Int) As PermissionType
    • valueOf (name As String) As PermissionType
  • ReadRequestBuilder
    Methods:
    • Initialize As ReadRequestBuilderWrapper
    • IsInitialized As Boolean
    • build As ReadRequest
    • setDataType (type As String) As ReadRequestBuilderWrapper
    • setFilter (filter As Filter) As ReadRequestBuilderWrapper
    • setPackageName (packageName As String) As ReadRequestBuilderWrapper
    • setProperties (properties() As String) As ReadRequestBuilderWrapper
    • setPropertyAlias (property As String, alias As String) As ReadRequestBuilderWrapper
    • setResultCount (offset As Int, count As Int) As ReadRequestBuilderWrapper
    • setSourceDevices (uuidList As List) As ReadRequestBuilderWrapper
    • setTimeAfter (time As Long) As ReadRequestBuilderWrapper
    • setTimeBefore (time As Long) As ReadRequestBuilderWrapper
  • Shealth
    Methods:
    • Initialize As ShealthWrapper
    • IsInitialized As Boolean
    • isFeatureEnabled (type As Int) As Boolean
    • isFeatureEnabled2 (types() As Int) As Boolean
    Properties:
    • VersionCode As Int [read only]
    • VersionName As String [read only]
  • UpdateRequestBuilder
    Methods:
    • Initialize As UpdateRequestBuilderWrapper
    • IsInitialized As Boolean
    • build As UpdateRequest
    • setDataType (type As String) As UpdateRequestBuilderWrapper
    • setFilter (filter As Filter) As UpdateRequestBuilderWrapper
    • setHealthData (data As HealthData) As UpdateRequestBuilderWrapper
    • setSourceDevices (uuidList As List) As UpdateRequestBuilderWrapper
 

Attachments

  • SamsungDigitalHealthV1.0.zip
    192.4 KB · Views: 413
  • SHealthEx.zip
    7.4 KB · Views: 418
  • src.zip
    11.3 KB · Views: 408
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
Wow! Thanks Don!
I've talked to Samsung (waiting on response) to see if they have a sandbox in which I can test.
I'll let you know.
Rusty
 
Upvote 0

Rusty

Well-Known Member
Licensed User
Longtime User
...argh...I had read that. Thanks for reminding me.
Your code is successful in development mode :)
However, I am lost as to how to read their "cursor" data.
In Resolver_onReadResult, it has data as cursor, although "cursor" is not an SQLite type cursor.
are you able to understand how to parse the cursor to get the (i.e.) stepcounts data etc.?
Thanks!
Rusty
 
Upvote 0
Top