I and my team mates are using MI band 3 to record data from Google Fit API. We are using a third party app - 'Notify and Fitness for MI Band' to sync the data. We are unable to get the Instantaneous Heart Rate.
We are using DataType.TYPE_HEART_RATE_BPM, DataType.AGGREGATE_HEART_RATE_SUMMARY to get the average highest and lowest heart readings but the Instaneous readings are not showning
package com.example.shubchintak;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.fitness.Fitness;
import com.google.android.gms.fitness.FitnessOptions;
import com.google.android.gms.fitness.data.Bucket;
import com.google.android.gms.fitness.data.DataPoint;
import com.google.android.gms.fitness.data.DataSet;
import com.google.android.gms.fitness.data.DataSource;
import com.google.android.gms.fitness.data.DataType;
import com.google.android.gms.fitness.data.Field;
import com.google.android.gms.fitness.data.Subscription;
import com.google.android.gms.fitness.data.Value;
import com.google.android.gms.fitness.request.DataReadRequest;
import com.google.android.gms.fitness.request.DataSourcesRequest;
import com.google.android.gms.fitness.request.OnDataPointListener;
import com.google.android.gms.fitness.request.SensorRequest;
import com.google.android.gms.fitness.result.DataReadResponse;
import com.google.android.gms.fitness.result.DataReadResult;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.Snackbar;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.logging.LogWrapper;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static java.text.DateFormat.getDateInstance;
import static java.text.DateFormat.getTimeInstance;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private Toolbar mToolbar;
private DrawerLayout mdrawerLayoout;
public static final String TAG = "StepCounter";
private static final int REQUEST_OAUTH_REQUEST_CODE = 0x1001;
private static final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 0x1001;
static TextView stepsNo;
private FirebaseAuth mAuth;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.main_page_toolbar);
setSupportActionBar(mToolbar);
getSupportActionBar().setTitle("Shubchintak");
stepsNo=(TextView)findViewById(R.id.main_stat);
mdrawerLayoout =(DrawerLayout)findViewById(R.id.main_activity);
NavigationView navigationView=findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
mAuth = FirebaseAuth.getInstance();
FitnessOptions fitnessOptions =
FitnessOptions.builder()
.addDataType(DataType.TYPE_HEART_RATE_BPM)
.addDataType(DataType.AGGREGATE_HEART_RATE_SUMMARY)
.build();
if (!GoogleSignIn.hasPermissions(GoogleSignIn.getLastSignedInAccount(this), fitnessOptions)) {
GoogleSignIn.requestPermissions(
this,
REQUEST_OAUTH_REQUEST_CODE,
GoogleSignIn.getLastSignedInAccount(this),
fitnessOptions);
} else {
subscribe();
}
if (!checkPermissions()) {
requestPermissions();
} else {
}
mHandler = new Handler();
startRepeatingTask();
ActionBarDrawerToggle toggle=new ActionBarDrawerToggle(this,mdrawerLayoout,mToolbar,
R.string.navigation_drawer_open,R.string.navigation_drawer_close);
mdrawerLayoout.addDrawerListener(toggle);
toggle.syncState();
}
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
int permissionState1 = ActivityCompat.checkSelfPermission(this,
Manifest.permission.BODY_SENSORS);
return permissionState == PackageManager.PERMISSION_GRANTED && permissionState1 == PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.BODY_SENSORS)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.BODY_SENSORS)) {
// Show an explanation to the user asynchronously -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.BODY_SENSORS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
// Permission has already been granted
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_OAUTH_REQUEST_CODE) {
subscribe();
}
}
}
public void subscribe() {
// To create a subscription, invoke the Recording API. As soon as the subscription is
// active, fitness data will start recording.
Fitness.getRecordingClient(this, GoogleSignIn.getLastSignedInAccount(this))
.subscribe(DataType.AGGREGATE_HEART_RATE_SUMMARY)
.addOnCompleteListener(
new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.i(TAG, "Successfully subscribed!");
} else {
Log.w(TAG, "There was a problem subscribing.", task.getException());
}
}
});
}
public static DataReadRequest queryFitnessData() {
// [START build_read_data_request]
// Setting a start and end date using a range of 1 week before this moment.
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.HOUR_OF_DAY, -1);
long startTime = cal.getTimeInMillis();
java.text.DateFormat dateFormat = getDateInstance();
Log.i(TAG, "Range Start: " + dateFormat.format(startTime));
Log.i(TAG, "Range End: " + dateFormat.format(endTime));
DataReadRequest readRequest =
new DataReadRequest.Builder()
// The data request can specify multiple data types to return, effectively
// combining multiple data queries into one call.
// In this example, it's very unlikely that the request is for several hundred
// datapoints each consisting of a few steps and a timestamp. The more likely
// scenario is wanting to see how many steps were walked per day, for 7 days.
.aggregate(DataType.TYPE_HEART_RATE_BPM, DataType.AGGREGATE_HEART_RATE_SUMMARY) // Analogous to a "Group By" in SQL, defines how data should be aggregated.
// bucketByTime allows for a time span, whereas bucketBySession would allow
// bucketing by "sessions", which would need to be defined in code.
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.build();
// [END build_read_data_request]
return readRequest;
}
private void readData() {
DataReadRequest readRequest = queryFitnessData();
Fitness.getHistoryClient(this, GoogleSignIn.getLastSignedInAccount(this))
.readData(readRequest)
.addOnSuccessListener(
new OnSuccessListener<DataReadResponse>() {
#Override
public void onSuccess(DataReadResponse dataReadResponse) {
printData(dataReadResponse);
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "There was a problem getting the step count.", e);
}
});
}
public static void printData(DataReadResponse dataReadResult) {
// [START parse_read_data_result]
// If the DataReadRequest object specified aggregated data, dataReadResult will be returned
// as buckets containing DataSets, instead of just DataSets.
if (dataReadResult.getBuckets().size() > 0) {
Log.i(
TAG, "Number of returned buckets of DataSets is: " + dataReadResult.getBuckets().size());
for (Bucket bucket : dataReadResult.getBuckets()) {
List<DataSet> dataSets = bucket.getDataSets();
for (DataSet dataSet : dataSets) {
dumpDataSet(dataSet);
}
}
} else if (dataReadResult.getDataSets().size() > 0) {
Log.i(
TAG, "Number of returned buckets of DataSets is:ajsbdajsb " );
}
// [END parse_read_data_result]
}
private static void dumpDataSet(DataSet dataSet) {
Log.i(TAG, "Data returned for Data type: " + dataSet.getDataType().getName());
DateFormat dateFormat = getTimeInstance();
for (DataPoint dp : dataSet.getDataPoints()) {
Log.i(TAG, "Data point:");
Log.i(TAG, "\tType: " + dp.getDataType().getName());
Log.i(TAG, "\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
Log.i(TAG, "\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS)));
for (Field field : dp.getDataType().getFields()) {
Log.i(TAG, "\tField: " + field.getName() + " Value: " + dp.getValue(field));
if(field.getName().equals("average")){
stepsNo.setText(dp.getValue(field).toString());
}
}
}
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch(menuItem.getItemId()) {
case R.id.nav_logout:
FirebaseAuth.getInstance().signOut();
sendToStart();
break;
case R.id.action_read_data:
Log.i(TAG, "Entered method read data");
readData();
break;
}
mdrawerLayoout.closeDrawer(GravityCompat.START);
return true;
}
#Override
public void onBackPressed() {
if(mdrawerLayoout.isDrawerOpen(GravityCompat.START)){
mdrawerLayoout.closeDrawer(GravityCompat.START);
}else {
super.onBackPressed();
}
}
#Override
public void onStart() {
super.onStart();
// Check if user is signed in (non-null) and update UI accordingly.
FirebaseUser currentUser = mAuth.getCurrentUser();
if(currentUser == null){
// sendToStart();
}
}
private void sendToStart() {
Intent startIntent = new Intent(MainActivity.this, StartActivity.class);
startActivity(startIntent);
finish();
}
private int mInterval = 3000; // 5 seconds by default, can be changed later
private Handler mHandler;
#Override
public void onDestroy() {
super.onDestroy();
stopRepeatingTask();
}
Runnable mStatusChecker = new Runnable() {
#Override
public void run() {
try {
readData(); //this function can change value of mInterval.
} finally {
// 100% guarantee that this always happens, even if
// your update method throws an exception
mHandler.postDelayed(mStatusChecker, mInterval);
}
}
};
void startRepeatingTask() {
mStatusChecker.run();
}
void stopRepeatingTask() {
mHandler.removeCallbacks(mStatusChecker);
}
}
Expected solution : to display the instaneous data
Fitness.getRecordingClient(this, GoogleSignIn.getLastSignedInAccount(this))
.subscribe(DataType.AGGREGATE_HEART_RATE_SUMMARY)
This won't work; you need to subscribe to DataType.TYPE_HEART_RATE_BPM.
Could you point me to where you're trying to read instantaneous heart rate? I couldn't see it in the code.
Related
So I have experienced some issue with in app purchase in my app.
I have started working on an old (8 months old) project I had earlier, but I am having some issue with app purchase. The app is already live on Play Store, so in app purchase is active.
In build.gradle (:app) I have changed from:
dependencies {
implementation 'com.android.billingclient:billing:2.2.1'
to this:
dependencies {
def billing_version = "3.0.0" // In App Purchase
implementation "com.android.billingclient:billing:$billing_version"
This is the full code of my UpgradeActivity.java:
package com.pinloop.testproj;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import java.util.ArrayList;
import java.util.List;
public class UpgradeActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private Button upgradeButton;
private TextView restoreButton;
private BillingClient billingClient;
private List skuList = new ArrayList();
private String sku = "com.pinloop.testproj.pro";
private SkuDetails mSkuDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upgrade);
upgradeButton = (Button) findViewById(R.id.upgradeButton);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
upgradeAction();
}
});
// Check In App Purchase
upgradeButton.setEnabled(true);
skuList.add(sku);
Boolean pro = getBoolFromPref(this,"myPref", sku);
if (pro)
{
Toast.makeText(this, "you are a premium user", Toast.LENGTH_SHORT).show();
//upgradeButton.setVisibility(View.INVISIBLE);
upgradeButton.setText(R.string.you_are_premium);
}
else
{
Toast.makeText(this, "not pro", Toast.LENGTH_SHORT).show();
setupBillingClient();
}
}
private void upgradeAction() {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
// In App Handler:
private void setupBillingClient() {
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener(){
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is setup successfully
loadAllSKUs();
}
}
#Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
private void loadAllSKUs() {
Toast.makeText(this, "loadAllSKUs", Toast.LENGTH_SHORT).show();
if (billingClient.isReady())
{
Toast.makeText(this, "billingclient ready", Toast.LENGTH_SHORT).show();
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "inside query" + billingResult.getResponseCode(), Toast.LENGTH_SHORT).show();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& !skuDetailsList.isEmpty())
{
for (Object skuDetailsObject : skuDetailsList) {
final SkuDetails skuDetails = (SkuDetails) skuDetailsObject;
Toast.makeText(UpgradeActivity.this, "" + skuDetails.getSku(), Toast.LENGTH_SHORT).show();
if (skuDetails.getSku() == sku)
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(skuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
});
}
}
}
});
}
else
Toast.makeText(this, "billingclient not ready", Toast.LENGTH_SHORT).show();
}
#Override
public void onPurchasesUpdated(BillingResult billingResult, #Nullable List<Purchase> purchases) {
int responseCode = billingResult.getResponseCode();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
}
else
if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
//Log.d(TAG, "User Canceled" + responseCode);
}
else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
}
else {
//Log.d(TAG, "Other code" + responseCode);
// Handle any other error codes.
}
}
private void handlePurchase(Purchase purchase) {
if (purchase.getSku().equals(sku)) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
Toast.makeText(this, "Purchase done. you are now a premium member.", Toast.LENGTH_SHORT).show();
}
}
private Boolean getBoolFromPref(Context context, String prefName, String constantName) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
return pref.getBoolean(constantName, false);
}
private void setBoolInPref(Context context,String prefName, String constantName, Boolean val) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(constantName, val);
//editor.commit();
editor.apply();
// Update 2015: Android recommends the use of apply() now over commit(),
// because apply() operates on a background thread instead of storing the persistent data immediately,
// and possible blocking the main thread.
}
}
Basically what happens when pressing the upgradeButton is that the app crashes. I cannot figure out what I am doing wrong. I am using the correct SKU, and the app has been live on Play Store for over a year now.
This is the error log I get when pressing the upgradeButton:
I/zygote: Do partial code cache collection, code=124KB, data=72KB
After code cache collection, code=124KB, data=72KB
Increasing code cache capacity to 512KB
D/EGL_emulation: eglMakeCurrent: 0xdb928540: ver 3 0 (tinfo 0xdb92bbd0)
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.pinloop.testproj, PID: 13352
java.lang.IllegalArgumentException: SKU cannot be null.
at com.android.billingclient.api.BillingFlowParams$Builder.build(com.android.billingclient:billing##3.0.0:23)
at com.pinloop.testproj.UpgradeActivity.upgradeAction(UpgradeActivity.java:130)
at com.pinloop.testproj.UpgradeActivity.access$000(UpgradeActivity.java:31)
at com.pinloop.testproj.UpgradeActivity$1.onClick(UpgradeActivity.java:56)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Not sure what to look at here, but I can see that java.lang.IllegalArgumentException: SKU cannot be null., but sku has already been declared: private String sku = "com.pinloop.testproj.pro";. Any ideas?
Here is the update code
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.aumhum.aumhum.R;
import java.util.ArrayList;
import java.util.List;
public class UpgradeActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private Button upgradeButton;
private TextView restoreButton;
private BillingClient billingClient;
private final List<String> skuList = new ArrayList();
private final String sku = "com.pinloop.testproj.pro";
private SkuDetails mSkuDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upgrade);
upgradeButton = (Button) findViewById(R.id.upgradeButton);
upgradeButton.setEnabled(false);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
upgradeAction();
}
});
// Check In App Purchase
skuList.add(sku);
setButtonStatus();
}
private void setButtonStatus(){
Boolean pro = getBoolFromPref(this,"myPref", sku);
if (pro)
{
Toast.makeText(this, "you are a premium user", Toast.LENGTH_SHORT).show();
//upgradeButton.setVisibility(View.INVISIBLE);
upgradeButton.setText(R.string.you_are_premium);
}
else
{
Toast.makeText(this, "not pro", Toast.LENGTH_SHORT).show();
setupBillingClient();
}
}
private void upgradeAction() {
if (mSkuDetails==null){
Toast.makeText(this,"Please wait while we get details",Toast.LENGTH_SHORT).show();
return;
}
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
// In App Handler:
private void setupBillingClient() {
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener(){
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is setup successfully
loadAllSKUs();
}
}
#Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
private void loadAllSKUs() {
Toast.makeText(this, "loadAllSKUs", Toast.LENGTH_SHORT).show();
if (billingClient.isReady())
{
Toast.makeText(this, "billingclient ready", Toast.LENGTH_SHORT).show();
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "inside query" + billingResult.getResponseCode(), Toast.LENGTH_SHORT).show();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& !skuDetailsList.isEmpty())
{
for (SkuDetails skuDetails : skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "" + skuDetails.getSku(), Toast.LENGTH_SHORT).show();
if (skuDetails.getSku().equals(sku)) {
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
}
}
}
}
});
}
else
Toast.makeText(this, "billingclient not ready", Toast.LENGTH_SHORT).show();
}
#Override
public void onPurchasesUpdated(BillingResult billingResult, #Nullable List<Purchase> purchases) {
int responseCode = billingResult.getResponseCode();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
}
else
if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
//Log.d(TAG, "User Canceled" + responseCode);
}
else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
}
else {
//Log.d(TAG, "Other code" + responseCode);
// Handle any other error codes.
}
}
private void handlePurchase(Purchase purchase) {
if (purchase.getSku().equals(sku)) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
Toast.makeText(this, "Purchase done. you are now a premium member.", Toast.LENGTH_SHORT).show();
}
}
private Boolean getBoolFromPref(Context context, String prefName, String constantName) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
return pref.getBoolean(constantName, false);
}
private void setBoolInPref(Context context,String prefName, String constantName, Boolean val) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(constantName, val);
//editor.commit();
editor.apply();
// Update 2015: Android recommends the use of apply() now over commit(),
// because apply() operates on a background thread instead of storing the persistent data immediately,
// and possible blocking the main thread.
}
}
3 problems
You have enabled upgrade button by default and don't have null check in upgradeAction method
You are comparing strings by == instead of .equals in java
You are setting upgrade onclick listener 2 times
the 'mSkuDetails' attribute?
if (skuDetails.getSku() == sku){
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
upgradeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
});
}
a. Verify the sku is the same as product code set in Google Play In App Billing section.
b. Debug through the code to see that null is not passed as sku in the call to launch billing flow.
c. Refresh Play Store cache in the device your are testing
So I have an application which has to monitor and range after beacons and than calculates the position of the user. After calculating this , the value is passed to the Wayfindigoverlayactivity.class where the value should be putt on the map with the blue dot.
I don know how to assign the value to the blue dot but before that my application is working on an endless loop and is opening the activity on ranging about 100x .
RangingActivity:
package com.indooratlas.android.sdk.examples.wayfinding;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.widget.EditText;
import android.content.Context;
import com.google.android.gms.maps.model.LatLng;
import com.indooratlas.android.sdk.IALocationRequest;
import com.indooratlas.android.sdk.examples.R;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
public class RangingActivity extends Activity implements BeaconConsumer,Runnable{
protected static final String TAG = "RangingActivity";
public LatLng center;
private final BlockingQueue queue;
private BeaconManager beaconManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ranging);
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.bind(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
beaconManager.unbind(this);
}
#Override
protected void onPause() {
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onBeaconServiceConnect() {
RangeNotifier rangeNotifier = new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
int beacon_number = beacons.size();
Beacon[] beacon_array = beacons.toArray(new Beacon[beacons.size()]);
Beacon device1 = null, device2 = null, device3 = null;
Constants constants = new Constants();
float txPow1 = 0;
double RSSI1Unfiltered = 0;
double RSSI2Unfiltered = 0;
float txPow2 = 0;
double RSSI3Unfiltered = 0;
float txPow3 = 0;
if (beacon_number == 4) {
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE1_UUID) {
device1 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE1_UUID) {
device1 = beacon_array[1];
} else {
device1 = beacon_array[2];
}
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE2_UUID) {
device2 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE2_UUID) {
device2 = beacon_array[1];
} else {
device2 = beacon_array[2];
}
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE3_UUID) {
device3 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE3_UUID) {
device3 = beacon_array[1];
} else {
device3 = beacon_array[2];
}
RSSI1Unfiltered = device1.getRssi();
RSSI2Unfiltered = device2.getRssi();
RSSI3Unfiltered = device3.getRssi();
txPow1 = device1.getTxPower();
txPow2 = device2.getTxPower();
txPow3 = device3.getTxPower();
} else if (beacon_number > 0) {
Log.d(TAG, "didRangeBeaconsInRegion called with beacon count: " + beacons.size());
for (int i = 0; i < beacon_number; i++) {
Beacon nextBeacon = beacon_array[i];
Log.d(TAG, "The next beacon " + nextBeacon.getIdentifier(0) + " is about " + nextBeacon.getDistance() + " meters away." + "RSSI is: " + nextBeacon.getRssi());
logToDisplay("The next beacon" + nextBeacon.getIdentifier(0) + " is about " + nextBeacon.getDistance() + " meters away." + "RSSI is: " + nextBeacon.getRssi());
}
}
Log.d(TAG, "FLOAT!!!!!!!!" + txPow1);
LocationFinder locationFinder = new LocationFinder();
//pass location
center = locationFinder.findLocation(RSSI1Unfiltered, txPow1, RSSI2Unfiltered, txPow2, RSSI3Unfiltered, txPow3);
Log.d(TAG, "Current coordinates: asta e asta e !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " + center.toString());
Bundle args = new Bundle();
args.putParcelable("b", center);
Intent intent00 = new Intent(RangingActivity.this, WayfindingOverlayActivity.class);
intent00.putExtras(args);
startActivity(intent00);
}
private void logToDisplay(final String s) {
runOnUiThread(new Runnable() {
#Override
public void run() {
EditText editText = RangingActivity.this.findViewById(R.id.textView3);
editText.append(s+"\n");
}
});
}
};
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
beaconManager.addRangeNotifier(rangeNotifier);
} catch (RemoteException e) {
}
}
/* Blockinqueue try---not working
RangingActivity(BlockingQueue q)
{
queue = q;
}
public void run() {
LatLng res;
try
{
res = center;
queue.put(res);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
*/
}
Everything works fine here , until I open the next class where my map is the WayfindingOverlayActivity
package com.indooratlas.android.sdk.examples.wayfinding;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import com.google.android.material.snackbar.Snackbar;
import androidx.fragment.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.indooratlas.android.sdk.IALocation;
import com.indooratlas.android.sdk.IALocationListener;
import com.indooratlas.android.sdk.IALocationManager;
import com.indooratlas.android.sdk.IALocationRequest;
import com.indooratlas.android.sdk.IAOrientationListener;
import com.indooratlas.android.sdk.IAOrientationRequest;
import com.indooratlas.android.sdk.IAPOI;
import com.indooratlas.android.sdk.IARegion;
import com.indooratlas.android.sdk.IARoute;
import com.indooratlas.android.sdk.IAWayfindingListener;
import com.indooratlas.android.sdk.IAWayfindingRequest;
import com.indooratlas.android.sdk.examples.R;
import com.indooratlas.android.sdk.examples.SdkExample;
import com.indooratlas.android.sdk.resources.IAFloorPlan;
import com.indooratlas.android.sdk.resources.IALatLng;
import com.indooratlas.android.sdk.resources.IALocationListenerSupport;
import com.indooratlas.android.sdk.resources.IAVenue;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator;
import com.squareup.picasso.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
#SdkExample(description = R.string.example_wayfinding_description)
public class WayfindingOverlayActivity extends FragmentActivity
implements GoogleMap.OnMapClickListener, OnMapReadyCallback ,Runnable{
private final BlockingQueue queue;
private static final String TAG = "IndoorAtlasExample";
/* used to decide when bitmap should be downscaled */
private static final int MAX_DIMENSION = 2048;
//kalman filter
private static final double KALMAN_R = 0.125d;
private static final double KALMAN_Q = 0.5d;
private GoogleMap mMap; // Might be null if Google Play services APK is not available.
private Circle mCircle;
private IARegion mOverlayFloorPlan = null;
private GroundOverlay mGroundOverlay = null;
private IALocationManager mIALocationManager;
private Target mLoadTarget;
private boolean mCameraPositionNeedsUpdating = true; // update on first location
private Marker mDestinationMarker;
private Marker mHeadingMarker;
private IAVenue mVenue;
private List<Marker> mPoIMarkers = new ArrayList<>();
private List<Polyline> mPolylines = new ArrayList<>();
private IARoute mCurrentRoute;
private IAWayfindingRequest mWayfindingDestination;
private IAWayfindingListener mWayfindingListener = new IAWayfindingListener() {
#Override
public void onWayfindingUpdate(IARoute route) {
mCurrentRoute = route;
if (hasArrivedToDestination(route)) {
// stop wayfinding
showInfo("You're there!");
mCurrentRoute = null;
mWayfindingDestination = null;
mIALocationManager.removeWayfindingUpdates();
}
updateRouteVisualization();
}
};
private IAOrientationListener mOrientationListener = new IAOrientationListener() {
#Override
public void onHeadingChanged(long timestamp, double heading) {
updateHeading(heading);
}
#Override
public void onOrientationChange(long timestamp, double[] quaternion) {
// we do not need full device orientation in this example, just the heading
}
};
private int mFloor;
// circle
private void showLocationCircle(LatLng center, double accuracyRadius) {
if (mCircle == null) {
// location can received before map is initialized, ignoring those updates
if (mMap != null) {
mCircle = mMap.addCircle(new CircleOptions()
.center(center)
.radius(accuracyRadius)
.fillColor(0x201681FB)
.strokeColor(0x500A78DD)
.zIndex(1.0f)
.visible(true)
.strokeWidth(5.0f));
mHeadingMarker = mMap.addMarker(new MarkerOptions()
.position(center)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.map_blue_dot))
.anchor(0.5f, 0.5f)
.flat(true));
}
} else {
// move existing markers position to received location
mCircle.setCenter(center);
mHeadingMarker.setPosition(center);
mCircle.setRadius(accuracyRadius);
}
}
private void updateHeading(double heading) {
if (mHeadingMarker != null) {
mHeadingMarker.setRotation((float) heading);
}
}
private IALocationListener mListener = new IALocationListenerSupport() {
public void onLocationChanged(IALocation location) {
Log.d(TAG, "NEW" + location.getLatitude() + " " + location.getLongitude());
if (mMap == null) {
return;
}
final LatLng center = new LatLng(location.getLatitude(),location.getLongitude());
final int newFloor = location.getFloorLevel();
if (mFloor != newFloor) {
updateRouteVisualization();
}
mFloor = newFloor;
showLocationCircle(center, location.getAccuracy());
if (mCameraPositionNeedsUpdating) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(center, 15.5f));
mCameraPositionNeedsUpdating = false;
}
}
};
/**
* Listener that changes overlay if needed
*/
private IARegion.Listener mRegionListener = new IARegion.Listener() {
#Override
public void onEnterRegion(final IARegion region) {
if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
Log.d(TAG, "enter floor plan " + region.getId());
mCameraPositionNeedsUpdating = true; // entering new fp, need to move camera
if (mGroundOverlay != null) {
mGroundOverlay.remove();
mGroundOverlay = null;
}
mOverlayFloorPlan = region; // overlay will be this (unless error in loading)
fetchFloorPlanBitmap(region.getFloorPlan());
//setupPoIs(mVenue.getPOIs(), region.getFloorPlan().getFloorLevel());
} else if (region.getType() == IARegion.TYPE_VENUE) {
mVenue = region.getVenue();
}
}
#Override
public void onExitRegion(IARegion region) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// prevent the screen going to sleep while app is on foreground
findViewById(android.R.id.content).setKeepScreenOn(true);
// instantiate IALocationManager
mIALocationManager = IALocationManager.create(this);
// Try to obtain the map from the SupportMapFragment.
((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map))
.getMapAsync(this);
Intent myIntent = new Intent(this, RangingActivity.class);
this.startActivity(myIntent);
Intent intent00 = getIntent();
LatLng center = intent00.getParcelableExtra("b");
Log.d(TAG,"Location!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + center);
}
#Override
protected void onDestroy() {
super.onDestroy();
// remember to clean up after ourselves
mIALocationManager.destroy();
}
/*Some blockingqueue---does not work
public class BlockinQueueExample
{
public void main(String[] args) throws Exception
{
BlockingQueue q = new ArrayBlockingQueue(1000);
RangingActivity producer = new RangingActivity(q);
WayfindingOverlayActivity consumer = new WayfindingOverlayActivity(q);
new Thread(producer).start();
new Thread(consumer).start();
}
}
WayfindingOverlayActivity(BlockingQueue q)
{
this.queue = q;
}
public void run() {
try{
queue.take();
Log.d(TAG,"BIANCABICA"+queue.take());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
*/
#Override
protected void onResume() {
super.onResume();
// start receiving location updates & monitor region changes
mIALocationManager.requestLocationUpdates(IALocationRequest.create(), mListener);
mIALocationManager.registerRegionListener(mRegionListener);
mIALocationManager.registerOrientationListener(
// update if heading changes by 1 degrees or more
new IAOrientationRequest(1, 0),
mOrientationListener);
if (mWayfindingDestination != null) {
mIALocationManager.requestWayfindingUpdates(mWayfindingDestination, mWayfindingListener);
}
}
EDIT , LAUNCHER ACTIVITY
package com.indooratlas.android.sdk.examples;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.indooratlas.android.sdk.examples.imageview.ImageViewActivity;
import com.indooratlas.android.sdk.examples.wayfinding.MonitoringActivity;
import com.indooratlas.android.sdk.examples.wayfinding.RangingActivity;
import com.indooratlas.android.sdk.examples.wayfinding.WayfindingOverlayActivity;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.powersave.BackgroundPowerSaver;
import org.altbeacon.beacon.startup.BootstrapNotifier;
import org.altbeacon.beacon.startup.RegionBootstrap;
public class Bianca extends Activity implements BootstrapNotifier {
private static final String TAG = "RANGE";
private RegionBootstrap regionBootstrap;
private Button button;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean haveDetectedBeaconsSinceBoot = false;
private MonitoringActivity monitoringActivity = null;
private String cumulativeLog = "";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_one);
BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
//--------------------------------meniu -------------------------------
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct();
}
});
Button button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct2();
}
});
Button button3 = findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct3();
}
});
Button button4 = findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct4();
}
});
//-----------------------------meniu----------------------------------
Notification.Builder builder = new Notification.Builder(this);
Intent intent = new Intent(this,WayfindingOverlayActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel channel = new NotificationChannel("My Notification Channel ID",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("My Notification Channel Description");
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
}
beaconManager.enableForegroundServiceScanning(builder.build(), 456);
Log.d(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap((BootstrapNotifier) this, region);
backgroundPowerSaver = new BackgroundPowerSaver(this);
}
public void openAct()
{
Intent intent = new Intent(this, WayfindingOverlayActivity.class);
startActivity(intent);
}
public void openAct2()
{
Intent intent2 = new Intent(this, RangingActivity.class);
startActivity(intent2);
}
public void openAct3()
{
Intent intent4 = new Intent(this, ImageViewActivity.class);
startActivity(intent4);
}
public void openAct4()
{
Intent intent5 = new Intent(this,RegionsActivity.class);
startActivity(intent5);
}
public void disableMonitoring() {
if (regionBootstrap != null) {
regionBootstrap.disable();
regionBootstrap = null;
}
}
public void enableMonitoring() {
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap((BootstrapNotifier) this, region);
}
public void didEnterRegion(Region arg0) {
// In this example, this class sends a notification to the user whenever a Beacon
// matching a Region (defined above) are first seen.
Log.d(TAG, "did enter region.");
if (!haveDetectedBeaconsSinceBoot) {
Log.d(TAG, "auto launching MainActivity");
// The very first time since boot that we detect an beacon, we launch the
// MainActivity
Intent intent = new Intent(this, WayfindingOverlayActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Important: make sure to add android:launchMode="singleInstance" in the manifest
// to keep multiple copies of this activity from getting created if the user has
// already manually launched the app.
this.startActivity(intent);
haveDetectedBeaconsSinceBoot = true;
} else {
if (monitoringActivity != null) {
// If the Monitoring Activity is visible, we log info about the beacons we have
// seen on its display
Log.d(TAG, "I see a beacon again");
} else {
// If we have already seen beacons before, but the monitoring activity is not in
// the foreground, we send a notification to the user on subsequent detections.
Log.d(TAG, "Sending notification.");
}
}
}
public void didExitRegion(Region arg0) {
Log.d(TAG,"I no longer see a beacon.");
}
#Override
public void didDetermineStateForRegion(int i, Region region) {
}
}
The second class is not fully posted , only where I make changes.
The intent in the second class is in the OnCreate part
The location is calculated in the logcat , the only problem is that the application is working in a loop
Please help me , I am stuck. Thanks
I guess, you only need to open RangingActivity from onCreate() of WayFindingOverlayActivity if center is null. This means, we need to open RangingActivity to get the value for center. Doing null check for the center will also ensure that the application doesn't go in loop and proceed when we have the value for center. The code in the onCreate() of WayFindingOverlayActivity may look like this :
// edited here
Bundle extras = getIntent().getExtras();
if(extras == null){
Intent myIntent = new Intent(this, RangingActivity.class);
this.startActivity(myIntent);
} else{
LatLng center = extras.getParcelable("b");
Log.d("Location!!", center);
// call showLocationCircle() to show the blue dot
showLocationCircle(center, yourAccuracyRadius);
}
Hi I am currently facing issues with connection and reading of BLE devices. I think I have connected as the code below prints a message for connection, however the reading of values from the BLE device does not seem to work. The service uuid returns a null value.
package com.example.asp_sqllite;
import android.Manifest;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class PlayActivity extends AppCompatActivity {
private static final int REQUEST_ENABLE_BT =1 ;
private Button btnPlay;
private Button btnConnect;
private ListView btList;
SQLiteDatabase db;
private Handler handler;
private ArrayList<String> deviceList = new ArrayList<>();
private ArrayAdapter<String> testAdapter;
private ArrayAdapter<String> deviceAdapter;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bleScanner;
private BluetoothGatt bleGatt;
private ArrayList<ScanResult> results = new ArrayList<>();
private ScanSettings settings;
private Intent intent;
private ListView bluetoothList;
private boolean completed = false;
static final UUID HR_SERVICE_UUID = UUID.fromString("0000110a-0000-1000-8000-00805f9b34fb");
private static final UUID HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
this.handler= new Handler();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
checkPermission();
}
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
this.btList = (ListView) findViewById(R.id.btlist);
deviceAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, android.R.id.text1);
testAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, android.R.id.text1);
intent = getIntent();
db = openOrCreateDatabase("myDB.db", MODE_PRIVATE, null);
checkBluetooth();
this.btnPlay = (Button) findViewById(R.id.btnPlay);
this.btnPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(bleGatt!=null) {
final String username = intent.getStringExtra("username");
System.out.println(username+"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
String sqlStatement = "insert into records (Name) values( '" + username + "')";
String result = updateTable(sqlStatement);
/*
* Run query to get recid to be passed over to the next activity
*
* */
final Cursor cursor = db.rawQuery("SELECT recID From records", null);
int num = 0;
if (cursor != null) {
cursor.moveToLast();
num = cursor.getInt(0);
cursor.close();
db.close();
}
Intent intent = new Intent(PlayActivity.this, testPlayActivity.class);
intent.putExtra("ID", Integer.toString(num));
startActivity(intent);
}
else
Toast.makeText(getApplicationContext(),"Connect to BLE device", Toast.LENGTH_LONG).show();
//finish();
}
});
this.btnConnect = (Button) findViewById(R.id.connect);
this.btnConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startScan();
Dialog d = new Dialog(PlayActivity.this); //open up dialog box with listview
d.setContentView(R.layout.bluetooth_device);
d.setTitle("Devices");
d.show();
//stopScan();
Button scanBtn = d.findViewById(R.id.scanBluetooth);
bluetoothList = d.findViewById(R.id.bluetoothDeviceList);
bluetoothList.setAdapter(deviceAdapter);
bluetoothList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
ScanResult device = results.get(i);
Toast.makeText(getApplicationContext(), device.getDevice().getName(), Toast.LENGTH_LONG).show();
bleGatt = device.getDevice().connectGatt(getApplicationContext(), false, bleGattCallback);
System.out.println("##############################################testing 123");
//finish();
try {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
Method getUuidsMethod = BluetoothAdapter.class.getDeclaredMethod("getUuids", null);
ParcelUuid[] uuids = (ParcelUuid[]) getUuidsMethod.invoke(adapter, null);
if(uuids != null) {
for (ParcelUuid uuid : uuids) {
System.out.println(uuid.getUuid().toString()+"#############################");
}
}else{
System.out.println("fail");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
scanBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) { //clear all list and adapters before scanning again
deviceList.clear();
deviceAdapter.clear();
results.clear();
startScan();
//stopScan();
handler.postDelayed(new Runnable() {
#Override
public void run() {
stopScan();
}
},5000);
}
});
handler.postDelayed(new Runnable() {
#Override
public void run() {
stopScan();
}
},5000);
}
});
}
public void checkPermission(){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
){//Can add more as per requirement
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},
123);
}
}
private void checkBluetooth()
{
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
private String updateTable(String sql) {
try {
db.beginTransaction();
db.execSQL(sql);
db.setTransactionSuccessful();
db.endTransaction();
} catch (Exception e) {
System.out.println(e.toString());
return ("Error");
}
Toast.makeText(this, "DB updated", Toast.LENGTH_LONG).show();
return ("Welcome");
}
private void stopScan(){
bleScanner = bluetoothAdapter.getBluetoothLeScanner();
bleScanner.stopScan(scanCallback);
}
private void startScan() {
bleScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bleScanner != null) { //setting up of scanner
final ScanFilter scanFilter =new ScanFilter.Builder().build();
settings =new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
bleScanner.startScan(Arrays.asList(scanFilter), settings, scanCallback);
//stopScan();
}
else
checkBluetooth();
}
private ScanCallback scanCallback = new ScanCallback() { //scan and return device results
#Override
public void onScanResult(int callbackType, ScanResult result) {
System.out.println("######### "+callbackType + result);
if (bleScanner != null && !deviceList.contains(result.getDevice().getName())) {
deviceList.add(result.getDevice().getName());
String device = result.getDevice().getName() + "\n" + result.getDevice().getAddress();
deviceAdapter.add(device); //Store device name and address
results.add(result); //records found devices as ScanResult
}
}
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e("TAG","onScanFailed");
}
};
private BluetoothGattCallback bleGattCallback = new BluetoothGattCallback()
{
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState== BluetoothProfile.STATE_CONNECTED){
System.out.println("#################################################################Connected");
}
else if (newState == BluetoothProfile.STATE_DISCONNECTED)
{
System.out.println("################################################################Not Connected");
}
gatt.discoverServices();
super.onConnectionStateChange(gatt, status, newState);
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
BluetoothGattService service = gatt.getService(HR_SERVICE_UUID);
System.out.println(service+"!!!!!!!!!!!!!!!!!!!!!!");
BluetoothGattCharacteristic temperatureCharacteristic = service.getCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID);
gatt.readCharacteristic(temperatureCharacteristic);
super.onServicesDiscovered(gatt, status);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {
final String value = characteristic.getStringValue(0);
runOnUiThread(new Runnable() {
#Override
public void run() {
if(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
//Toast.makeText(getApplicationContext(), "Correct Bluetooth: " + value, Toast.LENGTH_LONG).show();
System.out.println("###########################################################correct");
} else {
//Toast.makeText(getApplicationContext(), "Wrong Bluetooth", Toast.LENGTH_LONG).show();
System.out.println("##############################################################wrong");
}
}
});
BluetoothGattService service = gatt.getService(HR_SERVICE_UUID);
//readNextCharacteristic(gatt, characteristic);
super.onCharacteristicRead(gatt, characteristic, status);
}
};
}
The code is an example in arduino library(BLE_Example/BLE_HRM)
/*
* Copyright (c) 2016 RedBear
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/**
* #note This demo is Nordic HRM example.
* You could use nRF toolbox tool to test it.
*/
#include <nRF5x_BLE_API.h>
#define DEVICE_NAME "Nordic_HRM"
BLE ble;
Ticker ticker_task1;
static uint8_t hrmCounter = 100;
static uint8_t bpm[2] = {0x00, hrmCounter};
static const uint8_t location = 0x03;
static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE};
// Create characteristic and service
GattCharacteristic hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, bpm, sizeof(bpm), sizeof(bpm), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR,(uint8_t *)&location, sizeof(location), sizeof(location),GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
GattCharacteristic *hrmChars[] = {&hrmRate, &hrmLocation, };
GattService hrmService(GattService::UUID_HEART_RATE_SERVICE, hrmChars, sizeof(hrmChars) / sizeof(GattCharacteristic *));
void disconnectionCallBack(const Gap::DisconnectionCallbackParams_t *params) {
Serial.println("Disconnected!");
Serial.println("Restarting the advertising process");
ble.startAdvertising();
}
void periodicCallback() {
if (ble.getGapState().connected) {
// Update the HRM measurement
// First byte = 8-bit values, no extra info, Second byte = uint8_t HRM value
// See --> https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
hrmCounter++;
if (hrmCounter == 175)
hrmCounter = 100;
bpm[1] = hrmCounter;
ble.updateCharacteristicValue(hrmRate.getValueAttribute().getHandle(), bpm, sizeof(bpm));
}
}
void setup() {
// put your setup code here, to run once
Serial.begin(9600);
Serial.println("Nordic_HRM Demo ");
// Init timer task
ticker_task1.attach(periodicCallback, 1);
// Init ble
ble.init();
ble.onDisconnection(disconnectionCallBack);
// setup adv_data and srp_data
ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
ble.accumulateAdvertisingPayload(GapAdvertisingData::HEART_RATE_SENSOR_HEART_RATE_BELT);
ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
// set adv_type
ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
// add service
ble.addService(hrmService);
// set device name
ble.setDeviceName((const uint8_t *)DEVICE_NAME);
// set tx power,valid values are -40, -20, -16, -12, -8, -4, 0, 4
ble.setTxPower(4);
// set adv_interval, 100ms in multiples of 0.625ms.
ble.setAdvertisingInterval(160);
// set adv_timeout, in seconds
ble.setAdvertisingTimeout(0);
// start advertising
ble.startAdvertising();
}
void loop() {
// put your main code here, to run repeatedly:
ble.waitForEvent();
}
My app is supposed to display a Google Map with a heatmap, but I can't even get the map to display. It's only showing the logo in the bottom left corner. There seems to be no errors. I also checked the gradle and all the necessary dependencies are there. I can't tell if this is due to an actual error or lack of memory/slow processing.
Here's the MainActivity
package com.example.alexlevine.oceanapp;
package com.example.alexlevine.oceanapp;
import android.app.Dialog;
import android.content.Intent;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.TileOverlayOptions;
import com.google.maps.android.heatmaps.HeatmapTileProvider;
import com.google.maps.android.heatmaps.WeightedLatLng;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import static com.example.alexlevine.oceanapp.R.id.seekBar;
import static com.example.alexlevine.oceanapp.fetchSOCATData.co2array;
public class MainActivity extends AppCompatActivity
implements OnMapReadyCallback {
public static SeekBar seekbar;
public static GoogleMap mGoogleMap;
private HeatmapTileProvider mProvider;
public static TextView minyear;
public static TextView maxyear;
public static TextView currentyeartext;
public static int displayyear;
public static boolean usingSOCAT;
public static String typeKey;
public static Spinner dropdown;
private String[] mNavigationDrawerItemTitles;
private DrawerLayout mDrawerLayout;
android.support.v7.app.ActionBarDrawerToggle mDrawerToggle;
Button b2;
Button b4;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (googleServicesAvailable()) {
Toast.makeText(this, "Play services available", Toast.LENGTH_LONG).show();;
initMap();
b2 = (Button) findViewById(R.id.button2);
b4 = (Button) findViewById(R.id.button4);
b2.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v.getId() == R.id.button2) {
Intent i = new Intent(MainActivity.this, CarbonCalcActivity.class);
startActivity(i);
finish();
}
}
});
b4.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.getId() == R.id.button4) {
Intent i = new Intent(MainActivity.this, CalcData.class);
startActivity(i);
finish();
}
}
}
);
//get the spinner from the xml.
dropdown = (Spinner)findViewById(R.id.spinner);
//create a list of items for the spinner.
String[] items = new String[]{"Ocean CO2 Densities", "Land CO2 Emissions (all sources)", "Electricity CO2 Emissions", "Aviation CO2 Emissions", "Ocean Shipping CO2 Emissions"};
//create an adapter to describe how the items are displayed, adapters are used in several places in android.
//There are multiple variations of this, but this is the basic variant.
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, items);
//set the spinners adapter to the previously created one.
dropdown.setAdapter(adapter);
currentyeartext = (TextView) findViewById(R.id.currentyear);
minyear = (TextView) findViewById(R.id.minyear);
maxyear = (TextView) findViewById(R.id.maxyear);
typeKey = "00totals";
dropdown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
switch (position) {
case 0: {
usingSOCAT = true;
displayyear = 1991;
seekbar.setMax(24);
minyear.setText("1991");
maxyear.setText("2015");
break;
}
case 1: {
usingSOCAT = false;
displayyear = 1997;
minyear.setText("1997");
maxyear.setText("2010");
seekbar.setMax(14);
typeKey = "00totals";
break;
}
case 2: {
usingSOCAT = false;
displayyear = 1997;
minyear.setText("1997");
maxyear.setText("2010");
seekbar.setMax(14);
typeKey = "01elec";
// Whatever you want to happen when the thrid item gets selected
break;
}
case 3: {
usingSOCAT = false;
displayyear = 1997;
minyear.setText("1997");
maxyear.setText("2010");
seekbar.setMax(14);
typeKey = "11aviation";;
// Whatever you want to happen when the thrid item gets selected
break;
}
case 4: {
usingSOCAT = false;
displayyear = 1997;
minyear.setText("1997");
maxyear.setText("2010");
seekbar.setMax(14);
typeKey = "12shipping";
// Whatever you want to happen when the thrid item gets selected
break;
}
}
}
public void onNothingSelected(AdapterView<?> parent)
{
}
});
seekbar = (SeekBar) findViewById(seekBar);
seekbar.setOnSeekBarChangeListener(
new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
displayyear = progress + 1991;
if(usingSOCAT)
{
displayyear = progress + 1991;
new fetchSOCATData().execute();
}
else
{
displayyear = progress + 1997;
new fetchFFDASData().execute();
//List<WeightedLatLng> calledData = fetchFFDASData.ffdasArray;
//createHeatMap(fetchFFDASData.ffdasArray);
minyear.setText((CharSequence) fetchFFDASData.ffdasArray);
}
currentyeartext.setText("Showing: " + displayyear);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
if(usingSOCAT) {
}
//new fetchSOCATData().execute();
/*minyear = (TextView) findViewById(R.id.minyear);
maxyear = (TextView) findViewById(R.id.maxyear);
minyear.setText("1957");
maxyear.setText("2015");*/
//fetchSOCATData process = new fetchSOCATData();
//process.execute();
} else {
//No maps layout
}
}
//#Override
public void onNothingSelected(AdapterView<?> parent) {
}
public void initMap() {
SupportMapFragment mapFragment = (SupportMapFragment) this.getSupportFragmentManager()
.findFragmentById(R.id.mapFragment);
if(mapFragment != null) {
mapFragment.getMapAsync((OnMapReadyCallback)this);
}
}
public void createHeatMap(List<WeightedLatLng> dataset)
{
List<WeightedLatLng> locations = dataset;
/*mProvider = new HeatmapTileProvider.Builder().weightedData(locations).build();
mProvider.setRadius( HeatmapTileProvider.DEFAULT_RADIUS );
mGoogleMap.addTileOverlay(new TileOverlayOptions().tileProvider(mProvider));
mProvider = new HeatmapTileProvider();
mProvider.setWeightedData(dataset);
mProvider = mProvider.build();*/
//for (LatLng coordinate : coordinates) {
//WeightedLatLng weightedCoordinate = new WeightedLatLng(coordinate);
//com.google.maps.android.geometry.Point point = weightedCoordinate.getPoint();
// Filter points at infinity
/* if (Double.isInfinite(point.x) || Double.isInfinite(point.y)) {
Log.w(TAG, "Attempted to add undefined point " + coordinate);
continue;
}
weightedCoordinates.add(weightedCoordinate);*/
//}
mProvider = new HeatmapTileProvider.Builder()
.weightedData(dataset)
.opacity(0.5)
.build();
mGoogleMap.addTileOverlay(new TileOverlayOptions().tileProvider(mProvider));
Toast.makeText(this, "working", Toast.LENGTH_SHORT).show();
}
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
//goToLocation(-65, -12);
}
public void goToLocation(double lat, double lng) {
LatLng ll = new LatLng(lat, lng);
CameraUpdate update = CameraUpdateFactory.newLatLng(ll);
mGoogleMap.moveCamera(update);
}
public boolean googleServicesAvailable() {
GoogleApiAvailability api = GoogleApiAvailability.getInstance();
int isAvailable = api.isGooglePlayServicesAvailable(this);
if (isAvailable == ConnectionResult.SUCCESS)
return true;
else if (api.isUserResolvableError(isAvailable)) {
Dialog dialog = api.getErrorDialog(this, isAvailable, 0);
dialog.show();
} else
Toast.makeText(this, "Can't connect to play services", Toast.LENGTH_LONG).show();
return false;
}
}
Here's my code to fetch the data for the heatmap:
package com.example.alexlevine.oceanapp;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import com.google.android.gms.maps.model.LatLng;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.maps.android.heatmaps.WeightedLatLng;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import static com.example.alexlevine.oceanapp.MainActivity.displayyear;
import static com.example.alexlevine.oceanapp.MainActivity.minyear;
#RequiresApi(api = Build.VERSION_CODES.CUPCAKE)
public class fetchSOCATData extends AsyncTask<Object, Object, ArrayList<WeightedLatLng>> {
//String data = "http://ferret.pmel.noaa.gov/socat/erddap/tabledap/socat_v4_fulldata.geoJson?year%2Cmonth%2Cday%2Chour%2Cminute%2Csecond%2Clongitude%2Clatitude%2Csal%2CTemperature_equi%2CTemperature_atm%2CpCO2_atm_wet_actual%2Cdelta_fCO2%2Crelative_humidity%2Cspecific_humidity%2Cwind_speed_true%2Cwind_speed_rel%2Cwind_dir_true%2Cwind_dir_rel%2CfCO2_recommended%2Cregion_id%2Ctime&organization=%22%22";
//public static SOCATCO2DataPoint.FeaturesBean[] co2array;
public static List<WeightedLatLng> co2array;
#Override
protected ArrayList<WeightedLatLng> doInBackground(Object... params) {
co2array = new ArrayList<WeightedLatLng>();
int year = displayyear;
URL url = null;
try {
//url = new URL("http://ferret.pmel.noaa.gov/socat/erddap/tabledap/socat_v4_fulldata.geoJson?year%2Cmonth%2Cday%2Chour%2Cminute%2Csecond%2Clongitude%2Clatitude%2Csal%2CTemperature_equi%2CTemperature_atm%2CpCO2_atm_wet_actual%2Cdelta_fCO2%2Crelative_humidity%2Cspecific_humidity%2Cwind_speed_true%2Cwind_speed_rel%2Cwind_dir_true%2Cwind_dir_rel%2CfCO2_recommended%2Cregion_id%2Ctime&organization=%22%22&year%3E=2015&month%3C=1&day%3C=1&hour%3C=1&minute%3C=1&second%3C=10");
//url = new URL("http://ferret.pmel.noaa.gov/socat/erddap/tabledap/socat_v4_fulldata.json?year%2Clongitude%2Clatitude%2CfCO2_recommended%2Ctime&organization=%22%22&year%3E=" + year + "&year%3C=" + year);
//url = new URL("http://ferret.pmel.noaa.gov/socat/erddap/tabledap/socat_v4_fulldata.json?year%2Clongitude%2Clatitude%2CfCO2_recommended%2Ctime&organization=%22%22&year%3E=2015&year%3C=2015&month%3E=1&month%3C=1&day%3E=1&day%3C=1&hour%3E=1&hour%3C=1");
//url = new URL("http://ferret.pmel.noaa.gov/socat/erddap/tabledap/socat_v4_fulldata.geoJson?year%2Cmonth%2Cday%2Chour%2Cminute%2Csecond%2Clongitude%2Clatitude%2CfCO2_recommended%2Ctime&organization=%22%22&year%3E=" + year + "&year%3C=" + year);
url = new URL("http://ferret.pmel.noaa.gov/socat/erddap/tabledap/socat_v4_fulldata.json?longitude%2Clatitude%2CfCO2_recommended&organization=%22%22&year=" + displayyear + "&day=1&hour=1&minute%3C=5&second%3C=30&longitude!=NaN&longitude!=NaN&latitude!=NaN&latitude!=NaN");
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = null;
try {
response = client.newCall(request).execute();
} catch (IOException e1) {
e1.printStackTrace();
}
String result = null;
try {
result = response.body().string();
} catch (IOException e1) {
e1.printStackTrace();
}
//System.out.print("result" + result);
Gson gson = new Gson();
Type collectionType = new TypeToken<Collection<OfficialSOCATPoint.TableBean>>() {}.getType();
OfficialSOCATPoint enums = gson.fromJson(result, OfficialSOCATPoint.class);
//Collection<OfficialSOCATPoint.TableBean> enums = gson.fromJson(result, collectionType);
/*SOCATCO2DataPoint.FeaturesBean[] protoExtract = enums.toArray(new SOCATCO2DataPoint.FeaturesBean[enums.size()]);*/
OfficialSOCATPoint protoExtract = gson.fromJson(result, OfficialSOCATPoint.class);
//OfficialSOCATPoint.TableBean[] protoExtract = enums.toArray(new OfficialSOCATPoint.TableBean[enums.size()]);
List<List<String>> dataArray = (List<List<String>>) protoExtract.getTable().getRows();
//for (int i = 0; i < dataArray.size(); i++)
for (int i = 0; i < 2; i++)
{
if(dataArray.get(i).get(2) != "null" && dataArray.get(i).get(1) != "null" && dataArray.get(i).get(0) != "null")
{
//co2array.add(new WeightedLatLng(new LatLng(Double.parseDouble(dataArray.get(i).get(0)), Double.parseDouble(dataArray.get(i).get(1))), Double.parseDouble(dataArray.get(i).get(2))));
}
}
return (ArrayList<WeightedLatLng>) co2array;
}
//#Override
protected void onPostExecute(ArrayList<WeightedLatLng> fb) {
super.onPostExecute(fb);
}
}
Here's the logcat:
10-17 16:45:27.884 1583-1593/? W/WallpaperManagerService: Cannot extract colors because wallpaper could not be read.
10-17 16:45:27.886 1583-1593/? W/WallpaperManagerService: Cannot extract colors because wallpaper could not be read.
10-17 16:45:17.747 2160-2160/? W/zygote: Common causes for lock verification issues are non-optimized dex code
10-17 16:45:17.955 1583-1807/? W/WallpaperManagerService: Cannot extract colors because wallpaper could not be read.
10-17 16:45:19.896 2138-2227/? W/zygote: Common causes for lock verification issues are non-optimized dex code
10-17 16:45:28.114 2188-2312/? W/ErrorProcessor: onFatalError, processing error from engine(4)
com.google.android.apps.gsa.shared.speech.b.g: Error reading from input stream
at com.google.android.apps.gsa.staticplugins.recognizer.j.a.a(SourceFile:28)
at com.google.android.apps.gsa.staticplugins.recognizer.j.b.run(SourceFile:15)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at com.google.android.apps.gsa.shared.util.concurrent.a.ag.run(Unknown Source:4)
at com.google.android.apps.gsa.shared.util.concurrent.a.bo.run(SourceFile:4)
at com.google.android.apps.gsa.shared.util.concurrent.a.bo.run(SourceFile:4)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
at com.google.android.apps.gsa.shared.util.concurrent.a.ak.run(SourceFile:6)
Caused by: com.google.android.apps.gsa.shared.exception.GsaIOException: Error code: 393238 | Buffer overflow, no available space.
at com.google.android.apps.gsa.speech.audio.Tee.f(SourceFile:103)
at com.google.android.apps.gsa.speech.audio.au.read(SourceFile:2)
at java.io.InputStream.read(InputStream.java:101)
at com.google.android.apps.gsa.speech.audio.ao.run(SourceFile:18)
at com.google.android.apps.gsa.speech.audio.an.run(SourceFile:2)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at com.google.android.apps.gsa.shared.util.concurrent.a.ag.run(Unknown Source:4)
at com.google.android.apps.gsa.shared.util.concurrent.a.bo.run(SourceFile:4)
at com.google.android.apps.gsa.shared.util.concurrent.a.bo.run(SourceFile:4)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
at com.google.android.apps.gsa.shared.util.concurrent.a.ak.run(SourceFile:6)
10-17 16:45:38.815 2510-2510/? I/Finsky: [2] com.google.android.finsky.setup.VpaService.a(32): Should not show workaround PAI step because experiment disabled
10-17 16:45:48.416 1767-2047/? I/GCoreUlr: WorldUpdater:init: Ensuring that reporting is stopped because of reasons: (no Google accounts)
10-17 16:45:49.205 2069-2069/? I/MediaRouter: Unselecting the current route because it is no longer selectable: null
10-17 16:46:16.401 1767-2047/? W/NetworkScheduler: Immediate task was not started com.google.android.videos/.service.drm.RefreshLicenseTaskService{u=0 tag="refresh_license_forced" trigger=window{start=0s,end=1s,earliest=-1s,latest=0s} requirements=[NET_CONNECTED] attributes=[] scheduled=-1s last_run=N/A jid=N/A status=PENDING retries=0 client_lib=MANCHEGO_GCM-10400000}. Rescheduling immediate tasks can cause excessive battery drain.
10-17 16:46:16.928 1767-2047/? W/NetworkScheduler: Immediate task was not started com.google.android.gms/.tapandpay.gcmtask.TapAndPayGcmTaskService{u=0 tag="immediate" trigger=window{start=0s,end=1s,earliest=-1s,latest=0s} requirements=[NET_CONNECTED] attributes=[] scheduled=-1s last_run=N/A jid=N/A status=PENDING retries=0 client_lib=MANCHEGO_GCM-14366000}. Rescheduling immediate tasks can cause excessive battery drain.
Please help me with this. Thanks!
I'd like to check GPS both when the app is started and when the refresh button is hit, and use those data points, in the form of mLatitude and mLongitude to call the weather api. Eventually I'm going to geocode the city but right now for debugging purposes I'm outputting the GPS coordinates to the locationLabel textview.
my MainActivity.java:
package com.example.paxie.stormy.ui;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.paxie.stormy.GPS_Service;
import com.example.paxie.stormy.R;
import com.example.paxie.stormy.weather.Current;
import com.example.paxie.stormy.weather.Day;
import com.example.paxie.stormy.weather.Forecast;
import com.example.paxie.stormy.weather.Hour;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
public static final String DAILY_FORECAST = "DAILY_FORECAST";
public static final String HOURLY_FORECAST = "HOURLY_FORECAST";
private Forecast mForecast;
private double mLatitude;
private double mLongitude;
private BroadcastReceiver broadcastReceiver;
#Bind(R.id.timeLabel)
TextView mTimeLabel;
#Bind(R.id.temperatureLabel)
TextView mTemperatureLabel;
#Bind(R.id.humidityValue)
TextView mHumidityValue;
#Bind(R.id.precipValue)
TextView mPrecipValue;
#Bind(R.id.summaryLabel)
TextView mSummaryLabel;
#Bind(R.id.iconImageView)
ImageView mIconImageView;
#Bind(R.id.refreshImageView)
ImageView mRefreshImageView;
#Bind(R.id.progressBar)
ProgressBar mProgressBar;
#Bind(R.id.locationLabel)
TextView mLocationlabel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getForecast();
}
});
getForecast();
Log.d(TAG, "Main UI code is running!");
}
private void getForecast() {
if(!runtime_permissions())
checkGPS();
String apiKey = "1621390f8c36997cb1904914b726df52";
String forecastUrl = "https://api.forecast.io/forecast/" + apiKey +
"/" + mLatitude + "," + mLongitude;
if (isNetworkAvailable()) {
toggleRefresh();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(forecastUrl)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
alertUserAboutError();
}
#Override
public void onResponse(Response response) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
try {
String jsonData = response.body().string();
Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mForecast = parseForecastDetails(jsonData);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateDisplay();
}
});
} else {
alertUserAboutError();
}
} catch (IOException e)
{
Log.e(TAG, "Exception caught: ", e);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
} else {
Toast.makeText(this, "Network is currently unavailable!", Toast.LENGTH_LONG).show();
}
}
private void toggleRefresh() {
if (mProgressBar.getVisibility() == View.INVISIBLE) {
mProgressBar.setVisibility(View.VISIBLE);
mRefreshImageView.setVisibility(View.INVISIBLE);
} else {
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImageView.setVisibility(View.VISIBLE);
}
}
private void updateDisplay() {
mLocationlabel.setText(mLatitude + " " + mLongitude);
Current current = mForecast.getCurrent();
mTemperatureLabel.setText(current.getTemperature() + "");
mTimeLabel.setText("At " + current.getFormattedTime() + " it will be:");
mHumidityValue.setText(current.getHumidity() + "");
mPrecipValue.setText(current.getPrecipChance() + "%");
mSummaryLabel.setText(current.getSummary());
Drawable drawable = ContextCompat.getDrawable(this, current.getIconId());
mIconImageView.setImageDrawable(drawable);
}
private Forecast parseForecastDetails(String jsonData) throws JSONException {
Forecast forecast = new Forecast();
forecast.setCurrent(getCurrentDetails(jsonData));
forecast.setHourlyForecast(getHourlyForecast(jsonData));
forecast.setDailyForecast(getDailyForecast(jsonData));
return forecast;
}
private Day[] getDailyForecast(String jsonData) throws JSONException {
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject daily = forecast.getJSONObject("daily");
JSONArray data = daily.getJSONArray("data");
Day[] days = new Day[data.length()];
for (int i = 0; i < data.length(); i++) {
JSONObject jsonDay = data.getJSONObject(i);
Day day = new Day();
day.setSummary(jsonDay.getString("summary"));
day.setIcon(jsonDay.getString("icon"));
day.setTemperatureMax(jsonDay.getDouble("temperatureMax"));
day.setTime(jsonDay.getLong("time"));
day.setTimeZone(timezone);
days[i] = day;
}
return days;
}
private Hour[] getHourlyForecast(String jsonData) throws JSONException {
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject hourly = forecast.getJSONObject("hourly");
JSONArray data = hourly.getJSONArray("data");
Hour[] hours = new Hour[data.length()];
for (int i = 0; i < data.length(); i++) {
JSONObject jsonHour = data.getJSONObject(i);
Hour hour = new Hour();
hour.setSummary(jsonHour.getString("summary"));
hour.setTemperature(jsonHour.getDouble("temperature"));
hour.setIcon(jsonHour.getString("icon"));
hour.setTime(jsonHour.getLong("time"));
hour.setTimeZone(timezone);
hours[i] = hour;
}
return hours;
}
private Current getCurrentDetails(String jsonData) throws JSONException {
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
Log.i(TAG, "From JSON: " + timezone);
JSONObject currently = forecast.getJSONObject("currently");
Current current = new Current();
current.setHumidity(currently.getDouble("humidity"));
current.setTime(currently.getLong("time"));
current.setIcon(currently.getString("icon"));
current.setPrecipChance(currently.getDouble("precipProbability"));
current.setSummary(currently.getString("summary"));
current.setTemperature(currently.getDouble("temperature"));
current.setTimeZone(timezone);
Log.d(TAG, current.getFormattedTime());
return current;
}
private boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
if (networkInfo != null && networkInfo.isConnected()) {
isAvailable = true;
}
return isAvailable;
}
private void alertUserAboutError() {
AlertDialogFragment dialog = new AlertDialogFragment();
dialog.show(getFragmentManager(), "error_dialog");
}
#OnClick(R.id.dailyButton)
public void startDailyActivity(View view) {
Intent intent = new Intent(this, DailyForecastActivity.class);
intent.putExtra(DAILY_FORECAST, mForecast.getDailyForecast());
startActivity(intent);
}
#OnClick(R.id.hourlyButton)
public void startHourlyActivity(View view) {
Intent intent = new Intent(this, HourlyForecastActivity.class);
intent.putExtra(HOURLY_FORECAST, mForecast.getHourlyForecast());
startActivity(intent);
}
private void checkGPS() {
if (broadcastReceiver == null) {
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
mLatitude = (double) intent.getExtras().get("latitude");
mLongitude = (double) intent.getExtras().get("longitude");
}
};
}
registerReceiver(broadcastReceiver, new IntentFilter("location_update"));
}
private boolean runtime_permissions() {
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 100);
return true;
}
return false;
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 100) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
getForecast();
} else {
runtime_permissions();
}
}
}
#Override
protected void onResume() {
super.onResume();
checkGPS();
}
}
my GPSservice.java:
package com.example.paxie.stormy;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.support.annotation.Nullable;
/**
* Created by paxie on 10/27/16.
*/
public class GPS_Service extends Service {
private LocationListener listener;
private LocationManager locationManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
listener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Intent i = new Intent("location_update");
i.putExtra("coordinates",location.getLongitude()+" "+location.getLatitude());
i.putExtra("longitude", location.getLongitude());
i.putExtra("latitude", location.getLatitude());
sendBroadcast(i);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
};
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
//noinspection MissingPermission
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,3000,0,listener);
}
#Override
public void onDestroy() {
super.onDestroy();
if(locationManager != null){
//noinspection MissingPermission
locationManager.removeUpdates(listener);
}
}
}
Here are two things you can try:
1. Please ensure you have included the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions in your manifest. If your app targets Android API 23 and above, you will need to include runtime permission handling.
2. Instead of having a LocationListener object as a class member, you should have your Service implement it as an interface and implement its onLocationChanged() method.
Apart from this, you really should consider using the location APIs' in Google Play Services, rather than those in AOSP (i.e. android.location).
See Making Your App Location-Aware